Skip to content

Commit a02e5ee

Browse files
authored
Painless: Complete Removal of Painless Type (#31699)
This completes the removal of Painless Type. The new data structures in the definition are a map of names (String) to Java Classes and a map of Java Classes to Painless Structs. The names to Java Classes map can contain a 2 to 1 ratio of names to classes depending on whether or not a short (imported) name is used. The Java Classes to Painless Structs is 1 to 1 always where the Java Class name must match the Painless Struct name. This should lead a significantly simpler type system in Painless moving forward since the Painless Type only held redundant information since Painless does not support generics.
1 parent ed41d4f commit a02e5ee

35 files changed

+541
-705
lines changed

modules/lang-painless/src/main/antlr/PainlessLexer.g4

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919

2020
lexer grammar PainlessLexer;
2121

22-
@members{
22+
@members {
2323
/**
2424
* Check against the current whitelist to determine whether a token is a type
2525
* or not. Called by the {@code TYPE} token defined in {@code PainlessLexer.g4}.
2626
* See also
2727
* <a href="https://en.wikipedia.org/wiki/The_lexer_hack">The lexer hack</a>.
2828
*/
29-
protected abstract boolean isSimpleType(String name);
29+
protected abstract boolean isType(String name);
3030
3131
/**
3232
* Is the preceding {@code /} a the beginning of a regex (true) or a division
@@ -133,7 +133,7 @@ NULL: 'null';
133133
// or not. Note this works by processing one character at a time
134134
// and the rule is added or removed as this happens. This is also known
135135
// as "the lexer hack." See (https://en.wikipedia.org/wiki/The_lexer_hack).
136-
TYPE: ID ( DOT ID )* { isSimpleType(getText()) }?;
136+
TYPE: ID ( DOT ID )* { isType(getText()) }?;
137137
ID: [_a-zA-Z] [_a-zA-Z0-9]*;
138138

139139
mode AFTER_DOT;

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

+18-18
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ static Method lookupMethodInternal(Definition definition, Class<?> receiverClass
185185
Definition.MethodKey key = new Definition.MethodKey(name, arity);
186186
// check whitelist for matching method
187187
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
188-
Struct struct = definition.RuntimeClassToStruct(clazz);
188+
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
189189

190190
if (struct != null) {
191191
Method method = struct.methods.get(key);
@@ -195,7 +195,7 @@ static Method lookupMethodInternal(Definition definition, Class<?> receiverClass
195195
}
196196

197197
for (Class<?> iface : clazz.getInterfaces()) {
198-
struct = definition.RuntimeClassToStruct(iface);
198+
struct = definition.getPainlessStructFromJavaClass(iface);
199199

200200
if (struct != null) {
201201
Method method = struct.methods.get(key);
@@ -279,7 +279,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp
279279
captures[capture] = callSiteType.parameterType(i + 1 + capture);
280280
}
281281
MethodHandle filter;
282-
Definition.Type interfaceType = definition.ClassToType(method.arguments.get(i - 1 - replaced));
282+
Class<?> interfaceType = method.arguments.get(i - 1 - replaced);
283283
if (signature.charAt(0) == 'S') {
284284
// the implementation is strongly typed, now that we know the interface type,
285285
// we have everything.
@@ -293,14 +293,14 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp
293293
// the interface type is now known, but we need to get the implementation.
294294
// this is dynamically based on the receiver type (and cached separately, underneath
295295
// this cache). It won't blow up since we never nest here (just references)
296-
MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
296+
MethodType nestedType = MethodType.methodType(interfaceType, captures);
297297
CallSite nested = DefBootstrap.bootstrap(definition,
298298
lookup,
299299
call,
300300
nestedType,
301301
0,
302302
DefBootstrap.REFERENCE,
303-
interfaceType.name);
303+
Definition.ClassToName(interfaceType));
304304
filter = nested.dynamicInvoker();
305305
} else {
306306
throw new AssertionError();
@@ -324,8 +324,8 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp
324324
*/
325325
static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass,
326326
Class<?> receiverClass, String name) throws Throwable {
327-
Definition.Type interfaceType = definition.getType(interfaceClass);
328-
Method interfaceMethod = interfaceType.struct.functionalMethod;
327+
Class<?> interfaceType = definition.getJavaClassFromPainlessType(interfaceClass);
328+
Method interfaceMethod = definition.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
329329
if (interfaceMethod == null) {
330330
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
331331
}
@@ -337,15 +337,15 @@ static MethodHandle lookupReference(Definition definition, Lookup lookup, String
337337

338338
/** Returns a method handle to an implementation of clazz, given method reference signature. */
339339
private static MethodHandle lookupReferenceInternal(Definition definition, Lookup lookup,
340-
Definition.Type clazz, String type, String call, Class<?>... captures)
340+
Class<?> clazz, String type, String call, Class<?>... captures)
341341
throws Throwable {
342342
final FunctionRef ref;
343343
if ("this".equals(type)) {
344344
// user written method
345-
Method interfaceMethod = clazz.struct.functionalMethod;
345+
Method interfaceMethod = definition.getPainlessStructFromJavaClass(clazz).functionalMethod;
346346
if (interfaceMethod == null) {
347347
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
348-
"to [" + clazz.name + "], not a functional interface");
348+
"to [" + Definition.ClassToName(clazz) + "], not a functional interface");
349349
}
350350
int arity = interfaceMethod.arguments.size() + captures.length;
351351
final MethodHandle handle;
@@ -359,14 +359,14 @@ private static MethodHandle lookupReferenceInternal(Definition definition, Looku
359359
// because the arity does not match the expected interface type.
360360
if (call.contains("$")) {
361361
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
362-
"] in [" + clazz.clazz + "]");
362+
"] in [" + clazz + "]");
363363
}
364364
throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
365365
}
366-
ref = new FunctionRef(clazz.clazz, interfaceMethod, call, handle.type(), captures.length);
366+
ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length);
367367
} else {
368368
// whitelist lookup
369-
ref = new FunctionRef(definition, clazz.clazz, type, call, captures.length);
369+
ref = new FunctionRef(definition, clazz, type, call, captures.length);
370370
}
371371
final CallSite callSite = LambdaBootstrap.lambdaBootstrap(
372372
lookup,
@@ -379,7 +379,7 @@ private static MethodHandle lookupReferenceInternal(Definition definition, Looku
379379
ref.delegateMethodType,
380380
ref.isDelegateInterface ? 1 : 0
381381
);
382-
return callSite.dynamicInvoker().asType(MethodType.methodType(clazz.clazz, captures));
382+
return callSite.dynamicInvoker().asType(MethodType.methodType(clazz, captures));
383383
}
384384

