Painless: Add PainlessConstructor (#32447)
PainlessMethod was being used as both a method and a constructor, and while there are similarities, there are also some major differences. This allows the reflection objects to be stored reducing the number of other pieces of data stored in a PainlessMethod as they are now redundant. This temporarily increases some of the code in FunctionRef and PainlessDocGenerator as they now differentiate between constructors and methods, BUT is also makes the code more maintainable because there aren't checks in several places anymore to differentiate.
This commit is contained in:
parent
1e0fcebfe1
commit
c69e62d96f
|
@ -368,7 +368,7 @@ public final class Def {
|
|||
ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length);
|
||||
} else {
|
||||
// whitelist lookup
|
||||
ref = new FunctionRef(painlessLookup, clazz, type, call, captures.length);
|
||||
ref = FunctionRef.resolveFromLookup(painlessLookup, clazz, type, call, captures.length);
|
||||
}
|
||||
final CallSite callSite = LambdaBootstrap.lambdaBootstrap(
|
||||
methodHandlesLookup,
|
||||
|
|
|
@ -20,13 +20,16 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessConstructor;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.painless.WriterConstants.CLASS_NAME;
|
||||
import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE;
|
||||
|
@ -59,8 +62,10 @@ public class FunctionRef {
|
|||
|
||||
/** interface method */
|
||||
public final PainlessMethod interfaceMethod;
|
||||
/** delegate method */
|
||||
public final PainlessMethod delegateMethod;
|
||||
/** delegate method type parameters */
|
||||
public final List<Class<?>> delegateTypeParameters;
|
||||
/** delegate method return type */
|
||||
public final Class<?> delegateReturnType;
|
||||
|
||||
/** factory method type descriptor */
|
||||
public final String factoryDescriptor;
|
||||
|
@ -80,9 +85,47 @@ public class FunctionRef {
|
|||
* @param call the right hand side of a method reference expression
|
||||
* @param numCaptures number of captured arguments
|
||||
*/
|
||||
public FunctionRef(PainlessLookup painlessLookup, Class<?> expected, String type, String call, int numCaptures) {
|
||||
this(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
|
||||
lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures);
|
||||
public static FunctionRef resolveFromLookup(
|
||||
PainlessLookup painlessLookup, Class<?> expected, String type, String call, int numCaptures) {
|
||||
|
||||
if ("new".equals(call)) {
|
||||
return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
|
||||
lookup(painlessLookup, expected, type), numCaptures);
|
||||
} else {
|
||||
return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
|
||||
lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FunctionRef (already resolved)
|
||||
* @param expected functional interface type to implement
|
||||
* @param interfaceMethod functional interface method
|
||||
* @param delegateConstructor implementation constructor
|
||||
* @param numCaptures number of captured arguments
|
||||
*/
|
||||
public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessConstructor delegateConstructor, int numCaptures) {
|
||||
Constructor<?> javaConstructor = delegateConstructor.javaConstructor;
|
||||
MethodType delegateMethodType = delegateConstructor.methodType;
|
||||
|
||||
interfaceMethodName = interfaceMethod.name;
|
||||
factoryMethodType = MethodType.methodType(expected,
|
||||
delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
|
||||
interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
|
||||
delegateClassName = javaConstructor.getDeclaringClass().getName();
|
||||
isDelegateInterface = false;
|
||||
delegateInvokeType = H_NEWINVOKESPECIAL;
|
||||
delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME;
|
||||
this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
|
||||
|
||||
this.interfaceMethod = interfaceMethod;
|
||||
delegateTypeParameters = delegateConstructor.typeParameters;
|
||||
delegateReturnType = void.class;
|
||||
|
||||
factoryDescriptor = factoryMethodType.toMethodDescriptorString();
|
||||
interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
|
||||
delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,9 +155,7 @@ public class FunctionRef {
|
|||
isDelegateInterface = delegateMethod.target.isInterface();
|
||||
}
|
||||
|
||||
if ("<init>".equals(delegateMethod.name)) {
|
||||
delegateInvokeType = H_NEWINVOKESPECIAL;
|
||||
} else if (Modifier.isStatic(delegateMethod.modifiers)) {
|
||||
if (Modifier.isStatic(delegateMethod.modifiers)) {
|
||||
delegateInvokeType = H_INVOKESTATIC;
|
||||
} else if (delegateMethod.target.isInterface()) {
|
||||
delegateInvokeType = H_INVOKEINTERFACE;
|
||||
|
@ -126,7 +167,8 @@ public class FunctionRef {
|
|||
this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
|
||||
|
||||
this.interfaceMethod = interfaceMethod;
|
||||
this.delegateMethod = delegateMethod;
|
||||
delegateTypeParameters = delegateMethod.arguments;
|
||||
delegateReturnType = delegateMethod.rtn;
|
||||
|
||||
factoryDescriptor = factoryMethodType.toMethodDescriptorString();
|
||||
interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
|
||||
|
@ -151,13 +193,37 @@ public class FunctionRef {
|
|||
isDelegateInterface = false;
|
||||
|
||||
this.interfaceMethod = null;
|
||||
delegateMethod = null;
|
||||
delegateTypeParameters = null;
|
||||
delegateReturnType = null;
|
||||
|
||||
factoryDescriptor = null;
|
||||
interfaceType = null;
|
||||
delegateType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up {@code type} from the whitelist, and returns a matching constructor.
|
||||
*/
|
||||
private static PainlessConstructor lookup(PainlessLookup painlessLookup, Class<?> expected, String type) {
|
||||
// check its really a functional interface
|
||||
// for e.g. Comparable
|
||||
PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
if (method == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::new] " +
|
||||
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface");
|
||||
}
|
||||
|
||||
// lookup requested constructor
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
|
||||
PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.arguments.size()));
|
||||
|
||||
if (impl == null) {
|
||||
throw new IllegalArgumentException("Unknown reference [" + type + "::new] matching [" + expected + "]");
|
||||
}
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up {@code type::call} from the whitelist, and returns a matching method.
|
||||
*/
|
||||
|
@ -174,27 +240,22 @@ public class FunctionRef {
|
|||
// lookup requested method
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
|
||||
final PainlessMethod impl;
|
||||
// ctor ref
|
||||
if ("new".equals(call)) {
|
||||
impl = struct.constructors.get(PainlessLookupUtility.buildPainlessMethodKey("<init>", method.arguments.size()));
|
||||
} else {
|
||||
// look for a static impl first
|
||||
PainlessMethod staticImpl =
|
||||
struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.arguments.size()));
|
||||
if (staticImpl == null) {
|
||||
// otherwise a virtual impl
|
||||
final int arity;
|
||||
if (receiverCaptured) {
|
||||
// receiver captured
|
||||
arity = method.arguments.size();
|
||||
} else {
|
||||
// receiver passed
|
||||
arity = method.arguments.size() - 1;
|
||||
}
|
||||
impl = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(call, arity));
|
||||
// look for a static impl first
|
||||
PainlessMethod staticImpl =
|
||||
struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.arguments.size()));
|
||||
if (staticImpl == null) {
|
||||
// otherwise a virtual impl
|
||||
final int arity;
|
||||
if (receiverCaptured) {
|
||||
// receiver captured
|
||||
arity = method.arguments.size();
|
||||
} else {
|
||||
impl = staticImpl;
|
||||
// receiver passed
|
||||
arity = method.arguments.size() - 1;
|
||||
}
|
||||
impl = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(call, arity));
|
||||
} else {
|
||||
impl = staticImpl;
|
||||
}
|
||||
if (impl == null) {
|
||||
throw new IllegalArgumentException("Unknown reference [" + type + "::" + call + "] matching " +
|
||||
|
|
|
@ -24,7 +24,8 @@ import java.util.Collections;
|
|||
import java.util.Map;
|
||||
|
||||
public final class PainlessClass {
|
||||
public final Map<String, PainlessMethod> constructors;
|
||||
public final Map<String, PainlessConstructor> constructors;
|
||||
|
||||
public final Map<String, PainlessMethod> staticMethods;
|
||||
public final Map<String, PainlessMethod> methods;
|
||||
|
||||
|
@ -36,13 +37,14 @@ public final class PainlessClass {
|
|||
|
||||
public final PainlessMethod functionalMethod;
|
||||
|
||||
PainlessClass(Map<String, PainlessMethod> constructors,
|
||||
PainlessClass(Map<String, PainlessConstructor> constructors,
|
||||
Map<String, PainlessMethod> staticMethods, Map<String, PainlessMethod> methods,
|
||||
Map<String, PainlessField> staticFields, Map<String, PainlessField> fields,
|
||||
Map<String, MethodHandle> getterMethodHandles, Map<String, MethodHandle> setterMethodHandles,
|
||||
PainlessMethod functionalMethod) {
|
||||
|
||||
this.constructors = Collections.unmodifiableMap(constructors);
|
||||
|
||||
this.staticMethods = Collections.unmodifiableMap(staticMethods);
|
||||
this.methods = Collections.unmodifiableMap(methods);
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
final class PainlessClassBuilder {
|
||||
final Map<String, PainlessMethod> constructors;
|
||||
final Map<String, PainlessConstructor> constructors;
|
||||
|
||||
final Map<String, PainlessMethod> staticMethods;
|
||||
final Map<String, PainlessMethod> methods;
|
||||
|
||||
|
@ -38,6 +39,7 @@ final class PainlessClassBuilder {
|
|||
|
||||
PainlessClassBuilder() {
|
||||
constructors = new HashMap<>();
|
||||
|
||||
staticMethods = new HashMap<>();
|
||||
methods = new HashMap<>();
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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.elasticsearch.painless.lookup;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
|
||||
public class PainlessConstructor {
|
||||
public final Constructor<?> javaConstructor;
|
||||
public final List<Class<?>> typeParameters;
|
||||
public final MethodHandle methodHandle;
|
||||
public final MethodType methodType;
|
||||
|
||||
PainlessConstructor(Constructor<?> javaConstructor, List<Class<?>> typeParameters, MethodHandle methodHandle, MethodType methodType) {
|
||||
this.javaConstructor = javaConstructor;
|
||||
this.typeParameters = typeParameters;
|
||||
this.methodHandle = methodHandle;
|
||||
this.methodType = methodType;
|
||||
}
|
||||
}
|
|
@ -40,15 +40,47 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.CONSTRUCTOR_NAME;
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.DEF_CLASS_NAME;
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey;
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey;
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessMethodKey;
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName;
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToJavaType;
|
||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typesToCanonicalTypeNames;
|
||||
|
||||
public class PainlessLookupBuilder {
|
||||
public final class PainlessLookupBuilder {
|
||||
|
||||
private static class PainlessConstructorCacheKey {
|
||||
|
||||
private final Class<?> targetType;
|
||||
private final List<Class<?>> typeParameters;
|
||||
|
||||
private PainlessConstructorCacheKey(Class<?> targetType, List<Class<?>> typeParameters) {
|
||||
this.targetType = targetType;
|
||||
this.typeParameters = Collections.unmodifiableList(typeParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (object == null || getClass() != object.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PainlessConstructorCacheKey that = (PainlessConstructorCacheKey)object;
|
||||
|
||||
return Objects.equals(targetType, that.targetType) &&
|
||||
Objects.equals(typeParameters, that.typeParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(targetType, typeParameters);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PainlessMethodCacheKey {
|
||||
|
||||
|
@ -120,8 +152,9 @@ public class PainlessLookupBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private static final Map<PainlessMethodCacheKey, PainlessMethod> painlessMethodCache = new HashMap<>();
|
||||
private static final Map<PainlessFieldCacheKey, PainlessField> painlessFieldCache = new HashMap<>();
|
||||
private static final Map<PainlessConstructorCacheKey, PainlessConstructor> painlessConstuctorCache = new HashMap<>();
|
||||
private static final Map<PainlessMethodCacheKey, PainlessMethod> painlessMethodCache = new HashMap<>();
|
||||
private static final Map<PainlessFieldCacheKey, PainlessField> painlessFieldCache = new HashMap<>();
|
||||
|
||||
private static final Pattern CLASS_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
|
||||
private static final Pattern METHOD_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][_a-zA-Z0-9]*$");
|
||||
|
@ -343,11 +376,10 @@ public class PainlessLookupBuilder {
|
|||
"[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] not found", nsme);
|
||||
}
|
||||
|
||||
String painlessMethodKey = buildPainlessMethodKey(CONSTRUCTOR_NAME, typeParametersSize);
|
||||
PainlessMethod painlessConstructor = painlessClassBuilder.constructors.get(painlessMethodKey);
|
||||
String painlessConstructorKey = buildPainlessConstructorKey(typeParametersSize);
|
||||
PainlessConstructor painlessConstructor = painlessClassBuilder.constructors.get(painlessConstructorKey);
|
||||
|
||||
if (painlessConstructor == null) {
|
||||
org.objectweb.asm.commons.Method asmConstructor = org.objectweb.asm.commons.Method.getMethod(javaConstructor);
|
||||
MethodHandle methodHandle;
|
||||
|
||||
try {
|
||||
|
@ -359,17 +391,16 @@ public class PainlessLookupBuilder {
|
|||
|
||||
MethodType methodType = methodHandle.type();
|
||||
|
||||
painlessConstructor = painlessMethodCache.computeIfAbsent(
|
||||
new PainlessMethodCacheKey(targetClass, CONSTRUCTOR_NAME, typeParameters),
|
||||
key -> new PainlessMethod(CONSTRUCTOR_NAME, targetClass, null, void.class, typeParameters,
|
||||
asmConstructor, javaConstructor.getModifiers(), methodHandle, methodType)
|
||||
painlessConstructor = painlessConstuctorCache.computeIfAbsent(
|
||||
new PainlessConstructorCacheKey(targetClass, typeParameters),
|
||||
key -> new PainlessConstructor(javaConstructor, typeParameters, methodHandle, methodType)
|
||||
);
|
||||
|
||||
painlessClassBuilder.constructors.put(painlessMethodKey, painlessConstructor);
|
||||
} else if (painlessConstructor.arguments.equals(typeParameters) == false){
|
||||
painlessClassBuilder.constructors.put(painlessConstructorKey, painlessConstructor);
|
||||
} else if (painlessConstructor.typeParameters.equals(typeParameters) == false){
|
||||
throw new IllegalArgumentException("cannot have constructors " +
|
||||
"[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] and " +
|
||||
"[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(painlessConstructor.arguments) + "] " +
|
||||
"[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(painlessConstructor.typeParameters) + "] " +
|
||||
"with the same arity and different type parameters");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -336,6 +336,13 @@ public final class PainlessLookupUtility {
|
|||
type == String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a painless constructor key used to lookup painless constructors from a painless class.
|
||||
*/
|
||||
public static String buildPainlessConstructorKey(int constructorArity) {
|
||||
return CONSTRUCTOR_NAME + "/" + constructorArity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a painless method key used to lookup painless methods from a painless class.
|
||||
*/
|
||||
|
|
|
@ -77,18 +77,18 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
|
|||
// static case
|
||||
if (captured.clazz != def.class) {
|
||||
try {
|
||||
ref = new FunctionRef(locals.getPainlessLookup(), expected,
|
||||
ref = FunctionRef.resolveFromLookup(locals.getPainlessLookup(), expected,
|
||||
PainlessLookupUtility.typeToCanonicalTypeName(captured.clazz), call, 1);
|
||||
|
||||
// check casts between the interface method and the delegate method are legal
|
||||
for (int i = 0; i < ref.interfaceMethod.arguments.size(); ++i) {
|
||||
Class<?> from = ref.interfaceMethod.arguments.get(i);
|
||||
Class<?> to = ref.delegateMethod.arguments.get(i);
|
||||
Class<?> to = ref.delegateTypeParameters.get(i);
|
||||
AnalyzerCaster.getLegalCast(location, from, to, false, true);
|
||||
}
|
||||
|
||||
if (ref.interfaceMethod.rtn != void.class) {
|
||||
AnalyzerCaster.getLegalCast(location, ref.delegateMethod.rtn, ref.interfaceMethod.rtn, false, true);
|
||||
AnalyzerCaster.getLegalCast(location, ref.delegateReturnType, ref.interfaceMethod.rtn, false, true);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw createError(e);
|
||||
|
|
|
@ -90,7 +90,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||
}
|
||||
} else {
|
||||
// whitelist lookup
|
||||
ref = new FunctionRef(locals.getPainlessLookup(), expected, type, call, 0);
|
||||
ref = FunctionRef.resolveFromLookup(locals.getPainlessLookup(), expected, type, call, 0);
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
|
|
@ -23,10 +23,12 @@ import org.elasticsearch.painless.Globals;
|
|||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.lookup.PainlessConstructor;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.def;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.Method;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -38,7 +40,7 @@ import java.util.Set;
|
|||
public final class EListInit extends AExpression {
|
||||
private final List<AExpression> values;
|
||||
|
||||
private PainlessMethod constructor = null;
|
||||
private PainlessConstructor constructor = null;
|
||||
private PainlessMethod method = null;
|
||||
|
||||
public EListInit(Location location, List<AExpression> values) {
|
||||
|
@ -62,8 +64,8 @@ public final class EListInit extends AExpression {
|
|||
|
||||
actual = ArrayList.class;
|
||||
|
||||
constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors
|
||||
.get(PainlessLookupUtility.buildPainlessMethodKey("<init>", 0));
|
||||
constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(
|
||||
PainlessLookupUtility.buildPainlessConstructorKey(0));
|
||||
|
||||
if (constructor == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
@ -92,7 +94,8 @@ public final class EListInit extends AExpression {
|
|||
|
||||
writer.newInstance(MethodWriter.getType(actual));
|
||||
writer.dup();
|
||||
writer.invokeConstructor(Type.getType(constructor.target), constructor.method);
|
||||
writer.invokeConstructor(
|
||||
Type.getType(constructor.javaConstructor.getDeclaringClass()), Method.getMethod(constructor.javaConstructor));
|
||||
|
||||
for (AExpression value : values) {
|
||||
writer.dup();
|
||||
|
|
|
@ -23,10 +23,12 @@ import org.elasticsearch.painless.Globals;
|
|||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.lookup.PainlessConstructor;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.def;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.Method;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -39,7 +41,7 @@ public final class EMapInit extends AExpression {
|
|||
private final List<AExpression> keys;
|
||||
private final List<AExpression> values;
|
||||
|
||||
private PainlessMethod constructor = null;
|
||||
private PainlessConstructor constructor = null;
|
||||
private PainlessMethod method = null;
|
||||
|
||||
public EMapInit(Location location, List<AExpression> keys, List<AExpression> values) {
|
||||
|
@ -68,8 +70,8 @@ public final class EMapInit extends AExpression {
|
|||
|
||||
actual = HashMap.class;
|
||||
|
||||
constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors
|
||||
.get(PainlessLookupUtility.buildPainlessMethodKey("<init>", 0));
|
||||
constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(
|
||||
PainlessLookupUtility.buildPainlessConstructorKey(0));
|
||||
|
||||
if (constructor == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
@ -111,7 +113,8 @@ public final class EMapInit extends AExpression {
|
|||
|
||||
writer.newInstance(MethodWriter.getType(actual));
|
||||
writer.dup();
|
||||
writer.invokeConstructor(Type.getType(constructor.target), constructor.method);
|
||||
writer.invokeConstructor(
|
||||
Type.getType(constructor.javaConstructor.getDeclaringClass()), Method.getMethod(constructor.javaConstructor));
|
||||
|
||||
for (int index = 0; index < keys.size(); ++index) {
|
||||
AExpression key = keys.get(index);
|
||||
|
|
|
@ -24,9 +24,10 @@ import org.elasticsearch.painless.Locals;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessConstructor;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.Method;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -40,7 +41,7 @@ public final class ENewObj extends AExpression {
|
|||
private final String type;
|
||||
private final List<AExpression> arguments;
|
||||
|
||||
private PainlessMethod constructor;
|
||||
private PainlessConstructor constructor;
|
||||
|
||||
public ENewObj(Location location, String type, List<AExpression> arguments) {
|
||||
super(location);
|
||||
|
@ -65,16 +66,16 @@ public final class ENewObj extends AExpression {
|
|||
}
|
||||
|
||||
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual);
|
||||
constructor = struct.constructors.get(PainlessLookupUtility.buildPainlessMethodKey("<init>", arguments.size()));
|
||||
constructor = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(arguments.size()));
|
||||
|
||||
if (constructor != null) {
|
||||
Class<?>[] types = new Class<?>[constructor.arguments.size()];
|
||||
constructor.arguments.toArray(types);
|
||||
Class<?>[] types = new Class<?>[constructor.typeParameters.size()];
|
||||
constructor.typeParameters.toArray(types);
|
||||
|
||||
if (constructor.arguments.size() != arguments.size()) {
|
||||
if (constructor.typeParameters.size() != arguments.size()) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "] " +
|
||||
"expected [" + constructor.arguments.size() + "] arguments, but found [" + arguments.size() + "]."));
|
||||
"expected [" + constructor.typeParameters.size() + "] arguments, but found [" + arguments.size() + "]."));
|
||||
}
|
||||
|
||||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||
|
@ -107,7 +108,8 @@ public final class ENewObj extends AExpression {
|
|||
argument.write(writer, globals);
|
||||
}
|
||||
|
||||
writer.invokeConstructor(Type.getType(constructor.target), constructor.method);
|
||||
writer.invokeConstructor(
|
||||
Type.getType(constructor.javaConstructor.getDeclaringClass()), Method.getMethod(constructor.javaConstructor));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.common.io.PathUtils;
|
|||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||
import org.elasticsearch.core.internal.io.IOUtils;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessConstructor;
|
||||
import org.elasticsearch.painless.lookup.PainlessField;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
|
||||
|
@ -57,7 +58,8 @@ public class PainlessDocGenerator {
|
|||
private static final Logger logger = ESLoggerFactory.getLogger(PainlessDocGenerator.class);
|
||||
private static final Comparator<PainlessField> FIELD_NAME = comparing(f -> f.name);
|
||||
private static final Comparator<PainlessMethod> METHOD_NAME = comparing(m -> m.name);
|
||||
private static final Comparator<PainlessMethod> NUMBER_OF_ARGS = comparing(m -> m.arguments.size());
|
||||
private static final Comparator<PainlessMethod> METHOD_NUMBER_OF_PARAMS = comparing(m -> m.arguments.size());
|
||||
private static final Comparator<PainlessConstructor> CONSTRUCTOR_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size());
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Path apiRootPath = PathUtils.get(args[0]);
|
||||
|
@ -103,12 +105,15 @@ public class PainlessDocGenerator {
|
|||
|
||||
Consumer<PainlessField> documentField = field -> PainlessDocGenerator.documentField(typeStream, field);
|
||||
Consumer<PainlessMethod> documentMethod = method -> PainlessDocGenerator.documentMethod(typeStream, method);
|
||||
Consumer<PainlessConstructor> documentConstructor =
|
||||
constructor -> PainlessDocGenerator.documentConstructor(typeStream, constructor);
|
||||
struct.staticFields.values().stream().sorted(FIELD_NAME).forEach(documentField);
|
||||
struct.fields.values().stream().sorted(FIELD_NAME).forEach(documentField);
|
||||
struct.staticMethods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(documentMethod);
|
||||
struct.constructors.values().stream().sorted(NUMBER_OF_ARGS).forEach(documentMethod);
|
||||
struct.staticMethods.values().stream().sorted(
|
||||
METHOD_NAME.thenComparing(METHOD_NUMBER_OF_PARAMS)).forEach(documentMethod);
|
||||
struct.constructors.values().stream().sorted(CONSTRUCTOR_NUMBER_OF_PARAMS).forEach(documentConstructor);
|
||||
Map<String, Class<?>> inherited = new TreeMap<>();
|
||||
struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(method -> {
|
||||
struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(METHOD_NUMBER_OF_PARAMS)).forEach(method -> {
|
||||
if (method.target == clazz) {
|
||||
documentMethod(typeStream, method);
|
||||
} else {
|
||||
|
@ -164,6 +169,41 @@ public class PainlessDocGenerator {
|
|||
stream.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Document a constructor.
|
||||
*/
|
||||
private static void documentConstructor(PrintStream stream, PainlessConstructor constructor) {
|
||||
stream.print("* ++[[");
|
||||
emitAnchor(stream, constructor);
|
||||
stream.print("]]");
|
||||
|
||||
String javadocRoot = javadocRoot(constructor.javaConstructor.getDeclaringClass());
|
||||
emitJavadocLink(stream, javadocRoot, constructor);
|
||||
stream.print('[');
|
||||
|
||||
stream.print(constructorName(constructor));
|
||||
|
||||
stream.print("](");
|
||||
boolean first = true;
|
||||
for (Class<?> arg : constructor.typeParameters) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
stream.print(", ");
|
||||
}
|
||||
emitType(stream, arg);
|
||||
}
|
||||
stream.print(")++");
|
||||
|
||||
if (javadocRoot.equals("java8")) {
|
||||
stream.print(" (");
|
||||
emitJavadocLink(stream, "java9", constructor);
|
||||
stream.print("[java 9])");
|
||||
}
|
||||
|
||||
stream.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Document a method.
|
||||
*/
|
||||
|
@ -176,10 +216,8 @@ public class PainlessDocGenerator {
|
|||
stream.print("static ");
|
||||
}
|
||||
|
||||
if (false == method.name.equals("<init>")) {
|
||||
emitType(stream, method.rtn);
|
||||
stream.print(' ');
|
||||
}
|
||||
emitType(stream, method.rtn);
|
||||
stream.print(' ');
|
||||
|
||||
String javadocRoot = javadocRoot(method);
|
||||
emitJavadocLink(stream, javadocRoot, method);
|
||||
|
@ -216,6 +254,17 @@ public class PainlessDocGenerator {
|
|||
stream.print(PainlessLookupUtility.typeToCanonicalTypeName(clazz).replace('.', '-'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Anchor text for a {@link PainlessConstructor}.
|
||||
*/
|
||||
private static void emitAnchor(PrintStream stream, PainlessConstructor constructor) {
|
||||
emitAnchor(stream, constructor.javaConstructor.getDeclaringClass());
|
||||
stream.print('-');
|
||||
stream.print(constructorName(constructor));
|
||||
stream.print('-');
|
||||
stream.print(constructor.typeParameters.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Anchor text for a {@link PainlessMethod}.
|
||||
*/
|
||||
|
@ -236,8 +285,12 @@ public class PainlessDocGenerator {
|
|||
stream.print(field.name);
|
||||
}
|
||||
|
||||
private static String constructorName(PainlessConstructor constructor) {
|
||||
return PainlessLookupUtility.typeToCanonicalTypeName(constructor.javaConstructor.getDeclaringClass());
|
||||
}
|
||||
|
||||
private static String methodName(PainlessMethod method) {
|
||||
return method.name.equals("<init>") ? PainlessLookupUtility.typeToCanonicalTypeName(method.target) : method.name;
|
||||
return PainlessLookupUtility.typeToCanonicalTypeName(method.target);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,6 +322,34 @@ public class PainlessDocGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an external link to Javadoc for a {@link PainlessMethod}.
|
||||
*
|
||||
* @param root name of the root uri variable
|
||||
*/
|
||||
private static void emitJavadocLink(PrintStream stream, String root, PainlessConstructor constructor) {
|
||||
stream.print("link:{");
|
||||
stream.print(root);
|
||||
stream.print("-javadoc}/");
|
||||
stream.print(classUrlPath(constructor.javaConstructor.getDeclaringClass()));
|
||||
stream.print(".html#");
|
||||
stream.print(constructorName(constructor));
|
||||
stream.print("%2D");
|
||||
boolean first = true;
|
||||
for (Class<?> clazz: constructor.typeParameters) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
stream.print("%2D");
|
||||
}
|
||||
stream.print(clazz.getName());
|
||||
if (clazz.isArray()) {
|
||||
stream.print(":A");
|
||||
}
|
||||
}
|
||||
stream.print("%2D");
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an external link to Javadoc for a {@link PainlessMethod}.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue