From a9c4ab6aa4e6ef1331472ddae64aec4727358218 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 24 Mar 2019 22:19:06 +0300 Subject: [PATCH] Support generic type signatures in IR --- .../org/teavm/cache/CachedClassReader.java | 20 + .../java/org/teavm/cache/CachedField.java | 7 + .../java/org/teavm/cache/CachedMethod.java | 25 + .../java/org/teavm/model/ClassHolder.java | 26 + .../java/org/teavm/model/ClassReader.java | 6 + .../java/org/teavm/model/FieldHolder.java | 10 + .../java/org/teavm/model/FieldReader.java | 2 + .../org/teavm/model/GenericTypeParameter.java | 41 ++ .../org/teavm/model/GenericValueType.java | 579 ++++++++++++++++++ .../java/org/teavm/model/MethodHolder.java | 38 ++ .../java/org/teavm/model/MethodReader.java | 8 + .../java/org/teavm/model/ReferenceCache.java | 63 +- .../org/teavm/parsing/ClassRefsRenamer.java | 91 +++ .../main/java/org/teavm/parsing/Parser.java | 132 ++++ 14 files changed, 1047 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/teavm/model/GenericTypeParameter.java create mode 100644 core/src/main/java/org/teavm/model/GenericValueType.java diff --git a/core/src/main/java/org/teavm/cache/CachedClassReader.java b/core/src/main/java/org/teavm/cache/CachedClassReader.java index 0675bbc28..dd5b69ce2 100644 --- a/core/src/main/java/org/teavm/cache/CachedClassReader.java +++ b/core/src/main/java/org/teavm/cache/CachedClassReader.java @@ -20,26 +20,46 @@ import java.util.Map; import java.util.Set; import org.teavm.model.ClassReader; import org.teavm.model.FieldReader; +import org.teavm.model.GenericTypeParameter; +import org.teavm.model.GenericValueType; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; class CachedClassReader extends CachedElement implements ClassReader { String parent; + GenericTypeParameter[] parameters; + GenericValueType.Object genericParent; String owner; Set interfaces; + Set genericInterfaces; Map methods; Map fields; + @Override + public GenericTypeParameter[] getGenericParameters() { + return parameters != null ? parameters.clone() : new GenericTypeParameter[0]; + } + @Override public String getParent() { return parent; } + @Override + public GenericValueType.Object getGenericParent() { + return genericParent; + } + @Override public Set getInterfaces() { return interfaces; } + @Override + public Set getGenericInterfaces() { + return genericInterfaces; + } + @Override public MethodReader getMethod(MethodDescriptor method) { return methods.get(method); diff --git a/core/src/main/java/org/teavm/cache/CachedField.java b/core/src/main/java/org/teavm/cache/CachedField.java index c5199470d..19c4ca3c8 100644 --- a/core/src/main/java/org/teavm/cache/CachedField.java +++ b/core/src/main/java/org/teavm/cache/CachedField.java @@ -17,10 +17,12 @@ package org.teavm.cache; import org.teavm.model.FieldReader; import org.teavm.model.FieldReference; +import org.teavm.model.GenericValueType; import org.teavm.model.ValueType; class CachedField extends CachedMember implements FieldReader { ValueType type; + GenericValueType genericType; Object initialValue; FieldReference reference; @@ -29,6 +31,11 @@ class CachedField extends CachedMember implements FieldReader { return type; } + @Override + public GenericValueType getGenericType() { + return genericType; + } + @Override public Object getInitialValue() { return initialValue; diff --git a/core/src/main/java/org/teavm/cache/CachedMethod.java b/core/src/main/java/org/teavm/cache/CachedMethod.java index 91414a2fa..80e51f134 100644 --- a/core/src/main/java/org/teavm/cache/CachedMethod.java +++ b/core/src/main/java/org/teavm/cache/CachedMethod.java @@ -19,6 +19,8 @@ import java.lang.ref.WeakReference; import java.util.function.Supplier; import org.teavm.model.AnnotationContainerReader; import org.teavm.model.AnnotationValue; +import org.teavm.model.GenericTypeParameter; +import org.teavm.model.GenericValueType; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; @@ -27,16 +29,29 @@ import org.teavm.model.ValueType; class CachedMethod extends CachedMember implements MethodReader { MethodReference reference; + GenericTypeParameter[] typeParameters; + GenericValueType genericReturnType; + GenericValueType[] genericParameterTypes; CachedAnnotations[] parameterAnnotations; AnnotationValue annotationDefault; WeakReference program; Supplier programSupplier; + @Override + public GenericTypeParameter[] getTypeParameters() { + return typeParameters != null ? typeParameters.clone() : new GenericTypeParameter[0]; + } + @Override public ValueType getResultType() { return reference.getReturnType(); } + @Override + public GenericValueType getGenericResultType() { + return genericReturnType; + } + @Override public int parameterCount() { return reference.parameterCount(); @@ -52,6 +67,16 @@ class CachedMethod extends CachedMember implements MethodReader { return reference.parameterType(index); } + @Override + public int genericParameterCount() { + return genericParameterTypes != null ? genericParameterTypes.length : 0; + } + + @Override + public GenericValueType genericParameterType(int index) { + return genericParameterTypes != null ? genericParameterTypes[index] : null; + } + @Override public ValueType[] getParameterTypes() { return reference.getParameterTypes(); diff --git a/core/src/main/java/org/teavm/model/ClassHolder.java b/core/src/main/java/org/teavm/model/ClassHolder.java index afe4cd296..0aeec5582 100644 --- a/core/src/main/java/org/teavm/model/ClassHolder.java +++ b/core/src/main/java/org/teavm/model/ClassHolder.java @@ -18,8 +18,11 @@ package org.teavm.model; import java.util.*; public class ClassHolder extends ElementHolder implements ClassReader { + private GenericTypeParameter[] genericParameters; private String parent = Object.class.getName(); + private GenericValueType.Object genericParent; private Set interfaces = new LinkedHashSet<>(); + private Set genericInterfaces = new LinkedHashSet<>(); private Map methods = new LinkedHashMap<>(); private Map fields = new LinkedHashMap<>(); private String ownerName; @@ -28,6 +31,15 @@ public class ClassHolder extends ElementHolder implements ClassReader { super(name); } + @Override + public GenericTypeParameter[] getGenericParameters() { + return genericParameters != null ? genericParameters.clone() : null; + } + + public void setGenericParameters(GenericTypeParameter[] genericParameters) { + this.genericParameters = genericParameters != null ? genericParameters.clone() : null; + } + @Override public String getParent() { return parent; @@ -37,11 +49,25 @@ public class ClassHolder extends ElementHolder implements ClassReader { this.parent = parent; } + @Override + public GenericValueType.Object getGenericParent() { + return genericParent; + } + + public void setGenericParent(GenericValueType.Object genericParent) { + this.genericParent = genericParent; + } + @Override public Set getInterfaces() { return interfaces; } + @Override + public Set getGenericInterfaces() { + return genericInterfaces; + } + @Override public MethodHolder getMethod(MethodDescriptor method) { return methods.get(method); diff --git a/core/src/main/java/org/teavm/model/ClassReader.java b/core/src/main/java/org/teavm/model/ClassReader.java index 4e4dc3385..a28c875f5 100644 --- a/core/src/main/java/org/teavm/model/ClassReader.java +++ b/core/src/main/java/org/teavm/model/ClassReader.java @@ -19,10 +19,16 @@ import java.util.Collection; import java.util.Set; public interface ClassReader extends ElementReader { + GenericTypeParameter[] getGenericParameters(); + String getParent(); Set getInterfaces(); + GenericValueType.Object getGenericParent(); + + Set getGenericInterfaces(); + MethodReader getMethod(MethodDescriptor method); Collection getMethods(); diff --git a/core/src/main/java/org/teavm/model/FieldHolder.java b/core/src/main/java/org/teavm/model/FieldHolder.java index 9398def4f..5d8f35d50 100644 --- a/core/src/main/java/org/teavm/model/FieldHolder.java +++ b/core/src/main/java/org/teavm/model/FieldHolder.java @@ -17,6 +17,7 @@ package org.teavm.model; public class FieldHolder extends MemberHolder implements FieldReader { private ValueType type; + private GenericValueType genericType; private Object initialValue; private ClassHolder owner; private FieldReference reference; @@ -30,6 +31,15 @@ public class FieldHolder extends MemberHolder implements FieldReader { return type; } + @Override + public GenericValueType getGenericType() { + return genericType; + } + + public void setGenericType(GenericValueType genericType) { + this.genericType = genericType; + } + public void setType(ValueType type) { this.type = type; } diff --git a/core/src/main/java/org/teavm/model/FieldReader.java b/core/src/main/java/org/teavm/model/FieldReader.java index b1108ac12..de876971e 100644 --- a/core/src/main/java/org/teavm/model/FieldReader.java +++ b/core/src/main/java/org/teavm/model/FieldReader.java @@ -18,6 +18,8 @@ package org.teavm.model; public interface FieldReader extends MemberReader { ValueType getType(); + GenericValueType getGenericType(); + Object getInitialValue(); FieldReference getReference(); diff --git a/core/src/main/java/org/teavm/model/GenericTypeParameter.java b/core/src/main/java/org/teavm/model/GenericTypeParameter.java new file mode 100644 index 000000000..c44983bd9 --- /dev/null +++ b/core/src/main/java/org/teavm/model/GenericTypeParameter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 konsoletyper. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model; + +public class GenericTypeParameter { + private String name; + private GenericValueType.Reference classBound; + private GenericValueType.Reference[] interfaceBounds; + + public GenericTypeParameter(String name, GenericValueType.Reference classBound, + GenericValueType.Reference[] interfaceBounds) { + this.name = name; + this.classBound = classBound; + this.interfaceBounds = interfaceBounds.clone(); + } + + public String getName() { + return name; + } + + public GenericValueType.Reference getClassBound() { + return classBound; + } + + public GenericValueType.Reference[] getInterfaceBounds() { + return interfaceBounds.clone(); + } +} diff --git a/core/src/main/java/org/teavm/model/GenericValueType.java b/core/src/main/java/org/teavm/model/GenericValueType.java new file mode 100644 index 000000000..2c0c7e9dd --- /dev/null +++ b/core/src/main/java/org/teavm/model/GenericValueType.java @@ -0,0 +1,579 @@ +/* + * Copyright 2019 konsoletyper. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public abstract class GenericValueType { + private static final Argument[] EMPTY_ARRAY = new Argument[0]; + + public static final class Primitive extends GenericValueType { + private final PrimitiveType kind; + private final int hash; + + private Primitive(PrimitiveType kind) { + this.kind = kind; + hash = 17988782 ^ (kind.ordinal() * 31); + } + + public PrimitiveType getKind() { + return kind; + } + + @Override + void toString(StringBuilder sb) { + switch (kind) { + case BOOLEAN: + sb.append("Z"); + break; + case BYTE: + sb.append("B"); + break; + case SHORT: + sb.append("S"); + break; + case INTEGER: + sb.append("I"); + break; + case LONG: + sb.append("J"); + break; + case FLOAT: + sb.append("F"); + break; + case CHARACTER: + sb.append("C"); + break; + case DOUBLE: + sb.append("D"); + break; + } + } + + @Override + public boolean equals(java.lang.Object obj) { + return this == obj; + } + + @Override + public int hashCode() { + return hash; + } + } + + public static final class Void extends GenericValueType { + private Void() { + } + + @Override + void toString(StringBuilder sb) { + sb.append("V"); + } + + @Override + public boolean equals(java.lang.Object obj) { + return this == obj; + } + + @Override + public int hashCode() { + return 53604390; + } + } + + public static abstract class Reference extends GenericValueType { + private Reference() { + } + } + + public static class Argument { + public static final Argument ANY = new Argument(ArgumentKind.ANY, null); + private final ArgumentKind kind; + private final Reference value; + private int hash; + + private Argument(ArgumentKind kind, Reference value) { + this.kind = kind; + this.value = value; + } + + public ArgumentKind getKind() { + return kind; + } + + public Reference getValue() { + return value; + } + + public static Argument invariant(Reference value) { + return new Argument(ArgumentKind.INVARIANT, value); + } + + public static Argument covariant(Reference value) { + return new Argument(ArgumentKind.COVARIANT, value); + } + + public static Argument contravariant(Reference value) { + return new Argument(ArgumentKind.CONTRAVARIANT, value); + } + + @Override + public int hashCode() { + if (hash == 0) { + switch (kind) { + case ANY: + hash = 43866465; + break; + case CONTRAVARIANT: + hash = 6993579; + break; + case COVARIANT: + hash = 4379540; + break; + case INVARIANT: + hash = 72320251; + break; + } + } + if (value != null) { + hash = hash * 47 + value.hashCode(); + } + return hash; + } + + @Override + public boolean equals(java.lang.Object obj) { + if (this == obj) { + return true; + } + return super.equals(obj); + } + } + + public enum ArgumentKind { + COVARIANT, + CONTRAVARIANT, + INVARIANT, + ANY + } + + public static final class Object extends Reference { + private final Object parent; + private final String className; + private final Argument[] arguments; + private int hash; + + public Object(Object parent, String className, Argument[] arguments) { + this.parent = parent; + this.className = className; + this.arguments = arguments != null ? arguments.clone() : EMPTY_ARRAY; + } + + public Object getParent() { + return parent; + } + + public String getClassName() { + return className; + } + + public Argument[] getArguments() { + return arguments.length == 0 ? EMPTY_ARRAY : arguments.clone(); + } + + @Override + void toString(StringBuilder sb) { + sb.append("L"); + toStringImpl(sb); + sb.append(";"); + } + + private void toStringImpl(StringBuilder sb) { + if (parent != null) { + parent.toStringImpl(sb); + sb.append("."); + } + sb.append(className.replace('.', '/')); + if (arguments.length > 0) { + sb.append("<"); + for (Argument argument : arguments) { + switch (argument.kind) { + case ANY: + sb.append("*"); + break; + case CONTRAVARIANT: + sb.append("-"); + break; + case COVARIANT: + sb.append("+"); + break; + case INVARIANT: + break; + } + if (argument.value != null) { + argument.value.toString(sb); + } + } + sb.append(">"); + } + } + + @Override + public boolean equals(java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Object)) { + return false; + } + Object that = (Object) obj; + if (that.arguments.length != arguments.length) { + return false; + } + if (!that.className.equals(className)) { + return false; + } + for (int i = 0; i < arguments.length; ++i) { + if (!arguments[i].equals(that.arguments[i])) { + return false; + } + } + return Objects.equals(parent, that.parent); + } + + @Override + public int hashCode() { + if (hash == 0) { + hash = 85396296 ^ (className.hashCode() * 167); + for (Argument arg : arguments) { + hash = hash * 31 + arg.hashCode(); + } + if (parent != null) { + hash = 167 * hash + parent.hashCode(); + } + if (hash == 0) { + ++hash; + } + } + return hash; + } + } + + public static final class Variable extends Reference { + private final String name; + private int hash; + + public Variable(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Variable)) { + return false; + } + Variable that = (Variable) obj; + return name.equals(that.name); + } + + @Override + public int hashCode() { + if (hash == 0) { + hash = 69257681 ^ (name.hashCode() * 173); + if (hash == 0) { + ++hash; + } + } + return hash; + } + + @Override + void toString(StringBuilder sb) { + sb.append("T"); + sb.append(name); + sb.append(";"); + } + } + + public static final class Array extends Reference { + private final GenericValueType itemType; + private int hash; + + + public Array(GenericValueType itemType) { + this.itemType = itemType; + } + + public GenericValueType getItemType() { + return itemType; + } + + @Override + public boolean equals(java.lang.Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Array)) { + return false; + } + + Array that = (Array) obj; + return itemType.equals(that.itemType); + } + + @Override + public int hashCode() { + if (hash == 0) { + hash = 27039876 ^ (itemType.hashCode() * 193); + if (hash == 0) { + ++hash; + } + } + return hash; + } + + @Override + void toString(StringBuilder sb) { + sb.append("["); + itemType.toString(sb); + } + } + + + public static final Void VOID = new Void(); + + public static final Primitive BOOLEAN = new Primitive(PrimitiveType.BOOLEAN); + + public static final Primitive BYTE = new Primitive(PrimitiveType.BYTE); + + public static final Primitive SHORT = new Primitive(PrimitiveType.SHORT); + + public static final Primitive INT = new Primitive(PrimitiveType.INTEGER); + + public static final Primitive FLOAT = new Primitive(PrimitiveType.FLOAT); + + public static final Primitive LONG = new Primitive(PrimitiveType.LONG); + + public static final Primitive DOUBLE = new Primitive(PrimitiveType.DOUBLE); + + public static final Primitive CHAR = new Primitive(PrimitiveType.CHARACTER); + + private GenericValueType() { + } + + abstract void toString(StringBuilder sb); + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + toString(sb); + return sb.toString(); + } + + public static GenericValueType parse(String text, ParsePosition position) { + int i = position.index; + if (i >= text.length()) { + return null; + } + switch (text.charAt(i++)) { + case 'V': + position.index = i; + return VOID; + case 'Z': + position.index = i; + return BOOLEAN; + case 'B': + position.index = i; + return BYTE; + case 'S': + position.index = i; + return SHORT; + case 'C': + position.index = i; + return CHAR; + case 'I': + position.index = i; + return INT; + case 'J': + position.index = i; + return LONG; + case 'F': + position.index = i; + return FLOAT; + case 'D': + position.index = i; + return DOUBLE; + case 'L': + position.index = i; + return parseObjectImpl(text, position); + case 'T': + position.index = i; + return parseVariable(text, position); + case '[': { + position.index = i; + GenericValueType itemType = parse(text, position); + return itemType != null ? new Array(itemType) : null; + } + default: + return null; + } + } + + private static GenericValueType parseObjectImpl(String text, ParsePosition position) { + int i = position.index; + int last = i; + Object parent = null; + while (i < text.length()) { + char c = text.charAt(i); + switch (c) { + case ';': { + if (last == i) { + return null; + } + String name = text.substring(last, i).replace('/', '.'); + i++; + position.index = i; + return new Object(parent, name, EMPTY_ARRAY); + } + case '.': { + if (i == last) { + return null; + } + parent = new Object(parent, text.substring(last, i).replace('/', '.'), EMPTY_ARRAY); + i++; + last = i; + break; + } + case '<': { + position.index = i + 1; + Argument[] arguments = parseArguments(text, position); + if (arguments == null || position.index >= text.length()) { + return null; + } + String name = text.substring(last, i).replace('/', '.'); + Object result = new Object(parent, name, arguments); + i = position.index; + c = text.charAt(i++); + if (c == ';') { + position.index = i; + return result; + } else if (c == '.') { + parent = result; + last = i; + break; + } else { + return null; + } + } + default: + i++; + break; + } + } + return null; + } + + private static Argument[] parseArguments(String text, ParsePosition position) { + List arguments = new ArrayList<>(); + while (position.index < text.length()) { + char c = text.charAt(position.index); + switch (c) { + case '>': + if (arguments.isEmpty()) { + return null; + } + position.index++; + return arguments.toArray(new Argument[0]); + case '*': + position.index++; + arguments.add(Argument.ANY); + break; + case '+': { + position.index++; + Reference constraint = parseReference(text, position); + if (constraint == null) { + return null; + } + arguments.add(Argument.covariant(constraint)); + break; + } + case '-': { + position.index++; + Reference constraint = parseReference(text, position); + if (constraint == null) { + return null; + } + arguments.add(Argument.contravariant(constraint)); + break; + } + default: { + Reference constraint = parseReference(text, position); + if (constraint == null) { + return null; + } + arguments.add(Argument.invariant(constraint)); + break; + } + } + } + return null; + } + + public static Reference parseReference(String text, ParsePosition position) { + GenericValueType type = parse(text, position); + return type instanceof Reference ? (Reference) type : null; + } + + public static Object parseObject(String text, ParsePosition position) { + GenericValueType type = parse(text, position); + return type instanceof Object ? (Object) type : null; + } + + private static GenericValueType parseVariable(String text, ParsePosition position) { + int i = position.index; + while (i < text.length()) { + if (text.charAt(i) == ';') { + if (i == position.index) { + return null; + } + Variable result = new Variable(text.substring(position.index, i)); + position.index = i + 1; + return result; + } + i++; + } + return null; + } + + public GenericValueType parse(String text) { + ParsePosition position = new ParsePosition(); + GenericValueType type = parse(text, position); + return position.index == text.length() ? type : null; + } + + public static class ParsePosition { + public int index; + } +} diff --git a/core/src/main/java/org/teavm/model/MethodHolder.java b/core/src/main/java/org/teavm/model/MethodHolder.java index 420950768..d38081c37 100644 --- a/core/src/main/java/org/teavm/model/MethodHolder.java +++ b/core/src/main/java/org/teavm/model/MethodHolder.java @@ -15,10 +15,14 @@ */ package org.teavm.model; +import java.util.Objects; import java.util.function.Function; public class MethodHolder extends MemberHolder implements MethodReader { private MethodDescriptor descriptor; + private GenericTypeParameter[] typeParameters; + private GenericValueType genericReturnType; + private GenericValueType[] genericParameterTypes; private ClassHolder owner; private Program program; private Function programSupplier; @@ -44,6 +48,40 @@ public class MethodHolder extends MemberHolder implements MethodReader { return descriptor.getResultType(); } + @Override + public GenericValueType getGenericResultType() { + return genericReturnType; + } + + @Override + public int genericParameterCount() { + return genericParameterTypes != null ? genericParameterTypes.length : 0; + } + + @Override + public GenericValueType genericParameterType(int index) { + return genericParameterTypes != null ? genericParameterTypes[index] : null; + } + + public void setGenericSignature(GenericValueType returnType, GenericValueType[] parameterTypes) { + genericReturnType = Objects.requireNonNull(returnType); + genericParameterTypes = parameterTypes.clone(); + } + + public void removeGenericSignature() { + genericReturnType = null; + genericParameterTypes = null; + } + + @Override + public GenericTypeParameter[] getTypeParameters() { + return typeParameters != null ? typeParameters.clone() : new GenericTypeParameter[0]; + } + + public void setTypeParameters(GenericTypeParameter[] typeParameters) { + this.typeParameters = typeParameters != null ? typeParameters.clone() : null; + } + @Override public int parameterCount() { return descriptor.parameterCount(); diff --git a/core/src/main/java/org/teavm/model/MethodReader.java b/core/src/main/java/org/teavm/model/MethodReader.java index 8d66e6f6c..eeea236d3 100644 --- a/core/src/main/java/org/teavm/model/MethodReader.java +++ b/core/src/main/java/org/teavm/model/MethodReader.java @@ -18,12 +18,20 @@ package org.teavm.model; public interface MethodReader extends MemberReader { ValueType getResultType(); + GenericTypeParameter[] getTypeParameters(); + + GenericValueType getGenericResultType(); + int parameterCount(); ValueType[] getSignature(); ValueType parameterType(int index); + int genericParameterCount(); + + GenericValueType genericParameterType(int index); + ValueType[] getParameterTypes(); AnnotationContainerReader parameterAnnotation(int index); diff --git a/core/src/main/java/org/teavm/model/ReferenceCache.java b/core/src/main/java/org/teavm/model/ReferenceCache.java index dcc813799..4b26a0f5f 100644 --- a/core/src/main/java/org/teavm/model/ReferenceCache.java +++ b/core/src/main/java/org/teavm/model/ReferenceCache.java @@ -23,8 +23,8 @@ public class ReferenceCache { private Map fieldRefenceCache = new HashMap<>(); private Map descriptorCache = new HashMap<>(); private Map valueTypeCache = new HashMap<>(); + private Map genericValueTypeCache = new HashMap<>(); private Map stringCache = new HashMap<>(); - private Map referenceParseCache = new HashMap<>(); private Map descriptorParseCache = new HashMap<>(); private Map valueTypeParseCache = new HashMap<>(); @@ -103,6 +103,67 @@ public class ReferenceCache { return result; } + public GenericValueType getCached(GenericValueType valueType) { + if (valueType instanceof GenericValueType.Primitive + || valueType instanceof GenericValueType.Variable + || valueType instanceof GenericValueType.Void) { + return valueType; + } + + GenericValueType result = genericValueTypeCache.get(valueType); + if (result == null) { + result = valueType; + if (result instanceof GenericValueType.Object) { + GenericValueType.Object objectType = (GenericValueType.Object) result; + String className = objectType.getClassName(); + String cachedClassName = getCached(className); + + boolean changed = false; + GenericValueType.Argument[] arguments = objectType.getArguments(); + for (int i = 0; i < arguments.length; ++i) { + GenericValueType.Argument argument = arguments[i]; + if (argument.getValue() != null) { + GenericValueType.Reference cachedValue = (GenericValueType.Reference) getCached( + argument.getValue()); + if (cachedValue != argument.getValue()) { + changed = true; + switch (argument.getKind()) { + case COVARIANT: + argument = GenericValueType.Argument.covariant(cachedValue); + break; + case CONTRAVARIANT: + argument = GenericValueType.Argument.contravariant(cachedValue); + break; + case INVARIANT: + argument = GenericValueType.Argument.invariant(cachedValue); + break; + } + arguments[i] = argument; + } + } + } + + GenericValueType.Object parent = objectType.getParent(); + GenericValueType.Object cachedParent = parent != null + ? (GenericValueType.Object) getCached(parent) + : null; + + if (changed || className != cachedClassName || parent != cachedParent) { + result = new GenericValueType.Object(parent, className, arguments); + } + } else if (result instanceof GenericValueType.Array) { + GenericValueType item = ((GenericValueType.Array) result).getItemType(); + GenericValueType cachedItem = getCached(item); + if (item != cachedItem) { + result = new GenericValueType.Array(cachedItem); + } + } + genericValueTypeCache.put(result, result); + } + + return result; + } + public String getCached(String s) { String result = stringCache.get(s); if (result == null) { diff --git a/core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java b/core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java index b08ec7325..a19a97f4d 100644 --- a/core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java +++ b/core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java @@ -28,6 +28,8 @@ import org.teavm.model.BasicBlock; import org.teavm.model.ClassHolder; import org.teavm.model.FieldHolder; import org.teavm.model.FieldReference; +import org.teavm.model.GenericTypeParameter; +import org.teavm.model.GenericValueType; import org.teavm.model.Instruction; import org.teavm.model.InvokeDynamicInstruction; import org.teavm.model.MethodDescriptor; @@ -94,6 +96,17 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor { renamedCls.getInterfaces().add(mappedIfaceName); } } + + GenericValueType.Object genericParent = cls.getGenericParent(); + if (genericParent != null) { + renamedCls.setGenericParent((GenericValueType.Object) rename(genericParent)); + } + for (GenericValueType.Object genericInterface : cls.getGenericInterfaces()) { + renamedCls.getGenericInterfaces().add((GenericValueType.Object) rename(genericInterface)); + } + + renamedCls.setGenericParameters(cls.getGenericParameters()); + return renamedCls; } @@ -114,9 +127,42 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor { renamedMethod.setProgram(method.getProgram()); rename(method.getAnnotations(), renamedMethod.getAnnotations()); rename(renamedMethod.getProgram()); + + renamedMethod.setTypeParameters(rename(method.getTypeParameters())); + GenericValueType genericResultType = method.getGenericResultType(); + if (genericResultType != null) { + genericResultType = rename(method.getGenericResultType()); + } + GenericValueType[] genericParameters = new GenericValueType[method.genericParameterCount()]; + for (int i = 0; i < genericParameters.length; ++i) { + genericParameters[i] = rename(method.genericParameterType(i)); + } + if (genericResultType != null) { + renamedMethod.setGenericSignature(genericResultType, genericParameters); + } + return renamedMethod; } + private GenericTypeParameter[] rename(GenericTypeParameter[] typeParameters) { + for (int i = 0; i < typeParameters.length; ++i) { + typeParameters[i] = rename(typeParameters[i]); + } + return typeParameters; + } + + private GenericTypeParameter rename(GenericTypeParameter typeParameter) { + GenericValueType.Reference classBound = typeParameter.getClassBound(); + if (classBound != null) { + classBound = (GenericValueType.Reference) rename(classBound); + } + GenericValueType.Reference[] interfaceBounds = typeParameter.getInterfaceBounds(); + for (int j = 0; j < interfaceBounds.length; ++j) { + interfaceBounds[j] = (GenericValueType.Reference) rename(interfaceBounds[j]); + } + return new GenericTypeParameter(typeParameter.getName(), classBound, interfaceBounds); + } + public FieldHolder rename(FieldHolder field) { FieldHolder renamedField = new FieldHolder(field.getName()); renamedField.getModifiers().addAll(field.getModifiers()); @@ -124,6 +170,12 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor { renamedField.setType(rename(field.getType())); renamedField.setInitialValue(field.getInitialValue()); rename(field.getAnnotations(), renamedField.getAnnotations()); + + GenericValueType genericType = field.getGenericType(); + if (genericType != null) { + renamedField.setGenericType(rename(genericType)); + } + return renamedField; } @@ -139,6 +191,45 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor { } } + private GenericValueType rename(GenericValueType type) { + if (type instanceof GenericValueType.Array) { + GenericValueType itemType = ((GenericValueType.Array) type).getItemType(); + return referenceCache.getCached(new GenericValueType.Array(rename(itemType))); + } else if (type instanceof GenericValueType.Object) { + GenericValueType.Object object = (GenericValueType.Object) type; + String className = classNameMapper.apply(object.getClassName()); + GenericValueType.Object parent = object.getParent(); + if (parent != null) { + parent = (GenericValueType.Object) rename(parent); + } + GenericValueType.Argument[] arguments = object.getArguments(); + for (int i = 0; i < arguments.length; ++i) { + GenericValueType.Argument argument = arguments[i]; + GenericValueType.Reference value = argument.getValue(); + if (value != null) { + value = (GenericValueType.Reference) rename(value); + } + switch (argument.getKind()) { + case INVARIANT: + arguments[i] = GenericValueType.Argument.invariant(value); + break; + case COVARIANT: + arguments[i] = GenericValueType.Argument.covariant(value); + break; + case CONTRAVARIANT: + arguments[i] = GenericValueType.Argument.contravariant(value); + break; + default: + break; + } + } + + return referenceCache.getCached(new GenericValueType.Object(parent, className, arguments)); + } else { + return type; + } + } + private ValueType[] rename(ValueType[] types) { return Arrays.stream(types).map(this::rename).toArray(ValueType[]::new); } diff --git a/core/src/main/java/org/teavm/parsing/Parser.java b/core/src/main/java/org/teavm/parsing/Parser.java index 2c381d1ff..3907f549f 100644 --- a/core/src/main/java/org/teavm/parsing/Parser.java +++ b/core/src/main/java/org/teavm/parsing/Parser.java @@ -42,6 +42,8 @@ import org.teavm.model.ElementHolder; import org.teavm.model.ElementModifier; import org.teavm.model.FieldHolder; import org.teavm.model.FieldReference; +import org.teavm.model.GenericTypeParameter; +import org.teavm.model.GenericValueType; import org.teavm.model.Instruction; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodHolder; @@ -101,9 +103,49 @@ public class Parser { node.visibleParameterAnnotations != null ? node.visibleParameterAnnotations[i] : null, node.invisibleParameterAnnotations != null ? node.invisibleParameterAnnotations[i] : null); } + + if (node.signature != null) { + parseMethodGenericSignature(node.signature, method); + } + return method; } + private void parseMethodGenericSignature(String signature, MethodHolder method) { + GenericValueType.ParsePosition position = new GenericValueType.ParsePosition(); + + List typeParameters = new ArrayList<>(); + String elementName = "method '" + method.getDescriptor() + "'"; + if (signature.charAt(position.index) == '<') { + parseTypeParameters(signature, typeParameters, position, elementName); + } + + List parameters = new ArrayList<>(); + if (signature.charAt(position.index) != '(') { + throw couldNotParseSignature(elementName, signature); + } + position.index++; + while (signature.charAt(position.index) != ')') { + GenericValueType parameter = GenericValueType.parse(signature, position); + if (parameter == null) { + throw couldNotParseSignature(elementName, signature); + } + parameters.add(parameter); + } + position.index++; + GenericValueType returnType = GenericValueType.parse(signature, position); + if (returnType == null) { + throw couldNotParseSignature(elementName, signature); + } + + if (position.index < signature.length() && signature.charAt(position.index) != '^') { + throw couldNotParseSignature(elementName, signature); + } + + method.setTypeParameters(typeParameters.toArray(new GenericTypeParameter[0])); + method.setGenericSignature(returnType, parameters.toArray(new GenericValueType[0])); + } + private static void applyDebugNames(Program program, PhiUpdater phiUpdater, ProgramParser parser, Variable[] argumentMapping) { if (program.basicBlockCount() == 0) { @@ -242,6 +284,11 @@ public class Parser { cls.getInterfaces().add(referenceCache.getCached(iface.replace('/', '.'))); } } + + if (node.signature != null) { + parseSignature(cls, node.signature); + } + for (Object obj : node.fields) { FieldNode fieldNode = (FieldNode) obj; FieldHolder field = parseField(fieldNode); @@ -267,12 +314,97 @@ public class Parser { return cls; } + private void parseSignature(ClassHolder cls, String signature) { + GenericValueType.ParsePosition position = new GenericValueType.ParsePosition(); + List typeParameters = new ArrayList<>(); + + String elementName = "class '" + cls.getName() + "'"; + if (signature.charAt(position.index) == '<') { + parseTypeParameters(signature, typeParameters, position, elementName); + } + + cls.setGenericParameters(typeParameters.toArray(new GenericTypeParameter[0])); + + GenericValueType.Object supertype = GenericValueType.parseObject(signature, position); + if (supertype == null) { + throw couldNotParseSignature(elementName, signature); + } + cls.setGenericParent(supertype); + + List interfaces = new ArrayList<>(); + while (position.index < signature.length()) { + GenericValueType.Object itf = GenericValueType.parseObject(signature, position); + if (itf == null) { + throw couldNotParseSignature(elementName, signature); + } + interfaces.add(itf); + } + + cls.getGenericInterfaces().addAll(interfaces); + } + + private void parseTypeParameters(String signature, List typeParameters, + GenericValueType.ParsePosition position, String elementName) { + position.index++; + do { + if (position.index >= signature.length()) { + throw couldNotParseSignature(elementName, signature); + } + int next = signature.indexOf(':', position.index); + if (next < 0 || next == position.index) { + throw couldNotParseSignature(elementName, signature); + } + String name = signature.substring(position.index, next); + position.index = next; + + List bounds = new ArrayList<>(); + while (true) { + if (position.index >= signature.length()) { + throw couldNotParseSignature(elementName, signature); + } + char c = signature.charAt(position.index); + if (c != ':') { + break; + } + position.index++; + if (bounds.isEmpty() && signature.charAt(position.index) == ':') { + bounds.add(null); + } else { + GenericValueType.Reference bound = GenericValueType.parseReference(signature, position); + if (bound == null) { + throw couldNotParseSignature(elementName, signature); + } + bounds.add(bound); + } + } + + typeParameters.add(new GenericTypeParameter(name, bounds.get(0), + bounds.subList(1, bounds.size()).toArray(new GenericValueType.Reference[0]))); + } while (signature.charAt(position.index) != '>'); + + position.index++; + } + + private IllegalArgumentException couldNotParseSignature(String forElement, String signature) { + return new IllegalArgumentException("Could not parse class signature '" + signature + "' for " + forElement); + } + public FieldHolder parseField(FieldNode node) { FieldHolder field = new FieldHolder(referenceCache.getCached(node.name)); field.setType(referenceCache.getCached(ValueType.parse(node.desc))); field.setInitialValue(node.value); parseModifiers(node.access, field, DECL_FIELD); parseAnnotations(field.getAnnotations(), node.visibleAnnotations, node.invisibleAnnotations); + + if (node.signature != null) { + GenericValueType.ParsePosition position = new GenericValueType.ParsePosition(); + GenericValueType type = GenericValueType.parse(node.signature, position); + if (type == null || position.index < node.signature.length()) { + throw couldNotParseSignature("field '" + field.getReference() + "'", node.signature); + } + field.setGenericType(type); + } + return field; }