Support generic type signatures in IR

This commit is contained in:
Alexey Andreev 2019-03-24 22:19:06 +03:00
parent 45d31da85c
commit a9c4ab6aa4
14 changed files with 1047 additions and 1 deletions

View File

@ -20,26 +20,46 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
import org.teavm.model.GenericTypeParameter;
import org.teavm.model.GenericValueType;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
class CachedClassReader extends CachedElement implements ClassReader { class CachedClassReader extends CachedElement implements ClassReader {
String parent; String parent;
GenericTypeParameter[] parameters;
GenericValueType.Object genericParent;
String owner; String owner;
Set<String> interfaces; Set<String> interfaces;
Set<GenericValueType.Object> genericInterfaces;
Map<MethodDescriptor, CachedMethod> methods; Map<MethodDescriptor, CachedMethod> methods;
Map<String, CachedField> fields; Map<String, CachedField> fields;
@Override
public GenericTypeParameter[] getGenericParameters() {
return parameters != null ? parameters.clone() : new GenericTypeParameter[0];
}
@Override @Override
public String getParent() { public String getParent() {
return parent; return parent;
} }
@Override
public GenericValueType.Object getGenericParent() {
return genericParent;
}
@Override @Override
public Set<String> getInterfaces() { public Set<String> getInterfaces() {
return interfaces; return interfaces;
} }
@Override
public Set<GenericValueType.Object> getGenericInterfaces() {
return genericInterfaces;
}
@Override @Override
public MethodReader getMethod(MethodDescriptor method) { public MethodReader getMethod(MethodDescriptor method) {
return methods.get(method); return methods.get(method);

View File

@ -17,10 +17,12 @@ package org.teavm.cache;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.GenericValueType;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
class CachedField extends CachedMember implements FieldReader { class CachedField extends CachedMember implements FieldReader {
ValueType type; ValueType type;
GenericValueType genericType;
Object initialValue; Object initialValue;
FieldReference reference; FieldReference reference;
@ -29,6 +31,11 @@ class CachedField extends CachedMember implements FieldReader {
return type; return type;
} }
@Override
public GenericValueType getGenericType() {
return genericType;
}
@Override @Override
public Object getInitialValue() { public Object getInitialValue() {
return initialValue; return initialValue;

View File

@ -19,6 +19,8 @@ import java.lang.ref.WeakReference;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.teavm.model.AnnotationContainerReader; import org.teavm.model.AnnotationContainerReader;
import org.teavm.model.AnnotationValue; import org.teavm.model.AnnotationValue;
import org.teavm.model.GenericTypeParameter;
import org.teavm.model.GenericValueType;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -27,16 +29,29 @@ import org.teavm.model.ValueType;
class CachedMethod extends CachedMember implements MethodReader { class CachedMethod extends CachedMember implements MethodReader {
MethodReference reference; MethodReference reference;
GenericTypeParameter[] typeParameters;
GenericValueType genericReturnType;
GenericValueType[] genericParameterTypes;
CachedAnnotations[] parameterAnnotations; CachedAnnotations[] parameterAnnotations;
AnnotationValue annotationDefault; AnnotationValue annotationDefault;
WeakReference<ProgramReader> program; WeakReference<ProgramReader> program;
Supplier<ProgramReader> programSupplier; Supplier<ProgramReader> programSupplier;
@Override
public GenericTypeParameter[] getTypeParameters() {
return typeParameters != null ? typeParameters.clone() : new GenericTypeParameter[0];
}
@Override @Override
public ValueType getResultType() { public ValueType getResultType() {
return reference.getReturnType(); return reference.getReturnType();
} }
@Override
public GenericValueType getGenericResultType() {
return genericReturnType;
}
@Override @Override
public int parameterCount() { public int parameterCount() {
return reference.parameterCount(); return reference.parameterCount();
@ -52,6 +67,16 @@ class CachedMethod extends CachedMember implements MethodReader {
return reference.parameterType(index); 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 @Override
public ValueType[] getParameterTypes() { public ValueType[] getParameterTypes() {
return reference.getParameterTypes(); return reference.getParameterTypes();

View File

@ -18,8 +18,11 @@ package org.teavm.model;
import java.util.*; import java.util.*;
public class ClassHolder extends ElementHolder implements ClassReader { public class ClassHolder extends ElementHolder implements ClassReader {
private GenericTypeParameter[] genericParameters;
private String parent = Object.class.getName(); private String parent = Object.class.getName();
private GenericValueType.Object genericParent;
private Set<String> interfaces = new LinkedHashSet<>(); private Set<String> interfaces = new LinkedHashSet<>();
private Set<GenericValueType.Object> genericInterfaces = new LinkedHashSet<>();
private Map<MethodDescriptor, MethodHolder> methods = new LinkedHashMap<>(); private Map<MethodDescriptor, MethodHolder> methods = new LinkedHashMap<>();
private Map<String, FieldHolder> fields = new LinkedHashMap<>(); private Map<String, FieldHolder> fields = new LinkedHashMap<>();
private String ownerName; private String ownerName;
@ -28,6 +31,15 @@ public class ClassHolder extends ElementHolder implements ClassReader {
super(name); 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 @Override
public String getParent() { public String getParent() {
return parent; return parent;
@ -37,11 +49,25 @@ public class ClassHolder extends ElementHolder implements ClassReader {
this.parent = parent; this.parent = parent;
} }
@Override
public GenericValueType.Object getGenericParent() {
return genericParent;
}
public void setGenericParent(GenericValueType.Object genericParent) {
this.genericParent = genericParent;
}
@Override @Override
public Set<String> getInterfaces() { public Set<String> getInterfaces() {
return interfaces; return interfaces;
} }
@Override
public Set<GenericValueType.Object> getGenericInterfaces() {
return genericInterfaces;
}
@Override @Override
public MethodHolder getMethod(MethodDescriptor method) { public MethodHolder getMethod(MethodDescriptor method) {
return methods.get(method); return methods.get(method);

View File

@ -19,10 +19,16 @@ import java.util.Collection;
import java.util.Set; import java.util.Set;
public interface ClassReader extends ElementReader { public interface ClassReader extends ElementReader {
GenericTypeParameter[] getGenericParameters();
String getParent(); String getParent();
Set<String> getInterfaces(); Set<String> getInterfaces();
GenericValueType.Object getGenericParent();
Set<GenericValueType.Object> getGenericInterfaces();
MethodReader getMethod(MethodDescriptor method); MethodReader getMethod(MethodDescriptor method);
Collection<? extends MethodReader> getMethods(); Collection<? extends MethodReader> getMethods();

View File

@ -17,6 +17,7 @@ package org.teavm.model;
public class FieldHolder extends MemberHolder implements FieldReader { public class FieldHolder extends MemberHolder implements FieldReader {
private ValueType type; private ValueType type;
private GenericValueType genericType;
private Object initialValue; private Object initialValue;
private ClassHolder owner; private ClassHolder owner;
private FieldReference reference; private FieldReference reference;
@ -30,6 +31,15 @@ public class FieldHolder extends MemberHolder implements FieldReader {
return type; return type;
} }
@Override
public GenericValueType getGenericType() {
return genericType;
}
public void setGenericType(GenericValueType genericType) {
this.genericType = genericType;
}
public void setType(ValueType type) { public void setType(ValueType type) {
this.type = type; this.type = type;
} }

View File

@ -18,6 +18,8 @@ package org.teavm.model;
public interface FieldReader extends MemberReader { public interface FieldReader extends MemberReader {
ValueType getType(); ValueType getType();
GenericValueType getGenericType();
Object getInitialValue(); Object getInitialValue();
FieldReference getReference(); FieldReference getReference();

View File

@ -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();
}
}

View File

@ -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<Argument> 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;
}
}

View File

@ -15,10 +15,14 @@
*/ */
package org.teavm.model; package org.teavm.model;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
public class MethodHolder extends MemberHolder implements MethodReader { public class MethodHolder extends MemberHolder implements MethodReader {
private MethodDescriptor descriptor; private MethodDescriptor descriptor;
private GenericTypeParameter[] typeParameters;
private GenericValueType genericReturnType;
private GenericValueType[] genericParameterTypes;
private ClassHolder owner; private ClassHolder owner;
private Program program; private Program program;
private Function<MethodHolder, Program> programSupplier; private Function<MethodHolder, Program> programSupplier;
@ -44,6 +48,40 @@ public class MethodHolder extends MemberHolder implements MethodReader {
return descriptor.getResultType(); 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 @Override
public int parameterCount() { public int parameterCount() {
return descriptor.parameterCount(); return descriptor.parameterCount();

View File

@ -18,12 +18,20 @@ package org.teavm.model;
public interface MethodReader extends MemberReader { public interface MethodReader extends MemberReader {
ValueType getResultType(); ValueType getResultType();
GenericTypeParameter[] getTypeParameters();
GenericValueType getGenericResultType();
int parameterCount(); int parameterCount();
ValueType[] getSignature(); ValueType[] getSignature();
ValueType parameterType(int index); ValueType parameterType(int index);
int genericParameterCount();
GenericValueType genericParameterType(int index);
ValueType[] getParameterTypes(); ValueType[] getParameterTypes();
AnnotationContainerReader parameterAnnotation(int index); AnnotationContainerReader parameterAnnotation(int index);

View File

@ -23,8 +23,8 @@ public class ReferenceCache {
private Map<FieldReference, FieldReference> fieldRefenceCache = new HashMap<>(); private Map<FieldReference, FieldReference> fieldRefenceCache = new HashMap<>();
private Map<MethodDescriptor, MethodDescriptor> descriptorCache = new HashMap<>(); private Map<MethodDescriptor, MethodDescriptor> descriptorCache = new HashMap<>();
private Map<ValueType, ValueType> valueTypeCache = new HashMap<>(); private Map<ValueType, ValueType> valueTypeCache = new HashMap<>();
private Map<GenericValueType, GenericValueType> genericValueTypeCache = new HashMap<>();
private Map<String, String> stringCache = new HashMap<>(); private Map<String, String> stringCache = new HashMap<>();
private Map<String, MethodReference> referenceParseCache = new HashMap<>();
private Map<String, MethodDescriptor> descriptorParseCache = new HashMap<>(); private Map<String, MethodDescriptor> descriptorParseCache = new HashMap<>();
private Map<String, ValueType> valueTypeParseCache = new HashMap<>(); private Map<String, ValueType> valueTypeParseCache = new HashMap<>();
@ -103,6 +103,67 @@ public class ReferenceCache {
return result; 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) { public String getCached(String s) {
String result = stringCache.get(s); String result = stringCache.get(s);
if (result == null) { if (result == null) {

View File

@ -28,6 +28,8 @@ import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.FieldHolder; import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.GenericTypeParameter;
import org.teavm.model.GenericValueType;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.InvokeDynamicInstruction; import org.teavm.model.InvokeDynamicInstruction;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
@ -94,6 +96,17 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
renamedCls.getInterfaces().add(mappedIfaceName); 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; return renamedCls;
} }
@ -114,9 +127,42 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
renamedMethod.setProgram(method.getProgram()); renamedMethod.setProgram(method.getProgram());
rename(method.getAnnotations(), renamedMethod.getAnnotations()); rename(method.getAnnotations(), renamedMethod.getAnnotations());
rename(renamedMethod.getProgram()); 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; 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) { public FieldHolder rename(FieldHolder field) {
FieldHolder renamedField = new FieldHolder(field.getName()); FieldHolder renamedField = new FieldHolder(field.getName());
renamedField.getModifiers().addAll(field.getModifiers()); renamedField.getModifiers().addAll(field.getModifiers());
@ -124,6 +170,12 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
renamedField.setType(rename(field.getType())); renamedField.setType(rename(field.getType()));
renamedField.setInitialValue(field.getInitialValue()); renamedField.setInitialValue(field.getInitialValue());
rename(field.getAnnotations(), renamedField.getAnnotations()); rename(field.getAnnotations(), renamedField.getAnnotations());
GenericValueType genericType = field.getGenericType();
if (genericType != null) {
renamedField.setGenericType(rename(genericType));
}
return renamedField; 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) { private ValueType[] rename(ValueType[] types) {
return Arrays.stream(types).map(this::rename).toArray(ValueType[]::new); return Arrays.stream(types).map(this::rename).toArray(ValueType[]::new);
} }

View File

@ -42,6 +42,8 @@ import org.teavm.model.ElementHolder;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder; import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.GenericTypeParameter;
import org.teavm.model.GenericValueType;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
@ -101,9 +103,49 @@ public class Parser {
node.visibleParameterAnnotations != null ? node.visibleParameterAnnotations[i] : null, node.visibleParameterAnnotations != null ? node.visibleParameterAnnotations[i] : null,
node.invisibleParameterAnnotations != null ? node.invisibleParameterAnnotations[i] : null); node.invisibleParameterAnnotations != null ? node.invisibleParameterAnnotations[i] : null);
} }
if (node.signature != null) {
parseMethodGenericSignature(node.signature, method);
}
return method; return method;
} }
private void parseMethodGenericSignature(String signature, MethodHolder method) {
GenericValueType.ParsePosition position = new GenericValueType.ParsePosition();
List<GenericTypeParameter> typeParameters = new ArrayList<>();
String elementName = "method '" + method.getDescriptor() + "'";
if (signature.charAt(position.index) == '<') {
parseTypeParameters(signature, typeParameters, position, elementName);
}
List<GenericValueType> 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, private static void applyDebugNames(Program program, PhiUpdater phiUpdater, ProgramParser parser,
Variable[] argumentMapping) { Variable[] argumentMapping) {
if (program.basicBlockCount() == 0) { if (program.basicBlockCount() == 0) {
@ -242,6 +284,11 @@ public class Parser {
cls.getInterfaces().add(referenceCache.getCached(iface.replace('/', '.'))); cls.getInterfaces().add(referenceCache.getCached(iface.replace('/', '.')));
} }
} }
if (node.signature != null) {
parseSignature(cls, node.signature);
}
for (Object obj : node.fields) { for (Object obj : node.fields) {
FieldNode fieldNode = (FieldNode) obj; FieldNode fieldNode = (FieldNode) obj;
FieldHolder field = parseField(fieldNode); FieldHolder field = parseField(fieldNode);
@ -267,12 +314,97 @@ public class Parser {
return cls; return cls;
} }
private void parseSignature(ClassHolder cls, String signature) {
GenericValueType.ParsePosition position = new GenericValueType.ParsePosition();
List<GenericTypeParameter> 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<GenericValueType.Object> 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<GenericTypeParameter> 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<GenericValueType.Reference> 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) { public FieldHolder parseField(FieldNode node) {
FieldHolder field = new FieldHolder(referenceCache.getCached(node.name)); FieldHolder field = new FieldHolder(referenceCache.getCached(node.name));
field.setType(referenceCache.getCached(ValueType.parse(node.desc))); field.setType(referenceCache.getCached(ValueType.parse(node.desc)));
field.setInitialValue(node.value); field.setInitialValue(node.value);
parseModifiers(node.access, field, DECL_FIELD); parseModifiers(node.access, field, DECL_FIELD);
parseAnnotations(field.getAnnotations(), node.visibleAnnotations, node.invisibleAnnotations); 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; return field;
} }