Skip to content

Commit f5407c5

Browse files
authored
polish the TypeInfo system
* always use method reference as test action in TypeInfoTest * remove `TypeInfo.collectComponentClass`: it should be replaced by a more general solution like `TypeInfo.visitComponents` * remove `NoCacheFactory`: Using this factory to resolve self-referencing type variables (`T extends Enum<T>`) will cause inf loop * support `Outer<T1>.Inner` style parameterized type * rewrite `TypeInfo` formatting mechanism - support formatting VariableTypeInfo to `K extends SingleBound` - cleaner overall design * doc for `TypeFormatContext` and better naming of `ClassSignatureFormatContext`
1 parent d2e0bc4 commit f5407c5

21 files changed

+510
-284
lines changed

rhino/build.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,5 @@ decycle {
102102
// currently accepted cycles (may be resolved, when it is clear, how to separate the liveconnect stuff)
103103
ignoring from: "org.mozilla.javascript.lc.type.*", to: "org.mozilla.javascript.(Scriptable|ScriptableObject)"
104104
ignoring from: "org.mozilla.javascript.lc.type.TypeInfoFactory", to: "org.mozilla.javascript.lc.type.impl.factory.*"
105-
ignoring from: "org.mozilla.javascript.lc.type.TypeInfo", to: "org.mozilla.javascript.lc.type.impl.*"
106-
ignoring from: "org.mozilla.javascript.lc.type.TypeFormatContext", to: "org.mozilla.javascript.lc.type.impl.ClassSignatureFormatContext"
107-
105+
ignoring from: "org.mozilla.javascript.lc.type.*", to: "org.mozilla.javascript.lc.type.impl.*"
108106
}

rhino/src/main/java/org/mozilla/javascript/lc/type/ParameterizedTypeInfo.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ default TypeInfo param(int index) {
3131
return index >= 0 && index < params.size() ? params.get(index) : TypeInfo.NONE;
3232
}
3333

