mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-03 05:44:10 -08:00
Support generic type signatures in IR
This commit is contained in:
parent
45d31da85c
commit
a9c4ab6aa4
|
@ -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<String> interfaces;
|
||||
Set<GenericValueType.Object> genericInterfaces;
|
||||
Map<MethodDescriptor, CachedMethod> methods;
|
||||
Map<String, CachedField> 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<String> getInterfaces() {
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GenericValueType.Object> getGenericInterfaces() {
|
||||
return genericInterfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodReader getMethod(MethodDescriptor method) {
|
||||
return methods.get(method);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<ProgramReader> program;
|
||||
Supplier<ProgramReader> 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();
|
||||
|
|
|
@ -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<String> interfaces = new LinkedHashSet<>();
|
||||
private Set<GenericValueType.Object> genericInterfaces = new LinkedHashSet<>();
|
||||
private Map<MethodDescriptor, MethodHolder> methods = new LinkedHashMap<>();
|
||||
private Map<String, FieldHolder> 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<String> getInterfaces() {
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GenericValueType.Object> getGenericInterfaces() {
|
||||
return genericInterfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHolder getMethod(MethodDescriptor method) {
|
||||
return methods.get(method);
|
||||
|
|
|
@ -19,10 +19,16 @@ import java.util.Collection;
|
|||
import java.util.Set;
|
||||
|
||||
public interface ClassReader extends ElementReader {
|
||||
GenericTypeParameter[] getGenericParameters();
|
||||
|
||||
String getParent();
|
||||
|
||||
Set<String> getInterfaces();
|
||||
|
||||
GenericValueType.Object getGenericParent();
|
||||
|
||||
Set<GenericValueType.Object> getGenericInterfaces();
|
||||
|
||||
MethodReader getMethod(MethodDescriptor method);
|
||||
|
||||
Collection<? extends MethodReader> getMethods();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.teavm.model;
|
|||
public interface FieldReader extends MemberReader {
|
||||
ValueType getType();
|
||||
|
||||
GenericValueType getGenericType();
|
||||
|
||||
Object getInitialValue();
|
||||
|
||||
FieldReference getReference();
|
||||
|
|
41
core/src/main/java/org/teavm/model/GenericTypeParameter.java
Normal file
41
core/src/main/java/org/teavm/model/GenericTypeParameter.java
Normal 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();
|
||||
}
|
||||
}
|
579
core/src/main/java/org/teavm/model/GenericValueType.java
Normal file
579
core/src/main/java/org/teavm/model/GenericValueType.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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<MethodHolder, Program> 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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -23,8 +23,8 @@ public class ReferenceCache {
|
|||
private Map<FieldReference, FieldReference> fieldRefenceCache = new HashMap<>();
|
||||
private Map<MethodDescriptor, MethodDescriptor> descriptorCache = 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, MethodReference> referenceParseCache = new HashMap<>();
|
||||
private Map<String, MethodDescriptor> descriptorParseCache = new HashMap<>();
|
||||
private Map<String, ValueType> 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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<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,
|
||||
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<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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user