385385
/** gets the field name used to lookup up the MethodHandle for a function. */
@@ -416,7 +416,7 @@ public static String getUserFunctionHandleFieldName(String name, int arity) {
416416
static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass, String name) {
417417
// first try whitelist
418418
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
419-
Struct struct = definition.RuntimeClassToStruct(clazz);
419+
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
420420

421421
if (struct != null) {
422422
MethodHandle handle = struct.getters.get(name);
@@ -426,7 +426,7 @@ static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass,
426426
}
427427

428428
for (final Class<?> iface : clazz.getInterfaces()) {
429-
struct = definition.RuntimeClassToStruct(iface);
429+
struct = definition.getPainlessStructFromJavaClass(iface);
430430

431431
if (struct != null) {
432432
MethodHandle handle = struct.getters.get(name);
@@ -487,7 +487,7 @@ static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass,
487487
static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass, String name) {
488488
// first try whitelist
489489
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
490-
Struct struct = definition.RuntimeClassToStruct(clazz);
490+
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
491491

492492
if (struct != null) {
493493
MethodHandle handle = struct.setters.get(name);
@@ -497,7 +497,7 @@ static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass,
497497
}
498498

499499
for (final Class<?> iface : clazz.getInterfaces()) {
500-
struct = definition.RuntimeClassToStruct(iface);
500+
struct = definition.getPainlessStructFromJavaClass(iface);
501501

502502
if (struct != null) {
503503
MethodHandle handle = struct.setters.get(name);

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

+30-30
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
package org.elasticsearch.painless;
2-
3-
import org.elasticsearch.common.SuppressForbidden;
4-
51
/*
62
* Licensed to Elasticsearch under one or more contributor
73
* license agreements. See the NOTICE file distributed with
@@ -21,6 +17,10 @@
2117
* under the License.
2218
*/
2319

20+
package org.elasticsearch.painless;
21+
22+
import org.elasticsearch.common.SuppressForbidden;
23+
2424
import java.lang.invoke.CallSite;
2525
import java.lang.invoke.MethodHandle;
2626
import java.lang.invoke.MethodHandles;
@@ -72,24 +72,24 @@ private DefBootstrap() {} // no instance!
7272
public static final int SHIFT_OPERATOR = 9;
7373
/** static bootstrap parameter indicating a request to normalize an index for array-like-access */
7474
public static final int INDEX_NORMALIZE = 10;
75-
75+
7676
// constants for the flags parameter of operators
77-
/**
78-
* static bootstrap parameter indicating the binary operator allows nulls (e.g. == and +)
77+
/**
78+
* static bootstrap parameter indicating the binary operator allows nulls (e.g. == and +)
7979
* <p>
8080
* requires additional {@link MethodHandles#catchException} guard, which will invoke
8181
* the fallback if a null is encountered.
8282
*/
8383
public static final int OPERATOR_ALLOWS_NULL = 1 << 0;
84-
84+
8585
/**
8686
* static bootstrap parameter indicating the binary operator is part of compound assignment (e.g. +=).
8787
* <p>
8888
* may require {@link MethodHandles#explicitCastArguments}, or a dynamic cast
8989
* to cast back to the receiver's type, depending on types seen.
9090
*/
9191
public static final int OPERATOR_COMPOUND_ASSIGNMENT = 1 << 1;
92-
92+
9393
/**
9494
* static bootstrap parameter indicating an explicit cast to the return type.
9595
* <p>
@@ -129,7 +129,7 @@ static final class PIC extends MutableCallSite {
129129

130130
setTarget(fallback);
131131
}
132-
132+
133133
/**
134134
* guard method for inline caching: checks the receiver's class is the same
135135
* as the cached class
@@ -162,7 +162,7 @@ private MethodHandle lookup(int flavor, String name, Class<?> receiver) throws T
162162
default: throw new AssertionError();
163163
}
164164
}
165-
165+
166166
/**
167167
* Creates the {@link MethodHandle} for the megamorphic call site
168168
* using {@link ClassValue} and {@link MethodHandles#exactInvoker(MethodType)}:
@@ -182,7 +182,7 @@ protected MethodHandle computeValue(Class<?> receiverType) {
182182
}
183183
};
184184
return MethodHandles.foldArguments(MethodHandles.exactInvoker(type),
185-
MEGAMORPHIC_LOOKUP.bindTo(megamorphicCache));
185+
MEGAMORPHIC_LOOKUP.bindTo(megamorphicCache));
186186
}
187187

188188
/**
@@ -195,18 +195,18 @@ Object fallback(final Object[] callArgs) throws Throwable {
195195
if (depth >= MAX_DEPTH) {
196196
// we revert the whole cache and build a new megamorphic one
197197
final MethodHandle target = this.createMegamorphicHandle();
198-
198+
199199
setTarget(target);
200-
return target.invokeWithArguments(callArgs);
200+
return target.invokeWithArguments(callArgs);
201201
} else {
202202
final Class<?> receiver = callArgs[0].getClass();
203203
final MethodHandle target = lookup(flavor, name, receiver).asType(type());
204-
204+
205205
MethodHandle test = CHECK_CLASS.bindTo(receiver);
206206
MethodHandle guard = MethodHandles.guardWithTest(test, target, getTarget());
207-
207+
208208
depth++;
209-
209+
210210
setTarget(guard);
211211
return target.invokeWithArguments(callArgs);
212212
}
@@ -225,15 +225,15 @@ Object fallback(final Object[] callArgs) throws Throwable {
225225
MethodType.methodType(Object.class, Object[].class));
226226
MethodHandle mh = publicLookup.findVirtual(ClassValue.class, "get",
227227
MethodType.methodType(Object.class, Class.class));
228-
mh = MethodHandles.filterArguments(mh, 1,
228+
mh = MethodHandles.filterArguments(mh, 1,
229229
publicLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class)));
230230
MEGAMORPHIC_LOOKUP = mh.asType(mh.type().changeReturnType(MethodHandle.class));
231231
} catch (ReflectiveOperationException e) {
232232
throw new AssertionError(e);
233233
}
234234
}
235235
}
236-
236+
237237
/**
238238
* CallSite that implements the monomorphic inlining cache (for operators).
239239
*/
@@ -252,14 +252,14 @@ static final class MIC extends MutableCallSite {
252252
if (initialDepth > 0) {
253253
initialized = true;
254254
}
255-
255+
256256
MethodHandle fallback = FALLBACK.bindTo(this)
257257
.asCollector(Object[].class, type.parameterCount())
258258
.asType(type);
259259

260260
setTarget(fallback);
261261
}
262-
262+
263263
/**
264264
* Does a slow lookup for the operator
265265
*/
@@ -290,7 +290,7 @@ private MethodHandle lookup(Object[] args) throws Throwable {
290290
default: throw new AssertionError();
291291
}
292292
}
293-
293+
294294
private MethodHandle lookupGeneric() {
295295
MethodHandle target = DefMath.lookupGeneric(name);
296296
if ((flags & OPERATOR_EXPLICIT_CAST) != 0) {
@@ -302,7 +302,7 @@ private MethodHandle lookupGeneric() {
302302
}
303303
return target;
304304
}
305-
305+
306306
/**
307307
* Called when a new type is encountered or if cached type does not match.
308308
* In that case we revert to a generic, but slower operator handling.
@@ -315,7 +315,7 @@ Object fallback(Object[] args) throws Throwable {
315315
setTarget(generic.asType(type()));
316316
return generic.invokeWithArguments(args);
317317
}
318-
318+
319319
final MethodType type = type();
320320
MethodHandle target = lookup(args);
321321
// for math operators: WrongMethodType can be confusing. convert into a ClassCastException if they screw up.
@@ -361,18 +361,18 @@ Object fallback(Object[] args) throws Throwable {
361361
// very special cases, where even the receiver can be null (see JLS rules for string concat)
362362
// we wrap + with an NPE catcher, and use our generic method in that case.
363363
if (flavor == BINARY_OPERATOR && (flags & OPERATOR_ALLOWS_NULL) != 0) {
364-
MethodHandle handler = MethodHandles.dropArguments(lookupGeneric().asType(type()),
365-
0,
364+
MethodHandle handler = MethodHandles.dropArguments(lookupGeneric().asType(type()),
365+
0,
366366
NullPointerException.class);
367367
guard = MethodHandles.catchException(guard, NullPointerException.class, handler);
368368
}
369-
369+
370370
initialized = true;
371371

372372
setTarget(guard);
373373
return target.invokeWithArguments(args);
374374
}
375-
375+
376376
/**
377377
* guard method for inline caching: checks the receiver's class is the same
378378
* as the cached class
@@ -388,15 +388,15 @@ static boolean checkLHS(Class<?> clazz, Object leftObject) {
388388
static boolean checkRHS(Class<?> left, Class<?> right, Object leftObject, Object rightObject) {
389389
return rightObject.getClass() == right;
390390
}
391-
391+
392392
/**
393393
* guard method for inline caching: checks the receiver's class and the first argument
394394
* are the same as the cached receiver and first argument.
395395
*/
396396
static boolean checkBoth(Class<?> left, Class<?> right, Object leftObject, Object rightObject) {
397397
return leftObject.getClass() == left && rightObject.getClass() == right;
398398
}
399-
399+
400400
private static final MethodHandle CHECK_LHS;
401401
private static final MethodHandle CHECK_RHS;
402402
private static final MethodHandle CHECK_BOTH;

0 commit comments

Comments
 (0)