34+
/**
35+
* @see ParameterizedType#getOwnerType()
36+
*/
37+
TypeInfo ownerType();
38+
3439
/**
3540
* Extract consolidation mapping based on {@link #params()} and {@link
3641
* Class#getTypeParameters()}
@@ -49,20 +54,26 @@ default Map<VariableTypeInfo, TypeInfo> extractConsolidationMapping(TypeInfoFact
4954
String.format(
5055
"Expecting %s type params for class '%s', but got %s",
5156
len, this.asClass().getName(), actualParams.size()));
52-
} else if (len == 0) {
53-
throw new IllegalStateException(
54-
String.format(
55-
"Base class '%s' is not a generic class", this.asClass().getName()));
5657
}
5758

58-
if (len == 1) {
59+
Map<VariableTypeInfo, TypeInfo> ownerTypeMapping;
60+
var ownerType = this.ownerType();
61+
if (ownerType instanceof ParameterizedTypeInfo) {
62+
ownerTypeMapping =
63+
((ParameterizedTypeInfo) ownerType).extractConsolidationMapping(factory);
64+
} else {
65+
ownerTypeMapping = Map.of();
66+
}
67+
68+
if (len == 1 && ownerTypeMapping.isEmpty()) {
5969
return Map.of((VariableTypeInfo) factory.create(typeVariables[0]), actualParams.get(0));
6070
}
6171

6272
var mapping = new HashMap<VariableTypeInfo, TypeInfo>();
6373
for (int i = 0; i < len; i++) {
6474
mapping.put((VariableTypeInfo) factory.create(typeVariables[i]), actualParams.get(i));
6575
}
76+
mapping.putAll(ownerTypeMapping);
6677
return mapping;
6778
}
6879

rhino/src/main/java/org/mozilla/javascript/lc/type/TypeFormatContext.java

Lines changed: 114 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,141 @@
11
package org.mozilla.javascript.lc.type;
22

3-
import org.mozilla.javascript.lc.type.impl.ClassSignatureFormatContext;
3+
import org.mozilla.javascript.lc.type.impl.ClassNameFormatContext;
44

55
/**
66
* @author ZZZank
77
*/
88
public interface TypeFormatContext {
9+
/**
10+
* Full feature formatting context
11+
*
12+
* <table>
13+
* <tr><th>Type</th><th>Full representation</th><th>Representation using this context</th></tr>
14+
* <tr><td>Class</td><td>java.lang.String</td><td>java.lang.String</td></tr>
15+
* <tr><td>Primitive Class</td><td>char</td><td>char</td></tr>
16+
* <tr><td>Nested Class</td><td>Entry (in java.util.Map)</td><td>java.util.Map$Entry</td></tr>
17+
* <tr><td>Array</td><td>java.lang.String[]</td><td>java.lang.String[]</td></tr>
18+
* <tr><td>Primitive Array</td><td>char[]</td><td>char[]</td></tr>
19+
* <tr><td>Parameterized</td><td>java.lang.List&lt;java.lang.String&gt;</td><td>java.util.List&lt;java.lang.String&gt;</td></tr>
20+
* <tr><td>Variable</td><td>T extends java.lang.String</td><td>T extends java.lang.String</td></tr>
21+
* <tr><td>{@link TypeInfo#NONE}</td><td>(No standard representation)</td><td>?</td></tr>
22+
* </table>
23+
*/
924
TypeFormatContext DEFAULT = Class::getName;
25+
26+
/**
27+
* Full feature formatting context with class name simplified
28+
*
29+
* <table>
30+
* <tr><th>Type</th><th>Full representation</th><th>Representation using this context</th></tr>
31+
* <tr><td>Class</td><td>java.lang.String</td><td>String</td></tr>
32+
* <tr><td>Primitive Class</td><td>char</td><td>char</td></tr>
33+
* <tr><td>Nested Class</td><td>Entry (in java.util.Map)</td><td>Entry</td></tr>
34+
* <tr><td>Array</td><td>java.lang.String[]</td><td>String[]</td></tr>
35+
* <tr><td>Primitive Array</td><td>char[]</td><td>char[]</td></tr>
36+
* <tr><td>Parameterized</td><td>java.lang.List&lt;java.lang.String&gt;</td><td>List&lt;String&gt;</td></tr>
37+
* <tr><td>Variable</td><td>T extends java.lang.String</td><td>T extends String</td></tr>
38+
* <tr><td>{@link TypeInfo#NONE}</td><td>(No standard representation)</td><td>?</td></tr>
39+
* </table>
40+
*
41+
* @see Class#getSimpleName()
42+
*/
1043
TypeFormatContext SIMPLE = Class::getSimpleName;
11-
TypeFormatContext CLASS_SIG = new ClassSignatureFormatContext();
44+
45+
/**
46+
* Formatting context that formats every type as the result of {@code type.asClass().getName()}
47+
*
48+
* <table>
49+
* <tr><th>Type</th><th>Full representation</th><th>Representation using this context</th></tr>
50+
* <tr><td>Class</td><td>java.lang.String</td><td>java.lang.String</td></tr>
51+
* <tr><td>Primitive Class</td><td>char</td><td>char</td></tr>
52+
* <tr><td>Nested Class</td><td>Entry (in java.util.Map)</td><td>java.util.Map$Entry</td></tr>
53+
* <tr><td>Array</td><td>java.lang.String[]</td><td>[Ljava.lang.String;</td></tr>
54+
* <tr><td>Primitive Array</td><td>char[]</td><td>[C</td></tr>
55+
* <tr><td>Parameterized</td><td>java.lang.List&lt;java.lang.String&gt;</td><td>java.util.List</td></tr>
56+
* <tr><td>Variable</td><td>T extends java.lang.String</td><td>java.lang.String</td></tr>
57+
* <tr><td>{@link TypeInfo#NONE}</td><td>(No standard representation)</td><td>java.lang.Object</td></tr>
58+
* </table>
59+
*
60+
* @see Class#getName()
61+
*/
62+
TypeFormatContext CLASS_NAME = new ClassNameFormatContext();
1263

1364
String getClassName(Class<?> c);
1465

15-
default void appendSpace(StringBuilder builder) {
16-
builder.append(' ');
66+
/**
67+
* Format a type and push the result to provided {@link StringBuilder}.
68+
*
69+
* @implNote Implementations are encouraged to override {@link #append(StringBuilder, TypeInfo,
70+
* boolean)} , instead of this method
71+
* @param builder Formatted string of {@code type} will be pushed to this builder
72+
* @param type The type to be formatted
73+
*/
74+
default void append(StringBuilder builder, TypeInfo type) {
75+
append(builder, type, true);
1776
}
1877

19-
default void formatArray(StringBuilder builder, TypeInfo arrayType) {
20-
arrayType.getComponentType().append(this, builder);
78+
/**
79+
* Format a type and push the result to provided {@link StringBuilder}.
80+
*
81+
* @param builder Formatted string of {@code type} will be pushed to this builder
82+
* @param type The type to be formatted
83+
* @param declaring {@code true} if the context should format the result as if the type is being
84+
* declared instead of being used. For example, in {@code E extends Enum<T>}, this param is
85+
* {@code true} for the first E, and {@code false} for the second, nested E
86+
*/
87+
default void append(StringBuilder builder, TypeInfo type, boolean declaring) {
88+
if (type == TypeInfo.NONE) {
89+
builder.append(getFormattedNone());
90+
} else if (type.isArray()) {
91+
appendArray(builder, type);
92+
} else if (type instanceof VariableTypeInfo) {
93+
appendVariable(builder, (VariableTypeInfo) type, declaring);
94+
} else if (type instanceof ParameterizedTypeInfo) {
95+
appendParameterized(builder, (ParameterizedTypeInfo) type);
96+
} else {
97+
builder.append(type.toString(this));
98+
}
99+
}
100+
101+
/**
102+
* @param type {@link TypeInfo#isArray()} will always be {@code true} for this object
103+
*/
104+
default void appendArray(StringBuilder builder, TypeInfo type) {
105+
append(builder, type.getComponentType(), false);
21106
builder.append('[').append(']');
22107
}
23108

24-
default void formatParameterized(StringBuilder builder, ParameterizedTypeInfo type) {
25-
type.rawType().append(this, builder);
109+
default void appendParameterized(StringBuilder builder, ParameterizedTypeInfo type) {
110+
append(builder, type.rawType(), false);
26111

27-
builder.append('<');
28112
var iterator = type.params().iterator();
29113
if (iterator.hasNext()) {
30-
iterator.next().append(this, builder);
114+
builder.append('<');
115+
append(builder, iterator.next(), false);
31116
while (iterator.hasNext()) {
32117
builder.append(',');
33-
appendSpace(builder);
34-
iterator.next().append(this, builder);
118+
builder.append(' ');
119+
append(builder, iterator.next(), false);
120+
}
121+
builder.append('>');
122+
}
123+
}
124+
125+
/**
126+
* @param declaring {@code true} if the context should format the result as if the type is being
127+
* declared instead of being used. For example, in {@code E extends Enum<T>}, {@code
128+
* declaring} is {@code true} for the first E, and {@code false} for the second, nested E
129+
*/
130+
default void appendVariable(StringBuilder builder, VariableTypeInfo type, boolean declaring) {
131+
builder.append(type.name());
132+
if (declaring) {
133+
var mainBound = type.mainBound();
134+
if (!mainBound.isObjectExact()) {
135+
builder.append(" extends ");
136+
append(builder, mainBound, false);
35137
}
36138
}
37-
builder.append('>');
38139
}
39140

40141
/**

rhino/src/main/java/org/mozilla/javascript/lc/type/TypeInfo.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,12 @@ default boolean shouldReplace() {
139139
}
140140

141141
/**
142-
* @see #append(TypeFormatContext, StringBuilder)
142+
* @see #toString(TypeFormatContext)
143143
*/
144144
@Override
145145
String toString();
146146

147-
void append(TypeFormatContext ctx, StringBuilder builder);
147+
String toString(TypeFormatContext ctx);
148148

149149
/**
150150
* @see Class#getComponentType()
@@ -277,10 +277,6 @@ default boolean isObjectExact() {
277277
return false;
278278
}
279279

280-
default void collectComponentClass(Consumer<Class<?>> collector) {
281-
collector.accept(asClass());
282-
}
283-
284280
/**
285281
* @see Class#isArray()
286282
*/

rhino/src/main/java/org/mozilla/javascript/lc/type/TypeInfoFactory.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.function.Supplier;
2424
import org.mozilla.javascript.Scriptable;
2525
import org.mozilla.javascript.ScriptableObject;
26-
import org.mozilla.javascript.lc.type.impl.factory.NoCacheFactory;
2726
import org.mozilla.javascript.lc.type.impl.factory.WeakReferenceFactory;
2827

2928
/**
@@ -62,14 +61,6 @@ private Object readResolve() {
6261
}
6362
};
6463

65-
/**
66-
* TypeInfoFactory used by very few actions with the intention of not caching any used types
67-
*
68-
* <p>This factory does not cache {@link TypeInfo}. If the same type is passed to this factory
69-
* multiple times, the return result may or may not be the exact same object
70-
*/
71-
TypeInfoFactory NO_CACHE = NoCacheFactory.INSTANCE;
72-
7364
TypeInfo[] EMPTY_ARRAY = new TypeInfo[0];
7465

7566
/// creating types

rhino/src/main/java/org/mozilla/javascript/lc/type/impl/ArrayTypeInfo.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
import java.util.Map;
44
import java.util.Objects;
5-
import java.util.function.Consumer;
65
import org.mozilla.javascript.FunctionObject;
7-
import org.mozilla.javascript.lc.type.TypeFormatContext;
86
import org.mozilla.javascript.lc.type.TypeInfo;
97
import org.mozilla.javascript.lc.type.VariableTypeInfo;
108

@@ -43,21 +41,11 @@ public int hashCode() {
4341
return component.hashCode() + 1;
4442
}
4543

46-
@Override
47-
public void append(TypeFormatContext ctx, StringBuilder builder) {
48-
ctx.formatArray(builder, this);
49-
}
50-
5144
@Override
5245
public TypeInfo getComponentType() {
5346
return component;
5447
}
5548

56-
@Override
57-
public void collectComponentClass(Consumer<Class<?>> collector) {
58-
component.collectComponentClass(collector);
59-
}
60-
6149
@Override
6250
public boolean isArray() {
6351
return true;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.mozilla.javascript.lc.type.impl;
2+
3+
import org.mozilla.javascript.lc.type.ParameterizedTypeInfo;
4+
import org.mozilla.javascript.lc.type.TypeFormatContext;
5+
import org.mozilla.javascript.lc.type.TypeInfo;
6+
import org.mozilla.javascript.lc.type.VariableTypeInfo;
7+
8+
/**
9+
* @author ZZZank
10+
*/
11+
public class ClassNameFormatContext implements TypeFormatContext {
12+
@Override
13+
public String getClassName(Class<?> c) {
14+
return c.getName();
15+
}
16+
17+
@Override
18+
public void append(StringBuilder builder, TypeInfo type) {
19+
builder.append(type.asClass().getName());
20+
}
21+
22+
@Override
23+
public void append(StringBuilder builder, TypeInfo type, boolean declaring) {
24+
builder.append(type.asClass().getName());
25+
}
26+
27+
@Override
28+
public void appendParameterized(StringBuilder builder, ParameterizedTypeInfo type) {
29+
append(builder, type.rawType());
30+
}
31+
32+
@Override
33+
public void appendVariable(StringBuilder builder, VariableTypeInfo type, boolean declaring) {
34+
append(builder, type.mainBound());
35+
}
36+
37+
@Override
38+
public void appendArray(StringBuilder builder, TypeInfo type) {
39+
builder.append(type.asClass().getName());
40+
}
41+
42+
@Override
43+
public String getFormattedNone() {
44+
return Object.class.getName();
45+
}
46+
}

rhino/src/main/java/org/mozilla/javascript/lc/type/impl/ClassSignatureFormatContext.java

Lines changed: 0 additions & 27 deletions
This file was deleted.

rhino/src/main/java/org/mozilla/javascript/lc/type/impl/ClassTypeInfo.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public boolean equals(Object o) {
3030
}
3131

3232
@Override
33-
public void append(TypeFormatContext ctx, StringBuilder builder) {
34-
builder.append(ctx.getClassName(this.type));
33+
public String toString(TypeFormatContext ctx) {
34+
return ctx.getClassName(this.type);
3535
}
3636
}

rhino/src/main/java/org/mozilla/javascript/lc/type/impl/NoTypeInfo.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.mozilla.javascript.lc.type.impl;
22

3-
import java.util.function.Consumer;
43
import org.mozilla.javascript.lc.type.TypeFormatContext;
54
import org.mozilla.javascript.lc.type.TypeInfo;
65

@@ -28,13 +27,10 @@ public String toString() {
2827
}
2928

3029
@Override
31-
public void append(TypeFormatContext ctx, StringBuilder builder) {
32-
builder.append(ctx.getFormattedNone());
30+
public String toString(TypeFormatContext ctx) {
31+
return ctx.getFormattedNone();
3332
}
3433

35-
@Override
36-
public void collectComponentClass(Consumer<Class<?>> collector) {}
37-
3834
/** {@link Object} class is assignable from any class */
3935
@Override
4036
public boolean isAssignableFrom(TypeInfo another) {

0 commit comments

Comments
 (0)