Skip to content

Commit 394a1dd

Browse files
committed
Painless: Replace Painless Type with Java Class during Casts (#27847)
This is the first step in a series to replace Painless Type with Java Class for any casting done during compilation. There should be no behavioural change.
1 parent f31fcb1 commit 394a1dd

File tree

6 files changed

+597
-463
lines changed

6 files changed

+597
-463
lines changed

modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java

Lines changed: 424 additions & 411 deletions
Large diffs are not rendered by default.

modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java

Lines changed: 107 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.lang.invoke.MethodType;
2828
import java.lang.reflect.Modifier;
2929
import java.util.ArrayList;
30+
import java.util.Arrays;
3031
import java.util.Collection;
3132
import java.util.Collections;
3233
import java.util.HashMap;
@@ -76,6 +77,13 @@ public final class Definition {
7677
public final Type ArrayListType;
7778
public final Type HashMapType;
7879

80+
/** Marker class for def type to be used during type analysis. */
81+
public static final class def {
82+
private def() {
83+
84+
}
85+
}
86+
7987
public static final class Type {
8088
public final String name;
8189
public final int dimensions;
@@ -365,40 +373,41 @@ public Method getFunctionalMethod() {
365373
}
366374

367375
public static class Cast {
376+
368377
/** Create a standard cast with no boxing/unboxing. */
369-
public static Cast standard(Type from, Type to, boolean explicit) {
378+
public static Cast standard(Class<?> from, Class<?> to, boolean explicit) {
370379
return new Cast(from, to, explicit, null, null, null, null);
371380
}
372381

373382
/** Create a cast where the from type will be unboxed, and then the cast will be performed. */
374-
public static Cast unboxFrom(Type from, Type to, boolean explicit, Type unboxFrom) {
383+
public static Cast unboxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom) {
375384
return new Cast(from, to, explicit, unboxFrom, null, null, null);
376385
}
377386

378387
/** Create a cast where the to type will be unboxed, and then the cast will be performed. */
379-
public static Cast unboxTo(Type from, Type to, boolean explicit, Type unboxTo) {
388+
public static Cast unboxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxTo) {
380389
return new Cast(from, to, explicit, null, unboxTo, null, null);
381390
}
382391

383392
/** Create a cast where the from type will be boxed, and then the cast will be performed. */
384-
public static Cast boxFrom(Type from, Type to, boolean explicit, Type boxFrom) {
393+
public static Cast boxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> boxFrom) {
385394
return new Cast(from, to, explicit, null, null, boxFrom, null);
386395
}
387396

388397
/** Create a cast where the to type will be boxed, and then the cast will be performed. */
389-
public static Cast boxTo(Type from, Type to, boolean explicit, Type boxTo) {
398+
public static Cast boxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> boxTo) {
390399
return new Cast(from, to, explicit, null, null, null, boxTo);
391400
}
392401

393-
public final Type from;
394-
public final Type to;
402+
public final Class<?> from;
403+
public final Class<?> to;
395404
public final boolean explicit;
396-
public final Type unboxFrom;
397-
public final Type unboxTo;
398-
public final Type boxFrom;
399-
public final Type boxTo;
405+
public final Class<?> unboxFrom;
406+
public final Class<?> unboxTo;
407+
public final Class<?> boxFrom;
408+
public final Class<?> boxTo;
400409

401-
private Cast(Type from, Type to, boolean explicit, Type unboxFrom, Type unboxTo, Type boxFrom, Type boxTo) {
410+
private Cast(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom, Class<?> unboxTo, Class<?> boxFrom, Class<?> boxTo) {
402411
this.from = from;
403412
this.to = to;
404413
this.explicit = explicit;
@@ -499,6 +508,92 @@ public static boolean isConstantType(Type constant) {
499508
constant.clazz == String.class;
500509
}
501510

511+
public static Class<?> ObjectClassTodefClass(Class<?> clazz) {
512+
if (clazz.isArray()) {
513+
Class<?> component = clazz.getComponentType();
514+
int dimensions = 1;
515+
516+
while (component.isArray()) {
517+
component = component.getComponentType();
518+
++dimensions;
519+
}
520+
521+
if (component == Object.class) {
522+
char[] braces = new char[dimensions];
523+
Arrays.fill(braces, '[');
524+
525+
String descriptor = new String(braces) + org.objectweb.asm.Type.getType(def.class).getDescriptor();
526+
org.objectweb.asm.Type type = org.objectweb.asm.Type.getType(descriptor);
527+
528+
try {
529+
return Class.forName(type.getInternalName().replace('/', '.'));
530+
} catch (ClassNotFoundException exception) {
531+
throw new IllegalStateException("internal error", exception);
532+
}
533+
}
534+
} else if (clazz == Object.class) {
535+
return def.class;
536+
}
537+
538+
return clazz;
539+
}
540+
541+
public static Class<?> defClassToObjectClass(Class<?> clazz) {
542+
if (clazz.isArray()) {
543+
Class<?> component = clazz.getComponentType();
544+
int dimensions = 1;
545+
546+
while (component.isArray()) {
547+
component = component.getComponentType();
548+
++dimensions;
549+
}
550+
551+
if (component == def.class) {
552+
char[] braces = new char[dimensions];
553+
Arrays.fill(braces, '[');
554+
555+
String descriptor = new String(braces) + org.objectweb.asm.Type.getType(Object.class).getDescriptor();
556+
org.objectweb.asm.Type type = org.objectweb.asm.Type.getType(descriptor);
557+
558+
try {
559+
return Class.forName(type.getInternalName().replace('/', '.'));
560+
} catch (ClassNotFoundException exception) {
561+
throw new IllegalStateException("internal error", exception);
562+
}
563+
}
564+
} else if (clazz == def.class) {
565+
return Object.class;
566+
}
567+
568+
return clazz;
569+
}
570+
571+
public static String ClassToName(Class<?> clazz) {
572+
if (clazz.isArray()) {
573+
Class<?> component = clazz.getComponentType();
574+
int dimensions = 1;
575+
576+
while (component.isArray()) {
577+
component = component.getComponentType();
578+
++dimensions;
579+
}
580+
581+
if (component == def.class) {
582+
StringBuilder builder = new StringBuilder("def");
583+
584+
for (int dimension = 0; dimension < dimensions; dimensions++) {
585+
builder.append("[]");
586+
}
587+
588+
return builder.toString();
589+
}
590+
} else if (clazz == def.class) {
591+
return "def";
592+
}
593+
594+
return clazz.getCanonicalName();
595+
}
596+
502597
public RuntimeClass getRuntimeClass(Class<?> clazz) {
503598
return runtimeMap.get(clazz);
504599
}

modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@
2020
package org.elasticsearch.painless;
2121

2222
import org.elasticsearch.painless.Definition.Cast;
23-
import org.elasticsearch.painless.Definition.Type;
23+
import org.elasticsearch.painless.Definition.def;
2424
import org.objectweb.asm.ClassVisitor;
2525
import org.objectweb.asm.Label;
2626
import org.objectweb.asm.Opcodes;
27+
import org.objectweb.asm.Type;
2728
import org.objectweb.asm.commons.GeneratorAdapter;
2829
import org.objectweb.asm.commons.Method;
2930

3031
import java.util.ArrayDeque;
3132
import java.util.ArrayList;
33+
import java.util.Arrays;
3234
import java.util.BitSet;
3335
import java.util.Deque;
3436
import java.util.List;
@@ -128,68 +130,68 @@ public void writeLoopCounter(int slot, int count, Location location) {
128130
mark(end);
129131
}
130132

131-
public void writeCast(final Cast cast) {
133+
public void writeCast(Cast cast) {
132134
if (cast != null) {
133-
if (cast.from.clazz == char.class && cast.to.clazz == String.class) {
135+
if (cast.from == char.class && cast.to == String.class) {
134136
invokeStatic(UTILITY_TYPE, CHAR_TO_STRING);
135-
} else if (cast.from.clazz == String.class && cast.to.clazz == char.class) {
137+
} else if (cast.from == String.class && cast.to == char.class) {
136138
invokeStatic(UTILITY_TYPE, STRING_TO_CHAR);
137139
} else if (cast.unboxFrom != null) {
138-
unbox(cast.unboxFrom.type);
140+
unbox(getType(cast.unboxFrom));
139141
writeCast(cast.from, cast.to);
140142
} else if (cast.unboxTo != null) {
141-
if (cast.from.dynamic) {
143+
if (cast.from == def.class) {
142144
if (cast.explicit) {
143-
if (cast.to.clazz == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
144-
else if (cast.to.clazz == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
145-
else if (cast.to.clazz == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
146-
else if (cast.to.clazz == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
147-
else if (cast.to.clazz == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
148-
else if (cast.to.clazz == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
149-
else if (cast.to.clazz == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
150-
else if (cast.to.clazz == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT);
145+
if (cast.to == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
146+
else if (cast.to == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
147+
else if (cast.to == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
148+
else if (cast.to == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
149+
else if (cast.to == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
150+
else if (cast.to == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
151+
else if (cast.to == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
152+
else if (cast.to == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT);
151153
else {
152154
throw new IllegalStateException("Illegal tree structure.");
153155
}
154156
} else {
155-
if (cast.to.clazz == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
156-
else if (cast.to.clazz == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
157-
else if (cast.to.clazz == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
158-
else if (cast.to.clazz == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
159-
else if (cast.to.clazz == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
160-
else if (cast.to.clazz == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
161-
else if (cast.to.clazz == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
162-
else if (cast.to.clazz == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT);
157+
if (cast.to == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
158+
else if (cast.to == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
159+
else if (cast.to == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
160+
else if (cast.to == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
161+
else if (cast.to == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
162+
else if (cast.to == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
163+
else if (cast.to == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
164+
else if (cast.to == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT);
163165
else {
164166
throw new IllegalStateException("Illegal tree structure.");
165167
}
166168
}
167169
} else {
168170
writeCast(cast.from, cast.to);
169-
unbox(cast.unboxTo.type);
171+
unbox(getType(cast.unboxTo));
170172
}
171173
} else if (cast.boxFrom != null) {
172-
box(cast.boxFrom.type);
174+
box(getType(cast.boxFrom));
173175
writeCast(cast.from, cast.to);
174176
} else if (cast.boxTo != null) {
175177
writeCast(cast.from, cast.to);
176-
box(cast.boxTo.type);
178+
box(getType(cast.boxTo));
177179
} else {
178180
writeCast(cast.from, cast.to);
179181
}
180182
}
181183
}
182184

183-
private void writeCast(final Type from, final Type to) {
185+
private void writeCast(Class<?> from, Class<?> to) {
184186
if (from.equals(to)) {
185187
return;
186188
}
187189

188-
if (from.clazz != boolean.class && from.clazz.isPrimitive() && to.clazz != boolean.class && to.clazz.isPrimitive()) {
189-
cast(from.type, to.type);
190+
if (from != boolean.class && from.isPrimitive() && to != boolean.class && to.isPrimitive()) {
191+
cast(getType(from), getType(to));
190192
} else {
191-
if (!to.clazz.isAssignableFrom(from.clazz)) {
192-
checkCast(to.type);
193+
if (!to.isAssignableFrom(from)) {
194+
checkCast(getType(to));
193195
}
194196
}
195197
}
@@ -202,6 +204,29 @@ public void box(org.objectweb.asm.Type type) {
202204
valueOf(type);
203205
}
204206

207+
public static Type getType(Class<?> clazz) {
208+
if (clazz.isArray()) {
209+
Class<?> component = clazz.getComponentType();
210+
int dimensions = 1;
211+
212+
while (component.isArray()) {
213+
component = component.getComponentType();
214+
++dimensions;
215+
}
216+
217+
if (component == def.class) {
218+
char[] braces = new char[dimensions];
219+
Arrays.fill(braces, '[');
220+
221+
return Type.getType(new String(braces) + Type.getType(Object.class).getDescriptor());
222+
}
223+
} else if (clazz == def.class) {
224+
return Type.getType(Object.class);
225+
}
226+
227+
return Type.getType(clazz);
228+
}
229+
205230
public void writeBranch(final Label tru, final Label fals) {
206231
if (tru != null) {
207232
visitJumpInsn(Opcodes.IFNE, tru);
@@ -227,7 +252,7 @@ public int writeNewStrings() {
227252
}
228253
}
229254

230-
public void writeAppendStrings(final Type type) {
255+
public void writeAppendStrings(final Definition.Type type) {
231256
if (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE != null) {
232257
// Java 9+: record type information
233258
stringConcatArgs.peek().add(type.type);
@@ -267,7 +292,7 @@ public void writeToStrings() {
267292
}
268293

269294
/** Writes a dynamic binary instruction: returnType, lhs, and rhs can be different */
270-
public void writeDynamicBinaryInstruction(Location location, Type returnType, Type lhs, Type rhs,
295+
public void writeDynamicBinaryInstruction(Location location, Definition.Type returnType, Definition.Type lhs, Definition.Type rhs,
271296
Operation operation, int flags) {
272297
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(returnType.type, lhs.type, rhs.type);
273298

@@ -318,7 +343,7 @@ public void writeDynamicBinaryInstruction(Location location, Type returnType, Ty
318343
}
319344

320345
/** Writes a static binary instruction */
321-
public void writeBinaryInstruction(Location location, Type type, Operation operation) {
346+
public void writeBinaryInstruction(Location location, Definition.Type type, Operation operation) {
322347
if ((type.clazz == float.class || type.clazz == double.class) &&
323348
(operation == Operation.LSH || operation == Operation.USH ||
324349
operation == Operation.RSH || operation == Operation.BWAND ||

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.elasticsearch.painless.node;
2121

22+
import org.elasticsearch.painless.Definition;
2223
import org.elasticsearch.painless.Definition.Cast;
2324

2425
import java.util.Objects;
@@ -63,6 +64,6 @@ void write(MethodWriter writer, Globals globals) {
6364

6465
@Override
6566
public String toString() {
66-
return singleLineToString(cast.to, child);
67+
return singleLineToString(Definition.ClassToName(cast.to), child);
6768
}
6869
}

modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ private static void assertCast(Type actual, Type expected, boolean mustBeExplici
3939
}
4040

4141
Cast cast = definition.caster.getLegalCast(location, actual, expected, true, false);
42-
assertEquals(actual, cast.from);
43-
assertEquals(expected, cast.to);
42+
assertEquals(actual.clazz, cast.from);
43+
assertEquals(expected.clazz, cast.to);
4444

4545
if (mustBeExplicit) {
4646
ClassCastException error = expectThrows(ClassCastException.class,
4747
() -> definition.caster.getLegalCast(location, actual, expected, false, false));
4848
assertTrue(error.getMessage().startsWith("Cannot cast"));
4949
} else {
5050
cast = definition.caster.getLegalCast(location, actual, expected, false, false);
51-
assertEquals(actual, cast.from);
52-
assertEquals(expected, cast.to);
51+
assertEquals(actual.clazz, cast.from);
52+
assertEquals(expected.clazz, cast.to);
5353
}
5454
}
5555

modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,12 @@ public void testECapturingFunctionRef() {
162162
public void testECast() {
163163
Location l = new Location(getTestName(), 0);
164164
AExpression child = new EConstant(l, "test");
165-
Cast cast = Cast.standard(definition.StringType, definition.IntegerType, true);
165+
Cast cast = Cast.standard(String.class, Integer.class, true);
166166
assertEquals("(ECast java.lang.Integer (EConstant String 'test'))", new ECast(l, child, cast).toString());
167167

168168
l = new Location(getTestName(), 1);
169169
child = new EBinary(l, Operation.ADD, new EConstant(l, "test"), new EConstant(l, 12));
170-
cast = Cast.standard(definition.IntegerType, definition.BooleanType, true);
170+
cast = Cast.standard(Integer.class, Boolean.class, true);
171171
assertEquals("(ECast java.lang.Boolean (EBinary (EConstant String 'test') + (EConstant Integer 12)))",
172172
new ECast(l, child, cast).toString());
173173
}

0 commit comments

Comments
 (0)