Merge branch 'master' into ccr
* master: HLRC: Move commercial clients from XPackClient (#32596) Add cluster UUID to Cluster Stats API response (#32206) Security: move User to protocol project (#32367) [TEST] Test for shard failures, add debug to testProfileMatchesRegular Minor fix for javadoc (applicable for java 11). (#32573) Painless: Move Some Lookup Logic to PainlessLookup (#32565) TEST: Avoid merges in testSeqNoAndCheckpoints [Rollup] Remove builders from HistoGroupConfig (#32533) Mutes failing SQL string function tests due to #32589 fixed elements in array of produced terms (#32519) INGEST: Enable default pipelines (#32286) Remove cluster state initial customs (#32501) Mutes LicensingDocumentationIT due to #32580 [ML] Remove multiple_bucket_spans (#32496) [ML] Rename JobProvider to JobResultsProvider (#32551) Correct minor typo in explain.asciidoc for HLRC Build: Add elastic maven to repos used by BuildPlugin (#32549) Clarify the error message when a pipeline agg is used in the 'order' parameter. (#32522) Revert "[test] turn on host io cache for opensuse (#32053)" Enable packaging tests on suse boxes [ML] Improve error when no available field exists for rule scope (#32550) [ML] Improve error for functions with limited rule condition support (#32548) Painless: Clean Up PainlessField (#32525) Add @AwaitsFix for #32554 Remove broken @link in Javadoc Scripting: Conditionally use java time api in scripting (#31441) [ML] Fix thread leak when waiting for job flush (#32196) (#32541) Add AwaitsFix to failing test - see #32546 Core: Minor size reduction for AbstractComponent (#32509) SQL: Added support for string manipulating functions with more than one parameter (#32356) [DOCS] Reloadable Secure Settings (#31713) Watcher: Reenable HttpSecretsIntegrationTests#testWebhookAction test (#32456) [Rollup] Remove builders from TermsGroupConfig (#32507) Use hostname instead of IP with SPNEGO test (#32514) Switch x-pack rolling restart to new style Requests (#32339) NETWORKING: Fix Netty Leaks by upgrading to 4.1.28 (#32511) [DOCS] Small fixes in rule configuration page (#32516) Painless: Clean up PainlessMethod (#32476) Build: Remove shadowing from benchmarks (#32475) Docs: Add all JDKs to CONTRIBUTING.md Add licensing enforcement for FIPS mode (#32437) SQL: Add test for handling of partial results (#32474) Mute testFilterCacheStats [ML][DOCS] Fix typo applied_to => applies_to Scripting: Fix painless compiler loader to know about context classes (#32385)
This commit is contained in:
commit
6eeb628d6d
|
@ -100,6 +100,12 @@ JDK 10 and testing on a JDK 8 runtime; to do this, set `RUNTIME_JAVA_HOME`
|
|||
pointing to the Java home of a JDK 8 installation. Note that this mechanism can
|
||||
be used to test against other JDKs as well, this is not only limited to JDK 8.
|
||||
|
||||
> Note: It is also required to have `JAVA7_HOME`, `JAVA8_HOME` and
|
||||
`JAVA10_HOME` available so that the tests can pass.
|
||||
|
||||
> Warning: do not use `sdkman` for Java installations which do not have proper
|
||||
`jrunscript` for jdk distributions.
|
||||
|
||||
Elasticsearch uses the Gradle wrapper for its build. You can execute Gradle
|
||||
using the wrapper via the `gradlew` script in the root of the repository.
|
||||
|
||||
|
|
|
@ -115,11 +115,6 @@ Vagrant.configure(2) do |config|
|
|||
'opensuse-42'.tap do |box|
|
||||
config.vm.define box, define_opts do |config|
|
||||
config.vm.box = 'elastic/opensuse-42-x86_64'
|
||||
|
||||
# https://github.com/elastic/elasticsearch/issues/30295
|
||||
config.vm.provider 'virtualbox' do |vbox|
|
||||
vbox.customize ['storagectl', :id, '--name', 'SATA Controller', '--hostiocache', 'on']
|
||||
end
|
||||
suse_common config, box
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,36 +4,39 @@ This directory contains the microbenchmark suite of Elasticsearch. It relies on
|
|||
|
||||
## Purpose
|
||||
|
||||
We do not want to microbenchmark everything but the kitchen sink and should typically rely on our
|
||||
[macrobenchmarks](https://elasticsearch-benchmarks.elastic.co/app/kibana#/dashboard/Nightly-Benchmark-Overview) with
|
||||
[Rally](http://github.com/elastic/rally). Microbenchmarks are intended to spot performance regressions in performance-critical components.
|
||||
We do not want to microbenchmark everything but the kitchen sink and should typically rely on our
|
||||
[macrobenchmarks](https://elasticsearch-benchmarks.elastic.co/app/kibana#/dashboard/Nightly-Benchmark-Overview) with
|
||||
[Rally](http://github.com/elastic/rally). Microbenchmarks are intended to spot performance regressions in performance-critical components.
|
||||
The microbenchmark suite is also handy for ad-hoc microbenchmarks but please remove them again before merging your PR.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Just run `gradle :benchmarks:jmh` from the project root directory. It will build all microbenchmarks, execute them and print the result.
|
||||
Just run `gradlew -p benchmarks run` from the project root
|
||||
directory. It will build all microbenchmarks, execute them and print
|
||||
the result.
|
||||
|
||||
## Running Microbenchmarks
|
||||
|
||||
Benchmarks are always run via Gradle with `gradle :benchmarks:jmh`.
|
||||
|
||||
Running via an IDE is not supported as the results are meaningless (we have no control over the JVM running the benchmarks).
|
||||
Running via an IDE is not supported as the results are meaningless
|
||||
because we have no control over the JVM running the benchmarks.
|
||||
|
||||
If you want to run a specific benchmark class, e.g. `org.elasticsearch.benchmark.MySampleBenchmark` or have special requirements
|
||||
generate the uberjar with `gradle :benchmarks:jmhJar` and run it directly with:
|
||||
If you want to run a specific benchmark class like, say,
|
||||
`MemoryStatsBenchmark`, you can use `--args`:
|
||||
|
||||
```
|
||||
java -jar benchmarks/build/distributions/elasticsearch-benchmarks-*.jar
|
||||
gradlew -p benchmarks run --args ' MemoryStatsBenchmark'
|
||||
```
|
||||
|
||||
JMH supports lots of command line parameters. Add `-h` to the command above to see the available command line options.
|
||||
Everything in the `'` gets sent on the command line to JMH. The leading ` `
|
||||
inside the `'`s is important. Without it parameters are sometimes sent to
|
||||
gradle.
|
||||
|
||||
## Adding Microbenchmarks
|
||||
|
||||
Before adding a new microbenchmark, make yourself familiar with the JMH API. You can check our existing microbenchmarks and also the
|
||||
Before adding a new microbenchmark, make yourself familiar with the JMH API. You can check our existing microbenchmarks and also the
|
||||
[JMH samples](http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/).
|
||||
|
||||
In contrast to tests, the actual name of the benchmark class is not relevant to JMH. However, stick to the naming convention and
|
||||
In contrast to tests, the actual name of the benchmark class is not relevant to JMH. However, stick to the naming convention and
|
||||
end the class name of a benchmark with `Benchmark`. To have JMH execute a benchmark, annotate the respective methods with `@Benchmark`.
|
||||
|
||||
## Tips and Best Practices
|
||||
|
@ -42,15 +45,15 @@ To get realistic results, you should exercise care when running benchmarks. Here
|
|||
|
||||
### Do
|
||||
|
||||
* Ensure that the system executing your microbenchmarks has as little load as possible. Shutdown every process that can cause unnecessary
|
||||
* Ensure that the system executing your microbenchmarks has as little load as possible. Shutdown every process that can cause unnecessary
|
||||
runtime jitter. Watch the `Error` column in the benchmark results to see the run-to-run variance.
|
||||
* Ensure to run enough warmup iterations to get the benchmark into a stable state. If you are unsure, don't change the defaults.
|
||||
* Avoid CPU migrations by pinning your benchmarks to specific CPU cores. On Linux you can use `taskset`.
|
||||
* Fix the CPU frequency to avoid Turbo Boost from kicking in and skewing your results. On Linux you can use `cpufreq-set` and the
|
||||
* Fix the CPU frequency to avoid Turbo Boost from kicking in and skewing your results. On Linux you can use `cpufreq-set` and the
|
||||
`performance` CPU governor.
|
||||
* Vary the problem input size with `@Param`.
|
||||
* Use the integrated profilers in JMH to dig deeper if benchmark results to not match your hypotheses:
|
||||
* Run the generated uberjar directly and use `-prof gc` to check whether the garbage collector runs during a microbenchmarks and skews
|
||||
* Run the generated uberjar directly and use `-prof gc` to check whether the garbage collector runs during a microbenchmarks and skews
|
||||
your results. If so, try to force a GC between runs (`-gc true`) but watch out for the caveats.
|
||||
* Use `-prof perf` or `-prof perfasm` (both only available on Linux) to see hotspots.
|
||||
* Have your benchmarks peer-reviewed.
|
||||
|
@ -59,4 +62,4 @@ To get realistic results, you should exercise care when running benchmarks. Here
|
|||
|
||||
* Blindly believe the numbers that your microbenchmark produces but verify them by measuring e.g. with `-prof perfasm`.
|
||||
* Run more threads than your number of CPU cores (in case you run multi-threaded microbenchmarks).
|
||||
* Look only at the `Score` column and ignore `Error`. Instead take countermeasures to keep `Error` low / variance explainable.
|
||||
* Look only at the `Score` column and ignore `Error`. Instead take countermeasures to keep `Error` low / variance explainable.
|
||||
|
|
|
@ -18,11 +18,8 @@
|
|||
*/
|
||||
|
||||
apply plugin: 'elasticsearch.build'
|
||||
|
||||
// order of this section matters, see: https://github.com/johnrengelman/shadow/issues/336
|
||||
apply plugin: 'application' // have the shadow plugin provide the runShadow task
|
||||
apply plugin: 'application'
|
||||
mainClassName = 'org.openjdk.jmh.Main'
|
||||
apply plugin: 'com.github.johnrengelman.shadow' // build an uberjar with all benchmarks
|
||||
|
||||
// Not published so no need to assemble
|
||||
tasks.remove(assemble)
|
||||
|
@ -50,10 +47,8 @@ compileJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-try,-u
|
|||
// needs to be added separately otherwise Gradle will quote it and javac will fail
|
||||
compileJava.options.compilerArgs.addAll(["-processor", "org.openjdk.jmh.generators.BenchmarkProcessor"])
|
||||
|
||||
forbiddenApis {
|
||||
// classes generated by JMH can use all sorts of forbidden APIs but we have no influence at all and cannot exclude these classes
|
||||
ignoreFailures = true
|
||||
}
|
||||
// classes generated by JMH can use all sorts of forbidden APIs but we have no influence at all and cannot exclude these classes
|
||||
forbiddenApisMain.enabled = false
|
||||
|
||||
// No licenses for our benchmark deps (we don't ship benchmarks)
|
||||
dependencyLicenses.enabled = false
|
||||
|
@ -69,20 +64,3 @@ thirdPartyAudit.excludes = [
|
|||
'org.openjdk.jmh.profile.HotspotRuntimeProfiler',
|
||||
'org.openjdk.jmh.util.Utils'
|
||||
]
|
||||
|
||||
runShadow {
|
||||
executable = new File(project.runtimeJavaHome, 'bin/java')
|
||||
}
|
||||
|
||||
// alias the shadowJar and runShadow tasks to abstract from the concrete plugin that we are using and provide a more consistent interface
|
||||
task jmhJar(
|
||||
dependsOn: shadowJar,
|
||||
description: 'Generates an uberjar with the microbenchmarks and all dependencies',
|
||||
group: 'Benchmark'
|
||||
)
|
||||
|
||||
task jmh(
|
||||
dependsOn: runShadow,
|
||||
description: 'Runs all microbenchmarks',
|
||||
group: 'Benchmark'
|
||||
)
|
||||
|
|
|
@ -405,6 +405,10 @@ class BuildPlugin implements Plugin<Project> {
|
|||
repos.mavenLocal()
|
||||
}
|
||||
repos.mavenCentral()
|
||||
repos.maven {
|
||||
name "elastic"
|
||||
url "https://artifacts.elastic.co/maven"
|
||||
}
|
||||
String luceneVersion = VersionProperties.lucene
|
||||
if (luceneVersion.contains('-snapshot')) {
|
||||
// extract the revision number from the version with a regex matcher
|
||||
|
@ -777,11 +781,16 @@ class BuildPlugin implements Plugin<Project> {
|
|||
systemProperty property.getKey(), property.getValue()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this once joda time is removed from scriptin in 7.0
|
||||
systemProperty 'es.scripting.use_java_time', 'true'
|
||||
|
||||
// Set the system keystore/truststore password if we're running tests in a FIPS-140 JVM
|
||||
if (project.inFipsJvm) {
|
||||
systemProperty 'javax.net.ssl.trustStorePassword', 'password'
|
||||
systemProperty 'javax.net.ssl.keyStorePassword', 'password'
|
||||
}
|
||||
|
||||
boolean assertionsEnabled = Boolean.parseBoolean(System.getProperty('tests.asserts', 'true'))
|
||||
enableSystemAssertions assertionsEnabled
|
||||
enableAssertions assertionsEnabled
|
||||
|
|
|
@ -526,11 +526,7 @@ class VagrantTestPlugin implements Plugin<Project> {
|
|||
project.gradle.removeListener(batsPackagingReproListener)
|
||||
}
|
||||
if (project.extensions.esvagrant.boxes.contains(box)) {
|
||||
// these tests are temporarily disabled for suse boxes while we debug an issue
|
||||
// https://github.com/elastic/elasticsearch/issues/30295
|
||||
if (box.equals("opensuse-42") == false && box.equals("sles-12") == false) {
|
||||
packagingTest.dependsOn(batsPackagingTest)
|
||||
}
|
||||
packagingTest.dependsOn(batsPackagingTest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -569,11 +565,7 @@ class VagrantTestPlugin implements Plugin<Project> {
|
|||
project.gradle.removeListener(javaPackagingReproListener)
|
||||
}
|
||||
if (project.extensions.esvagrant.boxes.contains(box)) {
|
||||
// these tests are temporarily disabled for suse boxes while we debug an issue
|
||||
// https://github.com/elastic/elasticsearch/issues/30295
|
||||
if (box.equals("opensuse-42") == false && box.equals("sles-12") == false) {
|
||||
packagingTest.dependsOn(javaPackagingTest)
|
||||
}
|
||||
packagingTest.dependsOn(javaPackagingTest)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -2,10 +2,18 @@
|
|||
|
||||
1. Build `client-benchmark-noop-api-plugin` with `gradle :client:client-benchmark-noop-api-plugin:assemble`
|
||||
2. Install it on the target host with `bin/elasticsearch-plugin install file:///full/path/to/client-benchmark-noop-api-plugin.zip`
|
||||
3. Start Elasticsearch on the target host (ideally *not* on the same machine)
|
||||
4. Build an uberjar with `gradle :client:benchmark:shadowJar` and execute it.
|
||||
3. Start Elasticsearch on the target host (ideally *not* on the machine
|
||||
that runs the benchmarks)
|
||||
4. Run the benchmark with
|
||||
```
|
||||
./gradlew -p client/benchmark run --args ' params go here'
|
||||
```
|
||||
|
||||
Repeat all steps above for the other benchmark candidate.
|
||||
Everything in the `'` gets sent on the command line to JMH. The leading ` `
|
||||
inside the `'`s is important. Without it parameters are sometimes sent to
|
||||
gradle.
|
||||
|
||||
See below for some example invocations.
|
||||
|
||||
### Example benchmark
|
||||
|
||||
|
@ -13,32 +21,35 @@ In general, you should define a few GC-related settings `-Xms8192M -Xmx8192M -XX
|
|||
|
||||
#### Bulk indexing
|
||||
|
||||
Download benchmark data from http://benchmarks.elastic.co/corpora/geonames/documents.json.bz2 and decompress them.
|
||||
Download benchmark data from http://benchmarks.elasticsearch.org.s3.amazonaws.com/corpora/geonames and decompress them.
|
||||
|
||||
Example command line parameters:
|
||||
Example invocation:
|
||||
|
||||
```
|
||||
rest bulk 192.168.2.2 ./documents.json geonames type 8647880 5000
|
||||
wget http://benchmarks.elasticsearch.org.s3.amazonaws.com/corpora/geonames/documents-2.json.bz2
|
||||
bzip2 -d documents-2.json.bz2
|
||||
mv documents-2.json client/benchmark/build
|
||||
gradlew -p client/benchmark run --args ' rest bulk localhost build/documents-2.json geonames type 8647880 5000'
|
||||
```
|
||||
|
||||
The parameters are in order:
|
||||
The parameters are all in the `'`s and are in order:
|
||||
|
||||
* Client type: Use either "rest" or "transport"
|
||||
* Benchmark type: Use either "bulk" or "search"
|
||||
* Benchmark target host IP (the host where Elasticsearch is running)
|
||||
* full path to the file that should be bulk indexed
|
||||
* name of the index
|
||||
* name of the (sole) type in the index
|
||||
* name of the (sole) type in the index
|
||||
* number of documents in the file
|
||||
* bulk size
|
||||
|
||||
|
||||
#### Bulk indexing
|
||||
#### Search
|
||||
|
||||
Example command line parameters:
|
||||
Example invocation:
|
||||
|
||||
```
|
||||
rest search 192.168.2.2 geonames "{ \"query\": { \"match_phrase\": { \"name\": \"Sankt Georgen\" } } }\"" 500,1000,1100,1200
|
||||
gradlew -p client/benchmark run --args ' rest search localhost geonames {"query":{"match_phrase":{"name":"Sankt Georgen"}}} 500,1000,1100,1200'
|
||||
```
|
||||
|
||||
The parameters are in order:
|
||||
|
@ -49,5 +60,3 @@ The parameters are in order:
|
|||
* name of the index
|
||||
* a search request body (remember to escape double quotes). The `TransportClientBenchmark` uses `QueryBuilders.wrapperQuery()` internally which automatically adds a root key `query`, so it must not be present in the command line parameter.
|
||||
* A comma-separated list of target throughput rates
|
||||
|
||||
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
*/
|
||||
|
||||
apply plugin: 'elasticsearch.build'
|
||||
// build an uberjar with all benchmarks
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
// have the shadow plugin provide the runShadow task
|
||||
apply plugin: 'application'
|
||||
|
||||
group = 'org.elasticsearch.client'
|
||||
|
@ -32,7 +29,6 @@ build.dependsOn.remove('assemble')
|
|||
archivesBaseName = 'client-benchmarks'
|
||||
mainClassName = 'org.elasticsearch.client.benchmark.BenchmarkMain'
|
||||
|
||||
|
||||
// never try to invoke tests on the benchmark project - there aren't any
|
||||
test.enabled = false
|
||||
|
||||
|
|
|
@ -205,6 +205,8 @@ public class RestHighLevelClient implements Closeable {
|
|||
private final SnapshotClient snapshotClient = new SnapshotClient(this);
|
||||
private final TasksClient tasksClient = new TasksClient(this);
|
||||
private final XPackClient xPackClient = new XPackClient(this);
|
||||
private final WatcherClient watcherClient = new WatcherClient(this);
|
||||
private final LicenseClient licenseClient = new LicenseClient(this);
|
||||
|
||||
/**
|
||||
* Creates a {@link RestHighLevelClient} given the low level {@link RestClientBuilder} that allows to build the
|
||||
|
@ -296,18 +298,38 @@ public class RestHighLevelClient implements Closeable {
|
|||
}
|
||||
|
||||
/**
|
||||
* A wrapper for the {@link RestHighLevelClient} that provides methods for
|
||||
* accessing the Elastic Licensed X-Pack APIs that are shipped with the
|
||||
* default distribution of Elasticsearch. All of these APIs will 404 if run
|
||||
* against the OSS distribution of Elasticsearch.
|
||||
* Provides methods for accessing the Elastic Licensed X-Pack Info
|
||||
* and Usage APIs that are shipped with the default distribution of
|
||||
* Elasticsearch. All of these APIs will 404 if run against the OSS
|
||||
* distribution of Elasticsearch.
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/xpack-api.html">
|
||||
* X-Pack APIs on elastic.co</a> for more information.
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/info-api.html">
|
||||
* Info APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public final XPackClient xpack() {
|
||||
return xPackClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for accessing the Elastic Licensed Watcher APIs that
|
||||
* are shipped with the default distribution of Elasticsearch. All of
|
||||
* these APIs will 404 if run against the OSS distribution of Elasticsearch.
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api.html">
|
||||
* Watcher APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public WatcherClient watcher() { return watcherClient; }
|
||||
|
||||
/**
|
||||
* Provides methods for accessing the Elastic Licensed Licensing APIs that
|
||||
* are shipped with the default distribution of Elasticsearch. All of
|
||||
* these APIs will 404 if run against the OSS distribution of Elasticsearch.
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/licensing-apis.html">
|
||||
* Licensing APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public LicenseClient license() { return licenseClient; }
|
||||
|
||||
/**
|
||||
* Executes a bulk request using the Bulk API.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html">Bulk API on elastic.co</a>
|
||||
|
|
|
@ -41,17 +41,9 @@ import static java.util.Collections.emptySet;
|
|||
public final class XPackClient {
|
||||
|
||||
private final RestHighLevelClient restHighLevelClient;
|
||||
private final WatcherClient watcherClient;
|
||||
private final LicenseClient licenseClient;
|
||||
|
||||
XPackClient(RestHighLevelClient restHighLevelClient) {
|
||||
this.restHighLevelClient = restHighLevelClient;
|
||||
this.watcherClient = new WatcherClient(restHighLevelClient);
|
||||
this.licenseClient = new LicenseClient(restHighLevelClient);
|
||||
}
|
||||
|
||||
public WatcherClient watcher() {
|
||||
return watcherClient;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,15 +94,4 @@ public final class XPackClient {
|
|||
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::xpackUsage, options,
|
||||
XPackUsageResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for the {@link RestHighLevelClient} that provides methods for
|
||||
* accessing the Elastic Licensing APIs.
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/licensing-apis.html">
|
||||
* X-Pack APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public LicenseClient license() {
|
||||
return licenseClient;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -755,7 +755,9 @@ public class RestHighLevelClientTests extends ESTestCase {
|
|||
method.isAnnotationPresent(Deprecated.class));
|
||||
} else {
|
||||
//TODO xpack api are currently ignored, we need to load xpack yaml spec too
|
||||
if (apiName.startsWith("xpack.") == false) {
|
||||
if (apiName.startsWith("xpack.") == false &&
|
||||
apiName.startsWith("license.") == false &&
|
||||
apiName.startsWith("watcher.") == false) {
|
||||
apiNotFound.add(apiName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class WatcherIT extends ESRestHighLevelClientTestCase {
|
|||
"}";
|
||||
BytesReference bytesReference = new BytesArray(json);
|
||||
PutWatchRequest putWatchRequest = new PutWatchRequest(watchId, bytesReference, XContentType.JSON);
|
||||
return highLevelClient().xpack().watcher().putWatch(putWatchRequest, RequestOptions.DEFAULT);
|
||||
return highLevelClient().watcher().putWatch(putWatchRequest, RequestOptions.DEFAULT);
|
||||
}
|
||||
|
||||
public void testDeleteWatch() throws Exception {
|
||||
|
@ -54,7 +54,7 @@ public class WatcherIT extends ESRestHighLevelClientTestCase {
|
|||
{
|
||||
String watchId = randomAlphaOfLength(10);
|
||||
createWatch(watchId);
|
||||
DeleteWatchResponse deleteWatchResponse = highLevelClient().xpack().watcher().deleteWatch(new DeleteWatchRequest(watchId),
|
||||
DeleteWatchResponse deleteWatchResponse = highLevelClient().watcher().deleteWatch(new DeleteWatchRequest(watchId),
|
||||
RequestOptions.DEFAULT);
|
||||
assertThat(deleteWatchResponse.getId(), is(watchId));
|
||||
assertThat(deleteWatchResponse.getVersion(), is(2L));
|
||||
|
@ -64,7 +64,7 @@ public class WatcherIT extends ESRestHighLevelClientTestCase {
|
|||
// delete watch that does not exist
|
||||
{
|
||||
String watchId = randomAlphaOfLength(10);
|
||||
DeleteWatchResponse deleteWatchResponse = highLevelClient().xpack().watcher().deleteWatch(new DeleteWatchRequest(watchId),
|
||||
DeleteWatchResponse deleteWatchResponse = highLevelClient().watcher().deleteWatch(new DeleteWatchRequest(watchId),
|
||||
RequestOptions.DEFAULT);
|
||||
assertThat(deleteWatchResponse.getId(), is(watchId));
|
||||
assertThat(deleteWatchResponse.getVersion(), is(1L));
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.client.documentation;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.LatchedActionListener;
|
||||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
|
@ -40,6 +41,7 @@ import static org.hamcrest.Matchers.startsWith;
|
|||
* Documentation for Licensing APIs in the high level java client.
|
||||
* Code wrapped in {@code tag} and {@code end} tags is included in the docs.
|
||||
*/
|
||||
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/32580")
|
||||
public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
||||
|
||||
public void testPutLicense() throws Exception {
|
||||
|
@ -60,7 +62,7 @@ public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
request.setLicenseDefinition(license); // <1>
|
||||
request.setAcknowledge(false); // <2>
|
||||
|
||||
PutLicenseResponse response = client.xpack().license().putLicense(request, RequestOptions.DEFAULT);
|
||||
PutLicenseResponse response = client.license().putLicense(request, RequestOptions.DEFAULT);
|
||||
//end::put-license-execute
|
||||
|
||||
//tag::put-license-response
|
||||
|
@ -96,7 +98,7 @@ public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::put-license-execute-async
|
||||
client.xpack().license().putLicenseAsync(
|
||||
client.license().putLicenseAsync(
|
||||
request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::put-license-execute-async
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public class WatcherDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
"}");
|
||||
PutWatchRequest request = new PutWatchRequest("my_watch_id", watch, XContentType.JSON);
|
||||
request.setActive(false); // <1>
|
||||
PutWatchResponse response = client.xpack().watcher().putWatch(request, RequestOptions.DEFAULT);
|
||||
PutWatchResponse response = client.watcher().putWatch(request, RequestOptions.DEFAULT);
|
||||
//end::x-pack-put-watch-execute
|
||||
|
||||
//tag::x-pack-put-watch-response
|
||||
|
@ -85,7 +85,7 @@ public class WatcherDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::x-pack-put-watch-execute-async
|
||||
client.xpack().watcher().putWatchAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
client.watcher().putWatchAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::x-pack-put-watch-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
|
@ -94,7 +94,7 @@ public class WatcherDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
{
|
||||
//tag::x-pack-delete-watch-execute
|
||||
DeleteWatchRequest request = new DeleteWatchRequest("my_watch_id");
|
||||
DeleteWatchResponse response = client.xpack().watcher().deleteWatch(request, RequestOptions.DEFAULT);
|
||||
DeleteWatchResponse response = client.watcher().deleteWatch(request, RequestOptions.DEFAULT);
|
||||
//end::x-pack-delete-watch-execute
|
||||
|
||||
//tag::x-pack-delete-watch-response
|
||||
|
@ -125,7 +125,7 @@ public class WatcherDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::x-pack-delete-watch-execute-async
|
||||
client.xpack().watcher().deleteWatchAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
client.watcher().deleteWatchAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::x-pack-delete-watch-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
|
|
|
@ -37,6 +37,9 @@ integTestCluster {
|
|||
extraConfigFile 'hunspell/en_US/en_US.dic', '../server/src/test/resources/indices/analyze/conf_dir/hunspell/en_US/en_US.dic'
|
||||
// Whitelist reindexing from the local node so we can test it.
|
||||
setting 'reindex.remote.whitelist', '127.0.0.1:*'
|
||||
|
||||
// TODO: remove this for 7.0, this exists to allow the doc examples in 6.x to continue using the defaults
|
||||
systemProperty 'es.scripting.use_java_time', 'false'
|
||||
}
|
||||
|
||||
// remove when https://github.com/elastic/elasticsearch/issues/31305 is fixed
|
||||
|
|
|
@ -80,7 +80,7 @@ A typical listener for `ExplainResponse` is constructed as follows:
|
|||
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-execute-listener]
|
||||
--------------------------------------------------
|
||||
<1> Called when the execution is successfully completed.
|
||||
<2> Called when the whole `FieldCapabilitiesRequest` fails.
|
||||
<2> Called when the whole `ExplainRequest` fails.
|
||||
|
||||
[[java-rest-high-explain-response]]
|
||||
==== ExplainResponse
|
||||
|
|
|
@ -198,7 +198,7 @@ POST hockey/player/1/_update
|
|||
==== Dates
|
||||
|
||||
Date fields are exposed as
|
||||
`ReadableDateTime`
|
||||
`ReadableDateTime` or
|
||||
so they support methods like
|
||||
`getYear`,
|
||||
and `getDayOfWeek`.
|
||||
|
@ -220,6 +220,11 @@ GET hockey/_search
|
|||
}
|
||||
----------------------------------------------------------------
|
||||
// CONSOLE
|
||||
// TEST[warning:The joda time api for doc values is deprecated. Use -Des.scripting.use_java_time=true to use the java time api for date field doc values]
|
||||
|
||||
NOTE: Date fields are changing in 7.0 to be exposed as `ZonedDateTime`
|
||||
from Java 8's time API. To switch to this functionality early,
|
||||
add `-Des.scripting.use_java_time=true` to `jvm.options`.
|
||||
|
||||
[float]
|
||||
[[modules-scripting-painless-regex]]
|
||||
|
|
|
@ -37,7 +37,6 @@ bin/elasticsearch-keystore add discovery.ec2.secret_key
|
|||
The following are the available discovery settings. All should be prefixed with `discovery.ec2.`.
|
||||
Those that must be stored in the keystore are marked as `Secure`.
|
||||
|
||||
|
||||
`access_key`::
|
||||
|
||||
An ec2 access key. The `secret_key` setting must also be specified. (Secure)
|
||||
|
@ -122,6 +121,10 @@ Defaults to `private_ip`.
|
|||
How long the list of hosts is cached to prevent further requests to the AWS API.
|
||||
Defaults to `10s`.
|
||||
|
||||
*All* secure settings of this plugin are {ref}/secure-settings.html#reloadable-secure-settings[reloadable].
|
||||
After you reload the settings, an aws sdk client with the latest settings
|
||||
from the keystore will be used.
|
||||
|
||||
[IMPORTANT]
|
||||
.Binding the network host
|
||||
==============================================
|
||||
|
|
|
@ -11,7 +11,7 @@ include::install_remove.asciidoc[]
|
|||
==== Azure Repository
|
||||
|
||||
To enable Azure repositories, you have first to define your azure storage settings as
|
||||
{ref}/secure-settings.html[secured settings]:
|
||||
{ref}/secure-settings.html[secure settings], before starting up the node:
|
||||
|
||||
[source,sh]
|
||||
----------------------------------------------------------------
|
||||
|
@ -20,6 +20,7 @@ bin/elasticsearch-keystore add azure.client.default.key
|
|||
----------------------------------------------------------------
|
||||
|
||||
Where `account` is the azure account name and `key` the azure secret key.
|
||||
These settings are used by the repository's internal azure client.
|
||||
|
||||
Note that you can also define more than one account:
|
||||
|
||||
|
@ -31,7 +32,18 @@ bin/elasticsearch-keystore add azure.client.secondary.account
|
|||
bin/elasticsearch-keystore add azure.client.secondary.key
|
||||
----------------------------------------------------------------
|
||||
|
||||
`default` is the default account name which will be used by a repository unless you set an explicit one.
|
||||
`default` is the default account name which will be used by a repository,
|
||||
unless you set an explicit one in the
|
||||
<<repository-azure-repository-settings, repository settings>>.
|
||||
|
||||
Both `account` and `key` storage settings are
|
||||
{ref}/secure-settings.html#reloadable-secure-settings[reloadable]. After you
|
||||
reload the settings, the internal azure clients, which are used to transfer the
|
||||
snapshot, will utilize the latest settings from the keystore.
|
||||
|
||||
NOTE: In progress snapshot/restore jobs will not be preempted by a *reload*
|
||||
of the storage secure settings. They will complete using the client as it was built
|
||||
when the operation started.
|
||||
|
||||
You can set the client side timeout to use when making any single request. It can be defined globally, per account or both.
|
||||
It's not set by default which means that Elasticsearch is using the
|
||||
|
|
|
@ -112,6 +112,15 @@ PUT _snapshot/my_gcs_repository
|
|||
// CONSOLE
|
||||
// TEST[skip:we don't have gcs setup while testing this]
|
||||
|
||||
The `credentials_file` settings are {ref}/secure-settings.html#reloadable-secure-settings[reloadable].
|
||||
After you reload the settings, the internal `gcs` clients, used to transfer the
|
||||
snapshot contents, will utilize the latest settings from the keystore.
|
||||
|
||||
|
||||
NOTE: In progress snapshot/restore jobs will not be preempted by a *reload*
|
||||
of the client's `credentials_file` settings. They will complete using the client
|
||||
as it was built when the operation started.
|
||||
|
||||
[[repository-gcs-client]]
|
||||
==== Client Settings
|
||||
|
||||
|
|
|
@ -35,9 +35,9 @@ PUT _snapshot/my_s3_repository
|
|||
==== Client Settings
|
||||
|
||||
The client used to connect to S3 has a number of settings available. Client setting names are of
|
||||
the form `s3.client.CLIENT_NAME.SETTING_NAME` and specified inside `elasticsearch.yml`. The
|
||||
default client name looked up by a `s3` repository is called `default`, but can be customized
|
||||
with the repository setting `client`. For example:
|
||||
the form `s3.client.CLIENT_NAME.SETTING_NAME`. The default client name, which is looked up by
|
||||
an `s3` repository, is called `default`. It can be modified using the
|
||||
<<repository-s3-repository, repository setting>> `client`. For example:
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
@ -53,8 +53,11 @@ PUT _snapshot/my_s3_repository
|
|||
// CONSOLE
|
||||
// TEST[skip:we don't have s3 setup while testing this]
|
||||
|
||||
Some settings are sensitive and must be stored in the {ref}/secure-settings.html[elasticsearch keystore].
|
||||
For example, to use explicit AWS access keys:
|
||||
Most client settings are specified inside `elasticsearch.yml`, but some are
|
||||
sensitive and must be stored in the {ref}/secure-settings.html[elasticsearch keystore].
|
||||
|
||||
For example, before you start the node, run these commands to add AWS access
|
||||
key settings to the keystore:
|
||||
|
||||
[source,sh]
|
||||
----
|
||||
|
@ -62,8 +65,19 @@ bin/elasticsearch-keystore add s3.client.default.access_key
|
|||
bin/elasticsearch-keystore add s3.client.default.secret_key
|
||||
----
|
||||
|
||||
The following are the available client settings. Those that must be stored in the keystore
|
||||
are marked as `Secure`.
|
||||
*All* client secure settings of this plugin are
|
||||
{ref}/secure-settings.html#reloadable-secure-settings[reloadable]. After you
|
||||
reload the settings, the internal `s3` clients, used to transfer the snapshot
|
||||
contents, will utilize the latest settings from the keystore. Any existing `s3`
|
||||
repositories, as well as any newly created ones, will pick up the new values
|
||||
stored in the keystore.
|
||||
|
||||
NOTE: In progress snapshot/restore tasks will not be preempted by a *reload*
|
||||
of the client's secure settings. The task will complete using the client as it
|
||||
was built when the operation started.
|
||||
|
||||
The following is the list of all the available client settings.
|
||||
Those that must be stored in the keystore are marked as `Secure` and are *reloadable*.
|
||||
|
||||
`access_key`::
|
||||
|
||||
|
|
|
@ -425,6 +425,7 @@ POST /sales/_search?size=0
|
|||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
// TEST[setup:sales]
|
||||
// TEST[warning:The joda time api for doc values is deprecated. Use -Des.scripting.use_java_time=true to use the java time api for date field doc values]
|
||||
|
||||
Response:
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ The above example produces the following term:
|
|||
|
||||
[source,text]
|
||||
---------------------------
|
||||
[ My, credit, card, is 123_456_789 ]
|
||||
[ My, credit, card, is, 123_456_789 ]
|
||||
---------------------------
|
||||
|
||||
WARNING: Using a replacement string that changes the length of the original
|
||||
|
|
|
@ -22,6 +22,7 @@ Will return, for example:
|
|||
"successful" : 1,
|
||||
"failed" : 0
|
||||
},
|
||||
"cluster_uuid": "YjAvIhsCQ9CbjWZb2qJw3Q",
|
||||
"cluster_name": "elasticsearch",
|
||||
"timestamp": 1459427693515,
|
||||
"status": "green",
|
||||
|
|
|
@ -75,3 +75,34 @@ To remove a setting from the keystore, use the `remove` command:
|
|||
bin/elasticsearch-keystore remove the.setting.name.to.remove
|
||||
----------------------------------------------------------------
|
||||
|
||||
[float]
|
||||
[[reloadable-secure-settings]]
|
||||
=== Reloadable secure settings
|
||||
|
||||
Just like the settings values in `elasticsearch.yml`, changes to the
|
||||
keystore contents are not automatically applied to the running
|
||||
elasticsearch node. Re-reading settings requires a node restart.
|
||||
However, certain secure settings are marked as *reloadable*. Such settings
|
||||
can be re-read and applied on a running node.
|
||||
|
||||
The values of all secure settings, *reloadable* or not, must be identical
|
||||
across all cluster nodes. After making the desired secure settings changes,
|
||||
using the `bin/elasticsearch-keystore add` command, call:
|
||||
[source,js]
|
||||
----
|
||||
POST _nodes/reload_secure_settings
|
||||
{
|
||||
"secure_settings_password": ""
|
||||
}
|
||||
----
|
||||
// CONSOLE
|
||||
This API will decrypt and re-read the entire keystore, on every cluster node,
|
||||
but only the *reloadable* secure settings will be applied. Changes to other
|
||||
settings will not go into effect until the next restart. Once the call returns,
|
||||
the reload has been completed, meaning that all internal datastructures dependent
|
||||
on these settings have been changed. Everything should look as if the settings
|
||||
had the new value from the start.
|
||||
|
||||
When changing multiple *reloadable* secure settings, modify all of them, on
|
||||
each cluster node, and then issue a `reload_secure_settings` call, instead
|
||||
of reloading after each modification.
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
teardown:
|
||||
- do:
|
||||
ingest.delete_pipeline:
|
||||
id: "my_pipeline"
|
||||
ignore: 404
|
||||
|
||||
---
|
||||
"Test index with default pipeline":
|
||||
- do:
|
||||
ingest.put_pipeline:
|
||||
id: "my_pipeline"
|
||||
body: >
|
||||
{
|
||||
"description": "_description",
|
||||
"processors": [
|
||||
{
|
||||
"bytes" : {
|
||||
"field" : "bytes_source_field",
|
||||
"target_field" : "bytes_target_field"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
- match: { acknowledged: true }
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: test
|
||||
body:
|
||||
settings:
|
||||
index:
|
||||
default_pipeline: "my_pipeline"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: test
|
||||
type: test
|
||||
id: 1
|
||||
body: {bytes_source_field: "1kb"}
|
||||
|
||||
- do:
|
||||
get:
|
||||
index: test
|
||||
type: test
|
||||
id: 1
|
||||
- match: { _source.bytes_source_field: "1kb" }
|
||||
- match: { _source.bytes_target_field: 1024 }
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: test
|
||||
type: test
|
||||
id: 2
|
||||
pipeline: "_none"
|
||||
body: {bytes_source_field: "1kb"}
|
||||
|
||||
- do:
|
||||
get:
|
||||
index: test
|
||||
type: test
|
||||
id: 2
|
||||
- match: { _source.bytes_source_field: "1kb" }
|
||||
- is_false: _source.bytes_target_field
|
||||
|
||||
- do:
|
||||
catch: bad_request
|
||||
index:
|
||||
index: test
|
||||
type: test
|
||||
id: 3
|
||||
pipeline: ""
|
||||
body: {bytes_source_field: "1kb"}
|
|
@ -17,8 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
esplugin {
|
||||
description 'An easy, safe and fast scripting language for Elasticsearch'
|
||||
classname 'org.elasticsearch.painless.PainlessPlugin'
|
||||
|
@ -26,6 +24,7 @@ esplugin {
|
|||
|
||||
integTestCluster {
|
||||
module project.project(':modules:mapper-extras')
|
||||
systemProperty 'es.scripting.use_java_time', 'true'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -69,17 +69,14 @@ final class Compiler {
|
|||
/**
|
||||
* A secure class loader used to define Painless scripts.
|
||||
*/
|
||||
static final class Loader extends SecureClassLoader {
|
||||
final class Loader extends SecureClassLoader {
|
||||
private final AtomicInteger lambdaCounter = new AtomicInteger(0);
|
||||
private final PainlessLookup painlessLookup;
|
||||
|
||||
/**
|
||||
* @param parent The parent ClassLoader.
|
||||
*/
|
||||
Loader(ClassLoader parent, PainlessLookup painlessLookup) {
|
||||
Loader(ClassLoader parent) {
|
||||
super(parent);
|
||||
|
||||
this.painlessLookup = painlessLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,7 +87,16 @@ final class Compiler {
|
|||
*/
|
||||
@Override
|
||||
public Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
Class<?> found = painlessLookup.getClassFromBinaryName(name);
|
||||
if (scriptClass.getName().equals(name)) {
|
||||
return scriptClass;
|
||||
}
|
||||
if (factoryClass != null && factoryClass.getName().equals(name)) {
|
||||
return factoryClass;
|
||||
}
|
||||
if (statefulFactoryClass != null && statefulFactoryClass.getName().equals(name)) {
|
||||
return statefulFactoryClass;
|
||||
}
|
||||
Class<?> found = painlessLookup.canonicalTypeNameToType(name.replace('$', '.'));
|
||||
|
||||
return found != null ? found : super.findClass(name);
|
||||
}
|
||||
|
@ -139,13 +145,23 @@ final class Compiler {
|
|||
* {@link Compiler}'s specified {@link PainlessLookup}.
|
||||
*/
|
||||
public Loader createLoader(ClassLoader parent) {
|
||||
return new Loader(parent, painlessLookup);
|
||||
return new Loader(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* The class/interface the script is guaranteed to derive/implement.
|
||||
* The class/interface the script will implement.
|
||||
*/
|
||||
private final Class<?> base;
|
||||
private final Class<?> scriptClass;
|
||||
|
||||
/**
|
||||
* The class/interface to create the {@code scriptClass} instance.
|
||||
*/
|
||||
private final Class<?> factoryClass;
|
||||
|
||||
/**
|
||||
* An optional class/interface to create the {@code factoryClass} instance.
|
||||
*/
|
||||
private final Class<?> statefulFactoryClass;
|
||||
|
||||
/**
|
||||
* The whitelist the script will use.
|
||||
|
@ -154,11 +170,15 @@ final class Compiler {
|
|||
|
||||
/**
|
||||
* Standard constructor.
|
||||
* @param base The class/interface the script is guaranteed to derive/implement.
|
||||
* @param scriptClass The class/interface the script will implement.
|
||||
* @param factoryClass An optional class/interface to create the {@code scriptClass} instance.
|
||||
* @param statefulFactoryClass An optional class/interface to create the {@code factoryClass} instance.
|
||||
* @param painlessLookup The whitelist the script will use.
|
||||
*/
|
||||
Compiler(Class<?> base, PainlessLookup painlessLookup) {
|
||||
this.base = base;
|
||||
Compiler(Class<?> scriptClass, Class<?> factoryClass, Class<?> statefulFactoryClass, PainlessLookup painlessLookup) {
|
||||
this.scriptClass = scriptClass;
|
||||
this.factoryClass = factoryClass;
|
||||
this.statefulFactoryClass = statefulFactoryClass;
|
||||
this.painlessLookup = painlessLookup;
|
||||
}
|
||||
|
||||
|
@ -177,7 +197,7 @@ final class Compiler {
|
|||
" plugin if a script longer than this length is a requirement.");
|
||||
}
|
||||
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base);
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass);
|
||||
SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, painlessLookup,
|
||||
null);
|
||||
root.analyze(painlessLookup);
|
||||
|
@ -209,7 +229,7 @@ final class Compiler {
|
|||
" plugin if a script longer than this length is a requirement.");
|
||||
}
|
||||
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base);
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass);
|
||||
SSource root = Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), name, source, settings, painlessLookup,
|
||||
debugStream);
|
||||
root.analyze(painlessLookup);
|
||||
|
|
|
@ -187,7 +187,7 @@ public final class Def {
|
|||
String key = PainlessLookupUtility.buildPainlessMethodKey(name, arity);
|
||||
// check whitelist for matching method
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
|
||||
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
PainlessMethod method = struct.methods.get(key);
|
||||
|
@ -197,7 +197,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
|
||||
struct = painlessLookup.lookupPainlessClass(iface);
|
||||
|
||||
if (struct != null) {
|
||||
PainlessMethod method = struct.methods.get(key);
|
||||
|
@ -238,7 +238,7 @@ public final class Def {
|
|||
int numArguments = callSiteType.parameterCount();
|
||||
// simple case: no lambdas
|
||||
if (recipeString.isEmpty()) {
|
||||
return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).handle;
|
||||
return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).methodHandle;
|
||||
}
|
||||
|
||||
// convert recipe string to a bitset for convenience (the code below should be refactored...)
|
||||
|
@ -262,7 +262,7 @@ public final class Def {
|
|||
// lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
|
||||
// based on these we can finally link any remaining lambdas that were deferred.
|
||||
PainlessMethod method = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
|
||||
MethodHandle handle = method.handle;
|
||||
MethodHandle handle = method.methodHandle;
|
||||
|
||||
int replaced = 0;
|
||||
upTo = 1;
|
||||
|
@ -281,7 +281,7 @@ public final class Def {
|
|||
captures[capture] = callSiteType.parameterType(i + 1 + capture);
|
||||
}
|
||||
MethodHandle filter;
|
||||
Class<?> interfaceType = method.arguments.get(i - 1 - replaced);
|
||||
Class<?> interfaceType = method.typeParameters.get(i - 1 - replaced);
|
||||
if (signature.charAt(0) == 'S') {
|
||||
// the implementation is strongly typed, now that we know the interface type,
|
||||
// we have everything.
|
||||
|
@ -326,15 +326,16 @@ public final class Def {
|
|||
*/
|
||||
static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass,
|
||||
Class<?> receiverClass, String name) throws Throwable {
|
||||
Class<?> interfaceType = painlessLookup.getJavaClassFromPainlessType(interfaceClass);
|
||||
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
|
||||
Class<?> interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass);
|
||||
PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(interfaceType).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
|
||||
}
|
||||
int arity = interfaceMethod.arguments.size();
|
||||
int arity = interfaceMethod.typeParameters.size();
|
||||
PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
|
||||
return lookupReferenceInternal(painlessLookup, methodHandlesLookup, interfaceType,
|
||||
PainlessLookupUtility.typeToCanonicalTypeName(implMethod.target), implMethod.name, receiverClass);
|
||||
PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass),
|
||||
implMethod.javaMethod.getName(), receiverClass);
|
||||
}
|
||||
|
||||
/** Returns a method handle to an implementation of clazz, given method reference signature. */
|
||||
|
@ -344,12 +345,12 @@ public final class Def {
|
|||
final FunctionRef ref;
|
||||
if ("this".equals(type)) {
|
||||
// user written method
|
||||
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(clazz).functionalMethod;
|
||||
PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(clazz).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(clazz) + "], not a functional interface");
|
||||
}
|
||||
int arity = interfaceMethod.arguments.size() + captures.length;
|
||||
int arity = interfaceMethod.typeParameters.size() + captures.length;
|
||||
final MethodHandle handle;
|
||||
try {
|
||||
MethodHandle accessor = methodHandlesLookup.findStaticGetter(methodHandlesLookup.lookupClass(),
|
||||
|
@ -360,7 +361,7 @@ public final class Def {
|
|||
// is it a synthetic method? If we generated the method ourselves, be more helpful. It can only fail
|
||||
// because the arity does not match the expected interface type.
|
||||
if (call.contains("$")) {
|
||||
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
|
||||
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.javaMethod.getName() +
|
||||
"] in [" + clazz + "]");
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
|
||||
|
@ -418,7 +419,7 @@ public final class Def {
|
|||
static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
|
||||
// first try whitelist
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
|
||||
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.getterMethodHandles.get(name);
|
||||
|
@ -428,7 +429,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
|
||||
struct = painlessLookup.lookupPainlessClass(iface);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.getterMethodHandles.get(name);
|
||||
|
@ -489,7 +490,7 @@ public final class Def {
|
|||
static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
|
||||
// first try whitelist
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
|
||||
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.setterMethodHandles.get(name);
|
||||
|
@ -499,7 +500,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
|
||||
struct = painlessLookup.lookupPainlessClass(iface);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.setterMethodHandles.get(name);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Locals.LocalMethod;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessConstructor;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
|
@ -89,10 +90,10 @@ public class FunctionRef {
|
|||
PainlessLookup painlessLookup, Class<?> expected, String type, String call, int numCaptures) {
|
||||
|
||||
if ("new".equals(call)) {
|
||||
return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
|
||||
return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod,
|
||||
lookup(painlessLookup, expected, type), numCaptures);
|
||||
} else {
|
||||
return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
|
||||
return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod,
|
||||
lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures);
|
||||
}
|
||||
}
|
||||
|
@ -108,24 +109,24 @@ public class FunctionRef {
|
|||
Constructor<?> javaConstructor = delegateConstructor.javaConstructor;
|
||||
MethodType delegateMethodType = delegateConstructor.methodType;
|
||||
|
||||
interfaceMethodName = interfaceMethod.name;
|
||||
factoryMethodType = MethodType.methodType(expected,
|
||||
this.interfaceMethodName = interfaceMethod.javaMethod.getName();
|
||||
this.factoryMethodType = MethodType.methodType(expected,
|
||||
delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
|
||||
interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
|
||||
delegateClassName = javaConstructor.getDeclaringClass().getName();
|
||||
isDelegateInterface = false;
|
||||
delegateInvokeType = H_NEWINVOKESPECIAL;
|
||||
delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME;
|
||||
this.delegateClassName = javaConstructor.getDeclaringClass().getName();
|
||||
this.isDelegateInterface = false;
|
||||
this.delegateInvokeType = H_NEWINVOKESPECIAL;
|
||||
this.delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME;
|
||||
this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
|
||||
|
||||
this.interfaceMethod = interfaceMethod;
|
||||
delegateTypeParameters = delegateConstructor.typeParameters;
|
||||
delegateReturnType = void.class;
|
||||
this.delegateTypeParameters = delegateConstructor.typeParameters;
|
||||
this.delegateReturnType = void.class;
|
||||
|
||||
factoryDescriptor = factoryMethodType.toMethodDescriptorString();
|
||||
interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
|
||||
delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
|
||||
this.factoryDescriptor = factoryMethodType.toMethodDescriptorString();
|
||||
this.interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
|
||||
this.delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,41 +139,63 @@ public class FunctionRef {
|
|||
public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) {
|
||||
MethodType delegateMethodType = delegateMethod.methodType;
|
||||
|
||||
interfaceMethodName = interfaceMethod.name;
|
||||
factoryMethodType = MethodType.methodType(expected,
|
||||
this.interfaceMethodName = interfaceMethod.javaMethod.getName();
|
||||
this.factoryMethodType = MethodType.methodType(expected,
|
||||
delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
|
||||
interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
|
||||
// the Painless$Script class can be inferred if owner is null
|
||||
if (delegateMethod.target == null) {
|
||||
delegateClassName = CLASS_NAME;
|
||||
isDelegateInterface = false;
|
||||
} else if (delegateMethod.augmentation != null) {
|
||||
delegateClassName = delegateMethod.augmentation.getName();
|
||||
isDelegateInterface = delegateMethod.augmentation.isInterface();
|
||||
this.delegateClassName = delegateMethod.javaMethod.getDeclaringClass().getName();
|
||||
this.isDelegateInterface = delegateMethod.javaMethod.getDeclaringClass().isInterface();
|
||||
|
||||
if (Modifier.isStatic(delegateMethod.javaMethod.getModifiers())) {
|
||||
this.delegateInvokeType = H_INVOKESTATIC;
|
||||
} else if (delegateMethod.javaMethod.getDeclaringClass().isInterface()) {
|
||||
this.delegateInvokeType = H_INVOKEINTERFACE;
|
||||
} else {
|
||||
delegateClassName = delegateMethod.target.getName();
|
||||
isDelegateInterface = delegateMethod.target.isInterface();
|
||||
this.delegateInvokeType = H_INVOKEVIRTUAL;
|
||||
}
|
||||
|
||||
if (Modifier.isStatic(delegateMethod.modifiers)) {
|
||||
delegateInvokeType = H_INVOKESTATIC;
|
||||
} else if (delegateMethod.target.isInterface()) {
|
||||
delegateInvokeType = H_INVOKEINTERFACE;
|
||||
} else {
|
||||
delegateInvokeType = H_INVOKEVIRTUAL;
|
||||
}
|
||||
|
||||
delegateMethodName = delegateMethod.name;
|
||||
this.delegateMethodName = delegateMethod.javaMethod.getName();
|
||||
this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
|
||||
|
||||
this.interfaceMethod = interfaceMethod;
|
||||
delegateTypeParameters = delegateMethod.arguments;
|
||||
delegateReturnType = delegateMethod.rtn;
|
||||
this.delegateTypeParameters = delegateMethod.typeParameters;
|
||||
this.delegateReturnType = delegateMethod.returnType;
|
||||
|
||||
factoryDescriptor = factoryMethodType.toMethodDescriptorString();
|
||||
interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
|
||||
delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
|
||||
this.factoryDescriptor = factoryMethodType.toMethodDescriptorString();
|
||||
this.interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
|
||||
this.delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FunctionRef (already resolved)
|
||||
* @param expected functional interface type to implement
|
||||
* @param interfaceMethod functional interface method
|
||||
* @param delegateMethod implementation method
|
||||
* @param numCaptures number of captured arguments
|
||||
*/
|
||||
public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, LocalMethod delegateMethod, int numCaptures) {
|
||||
MethodType delegateMethodType = delegateMethod.methodType;
|
||||
|
||||
this.interfaceMethodName = interfaceMethod.javaMethod.getName();
|
||||
this.factoryMethodType = MethodType.methodType(expected,
|
||||
delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
|
||||
this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
|
||||
this.delegateClassName = CLASS_NAME;
|
||||
this.isDelegateInterface = false;
|
||||
this.delegateInvokeType = H_INVOKESTATIC;
|
||||
|
||||
this.delegateMethodName = delegateMethod.name;
|
||||
this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
|
||||
|
||||
this.interfaceMethod = interfaceMethod;
|
||||
this.delegateTypeParameters = delegateMethod.typeParameters;
|
||||
this.delegateReturnType = delegateMethod.returnType;
|
||||
|
||||
this.factoryDescriptor = factoryMethodType.toMethodDescriptorString();
|
||||
this.interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
|
||||
this.delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,24 +204,24 @@ public class FunctionRef {
|
|||
*/
|
||||
public FunctionRef(Class<?> expected,
|
||||
PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) {
|
||||
interfaceMethodName = interfaceMethod.name;
|
||||
factoryMethodType = MethodType.methodType(expected,
|
||||
this.interfaceMethodName = interfaceMethod.javaMethod.getName();
|
||||
this.factoryMethodType = MethodType.methodType(expected,
|
||||
delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
|
||||
interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
|
||||
|
||||
delegateClassName = CLASS_NAME;
|
||||
delegateInvokeType = H_INVOKESTATIC;
|
||||
this.delegateClassName = CLASS_NAME;
|
||||
this.delegateInvokeType = H_INVOKESTATIC;
|
||||
this.delegateMethodName = delegateMethodName;
|
||||
this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
|
||||
isDelegateInterface = false;
|
||||
this.isDelegateInterface = false;
|
||||
|
||||
this.interfaceMethod = null;
|
||||
delegateTypeParameters = null;
|
||||
delegateReturnType = null;
|
||||
this.delegateTypeParameters = null;
|
||||
this.delegateReturnType = null;
|
||||
|
||||
factoryDescriptor = null;
|
||||
interfaceType = null;
|
||||
delegateType = null;
|
||||
this.factoryDescriptor = null;
|
||||
this.interfaceType = null;
|
||||
this.delegateType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,15 +230,15 @@ public class FunctionRef {
|
|||
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;
|
||||
PainlessMethod method = painlessLookup.lookupPainlessClass(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()));
|
||||
PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type));
|
||||
PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.typeParameters.size()));
|
||||
|
||||
if (impl == null) {
|
||||
throw new IllegalArgumentException("Unknown reference [" + type + "::new] matching [" + expected + "]");
|
||||
|
@ -231,27 +254,27 @@ public class FunctionRef {
|
|||
String type, String call, boolean receiverCaptured) {
|
||||
// check its really a functional interface
|
||||
// for e.g. Comparable
|
||||
PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
PainlessMethod method = painlessLookup.lookupPainlessClass(expected).functionalMethod;
|
||||
if (method == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface");
|
||||
}
|
||||
|
||||
// lookup requested method
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
|
||||
PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type));
|
||||
final PainlessMethod impl;
|
||||
// look for a static impl first
|
||||
PainlessMethod staticImpl =
|
||||
struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.arguments.size()));
|
||||
struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.typeParameters.size()));
|
||||
if (staticImpl == null) {
|
||||
// otherwise a virtual impl
|
||||
final int arity;
|
||||
if (receiverCaptured) {
|
||||
// receiver captured
|
||||
arity = method.arguments.size();
|
||||
arity = method.typeParameters.size();
|
||||
} else {
|
||||
// receiver passed
|
||||
arity = method.arguments.size() - 1;
|
||||
arity = method.typeParameters.size() - 1;
|
||||
}
|
||||
impl = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(call, arity));
|
||||
} else {
|
||||
|
|
|
@ -36,7 +36,6 @@ import java.security.AccessController;
|
|||
import java.security.PrivilegedAction;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.Lookup;
|
||||
import static org.elasticsearch.painless.Compiler.Loader;
|
||||
import static org.elasticsearch.painless.WriterConstants.CLASS_VERSION;
|
||||
import static org.elasticsearch.painless.WriterConstants.CTOR_METHOD_NAME;
|
||||
import static org.elasticsearch.painless.WriterConstants.DELEGATE_BOOTSTRAP_HANDLE;
|
||||
|
@ -207,7 +206,7 @@ public final class LambdaBootstrap {
|
|||
MethodType delegateMethodType,
|
||||
int isDelegateInterface)
|
||||
throws LambdaConversionException {
|
||||
Loader loader = (Loader)lookup.lookupClass().getClassLoader();
|
||||
Compiler.Loader loader = (Compiler.Loader)lookup.lookupClass().getClassLoader();
|
||||
String lambdaClassName = Type.getInternalName(lookup.lookupClass()) + "$$Lambda" + loader.newLambdaIdentifier();
|
||||
Type lambdaClassType = Type.getObjectType(lambdaClassName);
|
||||
Type delegateClassType = Type.getObjectType(delegateClassName.replace('.', '/'));
|
||||
|
@ -457,11 +456,11 @@ public final class LambdaBootstrap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Defines the {@link Class} for the lambda class using the same {@link Loader}
|
||||
* Defines the {@link Class} for the lambda class using the same {@link Compiler.Loader}
|
||||
* that originally defined the class for the Painless script.
|
||||
*/
|
||||
private static Class<?> createLambdaClass(
|
||||
Loader loader,
|
||||
Compiler.Loader loader,
|
||||
ClassWriter cw,
|
||||
Type lambdaClassType) {
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ package org.elasticsearch.painless;
|
|||
import org.elasticsearch.painless.ScriptClassInfo.MethodArgument;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -38,6 +38,30 @@ import java.util.Set;
|
|||
*/
|
||||
public final class Locals {
|
||||
|
||||
/**
|
||||
* Constructs a local method key used to lookup local methods from a painless class.
|
||||
*/
|
||||
public static String buildLocalMethodKey(String methodName, int methodArity) {
|
||||
return methodName + "/" + methodArity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores information about methods directly callable on the generated script class.
|
||||
*/
|
||||
public static class LocalMethod {
|
||||
public final String name;
|
||||
public final Class<?> returnType;
|
||||
public final List<Class<?>> typeParameters;
|
||||
public final MethodType methodType;
|
||||
|
||||
public LocalMethod(String name, Class<?> returnType, List<Class<?>> typeParameters, MethodType methodType) {
|
||||
this.name = name;
|
||||
this.returnType = returnType;
|
||||
this.typeParameters = typeParameters;
|
||||
this.methodType = methodType;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reserved word: loop counter */
|
||||
public static final String LOOP = "#loop";
|
||||
/** Reserved word: unused */
|
||||
|
@ -110,9 +134,9 @@ public final class Locals {
|
|||
}
|
||||
|
||||
/** Creates a new program scope: the list of methods. It is the parent for all methods */
|
||||
public static Locals newProgramScope(PainlessLookup painlessLookup, Collection<PainlessMethod> methods) {
|
||||
public static Locals newProgramScope(PainlessLookup painlessLookup, Collection<LocalMethod> methods) {
|
||||
Locals locals = new Locals(null, painlessLookup, null, null);
|
||||
for (PainlessMethod method : methods) {
|
||||
for (LocalMethod method : methods) {
|
||||
locals.addMethod(method);
|
||||
}
|
||||
return locals;
|
||||
|
@ -143,8 +167,8 @@ public final class Locals {
|
|||
}
|
||||
|
||||
/** Looks up a method. Returns null if the method does not exist. */
|
||||
public PainlessMethod getMethod(String key) {
|
||||
PainlessMethod method = lookupMethod(key);
|
||||
public LocalMethod getMethod(String key) {
|
||||
LocalMethod method = lookupMethod(key);
|
||||
if (method != null) {
|
||||
return method;
|
||||
}
|
||||
|
@ -199,7 +223,7 @@ public final class Locals {
|
|||
// variable name -> variable
|
||||
private Map<String,Variable> variables;
|
||||
// method name+arity -> methods
|
||||
private Map<String,PainlessMethod> methods;
|
||||
private Map<String,LocalMethod> methods;
|
||||
|
||||
/**
|
||||
* Create a new Locals
|
||||
|
@ -237,7 +261,7 @@ public final class Locals {
|
|||
}
|
||||
|
||||
/** Looks up a method at this scope only. Returns null if the method does not exist. */
|
||||
private PainlessMethod lookupMethod(String key) {
|
||||
private LocalMethod lookupMethod(String key) {
|
||||
if (methods == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -256,11 +280,11 @@ public final class Locals {
|
|||
return variable;
|
||||
}
|
||||
|
||||
private void addMethod(PainlessMethod method) {
|
||||
private void addMethod(LocalMethod method) {
|
||||
if (methods == null) {
|
||||
methods = new HashMap<>();
|
||||
}
|
||||
methods.put(PainlessLookupUtility.buildPainlessMethodKey(method.name, method.arguments.size()), method);
|
||||
methods.put(buildLocalMethodKey(method.name, method.typeParameters.size()), method);
|
||||
// TODO: check result
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.lookup.PainlessCast;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.def;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
|
@ -28,6 +29,7 @@ import org.objectweb.asm.Type;
|
|||
import org.objectweb.asm.commons.GeneratorAdapter;
|
||||
import org.objectweb.asm.commons.Method;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -415,4 +417,26 @@ public final class MethodWriter extends GeneratorAdapter {
|
|||
System.arraycopy(params, 0, args, 2, params.length);
|
||||
invokeDynamic(name, methodType.getDescriptor(), DEF_BOOTSTRAP_HANDLE, args);
|
||||
}
|
||||
|
||||
public void invokeMethodCall(PainlessMethod painlessMethod) {
|
||||
Type type = Type.getType(painlessMethod.javaMethod.getDeclaringClass());
|
||||
Method method = Method.getMethod(painlessMethod.javaMethod);
|
||||
|
||||
if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) {
|
||||
// invokeStatic assumes that the owner class is not an interface, so this is a
|
||||
// special case for interfaces where the interface method boolean needs to be set to
|
||||
// true to reference the appropriate class constant when calling a static interface
|
||||
// method since java 8 did not check, but java 9 and 10 do
|
||||
if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) {
|
||||
visitMethodInsn(Opcodes.INVOKESTATIC, type.getInternalName(),
|
||||
painlessMethod.javaMethod.getName(), painlessMethod.methodType.toMethodDescriptorString(), true);
|
||||
} else {
|
||||
invokeStatic(type, method);
|
||||
}
|
||||
} else if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) {
|
||||
invokeInterface(type, method);
|
||||
} else {
|
||||
invokeVirtual(type, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class PainlessExplainError extends Error {
|
|||
if (objectToExplain != null) {
|
||||
toString = objectToExplain.toString();
|
||||
javaClassName = objectToExplain.getClass().getName();
|
||||
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(objectToExplain.getClass());
|
||||
PainlessClass struct = painlessLookup.lookupPainlessClass(objectToExplain.getClass());
|
||||
if (struct != null) {
|
||||
painlessClassName = PainlessLookupUtility.typeToCanonicalTypeName(objectToExplain.getClass());
|
||||
}
|
||||
|
|
|
@ -102,10 +102,10 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
|
|||
for (Map.Entry<ScriptContext<?>, List<Whitelist>> entry : contexts.entrySet()) {
|
||||
ScriptContext<?> context = entry.getKey();
|
||||
if (context.instanceClazz.equals(SearchScript.class) || context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class,
|
||||
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, null, null,
|
||||
PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
|
||||
} else {
|
||||
contextsToCompilers.put(context, new Compiler(context.instanceClazz,
|
||||
contextsToCompilers.put(context, new Compiler(context.instanceClazz, context.factoryClazz, context.statefulFactoryClazz,
|
||||
PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ public class ScriptClassInfo {
|
|||
componentType = componentType.getComponentType();
|
||||
}
|
||||
|
||||
if (painlessLookup.getPainlessStructFromJavaClass(componentType) == null) {
|
||||
if (painlessLookup.lookupPainlessClass(componentType) == null) {
|
||||
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ final class EnhancedPainlessLexer extends PainlessLexer {
|
|||
|
||||
@Override
|
||||
protected boolean isType(String name) {
|
||||
return painlessLookup.isSimplePainlessType(name);
|
||||
return painlessLookup.isValidCanonicalClassName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,24 +20,20 @@
|
|||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public final class PainlessField {
|
||||
public final String name;
|
||||
public final Class<?> target;
|
||||
public final Class<?> clazz;
|
||||
public final String javaName;
|
||||
public final int modifiers;
|
||||
public final MethodHandle getter;
|
||||
public final MethodHandle setter;
|
||||
public final Field javaField;
|
||||
public final Class<?> typeParameter;
|
||||
|
||||
PainlessField(String name, String javaName, Class<?> target, Class<?> clazz, int modifiers,
|
||||
MethodHandle getter, MethodHandle setter) {
|
||||
this.name = name;
|
||||
this.javaName = javaName;
|
||||
this.target = target;
|
||||
this.clazz = clazz;
|
||||
this.modifiers = modifiers;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
public final MethodHandle getterMethodHandle;
|
||||
public final MethodHandle setterMethodHandle;
|
||||
|
||||
PainlessField(Field javaField, Class<?> typeParameter, MethodHandle getterMethodHandle, MethodHandle setterMethodHandle) {
|
||||
this.javaField = javaField;
|
||||
this.typeParameter = typeParameter;
|
||||
|
||||
this.getterMethodHandle = getterMethodHandle;
|
||||
this.setterMethodHandle = setterMethodHandle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,41 +19,119 @@
|
|||
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* The entire API for Painless. Also used as a whitelist for checking for legal
|
||||
* methods and fields during at both compile-time and runtime.
|
||||
*/
|
||||
public final class PainlessLookup {
|
||||
|
||||
public Collection<Class<?>> getStructs() {
|
||||
return classesToPainlessClasses.keySet();
|
||||
}
|
||||
|
||||
private final Map<String, Class<?>> canonicalClassNamesToClasses;
|
||||
private final Map<Class<?>, PainlessClass> classesToPainlessClasses;
|
||||
|
||||
PainlessLookup(Map<String, Class<?>> canonicalClassNamesToClasses, Map<Class<?>, PainlessClass> classesToPainlessClasses) {
|
||||
Objects.requireNonNull(canonicalClassNamesToClasses);
|
||||
Objects.requireNonNull(classesToPainlessClasses);
|
||||
|
||||
this.canonicalClassNamesToClasses = Collections.unmodifiableMap(canonicalClassNamesToClasses);
|
||||
this.classesToPainlessClasses = Collections.unmodifiableMap(classesToPainlessClasses);
|
||||
}
|
||||
|
||||
public Class<?> getClassFromBinaryName(String painlessType) {
|
||||
return canonicalClassNamesToClasses.get(painlessType.replace('$', '.'));
|
||||
public boolean isValidCanonicalClassName(String canonicalClassName) {
|
||||
Objects.requireNonNull(canonicalClassName);
|
||||
|
||||
return canonicalClassNamesToClasses.containsKey(canonicalClassName);
|
||||
}
|
||||
|
||||
public boolean isSimplePainlessType(String painlessType) {
|
||||
return canonicalClassNamesToClasses.containsKey(painlessType);
|
||||
}
|
||||
public Class<?> canonicalTypeNameToType(String painlessType) {
|
||||
Objects.requireNonNull(painlessType);
|
||||
|
||||
public PainlessClass getPainlessStructFromJavaClass(Class<?> clazz) {
|
||||
return classesToPainlessClasses.get(clazz);
|
||||
}
|
||||
|
||||
public Class<?> getJavaClassFromPainlessType(String painlessType) {
|
||||
return PainlessLookupUtility.canonicalTypeNameToType(painlessType, canonicalClassNamesToClasses);
|
||||
}
|
||||
|
||||
public Set<Class<?>> getClasses() {
|
||||
return classesToPainlessClasses.keySet();
|
||||
}
|
||||
|
||||
public PainlessClass lookupPainlessClass(Class<?> targetClass) {
|
||||
return classesToPainlessClasses.get(targetClass);
|
||||
}
|
||||
|
||||
public PainlessConstructor lookupPainlessConstructor(Class<?> targetClass, int constructorArity) {
|
||||
Objects.requireNonNull(targetClass);
|
||||
|
||||
PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
|
||||
String painlessConstructorKey = buildPainlessConstructorKey(constructorArity);
|
||||
|
||||
if (targetPainlessClass == null) {
|
||||
throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] " +
|
||||
"not found for constructor [" + painlessConstructorKey + "]");
|
||||
}
|
||||
|
||||
PainlessConstructor painlessConstructor = targetPainlessClass.constructors.get(painlessConstructorKey);
|
||||
|
||||
if (painlessConstructor == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"constructor [" + typeToCanonicalTypeName(targetClass) + ", " + painlessConstructorKey + "] not found");
|
||||
}
|
||||
|
||||
return painlessConstructor;
|
||||
}
|
||||
|
||||
public PainlessMethod lookupPainlessMethod(Class<?> targetClass, boolean isStatic, String methodName, int methodArity) {
|
||||
Objects.requireNonNull(targetClass);
|
||||
Objects.requireNonNull(methodName);
|
||||
|
||||
if (targetClass.isPrimitive()) {
|
||||
targetClass = PainlessLookupUtility.typeToBoxedType(targetClass);
|
||||
}
|
||||
|
||||
PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
|
||||
String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity);
|
||||
|
||||
if (targetPainlessClass == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"target class [" + typeToCanonicalTypeName(targetClass) + "] not found for method [" + painlessMethodKey + "]");
|
||||
}
|
||||
|
||||
PainlessMethod painlessMethod = isStatic ?
|
||||
targetPainlessClass.staticMethods.get(painlessMethodKey) :
|
||||
targetPainlessClass.methods.get(painlessMethodKey);
|
||||
|
||||
if (painlessMethod == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"method [" + typeToCanonicalTypeName(targetClass) + ", " + painlessMethodKey + "] not found");
|
||||
}
|
||||
|
||||
return painlessMethod;
|
||||
}
|
||||
|
||||
public PainlessField lookupPainlessField(Class<?> targetClass, boolean isStatic, String fieldName) {
|
||||
Objects.requireNonNull(targetClass);
|
||||
Objects.requireNonNull(fieldName);
|
||||
|
||||
PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
|
||||
String painlessFieldKey = buildPainlessFieldKey(fieldName);
|
||||
|
||||
if (targetPainlessClass == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"target class [" + typeToCanonicalTypeName(targetClass) + "] not found for field [" + painlessFieldKey + "]");
|
||||
}
|
||||
|
||||
PainlessField painlessField = isStatic ?
|
||||
targetPainlessClass.staticFields.get(painlessFieldKey) :
|
||||
targetPainlessClass.fields.get(painlessFieldKey);
|
||||
|
||||
if (painlessField == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"field [" + typeToCanonicalTypeName(targetClass) + ", " + painlessFieldKey + "] not found");
|
||||
}
|
||||
|
||||
return painlessField;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -540,7 +540,6 @@ public final class PainlessLookupBuilder {
|
|||
PainlessMethod painlessMethod = painlessClassBuilder.staticMethods.get(painlessMethodKey);
|
||||
|
||||
if (painlessMethod == null) {
|
||||
org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod);
|
||||
MethodHandle methodHandle;
|
||||
|
||||
try {
|
||||
|
@ -554,19 +553,17 @@ public final class PainlessLookupBuilder {
|
|||
|
||||
painlessMethod = painlessMethodCache.computeIfAbsent(
|
||||
new PainlessMethodCacheKey(targetClass, methodName, typeParameters),
|
||||
key -> new PainlessMethod(methodName, targetClass, null, returnType,
|
||||
typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle, methodType));
|
||||
key -> new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType));
|
||||
|
||||
painlessClassBuilder.staticMethods.put(painlessMethodKey, painlessMethod);
|
||||
} else if ((painlessMethod.name.equals(methodName) && painlessMethod.rtn == returnType &&
|
||||
painlessMethod.arguments.equals(typeParameters)) == false) {
|
||||
} else if (painlessMethod.returnType == returnType && painlessMethod.typeParameters.equals(typeParameters) == false) {
|
||||
throw new IllegalArgumentException("cannot have static methods " +
|
||||
"[[" + targetCanonicalClassName + "], [" + methodName + "], " +
|
||||
"[" + typeToCanonicalTypeName(returnType) + "], " +
|
||||
typesToCanonicalTypeNames(typeParameters) + "] and " +
|
||||
"[[" + targetCanonicalClassName + "], [" + methodName + "], " +
|
||||
"[" + typeToCanonicalTypeName(painlessMethod.rtn) + "], " +
|
||||
typesToCanonicalTypeNames(painlessMethod.arguments) + "] " +
|
||||
"[" + typeToCanonicalTypeName(painlessMethod.returnType) + "], " +
|
||||
typesToCanonicalTypeNames(painlessMethod.typeParameters) + "] " +
|
||||
"with the same arity and different return type or type parameters");
|
||||
}
|
||||
} else {
|
||||
|
@ -597,19 +594,17 @@ public final class PainlessLookupBuilder {
|
|||
|
||||
painlessMethod = painlessMethodCache.computeIfAbsent(
|
||||
new PainlessMethodCacheKey(targetClass, methodName, typeParameters),
|
||||
key -> new PainlessMethod(methodName, targetClass, augmentedClass, returnType,
|
||||
typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle, methodType));
|
||||
key -> new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType));
|
||||
|
||||
painlessClassBuilder.methods.put(painlessMethodKey, painlessMethod);
|
||||
} else if ((painlessMethod.name.equals(methodName) && painlessMethod.rtn == returnType &&
|
||||
painlessMethod.arguments.equals(typeParameters)) == false) {
|
||||
} else if (painlessMethod.returnType == returnType && painlessMethod.typeParameters.equals(typeParameters) == false) {
|
||||
throw new IllegalArgumentException("cannot have methods " +
|
||||
"[[" + targetCanonicalClassName + "], [" + methodName + "], " +
|
||||
"[" + typeToCanonicalTypeName(returnType) + "], " +
|
||||
typesToCanonicalTypeNames(typeParameters) + "] and " +
|
||||
"[[" + targetCanonicalClassName + "], [" + methodName + "], " +
|
||||
"[" + typeToCanonicalTypeName(painlessMethod.rtn) + "], " +
|
||||
typesToCanonicalTypeNames(painlessMethod.arguments) + "] " +
|
||||
"[" + typeToCanonicalTypeName(painlessMethod.returnType) + "], " +
|
||||
typesToCanonicalTypeNames(painlessMethod.typeParameters) + "] " +
|
||||
"with the same arity and different return type or type parameters");
|
||||
}
|
||||
}
|
||||
|
@ -684,11 +679,20 @@ public final class PainlessLookupBuilder {
|
|||
"for field [[" + targetCanonicalClassName + "], [" + fieldName + "]");
|
||||
}
|
||||
|
||||
MethodHandle methodHandleGetter;
|
||||
|
||||
try {
|
||||
methodHandleGetter = MethodHandles.publicLookup().unreflectGetter(javaField);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new IllegalArgumentException(
|
||||
"getter method handle not found for field [[" + targetCanonicalClassName + "], [" + fieldName + "]]");
|
||||
}
|
||||
|
||||
String painlessFieldKey = buildPainlessFieldKey(fieldName);
|
||||
|
||||
if (Modifier.isStatic(javaField.getModifiers())) {
|
||||
if (Modifier.isFinal(javaField.getModifiers()) == false) {
|
||||
throw new IllegalArgumentException("static field [[" + targetCanonicalClassName + "]. [" + fieldName + "]] must be final");
|
||||
throw new IllegalArgumentException("static field [[" + targetCanonicalClassName + "], [" + fieldName + "]] must be final");
|
||||
}
|
||||
|
||||
PainlessField painlessField = painlessClassBuilder.staticFields.get(painlessFieldKey);
|
||||
|
@ -696,28 +700,18 @@ public final class PainlessLookupBuilder {
|
|||
if (painlessField == null) {
|
||||
painlessField = painlessFieldCache.computeIfAbsent(
|
||||
new PainlessFieldCacheKey(targetClass, fieldName, typeParameter),
|
||||
key -> new PainlessField(fieldName, javaField.getName(), targetClass,
|
||||
typeParameter, javaField.getModifiers(), null, null));
|
||||
key -> new PainlessField(javaField, typeParameter, methodHandleGetter, null));
|
||||
|
||||
painlessClassBuilder.staticFields.put(painlessFieldKey, painlessField);
|
||||
} else if (painlessField.clazz != typeParameter) {
|
||||
} else if (painlessField.typeParameter != typeParameter) {
|
||||
throw new IllegalArgumentException("cannot have static fields " +
|
||||
"[[" + targetCanonicalClassName + "], [" + fieldName + "], [" +
|
||||
typeToCanonicalTypeName(typeParameter) + "] and " +
|
||||
"[[" + targetCanonicalClassName + "], [" + painlessField.name + "], " +
|
||||
typeToCanonicalTypeName(painlessField.clazz) + "] " +
|
||||
"with the same and different type parameters");
|
||||
"[[" + targetCanonicalClassName + "], [" + painlessField.javaField.getName() + "], " +
|
||||
typeToCanonicalTypeName(painlessField.typeParameter) + "] " +
|
||||
"with the same name and different type parameters");
|
||||
}
|
||||
} else {
|
||||
MethodHandle methodHandleGetter;
|
||||
|
||||
try {
|
||||
methodHandleGetter = MethodHandles.publicLookup().unreflectGetter(javaField);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new IllegalArgumentException(
|
||||
"getter method handle not found for field [[" + targetCanonicalClassName + "], [" + fieldName + "]]");
|
||||
}
|
||||
|
||||
MethodHandle methodHandleSetter;
|
||||
|
||||
try {
|
||||
|
@ -732,17 +726,16 @@ public final class PainlessLookupBuilder {
|
|||
if (painlessField == null) {
|
||||
painlessField = painlessFieldCache.computeIfAbsent(
|
||||
new PainlessFieldCacheKey(targetClass, painlessFieldKey, typeParameter),
|
||||
key -> new PainlessField(fieldName, javaField.getName(), targetClass,
|
||||
typeParameter, javaField.getModifiers(), methodHandleGetter, methodHandleSetter));
|
||||
key -> new PainlessField(javaField, typeParameter, methodHandleGetter, methodHandleSetter));
|
||||
|
||||
painlessClassBuilder.fields.put(fieldName, painlessField);
|
||||
} else if (painlessField.clazz != typeParameter) {
|
||||
} else if (painlessField.typeParameter != typeParameter) {
|
||||
throw new IllegalArgumentException("cannot have fields " +
|
||||
"[[" + targetCanonicalClassName + "], [" + fieldName + "], [" +
|
||||
typeToCanonicalTypeName(typeParameter) + "] and " +
|
||||
"[[" + targetCanonicalClassName + "], [" + painlessField.name + "], " +
|
||||
typeToCanonicalTypeName(painlessField.clazz) + "] " +
|
||||
"with the same and different type parameters");
|
||||
"[[" + targetCanonicalClassName + "], [" + painlessField.javaField.getName() + "], " +
|
||||
typeToCanonicalTypeName(painlessField.typeParameter) + "] " +
|
||||
"with the same name and different type parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -806,8 +799,8 @@ public final class PainlessLookupBuilder {
|
|||
PainlessMethod newPainlessMethod = painlessMethodEntry.getValue();
|
||||
PainlessMethod existingPainlessMethod = targetPainlessClassBuilder.methods.get(painlessMethodKey);
|
||||
|
||||
if (existingPainlessMethod == null || existingPainlessMethod.target != newPainlessMethod.target &&
|
||||
existingPainlessMethod.target.isAssignableFrom(newPainlessMethod.target)) {
|
||||
if (existingPainlessMethod == null || existingPainlessMethod.targetClass != newPainlessMethod.targetClass &&
|
||||
existingPainlessMethod.targetClass.isAssignableFrom(newPainlessMethod.targetClass)) {
|
||||
targetPainlessClassBuilder.methods.put(painlessMethodKey, newPainlessMethod);
|
||||
}
|
||||
}
|
||||
|
@ -817,8 +810,9 @@ public final class PainlessLookupBuilder {
|
|||
PainlessField newPainlessField = painlessFieldEntry.getValue();
|
||||
PainlessField existingPainlessField = targetPainlessClassBuilder.fields.get(painlessFieldKey);
|
||||
|
||||
if (existingPainlessField == null || existingPainlessField.target != newPainlessField.target &&
|
||||
existingPainlessField.target.isAssignableFrom(newPainlessField.target)) {
|
||||
if (existingPainlessField == null ||
|
||||
existingPainlessField.javaField.getDeclaringClass() != newPainlessField.javaField.getDeclaringClass() &&
|
||||
existingPainlessField.javaField.getDeclaringClass().isAssignableFrom(newPainlessField.javaField.getDeclaringClass())) {
|
||||
targetPainlessClassBuilder.fields.put(painlessFieldKey, newPainlessField);
|
||||
}
|
||||
}
|
||||
|
@ -832,27 +826,27 @@ public final class PainlessLookupBuilder {
|
|||
|
||||
private void cacheRuntimeHandles(PainlessClassBuilder painlessClassBuilder) {
|
||||
for (PainlessMethod painlessMethod : painlessClassBuilder.methods.values()) {
|
||||
String methodName = painlessMethod.name;
|
||||
int typeParametersSize = painlessMethod.arguments.size();
|
||||
String methodName = painlessMethod.javaMethod.getName();
|
||||
int typeParametersSize = painlessMethod.typeParameters.size();
|
||||
|
||||
if (typeParametersSize == 0 && methodName.startsWith("get") && methodName.length() > 3 &&
|
||||
Character.isUpperCase(methodName.charAt(3))) {
|
||||
painlessClassBuilder.getterMethodHandles.putIfAbsent(
|
||||
Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.handle);
|
||||
Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.methodHandle);
|
||||
} else if (typeParametersSize == 0 && methodName.startsWith("is") && methodName.length() > 2 &&
|
||||
Character.isUpperCase(methodName.charAt(2))) {
|
||||
painlessClassBuilder.getterMethodHandles.putIfAbsent(
|
||||
Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3), painlessMethod.handle);
|
||||
Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3), painlessMethod.methodHandle);
|
||||
} else if (typeParametersSize == 1 && methodName.startsWith("set") && methodName.length() > 3 &&
|
||||
Character.isUpperCase(methodName.charAt(3))) {
|
||||
painlessClassBuilder.setterMethodHandles.putIfAbsent(
|
||||
Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.handle);
|
||||
Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.methodHandle);
|
||||
}
|
||||
}
|
||||
|
||||
for (PainlessField painlessField : painlessClassBuilder.fields.values()) {
|
||||
painlessClassBuilder.getterMethodHandles.put(painlessField.name, painlessField.getter);
|
||||
painlessClassBuilder.setterMethodHandles.put(painlessField.name, painlessField.setter);
|
||||
painlessClassBuilder.getterMethodHandles.put(painlessField.javaField.getName(), painlessField.getterMethodHandle);
|
||||
painlessClassBuilder.setterMethodHandles.put(painlessField.javaField.getName(), painlessField.setterMethodHandle);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,67 +19,28 @@
|
|||
|
||||
package org.elasticsearch.painless.lookup;
|
||||
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PainlessMethod {
|
||||
public final String name;
|
||||
public final Class<?> target;
|
||||
public final Class<?> augmentation;
|
||||
public final Class<?> rtn;
|
||||
public final List<Class<?>> arguments;
|
||||
public final org.objectweb.asm.commons.Method method;
|
||||
public final int modifiers;
|
||||
public final MethodHandle handle;
|
||||
public final Method javaMethod;
|
||||
public final Class<?> targetClass;
|
||||
public final Class<?> returnType;
|
||||
public final List<Class<?>> typeParameters;
|
||||
public final MethodHandle methodHandle;
|
||||
public final MethodType methodType;
|
||||
|
||||
public PainlessMethod(String name, Class<?> target, Class<?> augmentation, Class<?> rtn, List<Class<?>> arguments,
|
||||
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle, MethodType methodType) {
|
||||
this.name = name;
|
||||
this.augmentation = augmentation;
|
||||
this.target = target;
|
||||
this.rtn = rtn;
|
||||
this.arguments = Collections.unmodifiableList(arguments);
|
||||
this.method = method;
|
||||
this.modifiers = modifiers;
|
||||
this.handle = handle;
|
||||
public PainlessMethod(Method javaMethod, Class<?> targetClass, Class<?> returnType, List<Class<?>> typeParameters,
|
||||
MethodHandle methodHandle, MethodType methodType) {
|
||||
|
||||
this.javaMethod = javaMethod;
|
||||
this.targetClass = targetClass;
|
||||
this.returnType = returnType;
|
||||
this.typeParameters = Collections.unmodifiableList(typeParameters);
|
||||
this.methodHandle = methodHandle;
|
||||
this.methodType = methodType;
|
||||
}
|
||||
|
||||
public void write(MethodWriter writer) {
|
||||
final org.objectweb.asm.Type type;
|
||||
final Class<?> clazz;
|
||||
if (augmentation != null) {
|
||||
assert Modifier.isStatic(modifiers);
|
||||
clazz = augmentation;
|
||||
type = org.objectweb.asm.Type.getType(augmentation);
|
||||
} else {
|
||||
clazz = target;
|
||||
type = Type.getType(target);
|
||||
}
|
||||
|
||||
if (Modifier.isStatic(modifiers)) {
|
||||
// invokeStatic assumes that the owner class is not an interface, so this is a
|
||||
// special case for interfaces where the interface method boolean needs to be set to
|
||||
// true to reference the appropriate class constant when calling a static interface
|
||||
// method since java 8 did not check, but java 9 and 10 do
|
||||
if (Modifier.isInterface(clazz.getModifiers())) {
|
||||
writer.visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||
type.getInternalName(), name, methodType.toMethodDescriptorString(), true);
|
||||
} else {
|
||||
writer.invokeStatic(type, method);
|
||||
}
|
||||
} else if (Modifier.isInterface(clazz.getModifiers())) {
|
||||
writer.invokeInterface(type, method);
|
||||
} else {
|
||||
writer.invokeVirtual(type, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,10 +21,11 @@ package org.elasticsearch.painless.node;
|
|||
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.LocalMethod;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.objectweb.asm.commons.Method;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -40,7 +41,7 @@ public final class ECallLocal extends AExpression {
|
|||
private final String name;
|
||||
private final List<AExpression> arguments;
|
||||
|
||||
private PainlessMethod method = null;
|
||||
private LocalMethod method = null;
|
||||
|
||||
public ECallLocal(Location location, String name, List<AExpression> arguments) {
|
||||
super(location);
|
||||
|
@ -68,14 +69,14 @@ public final class ECallLocal extends AExpression {
|
|||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||
AExpression expression = arguments.get(argument);
|
||||
|
||||
expression.expected = method.arguments.get(argument);
|
||||
expression.expected = method.typeParameters.get(argument);
|
||||
expression.internal = true;
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
actual = method.rtn;
|
||||
actual = method.returnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,7 +87,7 @@ public final class ECallLocal extends AExpression {
|
|||
argument.write(writer, globals);
|
||||
}
|
||||
|
||||
writer.invokeStatic(CLASS_TYPE, method.method);
|
||||
writer.invokeStatic(CLASS_TYPE, new Method(method.name, method.methodType.toMethodDescriptorString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -81,14 +81,14 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
|
|||
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);
|
||||
for (int i = 0; i < ref.interfaceMethod.typeParameters.size(); ++i) {
|
||||
Class<?> from = ref.interfaceMethod.typeParameters.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.delegateReturnType, ref.interfaceMethod.rtn, false, true);
|
||||
if (ref.interfaceMethod.returnType != void.class) {
|
||||
AnalyzerCaster.getLegalCast(location, ref.delegateReturnType, ref.interfaceMethod.returnType, false, true);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw createError(e);
|
||||
|
|
|
@ -50,7 +50,7 @@ public final class EExplicit extends AExpression {
|
|||
@Override
|
||||
void analyze(Locals locals) {
|
||||
try {
|
||||
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type);
|
||||
actual = locals.getPainlessLookup().canonicalTypeNameToType(type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.painless.AnalyzerCaster;
|
|||
import org.elasticsearch.painless.FunctionRef;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.LocalMethod;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
|
@ -65,13 +66,12 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||
try {
|
||||
if ("this".equals(type)) {
|
||||
// user's own function
|
||||
PainlessMethod interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
PainlessMethod interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface");
|
||||
}
|
||||
PainlessMethod delegateMethod =
|
||||
locals.getMethod(PainlessLookupUtility.buildPainlessMethodKey(call, interfaceMethod.arguments.size()));
|
||||
LocalMethod delegateMethod = locals.getMethod(Locals.buildLocalMethodKey(call, interfaceMethod.typeParameters.size()));
|
||||
if (delegateMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], function not found");
|
||||
|
@ -79,14 +79,14 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||
ref = new FunctionRef(expected, interfaceMethod, delegateMethod, 0);
|
||||
|
||||
// check casts between the interface method and the delegate method are legal
|
||||
for (int i = 0; i < interfaceMethod.arguments.size(); ++i) {
|
||||
Class<?> from = interfaceMethod.arguments.get(i);
|
||||
Class<?> to = delegateMethod.arguments.get(i);
|
||||
for (int i = 0; i < interfaceMethod.typeParameters.size(); ++i) {
|
||||
Class<?> from = interfaceMethod.typeParameters.get(i);
|
||||
Class<?> to = delegateMethod.typeParameters.get(i);
|
||||
AnalyzerCaster.getLegalCast(location, from, to, false, true);
|
||||
}
|
||||
|
||||
if (interfaceMethod.rtn != void.class) {
|
||||
AnalyzerCaster.getLegalCast(location, delegateMethod.rtn, interfaceMethod.rtn, false, true);
|
||||
if (interfaceMethod.returnType != void.class) {
|
||||
AnalyzerCaster.getLegalCast(location, delegateMethod.returnType, interfaceMethod.returnType, false, true);
|
||||
}
|
||||
} else {
|
||||
// whitelist lookup
|
||||
|
|
|
@ -58,7 +58,7 @@ public final class EInstanceof extends AExpression {
|
|||
|
||||
// ensure the specified type is part of the definition
|
||||
try {
|
||||
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.painless.AnalyzerCaster;
|
|||
import org.elasticsearch.painless.FunctionRef;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.LocalMethod;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
@ -120,27 +121,27 @@ public final class ELambda extends AExpression implements ILambda {
|
|||
}
|
||||
} else {
|
||||
// we know the method statically, infer return type and any unknown/def types
|
||||
interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
|
||||
interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw createError(new IllegalArgumentException("Cannot pass lambda to " +
|
||||
"[" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"));
|
||||
}
|
||||
// check arity before we manipulate parameters
|
||||
if (interfaceMethod.arguments.size() != paramTypeStrs.size())
|
||||
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
|
||||
if (interfaceMethod.typeParameters.size() != paramTypeStrs.size())
|
||||
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.javaMethod.getName() +
|
||||
"] in [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "]");
|
||||
// for method invocation, its allowed to ignore the return value
|
||||
if (interfaceMethod.rtn == void.class) {
|
||||
if (interfaceMethod.returnType == void.class) {
|
||||
returnType = def.class;
|
||||
} else {
|
||||
returnType = interfaceMethod.rtn;
|
||||
returnType = interfaceMethod.returnType;
|
||||
}
|
||||
// replace any null types with the actual type
|
||||
actualParamTypeStrs = new ArrayList<>(paramTypeStrs.size());
|
||||
for (int i = 0; i < paramTypeStrs.size(); i++) {
|
||||
String paramType = paramTypeStrs.get(i);
|
||||
if (paramType == null) {
|
||||
actualParamTypeStrs.add(PainlessLookupUtility.typeToCanonicalTypeName(interfaceMethod.arguments.get(i)));
|
||||
actualParamTypeStrs.add(PainlessLookupUtility.typeToCanonicalTypeName(interfaceMethod.typeParameters.get(i)));
|
||||
} else {
|
||||
actualParamTypeStrs.add(paramType);
|
||||
}
|
||||
|
@ -183,20 +184,22 @@ public final class ELambda extends AExpression implements ILambda {
|
|||
} else {
|
||||
defPointer = null;
|
||||
try {
|
||||
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captures.size());
|
||||
LocalMethod localMethod =
|
||||
new LocalMethod(desugared.name, desugared.returnType, desugared.typeParameters, desugared.methodType);
|
||||
ref = new FunctionRef(expected, interfaceMethod, localMethod, captures.size());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw createError(e);
|
||||
}
|
||||
|
||||
// check casts between the interface method and the delegate method are legal
|
||||
for (int i = 0; i < interfaceMethod.arguments.size(); ++i) {
|
||||
Class<?> from = interfaceMethod.arguments.get(i);
|
||||
for (int i = 0; i < interfaceMethod.typeParameters.size(); ++i) {
|
||||
Class<?> from = interfaceMethod.typeParameters.get(i);
|
||||
Class<?> to = desugared.parameters.get(i + captures.size()).clazz;
|
||||
AnalyzerCaster.getLegalCast(location, from, to, false, true);
|
||||
}
|
||||
|
||||
if (interfaceMethod.rtn != void.class) {
|
||||
AnalyzerCaster.getLegalCast(location, desugared.rtnType, interfaceMethod.rtn, false, true);
|
||||
if (interfaceMethod.returnType != void.class) {
|
||||
AnalyzerCaster.getLegalCast(location, desugared.returnType, interfaceMethod.returnType, false, true);
|
||||
}
|
||||
|
||||
actual = expected;
|
||||
|
|
|
@ -24,7 +24,6 @@ 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;
|
||||
|
@ -64,18 +63,16 @@ public final class EListInit extends AExpression {
|
|||
|
||||
actual = ArrayList.class;
|
||||
|
||||
constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(
|
||||
PainlessLookupUtility.buildPainlessConstructorKey(0));
|
||||
|
||||
if (constructor == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
try {
|
||||
constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
|
||||
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods
|
||||
.get(PainlessLookupUtility.buildPainlessMethodKey("add", 1));
|
||||
|
||||
if (method == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
try {
|
||||
method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "add", 1);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
|
||||
for (int index = 0; index < values.size(); ++index) {
|
||||
|
@ -100,7 +97,7 @@ public final class EListInit extends AExpression {
|
|||
for (AExpression value : values) {
|
||||
writer.dup();
|
||||
value.write(writer, globals);
|
||||
method.write(writer);
|
||||
writer.invokeMethodCall(method);
|
||||
writer.pop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ 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;
|
||||
|
@ -70,18 +69,16 @@ public final class EMapInit extends AExpression {
|
|||
|
||||
actual = HashMap.class;
|
||||
|
||||
constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(
|
||||
PainlessLookupUtility.buildPainlessConstructorKey(0));
|
||||
|
||||
if (constructor == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
try {
|
||||
constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
|
||||
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods
|
||||
.get(PainlessLookupUtility.buildPainlessMethodKey("put", 2));
|
||||
|
||||
if (method == null) {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
try {
|
||||
method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "put", 2);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
|
||||
if (keys.size() != values.size()) {
|
||||
|
@ -123,7 +120,7 @@ public final class EMapInit extends AExpression {
|
|||
writer.dup();
|
||||
key.write(writer, globals);
|
||||
value.write(writer, globals);
|
||||
method.write(writer);
|
||||
writer.invokeMethodCall(method);
|
||||
writer.pop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public final class ENewArray extends AExpression {
|
|||
Class<?> clazz;
|
||||
|
||||
try {
|
||||
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ 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.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessConstructor;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.objectweb.asm.Type;
|
||||
|
@ -60,38 +59,36 @@ public final class ENewObj extends AExpression {
|
|||
@Override
|
||||
void analyze(Locals locals) {
|
||||
try {
|
||||
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
actual = locals.getPainlessLookup().canonicalTypeNameToType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
||||
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual);
|
||||
constructor = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(arguments.size()));
|
||||
|
||||
if (constructor != null) {
|
||||
Class<?>[] types = new Class<?>[constructor.typeParameters.size()];
|
||||
constructor.typeParameters.toArray(types);
|
||||
|
||||
if (constructor.typeParameters.size() != arguments.size()) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "] " +
|
||||
"expected [" + constructor.typeParameters.size() + "] arguments, but found [" + arguments.size() + "]."));
|
||||
}
|
||||
|
||||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||
AExpression expression = arguments.get(argument);
|
||||
|
||||
expression.expected = types[argument];
|
||||
expression.internal = true;
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
} else {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Unknown new call on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "]."));
|
||||
try {
|
||||
constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, arguments.size());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
|
||||
Class<?>[] types = new Class<?>[constructor.typeParameters.size()];
|
||||
constructor.typeParameters.toArray(types);
|
||||
|
||||
if (constructor.typeParameters.size() != arguments.size()) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "] " +
|
||||
"expected [" + constructor.typeParameters.size() + "] arguments, but found [" + arguments.size() + "]."));
|
||||
}
|
||||
|
||||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||
AExpression expression = arguments.get(argument);
|
||||
|
||||
expression.expected = types[argument];
|
||||
expression.internal = true;
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -48,7 +48,7 @@ public final class EStatic extends AExpression {
|
|||
@Override
|
||||
void analyze(Locals locals) {
|
||||
try {
|
||||
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type);
|
||||
actual = locals.getPainlessLookup().canonicalTypeNameToType(type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ 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.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.def;
|
||||
|
||||
|
@ -66,26 +64,16 @@ public final class PCallInvoke extends AExpression {
|
|||
prefix.expected = prefix.actual;
|
||||
prefix = prefix.cast(locals);
|
||||
|
||||
if (prefix.actual.isArray()) {
|
||||
throw createError(new IllegalArgumentException("Illegal call [" + name + "] on array type."));
|
||||
}
|
||||
|
||||
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual);
|
||||
|
||||
if (prefix.actual.isPrimitive()) {
|
||||
struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(PainlessLookupUtility.typeToBoxedType(prefix.actual));
|
||||
}
|
||||
|
||||
String methodKey = PainlessLookupUtility.buildPainlessMethodKey(name, arguments.size());
|
||||
PainlessMethod method = prefix instanceof EStatic ? struct.staticMethods.get(methodKey) : struct.methods.get(methodKey);
|
||||
|
||||
if (method != null) {
|
||||
sub = new PSubCallInvoke(location, method, prefix.actual, arguments);
|
||||
} else if (prefix.actual == def.class) {
|
||||
if (prefix.actual == def.class) {
|
||||
sub = new PSubDefCall(location, name, arguments);
|
||||
} else {
|
||||
throw createError(new IllegalArgumentException("Unknown call [" + name + "] with [" + arguments.size() + "] arguments " +
|
||||
"on type [" + PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual) + "]."));
|
||||
try {
|
||||
PainlessMethod method =
|
||||
locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, prefix instanceof EStatic, name, arguments.size());
|
||||
sub = new PSubCallInvoke(location, method, prefix.actual, arguments);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
}
|
||||
|
||||
if (nullSafe) {
|
||||
|
|
|
@ -23,8 +23,6 @@ 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.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessField;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.lookup.def;
|
||||
|
@ -67,26 +65,34 @@ public final class PField extends AStoreable {
|
|||
} else if (prefix.actual == def.class) {
|
||||
sub = new PSubDefField(location, value);
|
||||
} else {
|
||||
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual);
|
||||
PainlessField field = prefix instanceof EStatic ? struct.staticFields.get(value) : struct.fields.get(value);
|
||||
try {
|
||||
sub = new PSubField(location,
|
||||
locals.getPainlessLookup().lookupPainlessField(prefix.actual, prefix instanceof EStatic, value));
|
||||
} catch (IllegalArgumentException fieldIAE) {
|
||||
PainlessMethod getter;
|
||||
PainlessMethod setter;
|
||||
|
||||
if (field != null) {
|
||||
sub = new PSubField(location, field);
|
||||
} else {
|
||||
PainlessMethod getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(
|
||||
"get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0));
|
||||
|
||||
if (getter == null) {
|
||||
getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(
|
||||
"is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0));
|
||||
try {
|
||||
getter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false,
|
||||
"get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0);
|
||||
} catch (IllegalArgumentException getIAE) {
|
||||
try {
|
||||
getter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false,
|
||||
"is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0);
|
||||
} catch (IllegalArgumentException isIAE) {
|
||||
getter = null;
|
||||
}
|
||||
}
|
||||
|
||||
PainlessMethod setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(
|
||||
"set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 1));
|
||||
try {
|
||||
setter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false,
|
||||
"set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0);
|
||||
} catch (IllegalArgumentException setIAE) {
|
||||
setter = null;
|
||||
}
|
||||
|
||||
if (getter != null || setter != null) {
|
||||
sub = new PSubShortcut(
|
||||
location, value, PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual), getter, setter);
|
||||
sub = new PSubShortcut(location, value, PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual), getter, setter);
|
||||
} else {
|
||||
EConstant index = new EConstant(location, value);
|
||||
index.analyze(locals);
|
||||
|
@ -99,12 +105,11 @@ public final class PField extends AStoreable {
|
|||
sub = new PSubListShortcut(location, prefix.actual, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sub == null) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Unknown field [" + value + "] for type [" + PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual) + "]."));
|
||||
if (sub == null) {
|
||||
throw createError(fieldIAE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nullSafe) {
|
||||
|
|
|
@ -56,14 +56,14 @@ final class PSubCallInvoke extends AExpression {
|
|||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||
AExpression expression = arguments.get(argument);
|
||||
|
||||
expression.expected = method.arguments.get(argument);
|
||||
expression.expected = method.typeParameters.get(argument);
|
||||
expression.internal = true;
|
||||
expression.analyze(locals);
|
||||
arguments.set(argument, expression.cast(locals));
|
||||
}
|
||||
|
||||
statement = true;
|
||||
actual = method.rtn;
|
||||
actual = method.returnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,11 +78,11 @@ final class PSubCallInvoke extends AExpression {
|
|||
argument.write(writer, globals);
|
||||
}
|
||||
|
||||
method.write(writer);
|
||||
writer.invokeMethodCall(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return singleLineToStringWithOptionalArgs(arguments, prefix, method.name);
|
||||
return singleLineToStringWithOptionalArgs(arguments, prefix, method.javaMethod.getName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,22 +51,24 @@ final class PSubField extends AStoreable {
|
|||
|
||||
@Override
|
||||
void analyze(Locals locals) {
|
||||
if (write && Modifier.isFinal(field.modifiers)) {
|
||||
throw createError(new IllegalArgumentException("Cannot write to read-only field [" + field.name + "] for type " +
|
||||
"[" + PainlessLookupUtility.typeToCanonicalTypeName(field.clazz) + "]."));
|
||||
if (write && Modifier.isFinal(field.javaField.getModifiers())) {
|
||||
throw createError(new IllegalArgumentException("Cannot write to read-only field [" + field.javaField.getName() + "] " +
|
||||
"for type [" + PainlessLookupUtility.typeToCanonicalTypeName(field.javaField.getDeclaringClass()) + "]."));
|
||||
}
|
||||
|
||||
actual = field.clazz;
|
||||
actual = field.typeParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
if (java.lang.reflect.Modifier.isStatic(field.modifiers)) {
|
||||
writer.getStatic(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz));
|
||||
if (java.lang.reflect.Modifier.isStatic(field.javaField.getModifiers())) {
|
||||
writer.getStatic(Type.getType(
|
||||
field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter));
|
||||
} else {
|
||||
writer.getField(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz));
|
||||
writer.getField(Type.getType(
|
||||
field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,10 +96,12 @@ final class PSubField extends AStoreable {
|
|||
void load(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
if (java.lang.reflect.Modifier.isStatic(field.modifiers)) {
|
||||
writer.getStatic(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz));
|
||||
if (java.lang.reflect.Modifier.isStatic(field.javaField.getModifiers())) {
|
||||
writer.getStatic(Type.getType(
|
||||
field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter));
|
||||
} else {
|
||||
writer.getField(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz));
|
||||
writer.getField(Type.getType(
|
||||
field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,15 +109,17 @@ final class PSubField extends AStoreable {
|
|||
void store(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
if (java.lang.reflect.Modifier.isStatic(field.modifiers)) {
|
||||
writer.putStatic(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz));
|
||||
if (java.lang.reflect.Modifier.isStatic(field.javaField.getModifiers())) {
|
||||
writer.putStatic(Type.getType(
|
||||
field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter));
|
||||
} else {
|
||||
writer.putField(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz));
|
||||
writer.putField(Type.getType(
|
||||
field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return singleLineToString(prefix, field.name);
|
||||
return singleLineToString(prefix, field.javaField.getName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.elasticsearch.painless.Locals;
|
|||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
import org.elasticsearch.painless.WriterConstants;
|
||||
import org.elasticsearch.painless.lookup.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
|
||||
|
@ -56,23 +55,26 @@ final class PSubListShortcut extends AStoreable {
|
|||
|
||||
@Override
|
||||
void analyze(Locals locals) {
|
||||
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(targetClass);
|
||||
String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass);
|
||||
|
||||
getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("get", 1));
|
||||
setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("set", 2));
|
||||
try {
|
||||
getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1);
|
||||
setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "set", 2);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
|
||||
if (getter != null && (getter.rtn == void.class || getter.arguments.size() != 1 ||
|
||||
getter.arguments.get(0) != int.class)) {
|
||||
if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1 ||
|
||||
getter.typeParameters.get(0) != int.class)) {
|
||||
throw createError(new IllegalArgumentException("Illegal list get shortcut for type [" + canonicalClassName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && (setter.arguments.size() != 2 || setter.arguments.get(0) != int.class)) {
|
||||
if (setter != null && (setter.typeParameters.size() != 2 || setter.typeParameters.get(0) != int.class)) {
|
||||
throw createError(new IllegalArgumentException("Illegal list set shortcut for type [" + canonicalClassName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null && (!getter.arguments.get(0).equals(setter.arguments.get(0))
|
||||
|| !getter.rtn.equals(setter.arguments.get(1)))) {
|
||||
if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0))
|
||||
|| !getter.returnType.equals(setter.typeParameters.get(1)))) {
|
||||
throw createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
|
@ -81,7 +83,7 @@ final class PSubListShortcut extends AStoreable {
|
|||
index.analyze(locals);
|
||||
index = index.cast(locals);
|
||||
|
||||
actual = setter != null ? setter.arguments.get(1) : getter.rtn;
|
||||
actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
||||
} else {
|
||||
throw createError(new IllegalArgumentException("Illegal list shortcut for type [" + canonicalClassName + "]."));
|
||||
}
|
||||
|
@ -119,21 +121,18 @@ final class PSubListShortcut extends AStoreable {
|
|||
@Override
|
||||
void load(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
writer.invokeMethodCall(getter);
|
||||
|
||||
getter.write(writer);
|
||||
|
||||
if (getter.rtn == getter.handle.type().returnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.rtn));
|
||||
if (getter.returnType == getter.javaMethod.getReturnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.returnType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void store(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
setter.write(writer);
|
||||
|
||||
writer.writePop(MethodWriter.getType(setter.rtn).getSize());
|
||||
writer.invokeMethodCall(setter);
|
||||
writer.writePop(MethodWriter.getType(setter.returnType).getSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,6 @@ 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.PainlessClass;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
|
||||
|
@ -55,31 +54,34 @@ final class PSubMapShortcut extends AStoreable {
|
|||
|
||||
@Override
|
||||
void analyze(Locals locals) {
|
||||
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(targetClass);
|
||||
String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass);
|
||||
|
||||
getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("get", 1));
|
||||
setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("put", 2));
|
||||
try {
|
||||
getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1);
|
||||
setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "put", 2);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
|
||||
if (getter != null && (getter.rtn == void.class || getter.arguments.size() != 1)) {
|
||||
if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1)) {
|
||||
throw createError(new IllegalArgumentException("Illegal map get shortcut for type [" + canonicalClassName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && setter.arguments.size() != 2) {
|
||||
if (setter != null && setter.typeParameters.size() != 2) {
|
||||
throw createError(new IllegalArgumentException("Illegal map set shortcut for type [" + canonicalClassName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null &&
|
||||
(!getter.arguments.get(0).equals(setter.arguments.get(0)) || !getter.rtn.equals(setter.arguments.get(1)))) {
|
||||
if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0)) ||
|
||||
!getter.returnType.equals(setter.typeParameters.get(1)))) {
|
||||
throw createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((read || write) && (!read || getter != null) && (!write || setter != null)) {
|
||||
index.expected = setter != null ? setter.arguments.get(0) : getter.arguments.get(0);
|
||||
index.expected = setter != null ? setter.typeParameters.get(0) : getter.typeParameters.get(0);
|
||||
index.analyze(locals);
|
||||
index = index.cast(locals);
|
||||
|
||||
actual = setter != null ? setter.arguments.get(1) : getter.rtn;
|
||||
actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
||||
} else {
|
||||
throw createError(new IllegalArgumentException("Illegal map shortcut for type [" + canonicalClassName + "]."));
|
||||
}
|
||||
|
@ -90,11 +92,10 @@ final class PSubMapShortcut extends AStoreable {
|
|||
index.write(writer, globals);
|
||||
|
||||
writer.writeDebugInfo(location);
|
||||
writer.invokeMethodCall(getter);
|
||||
|
||||
getter.write(writer);
|
||||
|
||||
if (getter.rtn != getter.handle.type().returnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.rtn));
|
||||
if (getter.returnType != getter.javaMethod.getReturnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.returnType));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,21 +122,18 @@ final class PSubMapShortcut extends AStoreable {
|
|||
@Override
|
||||
void load(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
writer.invokeMethodCall(getter);
|
||||
|
||||
getter.write(writer);
|
||||
|
||||
if (getter.rtn != getter.handle.type().returnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.rtn));
|
||||
if (getter.returnType != getter.javaMethod.getReturnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.returnType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void store(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
setter.write(writer);
|
||||
|
||||
writer.writePop(MethodWriter.getType(setter.rtn).getSize());
|
||||
writer.invokeMethodCall(setter);
|
||||
writer.writePop(MethodWriter.getType(setter.returnType).getSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -53,22 +53,22 @@ final class PSubShortcut extends AStoreable {
|
|||
|
||||
@Override
|
||||
void analyze(Locals locals) {
|
||||
if (getter != null && (getter.rtn == void.class || !getter.arguments.isEmpty())) {
|
||||
if (getter != null && (getter.returnType == void.class || !getter.typeParameters.isEmpty())) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Illegal get shortcut on field [" + value + "] for type [" + type + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && (setter.rtn != void.class || setter.arguments.size() != 1)) {
|
||||
if (setter != null && (setter.returnType != void.class || setter.typeParameters.size() != 1)) {
|
||||
throw createError(new IllegalArgumentException(
|
||||
"Illegal set shortcut on field [" + value + "] for type [" + type + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null && setter.arguments.get(0) != getter.rtn) {
|
||||
if (getter != null && setter != null && setter.typeParameters.get(0) != getter.returnType) {
|
||||
throw createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((getter != null || setter != null) && (!read || getter != null) && (!write || setter != null)) {
|
||||
actual = setter != null ? setter.arguments.get(0) : getter.rtn;
|
||||
actual = setter != null ? setter.typeParameters.get(0) : getter.returnType;
|
||||
} else {
|
||||
throw createError(new IllegalArgumentException("Illegal shortcut on field [" + value + "] for type [" + type + "]."));
|
||||
}
|
||||
|
@ -78,10 +78,10 @@ final class PSubShortcut extends AStoreable {
|
|||
void write(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
getter.write(writer);
|
||||
writer.invokeMethodCall(getter);
|
||||
|
||||
if (!getter.rtn.equals(getter.handle.type().returnType())) {
|
||||
writer.checkCast(MethodWriter.getType(getter.rtn));
|
||||
if (!getter.returnType.equals(getter.javaMethod.getReturnType())) {
|
||||
writer.checkCast(MethodWriter.getType(getter.returnType));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,10 +109,10 @@ final class PSubShortcut extends AStoreable {
|
|||
void load(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
getter.write(writer);
|
||||
writer.invokeMethodCall(getter);
|
||||
|
||||
if (getter.rtn != getter.handle.type().returnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.rtn));
|
||||
if (getter.returnType != getter.javaMethod.getReturnType()) {
|
||||
writer.checkCast(MethodWriter.getType(getter.returnType));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,9 +120,9 @@ final class PSubShortcut extends AStoreable {
|
|||
void store(MethodWriter writer, Globals globals) {
|
||||
writer.writeDebugInfo(location);
|
||||
|
||||
setter.write(writer);
|
||||
writer.invokeMethodCall(setter);
|
||||
|
||||
writer.writePop(MethodWriter.getType(setter.rtn).getSize());
|
||||
writer.writePop(MethodWriter.getType(setter.returnType).getSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -67,7 +67,7 @@ public final class SCatch extends AStatement {
|
|||
Class<?> clazz;
|
||||
|
||||
try {
|
||||
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public final class SDeclaration extends AStatement {
|
|||
Class<?> clazz;
|
||||
|
||||
try {
|
||||
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class SEach extends AStatement {
|
|||
Class<?> clazz;
|
||||
|
||||
try {
|
||||
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
|
||||
clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||
}
|
||||
|
|
|
@ -31,14 +31,12 @@ import org.elasticsearch.painless.MethodWriter;
|
|||
import org.elasticsearch.painless.WriterConstants;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.node.SSource.Reserved;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -92,9 +90,12 @@ public final class SFunction extends AStatement {
|
|||
private final List<AStatement> statements;
|
||||
public final boolean synthetic;
|
||||
|
||||
Class<?> rtnType = null;
|
||||
Class<?> returnType;
|
||||
List<Class<?>> typeParameters;
|
||||
MethodType methodType;
|
||||
|
||||
org.objectweb.asm.commons.Method method;
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
PainlessMethod method = null;
|
||||
|
||||
private Variable loop = null;
|
||||
|
||||
|
@ -120,7 +121,7 @@ public final class SFunction extends AStatement {
|
|||
|
||||
void generateSignature(PainlessLookup painlessLookup) {
|
||||
try {
|
||||
rtnType = painlessLookup.getJavaClassFromPainlessType(rtnTypeStr);
|
||||
returnType = painlessLookup.canonicalTypeNameToType(rtnTypeStr);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "]."));
|
||||
}
|
||||
|
@ -134,7 +135,7 @@ public final class SFunction extends AStatement {
|
|||
|
||||
for (int param = 0; param < this.paramTypeStrs.size(); ++param) {
|
||||
try {
|
||||
Class<?> paramType = painlessLookup.getJavaClassFromPainlessType(this.paramTypeStrs.get(param));
|
||||
Class<?> paramType = painlessLookup.canonicalTypeNameToType(this.paramTypeStrs.get(param));
|
||||
|
||||
paramClasses[param] = PainlessLookupUtility.typeToJavaType(paramType);
|
||||
paramTypes.add(paramType);
|
||||
|
@ -145,11 +146,10 @@ public final class SFunction extends AStatement {
|
|||
}
|
||||
}
|
||||
|
||||
int modifiers = Modifier.STATIC | Modifier.PRIVATE;
|
||||
org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(name, MethodType.methodType(
|
||||
PainlessLookupUtility.typeToJavaType(rtnType), paramClasses).toMethodDescriptorString());
|
||||
MethodType methodType = MethodType.methodType(PainlessLookupUtility.typeToJavaType(rtnType), paramClasses);
|
||||
this.method = new PainlessMethod(name, null, null, rtnType, paramTypes, method, modifiers, null, methodType);
|
||||
typeParameters = paramTypes;
|
||||
methodType = MethodType.methodType(PainlessLookupUtility.typeToJavaType(returnType), paramClasses);
|
||||
method = new org.objectweb.asm.commons.Method(name, MethodType.methodType(
|
||||
PainlessLookupUtility.typeToJavaType(returnType), paramClasses).toMethodDescriptorString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -177,7 +177,7 @@ public final class SFunction extends AStatement {
|
|||
allEscape = statement.allEscape;
|
||||
}
|
||||
|
||||
if (!methodEscape && rtnType != void.class) {
|
||||
if (!methodEscape && returnType != void.class) {
|
||||
throw createError(new IllegalArgumentException("Not all paths provide a return value for method [" + name + "]."));
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ public final class SFunction extends AStatement {
|
|||
if (synthetic) {
|
||||
access |= Opcodes.ACC_SYNTHETIC;
|
||||
}
|
||||
final MethodWriter function = new MethodWriter(access, method.method, writer, globals.getStatements(), settings);
|
||||
final MethodWriter function = new MethodWriter(access, method, writer, globals.getStatements(), settings);
|
||||
function.visitCode();
|
||||
write(function, globals);
|
||||
function.endMethod();
|
||||
|
@ -212,7 +212,7 @@ public final class SFunction extends AStatement {
|
|||
}
|
||||
|
||||
if (!methodEscape) {
|
||||
if (rtnType == void.class) {
|
||||
if (returnType == void.class) {
|
||||
function.returnValue();
|
||||
} else {
|
||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||
|
@ -225,11 +225,7 @@ public final class SFunction extends AStatement {
|
|||
}
|
||||
|
||||
private void initializeConstant(MethodWriter writer) {
|
||||
final Handle handle = new Handle(Opcodes.H_INVOKESTATIC,
|
||||
CLASS_TYPE.getInternalName(),
|
||||
name,
|
||||
method.method.getDescriptor(),
|
||||
false);
|
||||
final Handle handle = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), name, method.getDescriptor(), false);
|
||||
writer.push(handle);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.painless.CompilerSettings;
|
|||
import org.elasticsearch.painless.Constant;
|
||||
import org.elasticsearch.painless.Globals;
|
||||
import org.elasticsearch.painless.Locals;
|
||||
import org.elasticsearch.painless.Locals.LocalMethod;
|
||||
import org.elasticsearch.painless.Locals.Variable;
|
||||
import org.elasticsearch.painless.Location;
|
||||
import org.elasticsearch.painless.MethodWriter;
|
||||
|
@ -30,8 +31,6 @@ import org.elasticsearch.painless.ScriptClassInfo;
|
|||
import org.elasticsearch.painless.SimpleChecksAdapter;
|
||||
import org.elasticsearch.painless.WriterConstants;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
||||
import org.elasticsearch.painless.lookup.PainlessMethod;
|
||||
import org.elasticsearch.painless.node.SFunction.FunctionReserved;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
|
@ -165,14 +164,15 @@ public final class SSource extends AStatement {
|
|||
}
|
||||
|
||||
public void analyze(PainlessLookup painlessLookup) {
|
||||
Map<String, PainlessMethod> methods = new HashMap<>();
|
||||
Map<String, LocalMethod> methods = new HashMap<>();
|
||||
|
||||
for (SFunction function : functions) {
|
||||
function.generateSignature(painlessLookup);
|
||||
|
||||
String key = PainlessLookupUtility.buildPainlessMethodKey(function.name, function.parameters.size());
|
||||
String key = Locals.buildLocalMethodKey(function.name, function.parameters.size());
|
||||
|
||||
if (methods.put(key, function.method) != null) {
|
||||
if (methods.put(key,
|
||||
new LocalMethod(function.name, function.returnType, function.typeParameters, function.methodType)) != null) {
|
||||
throw createError(new IllegalArgumentException("Duplicate functions with name [" + function.name + "]."));
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ public final class SSource extends AStatement {
|
|||
void analyze(Locals program) {
|
||||
for (SFunction function : functions) {
|
||||
Locals functionLocals =
|
||||
Locals.newFunctionScope(program, function.rtnType, function.parameters, function.reserved.getMaxLoopCounter());
|
||||
Locals.newFunctionScope(program, function.returnType, function.parameters, function.reserved.getMaxLoopCounter());
|
||||
function.analyze(functionLocals);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,12 +76,10 @@ final class SSubEachIterable extends AStatement {
|
|||
if (expression.actual == def.class) {
|
||||
method = null;
|
||||
} else {
|
||||
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(expression.actual).methods
|
||||
.get(PainlessLookupUtility.buildPainlessMethodKey("iterator", 0));
|
||||
|
||||
if (method == null) {
|
||||
throw createError(new IllegalArgumentException("Unable to create iterator for the type " +
|
||||
"[" + PainlessLookupUtility.typeToCanonicalTypeName(expression.actual) + "]."));
|
||||
try {
|
||||
method = locals.getPainlessLookup().lookupPainlessMethod(expression.actual, false, "iterator", 0);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw createError(iae);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +97,7 @@ final class SSubEachIterable extends AStatement {
|
|||
.getMethodType(org.objectweb.asm.Type.getType(Iterator.class), org.objectweb.asm.Type.getType(Object.class));
|
||||
writer.invokeDefCall("iterator", methodType, DefBootstrap.ITERATOR);
|
||||
} else {
|
||||
method.write(writer);
|
||||
writer.invokeMethodCall(method);
|
||||
}
|
||||
|
||||
writer.visitVarInsn(MethodWriter.getType(iterator.clazz).getOpcode(Opcodes.ISTORE), iterator.getSlot());
|
||||
|
|
|
@ -77,8 +77,8 @@ class org.elasticsearch.index.fielddata.ScriptDocValues$Longs {
|
|||
}
|
||||
|
||||
class org.elasticsearch.index.fielddata.ScriptDocValues$Dates {
|
||||
org.joda.time.ReadableDateTime get(int)
|
||||
org.joda.time.ReadableDateTime getValue()
|
||||
Object get(int)
|
||||
Object getValue()
|
||||
List getValues()
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
}
|
||||
|
||||
public void testGets() {
|
||||
Compiler compiler = new Compiler(Gets.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(Gets.class, null, null, painlessLookup);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("s", 1);
|
||||
|
||||
|
@ -87,7 +87,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute();
|
||||
}
|
||||
public void testNoArgs() {
|
||||
Compiler compiler = new Compiler(NoArgs.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(NoArgs.class, null, null, painlessLookup);
|
||||
assertEquals(1, ((NoArgs)scriptEngine.compile(compiler, null, "1", emptyMap())).execute());
|
||||
assertEquals("foo", ((NoArgs)scriptEngine.compile(compiler, null, "'foo'", emptyMap())).execute());
|
||||
|
||||
|
@ -111,13 +111,13 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(Object arg);
|
||||
}
|
||||
public void testOneArg() {
|
||||
Compiler compiler = new Compiler(OneArg.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(OneArg.class, null, null, painlessLookup);
|
||||
Object rando = randomInt();
|
||||
assertEquals(rando, ((OneArg)scriptEngine.compile(compiler, null, "arg", emptyMap())).execute(rando));
|
||||
rando = randomAlphaOfLength(5);
|
||||
assertEquals(rando, ((OneArg)scriptEngine.compile(compiler, null, "arg", emptyMap())).execute(rando));
|
||||
|
||||
Compiler noargs = new Compiler(NoArgs.class, painlessLookup);
|
||||
Compiler noargs = new Compiler(NoArgs.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, () ->
|
||||
scriptEngine.compile(noargs, null, "doc", emptyMap()));
|
||||
assertEquals("Variable [doc] is not defined.", e.getMessage());
|
||||
|
@ -132,7 +132,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(String[] arg);
|
||||
}
|
||||
public void testArrayArg() {
|
||||
Compiler compiler = new Compiler(ArrayArg.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(ArrayArg.class, null, null, painlessLookup);
|
||||
String rando = randomAlphaOfLength(5);
|
||||
assertEquals(rando, ((ArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new String[] {rando, "foo"}));
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(int[] arg);
|
||||
}
|
||||
public void testPrimitiveArrayArg() {
|
||||
Compiler compiler = new Compiler(PrimitiveArrayArg.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(PrimitiveArrayArg.class, null, null, painlessLookup);
|
||||
int rando = randomInt();
|
||||
assertEquals(rando, ((PrimitiveArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new int[] {rando, 10}));
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(Object[] arg);
|
||||
}
|
||||
public void testDefArrayArg() {
|
||||
Compiler compiler = new Compiler(DefArrayArg.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(DefArrayArg.class, null, null, painlessLookup);
|
||||
Object rando = randomInt();
|
||||
assertEquals(rando, ((DefArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new Object[] {rando, 10}));
|
||||
rando = randomAlphaOfLength(5);
|
||||
|
@ -170,7 +170,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract boolean needsD();
|
||||
}
|
||||
public void testManyArgs() {
|
||||
Compiler compiler = new Compiler(ManyArgs.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(ManyArgs.class, null, null, painlessLookup);
|
||||
int rando = randomInt();
|
||||
assertEquals(rando, ((ManyArgs)scriptEngine.compile(compiler, null, "a", emptyMap())).execute(rando, 0, 0, 0));
|
||||
assertEquals(10, ((ManyArgs)scriptEngine.compile(compiler, null, "a + b + c + d", emptyMap())).execute(1, 2, 3, 4));
|
||||
|
@ -198,7 +198,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(String... arg);
|
||||
}
|
||||
public void testVararg() {
|
||||
Compiler compiler = new Compiler(VarargTest.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(VarargTest.class, null, null, painlessLookup);
|
||||
assertEquals("foo bar baz", ((VarargTest)scriptEngine.compile(compiler, null, "String.join(' ', Arrays.asList(arg))", emptyMap()))
|
||||
.execute("foo", "bar", "baz"));
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
}
|
||||
}
|
||||
public void testDefaultMethods() {
|
||||
Compiler compiler = new Compiler(DefaultMethods.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(DefaultMethods.class, null, null, painlessLookup);
|
||||
int rando = randomInt();
|
||||
assertEquals(rando, ((DefaultMethods)scriptEngine.compile(compiler, null, "a", emptyMap())).execute(rando, 0, 0, 0));
|
||||
assertEquals(rando, ((DefaultMethods)scriptEngine.compile(compiler, null, "a", emptyMap())).executeWithASingleOne(rando, 0, 0));
|
||||
|
@ -228,7 +228,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract void execute(Map<String, Object> map);
|
||||
}
|
||||
public void testReturnsVoid() {
|
||||
Compiler compiler = new Compiler(ReturnsVoid.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(ReturnsVoid.class, null, null, painlessLookup);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
((ReturnsVoid)scriptEngine.compile(compiler, null, "map.a = 'foo'", emptyMap())).execute(map);
|
||||
assertEquals(singletonMap("a", "foo"), map);
|
||||
|
@ -247,7 +247,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract boolean execute();
|
||||
}
|
||||
public void testReturnsPrimitiveBoolean() {
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveBoolean.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveBoolean.class, null, null, painlessLookup);
|
||||
|
||||
assertEquals(true, ((ReturnsPrimitiveBoolean)scriptEngine.compile(compiler, null, "true", emptyMap())).execute());
|
||||
assertEquals(false, ((ReturnsPrimitiveBoolean)scriptEngine.compile(compiler, null, "false", emptyMap())).execute());
|
||||
|
@ -289,7 +289,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract int execute();
|
||||
}
|
||||
public void testReturnsPrimitiveInt() {
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveInt.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveInt.class, null, null, painlessLookup);
|
||||
|
||||
assertEquals(1, ((ReturnsPrimitiveInt)scriptEngine.compile(compiler, null, "1", emptyMap())).execute());
|
||||
assertEquals(1, ((ReturnsPrimitiveInt)scriptEngine.compile(compiler, null, "(int) 1L", emptyMap())).execute());
|
||||
|
@ -331,7 +331,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract float execute();
|
||||
}
|
||||
public void testReturnsPrimitiveFloat() {
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveFloat.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveFloat.class, null, null, painlessLookup);
|
||||
|
||||
assertEquals(1.1f, ((ReturnsPrimitiveFloat)scriptEngine.compile(compiler, null, "1.1f", emptyMap())).execute(), 0);
|
||||
assertEquals(1.1f, ((ReturnsPrimitiveFloat)scriptEngine.compile(compiler, null, "(float) 1.1d", emptyMap())).execute(), 0);
|
||||
|
@ -362,7 +362,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract double execute();
|
||||
}
|
||||
public void testReturnsPrimitiveDouble() {
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveDouble.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(ReturnsPrimitiveDouble.class, null, null, painlessLookup);
|
||||
|
||||
assertEquals(1.0, ((ReturnsPrimitiveDouble)scriptEngine.compile(compiler, null, "1", emptyMap())).execute(), 0);
|
||||
assertEquals(1.0, ((ReturnsPrimitiveDouble)scriptEngine.compile(compiler, null, "1L", emptyMap())).execute(), 0);
|
||||
|
@ -396,7 +396,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(String foo);
|
||||
}
|
||||
public void testNoArgumentsConstant() {
|
||||
Compiler compiler = new Compiler(NoArgumentsConstant.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(NoArgumentsConstant.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
|
||||
scriptEngine.compile(compiler, null, "1", emptyMap()));
|
||||
assertThat(e.getMessage(), startsWith(
|
||||
|
@ -409,7 +409,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(String foo);
|
||||
}
|
||||
public void testWrongArgumentsConstant() {
|
||||
Compiler compiler = new Compiler(WrongArgumentsConstant.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(WrongArgumentsConstant.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
|
||||
scriptEngine.compile(compiler, null, "1", emptyMap()));
|
||||
assertThat(e.getMessage(), startsWith(
|
||||
|
@ -422,7 +422,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(String foo);
|
||||
}
|
||||
public void testWrongLengthOfArgumentConstant() {
|
||||
Compiler compiler = new Compiler(WrongLengthOfArgumentConstant.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(WrongLengthOfArgumentConstant.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
|
||||
scriptEngine.compile(compiler, null, "1", emptyMap()));
|
||||
assertThat(e.getMessage(), startsWith("[" + WrongLengthOfArgumentConstant.class.getName() + "#ARGUMENTS] has length [2] but ["
|
||||
|
@ -434,7 +434,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(UnknownArgType foo);
|
||||
}
|
||||
public void testUnknownArgType() {
|
||||
Compiler compiler = new Compiler(UnknownArgType.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(UnknownArgType.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
|
||||
scriptEngine.compile(compiler, null, "1", emptyMap()));
|
||||
assertEquals("[foo] is of unknown type [" + UnknownArgType.class.getName() + ". Painless interfaces can only accept arguments "
|
||||
|
@ -446,7 +446,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract UnknownReturnType execute(String foo);
|
||||
}
|
||||
public void testUnknownReturnType() {
|
||||
Compiler compiler = new Compiler(UnknownReturnType.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(UnknownReturnType.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
|
||||
scriptEngine.compile(compiler, null, "1", emptyMap()));
|
||||
assertEquals("Painless can only implement execute methods returning a whitelisted type but [" + UnknownReturnType.class.getName()
|
||||
|
@ -458,7 +458,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(UnknownArgTypeInArray[] foo);
|
||||
}
|
||||
public void testUnknownArgTypeInArray() {
|
||||
Compiler compiler = new Compiler(UnknownArgTypeInArray.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(UnknownArgTypeInArray.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
|
||||
scriptEngine.compile(compiler, null, "1", emptyMap()));
|
||||
assertEquals("[foo] is of unknown type [" + UnknownArgTypeInArray.class.getName() + ". Painless interfaces can only accept "
|
||||
|
@ -470,7 +470,7 @@ public class BaseClassTests extends ScriptTestCase {
|
|||
public abstract Object execute(boolean foo);
|
||||
}
|
||||
public void testTwoExecuteMethods() {
|
||||
Compiler compiler = new Compiler(TwoExecuteMethods.class, painlessLookup);
|
||||
Compiler compiler = new Compiler(TwoExecuteMethods.class, null, null, painlessLookup);
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, false, () ->
|
||||
scriptEngine.compile(compiler, null, "null", emptyMap()));
|
||||
assertEquals("Painless can only implement interfaces that have a single method named [execute] but ["
|
||||
|
|
|
@ -40,7 +40,7 @@ final class Debugger {
|
|||
PrintWriter outputWriter = new PrintWriter(output);
|
||||
Textifier textifier = new Textifier();
|
||||
try {
|
||||
new Compiler(iface, PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS))
|
||||
new Compiler(iface, null, null, PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS))
|
||||
.compile("<debugging>", source, settings, textifier);
|
||||
} catch (RuntimeException e) {
|
||||
textifier.print(outputWriter);
|
||||
|
|
|
@ -23,12 +23,12 @@ package org.elasticsearch.painless;
|
|||
public class OverloadTests extends ScriptTestCase {
|
||||
|
||||
public void testMethod() {
|
||||
assertEquals(2, exec("return 'abc123abc'.indexOf('c');"));
|
||||
assertEquals(8, exec("return 'abc123abc'.indexOf('c', 3);"));
|
||||
//assertEquals(2, exec("return 'abc123abc'.indexOf('c');"));
|
||||
//assertEquals(8, exec("return 'abc123abc'.indexOf('c', 3);"));
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("return 'abc123abc'.indexOf('c', 3, 'bogus');");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("[indexOf] with [3] arguments"));
|
||||
assertTrue(expected.getMessage().contains("[java.lang.String, indexOf/3]"));
|
||||
}
|
||||
|
||||
public void testMethodDynamic() {
|
||||
|
|
|
@ -45,9 +45,9 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* Generates an API reference from the method and type whitelists in {@link PainlessLookup}.
|
||||
|
@ -56,9 +56,9 @@ public class PainlessDocGenerator {
|
|||
|
||||
private static final PainlessLookup PAINLESS_LOOKUP = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
|
||||
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> METHOD_NUMBER_OF_PARAMS = comparing(m -> m.arguments.size());
|
||||
private static final Comparator<PainlessField> FIELD_NAME = comparing(f -> f.javaField.getName());
|
||||
private static final Comparator<PainlessMethod> METHOD_NAME = comparing(m -> m.javaMethod.getName());
|
||||
private static final Comparator<PainlessMethod> METHOD_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size());
|
||||
private static final Comparator<PainlessConstructor> CONSTRUCTOR_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size());
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
@ -74,9 +74,10 @@ public class PainlessDocGenerator {
|
|||
Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
|
||||
false, StandardCharsets.UTF_8.name())) {
|
||||
emitGeneratedWarning(indexStream);
|
||||
List<Class<?>> classes = PAINLESS_LOOKUP.getStructs().stream().sorted(comparing(Class::getCanonicalName)).collect(toList());
|
||||
List<Class<?>> classes = PAINLESS_LOOKUP.getClasses().stream().sorted(
|
||||
Comparator.comparing(Class::getCanonicalName)).collect(Collectors.toList());
|
||||
for (Class<?> clazz : classes) {
|
||||
PainlessClass struct = PAINLESS_LOOKUP.getPainlessStructFromJavaClass(clazz);
|
||||
PainlessClass struct = PAINLESS_LOOKUP.lookupPainlessClass(clazz);
|
||||
String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(clazz);
|
||||
|
||||
if (clazz.isPrimitive()) {
|
||||
|
@ -114,10 +115,10 @@ public class PainlessDocGenerator {
|
|||
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(METHOD_NUMBER_OF_PARAMS)).forEach(method -> {
|
||||
if (method.target == clazz) {
|
||||
if (method.targetClass == clazz) {
|
||||
documentMethod(typeStream, method);
|
||||
} else {
|
||||
inherited.put(canonicalClassName, method.target);
|
||||
inherited.put(canonicalClassName, method.targetClass);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -147,17 +148,17 @@ public class PainlessDocGenerator {
|
|||
emitAnchor(stream, field);
|
||||
stream.print("]]");
|
||||
|
||||
if (Modifier.isStatic(field.modifiers)) {
|
||||
if (Modifier.isStatic(field.javaField.getModifiers())) {
|
||||
stream.print("static ");
|
||||
}
|
||||
|
||||
emitType(stream, field.clazz);
|
||||
emitType(stream, field.typeParameter);
|
||||
stream.print(' ');
|
||||
|
||||
String javadocRoot = javadocRoot(field);
|
||||
emitJavadocLink(stream, javadocRoot, field);
|
||||
stream.print('[');
|
||||
stream.print(field.name);
|
||||
stream.print(field.javaField.getName());
|
||||
stream.print(']');
|
||||
|
||||
if (javadocRoot.equals("java8")) {
|
||||
|
@ -212,11 +213,11 @@ public class PainlessDocGenerator {
|
|||
emitAnchor(stream, method);
|
||||
stream.print("]]");
|
||||
|
||||
if (null == method.augmentation && Modifier.isStatic(method.modifiers)) {
|
||||
if (method.targetClass == method.javaMethod.getDeclaringClass() && Modifier.isStatic(method.javaMethod.getModifiers())) {
|
||||
stream.print("static ");
|
||||
}
|
||||
|
||||
emitType(stream, method.rtn);
|
||||
emitType(stream, method.returnType);
|
||||
stream.print(' ');
|
||||
|
||||
String javadocRoot = javadocRoot(method);
|
||||
|
@ -227,7 +228,7 @@ public class PainlessDocGenerator {
|
|||
|
||||
stream.print("](");
|
||||
boolean first = true;
|
||||
for (Class<?> arg : method.arguments) {
|
||||
for (Class<?> arg : method.typeParameters) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
|
@ -269,20 +270,20 @@ public class PainlessDocGenerator {
|
|||
* Anchor text for a {@link PainlessMethod}.
|
||||
*/
|
||||
private static void emitAnchor(PrintStream stream, PainlessMethod method) {
|
||||
emitAnchor(stream, method.target);
|
||||
emitAnchor(stream, method.targetClass);
|
||||
stream.print('-');
|
||||
stream.print(methodName(method));
|
||||
stream.print('-');
|
||||
stream.print(method.arguments.size());
|
||||
stream.print(method.typeParameters.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Anchor text for a {@link PainlessField}.
|
||||
*/
|
||||
private static void emitAnchor(PrintStream stream, PainlessField field) {
|
||||
emitAnchor(stream, field.target);
|
||||
emitAnchor(stream, field.javaField.getDeclaringClass());
|
||||
stream.print('-');
|
||||
stream.print(field.name);
|
||||
stream.print(field.javaField.getName());
|
||||
}
|
||||
|
||||
private static String constructorName(PainlessConstructor constructor) {
|
||||
|
@ -290,7 +291,7 @@ public class PainlessDocGenerator {
|
|||
}
|
||||
|
||||
private static String methodName(PainlessMethod method) {
|
||||
return PainlessLookupUtility.typeToCanonicalTypeName(method.target);
|
||||
return PainlessLookupUtility.typeToCanonicalTypeName(method.targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -359,16 +360,16 @@ public class PainlessDocGenerator {
|
|||
stream.print("link:{");
|
||||
stream.print(root);
|
||||
stream.print("-javadoc}/");
|
||||
stream.print(classUrlPath(method.augmentation != null ? method.augmentation : method.target));
|
||||
stream.print(classUrlPath(method.javaMethod.getDeclaringClass()));
|
||||
stream.print(".html#");
|
||||
stream.print(methodName(method));
|
||||
stream.print("%2D");
|
||||
boolean first = true;
|
||||
if (method.augmentation != null) {
|
||||
if (method.targetClass != method.javaMethod.getDeclaringClass()) {
|
||||
first = false;
|
||||
stream.print(method.target.getName());
|
||||
stream.print(method.javaMethod.getDeclaringClass().getName());
|
||||
}
|
||||
for (Class<?> clazz: method.arguments) {
|
||||
for (Class<?> clazz: method.typeParameters) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
|
@ -391,26 +392,26 @@ public class PainlessDocGenerator {
|
|||
stream.print("link:{");
|
||||
stream.print(root);
|
||||
stream.print("-javadoc}/");
|
||||
stream.print(classUrlPath(field.target));
|
||||
stream.print(classUrlPath(field.javaField.getDeclaringClass()));
|
||||
stream.print(".html#");
|
||||
stream.print(field.javaName);
|
||||
stream.print(field.javaField.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the javadoc root for a {@link PainlessMethod}.
|
||||
*/
|
||||
private static String javadocRoot(PainlessMethod method) {
|
||||
if (method.augmentation != null) {
|
||||
if (method.targetClass != method.javaMethod.getDeclaringClass()) {
|
||||
return "painless";
|
||||
}
|
||||
return javadocRoot(method.target);
|
||||
return javadocRoot(method.targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the javadoc root for a {@link PainlessField}.
|
||||
*/
|
||||
private static String javadocRoot(PainlessField field) {
|
||||
return javadocRoot(field.target);
|
||||
return javadocRoot(field.javaField.getDeclaringClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -252,7 +252,7 @@ public class RegexTests extends ScriptTestCase {
|
|||
IllegalArgumentException e = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("Pattern.compile('aa')");
|
||||
});
|
||||
assertEquals("Unknown call [compile] with [1] arguments on type [java.util.regex.Pattern].", e.getMessage());
|
||||
assertTrue(e.getMessage().contains("[java.util.regex.Pattern, compile/1]"));
|
||||
}
|
||||
|
||||
public void testBadRegexPattern() {
|
||||
|
|
|
@ -404,7 +404,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||
|
||||
public void testPSubCallInvoke() {
|
||||
Location l = new Location(getTestName(), 0);
|
||||
PainlessClass c = painlessLookup.getPainlessStructFromJavaClass(Integer.class);
|
||||
PainlessClass c = painlessLookup.lookupPainlessClass(Integer.class);
|
||||
PainlessMethod m = c.methods.get(PainlessLookupUtility.buildPainlessMethodKey("toString", 0));
|
||||
PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList());
|
||||
node.prefix = new EVariable(l, "a");
|
||||
|
@ -459,7 +459,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||
|
||||
public void testPSubField() {
|
||||
Location l = new Location(getTestName(), 0);
|
||||
PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(Boolean.class);
|
||||
PainlessClass s = painlessLookup.lookupPainlessClass(Boolean.class);
|
||||
PainlessField f = s.staticFields.get("TRUE");
|
||||
PSubField node = new PSubField(l, f);
|
||||
node.prefix = new EStatic(l, "Boolean");
|
||||
|
@ -497,7 +497,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||
|
||||
public void testPSubShortcut() {
|
||||
Location l = new Location(getTestName(), 0);
|
||||
PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(FeatureTest.class);
|
||||
PainlessClass s = painlessLookup.lookupPainlessClass(FeatureTest.class);
|
||||
PainlessMethod getter = s.methods.get(PainlessLookupUtility.buildPainlessMethodKey("getX", 0));
|
||||
PainlessMethod setter = s.methods.get(PainlessLookupUtility.buildPainlessMethodKey("setX", 1));
|
||||
PSubShortcut node = new PSubShortcut(l, "x", FeatureTest.class.getName(), getter, setter);
|
||||
|
|
|
@ -108,7 +108,7 @@ setup:
|
|||
script_fields:
|
||||
bar:
|
||||
script:
|
||||
source: "doc.date.value.dayOfWeek"
|
||||
source: "doc.date.value.dayOfWeek.value"
|
||||
|
||||
- match: { hits.hits.0.fields.bar.0: 7}
|
||||
|
||||
|
@ -123,7 +123,7 @@ setup:
|
|||
source: >
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (def date : doc.dates) {
|
||||
b.append(" ").append(date.getDayOfWeek());
|
||||
b.append(" ").append(date.getDayOfWeek().value);
|
||||
}
|
||||
return b.toString().trim()
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ setup:
|
|||
field:
|
||||
script:
|
||||
source: "doc.date.get(0)"
|
||||
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' }
|
||||
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12Z' }
|
||||
|
||||
- do:
|
||||
search:
|
||||
|
@ -104,7 +104,7 @@ setup:
|
|||
field:
|
||||
script:
|
||||
source: "doc.date.value"
|
||||
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' }
|
||||
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12Z' }
|
||||
|
||||
---
|
||||
"geo_point":
|
||||
|
|
|
@ -34,13 +34,13 @@ compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-tr
|
|||
|
||||
dependencies {
|
||||
// network stack
|
||||
compile "io.netty:netty-buffer:4.1.16.Final"
|
||||
compile "io.netty:netty-codec:4.1.16.Final"
|
||||
compile "io.netty:netty-codec-http:4.1.16.Final"
|
||||
compile "io.netty:netty-common:4.1.16.Final"
|
||||
compile "io.netty:netty-handler:4.1.16.Final"
|
||||
compile "io.netty:netty-resolver:4.1.16.Final"
|
||||
compile "io.netty:netty-transport:4.1.16.Final"
|
||||
compile "io.netty:netty-buffer:4.1.28.Final"
|
||||
compile "io.netty:netty-codec:4.1.28.Final"
|
||||
compile "io.netty:netty-codec-http:4.1.28.Final"
|
||||
compile "io.netty:netty-common:4.1.28.Final"
|
||||
compile "io.netty:netty-handler:4.1.28.Final"
|
||||
compile "io.netty:netty-resolver:4.1.28.Final"
|
||||
compile "io.netty:netty-transport:4.1.28.Final"
|
||||
}
|
||||
|
||||
dependencyLicenses {
|
||||
|
@ -134,7 +134,6 @@ thirdPartyAudit.excludes = [
|
|||
'net.jpountz.xxhash.StreamingXXHash32',
|
||||
'net.jpountz.xxhash.XXHashFactory',
|
||||
'io.netty.internal.tcnative.CertificateRequestedCallback',
|
||||
'io.netty.internal.tcnative.CertificateRequestedCallback$KeyMaterial',
|
||||
'io.netty.internal.tcnative.CertificateVerifier',
|
||||
'io.netty.internal.tcnative.SessionTicketKey',
|
||||
'io.netty.internal.tcnative.SniHostNameMatcher',
|
||||
|
@ -161,6 +160,6 @@ thirdPartyAudit.excludes = [
|
|||
|
||||
'org.conscrypt.AllocatedBuffer',
|
||||
'org.conscrypt.BufferAllocator',
|
||||
'org.conscrypt.Conscrypt$Engines',
|
||||
'org.conscrypt.Conscrypt',
|
||||
'org.conscrypt.HandshakeListener'
|
||||
]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
63b5fa95c74785e16f2c30ce268bc222e35c8cb5
|
|
@ -0,0 +1 @@
|
|||
d6c2d13492778009d33f60e05ed90bcb535d1fd1
|
|
@ -1 +0,0 @@
|
|||
d84a1f21768b7309c2954521cf5a1f46c2309eb1
|
|
@ -0,0 +1 @@
|
|||
a38361d893900947524f8a9da980555950e73d6a
|
|
@ -1 +0,0 @@
|
|||
d64312378b438dfdad84267c599a053327c6f02a
|
|
@ -0,0 +1 @@
|
|||
897100c1022c780b0a436b9349e507e8fa9800dc
|
|
@ -1 +0,0 @@
|
|||
177a6b30cca92f6f5f9873c9befd681377a4c328
|
|
@ -0,0 +1 @@
|
|||
df69ce8bb9b544a71e7bbee290253cf7c93e6bad
|
|
@ -1 +0,0 @@
|
|||
fec0e63e7dd7f4eeef7ea8dc47a1ff32dfc7ebc2
|
|
@ -0,0 +1 @@
|
|||
a035784682da0126bc25f10713dac732b5082a6d
|
|
@ -1 +0,0 @@
|
|||
f6eb553b53fb3a90a8ac1170697093fed82eae28
|
|
@ -0,0 +1 @@
|
|||
f33557dcb31fa20da075ac05e4808115e32ef9b7
|
|
@ -1 +0,0 @@
|
|||
3c8ee2c4d4a1cbb947a5c184c7aeb2204260958b
|
|
@ -0,0 +1 @@
|
|||
d2ef28f49d726737f0ffe84bf66529b3bf6e0c0d
|
|
@ -23,6 +23,9 @@ grant codeBase "${codebase.netty-common}" {
|
|||
|
||||
// netty makes and accepts socket connections
|
||||
permission java.net.SocketPermission "*", "accept,connect";
|
||||
|
||||
// Netty sets custom classloader for some of its internal threads
|
||||
permission java.lang.RuntimePermission "*", "setContextClassLoader";
|
||||
};
|
||||
|
||||
grant codeBase "${codebase.netty-transport}" {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.http.netty4;
|
||||
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.util.ReferenceCounted;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.network.NetworkService;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -92,15 +93,19 @@ public class Netty4BadRequestTests extends ESTestCase {
|
|||
try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) {
|
||||
final Collection<FullHttpResponse> responses =
|
||||
nettyHttpClient.get(transportAddress.address(), "/_cluster/settings?pretty=%");
|
||||
assertThat(responses, hasSize(1));
|
||||
assertThat(responses.iterator().next().status().code(), equalTo(400));
|
||||
final Collection<String> responseBodies = Netty4HttpClient.returnHttpResponseBodies(responses);
|
||||
assertThat(responseBodies, hasSize(1));
|
||||
assertThat(responseBodies.iterator().next(), containsString("\"type\":\"bad_parameter_exception\""));
|
||||
assertThat(
|
||||
try {
|
||||
assertThat(responses, hasSize(1));
|
||||
assertThat(responses.iterator().next().status().code(), equalTo(400));
|
||||
final Collection<String> responseBodies = Netty4HttpClient.returnHttpResponseBodies(responses);
|
||||
assertThat(responseBodies, hasSize(1));
|
||||
assertThat(responseBodies.iterator().next(), containsString("\"type\":\"bad_parameter_exception\""));
|
||||
assertThat(
|
||||
responseBodies.iterator().next(),
|
||||
containsString(
|
||||
"\"reason\":\"java.lang.IllegalArgumentException: unterminated escape sequence at end of string: %\""));
|
||||
"\"reason\":\"java.lang.IllegalArgumentException: unterminated escape sequence at end of string: %\""));
|
||||
} finally {
|
||||
responses.forEach(ReferenceCounted::release);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.http.netty4;
|
||||
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.util.ReferenceCounted;
|
||||
import org.elasticsearch.ESNetty4IntegTestCase;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -88,12 +89,20 @@ public class Netty4HttpRequestSizeLimitIT extends ESNetty4IntegTestCase {
|
|||
|
||||
try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) {
|
||||
Collection<FullHttpResponse> singleResponse = nettyHttpClient.post(transportAddress.address(), requests[0]);
|
||||
assertThat(singleResponse, hasSize(1));
|
||||
assertAtLeastOnceExpectedStatus(singleResponse, HttpResponseStatus.OK);
|
||||
try {
|
||||
assertThat(singleResponse, hasSize(1));
|
||||
assertAtLeastOnceExpectedStatus(singleResponse, HttpResponseStatus.OK);
|
||||
|
||||
Collection<FullHttpResponse> multipleResponses = nettyHttpClient.post(transportAddress.address(), requests);
|
||||
assertThat(multipleResponses, hasSize(requests.length));
|
||||
assertAtLeastOnceExpectedStatus(multipleResponses, HttpResponseStatus.SERVICE_UNAVAILABLE);
|
||||
Collection<FullHttpResponse> multipleResponses = nettyHttpClient.post(transportAddress.address(), requests);
|
||||
try {
|
||||
assertThat(multipleResponses, hasSize(requests.length));
|
||||
assertAtLeastOnceExpectedStatus(multipleResponses, HttpResponseStatus.SERVICE_UNAVAILABLE);
|
||||
} finally {
|
||||
multipleResponses.forEach(ReferenceCounted::release);
|
||||
}
|
||||
} finally {
|
||||
singleResponse.forEach(ReferenceCounted::release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,8 +122,12 @@ public class Netty4HttpRequestSizeLimitIT extends ESNetty4IntegTestCase {
|
|||
|
||||
try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) {
|
||||
Collection<FullHttpResponse> responses = nettyHttpClient.put(transportAddress.address(), requestUris);
|
||||
assertThat(responses, hasSize(requestUris.length));
|
||||
assertAllInExpectedStatus(responses, HttpResponseStatus.OK);
|
||||
try {
|
||||
assertThat(responses, hasSize(requestUris.length));
|
||||
assertAllInExpectedStatus(responses, HttpResponseStatus.OK);
|
||||
} finally {
|
||||
responses.forEach(ReferenceCounted::release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import io.netty.channel.SimpleChannelInboundHandler;
|
|||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.util.ReferenceCounted;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.network.NetworkService;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -98,8 +99,12 @@ public class Netty4HttpServerPipeliningTests extends ESTestCase {
|
|||
|
||||
try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) {
|
||||
Collection<FullHttpResponse> responses = nettyHttpClient.get(transportAddress.address(), requests.toArray(new String[]{}));
|
||||
Collection<String> responseBodies = Netty4HttpClient.returnHttpResponseBodies(responses);
|
||||
assertThat(responseBodies, contains(requests.toArray()));
|
||||
try {
|
||||
Collection<String> responseBodies = Netty4HttpClient.returnHttpResponseBodies(responses);
|
||||
assertThat(responseBodies, contains(requests.toArray()));
|
||||
} finally {
|
||||
responses.forEach(ReferenceCounted::release);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,14 +207,23 @@ public class Netty4HttpServerTransportTests extends ESTestCase {
|
|||
HttpUtil.setContentLength(request, contentLength);
|
||||
|
||||
final FullHttpResponse response = client.post(remoteAddress.address(), request);
|
||||
assertThat(response.status(), equalTo(expectedStatus));
|
||||
if (expectedStatus.equals(HttpResponseStatus.CONTINUE)) {
|
||||
final FullHttpRequest continuationRequest =
|
||||
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.EMPTY_BUFFER);
|
||||
final FullHttpResponse continuationResponse = client.post(remoteAddress.address(), continuationRequest);
|
||||
|
||||
assertThat(continuationResponse.status(), is(HttpResponseStatus.OK));
|
||||
assertThat(new String(ByteBufUtil.getBytes(continuationResponse.content()), StandardCharsets.UTF_8), is("done"));
|
||||
try {
|
||||
assertThat(response.status(), equalTo(expectedStatus));
|
||||
if (expectedStatus.equals(HttpResponseStatus.CONTINUE)) {
|
||||
final FullHttpRequest continuationRequest =
|
||||
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.EMPTY_BUFFER);
|
||||
final FullHttpResponse continuationResponse = client.post(remoteAddress.address(), continuationRequest);
|
||||
try {
|
||||
assertThat(continuationResponse.status(), is(HttpResponseStatus.OK));
|
||||
assertThat(
|
||||
new String(ByteBufUtil.getBytes(continuationResponse.content()), StandardCharsets.UTF_8), is("done")
|
||||
);
|
||||
} finally {
|
||||
continuationResponse.release();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
response.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -280,10 +289,14 @@ public class Netty4HttpServerTransportTests extends ESTestCase {
|
|||
final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url);
|
||||
|
||||
final FullHttpResponse response = client.post(remoteAddress.address(), request);
|
||||
assertThat(response.status(), equalTo(HttpResponseStatus.BAD_REQUEST));
|
||||
assertThat(
|
||||
try {
|
||||
assertThat(response.status(), equalTo(HttpResponseStatus.BAD_REQUEST));
|
||||
assertThat(
|
||||
new String(response.content().array(), Charset.forName("UTF-8")),
|
||||
containsString("you sent a bad request and you should feel bad"));
|
||||
} finally {
|
||||
response.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.http.netty4;
|
||||
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.util.ReferenceCounted;
|
||||
import org.elasticsearch.ESNetty4IntegTestCase;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
|
@ -45,14 +46,18 @@ public class Netty4PipeliningIT extends ESNetty4IntegTestCase {
|
|||
|
||||
HttpServerTransport httpServerTransport = internalCluster().getInstance(HttpServerTransport.class);
|
||||
TransportAddress[] boundAddresses = httpServerTransport.boundAddress().boundAddresses();
|
||||
TransportAddress transportAddress = (TransportAddress) randomFrom(boundAddresses);
|
||||
TransportAddress transportAddress = randomFrom(boundAddresses);
|
||||
|
||||
try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) {
|
||||
Collection<FullHttpResponse> responses = nettyHttpClient.get(transportAddress.address(), requests);
|
||||
assertThat(responses, hasSize(5));
|
||||
try {
|
||||
assertThat(responses, hasSize(5));
|
||||
|
||||
Collection<String> opaqueIds = Netty4HttpClient.returnOpaqueIds(responses);
|
||||
assertOpaqueIdsInOrder(opaqueIds);
|
||||
Collection<String> opaqueIds = Netty4HttpClient.returnOpaqueIds(responses);
|
||||
assertOpaqueIdsInOrder(opaqueIds);
|
||||
} finally {
|
||||
responses.forEach(ReferenceCounted::release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,13 @@ dependencies {
|
|||
compile "org.elasticsearch:elasticsearch-nio:${version}"
|
||||
|
||||
// network stack
|
||||
compile "io.netty:netty-buffer:4.1.16.Final"
|
||||
compile "io.netty:netty-codec:4.1.16.Final"
|
||||
compile "io.netty:netty-codec-http:4.1.16.Final"
|
||||
compile "io.netty:netty-common:4.1.16.Final"
|
||||
compile "io.netty:netty-handler:4.1.16.Final"
|
||||
compile "io.netty:netty-resolver:4.1.16.Final"
|
||||
compile "io.netty:netty-transport:4.1.16.Final"
|
||||
compile "io.netty:netty-buffer:4.1.28.Final"
|
||||
compile "io.netty:netty-codec:4.1.28.Final"
|
||||
compile "io.netty:netty-codec-http:4.1.28.Final"
|
||||
compile "io.netty:netty-common:4.1.28.Final"
|
||||
compile "io.netty:netty-handler:4.1.28.Final"
|
||||
compile "io.netty:netty-resolver:4.1.28.Final"
|
||||
compile "io.netty:netty-transport:4.1.28.Final"
|
||||
}
|
||||
|
||||
dependencyLicenses {
|
||||
|
@ -113,7 +113,6 @@ thirdPartyAudit.excludes = [
|
|||
'net.jpountz.xxhash.StreamingXXHash32',
|
||||
'net.jpountz.xxhash.XXHashFactory',
|
||||
'io.netty.internal.tcnative.CertificateRequestedCallback',
|
||||
'io.netty.internal.tcnative.CertificateRequestedCallback$KeyMaterial',
|
||||
'io.netty.internal.tcnative.CertificateVerifier',
|
||||
'io.netty.internal.tcnative.SessionTicketKey',
|
||||
'io.netty.internal.tcnative.SniHostNameMatcher',
|
||||
|
@ -140,6 +139,6 @@ thirdPartyAudit.excludes = [
|
|||
|
||||
'org.conscrypt.AllocatedBuffer',
|
||||
'org.conscrypt.BufferAllocator',
|
||||
'org.conscrypt.Conscrypt$Engines',
|
||||
'org.conscrypt.Conscrypt',
|
||||
'org.conscrypt.HandshakeListener'
|
||||
]
|
|
@ -1 +0,0 @@
|
|||
63b5fa95c74785e16f2c30ce268bc222e35c8cb5
|
|
@ -0,0 +1 @@
|
|||
d6c2d13492778009d33f60e05ed90bcb535d1fd1
|
|
@ -1 +0,0 @@
|
|||
d84a1f21768b7309c2954521cf5a1f46c2309eb1
|
|
@ -0,0 +1 @@
|
|||
a38361d893900947524f8a9da980555950e73d6a
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue