mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-25 14:26:27 +00:00
Merge pull request #19003 from rmuir/augmentation
painless: add augmentation
This commit is contained in:
commit
f70211dedb
@ -0,0 +1,410 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.ObjIntConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.ToDoubleFunction;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
/** Additional methods added to classes. These must be static methods with receiver as first argument */
|
||||||
|
public class Augmentation {
|
||||||
|
|
||||||
|
// static methods only!
|
||||||
|
private Augmentation() {}
|
||||||
|
|
||||||
|
/** Exposes List.size() as getLength(), so that .length shortcut works on lists */
|
||||||
|
public static <T> int getLength(List<T> receiver) {
|
||||||
|
return receiver.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Exposes Matcher.group(String) as namedGroup(String), so it doesn't conflict with group(int) */
|
||||||
|
public static String namedGroup(Matcher receiver, String name) {
|
||||||
|
return receiver.group(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// some groovy methods on iterable
|
||||||
|
// see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Iterable.html
|
||||||
|
|
||||||
|
/** Iterates over the contents of an iterable, and checks whether a predicate is valid for at least one element. */
|
||||||
|
public static <T> boolean any(Iterable<T> receiver, Predicate<T> predicate) {
|
||||||
|
for (T t : receiver) {
|
||||||
|
if (predicate.test(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Counts the number of occurrences which satisfy the given predicate from inside this Iterable. */
|
||||||
|
public static <T> int count(Iterable<T> receiver, Predicate<T> predicate) {
|
||||||
|
int count = 0;
|
||||||
|
for (T t : receiver) {
|
||||||
|
if (predicate.test(t)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// instead of covariant overrides for every possibility, we just return receiver as 'def' for now
|
||||||
|
// that way if someone chains the calls, everything works.
|
||||||
|
|
||||||
|
/** Iterates through an Iterable, passing each item to the given consumer. */
|
||||||
|
public static <T> Object each(Iterable<T> receiver, Consumer<T> consumer) {
|
||||||
|
receiver.forEach(consumer);
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through an iterable type, passing each item and the item's index
|
||||||
|
* (a counter starting at zero) to the given consumer.
|
||||||
|
*/
|
||||||
|
public static <T> Object eachWithIndex(Iterable<T> receiver, ObjIntConsumer<T> consumer) {
|
||||||
|
int count = 0;
|
||||||
|
for (T t : receiver) {
|
||||||
|
consumer.accept(t, count++);
|
||||||
|
}
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine if the given predicate is valid (i.e. returns true for all items in this iterable).
|
||||||
|
*/
|
||||||
|
public static <T> boolean every(Iterable<T> receiver, Predicate<T> predicate) {
|
||||||
|
for (T t : receiver) {
|
||||||
|
if (predicate.test(t) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the Iterable transforming items using the supplied function and
|
||||||
|
* collecting any non-null results.
|
||||||
|
*/
|
||||||
|
public static <T,U> List<U> findResults(Iterable<T> receiver, Function<T,U> filter) {
|
||||||
|
List<U> list = new ArrayList<>();
|
||||||
|
for (T t: receiver) {
|
||||||
|
U result = filter.apply(t);
|
||||||
|
if (result != null) {
|
||||||
|
list.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts all Iterable members into groups determined by the supplied mapping function.
|
||||||
|
*/
|
||||||
|
public static <T,U> Map<U,List<T>> groupBy(Iterable<T> receiver, Function<T,U> mapper) {
|
||||||
|
Map<U,List<T>> map = new LinkedHashMap<>();
|
||||||
|
for (T t : receiver) {
|
||||||
|
U mapped = mapper.apply(t);
|
||||||
|
List<T> results = map.get(mapped);
|
||||||
|
if (results == null) {
|
||||||
|
results = new ArrayList<>();
|
||||||
|
map.put(mapped, results);
|
||||||
|
}
|
||||||
|
results.add(t);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates the toString() representation of each item in this Iterable,
|
||||||
|
* with the given String as a separator between each item.
|
||||||
|
*/
|
||||||
|
public static <T> String join(Iterable<T> receiver, String separator) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (T t : receiver) {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.append(separator);
|
||||||
|
}
|
||||||
|
sb.append(t);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sums the result of applying a function to each item of an Iterable.
|
||||||
|
*/
|
||||||
|
public static <T> double sum(Iterable<T> receiver, ToDoubleFunction<T> function) {
|
||||||
|
double sum = 0;
|
||||||
|
for (T t : receiver) {
|
||||||
|
sum += function.applyAsDouble(t);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some groovy methods on collection
|
||||||
|
// see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Collection.html
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through this collection transforming each entry into a new value using
|
||||||
|
* the function, returning a list of transformed values.
|
||||||
|
*/
|
||||||
|
public static <T,U> List<U> collect(Collection<T> receiver, Function<T,U> function) {
|
||||||
|
List<U> list = new ArrayList<>();
|
||||||
|
for (T t : receiver) {
|
||||||
|
list.add(function.apply(t));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through this collection transforming each entry into a new value using
|
||||||
|
* the function, adding the values to the specified collection.
|
||||||
|
*/
|
||||||
|
public static <T,U> Object collect(Collection<T> receiver, Collection<U> collection, Function<T,U> function) {
|
||||||
|
for (T t : receiver) {
|
||||||
|
collection.add(function.apply(t));
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first value matching the predicate, or returns null.
|
||||||
|
*/
|
||||||
|
public static <T> T find(Collection<T> receiver, Predicate<T> predicate) {
|
||||||
|
for (T t : receiver) {
|
||||||
|
if (predicate.test(t)) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all values matching the predicate, returns as a list
|
||||||
|
*/
|
||||||
|
public static <T> List<T> findAll(Collection<T> receiver, Predicate<T> predicate) {
|
||||||
|
List<T> list = new ArrayList<>();
|
||||||
|
for (T t : receiver) {
|
||||||
|
if (predicate.test(t)) {
|
||||||
|
list.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the collection calling the given function for each item
|
||||||
|
* but stopping once the first non-null result is found and returning that result.
|
||||||
|
* If all results are null, null is returned.
|
||||||
|
*/
|
||||||
|
public static <T,U> Object findResult(Collection<T> receiver, Function<T,U> function) {
|
||||||
|
return findResult(receiver, null, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the collection calling the given function for each item
|
||||||
|
* but stopping once the first non-null result is found and returning that result.
|
||||||
|
* If all results are null, defaultResult is returned.
|
||||||
|
*/
|
||||||
|
public static <T,U> Object findResult(Collection<T> receiver, Object defaultResult, Function<T,U> function) {
|
||||||
|
for (T t : receiver) {
|
||||||
|
U value = function.apply(t);
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits all items into two collections based on the predicate.
|
||||||
|
* The first list contains all items which match the closure expression. The second list all those that don't.
|
||||||
|
*/
|
||||||
|
public static <T> List<List<T>> split(Collection<T> receiver, Predicate<T> predicate) {
|
||||||
|
List<T> matched = new ArrayList<>();
|
||||||
|
List<T> unmatched = new ArrayList<>();
|
||||||
|
List<List<T>> result = new ArrayList<>(2);
|
||||||
|
result.add(matched);
|
||||||
|
result.add(unmatched);
|
||||||
|
for (T t : receiver) {
|
||||||
|
if (predicate.test(t)) {
|
||||||
|
matched.add(t);
|
||||||
|
} else {
|
||||||
|
unmatched.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some groovy methods on map
|
||||||
|
// see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Map.html
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through this map transforming each entry into a new value using
|
||||||
|
* the function, returning a list of transformed values.
|
||||||
|
*/
|
||||||
|
public static <K,V,T> List<T> collect(Map<K,V> receiver, BiFunction<K,V,T> function) {
|
||||||
|
List<T> list = new ArrayList<>();
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
list.add(function.apply(kvPair.getKey(), kvPair.getValue()));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through this map transforming each entry into a new value using
|
||||||
|
* the function, adding the values to the specified collection.
|
||||||
|
*/
|
||||||
|
public static <K,V,T> Object collect(Map<K,V> receiver, Collection<T> collection, BiFunction<K,V,T> function) {
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
collection.add(function.apply(kvPair.getKey(), kvPair.getValue()));
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Counts the number of occurrences which satisfy the given predicate from inside this Map */
|
||||||
|
public static <K,V> int count(Map<K,V> receiver, BiPredicate<K,V> predicate) {
|
||||||
|
int count = 0;
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Iterates through a Map, passing each item to the given consumer. */
|
||||||
|
public static <K,V> Object each(Map<K,V> receiver, BiConsumer<K,V> consumer) {
|
||||||
|
receiver.forEach(consumer);
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine if the given predicate is valid (i.e. returns true for all items in this map).
|
||||||
|
*/
|
||||||
|
public static <K,V> boolean every(Map<K,V> receiver, BiPredicate<K,V> predicate) {
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
if (predicate.test(kvPair.getKey(), kvPair.getValue()) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first entry matching the predicate, or returns null.
|
||||||
|
*/
|
||||||
|
public static <K,V> Map.Entry<K,V> find(Map<K,V> receiver, BiPredicate<K,V> predicate) {
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
|
||||||
|
return kvPair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all values matching the predicate, returns as a map.
|
||||||
|
*/
|
||||||
|
public static <K,V> Map<K,V> findAll(Map<K,V> receiver, BiPredicate<K,V> predicate) {
|
||||||
|
// try to preserve some properties of the receiver (see the groovy javadocs)
|
||||||
|
final Map<K,V> map;
|
||||||
|
if (receiver instanceof TreeMap) {
|
||||||
|
map = new TreeMap<>();
|
||||||
|
} else {
|
||||||
|
map = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
|
||||||
|
map.put(kvPair.getKey(), kvPair.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the map calling the given function for each item
|
||||||
|
* but stopping once the first non-null result is found and returning that result.
|
||||||
|
* If all results are null, null is returned.
|
||||||
|
*/
|
||||||
|
public static <K,V,T> Object findResult(Map<K,V> receiver, BiFunction<K,V,T> function) {
|
||||||
|
return findResult(receiver, null, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the map calling the given function for each item
|
||||||
|
* but stopping once the first non-null result is found and returning that result.
|
||||||
|
* If all results are null, defaultResult is returned.
|
||||||
|
*/
|
||||||
|
public static <K,V,T> Object findResult(Map<K,V> receiver, Object defaultResult, BiFunction<K,V,T> function) {
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
T value = function.apply(kvPair.getKey(), kvPair.getValue());
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the map transforming items using the supplied function and
|
||||||
|
* collecting any non-null results.
|
||||||
|
*/
|
||||||
|
public static <K,V,T> List<T> findResults(Map<K,V> receiver, BiFunction<K,V,T> filter) {
|
||||||
|
List<T> list = new ArrayList<>();
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
T result = filter.apply(kvPair.getKey(), kvPair.getValue());
|
||||||
|
if (result != null) {
|
||||||
|
list.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts all Map members into groups determined by the supplied mapping function.
|
||||||
|
*/
|
||||||
|
public static <K,V,T> Map<T,Map<K,V>> groupBy(Map<K,V> receiver, BiFunction<K,V,T> mapper) {
|
||||||
|
Map<T,Map<K,V>> map = new LinkedHashMap<>();
|
||||||
|
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
|
||||||
|
T mapped = mapper.apply(kvPair.getKey(), kvPair.getValue());
|
||||||
|
Map<K,V> results = map.get(mapped);
|
||||||
|
if (results == null) {
|
||||||
|
// try to preserve some properties of the receiver (see the groovy javadocs)
|
||||||
|
if (receiver instanceof TreeMap) {
|
||||||
|
results = new TreeMap<>();
|
||||||
|
} else {
|
||||||
|
results = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
map.put(mapped, results);
|
||||||
|
}
|
||||||
|
results.put(kvPair.getKey(), kvPair.getValue());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
@ -350,10 +350,10 @@ public final class Def {
|
|||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
|
throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
|
||||||
}
|
}
|
||||||
ref = new FunctionRef(clazz, interfaceMethod, handle, captures);
|
ref = new FunctionRef(clazz, interfaceMethod, handle, captures.length);
|
||||||
} else {
|
} else {
|
||||||
// whitelist lookup
|
// whitelist lookup
|
||||||
ref = new FunctionRef(clazz, type, call, captures);
|
ref = new FunctionRef(clazz, type, call, captures.length);
|
||||||
}
|
}
|
||||||
final CallSite callSite;
|
final CallSite callSite;
|
||||||
if (ref.needsBridges()) {
|
if (ref.needsBridges()) {
|
||||||
|
@ -186,15 +186,17 @@ public final class Definition {
|
|||||||
public static class Method {
|
public static class Method {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final Struct owner;
|
public final Struct owner;
|
||||||
|
public final boolean augmentation;
|
||||||
public final Type rtn;
|
public final Type rtn;
|
||||||
public final List<Type> arguments;
|
public final List<Type> arguments;
|
||||||
public final org.objectweb.asm.commons.Method method;
|
public final org.objectweb.asm.commons.Method method;
|
||||||
public final int modifiers;
|
public final int modifiers;
|
||||||
public final MethodHandle handle;
|
public final MethodHandle handle;
|
||||||
|
|
||||||
public Method(String name, Struct owner, Type rtn, List<Type> arguments,
|
public Method(String name, Struct owner, boolean augmentation, Type rtn, List<Type> arguments,
|
||||||
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
|
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.augmentation = augmentation;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.rtn = rtn;
|
this.rtn = rtn;
|
||||||
this.arguments = Collections.unmodifiableList(arguments);
|
this.arguments = Collections.unmodifiableList(arguments);
|
||||||
@ -217,7 +219,15 @@ public final class Definition {
|
|||||||
// otherwise compute it
|
// otherwise compute it
|
||||||
final Class<?> params[];
|
final Class<?> params[];
|
||||||
final Class<?> returnValue;
|
final Class<?> returnValue;
|
||||||
if (Modifier.isStatic(modifiers)) {
|
if (augmentation) {
|
||||||
|
// static method disguised as virtual/interface method
|
||||||
|
params = new Class<?>[1 + arguments.size()];
|
||||||
|
params[0] = Augmentation.class;
|
||||||
|
for (int i = 0; i < arguments.size(); i++) {
|
||||||
|
params[i + 1] = arguments.get(i).clazz;
|
||||||
|
}
|
||||||
|
returnValue = rtn.clazz;
|
||||||
|
} else if (Modifier.isStatic(modifiers)) {
|
||||||
// static method: straightforward copy
|
// static method: straightforward copy
|
||||||
params = new Class<?>[arguments.size()];
|
params = new Class<?>[arguments.size()];
|
||||||
for (int i = 0; i < arguments.size(); i++) {
|
for (int i = 0; i < arguments.size(); i++) {
|
||||||
@ -242,6 +252,24 @@ public final class Definition {
|
|||||||
}
|
}
|
||||||
return MethodType.methodType(returnValue, params);
|
return MethodType.methodType(returnValue, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void write(MethodWriter writer) {
|
||||||
|
final org.objectweb.asm.Type type;
|
||||||
|
if (augmentation) {
|
||||||
|
assert java.lang.reflect.Modifier.isStatic(modifiers);
|
||||||
|
type = WriterConstants.AUGMENTATION_TYPE;
|
||||||
|
} else {
|
||||||
|
type = owner.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (java.lang.reflect.Modifier.isStatic(modifiers)) {
|
||||||
|
writer.invokeStatic(type, method);
|
||||||
|
} else if (java.lang.reflect.Modifier.isInterface(owner.clazz.getModifiers())) {
|
||||||
|
writer.invokeInterface(type, method);
|
||||||
|
} else {
|
||||||
|
writer.invokeVirtual(type, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Field {
|
public static final class Field {
|
||||||
@ -690,7 +718,7 @@ public final class Definition {
|
|||||||
" with arguments " + Arrays.toString(classes) + ".");
|
" with arguments " + Arrays.toString(classes) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
final Method constructor = new Method(name, owner, returnType, Arrays.asList(args), asm, reflect.getModifiers(), handle);
|
final Method constructor = new Method(name, owner, false, returnType, Arrays.asList(args), asm, reflect.getModifiers(), handle);
|
||||||
|
|
||||||
owner.constructors.put(methodKey, constructor);
|
owner.constructors.put(methodKey, constructor);
|
||||||
}
|
}
|
||||||
@ -734,24 +762,20 @@ public final class Definition {
|
|||||||
}
|
}
|
||||||
addConstructorInternal(className, "<init>", args);
|
addConstructorInternal(className, "<init>", args);
|
||||||
} else {
|
} else {
|
||||||
if (methodName.indexOf('/') >= 0) {
|
if (methodName.indexOf("*") >= 0) {
|
||||||
String nameAndAlias[] = methodName.split("/");
|
addMethodInternal(className, methodName.substring(0, methodName.length() - 1), true, rtn, args);
|
||||||
if (nameAndAlias.length != 2) {
|
|
||||||
throw new IllegalArgumentException("Currently only two aliases are allowed!");
|
|
||||||
}
|
|
||||||
addMethodInternal(className, nameAndAlias[0], nameAndAlias[1], rtn, args);
|
|
||||||
} else {
|
} else {
|
||||||
addMethodInternal(className, methodName, null, rtn, args);
|
addMethodInternal(className, methodName, false, rtn, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// field
|
// field
|
||||||
addFieldInternal(className, elements[1], null, rtn);
|
addFieldInternal(className, elements[1], rtn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final void addMethodInternal(final String struct, final String name, final String alias,
|
private final void addMethodInternal(String struct, String name, boolean augmentation,
|
||||||
final Type rtn, final Type[] args) {
|
Type rtn, Type[] args) {
|
||||||
final Struct owner = structsMap.get(struct);
|
final Struct owner = structsMap.get(struct);
|
||||||
|
|
||||||
if (owner == null) {
|
if (owner == null) {
|
||||||
@ -777,20 +801,32 @@ public final class Definition {
|
|||||||
"Duplicate method signature [" + methodKey + "] found within the struct [" + owner.name + "].");
|
"Duplicate method signature [" + methodKey + "] found within the struct [" + owner.name + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
final Class<?>[] classes = new Class<?>[args.length];
|
final Class<?> implClass;
|
||||||
|
final Class<?>[] params;
|
||||||
|
|
||||||
for (int count = 0; count < classes.length; ++count) {
|
if (augmentation == false) {
|
||||||
classes[count] = args[count].clazz;
|
implClass = owner.clazz;
|
||||||
|
params = new Class<?>[args.length];
|
||||||
|
for (int count = 0; count < args.length; ++count) {
|
||||||
|
params[count] = args[count].clazz;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
implClass = Augmentation.class;
|
||||||
|
params = new Class<?>[args.length + 1];
|
||||||
|
params[0] = owner.clazz;
|
||||||
|
for (int count = 0; count < args.length; ++count) {
|
||||||
|
params[count+1] = args[count].clazz;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final java.lang.reflect.Method reflect;
|
final java.lang.reflect.Method reflect;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reflect = owner.clazz.getMethod(alias == null ? name : alias, classes);
|
reflect = implClass.getMethod(name, params);
|
||||||
} catch (final NoSuchMethodException exception) {
|
} catch (NoSuchMethodException exception) {
|
||||||
throw new IllegalArgumentException("Method [" + (alias == null ? name : alias) +
|
throw new IllegalArgumentException("Method [" + name +
|
||||||
"] not found for class [" + owner.clazz.getName() + "]" +
|
"] not found for class [" + implClass.getName() + "]" +
|
||||||
" with arguments " + Arrays.toString(classes) + ".");
|
" with arguments " + Arrays.toString(params) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reflect.getReturnType().equals(rtn.clazz)) {
|
if (!reflect.getReturnType().equals(rtn.clazz)) {
|
||||||
@ -805,25 +841,24 @@ public final class Definition {
|
|||||||
MethodHandle handle;
|
MethodHandle handle;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
handle = MethodHandles.publicLookup().in(owner.clazz).unreflect(reflect);
|
handle = MethodHandles.publicLookup().in(implClass).unreflect(reflect);
|
||||||
} catch (final IllegalAccessException exception) {
|
} catch (final IllegalAccessException exception) {
|
||||||
throw new IllegalArgumentException("Method [" + (alias == null ? name : alias) + "]" +
|
throw new IllegalArgumentException("Method [" + name + "]" +
|
||||||
" not found for class [" + owner.clazz.getName() + "]" +
|
" not found for class [" + implClass.getName() + "]" +
|
||||||
" with arguments " + Arrays.toString(classes) + ".");
|
" with arguments " + Arrays.toString(params) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
final int modifiers = reflect.getModifiers();
|
final int modifiers = reflect.getModifiers();
|
||||||
final Method method = new Method(name, owner, rtn, Arrays.asList(args), asm, modifiers, handle);
|
final Method method = new Method(name, owner, augmentation, rtn, Arrays.asList(args), asm, modifiers, handle);
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isStatic(modifiers)) {
|
if (augmentation == false && java.lang.reflect.Modifier.isStatic(modifiers)) {
|
||||||
owner.staticMethods.put(methodKey, method);
|
owner.staticMethods.put(methodKey, method);
|
||||||
} else {
|
} else {
|
||||||
owner.methods.put(methodKey, method);
|
owner.methods.put(methodKey, method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final void addFieldInternal(final String struct, final String name, final String alias,
|
private final void addFieldInternal(String struct, String name, Type type) {
|
||||||
final Type type) {
|
|
||||||
final Struct owner = structsMap.get(struct);
|
final Struct owner = structsMap.get(struct);
|
||||||
|
|
||||||
if (owner == null) {
|
if (owner == null) {
|
||||||
@ -844,9 +879,9 @@ public final class Definition {
|
|||||||
java.lang.reflect.Field reflect;
|
java.lang.reflect.Field reflect;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reflect = owner.clazz.getField(alias == null ? name : alias);
|
reflect = owner.clazz.getField(name);
|
||||||
} catch (final NoSuchFieldException exception) {
|
} catch (final NoSuchFieldException exception) {
|
||||||
throw new IllegalArgumentException("Field [" + (alias == null ? name : alias) + "]" +
|
throw new IllegalArgumentException("Field [" + name + "]" +
|
||||||
" not found for class [" + owner.clazz.getName() + "].");
|
" not found for class [" + owner.clazz.getName() + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,7 +897,7 @@ public final class Definition {
|
|||||||
setter = MethodHandles.publicLookup().unreflectSetter(reflect);
|
setter = MethodHandles.publicLookup().unreflectSetter(reflect);
|
||||||
}
|
}
|
||||||
} catch (final IllegalAccessException exception) {
|
} catch (final IllegalAccessException exception) {
|
||||||
throw new IllegalArgumentException("Getter/Setter [" + (alias == null ? name : alias) + "]" +
|
throw new IllegalArgumentException("Getter/Setter [" + name + "]" +
|
||||||
" not found for class [" + owner.clazz.getName() + "].");
|
" not found for class [" + owner.clazz.getName() + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -875,9 +910,9 @@ public final class Definition {
|
|||||||
" within the struct [" + owner.name + "] is not final.");
|
" within the struct [" + owner.name + "] is not final.");
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.staticMembers.put(alias == null ? name : alias, field);
|
owner.staticMembers.put(name, field);
|
||||||
} else {
|
} else {
|
||||||
owner.members.put(alias == null ? name : alias, field);
|
owner.members.put(name, field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -915,11 +950,24 @@ public final class Definition {
|
|||||||
// https://bugs.openjdk.java.net/browse/JDK-8072746
|
// https://bugs.openjdk.java.net/browse/JDK-8072746
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
Class<?> arguments[] = new Class<?>[method.arguments.size()];
|
// TODO: we *have* to remove all these public members and use getter methods to encapsulate!
|
||||||
for (int i = 0; i < method.arguments.size(); i++) {
|
final Class<?> impl;
|
||||||
arguments[i] = method.arguments.get(i).clazz;
|
final Class<?> arguments[];
|
||||||
|
if (method.augmentation) {
|
||||||
|
impl = Augmentation.class;
|
||||||
|
arguments = new Class<?>[method.arguments.size() + 1];
|
||||||
|
arguments[0] = method.owner.clazz;
|
||||||
|
for (int i = 0; i < method.arguments.size(); i++) {
|
||||||
|
arguments[i + 1] = method.arguments.get(i).clazz;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
impl = owner.clazz;
|
||||||
|
arguments = new Class<?>[method.arguments.size()];
|
||||||
|
for (int i = 0; i < method.arguments.size(); i++) {
|
||||||
|
arguments[i] = method.arguments.get(i).clazz;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
java.lang.reflect.Method m = owner.clazz.getMethod(method.method.getName(), arguments);
|
java.lang.reflect.Method m = impl.getMethod(method.method.getName(), arguments);
|
||||||
if (m.getReturnType() != method.rtn.clazz) {
|
if (m.getReturnType() != method.rtn.clazz) {
|
||||||
throw new IllegalStateException("missing covariant override for: " + m + " in " + owner.name);
|
throw new IllegalStateException("missing covariant override for: " + m + " in " + owner.name);
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,10 @@ public class FunctionRef {
|
|||||||
* @param expected interface type to implement.
|
* @param expected interface type to implement.
|
||||||
* @param type the left hand side of a method reference expression
|
* @param type the left hand side of a method reference expression
|
||||||
* @param call the right hand side of a method reference expression
|
* @param call the right hand side of a method reference expression
|
||||||
* @param captures captured arguments
|
* @param numCaptures number of captured arguments
|
||||||
*/
|
*/
|
||||||
public FunctionRef(Definition.Type expected, String type, String call, Class<?>... captures) {
|
public FunctionRef(Definition.Type expected, String type, String call, int numCaptures) {
|
||||||
this(expected, expected.struct.getFunctionalMethod(), lookup(expected, type, call, captures.length > 0), captures);
|
this(expected, expected.struct.getFunctionalMethod(), lookup(expected, type, call, numCaptures > 0), numCaptures);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,13 +64,16 @@ public class FunctionRef {
|
|||||||
* @param expected interface type to implement
|
* @param expected interface type to implement
|
||||||
* @param method functional interface method
|
* @param method functional interface method
|
||||||
* @param impl implementation method
|
* @param impl implementation method
|
||||||
* @param captures captured arguments
|
* @param numCaptures number of captured arguments
|
||||||
*/
|
*/
|
||||||
public FunctionRef(Definition.Type expected, Definition.Method method, Definition.Method impl, Class<?>... captures) {
|
public FunctionRef(Definition.Type expected, Definition.Method method, Definition.Method impl, int numCaptures) {
|
||||||
// e.g. compareTo
|
// e.g. compareTo
|
||||||
invokedName = method.name;
|
invokedName = method.name;
|
||||||
// e.g. (Object)Comparator
|
// e.g. (Object)Comparator
|
||||||
invokedType = MethodType.methodType(expected.clazz, captures);
|
MethodType implType = impl.getMethodType();
|
||||||
|
// only include captured parameters as arguments
|
||||||
|
invokedType = MethodType.methodType(expected.clazz,
|
||||||
|
implType.dropParameterTypes(numCaptures, implType.parameterCount()));
|
||||||
// e.g. (Object,Object)int
|
// e.g. (Object,Object)int
|
||||||
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);
|
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);
|
||||||
|
|
||||||
@ -90,6 +93,9 @@ public class FunctionRef {
|
|||||||
// owner == null: script class itself
|
// owner == null: script class itself
|
||||||
ownerIsInterface = false;
|
ownerIsInterface = false;
|
||||||
owner = WriterConstants.CLASS_TYPE.getInternalName();
|
owner = WriterConstants.CLASS_TYPE.getInternalName();
|
||||||
|
} else if (impl.augmentation) {
|
||||||
|
ownerIsInterface = false;
|
||||||
|
owner = WriterConstants.AUGMENTATION_TYPE.getInternalName();
|
||||||
} else {
|
} else {
|
||||||
ownerIsInterface = impl.owner.clazz.isInterface();
|
ownerIsInterface = impl.owner.clazz.isInterface();
|
||||||
owner = impl.owner.type.getInternalName();
|
owner = impl.owner.type.getInternalName();
|
||||||
@ -98,7 +104,7 @@ public class FunctionRef {
|
|||||||
implMethod = impl.handle;
|
implMethod = impl.handle;
|
||||||
|
|
||||||
// remove any prepended captured arguments for the 'natural' signature.
|
// remove any prepended captured arguments for the 'natural' signature.
|
||||||
samMethodType = adapt(interfaceMethodType, impl.getMethodType().dropParameterTypes(0, captures.length));
|
samMethodType = adapt(interfaceMethodType, impl.getMethodType().dropParameterTypes(0, numCaptures));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,11 +112,14 @@ public class FunctionRef {
|
|||||||
* <p>
|
* <p>
|
||||||
* This will <b>not</b> set implMethodASM. It is for runtime use only.
|
* This will <b>not</b> set implMethodASM. It is for runtime use only.
|
||||||
*/
|
*/
|
||||||
public FunctionRef(Definition.Type expected, Definition.Method method, MethodHandle impl, Class<?>... captures) {
|
public FunctionRef(Definition.Type expected, Definition.Method method, MethodHandle impl, int numCaptures) {
|
||||||
// e.g. compareTo
|
// e.g. compareTo
|
||||||
invokedName = method.name;
|
invokedName = method.name;
|
||||||
// e.g. (Object)Comparator
|
// e.g. (Object)Comparator
|
||||||
invokedType = MethodType.methodType(expected.clazz, captures);
|
MethodType implType = impl.type();
|
||||||
|
// only include captured parameters as arguments
|
||||||
|
invokedType = MethodType.methodType(expected.clazz,
|
||||||
|
implType.dropParameterTypes(numCaptures, implType.parameterCount()));
|
||||||
// e.g. (Object,Object)int
|
// e.g. (Object,Object)int
|
||||||
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);
|
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);
|
||||||
|
|
||||||
@ -119,7 +128,7 @@ public class FunctionRef {
|
|||||||
implMethodASM = null;
|
implMethodASM = null;
|
||||||
|
|
||||||
// remove any prepended captured arguments for the 'natural' signature.
|
// remove any prepended captured arguments for the 'natural' signature.
|
||||||
samMethodType = adapt(interfaceMethodType, impl.type().dropParameterTypes(0, captures.length));
|
samMethodType = adapt(interfaceMethodType, impl.type().dropParameterTypes(0, numCaptures));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +73,8 @@ public final class WriterConstants {
|
|||||||
|
|
||||||
public final static Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
|
public final static Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
|
||||||
|
|
||||||
|
public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there
|
* A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there
|
||||||
* so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building regexes
|
* so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building regexes
|
||||||
|
@ -76,7 +76,7 @@ public class ECapturingFunctionRef extends AExpression implements ILambda {
|
|||||||
// static case
|
// static case
|
||||||
if (captured.type.sort != Definition.Sort.DEF) {
|
if (captured.type.sort != Definition.Sort.DEF) {
|
||||||
try {
|
try {
|
||||||
ref = new FunctionRef(expected, captured.type.name, call, captured.type.clazz);
|
ref = new FunctionRef(expected, captured.type.name, call, 1);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw createError(e);
|
throw createError(e);
|
||||||
}
|
}
|
||||||
|
@ -76,10 +76,10 @@ public class EFunctionRef extends AExpression implements ILambda {
|
|||||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||||
"to [" + expected.name + "], function not found");
|
"to [" + expected.name + "], function not found");
|
||||||
}
|
}
|
||||||
ref = new FunctionRef(expected, interfaceMethod, implMethod);
|
ref = new FunctionRef(expected, interfaceMethod, implMethod, 0);
|
||||||
} else {
|
} else {
|
||||||
// whitelist lookup
|
// whitelist lookup
|
||||||
ref = new FunctionRef(expected, type, call);
|
ref = new FunctionRef(expected, type, call, 0);
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw createError(e);
|
throw createError(e);
|
||||||
|
@ -175,11 +175,7 @@ public class ELambda extends AExpression implements ILambda {
|
|||||||
} else {
|
} else {
|
||||||
defPointer = null;
|
defPointer = null;
|
||||||
try {
|
try {
|
||||||
Class<?> captureClasses[] = new Class<?>[captures.size()];
|
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captures.size());
|
||||||
for (int i = 0; i < captures.size(); i++) {
|
|
||||||
captureClasses[i] = captures.get(i).type.clazz;
|
|
||||||
}
|
|
||||||
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captureClasses);
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw createError(e);
|
throw createError(e);
|
||||||
}
|
}
|
||||||
|
@ -123,13 +123,7 @@ public final class LCallInvoke extends ALink {
|
|||||||
argument.write(writer, globals);
|
argument.write(writer, globals);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isStatic(method.modifiers)) {
|
method.write(writer);
|
||||||
writer.invokeStatic(method.owner.type, method.method);
|
|
||||||
} else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) {
|
|
||||||
writer.invokeInterface(method.owner.type, method.method);
|
|
||||||
} else {
|
|
||||||
writer.invokeVirtual(method.owner.type, method.method);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,11 +92,7 @@ final class LListShortcut extends ALink {
|
|||||||
void load(MethodWriter writer, Globals globals) {
|
void load(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) {
|
getter.write(writer);
|
||||||
writer.invokeInterface(getter.owner.type, getter.method);
|
|
||||||
} else {
|
|
||||||
writer.invokeVirtual(getter.owner.type, getter.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getter.rtn.clazz.equals(getter.handle.type().returnType())) {
|
if (!getter.rtn.clazz.equals(getter.handle.type().returnType())) {
|
||||||
writer.checkCast(getter.rtn.type);
|
writer.checkCast(getter.rtn.type);
|
||||||
@ -107,11 +103,7 @@ final class LListShortcut extends ALink {
|
|||||||
void store(MethodWriter writer, Globals globals) {
|
void store(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isInterface(setter.owner.clazz.getModifiers())) {
|
setter.write(writer);
|
||||||
writer.invokeInterface(setter.owner.type, setter.method);
|
|
||||||
} else {
|
|
||||||
writer.invokeVirtual(setter.owner.type, setter.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writePop(setter.rtn.sort.size);
|
writer.writePop(setter.rtn.sort.size);
|
||||||
}
|
}
|
||||||
|
@ -91,11 +91,7 @@ final class LMapShortcut extends ALink {
|
|||||||
void load(MethodWriter writer, Globals globals) {
|
void load(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) {
|
getter.write(writer);
|
||||||
writer.invokeInterface(getter.owner.type, getter.method);
|
|
||||||
} else {
|
|
||||||
writer.invokeVirtual(getter.owner.type, getter.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getter.rtn.clazz.equals(getter.handle.type().returnType())) {
|
if (!getter.rtn.clazz.equals(getter.handle.type().returnType())) {
|
||||||
writer.checkCast(getter.rtn.type);
|
writer.checkCast(getter.rtn.type);
|
||||||
@ -106,11 +102,7 @@ final class LMapShortcut extends ALink {
|
|||||||
void store(MethodWriter writer, Globals globals) {
|
void store(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isInterface(setter.owner.clazz.getModifiers())) {
|
setter.write(writer);
|
||||||
writer.invokeInterface(setter.owner.type, setter.method);
|
|
||||||
} else {
|
|
||||||
writer.invokeVirtual(setter.owner.type, setter.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writePop(setter.rtn.sort.size);
|
writer.writePop(setter.rtn.sort.size);
|
||||||
}
|
}
|
||||||
|
@ -95,11 +95,7 @@ final class LShortcut extends ALink {
|
|||||||
void load(MethodWriter writer, Globals globals) {
|
void load(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) {
|
getter.write(writer);
|
||||||
writer.invokeInterface(getter.owner.type, getter.method);
|
|
||||||
} else {
|
|
||||||
writer.invokeVirtual(getter.owner.type, getter.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getter.rtn.clazz.equals(getter.handle.type().returnType())) {
|
if (!getter.rtn.clazz.equals(getter.handle.type().returnType())) {
|
||||||
writer.checkCast(getter.rtn.type);
|
writer.checkCast(getter.rtn.type);
|
||||||
@ -110,11 +106,7 @@ final class LShortcut extends ALink {
|
|||||||
void store(MethodWriter writer, Globals globals) {
|
void store(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isInterface(setter.owner.clazz.getModifiers())) {
|
setter.write(writer);
|
||||||
writer.invokeInterface(setter.owner.type, setter.method);
|
|
||||||
} else {
|
|
||||||
writer.invokeVirtual(setter.owner.type, setter.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writePop(setter.rtn.sort.size);
|
writer.writePop(setter.rtn.sort.size);
|
||||||
}
|
}
|
||||||
|
@ -206,10 +206,8 @@ public class SEach extends AStatement {
|
|||||||
Type itr = Definition.getType("Iterator");
|
Type itr = Definition.getType("Iterator");
|
||||||
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(itr.type, Definition.DEF_TYPE.type);
|
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(itr.type, Definition.DEF_TYPE.type);
|
||||||
writer.invokeDefCall("iterator", methodType, DefBootstrap.ITERATOR);
|
writer.invokeDefCall("iterator", methodType, DefBootstrap.ITERATOR);
|
||||||
} else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) {
|
|
||||||
writer.invokeInterface(method.owner.type, method.method);
|
|
||||||
} else {
|
} else {
|
||||||
writer.invokeVirtual(method.owner.type, method.method);
|
method.write(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.visitVarInsn(iterator.type.type.getOpcode(Opcodes.ISTORE), iterator.getSlot());
|
writer.visitVarInsn(iterator.type.type.getOpcode(Opcodes.ISTORE), iterator.getSlot());
|
||||||
|
@ -114,7 +114,7 @@ public class SFunction extends AStatement {
|
|||||||
|
|
||||||
org.objectweb.asm.commons.Method method =
|
org.objectweb.asm.commons.Method method =
|
||||||
new org.objectweb.asm.commons.Method(name, MethodType.methodType(rtnType.clazz, paramClasses).toMethodDescriptorString());
|
new org.objectweb.asm.commons.Method(name, MethodType.methodType(rtnType.clazz, paramClasses).toMethodDescriptorString());
|
||||||
this.method = new Method(name, null, rtnType, paramTypes, method, Modifier.STATIC | Modifier.PRIVATE, null);
|
this.method = new Method(name, null, false, rtnType, paramTypes, method, Modifier.STATIC | Modifier.PRIVATE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -50,6 +50,15 @@ class Iterable -> java.lang.Iterable {
|
|||||||
void forEach(Consumer)
|
void forEach(Consumer)
|
||||||
Iterator iterator()
|
Iterator iterator()
|
||||||
Spliterator spliterator()
|
Spliterator spliterator()
|
||||||
|
# some adaptations of groovy methods
|
||||||
|
boolean any*(Predicate)
|
||||||
|
def each*(Consumer)
|
||||||
|
def eachWithIndex*(ObjIntConsumer)
|
||||||
|
boolean every*(Predicate)
|
||||||
|
List findResults*(Function)
|
||||||
|
Map groupBy*(Function)
|
||||||
|
String join*(String)
|
||||||
|
double sum*(ToDoubleFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Readable: i/o
|
# Readable: i/o
|
||||||
|
@ -42,7 +42,7 @@ class Matcher -> java.util.regex.Matcher extends Object {
|
|||||||
boolean find(int)
|
boolean find(int)
|
||||||
String group()
|
String group()
|
||||||
String group(int)
|
String group(int)
|
||||||
String namedGroup/group(String)
|
String namedGroup*(String)
|
||||||
int groupCount()
|
int groupCount()
|
||||||
boolean hasAnchoringBounds()
|
boolean hasAnchoringBounds()
|
||||||
boolean hasTransparentBounds()
|
boolean hasTransparentBounds()
|
||||||
|
@ -39,6 +39,15 @@ class Collection -> java.util.Collection extends Iterable {
|
|||||||
Stream stream()
|
Stream stream()
|
||||||
def[] toArray()
|
def[] toArray()
|
||||||
def[] toArray(def[])
|
def[] toArray(def[])
|
||||||
|
|
||||||
|
# some adaptations of groovy methods
|
||||||
|
List collect*(Function)
|
||||||
|
def collect*(Collection,Function)
|
||||||
|
def find*(Predicate)
|
||||||
|
List findAll*(Predicate)
|
||||||
|
def findResult*(Function)
|
||||||
|
def findResult*(def,Function)
|
||||||
|
List split*(Predicate)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Comparator -> java.util.Comparator {
|
class Comparator -> java.util.Comparator {
|
||||||
@ -114,8 +123,7 @@ class List -> java.util.List extends Collection,Iterable {
|
|||||||
def remove(int)
|
def remove(int)
|
||||||
void replaceAll(UnaryOperator)
|
void replaceAll(UnaryOperator)
|
||||||
def set(int,def)
|
def set(int,def)
|
||||||
# TODO: wtf?
|
int getLength*()
|
||||||
int getLength/size()
|
|
||||||
void sort(Comparator)
|
void sort(Comparator)
|
||||||
List subList(int,int)
|
List subList(int,int)
|
||||||
}
|
}
|
||||||
@ -153,6 +161,19 @@ class Map -> java.util.Map {
|
|||||||
void replaceAll(BiFunction)
|
void replaceAll(BiFunction)
|
||||||
int size()
|
int size()
|
||||||
Collection values()
|
Collection values()
|
||||||
|
|
||||||
|
# some adaptations of groovy methods
|
||||||
|
List collect*(BiFunction)
|
||||||
|
def collect*(Collection,BiFunction)
|
||||||
|
int count*(BiPredicate)
|
||||||
|
def each*(BiConsumer)
|
||||||
|
boolean every*(BiPredicate)
|
||||||
|
Map.Entry find*(BiPredicate)
|
||||||
|
Map findAll*(BiPredicate)
|
||||||
|
def findResult*(BiFunction)
|
||||||
|
def findResult*(def,BiFunction)
|
||||||
|
List findResults*(BiFunction)
|
||||||
|
Map groupBy*(BiFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Map.Entry -> java.util.Map$Entry {
|
class Map.Entry -> java.util.Map$Entry {
|
||||||
|
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class AugmentationTests extends ScriptTestCase {
|
||||||
|
|
||||||
|
public void testStatic() {
|
||||||
|
assertEquals(1, exec("ArrayList l = new ArrayList(); l.add(1); return l.getLength();"));
|
||||||
|
assertEquals(1, exec("ArrayList l = new ArrayList(); l.add(1); return l.length;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSubclass() {
|
||||||
|
assertEquals(1, exec("List l = new ArrayList(); l.add(1); return l.getLength();"));
|
||||||
|
assertEquals(1, exec("List l = new ArrayList(); l.add(1); return l.length;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDef() {
|
||||||
|
assertEquals(1, exec("def l = new ArrayList(); l.add(1); return l.getLength();"));
|
||||||
|
assertEquals(1, exec("def l = new ArrayList(); l.add(1); return l.length;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCapturingReference() {
|
||||||
|
assertEquals(1, exec("int foo(Supplier t) { return t.get() }" +
|
||||||
|
"ArrayList l = new ArrayList(); l.add(1);" +
|
||||||
|
"return foo(l::getLength);"));
|
||||||
|
assertEquals(1, exec("int foo(Supplier t) { return t.get() }" +
|
||||||
|
"List l = new ArrayList(); l.add(1);" +
|
||||||
|
"return foo(l::getLength);"));
|
||||||
|
assertEquals(1, exec("int foo(Supplier t) { return t.get() }" +
|
||||||
|
"def l = new ArrayList(); l.add(1);" +
|
||||||
|
"return foo(l::getLength);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_Any() {
|
||||||
|
assertEquals(true,
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.any(x -> x == 1)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_Each() {
|
||||||
|
assertEquals(1,
|
||||||
|
exec("List l = new ArrayList(); l.add(1); List l2 = new ArrayList(); l.each(l2::add); return l2.size()"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_EachWithIndex() {
|
||||||
|
assertEquals(0,
|
||||||
|
exec("List l = new ArrayList(); l.add(2); Map m = new HashMap(); l.eachWithIndex(m::put); return m.get(2)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_Every() {
|
||||||
|
assertEquals(false, exec("List l = new ArrayList(); l.add(1); l.add(2); l.every(x -> x == 1)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_FindResults() {
|
||||||
|
assertEquals(1,
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); l.findResults(x -> x == 1 ? x : null).size()"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_GroupBy() {
|
||||||
|
assertEquals(2,
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(-1); l.groupBy(x -> x < 0 ? 'negative' : 'positive').size()"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_Join() {
|
||||||
|
assertEquals("test,ing",
|
||||||
|
exec("List l = new ArrayList(); l.add('test'); l.add('ing'); l.join(',')"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable_Sum() {
|
||||||
|
assertEquals(5.0D,
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); l.sum(x -> x + 1)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCollection_Collect() {
|
||||||
|
assertEquals(Arrays.asList(2, 3),
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); l.collect(x -> x + 1)"));
|
||||||
|
assertEquals(asSet(2, 3),
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); l.collect(new HashSet(), x -> x + 1)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCollection_Find() {
|
||||||
|
assertEquals(2,
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.find(x -> x == 2)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCollection_FindAll() {
|
||||||
|
assertEquals(Arrays.asList(2),
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findAll(x -> x == 2)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCollection_FindResult() {
|
||||||
|
assertEquals("found",
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findResult(x -> x > 1 ? 'found' : null)"));
|
||||||
|
assertEquals("notfound",
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findResult('notfound', x -> x > 10 ? 'found' : null)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCollection_Split() {
|
||||||
|
assertEquals(Arrays.asList(Arrays.asList(2), Arrays.asList(1)),
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.split(x -> x == 2)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_Collect() {
|
||||||
|
assertEquals(Arrays.asList("one1", "two2"),
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.collect((key,value) -> key + value)"));
|
||||||
|
assertEquals(asSet("one1", "two2"),
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.collect(new HashSet(), (key,value) -> key + value)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_Count() {
|
||||||
|
assertEquals(1,
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.count((key,value) -> value == 2)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_Each() {
|
||||||
|
assertEquals(2,
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; Map m2 = new TreeMap(); m.each(m2::put); return m2.size()"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_Every() {
|
||||||
|
assertEquals(false,
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.every((key,value) -> value == 2)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_Find() {
|
||||||
|
assertEquals("two",
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.find((key,value) -> value == 2).key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_FindAll() {
|
||||||
|
assertEquals(Collections.singletonMap("two", 2),
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.findAll((key,value) -> value == 2)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_FindResult() {
|
||||||
|
assertEquals("found",
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.findResult((key,value) -> value == 2 ? 'found' : null)"));
|
||||||
|
assertEquals("notfound",
|
||||||
|
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; " +
|
||||||
|
"return m.findResult('notfound', (key,value) -> value == 10 ? 'found' : null)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_FindResults() {
|
||||||
|
assertEquals(Arrays.asList("negative", "positive"),
|
||||||
|
exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " +
|
||||||
|
"return m.findResults((key,value) -> value < 0 ? 'negative' : 'positive')"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMap_GroupBy() {
|
||||||
|
Map<String,Map<String,Integer>> expected = new HashMap<>();
|
||||||
|
expected.put("negative", Collections.singletonMap("a", -1));
|
||||||
|
expected.put("positive", Collections.singletonMap("b", 1));
|
||||||
|
assertEquals(expected,
|
||||||
|
exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " +
|
||||||
|
"return m.groupBy((key,value) -> value < 0 ? 'negative' : 'positive')"));
|
||||||
|
}
|
||||||
|
}
|
@ -170,10 +170,24 @@ public class FunctionRefTests extends ScriptTestCase {
|
|||||||
assertTrue(expected.getMessage().contains("Unknown reference"));
|
assertTrue(expected.getMessage().contains("Unknown reference"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWrongArityNotEnough() {
|
||||||
|
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||||
|
exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);");
|
||||||
|
});
|
||||||
|
assertTrue(expected.getMessage().contains("Unknown reference"));
|
||||||
|
}
|
||||||
|
|
||||||
public void testWrongArityDef() {
|
public void testWrongArityDef() {
|
||||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||||
exec("def y = Optional.empty(); return y.orElseGet(String::startsWith);");
|
exec("def y = Optional.empty(); return y.orElseGet(String::startsWith);");
|
||||||
});
|
});
|
||||||
assertTrue(expected.getMessage().contains("Unknown reference"));
|
assertTrue(expected.getMessage().contains("Unknown reference"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWrongArityNotEnoughDef() {
|
||||||
|
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||||
|
exec("def l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);");
|
||||||
|
});
|
||||||
|
assertTrue(expected.getMessage().contains("Unknown reference"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,6 +180,22 @@ public class LambdaTests extends ScriptTestCase {
|
|||||||
assertTrue(expected.getMessage(), expected.getMessage().contains("Incorrect number of parameters"));
|
assertTrue(expected.getMessage(), expected.getMessage().contains("Incorrect number of parameters"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWrongArityNotEnough() {
|
||||||
|
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||||
|
exec("List l = new ArrayList(); l.add(1); l.add(1); "
|
||||||
|
+ "return l.stream().mapToInt(() -> 5).sum();");
|
||||||
|
});
|
||||||
|
assertTrue(expected.getMessage().contains("Incorrect number of parameters"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWrongArityNotEnoughDef() {
|
||||||
|
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||||
|
exec("def l = new ArrayList(); l.add(1); l.add(1); "
|
||||||
|
+ "return l.stream().mapToInt(() -> 5).sum();");
|
||||||
|
});
|
||||||
|
assertTrue(expected.getMessage().contains("Incorrect number of parameters"));
|
||||||
|
}
|
||||||
|
|
||||||
public void testLambdaInFunction() {
|
public void testLambdaInFunction() {
|
||||||
assertEquals(5, exec("def foo() { Optional.empty().orElseGet(() -> 5) } return foo();"));
|
assertEquals(5, exec("def foo() { Optional.empty().orElseGet(() -> 5) } return foo();"));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user