diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index c0260c9e..c1ccf6d6 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -24,12 +24,6 @@ jobs: distribution: zulu - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' run: chmod +x ${{ github.workspace }}/gradlew - - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' - run: chmod +x ${{ github.workspace }}/setup-shared-libs.sh - - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' - run: ${{ github.workspace }}/setup-shared-libs.sh - - if: matrix.config.preset == 'mingwX64' - run: ${{ github.workspace }}/setup-shared-libs.bat - uses: gradle/gradle-build-action@v2 with: arguments: check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 54deaad3..a2f9609f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,12 +25,6 @@ jobs: distribution: zulu - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' run: chmod +x ${{ github.workspace }}/gradlew - - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' - run: chmod +x ${{ github.workspace }}/setup-shared-libs.sh - - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' - run: ${{ github.workspace }}/setup-shared-libs.sh - - if: matrix.config.preset == 'mingwX64' - run: ${{ github.workspace }}/setup-shared-libs.bat - uses: gradle/gradle-build-action@v2 with: arguments: check @@ -90,12 +84,6 @@ jobs: distribution: zulu - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' run: chmod +x ${{ github.workspace }}/gradlew - - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' - run: chmod +x ${{ github.workspace }}/setup-shared-libs.sh - - if: matrix.config.preset == 'linuxX64' || matrix.config.preset == 'macosX64' || matrix.config.preset == 'macosArm64' - run: ${{ github.workspace }}/setup-shared-libs.sh - - if: matrix.config.preset == 'mingwX64' - run: ${{ github.workspace }}/setup-shared-libs.bat - uses: gradle/gradle-build-action@v2 with: arguments: assemble @@ -120,17 +108,9 @@ jobs: java-version: 8.0 distribution: zulu - run: chmod +x ${{ github.workspace }}/gradlew - - run: chmod +x ${{ github.workspace }}/setup-shared-libs.sh - - run: ${{ github.workspace }}/setup-shared-libs.sh - run: 'git switch -c release/${{ needs.assets.outputs.tag }}' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: gradle/gradle-build-action@v2 - env: - SHARED_LIBS_USER: ${{ secrets.GH_PACKAGE_USERNAME }} - SHARED_LIBS_PASSWORD: ${{ secrets.GH_PACKAGE_TOKEN }} - with: - arguments: tagSharedLibs - run: 'git commit -am "release: branch ${{ needs.assets.outputs.tag }}"' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index ec3f50db..6dba031b 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -114,7 +114,6 @@ Following files are included in release (per component): To set up environment for local development please perform following steps: 1. Install tools (git, gradle, java) 2. Clone component repository, checkout corresponding branch -3. Run script to set up lib-jvm-shared repository from Git: setup-shared-libs.bat/setup-shared-libs.sh ### Moving lib-jvm-shared to custom directory diff --git a/build.gradle.kts b/build.gradle.kts index 6f1997e8..28e54129 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -104,19 +104,4 @@ tasks { gitrepo.pull() } } - val tagSharedLibs by registering { - group = "other" - doLast { - val tag = "${project.name}-v${project.version}" - val gitrepo = Grgit.open { - dir = sharedLibsDir - credentials = Credentials(System.getenv("SHARED_LIBS_USER"), System.getenv("SHARED_LIBS_PASSWORD")) - } - gitrepo.tag.add { name = tag } - gitrepo.push { refsOrSpecs = listOf("tags/$tag") } - val properties = Configurations().propertiesBuilder(file("gradle.properties")) - properties.configuration.setProperty("sharedLibsRef", tag) - properties.save() - } - } } diff --git a/gradle.properties b/gradle.properties index a779e0e0..265d4bb6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ aesyDatasizeVersion = 1.0.0 bytebuddyVersion = 1.14.11 asmVersion = 9.8 -sharedLibsRef = main +sharedLibsRef = feature/united-java-agent sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false diff --git a/java-agent/build.gradle.kts b/java-agent/build.gradle.kts index 94775e71..842b21a3 100644 --- a/java-agent/build.gradle.kts +++ b/java-agent/build.gradle.kts @@ -75,6 +75,7 @@ kotlin { languageSettings.optIn("kotlin.ExperimentalStdlibApi") languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi") languageSettings.optIn("io.ktor.utils.io.core.ExperimentalIoApi") + languageSettings.optIn("kotlinx.serialization.InternalSerializationApi") } targets.withType()[HostManager.host.presetName].compilations.forEach { it.defaultSourceSet.kotlin.srcDir("src/native${it.compilationName.capitalize()}/kotlin") @@ -117,6 +118,7 @@ kotlin { implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${kotlinxSerializationVersion}") implementation("org.jetbrains.kotlinx:kotlinx-cli:${kotlinxCliVersion}") implementation("org.ow2.asm:asm:${asmVersion}") + implementation("com.benasher44:uuid:${uuidVersion}") implementation(project(":common")) implementation(project(":agent-transport")) implementation(project(":agent-instrumentation")) diff --git a/java-agent/drill.properties b/java-agent/drill.properties index 5c397b1d..b370a202 100644 --- a/java-agent/drill.properties +++ b/java-agent/drill.properties @@ -1 +1,6 @@ logLevel=INFO +#Capabilities +coverageCollectionEnabled=true +classScanningEnabled=true +contextPropagationEnabled=true +testTracingEnabled=false diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt index 058faaea..b0e5394f 100644 --- a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt @@ -1,47 +1,74 @@ -/** - * Copyright 2020 - 2022 EPAM Systems - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.epam.drill.agent.configuration - -import com.epam.drill.agent.common.configuration.AgentParameterDefinition -import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection -import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition - -object ParameterDefinitions: AgentParameterDefinitionCollection() { - - val API_URL = AgentParameterDefinition.forString( - name = "apiUrl", - description = "URL to Drill4J Backend /api endpoint. Example: http://localhost:8090/api", - parser = { if (!it.endsWith("/")) "$it/" else it }, - validator = { validTransportUrl() }).register() - val API_KEY = NullableAgentParameterDefinition.forString( - name = "apiKey", - description = "Drill4J API key. It is recommended to set it with DRILL_API_KEY env variable, rather than using command line argument" - ).register() - val MESSAGE_SENDING_MODE = AgentParameterDefinition.forString( - name = "messageSendingMode", - description = "Message sending mode. Possible values: DIRECT, QUEUED", - defaultValue = "QUEUED").register() - val MESSAGE_QUEUE_LIMIT = AgentParameterDefinition.forString(name = "messageQueueLimit", defaultValue = "512Mb").register() - val MESSAGE_MAX_RETRIES = AgentParameterDefinition.forInt(name = "messageMaxRetries", defaultValue = Int.MAX_VALUE).register() - val SSL_TRUSTSTORE = NullableAgentParameterDefinition.forString(name = "sslTruststore").register() - val SSL_TRUSTSTORE_PASSWORD = NullableAgentParameterDefinition.forString(name = "sslTruststorePassword").register() - val LOG_LEVEL = AgentParameterDefinition.forString(name = "logLevel", defaultValue = "INFO").register() - val LOG_FILE = NullableAgentParameterDefinition.forString(name = "logFile").register() - val LOG_LIMIT = AgentParameterDefinition.forInt(name = "logLimit", defaultValue = 512).register() - val USE_PROTOBUF_SERIALIZER = - AgentParameterDefinition.forBoolean(name = "useProtobufSerializer", defaultValue = true).register() - val USE_GZIP_COMPRESSION = AgentParameterDefinition.forBoolean(name = "useGzipCompression", defaultValue = true).register() -} +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection +import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition + +object ParameterDefinitions: AgentParameterDefinitionCollection() { + + val API_URL = AgentParameterDefinition.forString( + name = "apiUrl", + description = "URL to Drill4J Backend /api endpoint. Example: http://localhost:8090/api", + parser = { if (!it.endsWith("/")) "$it/" else it }, + validator = { validTransportUrl() }).register() + val API_KEY = NullableAgentParameterDefinition.forString( + name = "apiKey", + description = "Drill4J API key. It is recommended to set it with DRILL_API_KEY env variable, rather than using command line argument" + ).register() + val MESSAGE_SENDING_MODE = AgentParameterDefinition.forString( + name = "messageSendingMode", + description = "Message sending mode. Possible values: DIRECT, QUEUED", + defaultValue = "QUEUED").register() + val MESSAGE_QUEUE_LIMIT = AgentParameterDefinition.forString(name = "messageQueueLimit", defaultValue = "512Mb").register() + val MESSAGE_MAX_RETRIES = AgentParameterDefinition.forInt(name = "messageMaxRetries", defaultValue = Int.MAX_VALUE).register() + val SSL_TRUSTSTORE = NullableAgentParameterDefinition.forString(name = "sslTruststore").register() + val SSL_TRUSTSTORE_PASSWORD = NullableAgentParameterDefinition.forString(name = "sslTruststorePassword").register() + val LOG_LEVEL = AgentParameterDefinition.forString(name = "logLevel", defaultValue = "INFO").register() + val LOG_FILE = NullableAgentParameterDefinition.forString(name = "logFile").register() + val LOG_LIMIT = AgentParameterDefinition.forInt(name = "logLimit", defaultValue = 512).register() + val USE_PROTOBUF_SERIALIZER = + AgentParameterDefinition.forBoolean(name = "useProtobufSerializer", defaultValue = true).register() + val USE_GZIP_COMPRESSION = AgentParameterDefinition.forBoolean(name = "useGzipCompression", defaultValue = true).register() + + val WITH_JS_COVERAGE = AgentParameterDefinition.forBoolean(name = "withJsCoverage", defaultValue = false).register() + val PROXY_ADDRESS = AgentParameterDefinition.forString(name = "browserProxyAddress", defaultValue = "").register() + val DEVTOOLS_PROXY_ADDRESS = AgentParameterDefinition.forString( + name = "devToolsProxyAddress", + defaultValue = "http://localhost:9222", + parser = { it.trim().takeIf(String::isBlank) ?: it.takeIf(URL_SCHEME_REGEX::matches) ?: "http://$it"} + ).register() + val DEVTOOLS_REPLACE_LOCALHOST = AgentParameterDefinition.forString(name = "devtoolsAddressReplaceLocalhost", defaultValue = "").register() + val TEST_SESSION_ID = NullableAgentParameterDefinition.forString(name = "testSessionId").register() + val LAUNCH_TYPE = AgentParameterDefinition.forString(name = "launchType", defaultValue = "").register() + + private val URL_SCHEME_REGEX = Regex("\\w+://.+") + + val JS_AGENT_BUILD_VERSION = NullableAgentParameterDefinition.forString(name = "jsAgentBuildVersion").register() + val JS_AGENT_ID = NullableAgentParameterDefinition.forString(name = "jsAgentId").register() + + val TEST_TASK_ID = AgentParameterDefinition.forString(name = "testTaskId", defaultValue = "").register() + val RECOMMENDED_TESTS_ENABLED = AgentParameterDefinition.forBoolean(name = "recommendedTestsEnabled", defaultValue = false).register() + val RECOMMENDED_TESTS_TARGET_APP_ID = AgentParameterDefinition.forString(name = "recommendedTestsTargetAppId", defaultValue = "").register() + val RECOMMENDED_TESTS_TARGET_COMMIT_SHA = AgentParameterDefinition.forString(name = "recommendedTestsTargetCommitSha", defaultValue = "").register() + val RECOMMENDED_TESTS_TARGET_BUILD_VERSION = AgentParameterDefinition.forString(name = "recommendedTestsTargetBuildVersion", defaultValue = "").register() + val RECOMMENDED_TESTS_BASELINE_COMMIT_SHA = AgentParameterDefinition.forString(name = "recommendedTestsBaselineCommitSha", defaultValue = "").register() + val RECOMMENDED_TESTS_BASELINE_BUILD_VERSION = AgentParameterDefinition.forString(name = "recommendedTestsBaselineBuildVersion", defaultValue = "").register() + + val TEST_TRACING_PER_SESSION_ENABLED = AgentParameterDefinition.forBoolean(name = "testTracingPerTestSessionEnabled", defaultValue = true).register() + val TEST_TRACING_PER_TEST_LAUNCH_ENABLED = AgentParameterDefinition.forBoolean(name = "testTracingPerTestLaunchEnabled", defaultValue = true).register() +} diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt new file mode 100644 index 00000000..f8127c72 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer + +expect object Cucumber4Transformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt new file mode 100644 index 00000000..43493a06 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer + +expect object Cucumber5Transformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt new file mode 100644 index 00000000..f30fe8d5 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer + +expect object Cucumber6Transformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt new file mode 100644 index 00000000..08d715e0 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.jmeter + +import com.epam.drill.agent.instrument.Transformer + +expect object JMeterTransformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt new file mode 100644 index 00000000..b1dd5d1c --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer + +expect object JUnit4PrioritizingTransformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt new file mode 100644 index 00000000..c6ed84f4 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer + +expect object JUnit4Transformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt new file mode 100644 index 00000000..eda5b6c2 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer + +expect object JUnit5Transformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt new file mode 100644 index 00000000..bffba82f --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer + +expect object JUnitPlatformPrioritizingTransformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt new file mode 100644 index 00000000..5b0b184a --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.selenium + +import com.epam.drill.agent.instrument.Transformer + +expect object SeleniumTransformer : Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt new file mode 100644 index 00000000..dc3503ca --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +expect object TestNG6PrioritizingTransformer: Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt new file mode 100644 index 00000000..27837eb5 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +expect object TestNG6Transformer: Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt new file mode 100644 index 00000000..8414131d --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +expect object TestNG7PrioritizingTransformer: Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt new file mode 100644 index 00000000..0df511af --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +expect object TestNG7Transformer: Transformer \ No newline at end of file diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt new file mode 100644 index 00000000..bb6309f9 --- /dev/null +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.session + +expect object SessionController { + fun startSession() +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt index f7e47af1..c21bb237 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt @@ -17,45 +17,17 @@ package com.epam.drill.agent import com.epam.drill.agent.configuration.AgentParameterValidationError import com.epam.drill.agent.configuration.AgentParametersValidator +import com.epam.drill.agent.configuration.CapabilityParameterDefinitions import com.epam.drill.agent.configuration.Configuration import com.epam.drill.agent.configuration.DefaultParameterDefinitions import com.epam.drill.agent.configuration.ParameterDefinitions -import com.epam.drill.agent.instrument.ApplicationClassTransformer import com.epam.drill.agent.instrument.CompositeTransformer -import com.epam.drill.agent.instrument.TransformerRegistrar -import com.epam.drill.agent.instrument.clients.ApacheHttpClientTransformer -import com.epam.drill.agent.instrument.clients.JavaHttpClientTransformer -import com.epam.drill.agent.instrument.clients.OkHttp3ClientTransformer -import com.epam.drill.agent.instrument.clients.SpringWebClientTransformer -import com.epam.drill.agent.instrument.jetty.Jetty10WsMessagesTransformer -import com.epam.drill.agent.instrument.jetty.Jetty11WsMessagesTransformer -import com.epam.drill.agent.instrument.jetty.Jetty9WsMessagesTransformer -import com.epam.drill.agent.instrument.jetty.JettyHttpServerTransformer -import com.epam.drill.agent.instrument.jetty.JettyWsClientTransformer -import com.epam.drill.agent.instrument.jetty.JettyWsServerTransformer -import com.epam.drill.agent.instrument.netty.NettyHttpServerTransformer -import com.epam.drill.agent.instrument.netty.NettyWsClientTransformer -import com.epam.drill.agent.instrument.netty.NettyWsMessagesTransformer -import com.epam.drill.agent.instrument.netty.NettyWsServerTransformer -import com.epam.drill.agent.instrument.servers.CadenceTransformer -import com.epam.drill.agent.instrument.servers.CompatibilityTestsTransformer -import com.epam.drill.agent.instrument.servers.KafkaTransformer -import com.epam.drill.agent.instrument.servers.ReactorTransformer -import com.epam.drill.agent.instrument.servers.TTLTransformer -import com.epam.drill.agent.instrument.tomcat.TomcatHttpServerTransformer -import com.epam.drill.agent.instrument.tomcat.TomcatWsClientTransformer -import com.epam.drill.agent.instrument.tomcat.TomcatWsMessagesTransformer -import com.epam.drill.agent.instrument.tomcat.TomcatWsServerTransformer -import com.epam.drill.agent.instrument.undertow.UndertowHttpServerTransformer -import com.epam.drill.agent.instrument.undertow.UndertowWsClientTransformer -import com.epam.drill.agent.instrument.undertow.UndertowWsMessagesTransformer -import com.epam.drill.agent.instrument.undertow.UndertowWsServerTransformer import com.epam.drill.agent.logging.LoggingConfiguration import com.epam.drill.agent.module.JvmModuleLoader +import com.epam.drill.agent.test.session.SessionController import com.epam.drill.agent.test2code.Test2Code import com.epam.drill.agent.test2code.configuration.Test2CodeParameterDefinitions import com.epam.drill.agent.transport.JvmModuleMessageSender -import org.objectweb.asm.ClassReader import mu.KotlinLogging import java.lang.instrument.ClassFileTransformer import java.lang.instrument.Instrumentation @@ -83,8 +55,13 @@ fun premain(agentArgs: String?, inst: Instrumentation) { updateJvmLoggingConfiguration() validateConfiguration() inst.addTransformer(DrillClassFileTransformer, true) - JvmModuleMessageSender.sendAgentMetadata() - JvmModuleLoader.loadJvmModule(Test2Code::class.java.name).load() + inst.retransformClasses(*inst.allLoadedClasses) + if (isClassScanningEnabled() || isCoverageCollectionEnabled()) { + JvmModuleMessageSender.sendAgentMetadata() + JvmModuleLoader.loadJvmModule(Test2Code::class.java.name).load() + } + + SessionController.startSession() } catch (e: Throwable) { println("Drill4J Initialization Error:\n${e.message ?: e::class.java.name}") } @@ -172,4 +149,7 @@ object DrillClassFileTransformer : ClassFileTransformer { return if (newClassBytes !== oldClassBytes) newClassBytes else null } -} \ No newline at end of file +} + +private fun isClassScanningEnabled(): Boolean = Configuration.parameters[CapabilityParameterDefinitions.CLASS_SCANNING_ENABLED] +private fun isCoverageCollectionEnabled(): Boolean = Configuration.parameters[CapabilityParameterDefinitions.COVERAGE_COLLECTION_ENABLED] \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt index 6569cdad..99f699c4 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt @@ -41,9 +41,9 @@ actual object Configuration : AgentConfiguration { DefaultParameterDefinitions.COMMIT_SHA, DefaultParameterDefinitions.ENV_ID, DefaultParameterDefinitions.INSTALLATION_DIR, - DefaultParameterDefinitions.CONFIG_PATH + DefaultParameterDefinitions.CONFIG_PATH, + DefaultParameterDefinitions.PACKAGE_PREFIXES ) - agentParameters.define(DefaultParameterDefinitions.PACKAGE_PREFIXES) } actual override val agentMetadata: AgentMetadata diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/CompositeTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/CompositeTransformer.kt index 737713bf..923b4bf9 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/CompositeTransformer.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/CompositeTransformer.kt @@ -40,7 +40,7 @@ actual object CompositeTransformer : Transformer { .onFailure { logger.warn(it) { "Can't read class: $classFileBuffer" } } .getOrNull() ?: return null val enabledTransformers = runCatching { transformers.enabledTransformers }.onFailure { - logger.warn(it) { "Can't get enabled transformers for class: $className" } + logger.warn { "Can't get enabled transformers for class: $className: " + it.message } }.getOrNull() ?: return null return enabledTransformers.fold(classFileBuffer) { bytes, transformer -> runCatching { diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt index 983cfe1f..2925f53b 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt @@ -44,6 +44,19 @@ import com.epam.drill.agent.instrument.undertow.UndertowHttpServerTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsClientTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsMessagesTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsServerTransformer +import com.epam.drill.agent.test.instrument.cucumber.Cucumber4Transformer +import com.epam.drill.agent.test.instrument.cucumber.Cucumber5Transformer +import com.epam.drill.agent.test.instrument.cucumber.Cucumber6Transformer +import com.epam.drill.agent.test.instrument.jmeter.JMeterTransformer +import com.epam.drill.agent.test.instrument.junit.JUnit4PrioritizingTransformer +import com.epam.drill.agent.test.instrument.junit.JUnit4Transformer +import com.epam.drill.agent.test.instrument.junit.JUnit5Transformer +import com.epam.drill.agent.test.instrument.junit.JUnitPlatformPrioritizingTransformer +import com.epam.drill.agent.test.instrument.selenium.SeleniumTransformer +import com.epam.drill.agent.test.instrument.testng.TestNG6PrioritizingTransformer +import com.epam.drill.agent.test.instrument.testng.TestNG6Transformer +import com.epam.drill.agent.test.instrument.testng.TestNG7PrioritizingTransformer +import com.epam.drill.agent.test.instrument.testng.TestNG7Transformer actual object TransformerRegistrar { private val logger = KotlinLogging.logger {} @@ -77,6 +90,20 @@ actual object TransformerRegistrar { UndertowWsServerTransformer, UndertowWsMessagesTransformer, CompatibilityTestsTransformer, + //Test instrumentation + Cucumber4Transformer, + Cucumber5Transformer, + Cucumber6Transformer, + JMeterTransformer, + JUnit4PrioritizingTransformer, + JUnit4Transformer, + JUnit5Transformer, + JUnitPlatformPrioritizingTransformer, + SeleniumTransformer, + TestNG6PrioritizingTransformer, + TestNG6Transformer, + TestNG7PrioritizingTransformer, + TestNG7Transformer ) actual val enabledTransformers: List by lazy { transformers.filter { transformer -> diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/GlobalConstants.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/GlobalConstants.kt new file mode 100644 index 00000000..b2c468f9 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/GlobalConstants.kt @@ -0,0 +1,34 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test + +import com.epam.drill.agent.test.instrument.selenium.* +import com.epam.drill.agent.test.session.SessionController +import com.epam.drill.agent.test.execution.TestController +import kotlin.jvm.java + +const val TEST_ID_HEADER = "drill-test-id" +const val SESSION_ID_HEADER = "drill-session-id" + +val TEST_NAME_VALUE_CALC_LINE = "((String)${TestController::class.qualifiedName}.INSTANCE.${TestController::getTestLaunchId.name}())" +val TEST_NAME_CALC_LINE = "\"$TEST_ID_HEADER\", $TEST_NAME_VALUE_CALC_LINE" +val SESSION_ID_VALUE_CALC_LINE = "${SessionController::class.qualifiedName}.INSTANCE.${SessionController::getSessionId.name}()" +val SESSION_ID_CALC_LINE = "\"$SESSION_ID_HEADER\", $SESSION_ID_VALUE_CALC_LINE" +val IF_CONDITION = "$TEST_NAME_VALUE_CALC_LINE != null && $SESSION_ID_VALUE_CALC_LINE != null" +val DEV_TOOL = "((${ChromeDevTool::class.java.name})${DevToolStorage::class.java.name}.INSTANCE.${DevToolStorage::get.name}())" +val IS_DEV_TOOL_NOT_NULL = "$DEV_TOOL != null" +val IS_HEADER_ADDED = "($DEV_TOOL != null && $DEV_TOOL.${ChromeDevTool::isHeadersAdded.name}())" +val ARE_DRILL_HEADERS_PRESENT = "$TEST_NAME_VALUE_CALC_LINE != null && $SESSION_ID_VALUE_CALC_LINE != null" diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/AddSessionData.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/AddSessionData.kt new file mode 100644 index 00000000..c99ff883 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/AddSessionData.kt @@ -0,0 +1,22 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.devtools + +import com.epam.drill.agent.common.transport.AgentMessage +import kotlinx.serialization.Serializable + +@Serializable +data class AddSessionData(val sessionId: String, val data: String): AgentMessage() \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/ChromeDevToolTestExecutionListener.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/ChromeDevToolTestExecutionListener.kt new file mode 100644 index 00000000..99786650 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/ChromeDevToolTestExecutionListener.kt @@ -0,0 +1,41 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.devtools + +import com.epam.drill.agent.test.instrument.selenium.DevToolStorage +import com.epam.drill.agent.test.instrument.selenium.WebDriverThreadStorage +import com.epam.drill.agent.test.execution.TestExecutionListener +import com.epam.drill.agent.test.execution.TestMethodInfo +import com.epam.drill.agent.test.execution.TestResult + +class ChromeDevToolTestExecutionListener( + private val jsCoverageSender: JsCoverageSender +): TestExecutionListener { + + override fun onTestStarted(testLaunchId: String, test: TestMethodInfo) { + DevToolStorage.get()?.startIntercept() + WebDriverThreadStorage.addCookies() + } + + override fun onTestFinished(testLaunchId: String, test: TestMethodInfo, result: TestResult) { + DevToolStorage.get()?.stopIntercept() + jsCoverageSender.sendJsCoverage(testLaunchId) + } + + override fun onTestIgnored(testLaunchId: String, test: TestMethodInfo) { + DevToolStorage.get()?.stopIntercept() + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/DevToolsMessageSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/DevToolsMessageSender.kt new file mode 100644 index 00000000..00bc632e --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/DevToolsMessageSender.kt @@ -0,0 +1,93 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.devtools + +import mu.KotlinLogging +import com.epam.drill.agent.transport.JsonAgentMessageSerializer +import com.epam.drill.agent.transport.http.HttpAgentMessageTransport +import com.epam.drill.agent.common.transport.AgentMessage +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.ResponseStatus +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.instrument.selenium.DevToolsMessage +import com.epam.drill.agent.transport.JsonAgentMessageDeserializer +import kotlin.reflect.KClass + +object DevToolsMessageSender { + + private val messageTransport = HttpAgentMessageTransport( + serverAddress = Configuration.parameters[ParameterDefinitions.DEVTOOLS_PROXY_ADDRESS], + drillInternal = false, + gzipCompression = false, + ) + private val messageSerializer = JsonAgentMessageSerializer() + private val messageDeserializer = JsonAgentMessageDeserializer() + private val logger = KotlinLogging.logger {} + + fun send( + method: String, + path: String, + message: DevToolsMessage + ) = messageTransport.send( + AgentMessageDestination(method, path), + messageSerializer.serialize(message, DevToolsMessage.serializer()), + messageSerializer.contentType() + ) + .mapContent { it.decodeToString() } + .also(DevToolsMessageSender::logResponseContent) + + fun send( + method: String, + path: String, + message: DevToolsMessage, + clazz: KClass + ) = messageTransport.send( + AgentMessageDestination(method, path), + messageSerializer.serialize(message, DevToolsMessage.serializer()), + messageSerializer.contentType() + ) + .mapContent { + messageDeserializer.deserialize(it, clazz) + } + .also(DevToolsMessageSender::logResponseContent) + + fun send( + serverAddress: String, + method: String, + path: String, + message: String + ): ResponseStatus = HttpAgentMessageTransport( + serverAddress = serverAddress, + drillInternal = false, + gzipCompression = false, + ).send( + AgentMessageDestination(method, path), + message.encodeToByteArray(), + messageSerializer.contentType() + ) + .mapContent { it.decodeToString() } + .also(DevToolsMessageSender::logResponseContent) + + private fun logResponseContent(responseContent: ResponseStatus<*>) = logger.trace { + val response = responseContent.content.toString() + .takeIf(String::isNotEmpty) + ?.let { "\n${it.prependIndent("\t")}" } + ?: " " + "send: Response received, success=${responseContent.success}: $response" + } + +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/JsCoverageSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/JsCoverageSender.kt new file mode 100644 index 00000000..eac17689 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/JsCoverageSender.kt @@ -0,0 +1,65 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.devtools + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.test.instrument.selenium.* +import com.epam.drill.agent.test.serialization.json +import com.epam.drill.agent.test.session.SessionController +import com.epam.drill.agent.test.transport.TestAgentMessageSender +import mu.KotlinLogging + +interface JsCoverageSender { + fun sendJsCoverage(testLaunchId: String) +} + +class JsCoverageSenderImpl : JsCoverageSender { + private val logger = KotlinLogging.logger {} + + override fun sendJsCoverage(testLaunchId: String) { + DevToolStorage.get()?.run { + val coverage = takePreciseCoverage() + if (coverage.isBlank()) { + logger.trace { "coverage is blank" } + return + } + val scripts = scriptParsed() + if (scripts.isBlank()) { + logger.trace { "script parsed is blank" } + return + } + logger.debug { "ThreadStorage.sendSessionData" } + sendSessionData(SessionData(coverage, scripts, testLaunchId)) + } + } + + private fun sendSessionData(data: SessionData) = runCatching { + val payload = AddSessionData( + sessionId = SessionController.getSessionId(), + data = json.encodeToString(SessionData.serializer(), data) + ) + TestAgentMessageSender.send( + destination = AgentMessageDestination( + "POST", + "raw-javascript-coverage" + ), + message = payload, + serializer = AddSessionData.serializer() + ) + }.onFailure { + logger.warn(it) { "can't send js raw coverage ${SessionController.getSessionId()}" } + }.getOrNull() +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/SessionData.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/SessionData.kt new file mode 100644 index 00000000..078c0740 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/devtools/SessionData.kt @@ -0,0 +1,25 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.devtools + +import kotlinx.serialization.* + +@Serializable +data class SessionData( + val coverage: String, + val scripts: String, + val testId: String, +) diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestController.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestController.kt new file mode 100644 index 00000000..794554cb --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestController.kt @@ -0,0 +1,115 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.execution + +import com.epam.drill.agent.request.DrillRequestHolder +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.TEST_ID_HEADER +import com.epam.drill.agent.test.devtools.ChromeDevToolTestExecutionListener +import com.epam.drill.agent.test.devtools.JsCoverageSenderImpl + +object TestController : TestExecutionRecorder by testExecutionRecorder(testExecutionListeners()) { + + //TODO EPMDJ-10251 add browser name for ui tests + @JvmOverloads + fun testStarted( + engine: String, + className: String?, + method: String?, + methodParams: String = "()", + testTags: List = emptyList(), + ) { + if (className == null || method == null) + return + + recordTestStarting( + TestMethodInfo( + engine = engine, + className = className, + method = method, + methodParams = methodParams, + tags = testTags + ) + ) + } + + @JvmOverloads + fun testFinished( + engine: String, + className: String?, + method: String?, + status: String, + methodParams: String = "()", + testTags: List = emptyList(), + ) { + if (className == null || method == null) + return + + recordTestFinishing( + TestMethodInfo( + engine = engine, + className = className, + method = method, + methodParams = methodParams, + tags = testTags + ), + status + ) + } + + @JvmOverloads + fun testIgnored( + engine: String, + className: String?, + method: String?, + methodParams: String = "()", + testTags: List = emptyList(), + ) { + if (className == null || method == null) + return + + recordTestIgnoring( + TestMethodInfo( + engine = engine, + className = className, + method = method, + methodParams = methodParams, + tags = testTags + ) + ) + } + + @Deprecated("Use explicit retrieve() instead", ReplaceWith("retrieve()")) + fun getTestLaunchId(): String? = DrillRequestHolder.retrieve()?.headers?.get(TEST_ID_HEADER) +} + +fun testExecutionListeners(): List { + val listeners = arrayListOf() + if (Configuration.parameters[ParameterDefinitions.WITH_JS_COVERAGE]) { + listeners.add( + ChromeDevToolTestExecutionListener( + jsCoverageSender = JsCoverageSenderImpl() + ) + ) + } + return listeners +} + +fun testExecutionRecorder(listeners: List) = ThreadTestExecutionRecorder( + requestHolder = DrillRequestHolder, + listeners = listeners +) \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionInfo.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionInfo.kt new file mode 100644 index 00000000..81260b95 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionInfo.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.execution + +class TestExecutionInfo( + val testLaunchId: String, + val testMethod: TestMethodInfo, + var result: TestResult = TestResult.UNKNOWN, + var startedAt: Long? = null, + var finishedAt: Long? = null, +) \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionListener.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionListener.kt new file mode 100644 index 00000000..c5caa637 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionListener.kt @@ -0,0 +1,22 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.execution + +interface TestExecutionListener { + fun onTestStarted(testLaunchId: String, test: TestMethodInfo) {} + fun onTestFinished(testLaunchId: String, test: TestMethodInfo, result: TestResult) {} + fun onTestIgnored(testLaunchId: String, test: TestMethodInfo) {} +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionRecorder.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionRecorder.kt new file mode 100644 index 00000000..d6fc9f68 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionRecorder.kt @@ -0,0 +1,36 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.execution + +interface TestExecutionRecorder { + fun recordTestStarting( + testMethod: TestMethodInfo + ) + + fun recordTestFinishing( + testMethod: TestMethodInfo, + status: String + ) + + fun recordTestIgnoring( + testMethod: TestMethodInfo, + isSmartSkip: Boolean = false + ) + + fun getFinishedTests(): List + + fun reset() +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestMethodInfo.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestMethodInfo.kt new file mode 100644 index 00000000..7b2280b2 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestMethodInfo.kt @@ -0,0 +1,45 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.execution + +data class TestMethodInfo @JvmOverloads constructor( + val engine: String, + val className: String, + val method: String, + val methodParams: String = "()", + val metadata: Map = emptyMap(), + val tags: List = emptyList(), +) : Comparable { + + val signature: String + get() = "$engine:$className.$method${methodParams}" + + override fun compareTo(other: TestMethodInfo): Int { + return signature.compareTo(other.signature) + } + + override fun equals(other: Any?): Boolean { + return if (other is TestMethodInfo) compareTo(other) == 0 else false + } + + override fun hashCode(): Int { + return signature.hashCode() + } + + override fun toString(): String { + return signature + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestResult.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestResult.kt new file mode 100644 index 00000000..eef459ae --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestResult.kt @@ -0,0 +1,25 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.execution + +enum class TestResult { + PASSED, + FAILED, + ERROR, + SKIPPED, + SMART_SKIPPED, + UNKNOWN +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/ThreadTestExecutionRecorder.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/ThreadTestExecutionRecorder.kt new file mode 100644 index 00000000..d09d59e5 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/ThreadTestExecutionRecorder.kt @@ -0,0 +1,139 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.execution + +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import com.epam.drill.agent.test.TEST_ID_HEADER +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.session.SessionController +import mu.KotlinLogging +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +class ThreadTestExecutionRecorder( + private val requestHolder: RequestHolder, + private val listeners: List = emptyList() +) : TestExecutionRecorder { + private val logger = KotlinLogging.logger {} + private val testExecutionData: ConcurrentHashMap = ConcurrentHashMap() + private val testLaunchHolder: ThreadLocal = ThreadLocal.withInitial { null } + + override fun recordTestStarting( + testMethod: TestMethodInfo + ) { + val testLaunchId = generateTestLaunchId() + testLaunchHolder.set(testLaunchId) + updateTestInfo(testLaunchId, testMethod) { + it.startedAt = System.currentTimeMillis() + } + addDrillHeaders(testLaunchId) + listeners.forEach { it.onTestStarted(testLaunchId, testMethod) } + logger.debug { "Test: $testMethod STARTED" } + } + + override fun recordTestFinishing( + testMethod: TestMethodInfo, + status: String + ) { + val testLaunchId = testLaunchHolder.get() + if (testLaunchId == null) { + logger.warn { "Test ${testMethod.className}::${testMethod.method} finished with result $status but no test launch id was found." } + return + } + testLaunchHolder.remove() + val testResult = mapToTestResult(status) + updateTestInfo(testLaunchId, testMethod) { + it.finishedAt = System.currentTimeMillis() + it.result = testResult + } + clearDrillHeaders() + listeners.forEach { it.onTestFinished(testLaunchId, testMethod, testResult) } + logger.debug { "Test: $testMethod FINISHED. Result: $status" } + } + + override fun recordTestIgnoring( + testMethod: TestMethodInfo, + isSmartSkip: Boolean + ) { + val testLaunchId = testLaunchHolder.get() ?: generateTestLaunchId() + testLaunchHolder.remove() + val skipResult = if (isSmartSkip) TestResult.SMART_SKIPPED else TestResult.SKIPPED + updateTestInfo(testLaunchId, testMethod) { + it.startedAt = null + it.finishedAt = null + it.result = skipResult + } + clearDrillHeaders() + listeners.forEach { it.onTestIgnored(testLaunchId, testMethod) } + logger.debug { "Test: $testMethod FINISHED. Result: $skipResult" } + } + + override fun reset() { + testExecutionData.clear() + } + + override fun getFinishedTests(): List = testExecutionData + .filterValues { test -> test.result != TestResult.UNKNOWN } + .onEach { + testExecutionData.remove(it.key) + }.values.toList() + + private fun updateTestInfo( + testLaunchId: String, + testMethodInfo: TestMethodInfo, + updateTestExecutionInfo: (TestExecutionInfo) -> Unit, + ) { + testExecutionData.compute(testLaunchId) { _, value -> + val testExecutionInfo = value ?: TestExecutionInfo(testLaunchId, testMethodInfo) + if (testExecutionInfo.result == TestResult.UNKNOWN) { + updateTestExecutionInfo(testExecutionInfo) + } else { + logger.warn { "Test ${testMethodInfo.method} already finished with result ${testExecutionInfo.result}" } + } + testExecutionInfo + } + } + + private fun generateTestLaunchId() = UUID.randomUUID().toString() + + private fun addDrillHeaders(testLaunchId: String) { + val isTestTracingPerTestLaunchEnabled = Configuration.parameters[ParameterDefinitions.TEST_TRACING_PER_TEST_LAUNCH_ENABLED] + val isTestTracingPerSessionEnabled = Configuration.parameters[ParameterDefinitions.TEST_TRACING_PER_SESSION_ENABLED] + + val drillRequest = when { + isTestTracingPerTestLaunchEnabled -> DrillRequest( + drillSessionId = SessionController.getSessionId(), + headers = mapOf(TEST_ID_HEADER to (testLaunchId)) + ) + isTestTracingPerSessionEnabled -> DrillRequest( + drillSessionId = SessionController.getSessionId() + ) + else -> null + } ?: return + requestHolder.store(drillRequest) + } + + private fun clearDrillHeaders() { + requestHolder.remove() + } + + private fun mapToTestResult(value: String): TestResult { + if (value == "SUCCESSFUL") return TestResult.PASSED + return TestResult.valueOf(value) + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/AbstractTestTransformerObject.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/AbstractTestTransformerObject.kt new file mode 100644 index 00000000..12e17daf --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/AbstractTestTransformerObject.kt @@ -0,0 +1,51 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.CapabilityParameterDefinitions.TEST_TRACING_ENABLED +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.ClassPathProvider +import com.epam.drill.agent.instrument.RuntimeClassPathProvider +import javassist.ClassPool +import javassist.CtClass +import java.security.ProtectionDomain + +abstract class AbstractTestTransformerObject : AbstractTransformerObject(Configuration), + ClassPathProvider by RuntimeClassPathProvider { + override fun enabled() = super.enabled() && Configuration.parameters[TEST_TRACING_ENABLED] + + override fun transform(className: String, ctClass: CtClass) { + throw NotImplementedError("AbstractTestTransformerObject.transform is not implemented") + } + + override fun transform( + className: String, + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + instrument(ctClass, pool, classLoader, protectionDomain) + } + + abstract fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/RuntimeClassPathProvider.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/RuntimeClassPathProvider.kt new file mode 100644 index 00000000..85087676 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/RuntimeClassPathProvider.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument + +import com.epam.drill.agent.configuration.DefaultParameterDefinitions.INSTALLATION_DIR +import com.epam.drill.agent.instrument.ClassPathProvider +import com.epam.drill.agent.configuration.Configuration + +object RuntimeClassPathProvider : ClassPathProvider { + + override fun getClassPath() = "${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar" + +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/TestSessionHeadersProcessor.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/TestSessionHeadersProcessor.kt new file mode 100644 index 00000000..de478380 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/TestSessionHeadersProcessor.kt @@ -0,0 +1,44 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument + +import com.epam.drill.agent.request.DrillRequestHolder +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.test.SESSION_ID_HEADER +import com.epam.drill.agent.test.TEST_ID_HEADER + +object TestSessionHeadersProcessor : HeadersProcessor { + + override fun removeHeaders() = Unit + + override fun storeHeaders(headers: Map) = Unit + + override fun retrieveHeaders() = mutableMapOf().apply { + DrillRequestHolder.retrieve()?.let { + put(SESSION_ID_HEADER, it.drillSessionId) + putAll(it.headers) + } + } + + override fun hasHeaders() = retrieveHeaders().run { + this.isNotEmpty() && this.get(SESSION_ID_HEADER) != null && this.get(TEST_ID_HEADER) != null + } + + override fun isProcessRequests() = true + + override fun isProcessResponses() = false + +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumber56Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumber56Transformer.kt new file mode 100644 index 00000000..444d79bd --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumber56Transformer.kt @@ -0,0 +1,68 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import javassist.* + +abstract class AbstractCucumber56Transformer() : AbstractCucumberTransformer() { + override val Status = "io.cucumber.plugin.event.Status" + override val EventBus: String = "io.cucumber.core.eventbus.EventBus" + override val EventHandler = """io.cucumber.plugin.event.EventHandler""" + override val PickleStepDefinitionMatch: String = "io.cucumber.core.runner.PickleStepDefinitionMatch" + override val testPackage = "io.cucumber.plugin.event" + abstract val versionPattern: Regex + + /** + * From cucumber 5 TestStep class location doesn't change + */ + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == "io/cucumber/core/runner/TestStep" + } + + override fun getFeaturePath(): String = """ + String[] paths = new java.io.File(".").toURI().relativize($1.getUri()).toString().split(":"); + int index = paths.length - 1; + String featurePath = paths[index]; + if (featurePath.startsWith("/")) { + featurePath = featurePath.replaceFirst("/", ""); + } + """.trimIndent() + + override fun getTestStatus(): String = """finishedTest.getResult().getStatus();""".trimIndent() + + override fun CtClass.implEventBusMethods() { + addMethod( + CtMethod.make( + """ + public java.time.Instant getInstant() { + return mainEventBus.getInstant(); + } + """.trimIndent(), + this + ) + ) + addMethod( + CtMethod.make( + """ + public java.util.UUID generateId() { + return mainEventBus.generateId(); + } + """.trimIndent(), + this + ) + ) + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumberTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumberTransformer.kt new file mode 100644 index 00000000..76e960b4 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumberTransformer.kt @@ -0,0 +1,140 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_CUCUMBER_ENABLED +import com.epam.drill.agent.test.execution.TestController +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject +import javassist.* +import mu.KotlinLogging +import java.security.* + +abstract class AbstractCucumberTransformer() : AbstractTestTransformerObject() { + val engineSegment = "cucumber" + val EventBusProxy = "EventBusProxy" + + override val logger = KotlinLogging.logger {} + abstract val Status: String + abstract val EventBus: String + abstract val EventHandler: String + abstract val Event: String + abstract val PickleStepDefinitionMatch: String + abstract val testPackage: String + + override fun enabled() = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_CUCUMBER_ENABLED] + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + val cc: CtClass = pool.makeClass(EventBusProxy) + cc.interfaces = arrayOf(pool.get(EventBus)) + cc.addField(CtField.make("$EventBus mainEventBus = null;", cc)) + cc.addField(CtField.make("String featurePath = \"\";", cc)) + cc.addConstructor( + CtNewConstructor.make( + """ + public $EventBusProxy($EventBus mainEventBus, String featurePath) { + this.mainEventBus = mainEventBus; + this.featurePath = featurePath; + } + """.trimMargin(), + cc + ) + ) + cc.implEventBusMethods() + + cc.addMethod( + CtMethod.make( + """ + public void send($Event event) { + mainEventBus.send(event); + if (event instanceof $testPackage.TestStepStarted) { + ${TestController::class.java.name}.INSTANCE.${TestController::testStarted.name}("$engineSegment", featurePath, (($testPackage.TestStepStarted) event).getTestCase().getName()); + } else if (event instanceof $testPackage.TestStepFinished) { + $testPackage.TestStepFinished finishedTest = ($testPackage.TestStepFinished) event; + $Status status = ${getTestStatus()} + if (status != $Status.PASSED) { + status = $Status.FAILED; + } + ${TestController::class.java.name}.INSTANCE.${TestController::testFinished.name}("$engineSegment", featurePath, finishedTest.getTestCase().getName(), status.name()); + } + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public void sendAll(Iterable queue) { + mainEventBus.sendAll(queue); + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public void removeHandlerFor(Class aClass, $EventHandler eventHandler) { + mainEventBus.removeHandlerFor(aClass, eventHandler); + } + """.trimIndent(), + cc + ) + ) + + cc.addMethod( + CtMethod.make( + """ + public void registerHandlerFor(Class aClass, $EventHandler eventHandler) { + mainEventBus.registerHandlerFor(aClass, eventHandler); + } + """.trimIndent(), + cc + ) + ) + cc.toClass(classLoader, protectionDomain) + + /** + * {@link PickleStepDefinitionMatch} is represent a step of scenario. + * Check for PickleStepDefinitionMatch is needed to determine what we are currently performing, + * a step from a scenario or before or after action. + * Instead of the class name, we use the path to the feature file. + * If the file is in the same repository as the tests, then we take the relative path, + * otherwise we take the absolute path without specifying the disk name + */ + ctClass.getDeclaredMethod("run").insertBefore( + """ + try { + if (stepDefinitionMatch instanceof $PickleStepDefinitionMatch) { + ${getFeaturePath()} + $2 = new $EventBusProxy($2, featurePath); + } + } catch (Throwable ignored) {} + """.trimIndent() + ) + } + + abstract fun getFeaturePath(): String + + abstract fun getTestStatus(): String + + abstract fun CtClass.implEventBusMethods() +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt new file mode 100644 index 00000000..e4f0181f --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt @@ -0,0 +1,66 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer +import javassist.* + +actual object Cucumber4Transformer : Transformer, AbstractCucumberTransformer() { + override val testPackage = "cucumber.api.event" + override val Status = "cucumber.api.Result.Type" + override val EventBus = "cucumber.runner.EventBus" + override val EventHandler: String = "cucumber.api.event.EventHandler" + override val Event = "cucumber.api.event.Event" + override val PickleStepDefinitionMatch = "cucumber.runner.PickleStepDefinitionMatch" + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == /*4.x.x*/"cucumber/runner/TestStep" + } + + override fun getFeaturePath(): String = """ + String[] paths = new java.io.File(".").toURI().resolve($1.getUri()).toString().split(":"); + int index = paths.length - 1; + String featurePath = paths[index]; + if (featurePath.startsWith("/")) { + featurePath = featurePath.replaceFirst("/", ""); + } + """.trimIndent() + + override fun getTestStatus(): String = """finishedTest.result.getStatus();""".trimIndent() + + override fun CtClass.implEventBusMethods() { + addMethod( + CtMethod.make( + """ + public Long getTime() { + return mainEventBus.getTime(); + } + """.trimIndent(), + this + ) + ) + addMethod( + CtMethod.make( + """ + public Long getTimeMillis() { + return mainEventBus.getTimeMillis(); + } + """.trimIndent(), + this + ) + ) + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt new file mode 100644 index 00000000..d906f039 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt @@ -0,0 +1,37 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer +import java.security.ProtectionDomain +import javassist.ClassPool +import javassist.CtClass + +actual object Cucumber5Transformer : Transformer, AbstractCucumber56Transformer() { + override val versionPattern: Regex = "5\\.[0-9]+\\.[0-9]+".toRegex() + override val Event: String = "io.cucumber.plugin.event.Event" + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + if ("${ctClass.url}".contains(versionPattern)) { + super.instrument(ctClass, pool, classLoader, protectionDomain) + } + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt new file mode 100644 index 00000000..2a58f327 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt @@ -0,0 +1,37 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer +import java.security.ProtectionDomain +import javassist.ClassPool +import javassist.CtClass + +actual object Cucumber6Transformer : Transformer, AbstractCucumber56Transformer() { + override val versionPattern: Regex = "6\\.[0-9]+\\.[0-9]+".toRegex() + override val Event: String = "java.lang.Object" + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + if ("${ctClass.url}".contains(versionPattern)) { + super.instrument(ctClass, pool, classLoader, protectionDomain) + } + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt new file mode 100644 index 00000000..909143f4 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt @@ -0,0 +1,54 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.jmeter + +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_JMETER_ENABLED +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject +import javassist.* +import mu.KotlinLogging +import java.security.* + +actual object JMeterTransformer : Transformer, AbstractTestTransformerObject() { + override val logger = KotlinLogging.logger {} + + override fun enabled() = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_JMETER_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == "org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl" + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + val setupRequestMethod = ctClass.getMethod( + "setupRequest", + "(Ljava/net/URL;Lorg/apache/http/client/methods/HttpRequestBase;" + + "Lorg/apache/jmeter/protocol/http/sampler/HTTPSampleResult;)V" + ) + //TODO memorizeTestName is not exist + setupRequestMethod.insertBefore( + """ + String drillTestName = $3.getSampleLabel(); + AgentClassTransformer.memorizeTestName(drillTestName); + + """.trimIndent() + ) + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/AbstractJUnitTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/AbstractJUnitTransformer.kt new file mode 100644 index 00000000..d6bfaac9 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/AbstractJUnitTransformer.kt @@ -0,0 +1,23 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_JUNIT_ENABLED +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +abstract class AbstractJUnitTransformer(): AbstractTestTransformerObject() { + override fun enabled() = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_JUNIT_ENABLED] +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt new file mode 100644 index 00000000..ac222357 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt @@ -0,0 +1,95 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.prioritization.RecommendedTests +import javassist.* +import mu.KotlinLogging +import java.security.ProtectionDomain + +private const val Filter = "org.junit.runner.manipulation.Filter" +private const val Description = "org.junit.runner.Description" + +actual object JUnit4PrioritizingTransformer : Transformer, AbstractJUnitTransformer() { + override val logger = KotlinLogging.logger {} + private val engineSegment = "junit" + private val DrillJUnit4Filter = "${this.javaClass.`package`.name}.gen.DrillJUnit4Filter" + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == "org/junit/runners/JUnit4" + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + createRecommendedTestsFilterClass(pool, classLoader, protectionDomain) + instrumentConstructor(ctClass) + } + + private fun instrumentConstructor(ctClass: CtClass) { + ctClass.constructors.forEach { constructor -> + constructor.insertAfter( + """ + $DrillJUnit4Filter drillFilter = new $DrillJUnit4Filter(); + filter(drillFilter); + """.trimIndent() + ) + } + } + + private fun createRecommendedTestsFilterClass( + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ): CtClass { + val cc = pool.makeClass(DrillJUnit4Filter, pool.get(Filter)) + cc.addMethod( + CtMethod.make( + """ + public boolean shouldRun($Description description) { + if (!description.isTest()) return true; + java.lang.String className = description.getClassName(); + if (className == null) return true; + java.lang.String methodName = description.getMethodName(); + if (methodName == null) return true; + boolean shouldSkip = ${RecommendedTests::class.java.name}.INSTANCE.${RecommendedTests::shouldSkip.name}("$engineSegment", className, methodName, null); + return !shouldSkip; + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public java.lang.String describe() { + return "skip tests by Drill4J"; + } + """.trimIndent(), + cc + ) + ) + cc.toClass(classLoader, protectionDomain) + return cc + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt new file mode 100644 index 00000000..d017ccca --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt @@ -0,0 +1,162 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.execution.TestController +import com.epam.drill.agent.test.execution.TestResult +import javassist.ClassPool +import javassist.CtClass +import javassist.CtField +import javassist.CtMethod +import javassist.CtNewConstructor +import mu.KotlinLogging +import java.security.ProtectionDomain + +actual object JUnit4Transformer: + Transformer, + AbstractJUnitTransformer() { + override val logger = KotlinLogging.logger {} + const val engineSegment = "junit" + + override fun permit( + className: String, + superName: String?, + interfaces: Array + ): Boolean { + return className == "org/junit/runner/notification/RunNotifier" + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + val cc: CtClass = pool.makeClass("MyList") + cc.superclass = pool.get("org.junit.runner.notification.RunListener") + cc.addField(CtField.make("org.junit.runner.notification.RunListener mainRunner = null;", cc)) + cc.addConstructor( + CtNewConstructor.make( + """ + public MyList(org.junit.runner.notification.RunListener mainRunner) { + this.mainRunner = mainRunner; + } + """.trimMargin(), cc + ) + ) + val dp = """description""" + cc.addMethod( + CtMethod.make( + """ + public void testRunStarted(org.junit.runner.Description $dp) throws Exception { + this.mainRunner.testRunStarted($dp); + } + """.trimIndent(), + cc + ) + ) + + cc.addMethod( + CtMethod.make( + """ + public void testStarted(org.junit.runner.Description $dp) throws Exception { + this.mainRunner.testStarted($dp); + ${TestController::class.java.name}.INSTANCE.${TestController::testStarted.name}("$engineSegment", $dp.getClassName(), $dp.getMethodName()); + } + """.trimIndent(), + cc + ) + ) + + + cc.addMethod( + CtMethod.make( + """ + public void testFinished(org.junit.runner.Description $dp) throws Exception { + this.mainRunner.testFinished(description); + ${TestController::class.java.name}.INSTANCE.${TestController::testFinished.name}("$engineSegment", $dp.getClassName(), $dp.getMethodName(), "${TestResult.PASSED.name}"); + } + """.trimIndent(), + cc + ) + ) + + cc.addMethod( + CtMethod.make( + """ + public void testRunFinished(org.junit.runner.Result result) throws Exception { + this.mainRunner.testRunFinished(result); + } + """.trimIndent(), + cc + ) + ) + + + val failureParamName = """failure""" + val desct = """$failureParamName.getDescription()""" + cc.addMethod( + CtMethod.make( + """ + public void testFailure(org.junit.runner.notification.Failure $failureParamName) throws Exception { + this.mainRunner.testFailure($failureParamName); + ${TestController::class.java.name}.INSTANCE.${TestController::testFinished.name}("$engineSegment", $desct.getClassName(), $desct.getMethodName(), "${TestResult.FAILED.name}"); + } + """.trimIndent(), + cc + ) + ) + + + cc.addMethod( + CtMethod.make( + """ + public void testAssumptionFailure(org.junit.runner.notification.Failure $failureParamName) { + this.mainRunner.testAssumptionFailure(failure); + } + """.trimIndent(), + cc + ) + ) + + + + cc.addMethod( + CtMethod.make( + """ + public void testIgnored(org.junit.runner.Description $dp) throws Exception { + this.mainRunner.testIgnored($dp); + ${TestController::class.java.name}.INSTANCE.${TestController::testIgnored.name}("$engineSegment", $dp.getClassName(), $dp.getMethodName()); + } + """.trimIndent(), + cc + ) + ) + + cc.toClass(classLoader, protectionDomain) + ctClass.getDeclaredMethod("addListener").insertBefore( + """ + $1= new MyList($1); + """.trimIndent() + ) + ctClass.getDeclaredMethod("addFirstListener").insertBefore( + """ + $1= new MyList($1); + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt new file mode 100644 index 00000000..b71dce79 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt @@ -0,0 +1,152 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.execution.TestController +import com.epam.drill.agent.test.execution.TestMethodInfo +import javassist.* +import mu.KotlinLogging +import java.security.* + +actual object JUnit5Transformer: Transformer, AbstractJUnitTransformer() { + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == "org/junit/platform/engine/support/hierarchical/NodeTestTaskContext" + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + val cc: CtClass = pool.makeClass("MyList") + cc.interfaces = arrayOf(pool.get("org.junit.platform.engine.EngineExecutionListener")) + cc.addField(CtField.make("org.junit.platform.engine.EngineExecutionListener mainRunner = null;", cc)) + cc.addConstructor( + CtNewConstructor.make( + """ + public MyList(org.junit.platform.engine.EngineExecutionListener mainRunner) { + this.mainRunner = mainRunner; + } + """.trimMargin(), cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public void dynamicTestRegistered(org.junit.platform.engine.TestDescriptor testDescriptor) { + mainRunner.dynamicTestRegistered(testDescriptor); + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public void executionSkipped(org.junit.platform.engine.TestDescriptor testDescriptor, String reason) { + mainRunner.executionSkipped(testDescriptor, reason); + if (!testDescriptor.isContainer()) { + ${getMetadata("testDescriptor")} + ${getTags("testDescriptor")} + ${TestMethodInfo::class.java.name} methodInfo = ${this::class.java.name}.INSTANCE.${this::convertToMethodInfo.name}(testMetadata, testTags); + if (methodInfo != null) { + ${TestController::class.java.name}.INSTANCE.${TestController::recordTestIgnoring.name}(methodInfo, false); + } + } + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public void executionStarted(org.junit.platform.engine.TestDescriptor testDescriptor) { + mainRunner.executionStarted(testDescriptor); + if (!testDescriptor.isContainer()) { + ${getMetadata("testDescriptor")} + ${getTags("testDescriptor")} + ${TestMethodInfo::class.java.name} methodInfo = ${this::class.java.name}.INSTANCE.${this::convertToMethodInfo.name}(testMetadata, testTags); + if (methodInfo != null) { + ${TestController::class.java.name}.INSTANCE.${TestController::recordTestStarting.name}(methodInfo); + } + } + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public void executionFinished(org.junit.platform.engine.TestDescriptor testDescriptor, org.junit.platform.engine.TestExecutionResult testExecutionResult) { + mainRunner.executionFinished(testDescriptor, testExecutionResult); + if (!testDescriptor.isContainer()) { + ${getMetadata("testDescriptor")} + ${getTags("testDescriptor")} + ${TestMethodInfo::class.java.name} methodInfo = ${this::class.java.name}.INSTANCE.${this::convertToMethodInfo.name}(testMetadata, testTags); + if (methodInfo != null) { + ${TestController::class.java.name}.INSTANCE.${TestController::recordTestFinishing.name}(methodInfo, testExecutionResult.getStatus().name()); + } + } + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public void reportingEntryPublished(org.junit.platform.engine.TestDescriptor testDescriptor, org.junit.platform.engine.reporting.ReportEntry entry) { + mainRunner.reportingEntryPublished(testDescriptor, entry); + } + """.trimIndent(), + cc + ) + ) + cc.toClass(classLoader, protectionDomain) + + ctClass.constructors.first().insertBefore( + """ + $1 = new MyList($1); + """.trimIndent() + ) + } + + @Suppress("MemberVisibilityCanBePrivate") + fun convertToMethodInfo( + testMetadata: Map, + testTags: List + ): TestMethodInfo? { + return TestMethodInfo( + engine = testMetadata["engine"] ?: "junit", + className = testMetadata["class"] ?: return null, + method = testMetadata["method"]?.substringBefore("(") ?: return null, + methodParams = testMetadata["method"]?.getMethodParams() ?: "()", + metadata = testMetadata, + tags = testTags + ) + } + + private fun String.getMethodParams(): String { + val params = this.substringAfter("(").substringBefore(")") + return if (params.isEmpty()) "()" else "($params)" + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitClassNames.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitClassNames.kt new file mode 100644 index 00000000..983d3ce4 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitClassNames.kt @@ -0,0 +1,43 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +const val PostDiscoveryFilter = "org.junit.platform.launcher.PostDiscoveryFilter" +const val FilterResult = "org.junit.platform.engine.FilterResult" +const val TestDescriptor = "org.junit.platform.engine.TestDescriptor" +const val Segment = "org.junit.platform.engine.UniqueId.Segment" +const val LauncherDiscoveryRequest = "org.junit.platform.launcher.LauncherDiscoveryRequest" +const val ConfigurationParameters = "org.junit.platform.engine.ConfigurationParameters" +const val TestTag = "org.junit.platform.engine.TestTag" + +fun getMetadata(descriptor: String) = """ + java.util.Map testMetadata = new java.util.HashMap(); + for (int i = 0; i < $descriptor.getUniqueId().getSegments().size(); i++) { + java.lang.String key = (($Segment)$descriptor.getUniqueId().getSegments().get(i)).getType(); + java.lang.String value = (($Segment)$descriptor.getUniqueId().getSegments().get(i)).getValue(); + testMetadata.put(key, value); + } + """.trimIndent() + +fun getTags(descriptor: String) = """ + java.util.List testTags = new java.util.ArrayList(); + java.util.Set tags = $descriptor.getTags(); + java.util.Iterator iterator = tags.iterator(); + while (iterator.hasNext()) { + $TestTag tag = ($TestTag) iterator.next(); + testTags.add(tag.getName()); + } + """.trimIndent() \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt new file mode 100644 index 00000000..14bf5717 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt @@ -0,0 +1,201 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.execution.TestMethodInfo +import com.epam.drill.agent.test.prioritization.RecommendedTests +import javassist.* +import mu.KotlinLogging +import java.security.ProtectionDomain + +actual object JUnitPlatformPrioritizingTransformer : Transformer, AbstractJUnitTransformer() { + + override val logger = KotlinLogging.logger {} + private val DrillJUnit5Filter = "${this.javaClass.`package`.name}.gen.DrillJUnit5Filter" + private val LauncherDiscoveryRequestAdapter = "${this.javaClass.`package`.name}.gen.LauncherDiscoveryRequestAdapter" + + override fun enabled(): Boolean = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == "org/junit/platform/launcher/core/DefaultLauncher" + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + createRecommendedTestsFilterClass(pool, classLoader, protectionDomain) + createLauncherDiscoveryRequestAdapterClass(pool, classLoader, protectionDomain) + instrumentDiscoverMethod(ctClass) + instrumentExecuteMethod(ctClass) + } + + private fun instrumentDiscoverMethod(ctClass: CtClass) { + ctClass.getMethod( + "discover", + "(Lorg/junit/platform/launcher/LauncherDiscoveryRequest;)Lorg/junit/platform/launcher/TestPlan;" + ).insertBefore( + """ + $1 = new $LauncherDiscoveryRequestAdapter($1, new $DrillJUnit5Filter()); + """.trimIndent() + ) + } + + private fun instrumentExecuteMethod(ctClass: CtClass) { + ctClass.getMethod( + "execute", + "(Lorg/junit/platform/launcher/LauncherDiscoveryRequest;[Lorg/junit/platform/launcher/TestExecutionListener;)V") + .insertBefore( + """ + $1 = new $LauncherDiscoveryRequestAdapter($1, new $DrillJUnit5Filter()); + """.trimIndent() + ) + } + + private fun createRecommendedTestsFilterClass( + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ): CtClass { + val cc: CtClass = pool.makeClass(DrillJUnit5Filter) + cc.interfaces = arrayOf(pool.get(PostDiscoveryFilter)) + cc.addMethod( + CtMethod.make( + """ + public $FilterResult apply(java.lang.Object object) { + $TestDescriptor descriptor = ($TestDescriptor)object; + if (descriptor.isContainer()) + return $FilterResult.included(""); + + ${getMetadata("descriptor")} + ${getTags("descriptor")} + + ${TestMethodInfo::class.java.name} methodInfo = ${this::class.java.name}.INSTANCE.${this::convertToMethodInfo.name}(testMetadata, testTags, descriptor.getDisplayName()); + boolean shouldSkip = methodInfo != null && ${RecommendedTests::class.java.name}.INSTANCE.${RecommendedTests::shouldSkipByTestMethod.name}(methodInfo); + if (shouldSkip) { + return $FilterResult.excluded("skipped by Drill4J"); + } else { + return $FilterResult.included("recommended by Drill4J"); + } + } + """.trimIndent(), + cc + ) + ) + cc.toClass(classLoader, protectionDomain) + return cc + } + + private fun createLauncherDiscoveryRequestAdapterClass( + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ): CtClass { + val cc: CtClass = pool.makeClass(LauncherDiscoveryRequestAdapter) + cc.interfaces = arrayOf(pool.get(LauncherDiscoveryRequest)) + cc.addField(CtField.make("$LauncherDiscoveryRequest delegate = null;", cc)) + cc.addField(CtField.make("$PostDiscoveryFilter additionalFilter = null;", cc)) + cc.addConstructor( + CtNewConstructor.make( + """ + public LauncherDiscoveryRequestAdapter($LauncherDiscoveryRequest delegate, $PostDiscoveryFilter additionalFilter) { + this.delegate = delegate; + this.additionalFilter = additionalFilter; + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public java.util.List getEngineFilters() { + return delegate.getEngineFilters(); + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public java.util.List getPostDiscoveryFilters() { + java.util.ArrayList modifiedList = new java.util.ArrayList(delegate.getPostDiscoveryFilters()); + modifiedList.add(additionalFilter); + return modifiedList; + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public java.util.List getSelectorsByType(java.lang.Class selectorType) { + return delegate.getSelectorsByType(selectorType); + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public java.util.List getFiltersByType(java.lang.Class filterType) { + return delegate.getFiltersByType(filterType); + } + """.trimIndent(), + cc + ) + ) + cc.addMethod( + CtMethod.make( + """ + public $ConfigurationParameters getConfigurationParameters() { + return delegate.getConfigurationParameters(); + } + """.trimIndent(), + cc + ) + ) + cc.toClass(classLoader, protectionDomain) + return cc + } + + @Suppress("MemberVisibilityCanBePrivate") + fun convertToMethodInfo(testMetadata: Map, + testTags: List, + displayName: String?): TestMethodInfo? { + val testPath = testMetadata["class"] ?: testMetadata["feature"] ?: testMetadata["suite"] + val testName = testMetadata["method"]?.substringBefore("(") ?: displayName + if (testPath == null || testName == null) { + logger.error { "Failed to convert test metadata to TestDetails: $testMetadata" } + return null + } + return TestMethodInfo( + engine = testMetadata["engine"] ?: "junit", + className = testPath, + method = testName, + tags = testTags, + metadata = testMetadata + ) + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/ChromeDevTool.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/ChromeDevTool.kt new file mode 100644 index 00000000..953c6528 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/ChromeDevTool.kt @@ -0,0 +1,354 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.selenium + +import com.epam.drill.agent.test.* +import com.epam.drill.agent.test.serialization.* +import com.epam.drill.agent.test.session.* +import com.epam.drill.agent.common.transport.AgentMessage +import com.epam.drill.agent.common.transport.ResponseStatus +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.instrument.TestSessionHeadersProcessor +import com.epam.drill.agent.test.devtools.DevToolsMessageSender +import com.epam.drill.agent.test.serialization.json +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.time.DurationUnit +import kotlin.time.measureTimedValue +import java.net.* +import java.util.* +import mu.KotlinLogging +import kotlin.reflect.KClass + +private const val DEBUGGER_ADDRESS = "debuggerAddress" +private const val DEV_TOOL_DEBUGGER_URL = "webSocketDebuggerUrl" +private val JAVA_TOGGLES = listOf("Network") +private val JS_TOGGLES = listOf("Debugger", "Profiler") + .takeIf { Configuration.parameters[ParameterDefinitions.WITH_JS_COVERAGE] } + ?: emptyList() +private val REPLACE_LOCALHOST = Configuration.parameters[ParameterDefinitions.DEVTOOLS_REPLACE_LOCALHOST] + +/** + * Works with local or Selenoid DevTools by websocket + */ +class ChromeDevTool( + private val capabilities: Map<*, *>?, + private val remoteHost: String? +) { + private val logger = KotlinLogging.logger {} + private val launchType = Configuration.parameters[ParameterDefinitions.LAUNCH_TYPE] + private var isClosed = false + + private lateinit var targetUrl: String + private lateinit var targetId: String + private var sessionId: SessionId = SessionId() + private var headersAdded: Boolean = false + + /** + * connect to remote Selenoid or local webDriver + */ + fun connect(browserSessionId: String?, currentUrl: String) = runCatching { + logger.debug { "starting connectToDevTools with cap='$capabilities' sessionId='$sessionId' remote='$remoteHost'..." } + retrieveDevToolAddress(capabilities ?: emptyMap(), browserSessionId, remoteHost).let { + trackTime("connect to devtools") { + connect(it, currentUrl) + } + } + /** + * Add this to thread local only if successfully connected + */ + }.onFailure { logger.warn(it) { "UI coverage will be lost. Reason: " } }.getOrNull() + + fun addHeaders(headers: Map<*, *>) { + @Suppress("UNCHECKED_CAST") + val casted = headers as Map + try { + logger.trace { "try to add headers: $headers" } + val success = setHeaders(casted) + logger.debug { "Chrome Tool activated: ${sessionId.sessionId.isNotBlank()}. Headers: $headers" } + if (!success) throw RuntimeException("Can't add headers: $headers") + } catch (ex: Exception) { + logger.debug { "exception $ex; try to resend" } + Thread.sleep(2000) + setHeaders(casted) + } + } + + fun switchSession(url: String) { + val targetId = retrieveTargetId(url) + logger.trace { "Reconnect to target: $targetId, sessionId: ${sessionId.sessionId}, url $url" } + targetId?.takeIf { it != this.targetId }?.let { attachToTarget(it) }?.let { + this.targetId = targetId + sessionId = it + enableToggles() + startCollectJsCoverage() + } + } + + fun isHeadersAdded(): Boolean = this.headersAdded + + private fun startCollectJsCoverage() = + Configuration.parameters[ParameterDefinitions.WITH_JS_COVERAGE].takeIf(true::equals)?.let { + disableCache() && startPreciseCoverage() && enableScriptParsed() + }?.also { success -> + if (!success) logger.warn { "JS coverage may be lost" } + } + + private fun startPreciseCoverage() = mapOf("detailed" to true, "callCount" to false).let { params -> + executeCommand( + "Profiler.startPreciseCoverage", + DevToolsRequest(targetUrl, sessionId.sessionId, params.toOutput()) + ).success + } + + private fun disableCache() = executeCommand( + "Network.setCacheDisabled", + DevToolsRequest(targetUrl, sessionId.sessionId, mapOf("cacheDisabled" to true).toOutput()) + ).success + + private fun enableScriptParsed() = DevToolsMessageSender.send( + "POST", + "/event/Debugger.scriptParsed", + DevToolsRequest(targetUrl, sessionId.sessionId) + ).success + + fun takePreciseCoverage(): String = executeCommand( + "Profiler.takePreciseCoverage", + DevToolsRequest(targetUrl, sessionId.sessionId) + ).takeIf(ResponseStatus::success)?.content ?: "" + + fun scriptParsed(): String = DevToolsMessageSender.send( + "POST", + "/event/Debugger.scriptParsed/get-data", + DevToolsRequest(targetUrl, sessionId.sessionId) + ).takeIf(ResponseStatus::success)?.content ?: "" + + fun close() { + if (!isClosed) { + disableToggles() + stopCollectJsCoverage() + DevToolsMessageSender.send( + "DELETE", + "/connection", + DevToolsRequest(targetUrl) + ) + } + isClosed = true + } + + private fun stopCollectJsCoverage() = + Configuration.parameters[ParameterDefinitions.WITH_JS_COVERAGE].takeIf(true::equals)?.let { + DevToolsMessageSender.send( + "DELETE", + "/event/Debugger.scriptParsed", + DevToolsRequest(targetUrl, sessionId.sessionId) + ) + } + + // todo is it necessary to disable toggles when browser exit? + private fun disableToggles() = (JAVA_TOGGLES + JS_TOGGLES).map { + executeCommand( + "$it.disable", + DevToolsRequest(targetUrl, sessionId.sessionId) + ).success + }.all { it } + + private fun enableToggles() = (JAVA_TOGGLES + JS_TOGGLES).map { + executeCommand( + "$it.enable", + DevToolsRequest(targetUrl, sessionId.sessionId) + ).success + }.all { it }.also { + if (!it) logger.warn { "Toggles wasn't enable" } else logger.info { "Toggles enabled" } + } + + private fun retrieveDevToolAddress( + capabilities: Map<*, *>, + sessionId: String?, + remoteHost: String? + ): String = when (launchType) { + + // selenoid provides no access to /json/version, but allows to connect to debugger directly + // see https://aerokube.com/selenoid/latest/#_accessing_browser_developer_tools + "selenoid" -> { + if (sessionId.isNullOrBlank()) + throw RuntimeException("Can't connect to debugger directly, because 'sessionId' is null") + if (remoteHost.isNullOrBlank()) + throw RuntimeException("Can't connect to debugger directly, because 'remoteHost' is null") + "ws://$remoteHost/devtools/$sessionId" + } + + else -> capabilities.run { + val debuggerURL = get(DEBUGGER_ADDRESS)?.toString() + + if (debuggerURL.isNullOrBlank()) { + error("Can't get debugger address by field name $DEBUGGER_ADDRESS from capabilities: $capabilities}") + } + + return DevToolsMessageSender.send("http://$debuggerURL", "GET", "/json/version", "") + .onError { error -> + error("Can't get debugger address from http://$debuggerURL/json/version: $error") + } + .onSuccess { content -> + logger.trace { "/json/version: $content" } + + }.content?.let { content -> + val chromeInfo = Json.parseToJsonElement(content) as JsonObject + chromeInfo[DEV_TOOL_DEBUGGER_URL]?.jsonPrimitive?.contentOrNull + } + ?: error("Can't get debugger address from '$DEV_TOOL_DEBUGGER_URL' field") + } + } + + + private fun connect(devToolAddress: String, currentUrl: String) { + if (REPLACE_LOCALHOST.isNotBlank()) { + targetUrl = devToolAddress.replace("localhost", REPLACE_LOCALHOST) + } else { + targetUrl = devToolAddress + } + val success: Boolean = connectToDevTools().takeIf { it }?.also { + val targetId = retrieveTargetId(currentUrl) + logger.info { "Retrieved target for url $currentUrl: $targetId" } + targetId?.let { attachToTarget(it) }?.also { + this.targetId = targetId + sessionId = it + logger.debug { "DevTools session created: $sessionId" } + enableToggles() + startCollectJsCoverage() + } + } ?: false + if (success) { + DevToolStorage.set(this) + } else throw RuntimeException("Can't connect to $targetUrl") + } + + private fun connectToDevTools(): Boolean { + logger.debug { "DevTools URL: $targetUrl" } + val response = DevToolsMessageSender.send( + "POST", + "/connection", + DevToolsRequest(targetUrl) + ) + return response.success + } + + fun startIntercept(): Boolean { + val headers = TestSessionHeadersProcessor.retrieveHeaders() + if (headers.isEmpty()) return false + logger.debug { "Start intercepting. Headers: $headers, sessionId: $sessionId" } + val response = DevToolsMessageSender.send( + "POST", + "/intercept", + DevToolInterceptRequest(targetUrl, params = mapOf("headers" to headers)) + ) + return response.success + } + + + fun stopIntercept(): Boolean { + logger.debug { "Stop intercepting: $targetUrl, sessionId $sessionId" } + val response = DevToolsMessageSender.send( + "DELETE", + "/intercept", + DevToolInterceptRequest(targetUrl) + ) + setHeaders(mapOf()) + return response.success + } + + private fun setHeaders( + params: Map + ): Boolean = executeCommand( + "Network.setExtraHTTPHeaders", + DevToolsHeaderRequest(targetUrl, sessionId.sessionId, mapOf("headers" to params)) + ).success + + @Deprecated(message = "Useless") + private fun autoAttach(): Boolean { + val params = mapOf("autoAttach" to true, "waitForDebuggerOnStart" to false).toOutput() + return executeCommand( + "Target.setAutoAttach", + DevToolsRequest(targetUrl, sessionId.sessionId, params = params) + ).success + } + + private fun attachToTarget(targetId: String): SessionId? { + val params = mapOf("targetId" to targetId, "flatten" to true).toOutput() + val response = executeCommand( + "Target.attachToTarget", + DevToolsRequest(target = targetUrl, params = params), + SessionId::class + ) + return response.takeIf(ResponseStatus::success) + ?.let(ResponseStatus::content) + } + + private fun retrieveTargetId(currentUrl: String): String? = targets() + .find { it.url == currentUrl } + ?.targetId + ?.uppercase(Locale.getDefault()) + ?.takeIf { it.isNotBlank() } + + private fun targets(): List = executeCommand( + "Target.getTargets", + DevToolsRequest(target = targetUrl), + TargetInfos::class + ).takeIf(ResponseStatus::success) + ?.let(ResponseStatus::content) + ?.let(TargetInfos::targetInfos) + ?: emptyList() + + private fun executeCommand( + commandName: String, + request: DevToolsMessage, + httpMethod: String = "POST" + ): ResponseStatus = DevToolsMessageSender.send(httpMethod, "/command/$commandName", request) + + private fun executeCommand( + commandName: String, + request: DevToolsMessage, + clazz: KClass, + httpMethod: String = "POST" + ): ResponseStatus = DevToolsMessageSender.send(httpMethod, "/command/$commandName", request, clazz) + + private fun Map.toOutput(): Map = mapValues { (_, value) -> + val serializer = value::class.serializer().cast() + json.encodeToJsonElement(serializer, value) + } + + @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") + private inline fun KSerializer.cast(): KSerializer = this as KSerializer + + private inline fun trackTime(tag: String = "", debug: Boolean = false, block: () -> T) = + measureTimedValue { block() }.apply { + val logger = KotlinLogging.logger {} + val message = "[$tag] took: $duration" + when { + duration.toDouble(DurationUnit.SECONDS) > 1 -> { + logger.warn { message } + } + + duration.toDouble(DurationUnit.SECONDS) > 30 -> { + logger.error { message } + } + + else -> if (debug) logger.debug { message } else logger.trace { message } + } + }.value + +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/DevToolStorage.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/DevToolStorage.kt new file mode 100644 index 00000000..4c5e7a54 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/DevToolStorage.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.selenium + +import mu.KotlinLogging + +object DevToolStorage { + private val logger = KotlinLogging.logger {} + private val storage: InheritableThreadLocal = InheritableThreadLocal() + + fun set(devtool: ChromeDevTool) { + storage.set(devtool) + logger.debug { "DevTool inited for: Thread id=${Thread.currentThread().id}, DevToolWS address=$devtool" } + } + + fun get(): ChromeDevTool? = storage.get() + + fun clear() = storage.remove() +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/Model.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/Model.kt new file mode 100644 index 00000000..21560fbc --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/Model.kt @@ -0,0 +1,61 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.selenium + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import com.epam.drill.agent.common.transport.AgentMessage + +@Serializable +data class TargetInfos(val targetInfos: List): AgentMessage() + +@Serializable +data class SessionId(val sessionId: String = ""): AgentMessage() + +@Serializable +data class Target( + val targetId: String, + val type: String, + val title: String, + val url: String, + val attached: Boolean, + val browserContextId: String, +) + +@Serializable +sealed class DevToolsMessage : AgentMessage() { + abstract val target: String +} + +@Serializable +data class DevToolsRequest( + override val target: String, + val sessionId: String = "", + val params: Map = emptyMap() +) : DevToolsMessage() + +@Serializable +data class DevToolInterceptRequest( + override val target: String, + val params: Map> = emptyMap() +) : DevToolsMessage() + +@Serializable +data class DevToolsHeaderRequest( + override val target: String, + val sessionId: String, + val params: Map> = emptyMap(), +) : DevToolsMessage() diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt new file mode 100644 index 00000000..4452abda --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt @@ -0,0 +1,280 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.selenium + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.* +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_SELENIUM_ENABLED +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject +import javassist.* +import mu.KLogger +import java.io.* +import mu.KotlinLogging +import java.security.ProtectionDomain + +@Suppress("PrivatePropertyName") +actual object SeleniumTransformer : Transformer, AbstractTestTransformerObject() { + + private const val Command = "org.openqa.selenium.remote.Command" + private const val ImmutableMap = "com.google.common.collect.ImmutableMap" + private const val ImmutableList = "com.google.common.collect.ImmutableList" + private const val Cookie = "org.openqa.selenium.Cookie" + private const val DesiredCapabilities = "org.openqa.selenium.remote.DesiredCapabilities" + private const val Proxy = "org.openqa.selenium.Proxy" + private const val initPages = "\"about:blank\", \"data:,\"" + private const val EXTENSION_NAME = "header-transmitter.xpi" + private val FirefoxDriver = "org.openqa.selenium.firefox.FirefoxDriver" + + private val extensionFile by lazy { + val extension = this::class.java.getResource("/$EXTENSION_NAME") + if (extension != null) { + File(System.getProperty("java.io.tmpdir")).resolve(EXTENSION_NAME).apply { + writeBytes(extension.readBytes()) + }.absolutePath + } else { + logger.warn { "Failed to load extension file: $EXTENSION_NAME" } + null + } + } + + internal const val addDrillCookiesMethod = "addDrillCookies" + private const val isFirefoxBrowser = "isFirefoxBrowser" + override val logger: KLogger = KotlinLogging.logger {} + + override fun enabled() = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_SELENIUM_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == "org/openqa/selenium/remote/RemoteWebDriver" + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + logger.debug { "starting instrument ${ctClass.name}..." } + + var remoteWebDriverConstructorInstrumented = false; + + fun tryCatch(fn: () -> Any) { + try { + fn(); + remoteWebDriverConstructorInstrumented = true; + } catch (e: Exception) { + logger.debug { "failed to instrument: $e" } + } + } + + ctClass.addField(CtField.make("java.lang.String drillRemoteAddress;", ctClass)) + + tryCatch { + ctClass + .getConstructor("(Lorg/openqa/selenium/remote/CommandExecutor;Lorg/openqa/selenium/Capabilities;)V") + .insertBefore( + """ + try { + if ($1 instanceof org.openqa.selenium.remote.HttpCommandExecutor) { + java.lang.System.out.println("Constructor called - (Lorg/openqa/selenium/remote/CommandExecutor;Lorg/openqa/selenium/Capabilities;)V"); + org.openqa.selenium.remote.HttpCommandExecutor drillHttpCommandExecutor = (org.openqa.selenium.remote.HttpCommandExecutor) $1; + drillRemoteAddress = drillHttpCommandExecutor.getAddressOfRemoteServer().getAuthority(); + } + } catch (Exception e) { + java.lang.System.out.println( + "Drill4J: failed to get remote address - Constructor: RemoteWebDriver(CommandExecutor executor, Capabilities desiredCapabilities) - Error: " + e.toString() + ); + } + """.trimIndent() + ) + } + + tryCatch { + ctClass + .getConstructor("(Ljava/net/URL;Lorg/openqa/selenium/Capabilities;)V") + .insertBefore( + """ + try { + java.lang.System.out.println("Constructor called - (Ljava/net/URL;Lorg/openqa/selenium/Capabilities;)V"); + + drillRemoteAddress = $1.getAuthority(); + } catch (Exception e) { + java.lang.System.out.println( + "Drill4J: failed to get remote address - Constructor: RemoteWebDriver(URL remoteAddress, Capabilities desiredCapabilities) - Error: " + e.toString() + ); + } + """.trimIndent() + ) + } + + tryCatch { + ctClass + .getConstructor("(Ljava/net/URL;Lorg/openqa/selenium/Capabilities;Lorg/openqa/selenium/Capabilities;)V") + .insertBefore( + """ + try { + java.lang.System.out.println("Constructor called - (Ljava/net/URL;Lorg/openqa/selenium/Capabilities;Lorg/openqa/selenium/Capabilities;)V"); + drillRemoteAddress = $1.getAuthority(); + } catch (Exception e) { + java.lang.System.out.println( + "Drill4J: failed to get remote address - Constructor: RemoteWebDriver(URL remoteAddress, Capabilities desiredCapabilities, Capabilities requiredCapabilities) - Error: " + e.toString() + ); + } + """.trimIndent() + ) + } + + tryCatch { + ctClass + .getConstructor("(Lorg/openqa/selenium/Capabilities;)V") + .insertBefore( + """ + java.lang.System.out.println("Constructor called - (Lorg/openqa/selenium/Capabilities;)V"); + """.trimIndent() + ) + } + + if (!remoteWebDriverConstructorInstrumented) { + logger.warn { "No RemoteWebDriver constructors were instrumented. Possibly unsupported Selenium version" } + } else { + logger.debug { "RemoteWebDriver constructors instrumented" } + } + + ctClass.addMethod( + CtMethod.make( + """ + public boolean $isFirefoxBrowser(org.openqa.selenium.Capabilities capabilities){ + return capabilities.getBrowserName().equalsIgnoreCase("firefox"); + } + """.trimIndent(), + ctClass + ) + ) + + val startSession = ctClass.getDeclaredMethod("startSession") + + /** + * Browser proxy is needed only for Firefox browser + */ + startSession.insertBefore( + """ + if (${this::class.java.name}.INSTANCE.${this::proxyUrl.name}() != null && $isFirefoxBrowser($1)) { + $DesiredCapabilities dCap = new $DesiredCapabilities(); + $Proxy dProxy = new $Proxy(); + dProxy.setHttpProxy(${this::class.java.name}.INSTANCE.${this::proxyUrl.name}()); + dProxy.setSslProxy(${this::class.java.name}.INSTANCE.${this::proxyUrl.name}()); + dCap.setCapability("proxy", dProxy); + $1 = $1.merge(dCap); + } + ${WebDriverThreadStorage::class.java.name}.INSTANCE.${WebDriverThreadStorage::set.name}(this); + """ + ) + startSession.insertAfter( + """ + if (${this::class.java.name}.INSTANCE.${this::devToolsProxyAddress.name}() != null){ + ${ChromeDevTool::class.java.name} drillDevTools = new ${ChromeDevTool::class.java.name}( + ((java.util.Map)this.capabilities.getCapability("goog:chromeOptions")), + drillRemoteAddress + ); + drillDevTools.${ChromeDevTool::connect.name}(sessionId.toString(), getCurrentUrl()); + } + try { + if (this instanceof $FirefoxDriver) { + java.util.HashMap hashMapq = new java.util.HashMap(); + hashMapq.put("path", "${extensionFile?.replace("\\", "\\\\")}"); + hashMapq.put("temporary", Boolean.TRUE); + this.execute("installExtension", hashMapq).getValue(); + } + } catch (Exception e){} + """ + ) + ctClass.addMethod( + CtMethod.make( + """ + public void $addDrillCookiesMethod() { + if ($isFirefoxBrowser(getCapabilities()) && $ARE_DRILL_HEADERS_PRESENT) { + try { + executor.execute(new $Command(sessionId, "addCookie", $ImmutableMap.of("cookie", new $Cookie($SESSION_ID_CALC_LINE)))); + executor.execute(new $Command(sessionId, "addCookie", $ImmutableMap.of("cookie", new $Cookie($TEST_NAME_CALC_LINE)))); + } catch(Exception e) { e.printStackTrace();} + } + } + """.trimIndent(), + ctClass + ) + ) + ctClass.addMethod( + CtMethod.make( + """ + public void addDrillHeaders() { + if ($IS_DEV_TOOL_NOT_NULL && $ARE_DRILL_HEADERS_PRESENT && !$IS_HEADER_ADDED) { + try { + java.util.HashMap hashMap = new java.util.HashMap(); + hashMap.put($SESSION_ID_CALC_LINE); + hashMap.put($TEST_NAME_CALC_LINE); + ${getChromeDevTool()}.${ChromeDevTool::addHeaders.name}(hashMap); + } catch(Exception e) { e.printStackTrace();} + } + } + """.trimIndent(), + ctClass + ) + ) + ctClass.getDeclaredMethod("get").insertBefore( + """ + boolean isInitPage = $ImmutableList.of($initPages).contains(getCurrentUrl()); + if (isInitPage) { execute("get", $ImmutableMap.of("url", $1)); } + addDrillHeaders(); + $addDrillCookiesMethod(); + """.trimIndent() + ) + /** + * todo enable js instrumentation on tab open @Roman_Davliatshin + */ + ctClass.getMethod( + "execute", + "(Ljava/lang/String;Ljava/util/Map;)Lorg/openqa/selenium/remote/Response;" + ).insertAfter( + """ + if ($1.equals(org.openqa.selenium.remote.DriverCommand.SWITCH_TO_WINDOW)){ + java.lang.String currentUrl = getCurrentUrl(); + if ($IS_DEV_TOOL_NOT_NULL){ + ${getChromeDevTool()}.${ChromeDevTool::switchSession.name}(currentUrl); + } else { + execute("get", $ImmutableMap.of("url",currentUrl)); + $addDrillCookiesMethod(); + } + } + """.trimIndent() + ) + ctClass.getDeclaredMethod("quit").insertBefore( + """ + if ($IS_DEV_TOOL_NOT_NULL){ + ${getChromeDevTool()}.${ChromeDevTool::close.name}(); + ${DevToolStorage::class.java.name}.INSTANCE.${DevToolStorage::clear.name}(); + } + ${WebDriverThreadStorage::class.java.name}.INSTANCE.${WebDriverThreadStorage::clear.name}(); + """.trimIndent() + ) + } + + fun proxyUrl() = Configuration.parameters[ParameterDefinitions.PROXY_ADDRESS] + + fun devToolsProxyAddress() = Configuration.parameters[ParameterDefinitions.DEVTOOLS_PROXY_ADDRESS] + + private fun getChromeDevTool() = "${DevToolStorage::class.java.name}.INSTANCE.${DevToolStorage::get.name}()" +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/WebDriverThreadStorage.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/WebDriverThreadStorage.kt new file mode 100644 index 00000000..a2d5319b --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/selenium/WebDriverThreadStorage.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.selenium + +object WebDriverThreadStorage { + private val webDriver = InheritableThreadLocal() + + fun set(obj: Any) = webDriver.set(obj) + + fun addCookies() { + runCatching { + webDriver.get()?.let { + it.javaClass.getMethod(SeleniumTransformer.addDrillCookiesMethod).invoke(it) + } + }.getOrNull() + } + + fun clear() = webDriver.remove() +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNG67Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNG67Transformer.kt new file mode 100644 index 00000000..b0eddae6 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNG67Transformer.kt @@ -0,0 +1,202 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.test.execution.TestController +import javassist.* +import java.lang.reflect.* +import java.security.* +import mu.KotlinLogging + +abstract class AbstractTestNG67Transformer() : AbstractTestNGTransformer() { + companion object { + const val engineSegment = "testng" + const val TestNGMethod = "org.testng.internal.TestNGMethod" + const val ITestResult = "org.testng.ITestResult" + + private const val DrillTestNGTestListner = "DrillTestNGTestListener" + private const val ITestContext = "org.testng.ITestContext" + } + + override val logger = KotlinLogging.logger {} + + abstract val versionRegex: Regex + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return className == "org/testng/TestRunner" + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + createTestListener(pool, classLoader, protectionDomain) + ctClass.constructors.forEach { it.insertAfter("addTestListener(new $DrillTestNGTestListner());") } + ctClass.supportIgnoredTestsTracking() + } + + private fun createTestListener( + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + val testListener = pool.makeClass(DrillTestNGTestListner) + testListener.interfaces = arrayOf(pool.get("org.testng.ITestListener")) + testListener.addMethod( + CtMethod.make(getParamsMethod(), testListener) + ) + testListener.addMethod( + CtMethod.make(getTestClassNameMethod(), testListener) + ) + testListener.addMethod( + CtMethod.make(getTestNameMethod(), testListener) + ) + testListener.addMethod( + CtMethod.make(getTestGroups(), testListener) + ) + testListener.addMethod( + CtMethod.make( + """ + public void onTestStart($ITestResult result) { + if (result.getThrowable() == null) { + ${TestController::class.java.name}.INSTANCE.${TestController::testStarted.name}("$engineSegment", getTestClassName(result), getTestName(result), getParamsString(result), getTestGroups(result)); + } else { + ${this::class.java.name}.INSTANCE.${this::debug.name}("The start of the test " + result.getName() + " is ignored by the drill"); + } + } + """.trimIndent(), + testListener + ) + ) + testListener.addMethod( + CtMethod.make( + """ + public void onTestSuccess($ITestResult result) { + ${TestController::class.java.name}.INSTANCE.${TestController::testFinished.name}("$engineSegment", getTestClassName(result), getTestName(result), "PASSED", getParamsString(result), getTestGroups(result)); + } + """.trimIndent(), + testListener + ) + ) + testListener.addMethod( + CtMethod.make( + """ + public void onTestFailure($ITestResult result) { + ${TestController::class.java.name}.INSTANCE.${TestController::testFinished.name}("$engineSegment", getTestClassName(result), getTestName(result), "FAILED", getParamsString(result), getTestGroups(result)); + } + """.trimIndent(), + testListener + ) + ) + testListener.addMethod( + CtMethod.make( + """ + public void onTestSkipped($ITestResult result) { + ${TestController::class.java.name}.INSTANCE.${TestController::testIgnored.name}("$engineSegment", getTestClassName(result), getTestName(result), getParamsString(result), getTestGroups(result)); + } + """.trimIndent(), + testListener + ) + ) + testListener.addMethod( + CtMethod.make( + """ + public void onTestFailedButWithinSuccessPercentage($ITestResult result) { + return; + } + """.trimIndent(), + testListener + ) + ) + testListener.addMethod( + CtMethod.make( + """ + public void onStart($ITestContext result) { + return; + } + """.trimIndent(), + testListener + ) + ) + testListener.addMethod( + CtMethod.make( + """ + public void onFinish($ITestContext result) { + return; + } + """.trimIndent(), + testListener + ) + ) + testListener.toClass(classLoader, protectionDomain) + } + + protected open fun getParamsMethod(): String = """ + private String getParamsString($ITestResult result) { + Object[] parameters = result.getParameters(); + String paramString = ${this::class.java.name}.INSTANCE.${this::paramTypes.name}(parameters); + return paramString; + } + """.trimIndent() + + protected open fun getTestClassNameMethod(): String = """ + private String getTestClassName($ITestResult result) { + return result.getInstanceName(); + } + """.trimIndent() + + protected open fun getTestNameMethod(): String = """ + private String getTestName($ITestResult result) { + return result.getName(); + } + """.trimIndent() + + protected open fun getTestGroups(): String = """ + private java.util.List getTestGroups($ITestResult result) { + String[] groups = result.getMethod().getGroups(); + return java.util.Arrays.asList(groups); + } + """.trimIndent() + + + private fun CtClass.supportIgnoredTestsTracking() = getDeclaredMethod("run").insertBefore( + """ + java.util.Iterator disabledTests = getExcludedMethods().iterator(); + while(disabledTests.hasNext()) { + java.lang.Object baseMethod = disabledTests.next(); + if (baseMethod instanceof $TestNGMethod) { + $TestNGMethod test = ($TestNGMethod) baseMethod; + ${TestController::class.java.name}.INSTANCE.${TestController::testIgnored.name}("$engineSegment", test.getTestClass().getName(), test.getMethodName()); + } + } + """.trimIndent() + ) + + fun paramTypes(objects: Array?): String = objects?.joinToString(",", "(", ")") { obj -> + when (obj) { + null -> obj.toString() + is Field -> obj.type.simpleName + else -> obj.javaClass.simpleName.substringBeforeLast("\$") + } + } ?: "" + + fun debug(message: String) { + logger.debug { message } + } + +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGPrioritizingTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGPrioritizingTransformer.kt new file mode 100644 index 00000000..85edecf0 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGPrioritizingTransformer.kt @@ -0,0 +1,80 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.prioritization.RecommendedTests +import javassist.ClassPool +import javassist.CtClass +import java.security.ProtectionDomain + +abstract class AbstractTestNGPrioritizingTransformer() : AbstractTestNGTransformer() { + private val engineSegment = "testng" + abstract val versionRegex: Regex + abstract fun getMethodParametersExpression(): String + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + return interfaces.any { it == "org/testng/IMethodSelector" } + } + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + if ("${ctClass.url}".contains(versionRegex)) { + instrumentIfSupport(ctClass, pool, classLoader, protectionDomain) + } + } + + private fun instrumentIfSupport( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ): ByteArray? { + instrumentIncludeMethod(ctClass) + return ctClass.toBytecode() + } + + private fun instrumentIncludeMethod(ctClass: CtClass) { + ctClass.getMethod( + "includeMethod", + "(Lorg/testng/IMethodSelectorContext;Lorg/testng/ITestNGMethod;Z)Z" + ).insertAfter( + """ + if (${'$'}_ == true && $3 == true) { + java.lang.String className = $2.getTestClass().getName(); + java.lang.String methodName = $2.getMethodName(); + java.lang.String methodParameters = ${this::class.java.name}.INSTANCE.${this::paramTypes.name}($2.${getMethodParametersExpression()}); + boolean shouldSkip = ${RecommendedTests::class.java.name}.INSTANCE.${RecommendedTests::shouldSkip.name}("$engineSegment", className, methodName, methodParameters); + if (shouldSkip) { + return false; + } + } + """.trimIndent() + ) + } + + @Suppress("MemberVisibilityCanBePrivate") + fun paramTypes(objects: Array?>?): String = objects?.joinToString(",", "(", ")") { obj -> + obj?.name ?: "" + } ?: "" + +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGTransformer.kt new file mode 100644 index 00000000..5e8ba129 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGTransformer.kt @@ -0,0 +1,23 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_TESTNG_ENABLED +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +abstract class AbstractTestNGTransformer(): AbstractTestTransformerObject() { + override fun enabled() = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_TESTNG_ENABLED] +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt new file mode 100644 index 00000000..c4c9caac --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer +import mu.KotlinLogging + +actual object TestNG6PrioritizingTransformer: Transformer, AbstractTestNGPrioritizingTransformer() { + override val logger = KotlinLogging.logger {} + override val versionRegex: Regex = "testng-6\\.[0-9]+(\\.[0-9]+)*".toRegex() + + override fun getMethodParametersExpression(): String { + return "getConstructorOrMethod().getParameterTypes()" + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt new file mode 100644 index 00000000..33e4b743 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt @@ -0,0 +1,42 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer +import javassist.* +import java.security.* + +actual object TestNG6Transformer : Transformer, AbstractTestNG67Transformer() { + override val versionRegex: Regex = "testng-6\\.[0-9]+(\\.[0-9]+)*".toRegex() + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + if ("${ctClass.url}".contains(versionRegex)) { + super.instrument(ctClass, pool, classLoader, protectionDomain) + } + } + + + override fun getTestClassNameMethod(): String = """ + private String getTestClassName($ITestResult result) { + return result.getTestClass().getName(); + } + """.trimIndent() +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt new file mode 100644 index 00000000..fa2e3e59 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer +import mu.KotlinLogging + +actual object TestNG7PrioritizingTransformer: Transformer, AbstractTestNGPrioritizingTransformer() { + override val logger = KotlinLogging.logger {} + override val versionRegex: Regex = "testng-7\\.[0-9]+(\\.[0-9]+)*".toRegex() + + override fun getMethodParametersExpression(): String { + return "getParameterTypes()" + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt new file mode 100644 index 00000000..9b99b9f3 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt @@ -0,0 +1,36 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer +import javassist.* +import java.security.* + +actual object TestNG7Transformer : Transformer, AbstractTestNG67Transformer() { + + override val versionRegex: Regex = "testng-7\\.[0-9]+(\\.[0-9]+)*".toRegex() + + override fun instrument( + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain?, + ) { + if ("${ctClass.url}".contains(versionRegex)) { + super.instrument(ctClass, pool, classLoader, protectionDomain) + } + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/prioritization/RecommendedTests.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/prioritization/RecommendedTests.kt new file mode 100644 index 00000000..b2b75bcb --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/prioritization/RecommendedTests.kt @@ -0,0 +1,59 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.prioritization + +import com.epam.drill.agent.test.execution.TestMethodInfo +import mu.KotlinLogging + +object RecommendedTests { + private val logger = KotlinLogging.logger {} + private val recommendedTestsReceiver: RecommendedTestsReceiver = RecommendedTestsReceiverImpl() + private val testsToSkip: Set by lazy { initTestsToSkip() } + + private fun initTestsToSkip() = recommendedTestsReceiver.getTestsToSkip() + .toSet() + .also { + logger.info { "${it.size} tests will be skipped by Drill4J" } + } + + + fun shouldSkip( + engine: String, + testClass: String, + testMethod: String, + methodParameters: String = "()" + ): Boolean { + val test = TestMethodInfo( + engine = engine, + className = testClass, + method = testMethod, + methodParams = methodParameters, + ) + return shouldSkipByTestMethod(test) + } + + fun shouldSkipByTestMethod(test: TestMethodInfo): Boolean { + return testsToSkip.contains(test).also { + if (it) { + logger.debug { "Test `${test.method}` will be skipped by Drill4J" } + recommendedTestsReceiver.sendSkippedTest(test) + } else { + logger.debug { "Test `${test.method}` will not be skipped by Drill4J" } + } + } + } + +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/prioritization/RecommendedTestsReceiver.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/prioritization/RecommendedTestsReceiver.kt new file mode 100644 index 00000000..3d6e5acf --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/prioritization/RecommendedTestsReceiver.kt @@ -0,0 +1,115 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.prioritization + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.AgentMessageReceiver +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.execution.TestController +import com.epam.drill.agent.test.execution.TestExecutionRecorder +import com.epam.drill.agent.test.execution.TestMethodInfo +import com.epam.drill.agent.test.sending.* +import com.epam.drill.agent.test.transport.TestAgentMessageReceiver +import kotlinx.serialization.Serializable +import mu.KotlinLogging + +interface RecommendedTestsReceiver { + fun getTestsToSkip(): List + fun sendSkippedTest(test: TestMethodInfo) +} + +class RecommendedTestsReceiverImpl( + private val agentMessageReceiver: AgentMessageReceiver = TestAgentMessageReceiver, + private val testExecutionRecorder: TestExecutionRecorder = TestController +) : RecommendedTestsReceiver { + private val logger = KotlinLogging.logger {} + + override fun getTestsToSkip(): List { + if (!Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_ENABLED]) + return emptyList() + val groupId = Configuration.parameters[DefaultParameterDefinitions.GROUP_ID] + val testTaskId = Configuration.parameters[ParameterDefinitions.TEST_TASK_ID] + val targetAppId = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_TARGET_APP_ID] + val targetBuildVersion = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_TARGET_BUILD_VERSION] + .takeIf { it.isNotEmpty() } + val targetCommitSha = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_TARGET_COMMIT_SHA] + .takeIf { it.isNotEmpty() } + val baselineCommitSha = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_BASELINE_COMMIT_SHA] + .takeIf { it.isNotEmpty() } + val baselineBuildVersion = + Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_BASELINE_BUILD_VERSION] + .takeIf { it.isNotEmpty() } + + val parameters: String = buildString { + append("?groupId=$groupId") + append("&appId=$targetAppId") + append("&testTaskId=$testTaskId") + append("&testsToSkip=true") + targetBuildVersion?.let { append("&targetBuildVersion=$it") } + targetCommitSha?.let { append("&targetCommitSha=$it") } + baselineCommitSha?.let { append("&baselineCommitSha=$it") } + baselineBuildVersion?.let { append("&baselineBuildVersion=$it") } + } + logger.debug { "Retrieving information about recommended tests, testTaskId: $testTaskId" } + return runCatching { + agentMessageReceiver.receive( + AgentMessageDestination( + "GET", + "/recommended-tests$parameters", + ), + RecommendedTestsApiResponse::class + ).data.recommendedTests.map { it.toTestMethodInfo() } + }.onFailure { + logger.warn { "Unable to retrieve information about recommended tests. Error message: $it" } + }.getOrElse { + emptyList() + } + } + + override fun sendSkippedTest(test: TestMethodInfo) { + testExecutionRecorder.recordTestIgnoring(test, isSmartSkip = true) + } +} + +@Serializable +class RecommendedTestsApiResponse( + val data: RecommendedTestsResponse +) + +@Serializable +class RecommendedTestsResponse( + val recommendedTests: List +) + +@Serializable +class TestDefinitionResponse( + val testDefinitionId: String, + val testRunner: String, + val testPath: String, + val testName: String, + val tags: List, + val metadata: Map, +) + +private fun TestDefinitionResponse.toTestMethodInfo() = TestMethodInfo( + engine = testRunner, + className = testPath, + method = testName, + metadata = metadata, + tags = tags, +) \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestsPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestsPayload.kt new file mode 100644 index 00000000..a67a6a89 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestsPayload.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.sending + +import com.epam.drill.agent.common.transport.AgentMessage +import kotlinx.serialization.Serializable + +@Serializable +data class AddTestsPayload( + val groupId: String, + val sessionId: String, + val tests: List = emptyList(), +): AgentMessage() \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestDefinitionPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestDefinitionPayload.kt new file mode 100644 index 00000000..00e68fa4 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestDefinitionPayload.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.sending + +import kotlinx.serialization.Serializable + +@Serializable +class TestDefinitionPayload( + val runner: String = "", + val path: String = "", + val testName: String = "", + val testParams: List = emptyList(), + val metadata: Map = emptyMap(), + val tags: List = emptyList(), +) \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestInfoSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestInfoSender.kt new file mode 100644 index 00000000..b3db15bc --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestInfoSender.kt @@ -0,0 +1,75 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.sending + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.AgentMessageSender +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.session.SessionController +import mu.KotlinLogging +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +interface TestInfoSender { + fun startSendingTests() + fun stopSendingTests() +} + +class IntervalTestInfoSender( + private val messageSender: AgentMessageSender, + private val intervalMs: Long = 1000, + private val collectTests: () -> List = { emptyList() } +) : TestInfoSender { + private val logger = KotlinLogging.logger {} + private val scheduledThreadPool = Executors.newSingleThreadScheduledExecutor() + + override fun startSendingTests() { + scheduledThreadPool.scheduleAtFixedRate( + { sendTests(collectTests()) }, + 0, + intervalMs, + TimeUnit.MILLISECONDS + ) + logger.debug { "Test sending job is started." } + } + + override fun stopSendingTests() { + scheduledThreadPool.shutdown() + if (!scheduledThreadPool.awaitTermination(1, TimeUnit.SECONDS)) { + logger.error("Failed to send some tests prior to shutdown") + scheduledThreadPool.shutdownNow(); + } + sendTests(collectTests()) + messageSender.shutdown() + logger.info { "Test sending job is stopped." } + } + + private fun sendTests(tests: List) { + if (tests.isEmpty()) return + logger.debug { "Sending ${tests.size} tests..." } + messageSender.send( + destination = AgentMessageDestination("POST", "tests-metadata"), + message = AddTestsPayload( + groupId = Configuration.parameters[DefaultParameterDefinitions.GROUP_ID], + sessionId = SessionController.getSessionId(), + tests = tests + ), + serializer = AddTestsPayload.serializer() + ) + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestLaunchPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestLaunchPayload.kt new file mode 100644 index 00000000..ee2196ee --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestLaunchPayload.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.sending + +import com.epam.drill.agent.test.execution.TestResult +import kotlinx.serialization.Serializable + +@Serializable +data class TestLaunchPayload( + val testLaunchId: String, + val testDefinitionId: String, + val result: TestResult, + val duration: Int?, + val details: TestDefinitionPayload, +) \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/serialization/Json.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/serialization/Json.kt new file mode 100644 index 00000000..bfdc44f8 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/serialization/Json.kt @@ -0,0 +1,23 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.serialization + +import kotlinx.serialization.json.* + +val json = Json { + encodeDefaults = true + ignoreUnknownKeys = true +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/serialization/PropertyDecoder.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/serialization/PropertyDecoder.kt new file mode 100644 index 00000000..2005423d --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/serialization/PropertyDecoder.kt @@ -0,0 +1,60 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.serialization + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.internal.* + +class PropertyDecoder(val map: Map) : NamedValueDecoder() { + private var currentIndex = 0 + + override fun decodeCollectionSize(descriptor: SerialDescriptor): Int { + return decodeTaggedInt(nested("size")) + } + + override fun decodeTaggedValue(tag: String): Any { + return map.getValue(tag) + } + + override fun decodeTaggedBoolean(tag: String): Boolean { + return map.getValue(tag) as Boolean + } + + override fun decodeTaggedLong(tag: String): Long { + return map.getValue(tag) as Long + } + + override fun decodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor): Int { + return (map.getValue(tag) as Enum<*>).ordinal + } + + @Suppress("UNCHECKED_CAST") + override fun decodeSerializableValue(deserializer: DeserializationStrategy, previousValue: T?): T { + return map[this.currentTagOrNull] as? T ?: super.decodeSerializableValue(deserializer, previousValue) + } + + override fun decodeElementIndex(descriptor: SerialDescriptor): Int { + val tag = nested("size") + val size = if (map.containsKey(tag)) decodeTaggedInt(tag) else descriptor.elementsCount + while (currentIndex < size) { + val name = descriptor.getTag(currentIndex++) + if (map.keys.any { it.startsWith(name) }) return currentIndex - 1 + } + return CompositeDecoder.DECODE_DONE + } +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt new file mode 100644 index 00000000..e5b28767 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt @@ -0,0 +1,118 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.session + +import com.benasher44.uuid.* +import com.epam.drill.agent.common.request.DrillInitialContext +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.configuration.CapabilityParameterDefinitions.TEST_TRACING_ENABLED +import com.epam.drill.agent.request.DrillRequestHolder +import com.epam.drill.agent.test.SESSION_ID_HEADER +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.test.sending.IntervalTestInfoSender +import com.epam.drill.agent.test.sending.TestInfoSender +import com.epam.drill.agent.test.execution.TestController +import com.epam.drill.agent.test.execution.TestExecutionInfo +import com.epam.drill.agent.test.sending.TestDefinitionPayload +import com.epam.drill.agent.test.sending.TestLaunchPayload +import com.epam.drill.agent.test.transport.TestAgentMessageSender +import mu.KotlinLogging +import java.time.Instant +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.util.zip.CRC32 + +actual object SessionController { + private val logger = KotlinLogging.logger {} + private val sessionSender: SessionSender = SessionSenderImpl( + messageSender = TestAgentMessageSender + ) + private val testInfoSender: TestInfoSender = IntervalTestInfoSender( + messageSender = TestAgentMessageSender, + collectTests = { TestController.getFinishedTests().toTestLaunchPayloads() } + ) + private lateinit var sessionId: String + + + actual fun startSession() { + if (!isTestTracingEnabled()) { + logger.info { "Test tracing is disabled." } + return + } + logger.info { "Test tracing is enabled." } + val customSessionId = Configuration.parameters[ParameterDefinitions.TEST_SESSION_ID] + sessionId = customSessionId ?: uuid4().toString() + DrillInitialContext.add(SESSION_ID_HEADER, sessionId) + DrillRequestHolder.store(DrillRequest(sessionId)) + logger.info { "Test session: $sessionId" } + + testInfoSender.startSendingTests() + Runtime.getRuntime().addShutdownHook(Thread { testInfoSender.stopSendingTests() }) + + val builds = + takeIf { Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_TARGET_APP_ID].isNotEmpty() }?.let { + SingleSessionBuildPayload( + appId = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_TARGET_APP_ID], + buildVersion = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_TARGET_BUILD_VERSION], + commitSha = Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_TARGET_COMMIT_SHA] + ) + }?.let { listOf(it) } ?: emptyList() + sessionSender.sendSession( + SessionPayload( + id = sessionId, + groupId = Configuration.parameters[DefaultParameterDefinitions.GROUP_ID], + testTaskId = Configuration.parameters[ParameterDefinitions.TEST_TASK_ID], + startedAt = System.currentTimeMillis().toIsoTimeFormat(), + builds = builds + ) + ) + } + + fun getSessionId(): String = sessionId + + private fun isTestTracingEnabled(): Boolean = Configuration.parameters[TEST_TRACING_ENABLED] + private fun isTestLaunchMetadataSendingEnabled(): Boolean = isTestTracingEnabled() && Configuration.parameters[ParameterDefinitions.TEST_TRACING_PER_TEST_LAUNCH_ENABLED] +} + +private fun List.toTestLaunchPayloads(): List = map { info -> + val testDefinitionPayload = TestDefinitionPayload( + runner = info.testMethod.engine, + path = info.testMethod.className, + testName = info.testMethod.method, + testParams = info.testMethod.methodParams.removeSurrounding("(", ")").split(",").filter { it.isNotEmpty() }, + metadata = info.testMethod.metadata, + tags = info.testMethod.tags + ) + TestLaunchPayload( + testLaunchId = info.testLaunchId, + testDefinitionId = hash(info.testMethod.signature), + result = info.result, + duration = info.finishedAt?.minus(info.startedAt ?: 0)?.toInt(), + details = testDefinitionPayload + ) +} + +private fun hash(signature: String): String = CRC32().let { + it.update(signature.toByteArray()) + java.lang.Long.toHexString(it.value) +} + +private fun Long.toIsoTimeFormat(): String = Instant.ofEpochMilli(this) + .let { ZonedDateTime.ofInstant(it, ZoneId.systemDefault()) } + .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionPayload.kt new file mode 100644 index 00000000..8f142f84 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionPayload.kt @@ -0,0 +1,36 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.session + +import com.epam.drill.agent.common.transport.AgentMessage +import kotlinx.serialization.Serializable + +@Serializable +class SingleSessionBuildPayload( + val appId: String, + val instanceId: String? = null, + val buildVersion: String? = null, + val commitSha: String? = null +) + +@Serializable +class SessionPayload( + val id: String, + val groupId: String, + val testTaskId: String, + val startedAt: String, + val builds: List = emptyList() +): AgentMessage() \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionSender.kt new file mode 100644 index 00000000..f9948371 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionSender.kt @@ -0,0 +1,37 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.session + +import com.epam.drill.agent.common.transport.AgentMessage +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.AgentMessageSender + +interface SessionSender { + fun sendSession(payload: SessionPayload) +} + +class SessionSenderImpl( + private val messageSender: AgentMessageSender +) : SessionSender { + + override fun sendSession(payload: SessionPayload) { + messageSender.send( + AgentMessageDestination("PUT", "sessions"), + payload, + SessionPayload.serializer() + ) + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/transport/TestAgentMessageReceiver.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/transport/TestAgentMessageReceiver.kt new file mode 100644 index 00000000..388d23d0 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/transport/TestAgentMessageReceiver.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.transport + +import com.epam.drill.agent.common.transport.AgentMessageReceiver +import com.epam.drill.agent.transport.JsonAgentMessageDeserializer +import com.epam.drill.agent.transport.SimpleAgentMessageReceiver +import com.epam.drill.agent.transport.HttpAgentMessageDestinationMapper + +object TestAgentMessageReceiver : AgentMessageReceiver by messageReceiver() + +fun messageReceiver(): AgentMessageReceiver { + return SimpleAgentMessageReceiver( + agentTransport(), + JsonAgentMessageDeserializer(), + HttpAgentMessageDestinationMapper("metrics") + ) +} + diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/transport/TestAgentMessageSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/transport/TestAgentMessageSender.kt new file mode 100644 index 00000000..67bfb79d --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/transport/TestAgentMessageSender.kt @@ -0,0 +1,69 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.transport + +import java.io.File +import io.aesy.datasize.ByteUnit +import io.aesy.datasize.DataSize +import mu.KotlinLogging +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import com.epam.drill.agent.transport.* +import com.epam.drill.agent.transport.http.HttpAgentMessageTransport +import com.epam.drill.agent.common.transport.AgentMessageSender +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions + +private val logger = KotlinLogging.logger {} +private const val QUEUE_DEFAULT_SIZE: Long = 512L * 1024 * 1024 + +object TestAgentMessageSender : AgentMessageSender by messageSender() + +fun agentTransport(): AgentMessageTransport = HttpAgentMessageTransport( + Configuration.parameters[ParameterDefinitions.API_URL], + Configuration.parameters[ParameterDefinitions.API_KEY] ?: "", + Configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE] + ?.let { resolvePath(it) } ?: "", + Configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE_PASSWORD] ?: "", + gzipCompression = false +) + +fun messageSender(): AgentMessageSender { + val transport = agentTransport() + val serializer = JsonAgentMessageSerializer() + val mapper = HttpAgentMessageDestinationMapper("data-ingest") + val queue = InMemoryAgentMessageQueue( + capacity = Configuration.parameters[ParameterDefinitions.MESSAGE_QUEUE_LIMIT].let(::parseBytes) + ) + return QueuedAgentMessageSender(transport, serializer, mapper, queue, + maxRetries = Configuration.parameters[ParameterDefinitions.MESSAGE_MAX_RETRIES] + ) +} + +private fun resolvePath(path: String) = File(path).run { + val installationDir = File(Configuration.parameters[DefaultParameterDefinitions.INSTALLATION_DIR] ?: "") + val resolved = this.takeIf(File::exists) + ?: this.takeUnless(File::isAbsolute)?.let(installationDir::resolve) + logger.trace { "resolvePath: Resolved $path to ${resolved?.absolutePath}" } + resolved?.absolutePath ?: path +} + +private fun parseBytes(value: String): Long = value.run { + val logError: (Throwable) -> Unit = { logger.warn(it) { "parseBytes: Exception while parsing value: $this" } } + this.runCatching(DataSize::parse) + .onFailure(logError) + .getOrDefault(DataSize.of(QUEUE_DEFAULT_SIZE, ByteUnit.BYTE)) + .toUnit(ByteUnit.BYTE).value.toLong() +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/HttpAgentMessageDestinationMapper.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/HttpAgentMessageDestinationMapper.kt index d1b06e30..f8e7522c 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/HttpAgentMessageDestinationMapper.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/HttpAgentMessageDestinationMapper.kt @@ -17,12 +17,10 @@ package com.epam.drill.agent.transport import com.epam.drill.agent.common.transport.AgentMessageDestination -class HttpAgentMessageDestinationMapper : AgentMessageDestinationMapper { - - private val dataIngestionPath = "data-ingest" +class HttpAgentMessageDestinationMapper(val path: String = "data-ingest") : AgentMessageDestinationMapper { override fun map(destination: AgentMessageDestination) = destination.copy( - target = if (destination.target.isEmpty()) dataIngestionPath else "${dataIngestionPath}/${destination.target}" + target = if (destination.target.isEmpty()) path else "${path}/${destination.target}" ) } diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt index 8150a767..90c35b54 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt @@ -15,7 +15,6 @@ */ package com.epam.drill.agent.transport -import com.epam.drill.agent.common.configuration.AgentMetadata import java.io.File import io.aesy.datasize.ByteUnit import io.aesy.datasize.DataSize @@ -24,12 +23,10 @@ import com.epam.drill.agent.configuration.Configuration import com.epam.drill.agent.configuration.DefaultParameterDefinitions import com.epam.drill.agent.configuration.ParameterDefinitions import com.epam.drill.agent.transport.http.HttpAgentMessageTransport -import com.epam.drill.agent.common.transport.AgentMessage import com.epam.drill.agent.common.transport.AgentMessageDestination import com.epam.drill.agent.common.transport.AgentMessageSender import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable actual object JvmModuleMessageSender : AgentMessageSender { diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt index 99159932..445f2c89 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt @@ -24,15 +24,19 @@ import kotlinx.cinterop.staticCFunction import platform.posix.getpid import mu.KotlinLogging import com.epam.drill.agent.configuration.AgentLoggingConfiguration +import com.epam.drill.agent.configuration.AgentParametersValidator +import com.epam.drill.agent.configuration.CapabilityParameterDefinitions import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.DefaultParameterDefinitions import com.epam.drill.agent.configuration.DefaultParameterDefinitions.INSTALLATION_DIR -import com.epam.drill.agent.instrument.ApplicationClassTransformer +import com.epam.drill.agent.configuration.ParameterDefinitions import com.epam.drill.agent.jvmti.classFileLoadHook import com.epam.drill.agent.jvmti.vmDeathEvent import com.epam.drill.agent.jvmti.vmInitEvent import com.epam.drill.agent.module.JvmModuleLoader import com.epam.drill.agent.transport.JvmModuleMessageSender import com.epam.drill.agent.jvmapi.gen.* +import com.epam.drill.agent.test.session.SessionController import kotlinx.cinterop.ExperimentalForeignApi import kotlin.experimental.ExperimentalNativeApi @@ -57,6 +61,7 @@ object Agent { AgentLoggingConfiguration.defaultNativeLoggingConfiguration() Configuration.initializeNative(options) AgentLoggingConfiguration.updateNativeLoggingConfiguration() + validateConfiguration() addCapabilities() setEventCallbacks() setUnhandledExceptionHook({ error: Throwable -> logger.error(error) { "Unhandled event: $error" }}.freeze()) @@ -80,7 +85,10 @@ object Agent { AgentLoggingConfiguration.updateJvmLoggingConfiguration() Configuration.initializeJvm() loadJvmModule("com.epam.drill.agent.test2code.Test2Code") - JvmModuleMessageSender.sendAgentMetadata() + if (isClassScanningEnabled() || isCoverageCollectionEnabled()) { + JvmModuleMessageSender.sendAgentMetadata() + } + SessionController.startSession() } fun agentOnVmDeath() { @@ -110,4 +118,13 @@ object Agent { logger.error(it) { "loadJvmModule: Fatal error: id=${clazz}" } } + private fun validateConfiguration() { + val validator = AgentParametersValidator(Configuration.parameters) + validator.validate( + DefaultParameterDefinitions, + ParameterDefinitions, + ) + } + private fun isClassScanningEnabled(): Boolean = Configuration.parameters[CapabilityParameterDefinitions.CLASS_SCANNING_ENABLED] + private fun isCoverageCollectionEnabled(): Boolean = Configuration.parameters[CapabilityParameterDefinitions.COVERAGE_COLLECTION_ENABLED] } diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt index 52c51c33..d405e792 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt @@ -54,21 +54,15 @@ actual object Configuration : AgentConfiguration { installationDirProvider )) logger.debug { "initializeNative: Found from properties file: ${propertiesFileProvider.configuration}" } - val validatedParametersProvider = ValidatedParametersProvider(setOf( - environmentVariablesProvider, - agentOptionsProvider, - installationDirProvider, - propertiesFileProvider - )) val runtimeParametersProvider = RuntimeParametersProvider() configuration.value = DefaultAgentConfiguration(setOf( - validatedParametersProvider, environmentVariablesProvider, agentOptionsProvider, installationDirProvider, propertiesFileProvider, runtimeParametersProvider )).freeze() + defineDefaults(parameters) } actual fun initializeJvm(inputParameters: String): Unit = @@ -78,4 +72,17 @@ actual object Configuration : AgentConfiguration { .joinToString(",") { "${it.key}=${it.value}" } .let(::initializeJvm) + private fun defineDefaults(agentParameters: AgentParameters) { + agentParameters.define( + DefaultParameterDefinitions.APP_ID, + DefaultParameterDefinitions.INSTANCE_ID, + DefaultParameterDefinitions.BUILD_VERSION, + DefaultParameterDefinitions.GROUP_ID, + DefaultParameterDefinitions.COMMIT_SHA, + DefaultParameterDefinitions.ENV_ID, + DefaultParameterDefinitions.INSTALLATION_DIR, + DefaultParameterDefinitions.CONFIG_PATH, + DefaultParameterDefinitions.PACKAGE_PREFIXES + ) + } } diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt index e38cd65e..207ff796 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/instrument/TransformerRegistrar.kt @@ -43,6 +43,19 @@ import com.epam.drill.agent.instrument.undertow.UndertowHttpServerTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsClientTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsMessagesTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsServerTransformer +import com.epam.drill.agent.test.instrument.cucumber.Cucumber4Transformer +import com.epam.drill.agent.test.instrument.cucumber.Cucumber5Transformer +import com.epam.drill.agent.test.instrument.cucumber.Cucumber6Transformer +import com.epam.drill.agent.test.instrument.jmeter.JMeterTransformer +import com.epam.drill.agent.test.instrument.junit.JUnit4PrioritizingTransformer +import com.epam.drill.agent.test.instrument.junit.JUnit4Transformer +import com.epam.drill.agent.test.instrument.junit.JUnit5Transformer +import com.epam.drill.agent.test.instrument.junit.JUnitPlatformPrioritizingTransformer +import com.epam.drill.agent.test.instrument.selenium.SeleniumTransformer +import com.epam.drill.agent.test.instrument.testng.TestNG6PrioritizingTransformer +import com.epam.drill.agent.test.instrument.testng.TestNG6Transformer +import com.epam.drill.agent.test.instrument.testng.TestNG7PrioritizingTransformer +import com.epam.drill.agent.test.instrument.testng.TestNG7Transformer import mu.KotlinLogging actual object TransformerRegistrar { @@ -77,6 +90,20 @@ actual object TransformerRegistrar { UndertowWsServerTransformer, UndertowWsMessagesTransformer, CompatibilityTestsTransformer, + //Test instrumentation + Cucumber4Transformer, + Cucumber5Transformer, + Cucumber6Transformer, + JMeterTransformer, + JUnit4PrioritizingTransformer, + JUnit4Transformer, + JUnit5Transformer, + JUnitPlatformPrioritizingTransformer, + SeleniumTransformer, + TestNG6PrioritizingTransformer, + TestNG6Transformer, + TestNG7PrioritizingTransformer, + TestNG7Transformer ) actual val enabledTransformers: List by lazy { transformers.filter { transformer -> diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/AbstractTestTransformerObject.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/AbstractTestTransformerObject.kt new file mode 100644 index 00000000..3d668890 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/AbstractTestTransformerObject.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.CapabilityParameterDefinitions.TEST_TRACING_ENABLED +import com.epam.drill.agent.instrument.AbstractTransformerObject + +abstract class AbstractTestTransformerObject: AbstractTransformerObject() { + override fun enabled() = super.enabled() && Configuration.parameters[TEST_TRACING_ENABLED] +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumberTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumberTransformer.kt new file mode 100644 index 00000000..2bf426c8 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/AbstractCucumberTransformer.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_CUCUMBER_ENABLED +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +abstract class AbstractCucumberTransformer(): AbstractTestTransformerObject() { + override fun enabled() = super.enabled() && Configuration.parameters[INSTRUMENTATION_CUCUMBER_ENABLED] +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt new file mode 100644 index 00000000..547030cf --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber4Transformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer + +actual object Cucumber4Transformer: Transformer, AbstractCucumberTransformer() { + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == /*4.x.x*/"cucumber/runner/TestStep" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt new file mode 100644 index 00000000..65c069d2 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber5Transformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer + +actual object Cucumber5Transformer: Transformer, AbstractCucumberTransformer() { + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "io/cucumber/core/runner/TestStep" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt new file mode 100644 index 00000000..a527f17c --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/cucumber/Cucumber6Transformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.cucumber + +import com.epam.drill.agent.instrument.Transformer + +actual object Cucumber6Transformer: Transformer, AbstractCucumberTransformer() { + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "io/cucumber/core/runner/TestStep" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt new file mode 100644 index 00000000..42f3a387 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/jmeter/JMeterTransformer.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.jmeter + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_JMETER_ENABLED +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +actual object JMeterTransformer: Transformer, AbstractTestTransformerObject() { + override fun enabled() = super.enabled() && Configuration.parameters[INSTRUMENTATION_JMETER_ENABLED] + + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/AbstractJUnitTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/AbstractJUnitTransformer.kt new file mode 100644 index 00000000..29cf2035 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/AbstractJUnitTransformer.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_JUNIT_ENABLED +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +abstract class AbstractJUnitTransformer(): AbstractTestTransformerObject() { + override fun enabled() = super.enabled() && Configuration.parameters[INSTRUMENTATION_JUNIT_ENABLED] +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt new file mode 100644 index 00000000..71bc5cb8 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4PrioritizingTransformer.kt @@ -0,0 +1,30 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.instrument.Transformer + +actual object JUnit4PrioritizingTransformer: Transformer, AbstractJUnitTransformer() { + override fun enabled(): Boolean = super.enabled() && Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_ENABLED] + + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/junit/runners/JUnit4" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt new file mode 100644 index 00000000..358e67b9 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit4Transformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer + +actual object JUnit4Transformer: Transformer, AbstractJUnitTransformer() { + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/junit/runner/notification/RunNotifier" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt new file mode 100644 index 00000000..0069a700 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnit5Transformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.instrument.Transformer + +actual object JUnit5Transformer: Transformer, AbstractJUnitTransformer() { + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/junit/platform/engine/support/hierarchical/NodeTestTaskContext" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt new file mode 100644 index 00000000..9ab372ab --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/junit/JUnitPlatformPrioritizingTransformer.kt @@ -0,0 +1,30 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.junit + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.instrument.Transformer + +actual object JUnitPlatformPrioritizingTransformer: Transformer, AbstractJUnitTransformer() { + override fun enabled(): Boolean = super.enabled() && Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_ENABLED] + + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/junit/platform/launcher/core/DefaultLauncher" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt new file mode 100644 index 00000000..49872007 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/selenium/SeleniumTransformer.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.selenium + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_SELENIUM_ENABLED +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +actual object SeleniumTransformer: Transformer, AbstractTestTransformerObject() { + override fun enabled() = super.enabled() && Configuration.parameters[INSTRUMENTATION_SELENIUM_ENABLED] + + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/openqa/selenium/remote/RemoteWebDriver" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGPrioritizingTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGPrioritizingTransformer.kt new file mode 100644 index 00000000..648a53c4 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGPrioritizingTransformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_TESTNG_ENABLED +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +abstract class AbstractTestNGPrioritizingTransformer(): AbstractTestNGTransformer() { + override fun enabled(): Boolean = super.enabled() + && Configuration.parameters[ParameterDefinitions.RECOMMENDED_TESTS_ENABLED] +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGTransformer.kt new file mode 100644 index 00000000..5d7d46f0 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/AbstractTestNGTransformer.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_TESTNG_ENABLED +import com.epam.drill.agent.test.instrument.AbstractTestTransformerObject + +abstract class AbstractTestNGTransformer(): AbstractTestTransformerObject() { + override fun enabled() = super.enabled() && Configuration.parameters[INSTRUMENTATION_TESTNG_ENABLED] + + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className.startsWith("org/testng/") + +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt new file mode 100644 index 00000000..9d20b7ec --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6PrioritizingTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +actual object TestNG6PrioritizingTransformer: Transformer, AbstractTestNGPrioritizingTransformer() \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt new file mode 100644 index 00000000..f512e0ac --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG6Transformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +actual object TestNG6Transformer: Transformer, AbstractTestNGTransformer() { + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/testng/TestRunner" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt new file mode 100644 index 00000000..b9c0eee0 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7PrioritizingTransformer.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +actual object TestNG7PrioritizingTransformer: Transformer, AbstractTestNGPrioritizingTransformer() \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt new file mode 100644 index 00000000..77c5fc51 --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/instrument/testng/TestNG7Transformer.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.instrument.testng + +import com.epam.drill.agent.instrument.Transformer + +actual object TestNG7Transformer: Transformer, AbstractTestNGTransformer() { + override fun precheck( + className: String, + loader: Any?, + protectionDomain: Any? + ): Boolean = className == "org/testng/TestRunner" +} \ No newline at end of file diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt new file mode 100644 index 00000000..852b934e --- /dev/null +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.session + +import com.epam.drill.agent.jvmapi.callObjectVoidMethod +import com.epam.drill.agent.jvmapi.callObjectVoidMethodWithString + +actual object SessionController { + + actual fun startSession(): Unit = + callObjectVoidMethod(SessionController::class, SessionController::startSession) + +} diff --git a/lib-jvm-shared/.github/workflows/check.yml b/lib-jvm-shared/.github/workflows/check.yml new file mode 100644 index 00000000..510843bc --- /dev/null +++ b/lib-jvm-shared/.github/workflows/check.yml @@ -0,0 +1,45 @@ +name: Check +on: + workflow_dispatch: + push: + branches: + - 'main' + - 'release/*' + - 'adoption/*' + paths: + - '**/src/**' + - '**.gradle.kts' + - 'gradle.properties' + pull_request: + branches: + - 'main' + - 'release/*' + - 'adoption/*' + paths: + - '**/src/**' + - '**.gradle.kts' + - 'gradle.properties' + +jobs: + check: + strategy: + matrix: + config: + - {os: ubuntu-latest, preset: linuxX64} + runs-on: ${{ matrix.config.os }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: 8.0 + distribution: zulu + - if: matrix.config.preset == 'linuxX64' + run: chmod +x ${{ github.workspace }}/gradlew + - uses: gradle/gradle-build-action@v2 + with: + arguments: check + - if: always() + uses: actions/upload-artifact@v4 + with: + name: test-reports-${{ matrix.config.preset }} + path: ${{ github.workspace }}/*/build/reports/* diff --git a/lib-jvm-shared/.gitignore b/lib-jvm-shared/.gitignore new file mode 100644 index 00000000..e088ec47 --- /dev/null +++ b/lib-jvm-shared/.gitignore @@ -0,0 +1,7 @@ +build/ +.gradle/ +.idea/ +*.iml +**/kotlin/gen/ +**/kotlin/kni/ +kni-meta-info diff --git a/lib-jvm-shared/DEVELOPMENT.md b/lib-jvm-shared/DEVELOPMENT.md new file mode 100644 index 00000000..7a282f76 --- /dev/null +++ b/lib-jvm-shared/DEVELOPMENT.md @@ -0,0 +1,151 @@ +# Development of Drill4J + +See README.md for high-level information about Drill4J component. + +This document describes building and the development of Drill4J. + +## Branches + +There are following branch types: +- main branch + - name pattern: **main** + - description: main branch, direct pushes are forbidden, only pull-request allowed +- release branch + - name pattern: **release/\** + - description: release branches automatically created during release publishing, used for hot-fixe releases development and publishing +- merge branch + - name pattern: **merge/\** + - description: temporary branches, may be created to simplify merge of feature branches into main branch +- adoption branch + - name pattern: **adoption/\** + - description: branches with adoption-specific changes that cannot be integrated into main branch +- feature branches + - name pattern: **feature/\** + - name pattern: **bugfix/\** + - name pattern: **docs/\** + - **\**: should be in format **\-\** (like feature/do-something-cool-epmdj-12356 or feature/do-something-cool) + - description: branches for feature (bugfix, documentation) development, should be merged into main/adoption branch via pull-request and removed at feature completion + +TODO: add visualization + +## Versions + +There are two types of versioning: +- version created from **main** branch + - format: **MAJOR.MINOR.PATCH** (like 0.8.3) + - increment: **PATCH** version auto-incremented after each release + - tags: new tag automatically created for release in **main** branch + - branches: new release branch automatically created for release forked from **main** branch +- versions created from **adoption** branches + - format: **MAJOR.MINOR.PATCH-suffix.PRERELEASE** (like 0.8.3-alpine.2) + - increment: **PRERELEASE** version auto-incremented after each release + - tags: new tag automatically created for release in **adoption** branch + - branches: new release branch automatically created for release forked from **adoption** branch +- versions created from **release** branches + - format: **MAJOR.MINOR.PATCH-suffix.PRERELEASE** (like 0.8.3-hf.0) + - increment: **PRERELEASE** version auto-incremented after each release + - tags: new tag automatically created for release in **release** branch + - branches: no release branches automatically created from **release** branches + +See also: https://semver.org/ + +## Commits + +Commit messages should be descriptive and use following format: +``` +(): + + + +Refs: +``` + +Where: +- **\**: commit type, one of following values: + - **\**: feature implementation + - **\**: bug-fix implementation + - **\**: add/update documentation + - **\**: code refactoring + - **\**: tests implementation + - **\**: build-scripts implementation +- **\**: affected modules of application +- **\**: description of ticket in which scope changes was made +- **\**: brief description of changes itself +- **\**: JIRA ticket number + +See also: https://www.conventionalcommits.org/ + +## Merges and pull-requests + +There are two types of merges: +- Merges from **main**/**merge**/**adoption**/**feature** branches: merges should be done using **merge-commits**, please **avoid cherry-picks and rebase** +- Changes from **release** branches: merges should be done using **cherry-pick-commits**, please **avoid merge-commits** + +Merges to **main** branch should be performed via **pull-requests only** with code review and automatic code checks: +- Tip of the main must always work +- Each merge request must have a result in working state + +To merge changes in main branch please perform following steps: +- Create **merge/\** branch from the tip of the **main** branch +- Create pull-requests from **feature** branches to **merge/\** branch +- Accept pull-requests for each feature branch as merge-commit +- Merge **merge/\** into **main** branch +- Remove **merge/\** branch and **feature** branches + +Keep feature branches up to date: +- Merge **main** branch into **feature** branches as frequently as possible +- Before merge **feature** branch into **main**/**merge** branch ensure that **feature** branch is updated by last changes from **main** (see above) + +## Release publishing + +New releases should be published using manual run of "**Release**" GitHub workflow in corresponding component repository. + +Version of **lib-jvm-shared** used by component is freezing during "**Release**" GitHub workflow execution: +- New tag in **lib-jvm-shared** repository is creating in format: **-** +- Variable `libJvmSharedRef` is changing to newly created tag, changed **gradle.properties** file is committing to **release** branch + +Following files are included in release (per component): +- admin: zip archive with application .jar files (admin-\.zip, admin-shadow-\.zip), docker image in GitHub container registry (https://github.com/drill4j/admin/pkgs/container/admin) +- test2code-plugin: zip archive with admin-part.jar, agent-part.jar and plugin_config.json files (test2code-plugin-\.zip) +- java-agent: zip archive with drillRuntime.jar file and libdrill_agent.so/libdrill_agent.dll library for supported platforms (agent-linuxX64-\.zip, agent-mingwX64-\.zip) + +## Local development environment + +To set up environment for local development please perform following steps: +1. Install tools (git, gradle, java) +2. Clone component repository, checkout corresponding branch +3. Run script to set up **lib-jvm-shared** repository from Git: setup-shared-libs.bat/setup-shared-libs.sh + +NOTE for MacOS users (Intel-base):MacOS users with Intel-based systems might encounter the error `unknown parameter --sdk-version` +during local builds. This issue arises due to the new linker in Xcode. To resolve it, set `macosLd64` to `true` in the `gradle.properties` +file and add the `-ld64` parameter to `linkerOpts` for the `macosX64` target. + +## Making changes + +There is following workflow to make changes to component code: +- Create **feature** branch and switch to it +- Update **lib-jvm-shared** libraries to actual state using `updateSharedLibs` gradle task +- If specific changes in **lib-jvm-shared** libraries required do following: + - create separate branch of **lib-jvm-shared** + - update variable `libJvmSharedRef` in **gradle.properties** file to corresponding tag name +- Implement code changes +- Ensure that component successfully assembled and tested using `assemble`+`check` or `build` gradle tasks +- Test on local development environment (see "**How to run**" section) +- Commit and push changes +- Create pull-request and merge changes in corresponding branch (**main**, **adoption** or **release**) +- Run "**Release**" GitHub workflow to publish new release + +## How to run + +### Run database + +For **admin** component database is required, database may be started using 2 options: +- Run database container: `docker run --name some-postgres -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword -d postgres` +- Use embedded database: set `embeddedMode=true` in **application.properties** file + - To clean data from it use `cleanData` gradle task in **admin-core** module + +### Run application + +There 2 options to run **admin** application: +- Run as java application using `run` gradle task in **admin-core** module +- Run as Docker container, container may be published into local Docker repository using `jibDockerBuild` gradle task in **admin-core** module diff --git a/lib-jvm-shared/LICENSE b/lib-jvm-shared/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/README.md b/lib-jvm-shared/README.md new file mode 100644 index 00000000..d3575054 --- /dev/null +++ b/lib-jvm-shared/README.md @@ -0,0 +1,77 @@ +> **DISCLAIMER**: We use Google Analytics for sending anonymous usage information such as agent's type, version +> after a successful agent registration. This information might help us to improve both Drill4J backend and client sides. It is used by the +> Drill4J team only and is not supposed for sharing with 3rd parties. +> You are able to turn off by set system property `analytic.disable = true` or send PATCH request `/api/analytic/toggle` + +[![Check](https://github.com/Drill4J/lib-jvm-shared/actions/workflows/check.yml/badge.svg)](https://github.com/Drill4J/lib-jvm-shared/actions/workflows/check.yml) +[![License](https://img.shields.io/github/license/Drill4J/lib-jvm-shared)](LICENSE) +[![Visit the website at drill4j.github.io](https://img.shields.io/badge/visit-website-green.svg?logo=firefox)](https://drill4j.github.io/) +[![Telegram Chat](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/drill4j) +![Docker Pulls](https://img.shields.io/docker/pulls/drill4j/lib-jvm-shared) +![GitHub contributors](https://img.shields.io/github/contributors/Drill4J/lib-jvm-shared) +![Lines of code](https://img.shields.io/tokei/lines/github/Drill4J/lib-jvm-shared) +![YouTube Channel Views](https://img.shields.io/youtube/channel/views/UCJtegUnUHr0bO6icF1CYjKw?style=social) + +# Drill4J JVM-based shared libraries + +The JVM-based shared libraries for Drill4J, used in java-agent, test2code-plugin and admin components of Drill4J. + +## Modules + +- **agent**: Contains the abstract agent core used to create platform specific JVMTI agents (Java, .NET), , see more in [here](agent/README.md) +- **agent-runner-common**: Common part of plugins for build tools (gradle and maven) to easily run [autotest agents](https://github.com/Drill4J/autotest-agent) +- **agent-runner-gradle**: Gradle part of plugins for build tools to easily run [autotest agents](https://github.com/Drill4J/autotest-agent) +- **common**: Common back-end related Drill4J project parts (common part) +- **drill-hook**: Library for intercepting system functions. +- **http-clients-instrumentation**: Common instrumentation for http clients +- **interceptor-http**: Library for intercepting http protocols calls +- **interceptor-http-test**: Tests for **interceptor-http** +- **interceptor-http2**: Library (v2) for intercepting http protocols calls +- **interceptor-http2-test**: Tests for **interceptor-http2** +- **interceptor-http2-test-grpc**: Tests for **interceptor-http2** +- **jvmapi**: Library to simplify JVMTI calls from java-agent code +- **knasm**: Port of Java ASM library to Kotlin native +- **kni-plugin**: Gradle plugin for KNI classes (stub classes to simplify JVMTI calls) generation +- **kni-runtime**: Annotation and interface classes to use with KNI plugin +- **ktor-swagger**: Library for ktor with swagger integration +- **ktor-swagger-sample**: Sample implementation for **ktor-swagger** +- **logging**: Pre-configuration tier for microutils kotlin-logging library (useful for multiplatform modules) +- **test-data**: Test data for admin/test2code-plugin testing +- **test-plugin**: Module for test-plugin distribution generation (includes **test-plugin-admin** and **test-plugin-agent** jars) +- **test-plugin-admin**: Test-plugin for **admin** component tests (admin-part) +- **test-plugin-agent**: Test-plugin for **admin** component tests (agent-part) +- **transport**: Library with transport protocol for backend communication + +## Modules kni-plugin and kni-runtime + +**Kni-runtime** module container two classes: +- **annotation class Kni**: To mark classes for which kni-stub classes should be generated. Generated stub classes used for 2 purposes: + - Call JVM methods from native code, example: `actual fun retrieveClassesData(config: String): ByteArray { return DataServiceStub.retrieveClassesData(config) }` + - Call native methods from JVM code, example: `actual external fun adminAddressHeader(): String?` +- **interface JvmtiAgent**: Interface should be implemented by JVMTI-agent class which will be called in kni-generated function: +```kotlin +@CName("Agent_OnLoad") +public fun agentOnLoad( + vmPointer: CPointer, + options: String, + reservedPtr: Long +): Int = memScoped { + com.epam.drill.agent.jvmapi.vmGlobal.value = vmPointer.freeze() + val vm = vmPointer.pointed + val jvmtiEnvPtr = alloc>() + vm.value!!.pointed.GetEnv!!(vm.ptr, jvmtiEnvPtr.ptr.reinterpret(), + com.epam.drill.agent.jvmapi.gen.JVMTI_VERSION.convert()) + com.epam.drill.agent.jvmapi.jvmti.value = jvmtiEnvPtr.value + jvmtiEnvPtr.value.freeze() + com.epam.drill.agent.Agent.agentOnLoad(options) // call method of class configured in gradle file using kni-block +} +``` + +**Kni-plugin** module contains classes for kni-stubs and JVMTI entry-point generation, generation details should be configured in gradle file using **kni** block, like: +```kotlin + kni { + jvmTargets = sequenceOf(jvm) + jvmtiAgentObjectPath = "com.epam.drill.agent.Agent" + nativeCrossCompileTarget = sequenceOf(linuxX64, mingwX64, macosX64) + } +``` diff --git a/lib-jvm-shared/REMOVED.md b/lib-jvm-shared/REMOVED.md new file mode 100644 index 00000000..d29891c2 --- /dev/null +++ b/lib-jvm-shared/REMOVED.md @@ -0,0 +1,126 @@ +# Removed obsolete features + +## Native plugins support + +* Revision: f88002dd2de8b33296ca837b27b465952dd2ffd6 +* Author: epamrsa +* Date: Friday, 30 June, 2023 16:17:26 +* Message: +``` +feat: Change java agent to transfer only class metadata instead of class bytes to the admin side + +Remove obsolete support for native plugins + +Refs: EPMDJ-10603 +``` +* Changes: +``` +Deleted: agent/src/commonMain/kotlin/com/epam/drill/DynamicLoader.kt +Modified: agent/src/jvmMain/kotlin/com/epam/drill/ActualStubs.kt +Deleted: agent/src/mingwX64Main/kotlin/com/epam/drill/DynamicLoader.kt +Modified: agent/src/nativeMain/kotlin/com/epam/drill/core/AgentApi.kt +Modified: agent/src/nativeMain/kotlin/com/epam/drill/core/ws/WsRouter.kt +Deleted: agent/src/nativeMain/kotlin/com/epam/drill/plugin/api/processing/NativePart.kt +Deleted: agent/src/posixMain/kotlin/com/epam/drill/DynamicLoader.kt +Modified: common/src/commonMain/kotlin/com/epam/drill/common/PluginMetadata.kt +``` + +## Removed modules: KNI (obsolete), interceptor-http2 (obsolete), agent-runner-gradle (moved to autotest-agent repo), agent-runner-common (moved to autotest-agent repo) + +* Revision: 3f711a163660737089c33bf7632ab86bbf1be058 +* Author: epamrsa +* Date: Thursday, 9 November, 2023 16:53:03 +* Message: +``` +feat: Remove obsolete modules in lib-jvm-shared and java-agent + +Removed modules: +- KNI (obsolete) +- interceptor-http2 (obsolete) +- agent-runner-gradle (moved to autotest-agent repo) +- agent-runner-common (moved to autotest-agent repo) + +Refs: EPMDJ-10708 +``` +* Changes: +``` +Deleted: agent-runner-common/LICENSE +Deleted: agent-runner-common/README.md +Deleted: agent-runner-common/build.gradle.kts +Deleted: agent-runner-common/src/main/kotlin/com/epam/drill/agent/runner/AgentConfiguration.kt +Deleted: agent-runner-common/src/main/kotlin/com/epam/drill/agent/runner/AgentLoader.kt +Deleted: agent-runner-common/src/main/kotlin/com/epam/drill/agent/runner/AppAgentConfiguration.kt +Deleted: agent-runner-common/src/main/kotlin/com/epam/drill/agent/runner/Configuration.kt +Deleted: agent-runner-common/src/main/kotlin/com/epam/drill/agent/runner/LogLevels.kt +Deleted: agent-runner-common/src/main/kotlin/com/epam/drill/agent/runner/OS.kt +Deleted: agent-runner-gradle/LICENSE +Deleted: agent-runner-gradle/README.md +Deleted: agent-runner-gradle/build.gradle.kts +Deleted: agent-runner-gradle/src/main/kotlin/com/epam/drill/autotest/gradle/Agent.kt +Deleted: agent-runner-gradle/src/main/kotlin/com/epam/drill/autotest/gradle/AppAgent.kt +Deleted: agent-runner-gradle/src/main/kotlin/com/epam/drill/autotest/gradle/AutoTestAgent.kt +Modified: gradle.properties +Deleted: interceptor-http2-test-grpc/LICENSE +Deleted: interceptor-http2-test-grpc/build.gradle.kts +Deleted: interceptor-http2-test-grpc/src/main/proto/helloworld/helloworld.proto +Deleted: interceptor-http2-test-grpc/src/test/kotlin/GrpcTest.kt +Deleted: interceptor-http2-test-grpc/src/test/kotlin/Xs.kt +Deleted: interceptor-http2-test/LICENSE +Deleted: interceptor-http2-test/build.gradle.kts +Deleted: interceptor-http2-test/src/commonMain/kotlin/CommonDataForTest.kt +Deleted: interceptor-http2-test/src/jvmJettyServerTest/kotlin/io/ktor/samples/http2push/Http2PushApplication.kt +Deleted: interceptor-http2-test/src/jvmJettyServerTest/resources/application.conf +Deleted: interceptor-http2-test/src/jvmJettyServerTest/resources/index.html +Deleted: interceptor-http2-test/src/jvmJettyServerTest/resources/keystore +Deleted: interceptor-http2-test/src/jvmJettyServerTest/resources/temporary.jks +Deleted: interceptor-http2-test/src/jvmMain/kotlin/bindings/Bindings.kt +Deleted: interceptor-http2-test/src/nativeAgentMain/kotlin/Main.kt +Deleted: interceptor-http2/LICENSE +Deleted: interceptor-http2/README.md +Deleted: interceptor-http2/build.gradle.kts +Deleted: interceptor-http2/src/commonMain/kotlin/com/epam/drill/interceptor/api.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/AutoCloseable.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/ByteArrayInputStream.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/ByteArrayOutputStream.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/Closeable.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/Decoder.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/DynamicTable.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/EOFException.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/Encoder.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/Flushable.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/HeaderField.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/HeaderListener.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/HpackUtil.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/Huffman.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/HuffmanDecoder.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/HuffmanEncoder.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/IOException.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/InputStream.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/OutputStream.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/StaticTable.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/hpack/System.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/interceptor/Arrays.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/interceptor/Http2.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/interceptor/Http2Parser.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/interceptor/ReadStream.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/interceptor/Stream.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/interceptor/WriteStream.kt +Deleted: interceptor-http2/src/commonNative/kotlin/com/epam/drill/interceptor/haffman/Util.kt +Deleted: interceptor-http2/src/linuxX64Main/kotlin/com/epam/drill/interceptor/Http2.kt +Deleted: interceptor-http2/src/macosX64Main/kotlin/com/epam/drill/interceptor/Http2.kt +Deleted: interceptor-http2/src/macosX64Test/kotlin/Arrays.kt +Deleted: interceptor-http2/src/mingwX64Main/kotlin/com/epam/drill/interceptor/Http2.kt +Deleted: kni-plugin/LICENSE +Deleted: kni-plugin/README.md +Deleted: kni-plugin/build.gradle.kts +Deleted: kni-plugin/src/main/kotlin/com/epam/drill/kni/gradle/Generator.kt +Deleted: kni-plugin/src/main/kotlin/com/epam/drill/kni/gradle/JvmtiGenerator.kt +Deleted: kni-plugin/src/main/kotlin/com/epam/drill/kni/gradle/Kni.kt +Deleted: kni-plugin/src/main/kotlin/com/epam/drill/kni/gradle/Mappings.kt +Deleted: kni-runtime/LICENSE +Deleted: kni-runtime/README.md +Deleted: kni-runtime/build.gradle.kts +Deleted: kni-runtime/src/commonMain/kotlin/com/epam/drill/kni/Annotation.kt +Deleted: kni-runtime/src/commonMain/kotlin/com/epam/drill/kni/JvmtiAgent.kt +Modified: settings.gradle.kts +``` diff --git a/lib-jvm-shared/agent-config/LICENSE b/lib-jvm-shared/agent-config/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/agent-config/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/agent-config/README.md b/lib-jvm-shared/agent-config/README.md new file mode 100644 index 00000000..9635a28a --- /dev/null +++ b/lib-jvm-shared/agent-config/README.md @@ -0,0 +1,3 @@ +# Drill agent module + +This module contains the abstract agent core used to create platform specific agents (Java, .NET). diff --git a/lib-jvm-shared/agent-config/build.gradle.kts b/lib-jvm-shared/agent-config/build.gradle.kts new file mode 100644 index 00000000..98b8e31f --- /dev/null +++ b/lib-jvm-shared/agent-config/build.gradle.kts @@ -0,0 +1,111 @@ +import java.net.URI +import java.util.Properties +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} + +val ktorVersion: String by parent!!.extra +val macosLd64: String by parent!!.extra +val microutilsLoggingVersion: String by parent!!.extra + +repositories { + mavenCentral() +} + +kotlin { + jvm() + linuxX64() + mingwX64() + macosX64().apply { + if(macosLd64.toBoolean()){ + binaries.all { + linkerOpts("-ld64") + } + } + } + macosArm64().apply { + if (macosLd64.toBoolean()) { + binaries.all { + linkerOpts("-ld64") + } + } + } + @Suppress("UNUSED_VARIABLE") + sourceSets { + all { + languageSettings.optIn("io.ktor.utils.io.core.ExperimentalIoApi") + } + val commonMain by getting { + dependencies { + implementation(project(":common")) + implementation(project(":konform")) + api("io.github.microutils:kotlin-logging:${microutilsLoggingVersion}") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + val nativeMain by creating { + dependsOn(commonMain) + dependencies { + implementation("io.ktor:ktor-utils:$ktorVersion") + implementation(project(":konform")) + } + } + val nativeTest by creating { + dependsOn(commonTest) + } + val mingwX64Main by getting { + dependsOn(nativeMain) + } + val mingwX64Test by getting { + dependsOn(nativeTest) + } + val linuxX64Main by getting { + dependsOn(nativeMain) + } + val linuxX64Test by getting { + dependsOn(nativeTest) + } + val macosX64Main by getting { + dependsOn(nativeMain) + } + val macosX64Test by getting { + dependsOn(nativeTest) + } + val macosArm64Main by getting { + dependsOn(nativeMain) + } + val macosArm64Test by getting { + dependsOn(nativeTest) + } + } +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } +} diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentConfigurationProvider.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentConfigurationProvider.kt new file mode 100644 index 00000000..df85c92f --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentConfigurationProvider.kt @@ -0,0 +1,21 @@ +package com.epam.drill.agent.configuration + +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +interface AgentConfigurationProvider { + val priority: Int + val configuration: Map +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentParameterValidationError.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentParameterValidationError.kt new file mode 100644 index 00000000..9678da2f --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentParameterValidationError.kt @@ -0,0 +1,18 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +class AgentParameterValidationError(message: String): RuntimeException(message) \ No newline at end of file diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentParametersValidator.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentParametersValidator.kt new file mode 100644 index 00000000..8c68f6c5 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentParametersValidator.kt @@ -0,0 +1,73 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.configuration.BaseAgentParameterDefinition +import com.epam.drill.agent.common.configuration.ValidationError +import mu.KotlinLogging + +class AgentParametersValidator(private val parameters: AgentParameters) { + private val logger = KotlinLogging.logger { } + + fun validate(vararg collections: AgentParameterDefinitionCollection) { + validate(*collections.flatMap { it.getAll() }.toTypedArray()) + } + + fun validate(vararg definitions: BaseAgentParameterDefinition<*>) { + getStrictValidationErrors(*definitions) + .takeIf { it.isNotEmpty() } + ?.let { errors -> + throw AgentParameterValidationError( + "Some parameters are set incorrectly.\n" + + "Please check the following parameters:\n" + + errors.joinToString("\n") + ) + } + getSoftValidationErrors(*definitions) + .takeIf { it.isNotEmpty() } + ?.let { errors -> + logger.warn { + "Some parameters were set incorrectly and were replaced with default values.\n" + + "Please check the following parameters:\n" + + errors.joinToString("\n") + } + } + } + + fun getValidationErrors(vararg definitions: BaseAgentParameterDefinition<*>): List { + return parameters.define(*definitions).flatMap(::formatErrors) + } + + fun getStrictValidationErrors(vararg definitions: BaseAgentParameterDefinition<*>): List { + return getValidationErrors(*definitions.filter { it.isStrictValidation() }.toTypedArray()) + } + + fun getSoftValidationErrors(vararg definitions: BaseAgentParameterDefinition<*>): List { + return getValidationErrors(*definitions.filter { it.isSoftValidation() }.toTypedArray()) + } + + private fun formatErrors(error: ValidationError<*>): List = + error.messages.associateBy { error.definition }.map { + "- ${it.key.name} ${it.value.removeRelocationPackage()}. ${it.key.description?: ""}\n" + } + + //TODO: workaround package relocation issue with konform + private fun String.removeRelocationPackage() = this + .removePrefix("com.epam.drill.agent.shadow.") + .removePrefix("com/epam/drill/agent/shadow/") +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt new file mode 100644 index 00000000..92b9a0ab --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt @@ -0,0 +1,21 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +expect object AgentProcessMetadata { + val commandLine: String + val environmentVars: Map +} diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/CapabilityParameterDefinitions.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/CapabilityParameterDefinitions.kt new file mode 100644 index 00000000..f1bb9bb8 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/CapabilityParameterDefinitions.kt @@ -0,0 +1,37 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection + +object CapabilityParameterDefinitions : AgentParameterDefinitionCollection() { + val COVERAGE_COLLECTION_ENABLED = AgentParameterDefinition.forBoolean( + name = "coverageCollectionEnabled", + description = "Enable/disable application code coverage collection", + defaultValue = true + ).register() + val CLASS_SCANNING_ENABLED = AgentParameterDefinition.forBoolean( + name = "classScanningEnabled", + description = "Enable/disable application classes scanning", + defaultValue = true + ).register() + val TEST_TRACING_ENABLED = AgentParameterDefinition.forBoolean( + name = "testTracingEnabled", + description = "Enable/disable test tracing", + defaultValue = false + ).register() +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt new file mode 100644 index 00000000..1f882eee --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentMetadata +import com.epam.drill.agent.common.configuration.AgentParameters + +expect class DefaultAgentConfiguration : AgentConfiguration { + override val agentMetadata: AgentMetadata + override val parameters: AgentParameters + val inputParameters: Map +} diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt new file mode 100644 index 00000000..31a68a95 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt @@ -0,0 +1,33 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlin.reflect.KProperty +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition +import com.epam.drill.agent.common.configuration.BaseAgentParameterDefinition +import com.epam.drill.agent.common.configuration.ValidationError + +expect class DefaultAgentParameters( + inputParameters: Map +) : AgentParameters { + override operator fun get(name: String): T? + override operator fun get(definition: AgentParameterDefinition): T + override operator fun getValue(ref: Any?, property: KProperty<*>): T? + override fun get(definition: NullableAgentParameterDefinition): T? + override fun define(vararg definitions: BaseAgentParameterDefinition): List> +} diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultParameterDefinitions.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultParameterDefinitions.kt new file mode 100644 index 00000000..473a1be3 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/DefaultParameterDefinitions.kt @@ -0,0 +1,93 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition +import com.epam.drill.agent.konform.validation.jsonschema.minItems +import com.epam.drill.agent.konform.validation.jsonschema.minLength + +object DefaultParameterDefinitions : AgentParameterDefinitionCollection() { + + val applicationAgentEnabled: (AgentParameters) -> Boolean = { config -> + config[CapabilityParameterDefinitions.CLASS_SCANNING_ENABLED] + || config[CapabilityParameterDefinitions.COVERAGE_COLLECTION_ENABLED] + } + + val GROUP_ID = AgentParameterDefinition.forString( + name = "groupId", + description = "Unique arbitrary string identifying your application group. Example: my-cool-app", + validator = { + identifier() + minLength(3) + }).register() + val APP_ID = NullableAgentParameterDefinition.forString( + name = "appId", + description = "Unique arbitrary string identifying your application. Example: api-service", + requiredIf = applicationAgentEnabled, + validator = { + identifier() + minLength(3) + }).register() + val BUILD_VERSION = NullableAgentParameterDefinition.forString( + name = "buildVersion", + description = "Build version of your application. Typically set to version tag. Example: 0.1.2", + ).register() + val COMMIT_SHA = NullableAgentParameterDefinition.forString( + name = "commitSha", + description = "Full SHA hash of commit from which your application .jar is built. Example: 8d87b0c2379a925f2f5f4d85c731c8e77d9f2b3c" + ).register() + val INSTANCE_ID = NullableAgentParameterDefinition.forString(name = "instanceId").register() + val ENV_ID = NullableAgentParameterDefinition.forString( + name = "envId", + description = "Environment identifier in which the application is running. Example: develop" + ).register() + val PACKAGE_PREFIXES = AgentParameterDefinition.forList( + name = "packagePrefixes", + description = """ + Packages starting with matching string will be scanned. + It's usually set to the topmost common package of your application. + + Syntax: + 1. Parts of package names are separated with forward slashes / (and not dots .) + 2. Multiple packages can be specified using ; delimiter + 3. To exclude a package use ! before package name. + + Examples: + my/org/some/cool/app; + my/org/some/cool/app;!my/org/some/cool/app/dto + my/org/some/cool/app;my/org/some/dependency + + Documentation: + https://drill4j.github.io/docs/agents/java-agent/#how-to-set-package-prefixes + """.trimIndent(), + requiredIf = applicationAgentEnabled, + parser = { it.split(";") }, + listValidator = { + minItems(1) + }, + itemValidator = { + isValidPackage() + } + ).register() + val INSTALLATION_DIR = NullableAgentParameterDefinition.forString(name = "drillInstallationDir", validator = { + minLength(1) + }).register() + val CONFIG_PATH = NullableAgentParameterDefinition.forString(name = "configPath").register() + +} diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt new file mode 100644 index 00000000..1f9c0f86 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt @@ -0,0 +1,48 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.konform.validation.Constraint +import com.epam.drill.agent.konform.validation.ValidationBuilder +import kotlin.time.Duration + +val TRANSPORT_SCHEMES = setOf("http://", "https://") + +fun ValidationBuilder.validTransportUrl() = addConstraint( + "must have a valid URL address, e.g. 'https://localhost:8090', but was '{value}'" +){ TRANSPORT_SCHEMES.any(it::startsWith) } + +fun ValidationBuilder.identifier() = addConstraint( + "must contain only lowercase latin characters", +) { + it.matches("^[a-z0-9_-]+\$".toRegex()) +} + +fun ValidationBuilder.isValidPackage(): Constraint = addConstraint( + "must have a valid Java package delimited by a forward slash, e.g. 'com/example', but was '{value}'" +) { it.matches("^!?[a-zA-Z_]\\w*(?:/[a-zA-Z_]\\w*)*$".toRegex()) } + +fun ValidationBuilder.isValidLogLevel(): Constraint = addConstraint( + "must have a valid logging level for a java package, e.g. 'com.example=INFO', but was '{value}'" +) { it.matches("(([a-zA-Z_]\\w*(\\.[a-zA-Z_]\\w*)*)?=)?(TRACE|DEBUG|INFO|WARN|ERROR)".toRegex()) } + +fun ValidationBuilder.minDuration(minimumInclusive: Duration) = addConstraint( + "must be at least '{0}' ms", minimumInclusive.inWholeMilliseconds.toString() +) { it.inWholeMilliseconds >= minimumInclusive.inWholeMilliseconds } + +fun ValidationBuilder.isNotBlank(): Constraint = addConstraint( + "must be not blank, but was {value}", +) { it.isNotBlank() } \ No newline at end of file diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/provider/AgentOptionsProvider.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/provider/AgentOptionsProvider.kt new file mode 100644 index 00000000..3207ab0b --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/provider/AgentOptionsProvider.kt @@ -0,0 +1,37 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import com.epam.drill.agent.configuration.AgentConfigurationProvider + +class AgentOptionsProvider( + private val agentOptions: String?, + override val priority: Int = 400 +) : AgentConfigurationProvider { + + override val configuration = configuration() + + private fun configuration(): Map { + if (agentOptions == null) { + return emptyMap() + } + return agentOptions.split(",") + .filter(String::isNotEmpty) + .associate { + it.substringBefore("=") to it.substringAfter("=", "") + } + } +} diff --git a/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/provider/EnvironmentVariablesProvider.kt b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/provider/EnvironmentVariablesProvider.kt new file mode 100644 index 00000000..a8663203 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/commonMain/kotlin/com/epam/drill/agent/configuration/provider/EnvironmentVariablesProvider.kt @@ -0,0 +1,42 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import com.epam.drill.agent.configuration.AgentConfigurationProvider +import com.epam.drill.agent.configuration.AgentProcessMetadata + +class EnvironmentVariablesProvider( + override val priority: Int = 500 +) : AgentConfigurationProvider { + + override val configuration = configuration() + + private fun configuration() = runCatching(AgentProcessMetadata::environmentVars::get).getOrNull() + ?.let(::parseKeys) + ?: emptyMap() + + internal fun parseKeys(vars: Map) = vars + .filterKeys { it.startsWith("DRILL_") } + .mapKeys(::toParameterName) + + internal fun toParameterName(entry: Map.Entry) = entry.key + .removePrefix("DRILL_") + .lowercase() + .split("_") + .joinToString("") { it.replaceFirstChar(Char::uppercase) } + .replaceFirstChar(Char::lowercase) + +} diff --git a/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt b/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt new file mode 100644 index 00000000..acc361d2 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt @@ -0,0 +1,23 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +actual object AgentProcessMetadata { + actual val commandLine: String + get() = throw NotImplementedError() + actual val environmentVars: Map + get() = System.getenv() +} diff --git a/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt b/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt new file mode 100644 index 00000000..d5b38031 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt @@ -0,0 +1,43 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentMetadata +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.configuration.AgentType + +actual class DefaultAgentConfiguration( + private val _inputParameters: Map +) : AgentConfiguration { + + actual override val parameters: AgentParameters = DefaultAgentParameters(_inputParameters) + actual override val agentMetadata by lazy { agentMetadata() } + + actual val inputParameters: Map + get() = _inputParameters.toMap() + + private fun agentMetadata() = AgentMetadata( + groupId = parameters[DefaultParameterDefinitions.GROUP_ID], + appId = parameters[DefaultParameterDefinitions.APP_ID] ?: "", + buildVersion = parameters[DefaultParameterDefinitions.BUILD_VERSION] ?: "", + commitSha = parameters[DefaultParameterDefinitions.COMMIT_SHA] ?: "", + envId = parameters[DefaultParameterDefinitions.ENV_ID] ?: "", + instanceId = parameters[DefaultParameterDefinitions.INSTANCE_ID] ?: "", + packagesPrefixes = parameters[DefaultParameterDefinitions.PACKAGE_PREFIXES] as List + ) + +} diff --git a/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt b/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt new file mode 100644 index 00000000..f3bf0b15 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/jvmMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt @@ -0,0 +1,129 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlin.reflect.KProperty +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.configuration.BaseAgentParameterDefinition +import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition +import com.epam.drill.agent.common.configuration.ValidationError +import com.epam.drill.agent.konform.validation.Invalid +import com.epam.drill.agent.konform.validation.Valid +import com.epam.drill.agent.konform.validation.ValidationResult +import mu.KotlinLogging +import kotlin.let + +actual class DefaultAgentParameters actual constructor( + private val inputParameters: Map +) : AgentParameters { + private val logger = KotlinLogging.logger {} + private val definedParameters = mutableMapOf() + private val parameterDefinitions = mutableMapOf>() + private val validationErrors = mutableMapOf>() + + @Suppress("UNCHECKED_CAST") + actual override operator fun get(name: String): T? = + definedParameters[name] as T? + + @Suppress("UNCHECKED_CAST") + actual override operator fun get(definition: AgentParameterDefinition): T { + if (!parameterDefinitions.containsKey(definition.name)) define(definition) + return definedParameters[definition.name] as T? + ?: validationErrors[definition.name]?.let { + throw IllegalArgumentException( + "Parameter '${definition.name}' has validation errors: \n" + + it.messages.joinToString("\n") + ) + } + ?: throw IllegalArgumentException( + "Parameter '${definition.name}' has no value." + ) + } + + @Suppress("UNCHECKED_CAST") + actual override operator fun getValue(ref: Any?, property: KProperty<*>): T? = + definedParameters[property.name] as T? + + @Suppress("UNCHECKED_CAST") + actual override fun get(definition: NullableAgentParameterDefinition): T? { + if (!parameterDefinitions.containsKey(definition.name)) define(definition) + return definedParameters[definition.name] as T? + } + + actual override fun define(vararg definitions: BaseAgentParameterDefinition<*>): List> { + val errors = mutableListOf>() + definitions.forEach { def -> + if (parameterDefinitions.containsKey(def.name)) { + validationErrors[def.name]?.also { + errors.add(it) + } + return@forEach + } + parameterDefinitions[def.name] = def + definedParameters[def.name] = (inputParameters[def.name] + ?.runCatching(def.parser) + ?.getOrNull() + ?.let { softValidate(it, def, errors) } + ?: (def as? AgentParameterDefinition)?.defaultValue) + .let { strictValidate(it, def, errors) } + errors.firstOrNull { it.definition.name == def.name }?.also { + validationErrors[def.name] = it + } ?: validationErrors.remove(def.name) + } + return errors + } + + private fun strictValidate( + value: Any?, + definition: BaseAgentParameterDefinition<*>, + errors: MutableList> = mutableListOf() + ): Any? = if (definition.isStrictValidation()) validate(value, definition, errors) else value + + private fun softValidate( + value: Any?, + definition: BaseAgentParameterDefinition<*>, + errors: MutableList> = mutableListOf() + ): Any? = if (definition.isSoftValidation()) validate(value, definition, errors) else value + + private fun validate( + value: Any?, + definition: BaseAgentParameterDefinition<*>, + errors: MutableList> = mutableListOf() + ): Any? { + val result = safeValidate(value, definition.validator) + when (result) { + is Invalid -> { + errors.add(ValidationError(definition, result.errors.map { it.message })) + logger.debug { "Validation failed for parameter '${definition.name}': ${result.errors.map { it.message }}" } + return null + } + + is Valid -> { + return value + } + } + } + + @Suppress("UNCHECKED_CAST") + private fun safeValidate( + value: Any?, + validator: (T?, AgentParameters) -> ValidationResult<*> + ): ValidationResult<*> { + val typedValue = value as T? + return validator(typedValue, this) + } +} diff --git a/lib-jvm-shared/agent-config/src/linuxX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt b/lib-jvm-shared/agent-config/src/linuxX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt new file mode 100644 index 00000000..97edd03c --- /dev/null +++ b/lib-jvm-shared/agent-config/src/linuxX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt @@ -0,0 +1,50 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlinx.cinterop.memScoped +import platform.posix.O_RDONLY +import platform.posix.close +import platform.posix.open +import platform.posix.pclose +import platform.posix.popen +import io.ktor.utils.io.core.readText +import io.ktor.utils.io.streams.Input +import kotlinx.cinterop.ExperimentalForeignApi + +actual object AgentProcessMetadata { + + actual val commandLine: String + get() = commandLine() + + actual val environmentVars: Map + get() = environmentVars() + + @OptIn(ExperimentalForeignApi::class) + private fun commandLine() = memScoped { + val file = open("/proc/self/cmdline", O_RDONLY) + Input(file).readText().also { close(file) } + } + + @OptIn(ExperimentalForeignApi::class) + private fun environmentVars() = memScoped { + val file = popen("env", "r")!! + Input(file).readText().also { pclose(file) } + .lines().filter(String::isNotEmpty) + .associate { it.substringBefore("=") to it.substringAfter("=", "") } + } + +} diff --git a/lib-jvm-shared/agent-config/src/macosArm64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt b/lib-jvm-shared/agent-config/src/macosArm64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt new file mode 100644 index 00000000..31b74aaa --- /dev/null +++ b/lib-jvm-shared/agent-config/src/macosArm64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt @@ -0,0 +1,50 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlinx.cinterop.memScoped +import platform.posix.O_RDONLY +import platform.posix.close +import platform.posix.open +import platform.posix.pclose +import platform.posix.popen +import io.ktor.utils.io.core.readText +import io.ktor.utils.io.streams.Input +import kotlinx.cinterop.ExperimentalForeignApi + +actual object AgentProcessMetadata { + + actual val commandLine: String + get() = commandLine() + + actual val environmentVars: Map + get() = environmentVars() + + @OptIn(ExperimentalForeignApi::class) + private fun commandLine() = memScoped { + val file = open("/proc/self/cmdline", O_RDONLY) + Input(file).readText().also { close(file) } + } + + @OptIn(ExperimentalForeignApi::class) + private fun environmentVars() = memScoped { + val file = popen("env", "r")!! + Input(file).readText().also { pclose(file) } + .lines().filter(String::isNotEmpty) + .associate { it.substringBefore("=") to it.substringAfter("=", "") } + } + +} diff --git a/lib-jvm-shared/agent-config/src/macosX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt b/lib-jvm-shared/agent-config/src/macosX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt new file mode 100644 index 00000000..97edd03c --- /dev/null +++ b/lib-jvm-shared/agent-config/src/macosX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt @@ -0,0 +1,50 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlinx.cinterop.memScoped +import platform.posix.O_RDONLY +import platform.posix.close +import platform.posix.open +import platform.posix.pclose +import platform.posix.popen +import io.ktor.utils.io.core.readText +import io.ktor.utils.io.streams.Input +import kotlinx.cinterop.ExperimentalForeignApi + +actual object AgentProcessMetadata { + + actual val commandLine: String + get() = commandLine() + + actual val environmentVars: Map + get() = environmentVars() + + @OptIn(ExperimentalForeignApi::class) + private fun commandLine() = memScoped { + val file = open("/proc/self/cmdline", O_RDONLY) + Input(file).readText().also { close(file) } + } + + @OptIn(ExperimentalForeignApi::class) + private fun environmentVars() = memScoped { + val file = popen("env", "r")!! + Input(file).readText().also { pclose(file) } + .lines().filter(String::isNotEmpty) + .associate { it.substringBefore("=") to it.substringAfter("=", "") } + } + +} diff --git a/lib-jvm-shared/agent-config/src/mingwX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt b/lib-jvm-shared/agent-config/src/mingwX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt new file mode 100644 index 00000000..8da8c3ca --- /dev/null +++ b/lib-jvm-shared/agent-config/src/mingwX64Main/kotlin/com/epam/drill/agent/configuration/AgentProcessMetadata.kt @@ -0,0 +1,55 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlinx.cinterop.cstr +import kotlinx.cinterop.invoke +import kotlinx.cinterop.memScoped +import platform.posix.getpid +import platform.posix.pclose +import platform.posix.popen +import io.ktor.utils.io.core.readText +import io.ktor.utils.io.streams.Input +import kotlinx.cinterop.ExperimentalForeignApi + +actual object AgentProcessMetadata { + + actual val commandLine: String + get() = commandLine() + + actual val environmentVars: Map + get() = environmentVars() + + @OptIn(ExperimentalForeignApi::class) + private fun commandLine() = memScoped { + val pid = getpid() + executeCommand("wmic process where \"processid='$pid'\" get commandline", "r").lines()[1] + } + + @OptIn(ExperimentalForeignApi::class) + private fun environmentVars() = memScoped { + executeCommand("set", "r").lines() + .filter(String::isNotEmpty) + .associate { it.substringBefore("=") to it.substringAfter("=", "") } + } + + @OptIn(ExperimentalForeignApi::class) + private fun executeCommand(command: String, mode: String) = memScoped { + val file = popen!!.invoke(command.cstr.getPointer(this), mode.cstr.getPointer(this))!! + Input(file).readText().also { pclose!!.invoke(file) } + } + +} diff --git a/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt new file mode 100644 index 00000000..89972118 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentConfiguration.kt @@ -0,0 +1,65 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentMetadata +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.configuration.AgentType + +actual class DefaultAgentConfiguration( + private val configurationProviders: Set +) : AgentConfiguration { + + private val _inputParameters = inputParameters() + + actual override val parameters: AgentParameters = DefaultAgentParameters(_inputParameters).also(::defineDefaults) + actual override val agentMetadata by lazy { agentMetadata() } + + actual val inputParameters: Map + get() = _inputParameters.toMap() + + private fun inputParameters() = configurationProviders + .sortedBy(AgentConfigurationProvider::priority) + .map(AgentConfigurationProvider::configuration) + .reduce { acc, map -> acc + map } + + private fun defineDefaults(agentParameters: AgentParameters) { + agentParameters.define( + DefaultParameterDefinitions.APP_ID, + DefaultParameterDefinitions.INSTANCE_ID, + DefaultParameterDefinitions.BUILD_VERSION, + DefaultParameterDefinitions.GROUP_ID, + DefaultParameterDefinitions.COMMIT_SHA, + DefaultParameterDefinitions.ENV_ID, + DefaultParameterDefinitions.INSTALLATION_DIR, + DefaultParameterDefinitions.CONFIG_PATH + ) + agentParameters.define(DefaultParameterDefinitions.PACKAGE_PREFIXES) + } + + @Suppress("UNCHECKED_CAST") + private fun agentMetadata() = AgentMetadata( + groupId = parameters[DefaultParameterDefinitions.GROUP_ID], + appId = parameters[DefaultParameterDefinitions.APP_ID] ?: "", + buildVersion = parameters[DefaultParameterDefinitions.BUILD_VERSION], + commitSha = parameters[DefaultParameterDefinitions.COMMIT_SHA], + envId = parameters[DefaultParameterDefinitions.ENV_ID], + instanceId = parameters[DefaultParameterDefinitions.INSTANCE_ID] ?: "", + packagesPrefixes = parameters[DefaultParameterDefinitions.PACKAGE_PREFIXES] as List + ) + +} diff --git a/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt new file mode 100644 index 00000000..61fda132 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/DefaultAgentParameters.kt @@ -0,0 +1,139 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlin.concurrent.AtomicReference +import kotlin.native.concurrent.freeze +import kotlin.reflect.KProperty +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.configuration.BaseAgentParameterDefinition +import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition +import com.epam.drill.agent.common.configuration.ValidationError +import com.epam.drill.agent.konform.validation.Invalid +import com.epam.drill.agent.konform.validation.Valid +import com.epam.drill.agent.konform.validation.ValidationResult +import mu.KotlinLogging +import kotlin.collections.set + +actual class DefaultAgentParameters actual constructor( + private val inputParameters: Map +) : AgentParameters { + private val logger = KotlinLogging.logger("com.epam.drill.agent.configuration.DefaultAgentParameters") + private val definedParameters = AtomicReference(mapOf()) + private val parameterDefinitions = AtomicReference(mapOf>()) + private val validationErrors = AtomicReference(mapOf>()) + + @Suppress("UNCHECKED_CAST") + actual override operator fun get(name: String): T? = + definedParameters.value[name] as T? + + @Suppress("UNCHECKED_CAST") + actual override operator fun get(definition: AgentParameterDefinition): T { + if (!parameterDefinitions.value.containsKey(definition.name)) define(definition) + return definedParameters.value[definition.name] as T? + ?: validationErrors.value[definition.name]?.let { + throw IllegalArgumentException( + "Parameter '${definition.name}' has validation errors: \n" + + it.messages.joinToString("\n") + ) + } + ?: throw IllegalArgumentException( + "Parameter '${definition.name}' has no value." + ) + } + + @Suppress("UNCHECKED_CAST") + actual override operator fun getValue(ref: Any?, property: KProperty<*>): T? = + definedParameters.value[property.name] as T? + + + actual override fun get(definition: NullableAgentParameterDefinition): T? { + if (!parameterDefinitions.value.containsKey(definition.name)) define(definition) + return definedParameters.value[definition.name] as T? + } + + actual override fun define(vararg definitions: BaseAgentParameterDefinition): List> { + val updatedDefinitions = parameterDefinitions.value.toMutableMap() + val updatedParameters = definedParameters.value.toMutableMap() + val updatedValidationErrors = validationErrors.value.toMutableMap() + val errors = mutableListOf>() + definitions.forEach { def -> + if (updatedDefinitions.containsKey(def.name)) { + updatedValidationErrors[def.name]?.also { + errors.add(it) + } + return@forEach + } + updatedDefinitions[def.name] = def + updatedParameters[def.name] = (inputParameters[def.name] + ?.runCatching(def.parser) + ?.getOrNull() + ?.let { softValidate(it, def, errors) } + ?: (def as? AgentParameterDefinition)?.defaultValue) + .let { strictValidate(it, def, errors) } + errors.firstOrNull { it.definition.name == def.name }?.also { + updatedValidationErrors[def.name] = it + } ?: updatedValidationErrors.remove(def.name) + } + parameterDefinitions.value = updatedDefinitions.freeze() + definedParameters.value = updatedParameters.freeze() + validationErrors.value = updatedValidationErrors.freeze() + return errors + } + + private fun strictValidate( + value: Any?, + definition: BaseAgentParameterDefinition<*>, + errors: MutableList> = mutableListOf() + ): Any? = if (definition.isStrictValidation()) validate(value, definition, errors) else value + + private fun softValidate( + value: Any, + definition: BaseAgentParameterDefinition<*>, + errors: MutableList> = mutableListOf() + ): Any? = if (definition.isSoftValidation()) validate(value, definition, errors) else value + + private fun validate( + value: Any?, + definition: BaseAgentParameterDefinition<*>, + errors: MutableList> = mutableListOf() + ): Any? { + val result = safeValidate(value, definition.validator) + when (result) { + is Invalid -> { + val error = ValidationError(definition, result.errors.map { it.message }) + validationErrors.value + errors.add(error) + logger.debug { "Validation failed for parameter '${definition.name}': ${result.errors.map { it.message }}" } + return null + } + + is Valid -> { + return value + } + } + } + + @Suppress("UNCHECKED_CAST") + private fun safeValidate( + value: Any?, + validator: (T?, AgentParameters) -> ValidationResult<*> + ): ValidationResult<*> { + val typedValue = value as T? + return validator(typedValue, this) + } +} diff --git a/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/provider/InstallationDirProvider.kt b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/provider/InstallationDirProvider.kt new file mode 100644 index 00000000..386647d3 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/provider/InstallationDirProvider.kt @@ -0,0 +1,56 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import kotlinx.cinterop.toKString +import platform.posix.getenv +import com.epam.drill.agent.configuration.AgentConfigurationProvider +import com.epam.drill.agent.configuration.AgentProcessMetadata +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import kotlinx.cinterop.ExperimentalForeignApi + +class InstallationDirProvider( + private val configurationProviders: Set, + override val priority: Int = 300, + agentLibName: String = "drill-agent" +) : AgentConfigurationProvider { + + private val agentLibNamePattern = "(lib)?${agentLibName.replace("-", "_")}(\\.so|\\.dll|\\.dylib)" + private val agentLibPathRegex = Regex("[\"]?(.*[/\\\\])?$agentLibNamePattern([ =\"].*)?") + + override val configuration = mapOf(Pair(DefaultParameterDefinitions.INSTALLATION_DIR.name, installationDir())) + + private fun installationDir() = fromProviders() + ?: fromJavaToolOptions() + ?: fromCommandLine() + ?: "." + + internal fun fromProviders() = configurationProviders + .sortedBy(AgentConfigurationProvider::priority) + .mapNotNull { it.configuration[DefaultParameterDefinitions.INSTALLATION_DIR.name] } + .lastOrNull() + + @OptIn(ExperimentalForeignApi::class) + private fun fromJavaToolOptions() = getenv("JAVA_TOOL_OPTIONS")?.toKString()?.let(::parse) + + private fun fromCommandLine() = runCatching(AgentProcessMetadata::commandLine::get).getOrNull()?.let(::parse) + + internal fun parse(value: String) = value.split("-agentpath:").drop(1).map(String::trim) + .find { it.matches(agentLibPathRegex) } + ?.let { agentLibPathRegex.matchEntire(it)!!.groupValues[1] } + ?.let { it.takeIf("/"::equals) ?: it.removeSuffix("/").removeSuffix("\\").takeIf(String::isNotEmpty) ?: "." } + +} diff --git a/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/provider/PropertiesFileProvider.kt b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/provider/PropertiesFileProvider.kt new file mode 100644 index 00000000..e0af68dd --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeMain/kotlin/com/epam/drill/agent/configuration/provider/PropertiesFileProvider.kt @@ -0,0 +1,80 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import kotlinx.cinterop.memScoped +import platform.posix.O_RDONLY +import platform.posix.close +import platform.posix.open +import io.ktor.utils.io.core.readText +import io.ktor.utils.io.streams.Input +import com.epam.drill.agent.configuration.AgentConfigurationProvider +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import kotlinx.cinterop.ExperimentalForeignApi +import kotlin.experimental.ExperimentalNativeApi + +class PropertiesFileProvider( + private val configurationProviders: Set, + override val priority: Int = 200 +) : AgentConfigurationProvider { + + @OptIn(ExperimentalNativeApi::class) + private val pathSeparator = if (Platform.osFamily == OsFamily.WINDOWS) "\\" else "/" + private val defaultPath = ".${pathSeparator}drill.properties" + + override val configuration = configuration() + + private fun configuration() = configPath().runCatching(::readFile).getOrNull() + ?.let(::parseLines) + ?: emptyMap() + + @OptIn(ExperimentalForeignApi::class) + private fun readFile(filepath: String) = memScoped { + val file = open(filepath, O_RDONLY) + Input(file).readText().also { close(file) } + } + + internal fun parseLines(text: String): Map { + return text + // normalize line endings + .replace("\r\n", "\n").replace("\r", "\n") + // remove comment lines + .replace(Regex("""(?<=\n)\s*#.*\n"""), "") + // compact multiline values into corresponding single lines + // regex matches backslash at the end of line surrounded by any number of whitespaces + .replace(Regex("""\s*\\\s*\n"""), "") + .lines() + .map(String::trim) + .filter { it.isNotEmpty() } + .associate { it.substringBefore("=") to it.substringAfter("=", "") } + } + + internal fun configPath() = fromProviders() + ?: fromInstallationDir() + ?: defaultPath + + internal fun fromProviders() = configurationProviders + .sortedBy(AgentConfigurationProvider::priority) + .mapNotNull { it.configuration[DefaultParameterDefinitions.CONFIG_PATH.name] } + .lastOrNull() + + internal fun fromInstallationDir() = configurationProviders + .sortedBy(AgentConfigurationProvider::priority) + .mapNotNull { it.configuration[DefaultParameterDefinitions.INSTALLATION_DIR.name] } + .lastOrNull() + ?.let { "${it}${pathSeparator}drill.properties" } + +} diff --git a/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/AgentOptionsProviderTest.kt b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/AgentOptionsProviderTest.kt new file mode 100644 index 00000000..613f98aa --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/AgentOptionsProviderTest.kt @@ -0,0 +1,62 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import kotlin.test.Test +import kotlin.test.assertEquals + +class AgentOptionsProviderTest { + + @Test + fun `null options`() { + val result = AgentOptionsProvider(null).configuration + assertEquals(0, result.size) + } + + @Test + fun `empty options`() { + val result = AgentOptionsProvider("").configuration + assertEquals(0, result.size) + } + + @Test + fun `simple options`() { + val result = AgentOptionsProvider("option1=value1,option2=value2").configuration + assertEquals(2, result.size) + assertEquals("value1", result["option1"]) + assertEquals("value2", result["option2"]) + } + + @Test + fun `options with separators`() { + val result = AgentOptionsProvider("option1=value1,option2=value2;value3;value4,,option3=value3,").configuration + assertEquals(3, result.size) + assertEquals("value1", result["option1"]) + assertEquals("value2;value3;value4", result["option2"]) + assertEquals("value3", result["option3"]) + } + + @Test + fun `options with equals`() { + val result = AgentOptionsProvider("option1=value1,option2=opt1=val1;opt2=val2,option3=opt3=;opt4=val4,option4=opt5=val5;opt6=").configuration + assertEquals(4, result.size) + assertEquals("value1", result["option1"]) + assertEquals("opt1=val1;opt2=val2", result["option2"]) + assertEquals("opt3=;opt4=val4", result["option3"]) + assertEquals("opt5=val5;opt6=", result["option4"]) + } + +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/EnvironmentVariablesProviderTest.kt b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/EnvironmentVariablesProviderTest.kt new file mode 100644 index 00000000..a079ab20 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/EnvironmentVariablesProviderTest.kt @@ -0,0 +1,70 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import kotlin.test.Test +import kotlin.test.assertEquals + +class EnvironmentVariablesProviderTest { + + @Test + fun `to parameter name upper case`() { + val result = EnvironmentVariablesProvider().toParameterName(SimpleEntry("DRILL_PARAM_NAME", "")) + assertEquals("paramName", result) + } + + @Test + fun `to parameter name lower case`() { + val result = EnvironmentVariablesProvider().toParameterName(SimpleEntry("DRILL_param_name", "")) + assertEquals("paramName", result) + } + + @Test + fun `to parameter name mixed case`() { + val result = EnvironmentVariablesProvider().toParameterName(SimpleEntry("DRILL_Param_name_foO_bAr", "")) + assertEquals("paramNameFooBar", result) + } + + @Test + fun `to parameter name underscores`() { + val result = EnvironmentVariablesProvider().toParameterName(SimpleEntry("DRILL_PARAM_NAME__FOO_BAR", "")) + assertEquals("paramNameFooBar", result) + } + + @Test + fun `parse keys empty`() { + val result = EnvironmentVariablesProvider().parseKeys(emptyMap()) + assertEquals(0, result.size) + } + + @Test + fun `parse keys mixed`() { + val result = EnvironmentVariablesProvider().parseKeys(mapOf( + "SOME_VAR" to "value", + "DRILL_PARAM" to "drillValue", + "DRILL_PARAM_TWO" to "drillValueTwo" + )) + assertEquals(2, result.size) + assertEquals("drillValue", result["param"]) + assertEquals("drillValueTwo", result["paramTwo"]) + } + + private class SimpleEntry( + override val key: String, + override val value: String + ) : Map.Entry + +} diff --git a/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/InstallationDirProviderTest.kt b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/InstallationDirProviderTest.kt new file mode 100644 index 00000000..79a8d198 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/InstallationDirProviderTest.kt @@ -0,0 +1,167 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import com.epam.drill.agent.configuration.AgentConfigurationProvider +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import kotlin.experimental.ExperimentalNativeApi + +class InstallationDirProviderTest { + + @OptIn(ExperimentalNativeApi::class) + private val separator = if (Platform.osFamily == OsFamily.WINDOWS) "\\" else "/" + @OptIn(ExperimentalNativeApi::class) + private val libName = if (Platform.osFamily == OsFamily.WINDOWS) "drill_agent.dll" else "libdrill_agent.so" + @OptIn(ExperimentalNativeApi::class) + private val fullPath = if (Platform.osFamily == OsFamily.WINDOWS) "C:\\data\\agent" else "/data/agent" + @OptIn(ExperimentalNativeApi::class) + private val rootPath = if (Platform.osFamily == OsFamily.WINDOWS) "C:" else "/" + @OptIn(ExperimentalNativeApi::class) + private val rootLibPath = if (Platform.osFamily == OsFamily.WINDOWS) "C:\\drill_agent.dll" else "/libdrill_agent.so" + + @Test + fun `parse platform specific style path`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:$fullPath$separator$libName=param1=1,param2=2") + assertEquals(fullPath, result) + } + + @Test + fun `parse with root directory`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:$rootLibPath") + assertEquals(rootPath, result) + } + + @Test + fun `parse without options`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:/data/agent/$libName") + assertEquals("/data/agent", result) + } + + @Test + fun `parse with equal separator without options`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:/data/agent/$libName=") + assertEquals("/data/agent", result) + } + + @Test + fun `parse with space separator without options`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:/data/agent/$libName -Dparam=foo/bar") + assertEquals("/data/agent", result) + } + + @Test + fun `parse with options`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:/data/agent/$libName=opt1,opt2") + assertEquals("/data/agent", result) + } + + @Test + fun `parse without leading directory separator`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:$libName") + assertEquals(".", result) + } + + @Test + fun `parse with other arguments`() { + val result = InstallationDirProvider(emptySet()) + .parse("-Dparam=foo/bar -agentpath:/data/agent/$libName /other/path") + assertEquals("/data/agent", result) + } + + @Test + fun `parse with quotes`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath:\"/data/agent/$libName=opt1,opt2\"") + assertEquals("/data/agent", result) + } + + @Test + fun `parse with quotes and spaces`() { + val result = InstallationDirProvider(emptySet()) + .parse("-agentpath: \"/data space/agent/$libName\" /other/path") + assertEquals("/data space/agent", result) + } + + @Test + fun `parse with multi lines`() { + val result = InstallationDirProvider(emptySet()).parse( + """ + java -jar some.jar + + -agentpath:/data/agent/$libName + """ + ) + assertEquals("/data/agent", result) + } + + @Test + fun `parse with multi agents`() { + val result = InstallationDirProvider(emptySet()).parse( + """ + -agentpath:/other/path/other_agent.dll + -agentpath:/data/agent/$libName + -agentpath:/another/path/libanother_agent.so + """.trimIndent() + ) + assertEquals("/data/agent", result) + } + + @Test + fun `from providers empty providers`() { + val result = InstallationDirProvider(emptySet()).fromProviders() + assertNull(result) + } + + @Test + fun `from providers no entry`() { + val provider1 = SimpleMapProvider(mapOf("foo" to "bar1")) + val provider2 = SimpleMapProvider(mapOf("foo" to "bar2")) + val result = InstallationDirProvider(setOf(provider1, provider2)).fromProviders() + assertNull(result) + } + + @Test + fun `from providers one entry`() { + val provider1 = SimpleMapProvider(mapOf("foo" to "bar1")) + val provider2 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.INSTALLATION_DIR.name to "/foo/bar")) + val result = InstallationDirProvider(setOf(provider1, provider2)).fromProviders() + assertEquals("/foo/bar", result) + } + + @Test + fun `from providers two entry prioritized`() { + val provider1 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.INSTALLATION_DIR.name to "/foo/bar1"), 100) + val provider2 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.INSTALLATION_DIR.name to "/foo/bar2"), 200) + val result = InstallationDirProvider(setOf(provider1, provider2)).fromProviders() + assertEquals("/foo/bar2", result) + } + + private class SimpleMapProvider( + override val configuration: Map, + override val priority: Int = 100 + ) : AgentConfigurationProvider + +} diff --git a/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/PropertiesFileProviderTest.kt b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/PropertiesFileProviderTest.kt new file mode 100644 index 00000000..39a28834 --- /dev/null +++ b/lib-jvm-shared/agent-config/src/nativeTest/kotlin/com/epam/drill/agent/configuration/provider/PropertiesFileProviderTest.kt @@ -0,0 +1,251 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration.provider + +import com.epam.drill.agent.configuration.AgentConfigurationProvider +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import kotlin.experimental.ExperimentalNativeApi +import kotlin.test.* + +class PropertiesFileProviderTest { + + @OptIn(ExperimentalNativeApi::class) + private val separator = if (Platform.osFamily == OsFamily.WINDOWS) "\\" else "/" + @OptIn(ExperimentalNativeApi::class) + private val fullPath = if (Platform.osFamily == OsFamily.WINDOWS) "C:\\data\\agent" else "/data/agent" + @OptIn(ExperimentalNativeApi::class) + private val testPath1 = if (Platform.osFamily == OsFamily.WINDOWS) "C:\\data1" else "/data1" + @OptIn(ExperimentalNativeApi::class) + private val testPath2 = if (Platform.osFamily == OsFamily.WINDOWS) "C:\\data2" else "/data2" + + @Test + fun `parse empty lines`() { + val result = PropertiesFileProvider(emptySet()).parseLines("") + assertTrue(result.isEmpty()) + } + + @Test + fun `parse mixed lines`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo1=bar1 + #foo2=bar2 + + foo3=bar3 + #foo4=bar4 + """) + assertEquals(2, result.size) + assertEquals("bar1", result["foo1"]) + assertEquals("bar3", result["foo3"]) + } + + @Test + fun `parse multiline properties`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo1=fiz\ + buz + """.trimIndent()) + assertEquals(1, result.size) + assertEquals("fizbuz", result["foo1"]) + } + + @Test + fun `parse multiline properties - with spaces around backslash`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo1=fiz \ + buz \ + fuz + """.trimIndent()) + assertEquals(1, result.size) + assertEquals("fizbuzfuz", result["foo1"]) + } + + @Test + fun `parse multiline properties - with other properties around`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo0=bar0 + + foo1=fiz\ + buz\ + fuz + + foo2=bar2 + """.trimIndent()) + assertEquals(3, result.size) + assertEquals("bar0", result["foo0"]) + assertEquals("fizbuzfuz", result["foo1"]) + assertEquals("bar2", result["foo2"]) + } + + // Ignored - we indent around multiline values does break parsers for now + @Test @Ignore + fun `parse multiline properties - with indent around values`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo0=bar0 + + foo1=fiz\ + buz\ + fuz + + foo2=bar2 + """) + assertEquals(3, result.size) + assertEquals("bar0", result["foo0"]) + assertEquals("fizbuzfuz", result["foo1"]) + assertEquals("bar2", result["foo2"]) + } + + @Test + fun `parse lines with comments containing backslashes`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo1=bar1 + foo2=bar2 + # \ must not break parsing + foo3=bar3 + """) + assertEquals("bar1", result["foo1"]) + assertEquals("bar2", result["foo2"]) + assertEquals("bar3", result["foo3"]) + assertEquals(3, result.size) + } + + @Test + fun `hash symbol at the beginning of the line is a comment`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + # this is a comment + foo1=bar1 + """) + assertEquals("bar1", result["foo1"]) + assertEquals(1, result.size) + } + + @Test + fun `hash symbol after = must be treated as part of a value`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo1=bar1#baz + """) + assertEquals("bar1#baz", result["foo1"]) + assertEquals(1, result.size) + } + +// TODO for now we allow hash symbol in a property name, it does not break the parsing +// there might be some edge cases to that + @Test @Ignore + fun `unescaped hash symbol is illegal in a property name`() { + assertFailsWith { + PropertiesFileProvider(emptySet()).parseLines(""" + foo1#bar=baz + """) + } + } + + @Test @Ignore + fun `escaped hash symbol is allowed in a property name`() { + val result = PropertiesFileProvider(emptySet()).parseLines(""" + foo1\#bar=baz + """) + assertEquals("baz", result["foo1bar"]) + assertEquals(1, result.size) + } + + @Test + fun `config path default`() { + val result = PropertiesFileProvider(emptySet()).configPath() + assertEquals(".${separator}drill.properties", result) + } + + @Test + fun `config path from installation dir`() { + val result = PropertiesFileProvider(setOf(SimpleMapProvider(mapOf( + DefaultParameterDefinitions.INSTALLATION_DIR.name to fullPath + )))).configPath() + assertEquals("$fullPath${separator}drill.properties", result) + } + + @Test + fun `config path from property`() { + val result = PropertiesFileProvider(setOf(SimpleMapProvider(mapOf( + DefaultParameterDefinitions.INSTALLATION_DIR.name to fullPath, + DefaultParameterDefinitions.CONFIG_PATH.name to "$fullPath${separator}config.properties" + )))).configPath() + assertEquals("$fullPath${separator}config.properties", result) + } + + @Test + fun `from providers empty providers`() { + val result = PropertiesFileProvider(emptySet()).fromProviders() + assertNull(result) + } + + @Test + fun `from providers no entry`() { + val provider1 = SimpleMapProvider(mapOf("foo" to "bar1")) + val provider2 = SimpleMapProvider(mapOf("foo" to "bar2")) + val result = PropertiesFileProvider(setOf(provider1, provider2)).fromProviders() + assertNull(result) + } + + @Test + fun `from providers one entry`() { + val provider1 = SimpleMapProvider(mapOf("foo" to "bar1")) + val provider2 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.CONFIG_PATH.name to "/config1.properties")) + val result = PropertiesFileProvider(setOf(provider1, provider2)).fromProviders() + assertEquals("/config1.properties", result) + } + + @Test + fun `from providers two entry prioritized`() { + val provider1 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.CONFIG_PATH.name to "/config1.properties"), 100) + val provider2 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.CONFIG_PATH.name to "/config2.properties"), 200) + val result = PropertiesFileProvider(setOf(provider1, provider2)).fromProviders() + assertEquals("/config2.properties", result) + } + + @Test + fun `from installation dir empty providers`() { + val result = PropertiesFileProvider(emptySet()).fromInstallationDir() + assertNull(result) + } + + @Test + fun `from installation dir no entry`() { + val provider1 = SimpleMapProvider(mapOf("foo" to "bar1")) + val provider2 = SimpleMapProvider(mapOf("foo" to "bar2")) + val result = PropertiesFileProvider(setOf(provider1, provider2)).fromInstallationDir() + assertNull(result) + } + + @Test + fun `from installation dir one entry`() { + val provider1 = SimpleMapProvider(mapOf("foo" to "bar1")) + val provider2 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.INSTALLATION_DIR.name to testPath1)) + val result = PropertiesFileProvider(setOf(provider1, provider2)).fromInstallationDir() + assertEquals("$testPath1${separator}drill.properties", result) + } + + @Test + fun `from installation dir two entry prioritized`() { + val provider1 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.INSTALLATION_DIR.name to testPath1), 100) + val provider2 = SimpleMapProvider(mapOf(DefaultParameterDefinitions.INSTALLATION_DIR.name to testPath2), 200) + val result = PropertiesFileProvider(setOf(provider1, provider2)).fromInstallationDir() + assertEquals("$testPath2${separator}drill.properties", result) + } + + private class SimpleMapProvider( + override val configuration: Map, + override val priority: Int = 100 + ) : AgentConfigurationProvider + +} diff --git a/lib-jvm-shared/agent-instrumentation/LICENSE b/lib-jvm-shared/agent-instrumentation/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/agent-instrumentation/README.md b/lib-jvm-shared/agent-instrumentation/README.md new file mode 100644 index 00000000..d337e7fa --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/README.md @@ -0,0 +1,15 @@ +# http-clients-instrumentation + +This repository contains common instrumentation for http clients such as +
  • Apache Client
  • +
  • OkHttpClient
  • +
  • JavaHttpClient
  • + +## What does instrumentation do + +Drill headers are added for every request sent using one of the http clients.
    +Also when receiving http requests, we save the drill headers. + +## Where library is using +- [Java Agent](https://github.com/Drill4J/java-agent) +- [Autotest Agent](https://github.com/Drill4J/autotest-agent) diff --git a/lib-jvm-shared/agent-instrumentation/build.gradle.kts b/lib-jvm-shared/agent-instrumentation/build.gradle.kts new file mode 100644 index 00000000..ee2e4fb9 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/build.gradle.kts @@ -0,0 +1,217 @@ +import java.net.URI +import java.util.Properties +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.presetName +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + id("com.github.johnrengelman.shadow") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} + +val javassistVersion: String by parent!!.extra +val transmittableThreadLocalVersion: String by parent!!.extra +val bytebuddyVersion: String by parent!!.extra +val nativeAgentLibName: String by parent!!.extra +val macosLd64: String by parent!!.extra +val kotlinxSerializationVersion: String by parent!!.extra +val kotlinxCollectionsVersion: String by parent!!.extra +val microutilsLoggingVersion: String by parent!!.extra +val atomicfuVersion: String by parent!!.extra +val ktorVersion: String by parent!!.extra + +repositories { + mavenCentral() +} + +kotlin { + val configureIntTestTarget: KotlinTarget.() -> Unit = { + compilations.create("intTest").associateWith(compilations["main"]) + (this as? KotlinNativeTarget)?.binaries?.sharedLib(nativeAgentLibName, setOf(DEBUG)) { + compilation = compilations["intTest"] + } + } + jvm(configure = configureIntTestTarget) + linuxX64(configure = configureIntTestTarget) + macosX64(configure = configureIntTestTarget).apply { + if (macosLd64.toBoolean()) { + binaries.all { + linkerOpts("-ld64") + } + } + } + macosArm64(configure = configureIntTestTarget).apply { + if (macosLd64.toBoolean()) { + binaries.all { + linkerOpts("-ld64") + } + } + } + mingwX64(configure = configureIntTestTarget).apply { + binaries.all { + linkerOpts("-lpsapi", "-lwsock32", "-lws2_32", "-lmswsock") + } + } + @Suppress("UNUSED_VARIABLE") + sourceSets { + all { + languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi") + } + targets.withType()[HostManager.host.presetName].compilations.forEach { + it.defaultSourceSet.kotlin.srcDir("src/native${it.compilationName.capitalize()}/kotlin") + it.defaultSourceSet.resources.srcDir("src/native${it.compilationName.capitalize()}/resources") + } + val commonMain by getting { + dependencies { + api("io.github.microutils:kotlin-logging:$microutilsLoggingVersion") + implementation(project(":common")) + } + } + val commonIntTest by creating + val jvmMain by getting { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinxSerializationVersion") + implementation("org.javassist:javassist:$javassistVersion") + implementation(project(":transmittable-thread-local")) + implementation("net.bytebuddy:byte-buddy:$bytebuddyVersion") + } + } + val jvmIntTest by getting { + dependsOn(commonIntTest) + dependencies { + implementation(kotlin("test-junit")) + implementation("org.apache.httpcomponents:httpclient:4.5.14") + implementation("org.apache.tomcat.embed:tomcat-embed-core:9.0.83") + implementation("org.apache.tomcat.embed:tomcat-embed-websocket:9.0.83") + implementation("org.eclipse.jetty:jetty-server:9.4.26.v20200117") + implementation("org.eclipse.jetty.websocket:javax-websocket-server-impl:9.4.26.v20200117") + implementation("io.undertow:undertow-core:2.0.29.Final") + implementation("io.undertow:undertow-websockets-jsr:2.0.29.Final") + implementation("io.netty:netty-codec-http:4.1.106.Final") + implementation("com.squareup.okhttp3:okhttp:3.12.13") + implementation("org.simpleframework:simple-http:6.0.1") + implementation("org.glassfish.tyrus:tyrus-client:1.20") + implementation("org.glassfish.tyrus:tyrus-server:1.20") + implementation("org.glassfish.tyrus:tyrus-container-grizzly-client:1.20") + implementation("org.glassfish.tyrus:tyrus-container-grizzly-server:1.20") + + implementation("org.springframework.kafka:spring-kafka:2.9.13") + implementation("org.springframework.kafka:spring-kafka-test:2.9.13") + implementation("org.springframework.boot:spring-boot-starter-webflux:2.7.18") + implementation("org.springframework.boot:spring-boot-starter-test:2.7.18") + } + } + val configureNativeMainDependencies: KotlinSourceSet.() -> Unit = { + dependencies { + implementation("org.jetbrains.kotlinx:atomicfu:$atomicfuVersion") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:$kotlinxCollectionsVersion") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinxSerializationVersion") + implementation(project(":jvmapi")) + } + } + val configureNativeIntTestDependencies: KotlinSourceSet.() -> Unit = { + dependsOn(commonIntTest) + dependencies { + implementation("io.ktor:ktor-utils:$ktorVersion") + implementation(project(":logging")) + } + } + val mingwX64Main by getting(configuration = configureNativeMainDependencies) + val linuxX64Main by getting(configuration = configureNativeMainDependencies) + val macosX64Main by getting(configuration = configureNativeMainDependencies) + val macosArm64Main by getting(configuration = configureNativeMainDependencies) + val mingwX64IntTest by getting(configuration = configureNativeIntTestDependencies) + val linuxX64IntTest by getting(configuration = configureNativeIntTestDependencies) + val macosX64IntTest by getting(configuration = configureNativeIntTestDependencies) + val macosArm64IntTest by getting(configuration = configureNativeIntTestDependencies) + } + tasks { + val filterOutCurrentPlatform: (KotlinNativeTarget) -> Boolean = { + it.targetName != HostManager.host.presetName + } + val copyNativeClassesTask: (KotlinCompilation<*>) -> Unit = { + val taskName = "copyNativeClasses${it.target.targetName.capitalize()}${it.compilationName.capitalize()}" + val copyNativeClasses: TaskProvider = register(taskName, Copy::class) { + group = "build" + from("src/native${it.compilationName.capitalize()}/kotlin") + into("src/${it.target.targetName}${it.compilationName.capitalize()}/kotlin/gen") + } + it.compileKotlinTask.dependsOn(copyNativeClasses.get()) + } + val cleanNativeClassesTask: (KotlinCompilation<*>) -> Unit = { + val taskName = "cleanNativeClasses${it.target.targetName.capitalize()}${it.compilationName.capitalize()}" + val cleanNativeClasses: TaskProvider = register(taskName, Delete::class) { + group = "build" + delete("src/${it.target.targetName}${it.compilationName.capitalize()}/kotlin/gen") + } + clean.get().dependsOn(cleanNativeClasses.get()) + } + targets.withType().filter(filterOutCurrentPlatform) + .flatMap(KotlinNativeTarget::compilations) + .onEach(copyNativeClassesTask) + .onEach(cleanNativeClassesTask) + val jvmMainCompilation = targets.withType()["jvm"].compilations["main"] + val jvmIntTestCompilation = targets.withType()["jvm"].compilations["intTest"] + val runtimeJar by registering(ShadowJar::class) { + mergeServiceFiles() + isZip64 = true + archiveFileName.set("drill-runtime.jar") + from(jvmMainCompilation.runtimeDependencyFiles, jvmMainCompilation.output, jvmIntTestCompilation.output.classesDirs) + dependencies { + exclude("/META-INF/services/javax.servlet.ServletContainerInitializer") + exclude("/ch/qos/logback/classic/servlet/*") + exclude("/com/epam/drill/agent/instrument/**/*Test.class") + exclude("/com/epam/drill/agent/instrument/**/*Test$*.class") + exclude("/com/epam/drill/agent/instrument/**/*TestServer.class") + exclude("/com/epam/drill/agent/instrument/**/*TestServer$*.class") + exclude("/com/epam/drill/agent/instrument/**/*TestClient.class") + exclude("/com/epam/drill/agent/instrument/**/*TestClient$*.class") + } + } + register("integrationTest", Test::class) { + val intTestAgentLib = targets.withType()[HostManager.host.presetName] + .binaries.getSharedLib(nativeAgentLibName, NativeBuildType.DEBUG) + val intTestClasspath = jvmIntTestCompilation.runtimeDependencyFiles + jvmIntTestCompilation.output.allOutputs + val mainClasspath = jvmMainCompilation.runtimeDependencyFiles + jvmMainCompilation.output.allOutputs + description = "Runs the integration tests using simple native agent" + group = "verification" + testClassesDirs = jvmIntTestCompilation.output.classesDirs + classpath = intTestClasspath - mainClasspath // as main classpath already loaded via agent runtime classes + jvmArgs = listOf("-agentpath:${intTestAgentLib.outputFile.path}=${runtimeJar.get().outputs.files.singleFile}") + dependsOn(runtimeJar) + dependsOn(intTestAgentLib.linkTask) + } + } +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + exclude("**/kni") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + exclude("**/kni") + } + } +} diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/ClassPathProvider.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/ClassPathProvider.kt new file mode 100644 index 00000000..e57543a2 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/ClassPathProvider.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +interface ClassPathProvider { + fun getClassPath(): String +} diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/HeadersProcessor.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/HeadersProcessor.kt new file mode 100644 index 00000000..a1caeb56 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/HeadersProcessor.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +interface HeadersProcessor { + companion object { + const val DRILL_HEADER_PREFIX = "drill-" + } + fun removeHeaders() + fun storeHeaders(headers: Map) + fun retrieveHeaders(): Map? + fun hasHeaders(): Boolean + fun isProcessRequests(): Boolean + fun isProcessResponses(): Boolean +} diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/InstrumentationParameterDefinitions.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/InstrumentationParameterDefinitions.kt new file mode 100644 index 00000000..a362f6f8 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/InstrumentationParameterDefinitions.kt @@ -0,0 +1,58 @@ +package com.epam.drill.agent.instrument + +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection + +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +object InstrumentationParameterDefinitions: AgentParameterDefinitionCollection() { + val INSTRUMENTATION_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationEnabled", defaultValue = true).register() + val CONTEXT_PROPAGATION_ENABLED = AgentParameterDefinition.forBoolean( + name = "contextPropagationEnabled", + description = "Enable/disable context propagation", + defaultValue = true + ).register() + val INSTRUMENTATION_COMPATIBILITY_TESTS_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationCompatibilityTestsEnabled", defaultValue = false).register() + + //Async frameworks + val INSTRUMENTATION_REACTOR_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationReactorEnabled", defaultValue = true).register() + val INSTRUMENTATION_TTL_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationTtlEnabled", defaultValue = true).register() + + //Messaging + val INSTRUMENTATION_KAFKA_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationKafkaEnabled", defaultValue = false).register() + val INSTRUMENTATION_CADENCE_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationCadenceEnabled", defaultValue = false).register() + + //WebSocket + val INSTRUMENTATION_WS_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationWsEnabled", defaultValue = false).register() + + //Http + val INSTRUMENTATION_HTTP_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationHttpEnabled", defaultValue = true).register() + val INSTRUMENTATION_SSL_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationSslEnabled", defaultValue = true).register() + + //Http Clients + val INSTRUMENTATION_JAVA_HTTP_CLIENT_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationJavaHttpClientEnabled", defaultValue = true).register() + val INSTRUMENTATION_APACHE_HTTP_CLIENT_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationApacheHttpClientEnabled", defaultValue = true).register() + val INSTRUMENTATION_OK_HTTP_CLIENT_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationOkHttpClientEnabled", defaultValue = true).register() + val INSTRUMENTATION_SPRING_WEB_CLIENT_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationSpringWebClientEnabled", defaultValue = true).register() + + //Test Frameworks + val INSTRUMENTATION_JUNIT_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationJUnitEnabled", defaultValue = true).register() + val INSTRUMENTATION_TESTNG_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationTestNGEnabled", defaultValue = true).register() + val INSTRUMENTATION_CUCUMBER_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationCucumberEnabled", defaultValue = true).register() + val INSTRUMENTATION_SELENIUM_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationSeleniumEnabled", defaultValue = true).register() + val INSTRUMENTATION_JMETER_ENABLED = AgentParameterDefinition.forBoolean(name = "instrumentationJMeterEnabled", defaultValue = true).register() + +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/PayloadProcessor.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/PayloadProcessor.kt new file mode 100644 index 00000000..7997cd60 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/PayloadProcessor.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +interface PayloadProcessor { + companion object { + const val HEADER_WS_PER_MESSAGE = "drill-ws-per-message" + const val PAYLOAD_PREFIX = "\n\ndrill-payload-begin\n" + const val PAYLOAD_SUFFIX = "\ndrill-payload-end" + } + fun retrieveDrillHeaders(message: String): String + fun retrieveDrillHeaders(message: ByteArray): ByteArray + fun retrieveDrillHeadersIndex(message: ByteArray): Int? + fun storeDrillHeaders(message: String?): String? + fun storeDrillHeaders(message: ByteArray?): ByteArray? + fun isPayloadProcessingEnabled(): Boolean + fun isPayloadProcessingSupported(headers: Map?): Boolean +} diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/Transformer.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/Transformer.kt new file mode 100644 index 00000000..da2bed13 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/Transformer.kt @@ -0,0 +1,23 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +interface Transformer { + fun precheck(className: String, loader: Any?, protectionDomain: Any?): Boolean = + loader != null && protectionDomain != null + fun transform(className: String, classFileBuffer: ByteArray, loader: Any?, protectionDomain: Any?): ByteArray? + fun enabled(): Boolean = true +} diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/TransformerConstants.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/TransformerConstants.kt new file mode 100644 index 00000000..4b1bb162 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/instrument/TransformerConstants.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +const val KAFKA_PRODUCER_INTERFACE = "org/apache/kafka/clients/producer/Producer" +const val KAFKA_CONSUMER_SPRING = "org/springframework/kafka/listener/KafkaMessageListenerContainer\$ListenerConsumer" + +const val CADENCE_PRODUCER = "com/uber/cadence/internal/sync/WorkflowStubImpl" +const val CADENCE_CONSUMER = "com/uber/cadence/internal/sync/WorkflowRunnable" + +const val TOMCAT_HTTP_FILTER = "org/apache/catalina/core/ApplicationFilterChain" +const val JETTY_SERVER_HANDLER = "org/eclipse/jetty/server/handler/HandlerWrapper" +const val UNDERTOW_SERVER_CONNECTORS = "io/undertow/server/Connectors" +const val NETTY_CHANNEL_HANDLER_CONTEXT = "io/netty/channel/AbstractChannelHandlerContext" + +const val SSL_ENGINE_CLASS_NAME = "javax/net/ssl/SSLEngine" + +const val COMPILER_GENERATED_NAMES_PREFIX = '$' diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt new file mode 100644 index 00000000..1eb55ecd --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt @@ -0,0 +1,27 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.module + +import com.epam.drill.agent.common.module.AgentModule + +expect object JvmModuleStorage { + + operator fun get(id: String): AgentModule? + + fun values(): Collection + + fun add(module: AgentModule) +} diff --git a/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt new file mode 100644 index 00000000..27c9cd39 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/commonMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt @@ -0,0 +1,27 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.request + +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder + +expect object DrillRequestHolder : RequestHolder { + override fun remove() + override fun retrieve(): DrillRequest? + override fun store(drillRequest: DrillRequest) + fun store(drillRequest: ByteArray) + fun dump(): ByteArray? +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractPropagationTransformer.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractPropagationTransformer.kt new file mode 100644 index 00000000..f2743e17 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractPropagationTransformer.kt @@ -0,0 +1,27 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.CONTEXT_PROPAGATION_ENABLED + +abstract class AbstractPropagationTransformer( + agentConfiguration: AgentConfiguration +) : AbstractTransformerObject(agentConfiguration) { + override fun enabled(): Boolean { + return super.enabled() && agentConfiguration.parameters[CONTEXT_PROPAGATION_ENABLED] + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractTransformerObject.kt new file mode 100644 index 00000000..93cf3bf3 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractTransformerObject.kt @@ -0,0 +1,96 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_ENABLED +import java.io.ByteArrayInputStream +import javassist.ClassPool +import javassist.CtBehavior +import javassist.CtClass +import javassist.LoaderClassPath +import mu.KLogger +import java.security.ProtectionDomain + +abstract class AbstractTransformerObject(protected val agentConfiguration: AgentConfiguration) : + TransformerObject, + ClassPathProvider { + + protected abstract val logger: KLogger + + override fun enabled(): Boolean { + return agentConfiguration.parameters[INSTRUMENTATION_ENABLED] + } + + override fun transform( + className: String, + classFileBuffer: ByteArray, + loader: Any?, + protectionDomain: Any? + ): ByteArray? = ClassPool.getDefault().run { + val classLoader = loader ?: ClassLoader.getSystemClassLoader() + this.appendClassPath(LoaderClassPath(classLoader as? ClassLoader)) + if (this.getOrNull(this::class.java.name) == null) { + this.appendClassPath(getClassPath()) + } + this.makeClass(ByteArrayInputStream(classFileBuffer), false).let { + val logError: (Throwable) -> Unit = { e -> + logger.error(e) { "transform: Error during instrumenting, class=${it.name}" } + } + val transform: (CtClass) -> Unit = { ctClass -> + transform(className, ctClass, this, classLoader as? ClassLoader, protectionDomain as? ProtectionDomain) + } + it.defrost() + it.runCatching(transform).onFailure(logError) + it.toBytecode() + } + } + + open fun transform(className: String, ctClass: CtClass) { + // Default implementation does nothing, can be overridden by subclasses + } + + open fun transform( + className: String, + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + transform(className, ctClass) + } + + open fun logInjectingHeaders(headers: Map) = + logger.trace { "logInjectingHeaders: Adding headers: $headers" } + + open fun logError(exception: Exception, message: String) = + logger.error(exception) { "logError: $message" } + + protected open fun CtBehavior.insertCatching(insert: CtBehavior.(String) -> Unit, code: String) = try { + insert( + """ + try { + $code + } catch (Exception e) { + ${this@AbstractTransformerObject::class.java.name}.INSTANCE.${this@AbstractTransformerObject::logError.name}(e, "Error in the injected code, method name: $name."); + } + """.trimIndent() + ) + } catch (e: Exception) { + logger.error(e) { "insertCatching: Can't insert code, method name: $name" } + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractWsMessagesProxyDelegate.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractWsMessagesProxyDelegate.kt new file mode 100644 index 00000000..3bc48849 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/AbstractWsMessagesProxyDelegate.kt @@ -0,0 +1,69 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +import kotlin.reflect.KCallable +import javassist.ByteArrayClassPath +import javassist.ClassPool +import net.bytebuddy.ByteBuddy +import net.bytebuddy.TypeCache +import net.bytebuddy.description.method.MethodDescription +import net.bytebuddy.description.modifier.Visibility +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy +import net.bytebuddy.implementation.FieldAccessor +import net.bytebuddy.implementation.MethodCall +import net.bytebuddy.implementation.MethodDelegation +import net.bytebuddy.matcher.ElementMatchers + +abstract class AbstractWsMessagesProxyDelegate { + + private val byteBuddy by lazy(::ByteBuddy) + private val proxyClassCache = TypeCache() + + protected fun createDelegatedGetterProxy( + className: String, + delegatedMethod: String, + delegateMethod: KCallable<*>, + classPool: ClassPool, + proxyName: String = "${className}Proxy", + targetField: String = "target", + constructorCall: (Class<*>) -> MethodCall + ): Class<*> = Class.forName(className, true, classPool.classLoader).let { clazz -> + proxyClassCache.findOrInsert(clazz.classLoader, proxyName) { + byteBuddy.subclass(clazz) + .name(proxyName) + .modifiers(Visibility.PUBLIC) + .defineField(targetField, clazz, Visibility.PRIVATE) + .defineConstructor(Visibility.PUBLIC) + .withParameter(clazz) + .intercept(constructorCall(clazz) + .andThen(FieldAccessor.ofField(targetField).setsArgumentAt(0))) + .method(ElementMatchers.isPublic() + .and(ElementMatchers.isDeclaredBy(clazz))) + .intercept(MethodCall.invokeSelf().onField(targetField).withAllArguments()) + .method(ElementMatchers.named(delegatedMethod) + .and(ElementMatchers.takesNoArguments())) + .intercept(MethodDelegation.withDefaultConfiguration() + .filter(ElementMatchers.named(delegateMethod.name)) + .to(this@AbstractWsMessagesProxyDelegate)) + .make() + .load(clazz.classLoader, ClassLoadingStrategy.Default.INJECTION) + .also { classPool.appendClassPath(ByteArrayClassPath(proxyName, it.bytes)) } + .loaded + } + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/DrillRequestHeadersProcessor.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/DrillRequestHeadersProcessor.kt new file mode 100644 index 00000000..80772cae --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/DrillRequestHeadersProcessor.kt @@ -0,0 +1,98 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +import java.util.Objects +import mu.KLogger +import mu.KotlinLogging +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.common.request.RequestHolder + +open class DrillRequestHeadersProcessor( + private val headersRetriever: HeadersRetriever, + private val requestHolder: RequestHolder +) : HeadersProcessor { + + private val logger: KLogger = KotlinLogging.logger {} + + override fun removeHeaders() = requestHolder.remove() + + override fun storeHeaders(headers: Map) { + try { + logger.trace { "storeHeaders: Unfiltered headers: $headers" } + + val sessionHeaderName = headersRetriever.sessionHeader() + val sessionIdFromHeader = headers[sessionHeaderName] + + if (sessionIdFromHeader != null) { + val filtered = headers + .filterKeys(Objects::nonNull) + .filter { it.key.startsWith(HeadersProcessor.DRILL_HEADER_PREFIX) } + logger.trace { "storeHeaders: from headers, sessionId=$sessionIdFromHeader: $filtered" } + requestHolder.store(DrillRequest(sessionIdFromHeader, filtered)) + } else { + val cookieHeader = headers.entries + .firstOrNull { it.key.equals("Cookie", ignoreCase = true) } + ?.value + val cookieMap = cookieHeader + ?.split(";") + ?.mapNotNull { + val parts = it.trim().split("=", limit = 2) + if (parts.size == 2) parts[0] to parts[1] else null + } + ?.toMap() + ?: emptyMap() + + val sessionIdFromCookie = cookieMap["drill-session-id"] + if (sessionIdFromCookie != null) { + val filtered = cookieMap + .filterKeys { it.startsWith(HeadersProcessor.DRILL_HEADER_PREFIX) } + logger.trace { "storeHeaders: from cookies, sessionId=$sessionIdFromCookie: $filtered" } + + requestHolder.store(DrillRequest(sessionIdFromCookie, filtered)) + } + } + } catch (e: Exception) { + logger.error(e) { "storeHeaders: Error while storing headers" } + } + } + + override fun retrieveHeaders() = try { + requestHolder.retrieve()?.let { drillRequest -> + logger.trace { "retrieveHeaders: Raw DrillRequest headers: ${drillRequest.headers}" } + + val filtered = drillRequest.headers + .filter { it.key.startsWith(HeadersProcessor.DRILL_HEADER_PREFIX) } + + val result = filtered + (headersRetriever.sessionHeader() to drillRequest.drillSessionId) + + logger.trace { "retrieveHeaders: Returning headers, sessionId=${drillRequest.drillSessionId}: $result" } + + result + } + } catch (e: Exception) { + logger.error(e) { "retrieveHeaders: Error while loading drill headers" } + null + } + + override fun hasHeaders() = requestHolder.retrieve() != null + + override fun isProcessRequests() = true + + override fun isProcessResponses() = true + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/DrillRequestPayloadProcessor.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/DrillRequestPayloadProcessor.kt new file mode 100644 index 00000000..6f1462a6 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/DrillRequestPayloadProcessor.kt @@ -0,0 +1,72 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +open class DrillRequestPayloadProcessor( + private val enabled: () -> Boolean = { true }, + private val headersProcessor: HeadersProcessor +) : PayloadProcessor { + + private val payloadPrefixBytes = PayloadProcessor.PAYLOAD_PREFIX.encodeToByteArray() + + override fun retrieveDrillHeaders(message: String) = message.takeIf { it.endsWith(PayloadProcessor.PAYLOAD_SUFFIX) } + ?.removeSuffix(PayloadProcessor.PAYLOAD_SUFFIX) + ?.substringAfter(PayloadProcessor.PAYLOAD_PREFIX) + ?.split("\n") + ?.associate { it.substringBefore("=") to it.substringAfter("=", "") } + ?.also(headersProcessor::storeHeaders) + ?.let { message.substringBefore(PayloadProcessor.PAYLOAD_PREFIX) } + ?: message + + override fun retrieveDrillHeaders(message: ByteArray) = + retrieveDrillHeaders(message.decodeToString()).encodeToByteArray() + + override fun retrieveDrillHeadersIndex(message: ByteArray) = message.decodeToString() + .takeIf { it.endsWith(PayloadProcessor.PAYLOAD_SUFFIX) } + ?.removeSuffix(PayloadProcessor.PAYLOAD_SUFFIX) + ?.substringAfter(PayloadProcessor.PAYLOAD_PREFIX) + ?.split("\n") + ?.associate { it.substringBefore("=") to it.substringAfter("=", "") } + ?.also(headersProcessor::storeHeaders) + ?.let { drillPayloadBytesIndex(message) } + + override fun storeDrillHeaders(message: String?) = message + ?.let { headersProcessor.retrieveHeaders() } + ?.map { (k, v) -> "$k=$v" } + ?.joinToString("\n", PayloadProcessor.PAYLOAD_PREFIX, PayloadProcessor.PAYLOAD_SUFFIX) + ?.let(message::plus) + + override fun storeDrillHeaders(message: ByteArray?) = message + ?.let { storeDrillHeaders(message.decodeToString())!!.encodeToByteArray() } + + override fun isPayloadProcessingEnabled() = enabled() + + override fun isPayloadProcessingSupported(headers: Map?) = + headers != null + && headers.containsKey(PayloadProcessor.HEADER_WS_PER_MESSAGE) + && headers[PayloadProcessor.HEADER_WS_PER_MESSAGE].toBoolean() + + private fun drillPayloadBytesIndex(bytes: ByteArray): Int { + for (currentIndex in IntRange(0, bytes.lastIndex - payloadPrefixBytes.lastIndex)) { + val regionMatches = payloadPrefixBytes.foldIndexed(true) { index, acc, byte -> + acc && bytes[currentIndex + index] == byte + } + if (regionMatches) return currentIndex + } + return -1 + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/TransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/TransformerObject.kt new file mode 100644 index 00000000..f839abc7 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/TransformerObject.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +interface TransformerObject: Transformer { + fun permit(className: String, superName: String?, interfaces: Array): Boolean +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/ApacheHttpClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/ApacheHttpClientTransformerObject.kt new file mode 100644 index 00000000..5c88d582 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/ApacheHttpClientTransformerObject.kt @@ -0,0 +1,81 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.clients + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_APACHE_HTTP_CLIENT_ENABLED + +/** + * Transformer for Apache HTTP client + + * Tested with: + * org.apache.httpcomponents:httpclient:4.2.6 + * org.apache.httpcomponents:httpclient:4.3.6 + * org.apache.httpcomponents:httpclient:4.4.1 + * org.apache.httpcomponents:httpclient:4.5.14 + * org.apache.httpcomponents:httpclient:5.3.1 + */ +abstract class ApacheHttpClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_APACHE_HTTP_CLIENT_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array) = + interfaces.any("org/apache/http/HttpClientConnection"::equals) || + interfaces.any("org/apache/hc/core5/http/io/HttpClientConnection"::equals) + + override fun transform(className: String, ctClass: CtClass) { + if (ctClass.isInterface) return + ctClass.getDeclaredMethod("sendRequestHeader").insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessRequests.name}() && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + $1.setHeader((String) entry.getKey(), (String) entry.getValue()); + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + ctClass.getDeclaredMethod("receiveResponseEntity").insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessResponses.name}()) { + java.util.Map allHeaders = new java.util.HashMap(); + java.util.Iterator iterator = $1.headerIterator(); + while (iterator.hasNext()) { + org.apache.http.Header header = (org.apache.http.Header) iterator.next(); + allHeaders.put(header.getName(), header.getValue()); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/JavaHttpClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/JavaHttpClientTransformerObject.kt new file mode 100644 index 00000000..9cb69b1c --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/JavaHttpClientTransformerObject.kt @@ -0,0 +1,96 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.clients + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_JAVA_HTTP_CLIENT_ENABLED + +/** + * Transformer for Java HttpURLConnection client + + * Tested with: + * jdk 1.8.0_241 + */ +abstract class JavaHttpClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_JAVA_HTTP_CLIENT_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array) = + "java/net/HttpURLConnection" == superName || "javax/net/ssl/HttpsURLConnection" == superName + + override fun transform(className: String, ctClass: CtClass) { + ctClass.constructors.forEach { + it.insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessRequests.name}() && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + this.setRequestProperty((String) entry.getKey(), (String) entry.getValue()); + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + } + if (className == "sun/net/www/protocol/http/HttpURLConnection") { + ctClass.getMethod("getInputStream", "()Ljava/io/InputStream;").insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessResponses.name}() && this.filteredHeaders == null) { + java.util.Map allHeaders = new java.util.HashMap(); + java.util.Iterator iterator = this.getFilteredHeaderFields().keySet().iterator(); + while (iterator.hasNext()) { + String key = (String) iterator.next(); + String value = this.getHeaderField(key); + allHeaders.put(key, value); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + } else { + ctClass.getMethod("getContent", "()Ljava/lang/Object;").insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessResponses.name}()) { + java.util.Map allHeaders = new java.util.HashMap(); + java.util.Iterator iterator = this.getHeaderFields().keySet().iterator(); + while (iterator.hasNext()) { + String key = (String) iterator.next(); + String value = this.getHeaderField(key); + allHeaders.put(key, value); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + } + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/OkHttp3ClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/OkHttp3ClientTransformerObject.kt new file mode 100644 index 00000000..95c62218 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/OkHttp3ClientTransformerObject.kt @@ -0,0 +1,85 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.clients + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtMethod +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_OK_HTTP_CLIENT_ENABLED + +/** + * Transformer for OkHttp3 client + + * Tested with: + * com.squareup.okhttp3:okhttp:3.12.13 + * com.squareup.okhttp3:okhttp:3.14.9 + * com.squareup.okhttp3:okhttp:4.12.0 + */ +abstract class OkHttp3ClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_OK_HTTP_CLIENT_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array) = + interfaces.any("okhttp3/internal/http/HttpCodec"::equals) || + interfaces.any("okhttp3/internal/http/ExchangeCodec"::equals) + + override fun transform(className: String, ctClass: CtClass) { + ctClass.getDeclaredMethod("writeRequestHeaders").insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessRequests.name}() && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + okhttp3.Request.Builder builder = $1.newBuilder(); + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + builder.addHeader((String) entry.getKey(), (String) entry.getValue()); + } + $1 = builder.build(); + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + val methodName = "openResponseBody".takeIf(ctClass.declaredMethods.map(CtMethod::getName)::contains) + ?: "openResponseBodySource" + ctClass.getDeclaredMethod(methodName) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessResponses.name}()) { + java.util.Map allHeaders = new java.util.HashMap(); + java.util.Iterator iterator = $1.headers().names().iterator(); + while (iterator.hasNext()) { + String key = (String) iterator.next(); + String value = $1.headers().get(key); + allHeaders.put(key, value); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/SpringWebClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/SpringWebClientTransformerObject.kt new file mode 100644 index 00000000..8b7211c9 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/clients/SpringWebClientTransformerObject.kt @@ -0,0 +1,57 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.clients + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_SPRING_WEB_CLIENT_ENABLED +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging + +/** + * Transformer for Spring Webflux WebClient + */ +abstract class SpringWebClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractPropagationTransformer(agentConfiguration) { + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_SPRING_WEB_CLIENT_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array) = + interfaces.any("org/springframework/web/reactive/function/client/ClientRequest"::equals) + + override fun transform(className: String, ctClass: CtClass) { + if (ctClass.isInterface) return + ctClass.getDeclaredMethod("writeTo").insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isProcessRequests.name}() && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + $1.getHeaders().add((String) entry.getKey(), (String) entry.getValue()); + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/http/AbstractHttpTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/http/AbstractHttpTransformerObject.kt new file mode 100644 index 00000000..864edf53 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/http/AbstractHttpTransformerObject.kt @@ -0,0 +1,29 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.http + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_HTTP_ENABLED + +abstract class AbstractHttpTransformerObject( + agentConfiguration: AgentConfiguration +) : HeadersProcessor, AbstractPropagationTransformer(agentConfiguration) { + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_HTTP_ENABLED] +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/jetty/JettyHttpServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/jetty/JettyHttpServerTransformerObject.kt new file mode 100644 index 00000000..207f9c9a --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/jetty/JettyHttpServerTransformerObject.kt @@ -0,0 +1,77 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.jetty + + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.JETTY_SERVER_HANDLER +import com.epam.drill.agent.instrument.http.AbstractHttpTransformerObject + +abstract class JettyHttpServerTransformerObject( + protected val headersRetriever: HeadersRetriever, + agentConfiguration: AgentConfiguration +) : HeadersProcessor, AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + JETTY_SERVER_HANDLER == className + + override fun transform(className: String, ctClass: CtClass) { + val adminHeader = headersRetriever.adminAddressHeader() + val adminUrl = headersRetriever.adminAddressValue() + val agentIdHeader = headersRetriever.agentIdHeader() + val agentIdValue = headersRetriever.agentIdHeaderValue() + val method = ctClass.getDeclaredMethod("handle") + method.insertCatching( + CtBehavior::insertBefore, + """ + if ($2 instanceof org.eclipse.jetty.server.Request && $3 instanceof org.eclipse.jetty.server.Request && $4 instanceof org.eclipse.jetty.server.Response) { + org.eclipse.jetty.server.Response jettyResponse = (org.eclipse.jetty.server.Response)$4; + if (!"$adminUrl".equals(jettyResponse.getHeader("$adminHeader"))) { + jettyResponse.addHeader("$adminHeader", "$adminUrl"); + jettyResponse.addHeader("$agentIdHeader", "$agentIdValue"); + } + org.eclipse.jetty.server.Request jettyRequest = (org.eclipse.jetty.server.Request)$3; + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Enumeration/**/ headerNames = jettyRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { + java.lang.String headerName = (java.lang.String) headerNames.nextElement(); + java.lang.String header = jettyRequest.getHeader(headerName); + allHeaders.put(headerName, header); + if (headerName.startsWith("${HeadersProcessor.DRILL_HEADER_PREFIX}") && jettyResponse.getHeader(headerName) == null) { + jettyResponse.addHeader(headerName, header); + } + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + """.trimIndent() + ) + } +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/netty/NettyCommonConstants.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/netty/NettyCommonConstants.kt new file mode 100644 index 00000000..9a9bc1f3 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/netty/NettyCommonConstants.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.netty + +const val HTTP_REQUEST = "io.netty.handler.codec.http.HttpRequest" +const val HTTP_RESPONSE = "io.netty.handler.codec.http.HttpResponse" +const val WEBSOCKET_FRAME_TEXT = "io.netty.handler.codec.http.websocketx.TextWebSocketFrame" +const val WEBSOCKET_FRAME_BINARY = "io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame" +const val WEBSOCKET_FRAME_COMMON = "io.netty.handler.codec.http.websocketx.WebSocketFrame" +const val DRILL_HTTP_CONTEXT_KEY = "com.epam.drill.agent.common.request.DrillRequest#DRILL_REQUEST_HTTP" +const val DRILL_WS_CONTEXT_KEY = "com.epam.drill.agent.common.request.DrillRequest#DRILL_REQUEST_WS" diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/netty/NettyHttpServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/netty/NettyHttpServerTransformerObject.kt new file mode 100644 index 00000000..30d54c0c --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/netty/NettyHttpServerTransformerObject.kt @@ -0,0 +1,129 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.netty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import javassist.ClassPool +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.COMPILER_GENERATED_NAMES_PREFIX +import com.epam.drill.agent.instrument.NETTY_CHANNEL_HANDLER_CONTEXT +import java.security.ProtectionDomain + +/** + * Transformer for simple Netty-based web servers + * + * Tested with: + * io.netty:netty-codec-http:4.1.106.Final + */ +abstract class NettyHttpServerTransformerObject( + private val headersRetriever: HeadersRetriever, + agentConfiguration: AgentConfiguration +) : HeadersProcessor, AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit( + className: String, + superName: String?, + interfaces: Array + ): Boolean = COMPILER_GENERATED_NAMES_PREFIX !in className && className.startsWith(NETTY_CHANNEL_HANDLER_CONTEXT) + + override fun transform( + className: String, + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + if (pool.find(HTTP_REQUEST) == null) { + logger.debug { "transform: Skipping $className because $HTTP_REQUEST class is not available" } + return + } + if (pool.find(HTTP_RESPONSE) == null) { + logger.debug { "transform: Skipping $className because $HTTP_RESPONSE class is not available" } + return + } + val invokeChannelReadMethod = ctClass.getMethod("fireChannelRead", "(Ljava/lang/Object;)Lio/netty/channel/ChannelHandlerContext;") + invokeChannelReadMethod.insertCatching( + CtBehavior::insertBefore, + """ + if ($1 instanceof $HTTP_REQUEST) { + $HTTP_REQUEST nettyRequest = ($HTTP_REQUEST) $1; + io.netty.handler.codec.http.HttpHeaders headers = nettyRequest.headers(); + java.util.Iterator iterator = headers.names().iterator(); + java.util.Map allHeaders = new java.util.HashMap(); + while(iterator.hasNext()){ + java.lang.String headerName = (String) iterator.next(); + java.lang.String headerValue = headers.get(headerName); + allHeaders.put(headerName, headerValue); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + java.util.Map drillHeaders = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + if (drillHeaders != null) { + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_HTTP_CONTEXT_KEY"); + this.channel().attr(drillContextKey).set(drillHeaders); + } + } + """.trimIndent() + ) + invokeChannelReadMethod.insertCatching( + { insertAfter(it, true) }, + """ + if ($1 instanceof $HTTP_REQUEST) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + + val adminHeader = headersRetriever.adminAddressHeader() + val adminUrl = headersRetriever.adminAddressValue() + val agentIdHeader = headersRetriever.agentIdHeader() + val agentIdValue = headersRetriever.agentIdHeaderValue() + ctClass.getMethod("write", "(Ljava/lang/Object;ZLio/netty/channel/ChannelPromise;)V").insertCatching( + CtBehavior::insertBefore, + """ + if ($1 instanceof $HTTP_RESPONSE) { + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_HTTP_CONTEXT_KEY"); + io.netty.util.Attribute drillContextAttr = this.channel().attr(drillContextKey); + java.util.Map drillHeaders = (java.util.Map) drillContextAttr.get(); + drillContextAttr.compareAndSet(drillHeaders, null); + $HTTP_RESPONSE nettyResponse = ($HTTP_RESPONSE) $1; + if (!"$adminUrl".equals(nettyResponse.headers().get("$adminHeader"))) { + nettyResponse.headers().add("$adminHeader", "$adminUrl"); + nettyResponse.headers().add("$agentIdHeader", "$agentIdValue"); + } + if (drillHeaders != null) { + java.util.Iterator iterator = drillHeaders.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + String headerName = (String) entry.getKey(); + String headerValue = (String) entry.getValue(); + if (!nettyResponse.headers().contains(headerName)) { + nettyResponse.headers().add(headerName, headerValue); + } + } + } + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PropagatedDrillRequestCallable.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PropagatedDrillRequestCallable.kt new file mode 100644 index 00000000..930e33ad --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PropagatedDrillRequestCallable.kt @@ -0,0 +1,41 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor + +import java.util.concurrent.Callable +import mu.KotlinLogging +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder + +private val logger = KotlinLogging.logger {} + +/** + * The Callable that propagates the Drill Request. + * @param drillRequest the value of the Drill Request + * @param decorate the Callable to be executed with the Drill Request + */ +class PropagatedDrillRequestCallable( + private val drillRequest: DrillRequest, + private val requestHolder: RequestHolder, + private val decorate: Callable +) : Callable { + override fun call() = propagateDrillRequest(drillRequest, requestHolder) { + logger.trace { "Scheduled task ran, sessionId = ${drillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + val result = decorate.call() + logger.trace { "Scheduled task finished, sessionId = ${drillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + result + } +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PropagatedDrillRequestRunnable.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PropagatedDrillRequestRunnable.kt new file mode 100644 index 00000000..40a2eab4 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PropagatedDrillRequestRunnable.kt @@ -0,0 +1,41 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor + +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import mu.KotlinLogging + +private val logger = KotlinLogging.logger {} + +/** + * The Runnable that propagates the Drill Request. + * @param drillRequest the value of the Drill Request + * @param decorate the Runnable to be executed with the Drill Request + */ +class PropagatedDrillRequestRunnable( + private val drillRequest: DrillRequest, + private val requestHolder: RequestHolder, + private val decorate: Runnable +) : Runnable { + override fun run() { + propagateDrillRequest(drillRequest, requestHolder) { + logger.trace { "Scheduled task ran, sessionId = ${drillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + decorate.run() + logger.trace { "Scheduled task finished, sessionId = ${drillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + } + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/ProxyUtils.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/ProxyUtils.kt new file mode 100644 index 00000000..d78611d3 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/ProxyUtils.kt @@ -0,0 +1,102 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor + +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import net.bytebuddy.ByteBuddy +import net.bytebuddy.TypeCache +import net.bytebuddy.description.modifier.Visibility +import net.bytebuddy.dynamic.DynamicType +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy +import net.bytebuddy.implementation.MethodDelegation +import net.bytebuddy.implementation.bind.annotation.* +import net.bytebuddy.matcher.ElementMatchers.* +import java.util.function.Function + +const val DRILL_DELEGATE_FIELD = "drillDelegate" +const val DRILL_REQUEST_FIELD = "drillRequest" +const val DRILL_CONTEXT_FIELD = "drillContext" +const val DRILL_CONTEXT_KEY = "drillRequest" +const val SUBSCRIPTION_CLASS = "org.reactivestreams.Subscription" +const val SUBSCRIBER_CLASS = "reactor.core.CoreSubscriber" + +/** + * The cache of proxy classes + */ +val proxyClassCache = TypeCache>() + +/** + * Constructs a proxy class and an associated instance for intercepting method calls using the specified interceptor. + * Each public method of the class will be intercepted by the provided interceptor. + * @param delegate the original instance of the class. + * @param clazz the class that will be delegated. Must be a superclass of the class of the delegate. + * @param interceptor the Byte buddy method interceptor. + * @param configure the Byte buddy configuration which will be applied before building proxy class. + * @param initialize the proxy instance initialization logic. + * @return the proxy instance. + */ +@Suppress("UNCHECKED_CAST") +inline fun createProxyDelegate( + delegate: Any, + clazz: Class, + interceptor: Any, + crossinline configure: DynamicType.Builder.FieldDefinition.Optional.Valuable<*>.() -> DynamicType.Builder.FieldDefinition.Optional.Valuable<*> = { this }, + crossinline initialize: (T, Class) -> Unit = { _, _ -> } +): T { + val proxyType = proxyClassCache.findOrInsert(clazz.classLoader, clazz) { + ByteBuddy() + .subclass(clazz) + .defineField(DRILL_DELEGATE_FIELD, clazz, Visibility.PUBLIC) + .let(configure) + .method(isPublic()) + .intercept(MethodDelegation.withDefaultConfiguration() + .withBinders(Pipe.Binder.install(Function::class.java)) + .to(interceptor)) + .make() + .load(clazz.classLoader, ClassLoadingStrategy.Default.INJECTION) + .loaded + } as Class + val proxy = proxyType.getConstructor().newInstance() + proxyType.getField(DRILL_DELEGATE_FIELD).set(proxy, delegate) + initialize(proxy, proxyType) + return proxy +} + +/** + * Propagates the drill request to the given "body" lambda expression via the ThreadLocal context. + * If the request was already propagated, the previous request will be restored after the body invocation. + * @param ctx the drill request + * @param body the body function in which the drill request will be propagated + * @return the result of the body function + */ +inline fun propagateDrillRequest(ctx: DrillRequest, requestHolder: RequestHolder, body: () -> T?): T? { + val previous = requestHolder.retrieve() + if (previous != ctx) { + requestHolder.store(ctx) + } + try { + return body() + } finally { + if (previous != ctx && previous != null) { + // If previous context is different from current one - restore previous + requestHolder.store(previous) + } else if (previous == null) { + // If no previous context available - just cleanup the current one + requestHolder.remove() + } + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PublisherAssembler.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PublisherAssembler.kt new file mode 100644 index 00000000..59f781e2 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/PublisherAssembler.kt @@ -0,0 +1,55 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor + +import com.epam.drill.agent.instrument.reactor.interceptors.PublisherInterceptor +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import mu.KotlinLogging +import net.bytebuddy.description.modifier.Visibility + +private val logger = KotlinLogging.logger {} + +/** + * The object responsible for creating proxy delegates instances of publisher classes. + */ +object PublisherAssembler { + /** + * Creates proxy delegate for the given publisher class. + * @param target the delegate instance. + * @param publisherClass the publisher class. Must be a superclass of the delegate class + * @return the proxy delegate instance + */ + @JvmStatic + fun onAssembly( + target: Any, + publisherClass: Class<*>, + requestHolder: RequestHolder + ): Any { + val drillRequest = requestHolder.retrieve() + logger.trace { "${publisherClass.simpleName}.onAssembly(${target.javaClass.simpleName}):${target.hashCode()}, sessionId = ${drillRequest?.drillSessionId}, threadId = ${Thread.currentThread().id}" } + return createProxyDelegate( + target, + publisherClass, + PublisherInterceptor(requestHolder), + configure = { defineField(DRILL_REQUEST_FIELD, DrillRequest::class.java, Visibility.PUBLIC) }, + initialize = { proxy, proxyType -> + if (drillRequest != null) + proxyType.getField(DRILL_REQUEST_FIELD).set(proxy, drillRequest) + } + ) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/PublisherInterceptor.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/PublisherInterceptor.kt new file mode 100644 index 00000000..c3db9cc2 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/PublisherInterceptor.kt @@ -0,0 +1,141 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.interceptors + +import com.epam.drill.agent.instrument.reactor.* +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import mu.KotlinLogging +import net.bytebuddy.description.modifier.Visibility +import net.bytebuddy.implementation.bind.annotation.* +import java.lang.reflect.Method +import java.util.function.Function + + +/** + * The Byte buddy method interceptor object for the {@link reactor.core.publisher.Flux} and {@link reactor.core.publisher.Mono}. + */ +class PublisherInterceptor( + private val requestHolder: RequestHolder, +) { + private val logger = KotlinLogging.logger {} + private val subscriberInterceptor = SubscriberInterceptor(requestHolder) + + /** + * Intercepts all public methods of {@link org.reactivestreams.Publisher} class and propagates the Drill Request. + * In the `subscribe()` method it changes {@link reactor.core.CoreSubscriber} argument on proxy copy and propagates the Drill Request. + * In other methods it calls the corresponding delegate method. + * @param target the delegate of the {@link org.reactivestreams.Publisher} class. + * @param drillRequest the value of the Drill Request which is located in field `DRILL_REQUEST_FIELD` in the {@link org.reactivestreams.Publisher} proxy class. + * @param superMethod the name of the intercepted method. + * @param pipe the Byte buddy method interceptor object. + * @param args the arguments of the intercepted method. + * @return the result of the intercepted method. + */ + @RuntimeType + fun intercept( + @FieldValue(DRILL_DELEGATE_FIELD) target: Any, + @FieldValue(DRILL_REQUEST_FIELD) drillRequest: DrillRequest?, + @Origin superMethod: Method, + @Pipe pipe: Function, + @AllArguments args: Array, + ): Any? { + return when (superMethod.name) { + "subscribe" -> subscribe(target, drillRequest, superMethod, pipe, args[0]) + else -> pipe.apply(target) + } + } + + private fun subscribe( + target: Any, + drillRequest: DrillRequest?, + superMethod: Method, + pipe: Function, + subscriber: Any?, + ): Any? { + if (subscriber == null) return pipe.apply(target) + val subscriberClass = Class.forName(SUBSCRIBER_CLASS, true, target.javaClass.classLoader) + if (!subscriberClass.isAssignableFrom(subscriber::class.java)) return pipe.apply(target) + + val context = subscriber.getCurrentContext() + val drillRequestFromContext = context.getOrDefault(DRILL_CONTEXT_KEY, null) + + //It is necessary to obtain the test context either from the subscriber context or from the current thread + val parentDrillRequest = drillRequestFromContext + ?: drillRequest + ?: return pipe.apply(target) + + //If the test context isn't already in the subscriber context, it needs to be added there. + val newContext = if (parentDrillRequest != drillRequestFromContext) { + context.put(DRILL_CONTEXT_KEY, parentDrillRequest) + } else context + + val subscriberProxy = createProxyDelegate( + subscriber, + subscriberClass, + subscriberInterceptor, + configure = { + defineField(DRILL_REQUEST_FIELD, DrillRequest::class.java, Visibility.PUBLIC) + .defineField(DRILL_CONTEXT_FIELD, Object::class.java, Visibility.PUBLIC) + }, + initialize = { proxy, proxyType -> + proxyType.getField(DRILL_REQUEST_FIELD).set(proxy, parentDrillRequest) + proxyType.getField(DRILL_CONTEXT_FIELD).set(proxy, newContext) + } + ) + return propagateDrillRequest(parentDrillRequest, requestHolder) { + logger.trace { "${target.javaClass.simpleName}.${superMethod.name}():${target.hashCode()}, sessionId = ${parentDrillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + superMethod.invoke(target, subscriberProxy) + } + } + + /** + * Calls the {@link reactor.util.context.Context#getOrDefault(Object, Object)} method and returns the result. + * @receiver a context {@link reactor.util.context.Context} + * @param key the key of the context + * @param defaultValue the default value + * @return the result of calling the {@link reactor.util.context.Context#getOrDefault(Object, Object)} method + */ + private fun Any.getOrDefault(key: String, defaultValue: Any?): DrillRequest? { + val getOrDefaultMethod = this.javaClass.getMethod("getOrDefault", Any::class.java, Any::class.java) + getOrDefaultMethod.isAccessible = true + return getOrDefaultMethod.invoke(this, key, defaultValue) as DrillRequest? + } + + /** + * Calls the {@link reactor.core.CoreSubscriber#currentContext()} method and returns the result. + * @receiver a subscriber {@link reactor.core.CoreSubscriber} + * @return a + */ + private fun Any.getCurrentContext(): Any { + val currentContextMethod = this.javaClass.getMethod("currentContext") + currentContextMethod.isAccessible = true + return currentContextMethod.invoke(this) + } + + /** + * Calls the {@link reactor.util.context.Context#put(Object, Object)} method and returns the result. + * @receiver a context {@link reactor.util.context.Context} + * @param key the key of the context + * @param value the value of the context + * @return the result of calling the {@link reactor.util.context.Context#put(Object, Object)} method + */ + private fun Any.put(key: String, value: Any): Any { + val putMethod = this.javaClass.getMethod("put", Any::class.java, Any::class.java) + putMethod.isAccessible = true + return putMethod.invoke(this, key, value) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/SubscriberInterceptor.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/SubscriberInterceptor.kt new file mode 100644 index 00000000..d53c3897 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/SubscriberInterceptor.kt @@ -0,0 +1,105 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.interceptors + +import com.epam.drill.agent.instrument.reactor.* +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import mu.KotlinLogging +import net.bytebuddy.description.modifier.Visibility +import net.bytebuddy.implementation.bind.annotation.* +import java.lang.reflect.Method +import java.util.function.Function + +/** + * The Byte buddy method interceptor object for the {@link reactor.core.CoreSubscriber} class. + */ +class SubscriberInterceptor( + private val requestHolder: RequestHolder +) { + private val logger = KotlinLogging.logger {} + private val subscriptionInterceptor = SubscriptionInterceptor(requestHolder) + + /** + * Intercepts all public methods of {@link reactor.core.CoreSubscriber} class and propagates the Drill Request. + * In the `onSubscribe()` method it changes {@link org.reactivestreams.Subscription} argument on proxy copy and propagates the Drill Request. + * In the `currentContext()` method it returns {drillContext} object as a {@link reactor.util.context.Context}. + * In the `onNext`, `onComplete`, `onError` methods it propagates the Drill Request. + * In other methods it calls the corresponding delegate method. + * @param target the delegate of the {@link reactor.core.CoreSubscriber} class. + * @param drillRequest the value of the Drill Request which is located in field `DRILL_REQUEST_FIELD` in the {@link reactor.core.CoreSubscriber} proxy class. + * @param superMethod the name of the intercepted method. + * @param pipe the Byte buddy method interceptor object. + * @param args the arguments of the intercepted method. + * @return the result of the intercepted method. + */ + @RuntimeType + fun intercept( + @FieldValue(DRILL_DELEGATE_FIELD) target: Any, + @FieldValue(DRILL_REQUEST_FIELD) drillRequest: DrillRequest, + @FieldValue(DRILL_CONTEXT_FIELD) drillContext: Any, + @Origin superMethod: Method, + @Pipe pipe: Function, + @AllArguments args: Array, + ): Any? { + return when (superMethod.name) { + "onSubscribe" -> onSubscribe(target, drillRequest, superMethod, args[0], pipe) + "currentContext" -> currentContext(drillContext) + "onNext", "onComplete", "onError" -> onOtherMethods(target, drillRequest, superMethod, pipe) + else -> pipe.apply(target) + } + } + + private fun onSubscribe( + target: Any, + drillRequest: DrillRequest, + superMethod: Method, + subscription: Any?, + pipe: Function + ): Any? { + if (subscription == null) + return pipe.apply(target) + val subscriptionProxy = createProxyDelegate( + subscription, + Class.forName(SUBSCRIPTION_CLASS, true, target.javaClass.classLoader), + subscriptionInterceptor, + configure = { defineField(DRILL_REQUEST_FIELD, DrillRequest::class.java, Visibility.PUBLIC) }, + initialize = { proxy, proxyType -> proxyType.getField(DRILL_REQUEST_FIELD).set(proxy, drillRequest) } + ) + + return propagateDrillRequest(drillRequest, requestHolder) { + logger.trace { "${target.javaClass.simpleName}.onSubscribe(${subscription.javaClass.name}):${target.hashCode()}, sessionId = ${drillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + superMethod.invoke(target, subscriptionProxy) + } + } + + private fun currentContext(drillContext: Any + ): Any { + return drillContext + } + + private fun onOtherMethods( + target: Any, + drillRequest: DrillRequest, + superMethod: Method, + pipe: Function, + ): Any? { + return propagateDrillRequest(drillRequest, requestHolder) { + logger.trace { "${target.javaClass.simpleName}.${superMethod.name}():${target.hashCode()}, sessionId = ${drillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + pipe.apply(target) + } + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/SubscriptionInterceptor.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/SubscriptionInterceptor.kt new file mode 100644 index 00000000..b9796ad5 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/interceptors/SubscriptionInterceptor.kt @@ -0,0 +1,71 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.interceptors + +import com.epam.drill.agent.instrument.reactor.DRILL_DELEGATE_FIELD +import com.epam.drill.agent.instrument.reactor.DRILL_REQUEST_FIELD +import com.epam.drill.agent.instrument.reactor.propagateDrillRequest +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import mu.KotlinLogging +import net.bytebuddy.implementation.bind.annotation.* +import java.lang.reflect.Method +import java.util.function.Function + +/** + * The Byte buddy method interceptor object for the {@link org.reactivestreams.Subscription} class. + */ +class SubscriptionInterceptor( + private val requestHolder: RequestHolder +) { + private val logger = KotlinLogging.logger {} + + + /** + * Intercepts all public methods of the {@link org.reactivestreams.Subscription} class, facilitating the propagation of the Drill context. + * Within the `request()` and `cancel()` methods, it ensures the seamless propagation of the Drill context. + * In other methods, it invokes the corresponding delegate method. + * @param target the delegate of the {@link org.reactivestreams.Subscription} class. + * @param drillRequest the value of the Drill Request which is located in field `DRILL_REQUEST_FIELD` in the {@link org.reactivestreams.Subscription} proxy class. + * @param superMethod the name of the intercepted method. + * @param pipe the Byte buddy method interceptor object. + * @return the result of the intercepted method. + */ + @RuntimeType + fun intercept( + @FieldValue(DRILL_DELEGATE_FIELD) target: Any, + @FieldValue(DRILL_REQUEST_FIELD) drillRequest: DrillRequest, + @Origin superMethod: Method, + @Pipe pipe: Function + ): Any? { + return when (superMethod.name) { + "request", "cancel" -> requestOrCancel(target, drillRequest, superMethod, pipe) + else -> pipe.apply(target) + } + } + + private fun requestOrCancel( + target: Any, + drillRequest: DrillRequest, + superMethod: Method, + pipe: Function, + ): Any? { + return propagateDrillRequest(drillRequest, requestHolder) { + logger.trace { "${target.javaClass.simpleName}.${superMethod.name}():${target.hashCode()}, sessionId = ${drillRequest.drillSessionId}, threadId = ${Thread.currentThread().id}" } + pipe.apply(target) + } + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/AbstractReactorTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/AbstractReactorTransformerObject.kt new file mode 100644 index 00000000..98932106 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/AbstractReactorTransformerObject.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.transformers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.common.request.RequestHolder +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_REACTOR_ENABLED + +abstract class AbstractReactorTransformerObject(agentConfiguration: AgentConfiguration) : + RequestHolder, + AbstractPropagationTransformer(agentConfiguration) { + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_REACTOR_ENABLED] + +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/FluxTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/FluxTransformerObject.kt new file mode 100644 index 00000000..1c3acfd4 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/FluxTransformerObject.kt @@ -0,0 +1,54 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.transformers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.reactor.PublisherAssembler +import com.epam.drill.agent.common.request.RequestHolder +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging + +/** + * Transformer for {@link reactor.core.publisher.Flux}. + */ +abstract class FluxTransformerObject(agentConfiguration: AgentConfiguration) : + AbstractReactorTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + className == "reactor/core/publisher/Flux" + + override fun transform(className: String, ctClass: CtClass) { + ctClass.getMethod("onAssembly", "(Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux;").insertCatching( + CtBehavior::insertBefore, + """ + $1 = (reactor.core.publisher.Flux) ${PublisherAssembler::class.java.name}.${PublisherAssembler::onAssembly.name}($1, reactor.core.publisher.Flux.class, ${this::class.java.name}.INSTANCE); + """.trimIndent() + ) + ctClass.getMethod( + "onAssembly", + "(Lreactor/core/publisher/ConnectableFlux;)Lreactor/core/publisher/ConnectableFlux;" + ).insertCatching( + CtBehavior::insertBefore, + """ + $1 = (reactor.core.publisher.ConnectableFlux) ${PublisherAssembler::class.java.name}.${PublisherAssembler::onAssembly.name}($1, reactor.core.publisher.ConnectableFlux.class, ${this::class.java.name}.INSTANCE); + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/MonoTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/MonoTransformerObject.kt new file mode 100644 index 00000000..80ce0aa9 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/MonoTransformerObject.kt @@ -0,0 +1,47 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.transformers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.instrument.reactor.PublisherAssembler +import com.epam.drill.agent.common.request.RequestHolder +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging + +/** + * Transformer for {@link reactor.core.publisher.Mono}. + */ +abstract class MonoTransformerObject(agentConfiguration: AgentConfiguration) : + AbstractReactorTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + className == "reactor/core/publisher/Mono" + + override fun transform(className: String, ctClass: CtClass) { + ctClass.getMethod("onAssembly", "(Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono;").insertCatching( + CtBehavior::insertBefore, + """ + $1 = (reactor.core.publisher.Mono) ${PublisherAssembler::class.java.name}.${PublisherAssembler::onAssembly.name}($1, reactor.core.publisher.Mono.class, ${this::class.java.name}.INSTANCE); + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/ParallelFluxTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/ParallelFluxTransformerObject.kt new file mode 100644 index 00000000..b1bcd4e2 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/ParallelFluxTransformerObject.kt @@ -0,0 +1,48 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.transformers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.instrument.reactor.PublisherAssembler +import com.epam.drill.agent.common.request.RequestHolder +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging + +/** + * Transformer for {@link reactor.core.publisher.ParallelFlux}. + */ +abstract class ParallelFluxTransformerObject(agentConfiguration: AgentConfiguration) : + AbstractReactorTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + className == "reactor/core/publisher/ParallelFlux" + + override fun transform(className: String, ctClass: CtClass) { + ctClass.getMethod("onAssembly", "(Lreactor/core/publisher/ParallelFlux;)Lreactor/core/publisher/ParallelFlux;") + .insertCatching( + CtBehavior::insertBefore, + """ + $1 = (reactor.core.publisher.ParallelFlux) ${PublisherAssembler::class.java.name}.${PublisherAssembler::onAssembly.name}($1, reactor.core.publisher.ParallelFlux.class, ${this::class.java.name}.INSTANCE); + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/SchedulersTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/SchedulersTransformerObject.kt new file mode 100644 index 00000000..1ac9a6fb --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/SchedulersTransformerObject.kt @@ -0,0 +1,51 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.transformers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.Transformer +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.reactor.PropagatedDrillRequestRunnable +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging + +/** + * Transformer for {@link reactor.core.scheduler.Schedulers}. + * It propagates {@link DrillRequest} using {@link PropagatedDrillRequestRunnable} on the method {@link Schedulers#onSchedule}. + */ +abstract class SchedulersTransformerObject(agentConfiguration: AgentConfiguration) : + AbstractReactorTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + className == "reactor/core/scheduler/Schedulers" + + override fun transform(className: String, ctClass: CtClass) { + ctClass.getDeclaredMethod("onSchedule").insertCatching( + CtBehavior::insertBefore, + """ + ${DrillRequest::class.java.name} drillRequest = ${this::class.java.name}.INSTANCE.${RequestHolder::retrieve.name}(); + if (drillRequest != null) + $1 = new ${PropagatedDrillRequestRunnable::class.java.name}(drillRequest, ${this::class.java.name}.INSTANCE, $1); + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/TaskExecutorTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/TaskExecutorTransformerObject.kt new file mode 100644 index 00000000..c1ea0fe1 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/reactor/transformers/TaskExecutorTransformerObject.kt @@ -0,0 +1,75 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.reactor.transformers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.reactor.PropagatedDrillRequestCallable +import com.epam.drill.agent.instrument.reactor.PropagatedDrillRequestRunnable +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder + +abstract class TaskExecutorTransformerObject(agentConfiguration: AgentConfiguration) : + AbstractReactorTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + listOf( + "org/springframework/core/task/SimpleAsyncTaskExecutor", + "org/springframework/scheduling/concurrent/ConcurrentTaskExecutor", + "org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor" + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting TaskExecutorTransformer for $className..." } + ctClass.getMethod( + "submitListenable", + "(Ljava/util/concurrent/Callable;)Lorg/springframework/util/concurrent/ListenableFuture;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + ${DrillRequest::class.java.name} drillRequest = ${this::class.java.name}.INSTANCE.${RequestHolder::retrieve.name}(); + if (drillRequest != null) $1 = new ${PropagatedDrillRequestCallable::class.java.name}(drillRequest, ${this::class.java.name}.INSTANCE, $1); + """.trimIndent() + ) + ctClass.getMethod( + "submitListenable", + "(Ljava/lang/Runnable;)Lorg/springframework/util/concurrent/ListenableFuture;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + ${DrillRequest::class.java.name} drillRequest = ${this::class.java.name}.INSTANCE.${RequestHolder::retrieve.name}(); + if (drillRequest != null) $1 = new ${PropagatedDrillRequestRunnable::class.java.name}(drillRequest, ${this::class.java.name}.INSTANCE, $1); + """.trimIndent() + ) + ctClass.getMethod("execute", "(Ljava/lang/Runnable;)V") + .insertCatching( + CtBehavior::insertBefore, + """ + ${DrillRequest::class.java.name} drillRequest = ${this::class.java.name}.INSTANCE.${RequestHolder::retrieve.name}(); + if (drillRequest != null) $1 = new ${PropagatedDrillRequestRunnable::class.java.name}(drillRequest, ${this::class.java.name}.INSTANCE, $1); + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/CadenceTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/CadenceTransformerObject.kt new file mode 100644 index 00000000..4a3d7ac1 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/CadenceTransformerObject.kt @@ -0,0 +1,138 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.servers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.CADENCE_CONSUMER +import com.epam.drill.agent.instrument.CADENCE_PRODUCER +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_CADENCE_ENABLED + +abstract class CadenceTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractPropagationTransformer(agentConfiguration) { + + private val producerInstrumentedMethods = listOf( + "signalAsync", + "signalAsyncWithTimeout", + "signalWithStart", + "start", + "startAsync", + "startAsyncWithTimeout" + ) + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_CADENCE_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array) = + CADENCE_PRODUCER == className || CADENCE_CONSUMER == className + + override fun transform(className: String, ctClass: CtClass) { + when (className) { + CADENCE_PRODUCER -> instrumentProducer(ctClass) + CADENCE_CONSUMER -> instrumentConsumer(ctClass) + } + } + + private fun instrumentProducer(ctClass: CtClass) { + ctClass.constructors + .mapNotNull { constructor -> + constructor.parameterTypes + .indexOfFirst { it.name.replace(".", "/") == "com/uber/cadence/client/WorkflowOptions" } + .takeIf { it >= 0 } + ?.let { constructor to it + 1 } // 0 - index of "this" object + } + .forEach { (constructor, paramIndex) -> + constructor.insertCatching( + CtBehavior::insertBefore, + """ + if ($$paramIndex.getMemo() == null) { + $$paramIndex = new com.uber.cadence.client.WorkflowOptions.Builder($$paramIndex).setMemo(new java.util.HashMap()).build(); + } + """.trimIndent() + ) + } + producerInstrumentedMethods + .mapNotNull { method -> + ctClass + .runCatching { this.getDeclaredMethod(method) } + .onFailure { logger.error(it) { "instrumentProducer: Method `$method` not found, check cadence api version" } } + .getOrNull() + } + .forEach { method -> + method.insertCatching( + CtBehavior::insertBefore, + """ + java.util.Map drillHeaders = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + if (drillHeaders != null) { + java.util.Iterator iterator = drillHeaders.entrySet().iterator(); + if (getOptions().isPresent()) { + com.uber.cadence.client.WorkflowOptions options = (com.uber.cadence.client.WorkflowOptions) getOptions().get(); + if (options.getMemo() != null) { + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + String key = ((String) entry.getKey()); + if (options.getMemo().get(key) == null) { + options.getMemo().put(key, entry.getValue()); + } + } + } + } + } + """.trimIndent() + ) + } + } + + private fun instrumentConsumer(ctClass: CtClass) { + ctClass.getDeclaredMethod("run").insertCatching( + CtBehavior::insertBefore, + """ + java.util.Map drillHeaders = new java.util.HashMap(); + com.uber.cadence.Memo memo = attributes.getMemo(); + if (memo != null) { + java.util.Map fields = memo.getFields(); + if (fields != null) { + java.util.Iterator iterator = fields.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + String key = ((String) entry.getKey()); + if (key.startsWith("${HeadersProcessor.DRILL_HEADER_PREFIX}")) { + java.nio.ByteBuffer byteBuffer = (java.nio.ByteBuffer) entry.getValue(); + if (byteBuffer != null) { + final byte[] valueBytes = new byte[byteBuffer.remaining()]; + byteBuffer.mark(); + byteBuffer.get(valueBytes); + byteBuffer.reset(); + String value = (String) com.uber.cadence.converter.JsonDataConverter.getInstance().fromData(valueBytes, String.class, String.class); + drillHeaders.put(key, value); + } + } + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(drillHeaders); + } + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/KafkaTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/KafkaTransformerObject.kt new file mode 100644 index 00000000..b67fa084 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/KafkaTransformerObject.kt @@ -0,0 +1,98 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.servers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_KAFKA_ENABLED +import com.epam.drill.agent.instrument.KAFKA_CONSUMER_SPRING +import com.epam.drill.agent.instrument.KAFKA_PRODUCER_INTERFACE + +/** + * Transformer for Kafka producer and Spring Kafka listener + * + * Tested with: + * org.apache.kafka:kafka-clients:3.2.3 + * org.springframework.kafka:spring-kafka:2.9.13 + */ +abstract class KafkaTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean { + return super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_KAFKA_ENABLED] + } + + override fun permit(className: String, superName: String?, interfaces: Array) = + KAFKA_CONSUMER_SPRING == className || interfaces.contains(KAFKA_PRODUCER_INTERFACE) + + override fun transform(className: String, ctClass: CtClass) { + val interfaces = ctClass.interfaces.map(CtClass::getName) + when { + interfaces.contains(KAFKA_PRODUCER_INTERFACE.replace("/", ".")) -> instrumentProducer(ctClass) + className == KAFKA_CONSUMER_SPRING -> instrumentConsumer(ctClass) + //TODO add Consumer for Kafka EPMDJ-8488 + } + } + + private fun instrumentProducer(ctClass: CtClass) { + ctClass.getDeclaredMethods("send").forEach { + it.insertCatching( + CtBehavior::insertBefore, + """ + java.util.Map drillHeaders = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + if (drillHeaders != null) { + java.util.Iterator iterator = drillHeaders.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + String key = ((String) entry.getKey()); + if (!$1.headers().headers(key).iterator().hasNext()) { + $1.headers().add(key, ((String) entry.getValue()).getBytes()); + } + } + } + """.trimIndent() + ) + } + } + + private fun instrumentConsumer(ctClass: CtClass) { + ctClass.getDeclaredMethods("doInvokeRecordListener").forEach { + it.insertCatching( + CtBehavior::insertBefore, + """ + java.util.Iterator headers = $1.headers().iterator(); + java.util.Map drillHeaders = new java.util.HashMap(); + while (headers.hasNext()) { + org.apache.kafka.common.header.Header header = (org.apache.kafka.common.header.Header) headers.next(); + if (header.key().startsWith("${HeadersProcessor.DRILL_HEADER_PREFIX}")) { + drillHeaders.put(header.key(), new String(header.value())); + } + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(drillHeaders); + """.trimIndent() + ) + } + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/ReactorTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/ReactorTransformerObject.kt new file mode 100644 index 00000000..41165f8e --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/ReactorTransformerObject.kt @@ -0,0 +1,44 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.servers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_REACTOR_ENABLED +import javassist.CtClass +import mu.KotlinLogging + +abstract class ReactorTransformerObject( + private val reactorTransformers: Set, + agentConfiguration: AgentConfiguration +) : AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean { + return super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_REACTOR_ENABLED] + } + + override fun permit(className: String, superName: String?, interfaces: Array) = + reactorTransformers.any { it.permit(className, null, emptyArray()) } + + override fun transform(className: String, ctClass: CtClass) { + reactorTransformers.find { it.permit(className, null, emptyArray()) } + ?.transform(className, ctClass) + ?: logger.error { "Reactor transformer object for class $className not found" } + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/SSLEngineTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/SSLEngineTransformerObject.kt new file mode 100644 index 00000000..25f4daea --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/SSLEngineTransformerObject.kt @@ -0,0 +1,154 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.servers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import java.nio.ByteBuffer +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_SSL_ENABLED +import com.epam.drill.agent.instrument.SSL_ENGINE_CLASS_NAME + +private const val HTTP_DETECTOR_BYTES_COUNT = 8 +private const val HTTP_HEADERS_SEPARATOR = "\r\n" +private const val HTTP_HEADERS_END_MARK = HTTP_HEADERS_SEPARATOR + HTTP_HEADERS_SEPARATOR +private const val HTTP_RESPONSE_MARK = "HTTP" + +/** + * Transformer for SSLEngine based web servers with java-side HTTPS termination + * + * Tested with: + * jdk 1.8.0_241 + */ +abstract class SSLEngineTransformerObject( + headersRetriever: HeadersRetriever, + agentConfiguration: AgentConfiguration +) : HeadersProcessor, AbstractPropagationTransformer(agentConfiguration) { + + private val httpVerbs = setOf("OPTIONS", "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", "CONNECT", "PRI") + private val httpHeadersEndMark = HTTP_HEADERS_END_MARK.encodeToByteArray() + + private val agentIdPair by lazy { headersRetriever.agentIdHeader() to headersRetriever.agentIdHeaderValue() } + private val adminAddressPair by lazy { headersRetriever.adminAddressHeader() to headersRetriever.adminAddressValue() } + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_SSL_ENABLED] + + override fun permit( + className: String, + superName: String?, + interfaces: Array + ): Boolean = superName == SSL_ENGINE_CLASS_NAME + + override fun transform(className: String, ctClass: CtClass) { + ctClass.getMethod( + "unwrap", + "(Ljava/nio/ByteBuffer;[Ljava/nio/ByteBuffer;II)Ljavax/net/ssl/SSLEngineResult;" + ).insertCatching( + CtBehavior::insertAfter, + """ + ${this::class.java.name}.INSTANCE.${this::readHttpRequest.name}($2); + """.trimIndent() + ) + val wrapMethod = ctClass.getMethod( + "wrap", + "([Ljava/nio/ByteBuffer;IILjava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;" + ) + wrapMethod.insertCatching( + CtBehavior::insertBefore, + """ + ${this::class.java.name}.INSTANCE.${this::writeHttpRequest.name}($1); + """.trimIndent() + ) + wrapMethod.insertCatching( + CtBehavior::insertAfter, + """ + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + """.trimIndent() + ) + } + + fun readHttpRequest(buffers: Array) = try { + val prefix = retrievePrefix(buffers[0]) + val bytes by lazy { retrieveBytes(buffers[0]) } + val headersEnd by lazy { bytes.indexOf(httpHeadersEndMark) } + when { + httpVerbs.any(prefix::startsWith) && headersEnd != -1 -> readHeaders(bytes, headersEnd) + else -> Unit + } + } catch (e: Exception) { + logger.error(e) { "readHttpRequest: Error while parse request buffer" } + } + + fun writeHttpRequest(buffers: Array) = try { + val prefix = retrievePrefix(buffers[0]) + val bytes by lazy { retrieveBytes(buffers[0]) } + val headersEnd by lazy { bytes.indexOf(httpHeadersEndMark) } + when { + prefix.startsWith(HTTP_RESPONSE_MARK) && headersEnd != -1 -> writeHeaders(buffers, bytes, headersEnd) + else -> Unit + } + } catch (e: Exception) { + logger.error(e) { "writeHttpRequest: Error while writing response buffer" } + } + + private fun retrievePrefix(buffer: ByteBuffer) = Integer.min(HTTP_DETECTOR_BYTES_COUNT, buffer.limit()) + .let { buffer.array().copyOfRange(buffer.arrayOffset(), it).decodeToString() } + + private fun retrieveBytes(buffer: ByteBuffer) = buffer.array() + .copyOfRange(buffer.arrayOffset(), buffer.arrayOffset() + buffer.limit()) + + private fun readHeaders(bytes: ByteArray, index: Int) = bytes.copyOf(index).decodeToString() + .also { logger.trace { "readHeaders: Reading HTTP request:\n${it.prependIndent("\t")}" } } + .substringAfter(HTTP_HEADERS_SEPARATOR) // skip first line with HTTP response status + .split(HTTP_HEADERS_SEPARATOR) + .filter(String::isNotBlank) + .map { it.split(":", limit = 2).map(String::trim) } + .onEach { logger.trace { "readHeaders: Read HTTP header: ${it[0]}=${it[1]}" } } + .associate { it[0] to it[1] } + .let(::storeHeaders) + + private fun writeHeaders(buffers: Array, bytes: ByteArray, index: Int) { + val headers = (retrieveHeaders() ?: emptyMap()) + mapOf(agentIdPair, adminAddressPair) + if (bytes.indexOf(headers.keys.first().encodeToByteArray()) != -1) return + headers.entries.forEach { logger.trace { "writeHeaders: Writing HTTP header: ${it.key}=${it.value}" } } + val responseHead = bytes.copyOfRange(0, index) + val responseTail = bytes.copyOfRange(index, bytes.size) + val injectedHeaders = headers.map { (k, v) -> "$k: $v" } + .joinToString(HTTP_HEADERS_SEPARATOR, HTTP_HEADERS_SEPARATOR) + .encodeToByteArray() + val modified = responseHead + injectedHeaders + responseTail + buffers[0].position(bytes.size) + buffers[0] = ByteBuffer.wrap(modified) + logger.trace { "writeHeaders: Written HTTP headers:\n${modified.decodeToString().prependIndent("\t")}" } + } + + private fun ByteArray.indexOf(bytes: ByteArray): Int { + for (thisIndex in IntRange(0, lastIndex - bytes.lastIndex)) { + val regionMatches = bytes.foldIndexed(true) { index, acc, byte -> acc && this[thisIndex + index] == byte } + if (regionMatches) return thisIndex + } + return -1 + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/TTLTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/TTLTransformerObject.kt new file mode 100644 index 00000000..f2b5cab1 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/servers/TTLTransformerObject.kt @@ -0,0 +1,82 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.servers + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.ttl.threadpool.agent.TtlAgent +import com.epam.drill.agent.ttl.threadpool.agent.internal.logging.Logger +import com.epam.drill.agent.ttl.threadpool.agent.internal.transformlet.ClassInfo +import com.epam.drill.agent.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet +import com.epam.drill.agent.ttl.threadpool.agent.internal.transformlet.impl.TtlExecutorTransformlet +import com.epam.drill.agent.ttl.threadpool.agent.internal.transformlet.impl.TtlForkJoinTransformlet +import com.epam.drill.agent.ttl.threadpool.agent.internal.transformlet.impl.TtlTimerTaskTransformlet +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_TTL_ENABLED + +abstract class TTLTransformerObject(agentConfiguration: AgentConfiguration) : AbstractPropagationTransformer(agentConfiguration) { + + private val directTtlClasses = listOf( + "java/util/concurrent/ScheduledThreadPoolExecutor", + "java/util/concurrent/ThreadPoolExecutor", + "java/util/concurrent/ForkJoinTask", + "java/util/concurrent/ForkJoinPool" + ) + private val threadPoolExecutorClass = "java/util/concurrent/ThreadPoolExecutor" + private val timerTaskClass = "java/util/TimerTask" + private val runnableInterface = "java/lang/Runnable" + private val jdkInternal = "jdk/internal" + + private val transformletList: MutableList = ArrayList() + + override val logger = KotlinLogging.logger {} + + init { + Logger.setLoggerImplType("") + transformletList.add(TtlExecutorTransformlet(false)) + transformletList.add(TtlForkJoinTransformlet(false)) + if (TtlAgent.isEnableTimerTask()) transformletList.add(TtlTimerTaskTransformlet()) + } + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_TTL_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array): Boolean { + if (directTtlClasses.contains(className)) return true + return (threadPoolExecutorClass == superName || interfaces.contains(runnableInterface)) + && className != timerTaskClass && !className.startsWith(jdkInternal) + } + + override fun transform( + className: String, + classFileBuffer: ByteArray, + loader: Any?, + protectionDomain: Any? + ) = try { + val classInfo = ClassInfo(className.replace('/', '.'), classFileBuffer, (loader as? ClassLoader)) + transformletList.firstOrNull { classInfo.also(it::doTransform).isModified } + ?.let { classInfo.ctClass.toBytecode() } + ?: classFileBuffer + } catch (e: Exception) { + logger.error(e) { "transform: Failed to transform class $className" } + classFileBuffer + } + + override fun transform(className: String, ctClass: CtClass): Unit = throw NotImplementedError() + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/test/CompatibilityTestsTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/test/CompatibilityTestsTransformerObject.kt new file mode 100644 index 00000000..35cf9980 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/test/CompatibilityTestsTransformerObject.kt @@ -0,0 +1,78 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.test + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_COMPATIBILITY_TESTS_ENABLED +import javassist.CtClass +import mu.KotlinLogging + +private const val COMPATIBILITY_TEST_CLASS_NAME = "com/epam/drill/compatibility/context/DrillTestContext" +private const val DRILL_SESSION_ID_HEADER = "drill-session-id" + +/** + * Uses for compatibility tests https://github.com/Drill4J/internal-compatibility-matrix-tests. + */ +abstract class CompatibilityTestsTransformerObject(agentConfiguration: AgentConfiguration) : + RequestHolder, + AbstractTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_COMPATIBILITY_TESTS_ENABLED] + + override fun permit(className: String, superName: String?, interfaces: Array) = + className == COMPATIBILITY_TEST_CLASS_NAME + + override fun transform(className: String, ctClass: CtClass) { + ctClass.getDeclaredMethod("retrieve").setBody( + """ + { + ${DrillRequest::class.java.name} drillRequest = ${this::class.java.name}.INSTANCE.${RequestHolder::retrieve.name}(); + if (drillRequest != null) { + java.util.Map context = new java.util.HashMap(); + context.putAll(drillRequest.getHeaders()); + context.put("$DRILL_SESSION_ID_HEADER", drillRequest.getDrillSessionId()); + return context; + } else { + return null; + } + } + """.trimIndent() + ) + ctClass.getDeclaredMethod("store").setBody( + """ + { + java.lang.String sessionId = (java.lang.String) $1.get("$DRILL_SESSION_ID_HEADER"); + ${DrillRequest::class.java.name} drillRequest = new ${DrillRequest::class.java.name}(sessionId, $1); + ${this::class.java.name}.INSTANCE.${RequestHolder::store.name}(drillRequest); + } + """.trimIndent() + ) + ctClass.getDeclaredMethod("remove").setBody( + """ + { + ${this::class.java.name}.INSTANCE.${RequestHolder::remove.name}(); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/tomcat/TomcatHttpServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/tomcat/TomcatHttpServerTransformerObject.kt new file mode 100644 index 00000000..9ad09de6 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/tomcat/TomcatHttpServerTransformerObject.kt @@ -0,0 +1,90 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.tomcat + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.NotFoundException +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.TOMCAT_HTTP_FILTER +import com.epam.drill.agent.instrument.http.AbstractHttpTransformerObject + +/** + * Transformer for Tomcat web server + * + * Tested with: + * org.apache.tomcat.embed:tomcat-embed-core:9.0.83 + * org.apache.tomcat.embed:tomcat-embed-core:10.0.27 + */ +abstract class TomcatHttpServerTransformerObject( + protected val headersRetriever: HeadersRetriever, + agentConfiguration: AgentConfiguration +) : HeadersProcessor, AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + TOMCAT_HTTP_FILTER == className + + override fun transform(className: String, ctClass: CtClass) { + val adminHeader = headersRetriever.adminAddressHeader() + val adminUrl = headersRetriever.adminAddressValue() + val agentIdHeader = headersRetriever.agentIdHeader() + val agentIdValue = headersRetriever.agentIdHeaderValue() + val method = try { + ctClass.getMethod("doFilter", "(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V") + } catch (e: NotFoundException) { + ctClass.getMethod("doFilter", "(Ljakarta/servlet/ServletRequest;Ljakarta/servlet/ServletResponse;)V") + } + method.insertCatching( + CtBehavior::insertBefore, + """ + if ($1 instanceof org.apache.catalina.connector.RequestFacade && $2 instanceof org.apache.catalina.connector.ResponseFacade) { + org.apache.catalina.connector.ResponseFacade tomcatResponse = (org.apache.catalina.connector.ResponseFacade)$2; + if (!"$adminUrl".equals(tomcatResponse.getHeader("$adminHeader"))) { + tomcatResponse.addHeader("$adminHeader", "$adminUrl"); + tomcatResponse.addHeader("$agentIdHeader", "$agentIdValue"); + } + org.apache.catalina.connector.RequestFacade tomcatRequest = (org.apache.catalina.connector.RequestFacade)${'$'}1; + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Enumeration/**/ headerNames = tomcatRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { + java.lang.String headerName = (java.lang.String) headerNames.nextElement(); + java.lang.String header = tomcatRequest.getHeader(headerName); + allHeaders.put(headerName, header); + if (headerName.startsWith("${HeadersProcessor.DRILL_HEADER_PREFIX}") && tomcatResponse.getHeader(headerName) == null) { + tomcatResponse.addHeader(headerName, header); + } + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/undertow/UndertowHttpServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/undertow/UndertowHttpServerTransformerObject.kt new file mode 100644 index 00000000..1d52ac4f --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/undertow/UndertowHttpServerTransformerObject.kt @@ -0,0 +1,92 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.undertow + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.UNDERTOW_SERVER_CONNECTORS +import com.epam.drill.agent.instrument.http.AbstractHttpTransformerObject +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging + +/** + * Transformer for simple Undertow-based web servers + * + * Tested with: + * io.undertow:undertow-core:2.0.29.Final + */ +abstract class UndertowHttpServerTransformerObject( + private val headersRetriever: HeadersRetriever, + agentConfiguration: AgentConfiguration +) : HeadersProcessor, AbstractPropagationTransformer(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + UNDERTOW_SERVER_CONNECTORS == className + + override fun transform(className: String, ctClass: CtClass) { + val adminHeader = headersRetriever.adminAddressHeader() + val adminUrl = headersRetriever.adminAddressValue() + val agentIdHeader = headersRetriever.agentIdHeader() + val agentIdValue = headersRetriever.agentIdHeaderValue() + + val method = ctClass.getMethod("executeRootHandler", "(Lio/undertow/server/HttpHandler;Lio/undertow/server/HttpServerExchange;)V") + + method.insertCatching( + CtBehavior::insertBefore, + """ + if ($1 instanceof io.undertow.server.HttpHandler && $2 instanceof io.undertow.server.HttpServerExchange) { + io.undertow.util.HeaderMap responseHeaders = (io.undertow.util.HeaderMap) $2.getResponseHeaders(); + if (responseHeaders.get("$adminHeader") == null || !responseHeaders.get("$adminHeader").contains("$adminUrl")) { + responseHeaders.add(io.undertow.util.HttpString.tryFromString("$adminHeader"), "$adminUrl"); + responseHeaders.add(io.undertow.util.HttpString.tryFromString("$agentIdHeader"), "$agentIdValue"); + } + + io.undertow.util.HeaderMap requestHeaders = (io.undertow.util.HeaderMap) $2.getRequestHeaders(); + java.util.Iterator/*io.undertow.util.HttpString>*/ headerNames = requestHeaders.getHeaderNames().iterator(); + java.util.Map/**/ drillHeaders = new java.util.HashMap(); + while (headerNames.hasNext()) { + java.lang.String headerName = (java.lang.String) headerNames.next().toString(); + java.util.Iterator/**/ requestHeaderIterator = requestHeaders.get(io.undertow.util.HttpString.tryFromString(headerName)).iterator(); + while (requestHeaderIterator.hasNext()) { + java.lang.String header = (java.lang.String) requestHeaderIterator.next(); + if (headerName.startsWith("${HeadersProcessor.DRILL_HEADER_PREFIX}")) { + drillHeaders.put(headerName, header); + if (responseHeaders.get(io.undertow.util.HttpString.tryFromString(headerName)) == null) { + responseHeaders.add(io.undertow.util.HttpString.tryFromString(headerName), header); + } + } + } + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(drillHeaders); + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/AbstractWsTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/AbstractWsTransformerObject.kt new file mode 100644 index 00000000..4fdded48 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/AbstractWsTransformerObject.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.ws + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.instrument.AbstractPropagationTransformer +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.InstrumentationParameterDefinitions.INSTRUMENTATION_WS_ENABLED + +abstract class AbstractWsTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractPropagationTransformer(agentConfiguration) { + override fun enabled(): Boolean = super.enabled() && agentConfiguration.parameters[INSTRUMENTATION_WS_ENABLED] +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty10WsMessagesTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty10WsMessagesTransformerObject.kt new file mode 100644 index 00000000..6b63c4be --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty10WsMessagesTransformerObject.kt @@ -0,0 +1,212 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.jetty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtField +import javassist.CtMethod +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.PayloadProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class Jetty10WsMessagesTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + PayloadProcessor, AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = listOf( + "org/eclipse/jetty/websocket/core/client/WebSocketCoreClient", + "org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler", + "org/eclipse/jetty/websocket/javax/common/JavaxWebSocketRemoteEndpoint", + "org/eclipse/jetty/websocket/javax/client/internal/JsrUpgradeListener", + "org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketCreator", + "org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerFrameHandlerFactory" + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting Jetty10WsMessagesTransformerObject for $className..." } + try { + if (className != "org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler") + ctClass.classPool.classLoader.loadClass("org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandler") + when (className) { + "org/eclipse/jetty/websocket/core/client/WebSocketCoreClient" -> transformWebSocketCoreClient(ctClass) + "org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler" -> transformJavaxWebSocketFrameHandler( + ctClass + ) + + "org/eclipse/jetty/websocket/javax/common/JavaxWebSocketRemoteEndpoint" -> transformWebSocketRemoteEndpoint( + ctClass + ) + + "org/eclipse/jetty/websocket/javax/client/internal/JsrUpgradeListener" -> transformJsrUpgradeListener( + ctClass + ) + + "org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketCreator" -> transformWebSocketCreator( + ctClass + ) + + "org/eclipse/jetty/websocket/javax/server/internal/JavaxWebSocketServerFrameHandlerFactory" -> transformFrameHandlerFactory( + ctClass + ) + } + } catch (e: ClassNotFoundException) { + logger.error { "transform: Skipping Jetty-10 transformations (probably Jetty versions isn't 10): $e" } + } + } + + private fun transformJavaxWebSocketFrameHandler(ctClass: CtClass) { + CtField.make( + "private java.util.Map/**/ handshakeHeaders = null;", + ctClass + ).also(ctClass::addField) + CtMethod.make( + """ + public java.util.Map/**/ getHandshakeHeaders() { + return this.handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + CtMethod.make( + """ + public void setHandshakeHeaders(java.util.Map/**/ handshakeHeaders) { + this.handshakeHeaders = handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + val acceptMessageMethod = ctClass.getMethod( + "acceptMessage", + "(Lorg/eclipse/jetty/websocket/core/Frame;Lorg/eclipse/jetty/util/Callback;)V" + ) + acceptMessageMethod.insertCatching( + CtBehavior::insertBefore, + """ + if ($1.isDataFrame() && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.handshakeHeaders)) { + byte[] bytes = new byte[$1.getPayload().limit()]; + int position = $1.getPayload().position(); + $1.getPayload().get(bytes); + $1.getPayload().position(position); + java.lang.Integer drillIndex = ${this::class.java.name}.INSTANCE.${this::retrieveDrillHeadersIndex.name}(bytes); + if (drillIndex != null) { + $1.getPayload().limit(drillIndex.intValue()); + } + } + """.trimIndent() + ) + acceptMessageMethod.insertCatching( + { insertAfter(it, true) }, + """ + if ($1.isDataFrame() && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.handshakeHeaders)) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformWebSocketRemoteEndpoint(ctClass: CtClass) = ctClass + .getMethod("sendFrame", "(Lorg/eclipse/jetty/websocket/core/Frame;Lorg/eclipse/jetty/util/Callback;Z)V") + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(((org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandler)this.session.getFrameHandler()).getHandshakeHeaders())) { + byte[] bytes = new byte[$1.getPayload().limit()]; + $1.getPayload().get(bytes); + $1.getPayload().clear(); + $1.setPayload(java.nio.ByteBuffer.wrap(${this::class.java.name}.INSTANCE.storeDrillHeaders(bytes))); + } + """.trimIndent() + ) + + private fun transformJsrUpgradeListener(ctClass: CtClass) = ctClass + .getMethod( + "onHandshakeResponse", + "(Lorg/eclipse/jetty/client/HttpRequest;Lorg/eclipse/jetty/client/HttpResponse;)V" + ) + .insertCatching( + CtBehavior::insertAfter, + """ + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = $2.getHeaders().getFieldNames().asIterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = (java.lang.String)headerNames.next(); + java.lang.String header = java.lang.String.join(",", $2.getHeaders().getValuesList(headerName)); + allHeaders.put(headerName, header); + } + org.eclipse.jetty.websocket.core.FrameHandler frameHandler = ((org.eclipse.jetty.websocket.javax.client.internal.JavaxClientUpgradeRequest)$1).getFrameHandler(); + ((org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandler)frameHandler).setHandshakeHeaders(allHeaders); + """.trimIndent() + ) + + private fun transformFrameHandlerFactory(ctClass: CtClass) = ctClass + .getMethod( + "newFrameHandler", + "(Ljava/lang/Object;Lorg/eclipse/jetty/websocket/core/server/ServerUpgradeRequest;Lorg/eclipse/jetty/websocket/core/server/ServerUpgradeResponse;)Lorg/eclipse/jetty/websocket/core/FrameHandler;" + ) + .insertCatching( + CtBehavior::insertAfter, + """ + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Map/*>*/ upgradeHeaders = $2.getHeadersMap(); + java.util.Iterator/**/ headerNames = upgradeHeaders.keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.lang.String header = java.lang.String.join(",", (java.util.List/**/)upgradeHeaders.get(headerName)); + allHeaders.put(headerName, header); + } + ((org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandler)${'$'}_).setHandshakeHeaders(allHeaders); + """.trimIndent() + ) + + private fun transformWebSocketCoreClient(ctClass: CtClass) = ctClass + .getMethod( + "connect", + "(Lorg/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest;)Ljava/util/concurrent/CompletableFuture;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + $1.addHeader(new org.eclipse.jetty.http.HttpField("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true")); + } + """.trimIndent() + ) + + private fun transformWebSocketCreator(ctClass: CtClass) = ctClass + .getMethod( + "createWebSocket", + "(Lorg/eclipse/jetty/websocket/core/server/ServerUpgradeRequest;Lorg/eclipse/jetty/websocket/core/server/ServerUpgradeResponse;)Ljava/lang/Object;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + $2.setHeader("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty11WsMessagesTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty11WsMessagesTransformerObject.kt new file mode 100644 index 00000000..b60a87a3 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty11WsMessagesTransformerObject.kt @@ -0,0 +1,231 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.jetty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtField +import javassist.CtMethod +import javassist.CtNewMethod +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.PayloadProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class Jetty11WsMessagesTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + PayloadProcessor, AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = listOf( + "org/eclipse/jetty/websocket/core/FrameHandler", + "org/eclipse/jetty/websocket/core/internal/WebSocketCoreSession", + "org/eclipse/jetty/websocket/core/client/WebSocketCoreClient", + "org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator", + "org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler", + "org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory", + "org/eclipse/jetty/websocket/jakarta/common/JakartaWebSocketFrameHandler", + "org/eclipse/jetty/websocket/jakarta/client/internal/JsrUpgradeListener" + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting Jetty11WsMessagesTransformerObject for $className..." } + try { + if (className != "org/eclipse/jetty/websocket/core/FrameHandler" + && className != "org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler" + && className != "org/eclipse/jetty/websocket/jakarta/common/JakartaWebSocketFrameHandler" + ) { + ctClass.classPool.classLoader.loadClass("org.eclipse.jetty.websocket.core.FrameHandler") + ctClass.classPool.classLoader.loadClass("org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandler") + ctClass.classPool.classLoader.loadClass("org.eclipse.jetty.websocket.jakarta.common.JakartaWebSocketFrameHandler") + } + when (className) { + "org/eclipse/jetty/websocket/core/FrameHandler" -> transformFrameHandler(ctClass) + "org/eclipse/jetty/websocket/core/internal/WebSocketCoreSession" -> transformCoreSession(ctClass) + "org/eclipse/jetty/websocket/core/client/WebSocketCoreClient" -> transformWebSocketCoreClient(ctClass) + "org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator" -> transformCreatorNegotiator( + ctClass + ) + + "org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler" -> transformWebSocketFrameHandler( + ctClass + ) + + "org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory" -> transformFrameHandlerFactory( + ctClass + ) + + "org/eclipse/jetty/websocket/jakarta/common/JakartaWebSocketFrameHandler" -> transformWebSocketFrameHandler( + ctClass + ) + + "org/eclipse/jetty/websocket/jakarta/client/internal/JsrUpgradeListener" -> transformJsrUpgradeListener( + ctClass + ) + } + } catch (e: ClassNotFoundException) { + logger.error { "transform: Skipping Jetty-11 transformations (probably Jetty versions isn't 11): $e" } + } + } + + private fun transformFrameHandler(ctClass: CtClass) = CtNewMethod.abstractMethod( + ctClass.classPool.get("java.util.Map"), + "getHandshakeHeaders", + null, + null, + ctClass + ).also(ctClass::addMethod) + + private fun transformWebSocketFrameHandler(ctClass: CtClass) { + CtField.make( + "private java.util.Map/**/ handshakeHeaders = null;", + ctClass + ).also(ctClass::addField) + CtMethod.make( + """ + public java.util.Map/**/ getHandshakeHeaders() { + return this.handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + CtMethod.make( + """ + public void setHandshakeHeaders(java.util.Map/**/ handshakeHeaders) { + this.handshakeHeaders = handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + val method = ctClass.getMethod( + "acceptMessage", + "(Lorg/eclipse/jetty/websocket/core/Frame;Lorg/eclipse/jetty/util/Callback;)V" + ) + method.insertCatching( + CtBehavior::insertBefore, + """ + if ($1.isDataFrame() && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.handshakeHeaders)) { + byte[] bytes = new byte[$1.getPayload().limit()]; + int position = $1.getPayload().position(); + $1.getPayload().get(bytes); + $1.getPayload().position(position); + java.lang.Integer drillIndex = ${this::class.java.name}.INSTANCE.${this::retrieveDrillHeadersIndex.name}(bytes); + if (drillIndex != null) { + $1.getPayload().limit(drillIndex.intValue()); + } + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + if ($1.isDataFrame() && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.handshakeHeaders)) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformCoreSession(ctClass: CtClass) = ctClass + .getMethod("sendFrame", "(Lorg/eclipse/jetty/websocket/core/Frame;Lorg/eclipse/jetty/util/Callback;Z)V") + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.handler.getHandshakeHeaders())) { + byte[] bytes = new byte[$1.getPayload().limit()]; + $1.getPayload().get(bytes); + $1.getPayload().clear(); + $1.setPayload(java.nio.ByteBuffer.wrap(${this::class.java.name}.INSTANCE.storeDrillHeaders(bytes))); + } + """.trimIndent() + ) + + private fun transformJsrUpgradeListener(ctClass: CtClass) = ctClass + .getMethod( + "onHandshakeResponse", + "(Lorg/eclipse/jetty/client/HttpRequest;Lorg/eclipse/jetty/client/HttpResponse;)V" + ) + .insertCatching( + CtBehavior::insertAfter, + """ + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = $2.getHeaders().getFieldNames().asIterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = (java.lang.String)headerNames.next(); + java.lang.String header = java.lang.String.join(",", $2.getHeaders().getValuesList(headerName)); + allHeaders.put(headerName, header); + } + org.eclipse.jetty.websocket.core.FrameHandler frameHandler = ((org.eclipse.jetty.websocket.jakarta.client.internal.JakartaClientUpgradeRequest)$1).getFrameHandler(); + ((org.eclipse.jetty.websocket.jakarta.common.JakartaWebSocketFrameHandler)frameHandler).setHandshakeHeaders(allHeaders); + """.trimIndent() + ) + + private fun transformFrameHandlerFactory(ctClass: CtClass) = ctClass + .getMethod( + "newFrameHandler", + "(Ljava/lang/Object;Lorg/eclipse/jetty/websocket/core/server/ServerUpgradeRequest;Lorg/eclipse/jetty/websocket/core/server/ServerUpgradeResponse;)Lorg/eclipse/jetty/websocket/core/FrameHandler;" + ) + .insertCatching( + CtBehavior::insertAfter, + """ + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Map/*>*/ upgradeHeaders = $2.getHeadersMap(); + java.util.Iterator/**/ headerNames = upgradeHeaders.keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.lang.String header = java.lang.String.join(",", (java.util.List/**/)upgradeHeaders.get(headerName)); + allHeaders.put(headerName, header); + } + ((org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandler)${'$'}_).setHandshakeHeaders(allHeaders); + """.trimIndent() + ) + + private fun transformWebSocketCoreClient(ctClass: CtClass) = ctClass + .getMethod( + "connect", + "(Lorg/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest;)Ljava/util/concurrent/CompletableFuture;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + $1.addHeader(new org.eclipse.jetty.http.HttpField("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true")); + } + """.trimIndent() + ) + + private fun transformCreatorNegotiator(ctClass: CtClass) = ctClass + .getMethod( + "negotiate", + "(Lorg/eclipse/jetty/websocket/core/server/WebSocketNegotiation;)Lorg/eclipse/jetty/websocket/core/FrameHandler;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + $1.getResponse().setHeader("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty9WsMessagesTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty9WsMessagesTransformerObject.kt new file mode 100644 index 00000000..5f570818 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/Jetty9WsMessagesTransformerObject.kt @@ -0,0 +1,230 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.jetty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtField +import javassist.CtMethod +import javassist.NotFoundException +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.PayloadProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class Jetty9WsMessagesTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, PayloadProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = listOf( + "org/eclipse/jetty/websocket/common/WebSocketSession", + "org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection", + "org/eclipse/jetty/websocket/common/events/AbstractEventDriver", + "org/eclipse/jetty/websocket/common/WebSocketRemoteEndpoint", + "org/eclipse/jetty/websocket/client/WebSocketClient", + "org/eclipse/jetty/websocket/server/HandshakeRFC6455", + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting Jetty9WsMessagesTransformerObject for $className..." } + if (className != "org/eclipse/jetty/websocket/common/WebSocketSession") + ctClass.classPool.classLoader?.loadClass("org.eclipse.jetty.websocket.common.WebSocketSession") + when (className) { + "org/eclipse/jetty/websocket/common/WebSocketSession" -> transformWebSocketSession(ctClass) + "org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection" -> transformWebSocketConnection(ctClass) + "org/eclipse/jetty/websocket/common/events/AbstractEventDriver" -> transformAbstractEventDriver(ctClass) + "org/eclipse/jetty/websocket/common/WebSocketRemoteEndpoint" -> transformRemoteEndpoint(ctClass) + "org/eclipse/jetty/websocket/client/WebSocketClient" -> transformWebSocketClient(ctClass) + "org/eclipse.jetty/websocket/server/HandshakeRFC6455" -> transformHandshakeRFC(ctClass) + } + } + + private fun transformWebSocketSession(ctClass: CtClass) { + try { + ctClass.getMethod("setUpgradeRequest", "(Lorg/eclipse/jetty/websocket/api/UpgradeRequest;)V") + ctClass.getMethod("setUpgradeResponse", "(Lorg/eclipse/jetty/websocket/api/UpgradeResponse;)V") + } catch (e: NotFoundException) { + logger.error { "transformWebSocketSession: Skipping transformation (probably Jetty versions isn't 9): $e" } + return + } + CtField.make( + "private java.util.Map/**/ handshakeHeaders = null;", + ctClass + ).also(ctClass::addField) + CtMethod.make( + """ + public java.util.Map/**/ getHandshakeHeaders() { + return this.handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + ctClass.getMethod("setUpgradeRequest", "(Lorg/eclipse/jetty/websocket/api/UpgradeRequest;)V").insertCatching( + CtBehavior::insertAfter, + """ + if($1 instanceof org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest) { + this.handshakeHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = $1.getHeaders().keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = $1.getHeaders().get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + this.handshakeHeaders.put(headerName, header); + } + } + """.trimIndent() + ) + ctClass.getMethod("setUpgradeResponse", "(Lorg/eclipse/jetty/websocket/api/UpgradeResponse;)V").insertCatching( + CtBehavior::insertAfter, + """ + if($1 instanceof org.eclipse.jetty.websocket.client.ClientUpgradeResponse) { + this.handshakeHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = $1.getHeaders().keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = $1.getHeaders().get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + this.handshakeHeaders.put(headerName, header); + } + } + """.trimIndent() + ) + } + + private fun transformWebSocketConnection(ctClass: CtClass) = CtMethod.make( + """ + public org.eclipse.jetty.websocket.common.WebSocketSession getSession() { + return this.session; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + + private fun transformAbstractEventDriver(ctClass: CtClass) { + val method = ctClass.getMethod("incomingFrame", "(Lorg/eclipse/jetty/websocket/api/extensions/Frame;)V") + method.insertCatching( + CtBehavior::insertBefore, + """ + if (($1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.TEXT || $1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.BINARY) + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.session.getHandshakeHeaders())) { + byte[] bytes = new byte[$1.getPayload().limit()]; + int position = $1.getPayload().position(); + $1.getPayload().get(bytes); + $1.getPayload().position(position); + java.lang.Integer drillIndex = ${this::class.java.name}.INSTANCE.${this::retrieveDrillHeadersIndex.name}(bytes); + if (drillIndex != null) { + $1.getPayload().limit(drillIndex.intValue()); + } + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + if (($1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.TEXT || $1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.BINARY) + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.session.getHandshakeHeaders())) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformRemoteEndpoint(ctClass: CtClass) { + val wrapStringCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(((org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection)this.connection).getSession().getHandshakeHeaders())) { + $1 = ${this::class.java.name}.INSTANCE.storeDrillHeaders($1); + } + """.trimIndent() + val wrapBinaryCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(((org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection)this.connection).getSession().getHandshakeHeaders())) { + byte[] bytes = new byte[$1.limit()]; + $1.get(bytes); + $1.clear(); + $1 = java.nio.ByteBuffer.wrap(${this::class.java.name}.INSTANCE.storeDrillHeaders(bytes)); + } + """.trimIndent() + ctClass.getMethod("sendString", "(Ljava/lang/String;)V") + .insertCatching(CtBehavior::insertBefore, wrapStringCode) + ctClass.getMethod("sendPartialString", "(Ljava/lang/String;Z)V") + .insertCatching(CtBehavior::insertBefore, wrapStringCode) + ctClass.getMethod("sendStringByFuture", "(Ljava/lang/String;)Ljava/util/concurrent/Future;") + .insertCatching(CtBehavior::insertBefore, wrapStringCode) + ctClass.getMethod("sendBytes", "(Ljava/nio/ByteBuffer;)V") + .insertCatching(CtBehavior::insertBefore, wrapBinaryCode) + ctClass.getMethod("sendPartialBytes", "(Ljava/nio/ByteBuffer;Z)V") + .insertCatching(CtBehavior::insertBefore, wrapBinaryCode) + ctClass.getMethod("sendBytesByFuture", "(Ljava/nio/ByteBuffer;)Ljava/util/concurrent/Future;") + .insertCatching(CtBehavior::insertBefore, wrapBinaryCode) + ctClass.getMethod( + "uncheckedSendFrame", + "(Lorg/eclipse/jetty/websocket/common/WebSocketFrame;Lorg/eclipse/jetty/websocket/api/WriteCallback;)V" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(((org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection)this.connection).getSession().getHandshakeHeaders())) { + byte[] bytes = new byte[$1.getPayload().limit()]; + $1.getPayload().get(bytes); + $1.getPayload().clear(); + $1.setPayload(java.nio.ByteBuffer.wrap(${this::class.java.name}.INSTANCE.storeDrillHeaders(bytes))); + } + """.trimIndent() + ) + } + + private fun transformWebSocketClient(ctClass: CtClass) = ctClass + .getMethod( + "connect", + "(Ljava/lang/Object;Ljava/net/URI;Lorg/eclipse/jetty/websocket/client/ClientUpgradeRequest;Lorg/eclipse/jetty/websocket/client/io/UpgradeListener;)Ljava/util/concurrent/Future;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + $3.setHeader("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + + private fun transformHandshakeRFC(ctClass: CtClass) = ctClass + .getMethod( + "doHandshakeResponse", + "(Lorg/eclipse/jetty/websocket/servlet/ServletUpgradeRequest;Lorg/eclipse/jetty/websocket/servlet/ServletUpgradeResponse;)V" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + $2.setHeader("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/JettyWsClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/JettyWsClientTransformerObject.kt new file mode 100644 index 00000000..debdb001 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/JettyWsClientTransformerObject.kt @@ -0,0 +1,86 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.jetty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class JettyWsClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + listOf( + "org/eclipse/jetty/websocket/client/WebSocketClient", + "org/eclipse/jetty/websocket/core/client/WebSocketCoreClient" + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting JettyWsClientTransformer for $className..." } + when (className) { + "org/eclipse/jetty/websocket/client/WebSocketClient" -> transformWebSocketClient(ctClass) + "org/eclipse/jetty/websocket/core/client/WebSocketCoreClient" -> transformWebSocketCoreClient(ctClass) + } + } + + private fun transformWebSocketClient(ctClass: CtClass) = ctClass + .getMethod( + "connect", + "(Ljava/lang/Object;Ljava/net/URI;Lorg/eclipse/jetty/websocket/client/ClientUpgradeRequest;Lorg/eclipse/jetty/websocket/client/io/UpgradeListener;)Ljava/util/concurrent/Future;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + $3.setHeader((String) entry.getKey(), (String) entry.getValue()); + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + + private fun transformWebSocketCoreClient(ctClass: CtClass) = ctClass + .getMethod( + "connect", + "(Lorg/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest;)Ljava/util/concurrent/CompletableFuture;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + $1.addHeader(new org.eclipse.jetty.http.HttpField((String) entry.getKey(), (String) entry.getValue())); + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/JettyWsServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/JettyWsServerTransformerObject.kt new file mode 100644 index 00000000..3bd73272 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/jetty/JettyWsServerTransformerObject.kt @@ -0,0 +1,199 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.jetty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtNewMethod +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +/** + * Transformer for Jetty based websockets + * + * Tested with: + * org.eclipse.jetty.websocket:javax-websocket-server-impl:9.4.26.v20200117 + */ +abstract class JettyWsServerTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + listOf( + "org/eclipse/jetty/websocket/common/events/AbstractEventDriver", + "org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler", + "org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler", + "org/eclipse/jetty/websocket/javax/common/UpgradeRequest", + "org/eclipse/jetty/websocket/javax/client/internal/JavaxClientUpgradeRequest", + "org/eclipse/jetty/websocket/javax/server/internal/JavaxServerUpgradeRequest" + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting JettyWsServerTransformerObject for $className..." } + when (className) { + "org/eclipse/jetty/websocket/common/events/AbstractEventDriver" -> transformAbstractEventDriver(ctClass) + "org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler" -> transformJettyWebSocketFrameHandler( + ctClass + ) + + "org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler" -> transformJavaxWebSocketFrameHandler( + ctClass + ) + + "org/eclipse/jetty/websocket/javax/common/UpgradeRequest" -> transformUpgradeRequest(ctClass) + "org/eclipse/jetty/websocket/javax/client/internal/JavaxClientUpgradeRequest" -> transformJavaxClientUpgradeRequest( + ctClass + ) + + "org/eclipse/jetty/websocket/javax/server/internal/JavaxServerUpgradeRequest" -> transformJavaxServerUpgradeRequest( + ctClass + ) + } + } + + private fun transformAbstractEventDriver(ctClass: CtClass) { + val method = ctClass.getMethod("incomingFrame", "(Lorg/eclipse/jetty/websocket/api/extensions/Frame;)V") + method.insertCatching( + CtBehavior::insertBefore, + """ + if (($1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.TEXT || $1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.BINARY) + && this.session.getUpgradeRequest() instanceof org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest) { + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = this.session.getUpgradeRequest().getHeaders().keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = this.session.getUpgradeRequest().getHeaders().get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + allHeaders.put(headerName, header); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + if (($1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.TEXT || $1.getOpCode() == org.eclipse.jetty.websocket.common.OpCode.BINARY) + && this.session.getUpgradeRequest() instanceof org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformJettyWebSocketFrameHandler(ctClass: CtClass) { + val method = ctClass.getMethod( + "acceptMessage", + "(Lorg/eclipse/jetty/websocket/core/Frame;Lorg/eclipse/jetty/util/Callback;)V" + ) + method.insertCatching( + CtBehavior::insertBefore, + """ + if ($1.isDataFrame()) { + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Map/*>*/ upgradeHeaders = this.upgradeRequest.getHeaders(); + java.util.Iterator/**/ headerNames = upgradeHeaders.keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = upgradeHeaders.get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + allHeaders.put(headerName, header); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + if ($1.isDataFrame() && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformJavaxWebSocketFrameHandler(ctClass: CtClass) { + val method = ctClass.getMethod( + "acceptMessage", + "(Lorg/eclipse/jetty/websocket/core/Frame;Lorg/eclipse/jetty/util/Callback;)V" + ) + method.insertCatching( + CtBehavior::insertBefore, + """ + if ($1.isDataFrame() && this.upgradeRequest.getHeadersMap() != null ) { + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Map/*>*/ upgradeHeaders = this.upgradeRequest.getHeadersMap(); + java.util.Iterator/**/ headerNames = upgradeHeaders.keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = upgradeHeaders.get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + allHeaders.put(headerName, header); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + if ($1.isDataFrame() && this.upgradeRequest.getHeadersMap() != null && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformUpgradeRequest(ctClass: CtClass) { + CtNewMethod.abstractMethod( + ctClass.classPool.get("java.util.Map"), + "getHeadersMap", + null, + null, + ctClass + ).also(ctClass::addMethod) + } + + private fun transformJavaxClientUpgradeRequest(ctClass: CtClass) { + CtNewMethod.make( + """ + public java.util.Map/*>*/ getHeadersMap() { + return null; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + } + + private fun transformJavaxServerUpgradeRequest(ctClass: CtClass) { + CtNewMethod.make( + """ + public java.util.Map/*>*/ getHeadersMap() { + return this.servletRequest.getHeadersMap(); + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsClientTransformerObject.kt new file mode 100644 index 00000000..6a13aba1 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsClientTransformerObject.kt @@ -0,0 +1,131 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.netty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class NettyWsClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + listOf( + "io/netty/bootstrap/Bootstrap", + "io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker", + "org/springframework/web/reactive/socket/client/ReactorNettyWebSocketClient" + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting NettyWsClientTransformer for $className..." } + when (className) { + "io/netty/bootstrap/Bootstrap" -> transformBootstrap(ctClass) + "io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker" -> transformClientHandshaker(ctClass) + "org/springframework/web/reactive/socket/client/ReactorNettyWebSocketClient" -> transformReactorNettyWebSocketClient( + ctClass + ) + } + } + + private fun transformBootstrap(ctClass: CtClass) = ctClass + .getMethod( + "doResolveAndConnect", + "(Ljava/net/SocketAddress;Ljava/net/SocketAddress;)Lio/netty/channel/ChannelFuture;" + ) + .insertCatching( + CtBehavior::insertAfter, + """ + if(${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map drillHeaders = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_WS_CONTEXT_KEY"); + ${'$'}_.channel().attr(drillContextKey).set(drillHeaders); + } + """.trimIndent() + ) + + private fun transformClientHandshaker(ctClass: CtClass) { + ctClass.getMethod( + "handshake", + "(Lio/netty/channel/Channel;Lio/netty/channel/ChannelPromise;)Lio/netty/channel/ChannelFuture;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_WS_CONTEXT_KEY"); + io.netty.util.Attribute drillContextAttr = $1.attr(drillContextKey); + java.util.Map drillHeaders = (java.util.Map) drillContextAttr.get(); + if (drillHeaders != null) { + java.util.Iterator iterator = drillHeaders.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + if (!customHeaders.contains((String) entry.getKey())) { + customHeaders.add((String) entry.getKey(), entry.getValue()); + } + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(drillHeaders); + } + """.trimIndent() + ) + ctClass.getMethod( + "finishHandshake", + "(Lio/netty/channel/Channel;Lio/netty/handler/codec/http/FullHttpResponse;)V" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + java.util.Iterator headerNames = $2.headers().names().iterator(); + java.util.Map allHeaders = new java.util.HashMap(); + while (headerNames.hasNext()) { + java.lang.String headerName = (String) headerNames.next(); + java.lang.String headerValue = $2.headers().get(headerName); + allHeaders.put(headerName, headerValue); + } + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_WS_CONTEXT_KEY"); + $1.attr(drillContextKey).set(allHeaders); + """.trimIndent() + ) + } + + private fun transformReactorNettyWebSocketClient(ctClass: CtClass) = ctClass + .getMethod( + "setNettyHeaders", + "(Lorg/springframework/http/HttpHeaders;Lio/netty/handler/codec/http/HttpHeaders;)V" + ) + .insertCatching( + CtBehavior::insertAfter, + """ + if(${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map drillHeaders = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = drillHeaders.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + if (!$2.contains((String) entry.getKey())) { + $2.add((String) entry.getKey(), entry.getValue()); + } + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(drillHeaders); + } + """.trimIndent() + ) + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsMessagesTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsMessagesTransformerObject.kt new file mode 100644 index 00000000..6466560a --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsMessagesTransformerObject.kt @@ -0,0 +1,149 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.netty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import javassist.ClassPool +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.PayloadProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject +import java.security.ProtectionDomain + +abstract class NettyWsMessagesTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, PayloadProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + listOf( + "io/netty/channel/AbstractChannelHandlerContext", + "io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker", + "io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker" + ).contains(className) + + override fun transform( + className: String, + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + if (pool.find(WEBSOCKET_FRAME_BINARY) == null) { + logger.debug { "transform: Skipping $className because $WEBSOCKET_FRAME_BINARY class is not available" } + return + } + if (pool.find(WEBSOCKET_FRAME_TEXT) == null) { + logger.debug { "transform: Skipping $className because $WEBSOCKET_FRAME_TEXT class is not available" } + return + } + logger.info { "transform: Starting NettyWsMessagesTransformer for $className..." } + when (className) { + "io/netty/channel/AbstractChannelHandlerContext" -> transformChannelHandlerContext(ctClass) + "io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker" -> transformServerHandshaker(ctClass) + "io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker" -> transformClientHandshaker(ctClass) + } + } + + private fun transformChannelHandlerContext(ctClass: CtClass) { + val invokeChannelReadMethod = ctClass.getMethod("invokeChannelRead", "(Ljava/lang/Object;)V") + invokeChannelReadMethod.insertCatching( + CtBehavior::insertBefore, + """ + if (($1 instanceof $WEBSOCKET_FRAME_BINARY || $1 instanceof $WEBSOCKET_FRAME_TEXT) && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_WS_CONTEXT_KEY"); + io.netty.util.Attribute drillContextAttr = this.channel().attr(drillContextKey); + java.util.Map drillHeaders = (java.util.Map) drillContextAttr.get(); + if(${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(drillHeaders)) { + io.netty.buffer.ByteBuf messageBuf = (($WEBSOCKET_FRAME_COMMON)$1).content(); + byte[] messageBytes = new byte[messageBuf.readableBytes()]; + int readerIndex = messageBuf.readerIndex(); + messageBuf.readBytes(messageBytes); + messageBuf.readerIndex(readerIndex); + java.lang.Integer drillIndex = ${this::class.java.name}.INSTANCE.${this::retrieveDrillHeadersIndex.name}(messageBytes); + if (drillIndex != null) { + $1 = (($WEBSOCKET_FRAME_COMMON)$1).replace(messageBuf.slice(0, drillIndex.intValue())); + } + } + } + """.trimIndent() + ) + invokeChannelReadMethod.insertCatching( + { insertAfter(it, true) }, + """ + if ($1 instanceof $WEBSOCKET_FRAME_BINARY || $1 instanceof $WEBSOCKET_FRAME_TEXT && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + ctClass.getMethod("write", "(Ljava/lang/Object;ZLio/netty/channel/ChannelPromise;)V").insertCatching( + CtBehavior::insertBefore, + """ + if (($1 instanceof $WEBSOCKET_FRAME_BINARY || $1 instanceof $WEBSOCKET_FRAME_TEXT) + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_WS_CONTEXT_KEY"); + io.netty.util.Attribute drillContextAttr = this.channel().attr(drillContextKey); + java.util.Map drillHeaders = (java.util.Map) drillContextAttr.get(); + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(drillHeaders)) { + io.netty.buffer.ByteBuf messageBuf = (($WEBSOCKET_FRAME_COMMON)$1).content(); + byte[] messageBytes = new byte[messageBuf.readableBytes()]; + messageBuf.readBytes(messageBytes); + messageBytes = ${this::class.java.name}.INSTANCE.storeDrillHeaders(messageBytes); + (($WEBSOCKET_FRAME_COMMON)$1).release(); + $1 = (($WEBSOCKET_FRAME_COMMON)$1).replace(io.netty.buffer.Unpooled.wrappedBuffer(messageBytes)); + } + } + """.trimIndent() + ) + } + + private fun transformServerHandshaker(ctClass: CtClass) { + val sendPerMessageHeaderCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + if ($3 == null) $3 = new io.netty.handler.codec.http.DefaultHttpHeaders(); + $3.set("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ctClass.getMethod( + "handshake", + "(Lio/netty/channel/Channel;Lio/netty/handler/codec/http/HttpRequest;Lio/netty/handler/codec/http/HttpHeaders;Lio/netty/channel/ChannelPromise;)Lio/netty/channel/ChannelFuture;" + ).insertCatching(CtBehavior::insertBefore, sendPerMessageHeaderCode) + ctClass.getMethod( + "handshake", + "(Lio/netty/channel/Channel;Lio/netty/handler/codec/http/FullHttpRequest;Lio/netty/handler/codec/http/HttpHeaders;Lio/netty/channel/ChannelPromise;)Lio/netty/channel/ChannelFuture;" + ).insertCatching(CtBehavior::insertBefore, sendPerMessageHeaderCode) + } + + private fun transformClientHandshaker(ctClass: CtClass) { + ctClass.getMethod( + "handshake", + "(Lio/netty/channel/Channel;Lio/netty/channel/ChannelPromise;)Lio/netty/channel/ChannelFuture;" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + this.customHeaders.add("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsServerTransformerObject.kt new file mode 100644 index 00000000..e5dee62f --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/netty/NettyWsServerTransformerObject.kt @@ -0,0 +1,117 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.netty + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import javassist.ClassPool +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject +import java.security.ProtectionDomain + +/** + * Transformer for simple Netty-based web servers + * + * Tested with: + * io.netty:netty-codec-http:4.1.106.Final + */ +abstract class NettyWsServerTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + listOf( + "io/netty/channel/AbstractChannelHandlerContext", + "io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker" + ).contains(className) + + override fun transform( + className: String, + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + if (pool.find(WEBSOCKET_FRAME_BINARY) == null) { + logger.debug { "transform: Skipping $className because $WEBSOCKET_FRAME_BINARY class is not available" } + return + } + if (pool.find(WEBSOCKET_FRAME_TEXT) == null) { + logger.debug { "transform: Skipping $className because $WEBSOCKET_FRAME_TEXT class is not available" } + return + } + logger.info { "transform: Starting NettyWsServerTransformerObject for $className..." } + when (className) { + "io/netty/channel/AbstractChannelHandlerContext" -> transformChannelHandlerContext(ctClass) + "io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker" -> transformServerHandshaker(ctClass) + } + } + + private fun transformChannelHandlerContext(ctClass: CtClass) { + val invokeChannelReadMethod = ctClass.getMethod("fireChannelRead", "(Ljava/lang/Object;)Lio/netty/channel/ChannelHandlerContext;") + invokeChannelReadMethod.insertCatching( + CtBehavior::insertBefore, + """ + if($1 instanceof $WEBSOCKET_FRAME_BINARY || $1 instanceof $WEBSOCKET_FRAME_TEXT) { + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_WS_CONTEXT_KEY"); + io.netty.util.Attribute drillContextAttr = this.channel().attr(drillContextKey); + java.util.Map drillHeaders = (java.util.Map) drillContextAttr.get(); + if (drillHeaders != null) { + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(drillHeaders); + } + } + """.trimIndent() + ) + invokeChannelReadMethod.insertCatching( + { insertAfter(it, true) }, + """ + if ($1 instanceof $WEBSOCKET_FRAME_BINARY || $1 instanceof $WEBSOCKET_FRAME_TEXT) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformServerHandshaker(ctClass: CtClass) { + val storeHandshakerAndHeadersCode = + """ + java.util.Iterator headerNames = $2.headers().names().iterator(); + java.util.Map allHeaders = new java.util.HashMap(); + while(headerNames.hasNext()){ + java.lang.String headerName = (String) headerNames.next(); + java.lang.String headerValue = $2.headers().get(headerName); + allHeaders.put(headerName, headerValue); + } + io.netty.util.AttributeKey drillContextKey = io.netty.util.AttributeKey.valueOf("$DRILL_WS_CONTEXT_KEY"); + $1.attr(drillContextKey).set(allHeaders); + if(io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.getHandshaker($1) == null) { + io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.setHandshaker($1, this); + } + """.trimIndent() + ctClass.getMethod( + "handshake", + "(Lio/netty/channel/Channel;Lio/netty/handler/codec/http/HttpRequest;Lio/netty/handler/codec/http/HttpHeaders;Lio/netty/channel/ChannelPromise;)Lio/netty/channel/ChannelFuture;" + ).insertCatching(CtBehavior::insertBefore, storeHandshakerAndHeadersCode) + ctClass.getMethod( + "handshake", + "(Lio/netty/channel/Channel;Lio/netty/handler/codec/http/FullHttpRequest;Lio/netty/handler/codec/http/HttpHeaders;Lio/netty/channel/ChannelPromise;)Lio/netty/channel/ChannelFuture;" + ).insertCatching(CtBehavior::insertBefore, storeHandshakerAndHeadersCode) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsClientTransformerObject.kt new file mode 100644 index 00000000..e0d2ee8a --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsClientTransformerObject.kt @@ -0,0 +1,64 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.tomcat + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtMethod +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class TomcatWsClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + "org/apache/tomcat/websocket/WsWebSocketContainer" == className + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting TomcatWsClientTransformer..." } + val signatures = sequenceOf( + "(Ljava/lang/String;IZLjakarta/websocket/ClientEndpointConfig;)Ljava/util/Map;", + "(Ljava/lang/String;IZLjavax/websocket/ClientEndpointConfig;)Ljava/util/Map;", + "(Ljava/lang/String;ILjavax/websocket/ClientEndpointConfig;)Ljava/util/Map;" + ) + val getMethod: (String) -> CtMethod? = { + ctClass + .runCatching { this.getMethod("createRequestHeaders", it) } + .getOrNull() + } + signatures.mapNotNull(getMethod).first().insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + ${'$'}_.put((String) entry.getKey(), java.util.Collections.singletonList((String) entry.getValue())); + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsMessagesTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsMessagesTransformerObject.kt new file mode 100644 index 00000000..67a9ff34 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsMessagesTransformerObject.kt @@ -0,0 +1,290 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.tomcat + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import javassist.ClassPool +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtField +import javassist.CtMethod +import javassist.NotFoundException +import mu.KotlinLogging +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.PayloadProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject +import java.security.ProtectionDomain + +abstract class TomcatWsMessagesTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, PayloadProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + private var openingSessionHeaders: ThreadLocal?> = ThreadLocal() + + override fun permit(className: String, superName: String?, interfaces: Array) = listOf( + "org/apache/tomcat/websocket/WsSession", + "org/apache/tomcat/websocket/WsFrameBase", + "org/apache/tomcat/websocket/WsRemoteEndpointImplBase", + "org/apache/tomcat/websocket/WsWebSocketContainer", + "org/apache/tomcat/websocket/server/WsHttpUpgradeHandler", + "org/apache/tomcat/websocket/server/UpgradeUtil" + ).contains(className) + + override fun transform( + className: String, + ctClass: CtClass, + pool: ClassPool, + classLoader: ClassLoader?, + protectionDomain: ProtectionDomain? + ) { + val wsSessionClass = "org.apache.tomcat.websocket.WsSession" + if (pool.find(wsSessionClass) == null) { + logger.debug { "transform: Skipping $className because $wsSessionClass class is not available" } + return + } + logger.info { "transform: Starting TomcatWsMessagesTransformer for $className..." } + if (className != "org/apache/tomcat/websocket/WsSession") + ctClass.classPool.classLoader.loadClass(wsSessionClass) + when (className) { + "org/apache/tomcat/websocket/WsSession" -> transformWsSession(ctClass) + "org/apache/tomcat/websocket/WsFrameBase" -> transformWsFrameBase(ctClass) + "org/apache/tomcat/websocket/WsRemoteEndpointImplBase" -> transformRemoteEndpointImplBase(ctClass) + "org/apache/tomcat/websocket/WsWebSocketContainer" -> transformWebSocketContainer(ctClass) + "org/apache/tomcat/websocket/server/WsHttpUpgradeHandler" -> transformUpgradeHandler(ctClass) + "org/apache/tomcat/websocket/server/UpgradeUtil" -> transformUpgradeUtil(ctClass) + } + } + + @Suppress("MemberVisibilityCanBePrivate") + fun setHandshakeHeaders(headers: Map?) = openingSessionHeaders.set(headers) + + @Suppress("MemberVisibilityCanBePrivate") + fun getHandshakeHeaders() = openingSessionHeaders.get() + + private fun transformWsSession(ctClass: CtClass) { + CtField.make( + "private java.util.Map/**/ handshakeHeaders = null;", + ctClass + ).also(ctClass::addField) + CtMethod.make( + """ + public void setHandshakeHeaders(java.util.Map/**/ handshakeHeaders) { + this.handshakeHeaders = handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + CtMethod.make( + """ + public java.util.Map/**/ getHandshakeHeaders() { + return this.handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + } + + private fun transformUpgradeHandler(ctClass: CtClass) { + val method = try { + ctClass.getMethod("init", "(Ljakarta/servlet/http/WebConnection;)V") + } catch (e: NotFoundException) { + ctClass.getMethod("init", "(Ljavax/servlet/http/WebConnection;)V") + } + method.insertCatching( + CtBehavior::insertAfter, + """ + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = this.handshakeRequest.getHeaders().keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = this.handshakeRequest.getHeaders().get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + allHeaders.put(headerName, header); + } + this.wsSession.setHandshakeHeaders(allHeaders); + """.trimIndent() + ) + } + + private fun transformWsFrameBase(ctClass: CtClass) { + val sendTextMethod = ctClass.getMethod("sendMessageText", "(Z)V") + val sendBinaryMethod = ctClass.getMethod("sendMessageBinary", "(Ljava/nio/ByteBuffer;Z)V") + CtField.make( + "private java.nio.CharBuffer messageBufferTextTmp = null;", + ctClass + ).also(ctClass::addField) + sendTextMethod.insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.wsSession.getHandshakeHeaders())) { + java.lang.String retrieved = ${this::class.java.name}.INSTANCE.retrieveDrillHeaders(this.messageBufferText.toString()); + this.messageBufferTextTmp = messageBufferText; + this.messageBufferText = java.nio.CharBuffer.wrap(retrieved); + } + """.trimIndent() + ) + sendBinaryMethod.insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.wsSession.getHandshakeHeaders())) { + byte[] bytes = new byte[$1.limit()]; + $1.get(bytes); + $1 = java.nio.ByteBuffer.wrap(${this::class.java.name}.INSTANCE.retrieveDrillHeaders(bytes)); + } + """.trimIndent() + ) + sendTextMethod.insertCatching( + { insertAfter(it, true) }, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.wsSession.getHandshakeHeaders())) { + this.messageBufferText = messageBufferTextTmp; + this.messageBufferText.clear(); + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + sendBinaryMethod.insertCatching( + { insertAfter(it, true) }, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.wsSession.getHandshakeHeaders())) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + + private fun transformRemoteEndpointImplBase(ctClass: CtClass) { + val wrapStringCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.wsSession.getHandshakeHeaders())) { + $1 = ${this::class.java.name}.INSTANCE.storeDrillHeaders($1); + } + """.trimIndent() + val wrapBinaryCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.wsSession.getHandshakeHeaders())) { + byte[] bytes = new byte[$1.limit()]; + $1.get(bytes); + $1.clear(); + $1 = java.nio.ByteBuffer.wrap(${this::class.java.name}.INSTANCE.storeDrillHeaders(bytes)); + } + """.trimIndent() + ctClass.getMethod("sendString", "(Ljava/lang/String;)V") + .insertCatching(CtBehavior::insertBefore, wrapStringCode) + ctClass.getMethod("sendPartialString", "(Ljava/lang/String;Z)V") + .insertCatching(CtBehavior::insertBefore, wrapStringCode) + ctClass.getMethod("sendBytes", "(Ljava/nio/ByteBuffer;)V") + .insertCatching(CtBehavior::insertBefore, wrapBinaryCode) + ctClass.getMethod("sendPartialBytes", "(Ljava/nio/ByteBuffer;Z)V") + .insertCatching(CtBehavior::insertBefore, wrapBinaryCode) + try { + ctClass.getMethod("sendStringByCompletion", "(Ljava/lang/String;Ljakarta/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, wrapStringCode) + ctClass.getMethod("sendBytesByCompletion", "(Ljava/nio/ByteBuffer;Ljakarta/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, wrapBinaryCode) + } catch (e: NotFoundException) { + ctClass.getMethod("sendStringByCompletion", "(Ljava/lang/String;Ljavax/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, wrapStringCode) + ctClass.getMethod("sendBytesByCompletion", "(Ljava/nio/ByteBuffer;Ljavax/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, wrapBinaryCode) + } + } + + private fun transformWebSocketContainer(ctClass: CtClass) { + val createRequestHeadersMethodSignatures = sequenceOf( + "(Ljava/lang/String;IZLjakarta/websocket/ClientEndpointConfig;)Ljava/util/Map;", + "(Ljava/lang/String;IZLjavax/websocket/ClientEndpointConfig;)Ljava/util/Map;", + "(Ljava/lang/String;ILjavax/websocket/ClientEndpointConfig;)Ljava/util/Map;" + ) + val connectToServerRecursiveMethodSignatures = sequenceOf( + "(Lorg/apache/tomcat/websocket/ClientEndpointHolder;Ljakarta/websocket/ClientEndpointConfig;Ljava/net/URI;Ljava/util/Set;)Ljakarta/websocket/Session;", + "(Lorg/apache/tomcat/websocket/ClientEndpointHolder;Ljavax/websocket/ClientEndpointConfig;Ljava/net/URI;Ljava/util/Set;)Ljavax/websocket/Session;", + "(Ljavax/websocket/Endpoint;Ljavax/websocket/ClientEndpointConfig;Ljava/net/URI;Ljava/util/Set;)Ljavax/websocket/Session;" + ) + val getCreateRequestHeadersMethod: (String) -> CtMethod? = { + ctClass + .runCatching { this.getMethod("createRequestHeaders", it) } + .getOrNull() + } + val getConnectToServerRecursiveMethod: (String) -> CtMethod? = { + ctClass + .runCatching { this.getMethod("connectToServerRecursive", it) } + .getOrNull() + } + createRequestHeadersMethodSignatures.mapNotNull(getCreateRequestHeadersMethod).first().insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + ${'$'}_.put("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", java.util.Collections.singletonList("true")); + } + """.trimIndent() + ) + connectToServerRecursiveMethodSignatures.mapNotNull(getConnectToServerRecursiveMethod).first().insertCatching( + CtBehavior::insertAfter, + """ + ((org.apache.tomcat.websocket.WsSession)${'$'}_).setHandshakeHeaders(${this::class.java.name}.INSTANCE.${this::getHandshakeHeaders.name}()); + ${this::class.java.name}.INSTANCE.${this::setHandshakeHeaders.name}(null); + """.trimIndent() + ) + ctClass.getMethod( + "processResponse", + "(Ljava/nio/ByteBuffer;Lorg/apache/tomcat/websocket/AsyncChannelWrapper;J)Lorg/apache/tomcat/websocket/WsWebSocketContainer\$HttpResponse;" + ) + .insertCatching( + CtBehavior::insertAfter, + """ + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = ${'$'}_.getHandshakeResponse().getHeaders().keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = ${'$'}_.getHandshakeResponse().getHeaders().get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + allHeaders.put(headerName, header); + } + ${this::class.java.name}.INSTANCE.${this::setHandshakeHeaders.name}(allHeaders); + """.trimIndent() + ) + } + + private fun transformUpgradeUtil(ctClass: CtClass) { + val signatures = sequenceOf( + "(Lorg/apache/tomcat/websocket/server/WsServerContainer;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Ljakarta/websocket/server/ServerEndpointConfig;Ljava/util/Map;)V", + "(Lorg/apache/tomcat/websocket/server/WsServerContainer;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/websocket/server/ServerEndpointConfig;Ljava/util/Map;)V" + ) + val getMethod: (String) -> CtMethod? = { + ctClass + .runCatching { this.getMethod("doUpgrade", it) } + .getOrNull() + } + signatures.mapNotNull(getMethod).first().insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + $3.setHeader("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsServerTransformerObject.kt new file mode 100644 index 00000000..61cc61a5 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/tomcat/TomcatWsServerTransformerObject.kt @@ -0,0 +1,73 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.tomcat + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +/** + * Transformer for Tomcat based websockets + * + * Tested with: + * org.apache.tomcat.embed:tomcat-embed-websocket:9.0.83 + */ +abstract class TomcatWsServerTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = + "org/apache/tomcat/websocket/server/WsHttpUpgradeHandler" == className + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting TomcatWsServerTransformer..." } + val method = ctClass.getMethod( + "upgradeDispatch", + "(Lorg/apache/tomcat/util/net/SocketEvent;)Lorg/apache/tomcat/util/net/AbstractEndpoint\$Handler\$SocketState;" + ) + method.insertCatching( + CtBehavior::insertBefore, + """ + if ($1 == org.apache.tomcat.util.net.SocketEvent.OPEN_READ) { + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = this.handshakeRequest.getHeaders().keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = this.handshakeRequest.getHeaders().get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + allHeaders.put(headerName, header); + } + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(allHeaders); + } + """.trimIndent() + ) + method.insertCatching( + { insertAfter(it, true) }, + """ + if ($1 == org.apache.tomcat.util.net.SocketEvent.OPEN_READ && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsClientTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsClientTransformerObject.kt new file mode 100644 index 00000000..0fb28eb4 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsClientTransformerObject.kt @@ -0,0 +1,129 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.undertow + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtField +import javassist.CtMethod +import javassist.NotFoundException +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class UndertowWsClientTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + override fun permit(className: String, superName: String?, interfaces: Array) = listOf( + "io/undertow/websockets/jsr/UndertowSession", + "io/undertow/websockets/jsr/ServerWebSocketContainer\$ClientNegotiation" + ).contains(className) || "io/undertow/websockets/client/WebSocketClientHandshake" == superName + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting UndertowWsClientTransformer for $className..." } + when (className) { + "io/undertow/websockets/jsr/UndertowSession" -> transformSession(ctClass) + "io/undertow/websockets/jsr/ServerWebSocketContainer\$ClientNegotiation" -> transformClientNegotiation( + ctClass + ) + } + when (ctClass.superclass.name) { + "io.undertow.websockets.client.WebSocketClientHandshake" -> transformClientHandshake(ctClass) + } + } + + private fun transformSession(ctClass: CtClass) { + try { + ctClass.getField("handshakeHeaders") + } catch (e: NotFoundException) { + CtField.make( + "private java.util.Map/**/ handshakeHeaders = null;", + ctClass + ).also(ctClass::addField) + CtMethod.make( + """ + public java.util.Map/**/ getHandshakeHeaders() { + return this.handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + } + ctClass.constructors[0].insertCatching( + CtBehavior::insertAfter, + """ + if (this.clientConnectionBuilder != null) { + java.util.Map/**/ responseHeaders = + ((io.undertow.websockets.jsr.ServerWebSocketContainer.ClientNegotiation) this.clientConnectionBuilder.getClientNegotiation()).getResponseHeaders(); + if (this.handshakeHeaders == null && responseHeaders != null) { + this.handshakeHeaders = responseHeaders; + } + } + """.trimIndent() + ) + } + + private fun transformClientHandshake(ctClass: CtClass) { + ctClass.getMethod("createHeaders", "()Ljava/util/Map;").insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::hasHeaders.name}()) { + java.util.Map headers = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + java.util.Iterator iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next(); + ${'$'}_.put((String) entry.getKey(), (String) entry.getValue()); + } + ${this::class.java.name}.INSTANCE.${this::logInjectingHeaders.name}(headers); + } + """.trimIndent() + ) + } + + private fun transformClientNegotiation(ctClass: CtClass) { + CtField.make( + "private java.util.Map/**/ responseHeaders = null;", + ctClass + ).also(ctClass::addField) + CtMethod.make( + """ + public java.util.Map/**/ getResponseHeaders() { + return this.responseHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + ctClass.getMethod("afterRequest", "(Ljava/util/Map;)V").insertCatching( + CtBehavior::insertAfter, + """ + this.responseHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = $1.keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = $1.get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + this.responseHeaders.put(headerName, header); + } + """.trimIndent() + ) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsMessagesProxyDelegate.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsMessagesProxyDelegate.kt new file mode 100644 index 00000000..56393d4e --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsMessagesProxyDelegate.kt @@ -0,0 +1,107 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.undertow + +import java.lang.reflect.Method +import java.nio.ByteBuffer +import javassist.ClassPool +import mu.KotlinLogging +import net.bytebuddy.implementation.MethodCall +import net.bytebuddy.implementation.bind.annotation.FieldValue +import net.bytebuddy.implementation.bind.annotation.Origin +import net.bytebuddy.implementation.bind.annotation.RuntimeType +import com.epam.drill.agent.instrument.AbstractWsMessagesProxyDelegate +import com.epam.drill.agent.instrument.PayloadProcessor + +class UndertowWsMessagesProxyDelegate( + private val payloadProcessor: PayloadProcessor +) : AbstractWsMessagesProxyDelegate() { + + private val logger = KotlinLogging.logger {} + private var pooledProxyClass: Class<*>? = null + private var textMessageProxyClass: Class<*>? = null + private var binaryMessageProxyClass: Class<*>? = null + + @RuntimeType + fun delegatedTextMessageData( + @Origin method: Method, + @FieldValue("target") target: Any + ) = (method.invoke(target) as String) + .also { logger.trace { "delegatedTextMessageData: Payload received: $it" } } + .let(payloadProcessor::retrieveDrillHeaders) + + @RuntimeType + fun delegatedBinaryMessageData( + @Origin method: Method, + @FieldValue("target") target: Any + ) = method.invoke(target).let { pooledProxyClass!!.constructors[0].newInstance(it)!! } + + @RuntimeType + @Suppress("unchecked_cast") + fun delegatedPooledResource( + @Origin method: Method, + @FieldValue("target") target: Any + ) = (method.invoke(target) as Array).let { buffers -> + val isSimpleArray = buffers.size == 1 && + buffers[0].hasArray() && + buffers[0].arrayOffset() == 0 && + buffers[0].position() == 0 && + buffers[0].array().size == buffers[0].remaining() + val array: ByteArray = if (isSimpleArray) { + buffers[0].array() + } + else { + Class.forName("org.xnio.Buffers", true, target::class.java.classLoader) + .getMethod("take", Array::class.java, Int::class.java, Int::class.java) + .invoke(null, buffers, 0, buffers.size) as ByteArray + } + array.also { logger.trace { "delegatedPooledResource: Payload received: ${it.decodeToString()}" } } + .let(payloadProcessor::retrieveDrillHeaders) + .let(ByteBuffer::wrap) + .let { arrayOf(it) } + } + + fun getTextMessageProxy(classPool: ClassPool) = textMessageProxyClass + ?: createTextMessageProxy(classPool).also(::textMessageProxyClass::set) + + fun getBinaryMessageProxy(classPool: ClassPool) = binaryMessageProxyClass + ?: createBinaryMessageProxy(classPool).also(::binaryMessageProxyClass::set).also { getPooledProxy(classPool) } + + private fun getPooledProxy(classPool: ClassPool) = pooledProxyClass + ?: createPooledProxy(classPool).also(::pooledProxyClass::set) + + private fun createTextMessageProxy(classPool: ClassPool) = createDelegatedGetterProxy( + "io.undertow.websockets.core.BufferedTextMessage", + "getData", + ::delegatedTextMessageData, + classPool + ) { MethodCall.invoke(it.getConstructor(Boolean::class.java)).with(false) } + + private fun createBinaryMessageProxy(classPool: ClassPool) = createDelegatedGetterProxy( + "io.undertow.websockets.core.BufferedBinaryMessage", + "getData", + ::delegatedBinaryMessageData, + classPool + ) { MethodCall.invoke(it.getConstructor(Boolean::class.java)).with(false) } + + private fun createPooledProxy(classPool: ClassPool) = createDelegatedGetterProxy( + "org.xnio.Pooled", + "getResource", + ::delegatedPooledResource, + classPool, + ) { MethodCall.invoke(Any::class.java.getConstructor()) } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsMessagesTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsMessagesTransformerObject.kt new file mode 100644 index 00000000..e2543394 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsMessagesTransformerObject.kt @@ -0,0 +1,296 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.undertow + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtMethod +import javassist.NotFoundException +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.instrument.PayloadProcessor +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +abstract class UndertowWsMessagesTransformerObject(agentConfiguration: AgentConfiguration) : HeadersProcessor, + PayloadProcessor, AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + + private val proxyDelegate = UndertowWsMessagesProxyDelegate(this) + + override fun permit(className: String, superName: String?, interfaces: Array) = listOf( + "io/undertow/websockets/jsr/FrameHandler", + "io/undertow/websockets/jsr/JsrWebSocketFilter", + "io/undertow/websockets/jsr/WebSocketSessionRemoteEndpoint", + "io/undertow/websockets/jsr/WebSocketSessionRemoteEndpoint\$BasicWebSocketSessionRemoteEndpoint", + "io/undertow/websockets/jsr/WebSocketSessionRemoteEndpoint\$AsyncWebSocketSessionRemoteEndpoint", + "io/undertow/websockets/core/WebSockets", + "org/springframework/web/reactive/socket/adapter/UndertowWebSocketHandlerAdapter", + "org/springframework/web/reactive/socket/adapter/UndertowWebSocketSession" + ).contains(className) || "io/undertow/websockets/client/WebSocketClientHandshake" == superName + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting UndertowWsMessagesTransformer for $className..." } + when (className) { + "io/undertow/websockets/jsr/FrameHandler" -> transformFrameHandler(ctClass) + "io/undertow/websockets/jsr/JsrWebSocketFilter" -> transformWebSocketFilter(ctClass) + "io/undertow/websockets/jsr/WebSocketSessionRemoteEndpoint" -> transformRemoteEndpoint(ctClass) + "io/undertow/websockets/jsr/WebSocketSessionRemoteEndpoint\$BasicWebSocketSessionRemoteEndpoint" -> transformSessionRemoteEndpoint( + ctClass + ) + + "io/undertow/websockets/jsr/WebSocketSessionRemoteEndpoint\$AsyncWebSocketSessionRemoteEndpoint" -> transformSessionRemoteEndpoint( + ctClass + ) + + "io/undertow/websockets/core/WebSockets" -> transformWebSockets(ctClass) + "org/springframework/web/reactive/socket/adapter/UndertowWebSocketHandlerAdapter" -> transformSpringWebSocketHandlerAdapter( + ctClass + ) + + "org/springframework/web/reactive/socket/adapter/UndertowWebSocketSession" -> transformSpringWebSocketSession( + ctClass + ) + } + when (ctClass.superclass.name) { + "io.undertow.websockets.client.WebSocketClientHandshake" -> transformClientHandshake(ctClass) + } + } + + private fun transformFrameHandler(ctClass: CtClass) { + val textMessageProxyClass = proxyDelegate.getTextMessageProxy(ctClass.classPool).name + val binaryMessageProxyClass = proxyDelegate.getBinaryMessageProxy(ctClass.classPool).name + val createProxyCode: (String) -> String = { proxy -> + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.session.getHandshakeHeaders())) { + $1 = new $proxy($1); + } + """.trimIndent() + } + val removeHeadersCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.session.getHandshakeHeaders())) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ctClass.getMethod( + "invokeTextHandler", + "(Lio/undertow/websockets/core/BufferedTextMessage;Lio/undertow/websockets/jsr/FrameHandler\$HandlerWrapper;Z)V" + ) + .also { it.insertCatching(CtBehavior::insertBefore, createProxyCode(textMessageProxyClass)) } + .also { it.insertCatching({ insertAfter(it, true) }, removeHeadersCode) } + ctClass.getMethod( + "invokeBinaryHandler", + "(Lio/undertow/websockets/core/BufferedBinaryMessage;Lio/undertow/websockets/jsr/FrameHandler\$HandlerWrapper;Z)V" + ) + .also { it.insertCatching(CtBehavior::insertBefore, createProxyCode(binaryMessageProxyClass)) } + .also { it.insertCatching({ insertAfter(it, true) }, removeHeadersCode) } + } + + private fun transformSpringWebSocketHandlerAdapter(ctClass: CtClass) { + val textMessageProxyClass = proxyDelegate.getTextMessageProxy(ctClass.classPool).name + val binaryMessageProxyClass = proxyDelegate.getBinaryMessageProxy(ctClass.classPool).name + val createProxyCode: (String) -> String = { proxy -> + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.session.getHandshakeInfo().getHeaders().toSingleValueMap())) { + $2 = new $proxy($2); + } + """.trimIndent() + } + val removeHeadersCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this.session.getHandshakeInfo().getHeaders().toSingleValueMap())) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + ctClass.getMethod( + "onFullTextMessage", + "(Lio/undertow/websockets/core/WebSocketChannel;Lio/undertow/websockets/core/BufferedTextMessage;)V" + ) + .also { it.insertCatching(CtBehavior::insertBefore, createProxyCode(textMessageProxyClass)) } + .also { it.insertCatching({ insertAfter(it, true) }, removeHeadersCode) } + ctClass.getMethod( + "onFullBinaryMessage", + "(Lio/undertow/websockets/core/WebSocketChannel;Lio/undertow/websockets/core/BufferedBinaryMessage;)V" + ) + .also { it.insertCatching(CtBehavior::insertBefore, createProxyCode(binaryMessageProxyClass)) } + .also { it.insertCatching({ insertAfter(it, true) }, removeHeadersCode) } + } + + private fun transformClientHandshake(ctClass: CtClass) { + ctClass.getMethod("createHeaders", "()Ljava/util/Map;").insertCatching( + CtBehavior::insertAfter, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + ${'$'}_.put("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + } + + private fun transformWebSocketFilter(ctClass: CtClass) = try { + ctClass.getMethod( + "doFilter", + "(Ljakarta/servlet/ServletRequest;Ljakarta/servlet/ServletResponse;Ljakarta/servlet/FilterChain;)V" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + ((jakarta.servlet.http.HttpServletResponse)$2).setHeader("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + } catch (e: NotFoundException) { + ctClass.getMethod( + "doFilter", + "(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;Ljavax/servlet/FilterChain;)V" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}()) { + ((javax.servlet.http.HttpServletResponse)$2).setHeader("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", "true"); + } + """.trimIndent() + ) + } + + private fun transformRemoteEndpoint(ctClass: CtClass) { + CtMethod.make( + """ + public io.undertow.websockets.jsr.UndertowSession getUndertowSession() { + return this.undertowSession; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + } + + private fun transformSessionRemoteEndpoint(ctClass: CtClass) { + val propagateHandshakeHeaderCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && this$0.getUndertowSession().getHandshakeHeaders() != null) { + java.util.Map drillHeaders = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + drillHeaders.put("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", this$0.getUndertowSession().getHandshakeHeaders().get("${PayloadProcessor.HEADER_WS_PER_MESSAGE}")); + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(drillHeaders); + } + """.trimIndent() + ctClass.declaringClass.defrost() + if (ctClass.simpleName == "WebSocketSessionRemoteEndpoint\$AsyncWebSocketSessionRemoteEndpoint") { + try { + ctClass.getMethod("sendText", "(Ljava/lang/String;Ljakarta/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendBinary", "(Ljava/nio/ByteBuffer;Ljakarta/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendObject", "(Ljava/lang/Object;Ljakarta/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + } catch (e: NotFoundException) { + ctClass.getMethod("sendText", "(Ljava/lang/String;Ljavax/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendBinary", "(Ljava/nio/ByteBuffer;Ljavax/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendObject", "(Ljava/lang/Object;Ljavax/websocket/SendHandler;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + } + ctClass.getMethod("sendText", "(Ljava/lang/String;)Ljava/util/concurrent/Future;") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendBinary", "(Ljava/nio/ByteBuffer;)Ljava/util/concurrent/Future;") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendObject", "(Ljava/lang/Object;)Ljava/util/concurrent/Future;") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + } + if (ctClass.simpleName == "WebSocketSessionRemoteEndpoint\$BasicWebSocketSessionRemoteEndpoint") { + ctClass.getMethod("sendText", "(Ljava/lang/String;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendBinary", "(Ljava/nio/ByteBuffer;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendObject", "(Ljava/lang/Object;)V") + .insertCatching(CtBehavior::insertBefore, propagateHandshakeHeaderCode) + ctClass.getMethod("sendText", "(Ljava/lang/String;Z)V").insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this$0.getUndertowSession().getHandshakeHeaders())) { + $1 = ${this::class.java.name}.INSTANCE.storeDrillHeaders($1); + } + """.trimIndent() + ) + ctClass.getMethod("sendBinary", "(Ljava/nio/ByteBuffer;Z)V").insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(this$0.getUndertowSession().getHandshakeHeaders())) { + byte[] modified = ${this::class.java.name}.INSTANCE.storeDrillHeaders(org.xnio.Buffers.take($1)); + $1.clear(); + $1 = java.nio.ByteBuffer.wrap(modified); + } + """.trimIndent() + ) + } + } + + private fun transformSpringWebSocketSession(ctClass: CtClass) { + ctClass.getMethod("sendMessage", "(Lorg/springframework/web/reactive/socket/WebSocketMessage;)Z") + .insertCatching( + CtBehavior::insertBefore, + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && this.getHandshakeInfo().getHeaders().get("${PayloadProcessor.HEADER_WS_PER_MESSAGE}") != null) { + java.util.Map drillHeaders = ${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}(); + drillHeaders.put("${PayloadProcessor.HEADER_WS_PER_MESSAGE}", this.getHandshakeInfo().getHeaders().get("${PayloadProcessor.HEADER_WS_PER_MESSAGE}").get(0)); + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(drillHeaders); + } + """.trimIndent() + ) + } + + private fun transformWebSockets(ctClass: CtClass) { + val wrapByteBufferCode = + """ + if (${this::class.java.name}.INSTANCE.${this::isPayloadProcessingEnabled.name}() + && ${this::class.java.name}.INSTANCE.${this::hasHeaders.name}() + && ${this::class.java.name}.INSTANCE.${this::isPayloadProcessingSupported.name}(${this::class.java.name}.INSTANCE.${this::retrieveHeaders.name}())) { + byte[] modified = ${this::class.java.name}.INSTANCE.storeDrillHeaders(org.xnio.Buffers.take($1)); + $1.clear(); + $1 = java.nio.ByteBuffer.wrap(modified); + } + """.trimIndent() + ctClass.getMethod( + "sendBlockingInternal", + "(Ljava/nio/ByteBuffer;Lio/undertow/websockets/core/WebSocketFrameType;Lio/undertow/websockets/core/WebSocketChannel;)V" + ) + .insertCatching(CtBehavior::insertBefore, wrapByteBufferCode) + ctClass.getMethod( + "sendInternal", + "(Ljava/nio/ByteBuffer;Lio/undertow/websockets/core/WebSocketFrameType;Lio/undertow/websockets/core/WebSocketChannel;Lio/undertow/websockets/core/WebSocketCallback;Ljava/lang/Object;J)V" + ) + .insertCatching(CtBehavior::insertBefore, wrapByteBufferCode) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsServerTransformerObject.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsServerTransformerObject.kt new file mode 100644 index 00000000..9003197d --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/instrument/ws/undertow/UndertowWsServerTransformerObject.kt @@ -0,0 +1,173 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument.undertow + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameters +import javassist.CtBehavior +import javassist.CtClass +import javassist.CtField +import javassist.CtMethod +import javassist.NotFoundException +import mu.KotlinLogging +import com.epam.drill.agent.instrument.AbstractTransformerObject +import com.epam.drill.agent.instrument.HeadersProcessor +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.instrument.ws.AbstractWsTransformerObject + +/** + * Transformer for Undertow-based websockets + * + * Tested with: + * io.undertow:undertow-websockets-jsr:2.0.29.Final + */ +abstract class UndertowWsServerTransformerObject( + private val headersRetriever: HeadersRetriever, + agentConfiguration: AgentConfiguration +) : HeadersProcessor, AbstractWsTransformerObject(agentConfiguration) { + + override val logger = KotlinLogging.logger {} + private var openingSessionHeaders: ThreadLocal?> = ThreadLocal() + + override fun permit(className: String, superName: String?, interfaces: Array) = listOf( + "io/undertow/websockets/jsr/UndertowSession", + "io/undertow/websockets/jsr/EndpointSessionHandler", + "io/undertow/websockets/jsr/FrameHandler", + "org/springframework/web/reactive/socket/adapter/UndertowWebSocketHandlerAdapter" + ).contains(className) + + override fun transform(className: String, ctClass: CtClass) { + logger.info { "transform: Starting UndertowWsServerTransformer for $className..." } + when (className) { + "io/undertow/websockets/jsr/UndertowSession" -> transformSession(ctClass) + "io/undertow/websockets/jsr/EndpointSessionHandler" -> transformSessionHandler(ctClass) + "io/undertow/websockets/jsr/FrameHandler" -> transformFrameHandler(ctClass) + "org/springframework/web/reactive/socket/adapter/UndertowWebSocketHandlerAdapter" -> transformSpringWebSocketHandlerAdapter( + ctClass + ) + } + } + + @Suppress("MemberVisibilityCanBePrivate") + fun setHandshakeHeaders(headers: Map?) = openingSessionHeaders.set(headers) + + @Suppress("MemberVisibilityCanBePrivate") + fun getHandshakeHeaders() = openingSessionHeaders.get() + + private fun transformSession(ctClass: CtClass) { + try { + ctClass.getField("handshakeHeaders") + } catch (e: NotFoundException) { + CtField.make( + "private java.util.Map/**/ handshakeHeaders = null;", + ctClass + ).also(ctClass::addField) + CtMethod.make( + """ + public java.util.Map/**/ getHandshakeHeaders() { + return this.handshakeHeaders; + } + """.trimIndent(), + ctClass + ).also(ctClass::addMethod) + } + ctClass.constructors[0].insertCatching( + CtBehavior::insertAfter, + """ + if (this.handshakeHeaders == null && ${this::class.java.name}.INSTANCE.${this::getHandshakeHeaders.name}() != null) { + this.handshakeHeaders = ${this::class.java.name}.INSTANCE.${this::getHandshakeHeaders.name}(); + ${this::class.java.name}.INSTANCE.${this::setHandshakeHeaders.name}(null); + } + """.trimIndent() + ) + } + + private fun transformSessionHandler(ctClass: CtClass) { + ctClass.getMethod( + "onConnect", + "(Lio/undertow/websockets/spi/WebSocketHttpExchange;Lio/undertow/websockets/core/WebSocketChannel;)V" + ) + .insertCatching( + CtBehavior::insertBefore, + """ + java.util.Map/**/ allHeaders = new java.util.HashMap(); + java.util.Iterator/**/ headerNames = $1.getRequestHeaders().keySet().iterator(); + while (headerNames.hasNext()) { + java.lang.String headerName = headerNames.next(); + java.util.List/**/ headerValues = $1.getRequestHeaders().get(headerName); + java.lang.String header = java.lang.String.join(",", headerValues); + allHeaders.put(headerName, header); + } + ${this::class.java.name}.INSTANCE.${this::setHandshakeHeaders.name}(allHeaders); + """.trimIndent() + ) + } + + private fun transformFrameHandler(ctClass: CtClass) { + val onTextMethod = ctClass.getMethod( + "onText", + "(Lio/undertow/websockets/core/WebSocketChannel;Lio/undertow/websockets/core/StreamSourceFrameChannel;)V" + ) + val onBinaryMethod = ctClass.getMethod( + "onBinary", + "(Lio/undertow/websockets/core/WebSocketChannel;Lio/undertow/websockets/core/StreamSourceFrameChannel;)V" + ) + val storeHeadersCode = + """ + if (this.session.getHandshakeHeaders() != null) { + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(this.session.getHandshakeHeaders()); + } + """.trimIndent() + val removeHeadersCode = + """ + if (this.session.getHandshakeHeaders() != null) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + onTextMethod.insertCatching(CtBehavior::insertBefore, storeHeadersCode) + onTextMethod.insertCatching({ insertAfter(it, true) }, removeHeadersCode) + onBinaryMethod.insertCatching(CtBehavior::insertBefore, storeHeadersCode) + onBinaryMethod.insertCatching({ insertAfter(it, true) }, removeHeadersCode) + } + + private fun transformSpringWebSocketHandlerAdapter(ctClass: CtClass) { + val onTextMethod = ctClass.getMethod( + "onFullTextMessage", + "(Lio/undertow/websockets/core/WebSocketChannel;Lio/undertow/websockets/core/BufferedTextMessage;)V" + ) + val onBinaryMethod = ctClass.getMethod( + "onFullBinaryMessage", + "(Lio/undertow/websockets/core/WebSocketChannel;Lio/undertow/websockets/core/BufferedBinaryMessage;)V" + ) + val storeHeadersCode = + """ + if (this.session.getHandshakeInfo().getHeaders().get("${headersRetriever.sessionHeader()}") != null) { + ${this::class.java.name}.INSTANCE.${this::storeHeaders.name}(this.session.getHandshakeInfo().getHeaders().toSingleValueMap()); + } + """.trimIndent() + val removeHeadersCode = + """ + if (this.session.getHandshakeInfo().getHeaders().get("${headersRetriever.sessionHeader()}") != null) { + ${this::class.java.name}.INSTANCE.${this::removeHeaders.name}(); + } + """.trimIndent() + onTextMethod.insertCatching(CtBehavior::insertBefore, storeHeadersCode) + onTextMethod.insertCatching({ insertAfter(it, true) }, removeHeadersCode) + onBinaryMethod.insertCatching(CtBehavior::insertBefore, storeHeadersCode) + onBinaryMethod.insertCatching({ insertAfter(it, true) }, removeHeadersCode) + } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt new file mode 100644 index 00000000..9c818006 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt @@ -0,0 +1,30 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.module + +import com.epam.drill.agent.common.module.AgentModule + +actual object JvmModuleStorage { + + private val storage = mutableMapOf() + + actual operator fun get(id: String) = storage.get(id) + + actual fun values(): Collection = storage.values + + actual fun add(module: AgentModule) = storage.put(module.id, module).let {} + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt new file mode 100644 index 00000000..d9c0373d --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt @@ -0,0 +1,53 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.request + +import com.epam.drill.agent.ttl.TransmittableThreadLocal +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.DrillInitialContext +import com.epam.drill.agent.common.request.RequestHolder +import kotlinx.serialization.protobuf.ProtoBuf +import mu.KotlinLogging + +actual object DrillRequestHolder : RequestHolder { + private val logger = KotlinLogging.logger {} + private var threadStorage: ThreadLocal = TransmittableThreadLocal.withInitial(DrillInitialContext::getDrillRequest) + + actual override fun remove() { + val request = threadStorage.get() + if (request == null) return + DrillRequestProcessor.processServerResponse() + threadStorage.remove() + logger.trace { "remove: Request ${request.drillSessionId} removed, threadId = ${Thread.currentThread().id}" } + } + + actual override fun retrieve(): DrillRequest? = + threadStorage.get() + + actual override fun store(drillRequest: DrillRequest) { + remove() + threadStorage.set(drillRequest) + DrillRequestProcessor.processServerRequest() + logger.trace { "store: Request ${drillRequest.drillSessionId} saved, threadId = ${Thread.currentThread().id}" } + } + + actual fun store(drillRequest: ByteArray) = + store(ProtoBuf.decodeFromByteArray(DrillRequest.serializer(), drillRequest)) + + actual fun dump(): ByteArray? = + retrieve()?.let { ProtoBuf.encodeToByteArray(DrillRequest.serializer(), it) } + +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/request/DrillRequestProcessor.kt b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/request/DrillRequestProcessor.kt new file mode 100644 index 00000000..e92f4642 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/jvmMain/kotlin/com/epam/drill/agent/request/DrillRequestProcessor.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.request + +import com.epam.drill.agent.module.JvmModuleStorage +import com.epam.drill.agent.common.request.RequestProcessor + +object DrillRequestProcessor : RequestProcessor { + + override fun processServerRequest() { + module().forEach { it.processServerRequest() } + } + + override fun processServerResponse() { + module().forEach { it.processServerResponse() } + } + + private fun module() = JvmModuleStorage.values().filterIsInstance() +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/instrument/JvmTransformer.kt b/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/instrument/JvmTransformer.kt new file mode 100644 index 00000000..a28ec75c --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/instrument/JvmTransformer.kt @@ -0,0 +1,50 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.instrument + +import com.epam.drill.agent.jvmapi.gen.CallObjectMethod +import com.epam.drill.agent.jvmapi.gen.NewStringUTF +import com.epam.drill.agent.jvmapi.gen.jobject +import com.epam.drill.agent.jvmapi.getObjectMethod +import com.epam.drill.agent.jvmapi.toByteArray +import com.epam.drill.agent.jvmapi.toJByteArray +import kotlinx.cinterop.ExperimentalForeignApi + +abstract class JvmTransformer : Transformer { + + @OptIn(ExperimentalForeignApi::class) + @Suppress("unchecked_cast") + override fun transform( + className: String, + classFileBuffer: ByteArray, + loader: Any?, + protectionDomain: Any?, + ): ByteArray? = + getObjectMethod( + this::class, + this::transform.name, + "(Ljava/lang/String;[BLjava/lang/Object;Ljava/lang/Object;)[B" + ).run { + CallObjectMethod( + this.first, + this.second, + NewStringUTF(className), + toJByteArray(classFileBuffer), + loader as jobject?, + protectionDomain as jobject? + )?.let(::toByteArray) + } +} diff --git a/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt b/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt new file mode 100644 index 00000000..e66dcf30 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/module/JvmModuleStorage.kt @@ -0,0 +1,33 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.module + +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.update +import kotlinx.collections.immutable.persistentHashMapOf +import com.epam.drill.agent.common.module.AgentModule + +actual object JvmModuleStorage { + + private val storage = atomic(persistentHashMapOf()) + + actual operator fun get(id: String) = storage.value.get(id) + + actual fun values(): Collection = storage.value.values + + actual fun add(module: AgentModule) = storage.update { it.put(module.id, module) } + +} diff --git a/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt b/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt new file mode 100644 index 00000000..75be77c8 --- /dev/null +++ b/lib-jvm-shared/agent-instrumentation/src/nativeMain/kotlin/com/epam/drill/agent/request/DrillRequestHolder.kt @@ -0,0 +1,42 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.request + +import kotlinx.serialization.protobuf.ProtoBuf +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder +import com.epam.drill.agent.jvmapi.callObjectByteArrayMethod +import com.epam.drill.agent.jvmapi.callObjectVoidMethod +import com.epam.drill.agent.jvmapi.callObjectVoidMethodWithBoolean +import com.epam.drill.agent.jvmapi.callObjectVoidMethodWithByteArray + +actual object DrillRequestHolder : RequestHolder { + + actual override fun remove() = + callObjectVoidMethod(this::class, RequestHolder::remove) + + actual override fun retrieve() = + dump()?.let { ProtoBuf.decodeFromByteArray(DrillRequest.serializer(), it) } + + actual override fun store(drillRequest: DrillRequest) = + store(ProtoBuf.encodeToByteArray(DrillRequest.serializer(), drillRequest)) + + actual fun store(drillRequest: ByteArray) = + callObjectVoidMethodWithByteArray(this::class, RequestHolder::store, drillRequest) + + actual fun dump(): ByteArray? = + callObjectByteArrayMethod(this::class, this::dump) +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/CODE_OF_CONDUCT.md b/lib-jvm-shared/agent-transport/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..ae731dda --- /dev/null +++ b/lib-jvm-shared/agent-transport/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ProjectEPM-D4J@epam.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/lib-jvm-shared/agent-transport/LICENSE b/lib-jvm-shared/agent-transport/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/agent-transport/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/agent-transport/README.md b/lib-jvm-shared/agent-transport/README.md new file mode 100644 index 00000000..9635a28a --- /dev/null +++ b/lib-jvm-shared/agent-transport/README.md @@ -0,0 +1,3 @@ +# Drill agent module + +This module contains the abstract agent core used to create platform specific agents (Java, .NET). diff --git a/lib-jvm-shared/agent-transport/build.gradle.kts b/lib-jvm-shared/agent-transport/build.gradle.kts new file mode 100644 index 00000000..d1816c6f --- /dev/null +++ b/lib-jvm-shared/agent-transport/build.gradle.kts @@ -0,0 +1,66 @@ +import java.net.URI +import java.util.Properties +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} + +val kotlinxSerializationVersion: String by parent!!.extra +val apacheHttpClientVersion: String by parent!!.extra +val microutilsLoggingVersion: String by parent!!.extra + +repositories { + mavenCentral() +} + +kotlin { + targets { + jvm() + } + @Suppress("UNUSED_VARIABLE") + sourceSets { + all { + languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi") + } + val jvmMain by getting { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinxSerializationVersion") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion") + implementation("org.apache.httpcomponents.client5:httpclient5:$apacheHttpClientVersion") + api("io.github.microutils:kotlin-logging:$microutilsLoggingVersion") + implementation(project(":common")) + } + } + val jvmTest by getting { + dependencies { + implementation(kotlin("test-junit")) + implementation("io.mockk:mockk:1.9.3") + } + } + } +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageDeserializer.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageDeserializer.kt new file mode 100644 index 00000000..00efc7e7 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageDeserializer.kt @@ -0,0 +1,23 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlin.reflect.KClass + +interface AgentMessageDeserializer { + fun contentType(): String + fun deserialize(message: ByteArray, clazz: KClass): T +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageDestinationMapper.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageDestinationMapper.kt new file mode 100644 index 00000000..02870b77 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageDestinationMapper.kt @@ -0,0 +1,35 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination + +/** + * A destination mapper interface for mapping [AgentMessageDestination] + * from common form to transport specific form. + * + * It's used to map [AgentMessageDestination] in the same time as + * [com.epam.drill.agent.common.transport.AgentMessage] serialization + * before storing in [AgentMessageQueue] or sending by [AgentMessageTransport]. + * + * @see AgentMessageDestination + * @see AgentMessageQueue + * @see AgentMessageTransport + * @see com.epam.drill.agent.common.transport.AgentMessage + */ +interface AgentMessageDestinationMapper { + fun map(destination: AgentMessageDestination): AgentMessageDestination +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageQueue.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageQueue.kt new file mode 100644 index 00000000..956e43e5 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageQueue.kt @@ -0,0 +1,115 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import java.util.concurrent.TimeUnit + +/** + * A queue interface for storing serialized messages when transport in unavailable state. + * Messages are stored with corresponding [AgentMessageDestination] object. + * It has the same method signatures as [java.util.Queue] interface. + * + * It's used to store messages after serialization by [AgentMessageSerializer] and + * destination mapping by [AgentMessageDestinationMapper] before sending by [AgentMessageTransport]. + * + * @see AgentMessageDestination + * @see AgentMessageSerializer + * @see AgentMessageDestinationMapper + * @see AgentMessageTransport + * @see java.util.Queue + */ +interface AgentMessageQueue { + + /** + * Inserts the specified element into this queue if it is possible to do so + * immediately without violating capacity restrictions, returning + * `true` upon success and throwing an `IllegalStateException` + * if no space is currently available. + * + * @param e the element to add + * @return `true` (as specified by [java.util.Collection.add]) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null and + * this queue does not permit null elements + * @throws IllegalArgumentException if some property of this element + * prevents it from being added to this queue + */ + fun add(e: Pair): Boolean + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions. + * When using a capacity-restricted queue, this method is generally + * preferable to [.add], which can fail to insert an element only + * by throwing an exception. + * + * @param e the element to add + * @return `true` if the element was added to this queue, else + * `false` + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null and + * this queue does not permit null elements + * @throws IllegalArgumentException if some property of this element + * prevents it from being added to this queue + */ + fun offer(e: Pair): Boolean + + /** + * Retrieves and removes the head of this queue. This method differs + * from [poll][.poll] only in that it throws an exception if this + * queue is empty. + * + * @return the head of this queue + * @throws NoSuchElementException if this queue is empty + */ + fun remove(): Pair + + /** + * Retrieves and removes the head of this queue, + * or returns `null` if this queue is empty. + * + * @return the head of this queue, or `null` if this queue is empty + */ + fun poll(): Pair? + + /** + * Retrieves and removes the head of this queue, waiting up to the specified wait time if necessary + */ + fun poll(timeout: Long, unit: TimeUnit): Pair? + + /** + * Retrieves, but does not remove, the head of this queue, + * or returns `null` if this queue is empty. + * + * @return the head of this queue, or `null` if this queue is empty + */ + fun peek(): Pair? + + /** + * Returns the number of elements in this collection. If this collection + * contains more than Integer.MAX_VALUE elements, returns + * Integer.MAX_VALUE. + * + * @return the number of elements in this collection + */ + fun size(): Int + +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageSerializer.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageSerializer.kt new file mode 100644 index 00000000..4ea1b1c8 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageSerializer.kt @@ -0,0 +1,35 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessage +import kotlinx.serialization.KSerializer + +/** + * A message serializer interface for [AgentMessage] serialization before sending by [AgentMessageTransport]. + * + * It's used to serialize [AgentMessage] before storing in [AgentMessageQueue] or sending by [AgentMessageTransport]. + * It contains [contentType] and [sizeOf] functions that can be used in [AgentMessageQueue] and [AgentMessageTransport] + * to abstract from serialized data details. + * + * @see AgentMessage + * @see AgentMessageQueue + * @see AgentMessageTransport + */ +interface AgentMessageSerializer { + fun contentType(): String + fun serialize(message: T, serializer: KSerializer): ByteArray +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageTransport.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageTransport.kt new file mode 100644 index 00000000..c951ee4d --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/AgentMessageTransport.kt @@ -0,0 +1,37 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.ResponseStatus + +/** + * A transport interface for serialized messages. + * + * It's used to send serialized [com.epam.drill.agent.common.transport.AgentMessage] + * to transport-specific [AgentMessageDestination]. Serialization and destination mapping should be done + * by [AgentMessageSerializer] and [AgentMessageDestinationMapper] correspondingly. + * In case of transport errors messages may be stored in [AgentMessageQueue] for subsequent retries. + * + * @see AgentMessageDestination + * @see AgentMessageSerializer + * @see AgentMessageDestinationMapper + * @see AgentMessageQueue + * @see com.epam.drill.agent.common.transport.AgentMessage + */ +interface AgentMessageTransport { + fun send(destination: AgentMessageDestination, message: ByteArray? = null, contentType: String = ""): ResponseStatus +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/ExponentialBackoff.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/ExponentialBackoff.kt new file mode 100644 index 00000000..a890dc46 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/ExponentialBackoff.kt @@ -0,0 +1,42 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +/** + * Interface for retrying an operation with exponential backoff. + */ +interface ExponentialBackoff { + /** + * Retries an operation with exponential backoff. + * @param initDelay Initial delay in milliseconds before the first retry. Default is 0ms (no delay). + * @param baseDelay Initial delay in milliseconds between retries. Default is 1000ms (1 second). + * @param maxDelay Maximum delay in milliseconds between retries. Default is 32000ms (32 seconds). + * @param factor Multiplier for increasing the delay after each failed attempt. Default is 2.0 (doubling the delay). + * @param maxRetries Maximum number of retry attempts before giving up. Default is 5. + * @param operation A lambda function representing the operation to be executed. + * It should return `true` if successful, or `false` if it failed. + * @return `true` if the operation was successful, `false` if all attempts were exhausted. + */ + fun tryWithExponentialBackoff( + initDelay: Long = 0L, + baseDelay: Long = 1000L, + maxDelay: Long = 32000L, + factor: Double = 2.0, + maxRetries: Int = 5, + onSleep: (Long) -> Unit = { Thread.sleep(it) }, + operation: (Int, Long) -> Boolean + ): Boolean +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/InMemoryAgentMessageQueue.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/InMemoryAgentMessageQueue.kt new file mode 100644 index 00000000..488d817e --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/InMemoryAgentMessageQueue.kt @@ -0,0 +1,73 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit + +class InMemoryAgentMessageQueue( + private val capacity: Long +) : AgentMessageQueue { + + private val queue: BlockingQueue> = LinkedBlockingQueue() + private var bytesSize: Long = 0 + + override fun add(e: Pair): Boolean = e + .takeIf(::isCapable) + ?.also(::increaseSize) + ?.run { + queue.add(this) + } ?: throw IllegalArgumentException("Queue is out of capacity") + + override fun offer(e: Pair) = e + .takeIf(::isCapable) + ?.also(::increaseSize) + ?.run { + queue.offer(this) + } ?: false + + override fun remove() = queue.remove() + .also(::decreaseSize) + + override fun poll() = queue.poll() + ?.also(::decreaseSize) + + override fun poll(timeout: Long, unit: TimeUnit) = queue.poll(timeout, unit) + ?.also(::decreaseSize) + + override fun peek() = queue.peek() + + override fun size(): Int = queue.size + + fun bytesSize(): Long = bytesSize + + private fun sizeOf(e: Pair) = + e.first.type.length + e.first.target.length + e.second.size.toLong() + + private fun increaseSize(e: Pair) { + bytesSize += sizeOf(e) + } + + private fun decreaseSize(e: Pair) { + bytesSize -= sizeOf(e) + } + + private fun isCapable(e: Pair) = + bytesSize + sizeOf(e) <= capacity + +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/JsonAgentMessageDeserializer.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/JsonAgentMessageDeserializer.kt new file mode 100644 index 00000000..6ebdd7e3 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/JsonAgentMessageDeserializer.kt @@ -0,0 +1,37 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import kotlinx.serialization.serializer +import java.io.ByteArrayInputStream +import kotlin.reflect.KClass + +class JsonAgentMessageDeserializer: AgentMessageDeserializer { + private val json = Json { + encodeDefaults = true + ignoreUnknownKeys = true + } + + override fun contentType(): String = "application/json" + + @OptIn(InternalSerializationApi::class) + override fun deserialize(message: ByteArray, clazz: KClass): T = ByteArrayInputStream(message).use { + json.decodeFromStream(clazz.serializer(), it) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/JsonAgentMessageSerializer.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/JsonAgentMessageSerializer.kt new file mode 100644 index 00000000..94dbfe21 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/JsonAgentMessageSerializer.kt @@ -0,0 +1,36 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToStream +import java.io.ByteArrayOutputStream +import kotlinx.serialization.KSerializer + +class JsonAgentMessageSerializer : AgentMessageSerializer { + + private val json = Json { + encodeDefaults = true + ignoreUnknownKeys = true + } + + override fun contentType(): String = "application/json" + + override fun serialize(message: T, serializer: KSerializer): ByteArray = ByteArrayOutputStream().use { + json.encodeToStream(serializer, message, it) + it.toByteArray() + } +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/MessageSendingListener.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/MessageSendingListener.kt new file mode 100644 index 00000000..01241b05 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/MessageSendingListener.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination + +/** + * Listener for unsent messages. + */ +interface MessageSendingListener { + /** + * Called when a message is sent. + */ + fun onSent(destination: AgentMessageDestination, message: ByteArray) + /** + * Called when a message could not be sent. + */ + fun onUnsent(destination: AgentMessageDestination, message: ByteArray) +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/NoOpAgentMessageSerializer.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/NoOpAgentMessageSerializer.kt new file mode 100644 index 00000000..2ae83790 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/NoOpAgentMessageSerializer.kt @@ -0,0 +1,27 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.drill.agent.transport + + +import kotlinx.serialization.KSerializer +import com.epam.drill.agent.common.transport.AgentMessage + +class NoOpAgentMessageSerializer : AgentMessageSerializer { + override fun contentType(): String = "noop" + + override fun serialize(message: T, serializer: KSerializer): ByteArray = ByteArray(0) +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/ProtoBufAgentMessageSerializer.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/ProtoBufAgentMessageSerializer.kt new file mode 100644 index 00000000..c84f8d86 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/ProtoBufAgentMessageSerializer.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlinx.serialization.protobuf.ProtoBuf +import kotlinx.serialization.serializer +import com.epam.drill.agent.common.transport.AgentMessage +import kotlinx.serialization.KSerializer +import kotlin.reflect.KClass + +@kotlinx.serialization.InternalSerializationApi +class ProtoBufAgentMessageSerializer: AgentMessageSerializer { + override fun contentType(): String = "application/protobuf" + + override fun serialize(message: T, serializer: KSerializer): ByteArray = + ProtoBuf.encodeToByteArray(serializer, message) + +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/QueuedAgentMessageSender.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/QueuedAgentMessageSender.kt new file mode 100644 index 00000000..b69962d0 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/QueuedAgentMessageSender.kt @@ -0,0 +1,167 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import mu.KotlinLogging +import com.epam.drill.agent.common.transport.AgentMessage +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.AgentMessageSender +import kotlinx.serialization.KSerializer +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +/** + * A [AgentMessageSender] implementation with [AgentMessageQueue] for storing + * serialized messages when transport in unavailable state. + * @see AgentMessageSender + * @see AgentMessageQueue + * @see AgentMessageTransport + */ +open class QueuedAgentMessageSender( + private val transport: AgentMessageTransport, + private val messageSerializer: AgentMessageSerializer, + private val destinationMapper: AgentMessageDestinationMapper, + private val messageQueue: AgentMessageQueue, + private val messageSendingListener: MessageSendingListener? = null, + private val exponentialBackoff: ExponentialBackoff = SimpleExponentialBackoff(), + maxThreads: Int = 1, + private val maxRetries: Int = 5 +) : AgentMessageSender { + private val logger = KotlinLogging.logger {} + private val executor: ExecutorService = Executors.newFixedThreadPool(maxThreads) + private val isRunning = AtomicBoolean(true) + + init { + repeat(maxThreads) { + executor.submit { processQueue() } + } + } + + override fun send(destination: AgentMessageDestination, message: T, serializer: KSerializer) { + val mappedDestination = destinationMapper.map(destination) + val serializedMessage = messageSerializer.serialize(message, serializer) + if (!isRunning.get()) { + handleUnsent(mappedDestination, serializedMessage, "sender is not running") + return + } + if (!messageQueue.offer(Pair(mappedDestination, serializedMessage))) { + handleUnsent(mappedDestination, serializedMessage, "queue capacity limit reached") + return + } + logger.trace { + "Queued message to $mappedDestination" + } + } + + override fun shutdown() { + isRunning.set(false) + executor.shutdown() + try { + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + executor.shutdownNow() + } + } catch (e: InterruptedException) { + logger.error(e) { "Failed to send some messages prior to shutdown" } + executor.shutdownNow() + } + unloadQueue("sender is shutting down") + } + + /** + * Processes the message queue. + * It will try to send the message from a queue to the destination with exponential backoff. + */ + private fun processQueue() { + while (isRunning.get()) { + val (destination, message) = messageQueue.poll(1, TimeUnit.SECONDS) ?: continue + runCatching { + exponentialBackoff.tryWithExponentialBackoff( + maxRetries = maxRetries + ) { attempt, delay -> + tryToSend(destination, message, attempt, delay) + } + }.onFailure { + tryToSend(destination, message) || handleUnsent(destination, message, "error occurred: ${it.message}") + }.onSuccess { + if (!it) { + handleUnsent(destination, message, "attempts exhausted") + } + } + } + } + + /** + * Tries to send the message to the destination. + * @param message The serialized message to send. + * @param destination The destination to which the message should be sent. + * @param attempt The current attempt number. + * @param delay The delay in milliseconds before the next attempt. + * @return `true` if the message was sent successfully, `false` otherwise. + */ + private fun tryToSend( + destination: AgentMessageDestination, + message: ByteArray, + attempt: Int = 0, + delay: Long = 0 + ): Boolean { + logger.trace { + "Sending to $destination on attempt: $attempt" + } + return transport.send(destination, message, messageSerializer.contentType()).onError { error -> + logger.trace { "Attempt $attempt send to $destination failed. Retrying in ${delay}ms. Error message: $error" } + }.onSuccess { + logger.debug { + val serializedAsString = message.decodeToString() + "Sent to $destination on attempt: $attempt, message: $serializedAsString" + } + messageSendingListener?.onSent(destination, message) + }.success + } + + /** + * Last attempt to send unsent messages, and register them as unsent if unsuccessful + */ + private fun unloadQueue(reason: String) { + if (messageQueue.size() == 0) return + logger.info { "Unloading a message queue as $reason, queue size: ${messageQueue.size()}" } + do { + val message = messageQueue.poll()?.also { (destination, message) -> + tryToSend(destination, message) || handleUnsent(destination, message, reason) + } + } while (message != null) + } + + /** + * Handles the case when a message cannot be sent because the queue is full, shutdown, or attempts have been exhausted. + * @param destination The destination to which the message was intended to be sent. + * @param message The serialized message that could not be sent. + * @return `true` if the message was handled, `false` otherwise. + */ + private fun handleUnsent( + destination: AgentMessageDestination, + message: ByteArray, + reason: String + ): Boolean = runCatching { + logger.debug { + val serializedAsString = message.decodeToString() + "Failed to send message because $reason, destination: $destination, message: $serializedAsString" + } + messageSendingListener?.onUnsent(destination, message) + true + }.getOrDefault(false) +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleAgentMessageReceiver.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleAgentMessageReceiver.kt new file mode 100644 index 00000000..264ca668 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleAgentMessageReceiver.kt @@ -0,0 +1,35 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.AgentMessageReceiver +import kotlin.reflect.KClass + +class SimpleAgentMessageReceiver( + private val transport: AgentMessageTransport, + private val messageDeserializer: AgentMessageDeserializer, + private val destinationMapper: AgentMessageDestinationMapper = StubAgentDestinationMapper +) : AgentMessageReceiver { + + override fun receive(destination: AgentMessageDestination, clazz: KClass): T = + transport.send(destinationMapper.map(destination), null) + .mapContent { messageDeserializer.deserialize(it, clazz) } + .onError { + error("Failed to receive message from $destination, error message: $it") + }.content + ?: error("Failed to receive message from $destination. There is no content in the response.") +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleAgentMessageSender.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleAgentMessageSender.kt new file mode 100644 index 00000000..cca3acac --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleAgentMessageSender.kt @@ -0,0 +1,38 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.AgentMessageSender +import kotlinx.serialization.KSerializer + +open class SimpleAgentMessageSender( + private val transport: AgentMessageTransport, + private val messageSerializer: AgentMessageSerializer, + private val destinationMapper: AgentMessageDestinationMapper = StubAgentDestinationMapper +) : AgentMessageSender { + + override fun send(destination: AgentMessageDestination, message: T, serializer: KSerializer) { + transport.send( + destinationMapper.map(destination), + messageSerializer.serialize(message, serializer), + messageSerializer.contentType() + ).onError { + error("Failed to send message from $destination, error message: $it") + } + } + +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleExponentialBackoff.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleExponentialBackoff.kt new file mode 100644 index 00000000..1f1f1089 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/SimpleExponentialBackoff.kt @@ -0,0 +1,43 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlin.math.min + +class SimpleExponentialBackoff: ExponentialBackoff { + override fun tryWithExponentialBackoff( + initDelay: Long, + baseDelay: Long, + maxDelay: Long, + factor: Double, + maxRetries: Int, + onSleep: (Long) -> Unit, + operation: (Int, Long) -> Boolean + ): Boolean { + initDelay.takeIf { it > 0 }?.let { onSleep(it) } + var currentDelay = baseDelay + for (attempt in 1..maxRetries) { + val success = operation(attempt, currentDelay) + if (success) { + return true + } else { + onSleep(currentDelay) + currentDelay = min(maxDelay, (currentDelay * factor).toLong()) + } + } + return false + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/StubAgentDestinationMapper.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/StubAgentDestinationMapper.kt new file mode 100644 index 00000000..d18509c0 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/StubAgentDestinationMapper.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import com.epam.drill.agent.common.transport.AgentMessageDestination + +object StubAgentDestinationMapper: AgentMessageDestinationMapper { + override fun map(destination: AgentMessageDestination): AgentMessageDestination { + return destination + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageDestinationMapper.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageDestinationMapper.kt new file mode 100644 index 00000000..661c533d --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageDestinationMapper.kt @@ -0,0 +1,38 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport.http + +import java.lang.UnsupportedOperationException +import com.epam.drill.agent.transport.AgentMessageDestinationMapper +import com.epam.drill.agent.common.transport.AgentMessageDestination + +class HttpAutotestAgentMessageDestinationMapper( + private val groupId: String, + private val jsAgentId: String, + private val jsAgentBuildVersion: String, +) : AgentMessageDestinationMapper { + + override fun map(destination: AgentMessageDestination): AgentMessageDestination { + val target = when(destination.target) { + // TODO configurations w/o JavaScript agent + "raw-javascript-coverage" -> "/api/groups/${groupId}/agents/$jsAgentId/builds/$jsAgentBuildVersion/raw-javascript-coverage" + "tests-metadata" -> "/api/tests-metadata" + else -> throw UnsupportedOperationException( + "HttpAutotestAgentMessageDestinationMapper does not support target ${destination.target}") + } + return destination.copy(target = target) + } +} diff --git a/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageTransport.kt b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageTransport.kt new file mode 100644 index 00000000..6daf27da --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmMain/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageTransport.kt @@ -0,0 +1,137 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport.http + +import java.io.File +import java.net.URI +import org.apache.hc.client5.http.classic.methods.HttpDelete +import org.apache.hc.client5.http.classic.methods.HttpGet +import org.apache.hc.client5.http.classic.methods.HttpPost +import org.apache.hc.client5.http.classic.methods.HttpPut +import org.apache.hc.client5.http.entity.GzipCompressingEntity +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder +import org.apache.hc.core5.http.ClassicHttpResponse +import org.apache.hc.core5.http.ContentType +import org.apache.hc.core5.http.HttpHeaders +import org.apache.hc.core5.http.io.entity.ByteArrayEntity +import org.apache.hc.core5.http.io.entity.EntityUtils +import org.apache.hc.core5.http.message.BasicHeader +import org.apache.hc.core5.ssl.SSLContextBuilder +import mu.KotlinLogging +import com.epam.drill.agent.transport.AgentMessageTransport +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.ResponseStatus +import org.apache.hc.core5.http.HttpStatus + +private const val HEADER_DRILL_INTERNAL = "drill-internal" +private const val HEADER_API_KEY = "X-Api-Key" + +class HttpAgentMessageTransport( + serverAddress: String, + apiKey: String = "", + sslTruststore: String = "", + sslTruststorePass: String = "", + drillInternal: Boolean = true, + private val gzipCompression: Boolean = true, +) : AgentMessageTransport { + + private val logger = KotlinLogging.logger {} + private val clientBuilder = HttpClientBuilder.create() + private val serverUri = URI(serverAddress) + private val drillInternalHeader = drillInternal.takeIf(true::equals)?.let { BasicHeader(HEADER_DRILL_INTERNAL, it) } + private val apiKeyHeader = apiKey.takeIf(String::isNotBlank)?.let { BasicHeader(HEADER_API_KEY, it) } + private val contentTypes = mutableMapOf() + + init { + logger.debug { "configure: Using serverAddress: $serverUri" } + if (serverUri.scheme == "https") { + val configureTrustStore: (SSLContextBuilder) -> Unit = { + if (sslTruststore.isEmpty()) it.loadTrustMaterial { _, _ -> true } + else it.loadTrustMaterial(File(sslTruststore), sslTruststorePass.toCharArray()) + } + val configureHostnameVerifier: (SSLConnectionSocketFactoryBuilder) -> Unit = { + if (sslTruststore.isEmpty()) it.setHostnameVerifier(NoopHostnameVerifier()) + } + val sslContext = SSLContextBuilder.create() + .also(configureTrustStore).build() + val sslSocketFactory = SSLConnectionSocketFactoryBuilder.create().setSslContext(sslContext) + .also(configureHostnameVerifier).build() + val connectionManager = PoolingHttpClientConnectionManagerBuilder.create() + .setSSLSocketFactory(sslSocketFactory).build() + clientBuilder.setConnectionManager(connectionManager) + clientBuilder.setConnectionManagerShared(true) + logger.debug { "constructor: SSL configured, truststore: $sslTruststore" } + logger.debug { "constructor: SSL configured, trustAll: ${sslTruststore.isEmpty()}" } + } + } + + override fun send( + destination: AgentMessageDestination, + message: ByteArray?, + contentType: String + ): ResponseStatus = clientBuilder.build().use { client -> + val request = when (destination.type) { + "GET" -> HttpGet(serverUri.resolve(destination.target)) + "POST" -> HttpPost(serverUri.resolve(destination.target)) + "PUT" -> HttpPut(serverUri.resolve(destination.target)) + "DELETE" -> HttpDelete(serverUri.resolve(destination.target)) + else -> throw IllegalArgumentException("Unknown destination type: ${destination.type}") + } + val mimeType = contentType.takeIf(String::isNotEmpty) ?: ContentType.WILDCARD.mimeType + drillInternalHeader?.also(request::setHeader) + apiKeyHeader?.also(request::setHeader) + request.setHeader(HttpHeaders.CONTENT_TYPE, mimeType) + if (message != null) { + request.entity = ByteArrayEntity(message, getContentType(mimeType)) + if (gzipCompression) { + request.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip") + request.entity = GzipCompressingEntity(request.entity) + } + } + logger.trace { + val messageAsString = message?.decodeToString() + "execute: Request to ${request.uri}, method: ${request.method}, request body: $messageAsString" + } + + try { + client.execute(request, ::contentResponseHandler) + } catch (e: Throwable) { + ResponseStatus(success = false, errorContent = e.message) + }.onSuccess { content -> + logger.debug { + val messageAsString = content?.decodeToString() + "execute: Successful response from ${request.uri}, method: ${request.method}, response body: $messageAsString" + } + }.onError { errorContent -> + logger.debug { + "execute: Failed response from ${request.uri}, method: ${request.method}, error message: $errorContent" + } + } + } + + private fun contentResponseHandler(response: ClassicHttpResponse) = ResponseStatus( + success = isSuccess(response), + content = response.takeIf(::isSuccess)?.let { EntityUtils.toByteArray(it.entity) }, + errorContent = response.takeIf(::isFail)?.let { EntityUtils.toString(it.entity) }, + ) + + private fun getContentType(mimeType: String) = contentTypes.getOrPut(mimeType) { ContentType.create(mimeType) } + private fun isSuccess(response: ClassicHttpResponse) = response.code == HttpStatus.SC_SUCCESS + private fun isFail(response: ClassicHttpResponse) = !isSuccess(response) +} diff --git a/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/InMemoryAgentMessageQueueTest.kt b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/InMemoryAgentMessageQueueTest.kt new file mode 100644 index 00000000..c5b7590b --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/InMemoryAgentMessageQueueTest.kt @@ -0,0 +1,141 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import com.epam.drill.agent.common.transport.AgentMessage +import com.epam.drill.agent.common.transport.AgentMessageDestination + +class InMemoryAgentMessageQueueTest { + + private lateinit var messageDestination: AgentMessageDestination + + @BeforeTest + fun setup() = MockKAnnotations.init(this).also { + messageDestination = AgentMessageDestination("foo", "bar") + } + + @Test + fun `add to non-full`() { + val queue = InMemoryAgentMessageQueue(1100) + for(i in 1..10) queue.add(Pair(messageDestination, "somestring".encodeToByteArray())) + + assertEquals(10, queue.size()) + assertEquals((6 + 10) * 10, queue.bytesSize()) + } + + @Test + fun `offer to non-full`() { + val queue = InMemoryAgentMessageQueue(1100) + for(i in 1..10) queue.offer(Pair(messageDestination, "somestring".encodeToByteArray())) + + assertEquals(10, queue.size()) + assertEquals((6 + 10) * 10, queue.bytesSize()) + } + + @Test + fun `remove from non-empty`() { + val queue = InMemoryAgentMessageQueue(1100) + for(i in 0..9) queue.offer(Pair(messageDestination, "somestring$i".encodeToByteArray())) + + val removed1 = queue.remove() + verifyQueueElement(queue, 9, removed1, "somestring0") + + val removed2 = queue.remove() + verifyQueueElement(queue, 8, removed2, "somestring1") + } + + @Test + fun `poll from non-empty`() { + val queue = InMemoryAgentMessageQueue( 1100) + for(i in 0..9) queue.offer(Pair(messageDestination, "somestring$i".encodeToByteArray())) + + val polled1 = queue.poll() + verifyQueueElement(queue, 9, polled1, "somestring0") + + val polled2 = queue.poll() + verifyQueueElement(queue, 8, polled2, "somestring1") + } + + @Test + fun `peek from non-empty`() { + val queue = InMemoryAgentMessageQueue(1100) + for(i in 0..9) queue.offer(Pair(messageDestination, "somestring$i".encodeToByteArray())) + + val element1 = queue.peek() + verifyQueueElement(queue, 10, element1, "somestring0") + + val element2 = queue.peek() + verifyQueueElement(queue, 10, element2, "somestring0") + } + + @Test(expected = IllegalArgumentException::class) + fun `add to full`() { + val queue = InMemoryAgentMessageQueue(140) + for(i in 0..8) queue.add(Pair(messageDestination, "somestring".encodeToByteArray())) + + assertEquals(9, queue.size()) + assertEquals((6 + 10) * 9, queue.bytesSize()) + + queue.add(Pair(messageDestination, "somestring".encodeToByteArray())) + } + + @Test + fun `offer to full`() { + val queue = InMemoryAgentMessageQueue(150) + for(i in 0..9) queue.offer(Pair(messageDestination, "somestring".encodeToByteArray())) + + assertEquals(9, queue.size()) + assertEquals((6 + 10) * 9, queue.bytesSize()) + } + + @Test(expected = NoSuchElementException::class) + fun `remove from empty`() { + val queue = InMemoryAgentMessageQueue(1100) + queue.remove() + } + + @Test + fun `poll from empty`() { + val queue = InMemoryAgentMessageQueue(1100) + assertNull(queue.poll()) + } + + @Test + fun `peek from empty`() { + val queue = InMemoryAgentMessageQueue(1100) + assertNull(queue.peek()) + } + + private fun verifyQueueElement( + queue: InMemoryAgentMessageQueue, + size: Int, + element: Pair?, + value: String + ) { + assertEquals(size, queue.size()) + assertEquals(size * (6 + 11L), queue.bytesSize()) + assertEquals(messageDestination, element?.first) + assertEquals(value, element?.second?.decodeToString()) + } + +} diff --git a/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/QueuedAgentMessageSenderTest.kt b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/QueuedAgentMessageSenderTest.kt new file mode 100644 index 00000000..5169a4ef --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/QueuedAgentMessageSenderTest.kt @@ -0,0 +1,232 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlin.test.Test +import com.epam.drill.agent.common.transport.AgentMessage +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.ResponseStatus +import io.mockk.* +import io.mockk.impl.annotations.MockK +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.serializer +import org.junit.After +import org.junit.Before +import java.lang.Thread.sleep +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit +import kotlin.test.assertEquals + +class QueuedAgentMessageSenderTest { + + @Serializable + private class TestAgentMessage(val msg: String) : AgentMessage() + + @MockK + private lateinit var messageTransport: AgentMessageTransport + + @MockK + private lateinit var messageSerializer: AgentMessageSerializer + + @MockK + private lateinit var destinationMapper: AgentMessageDestinationMapper + + @MockK + private lateinit var messageQueue: AgentMessageQueue + + @MockK + private lateinit var messageSendingListener: MessageSendingListener + + private lateinit var sender: QueuedAgentMessageSender + + private val incomingMessage = mutableListOf() + private val incomingDestinations = mutableListOf() + private val toSendMessages = mutableListOf() + private val toSendContentTypes = mutableListOf() + private val toSendDestinations = mutableListOf() + private val unsentMessages = mutableListOf() + private val unsentDestinations = mutableListOf() + private val sentMessages = mutableListOf() + private val sentDestinations = mutableListOf() + private val queueOffers = mutableListOf() + private val queuePolls = mutableListOf?>() + private val queue: BlockingQueue> = LinkedBlockingQueue(10) + + @Suppress("UNCHECKED_CAST") + @Before + fun setup() = MockKAnnotations.init(this).also { + incomingMessage.clear() + incomingDestinations.clear() + toSendMessages.clear() + toSendContentTypes.clear() + toSendDestinations.clear() + unsentDestinations.clear() + unsentMessages.clear() + sentDestinations.clear() + sentMessages.clear() + queueOffers.clear() + queuePolls.clear() + queue.clear() + + val serialize: (TestAgentMessage) -> ByteArray = { + "serialized-${it.msg}".encodeToByteArray() + } + val mapDestination: (AgentMessageDestination) -> AgentMessageDestination = { + AgentMessageDestination("MAP", "mapped-${it.target}") + } + every { messageSerializer.serialize(capture(incomingMessage), TestAgentMessage.serializer()) } answers FunctionAnswer { + serialize(it.invocation.args[0] as TestAgentMessage) + } + every { messageSerializer.contentType() } returns "test/test" + every { destinationMapper.map(capture(incomingDestinations)) } answers FunctionAnswer { + mapDestination(it.invocation.args[0] as AgentMessageDestination) + } + every { messageSendingListener.onUnsent(capture(unsentDestinations), capture(unsentMessages)) } returns Unit + every { messageSendingListener.onSent(capture(sentDestinations), capture(sentMessages)) } returns Unit + every { messageQueue.size() } answers FunctionAnswer { queue.size } + every { messageQueue.poll(any(), any()) } answers FunctionAnswer { + queue.poll(1, TimeUnit.SECONDS).also(queuePolls::add) + } + every { messageQueue.poll() } answers FunctionAnswer { + queue.poll().also(queuePolls::add) + } + every { messageQueue.offer(any()) } answers FunctionAnswer { + queue.offer(it.invocation.args[0] as Pair).also(queueOffers::add) + } + + sender = QueuedAgentMessageSender( + messageTransport, + messageSerializer, + destinationMapper, + messageQueue, + messageSendingListener, + StubExponentialBackoff() + ) + } + + @After + fun shutdown() { + sender.shutdown() + } + + @Test + fun `given ok response, QueuedAgentMessageSender should send messages`() { + every { messageTransportSending() } returns ResponseStatus(true) + + repeat(10) { + sender.send(AgentMessageDestination("TYPE", "target-$it"), TestAgentMessage("message-$it"), TestAgentMessage.serializer()) + } + + verifyMethodCalls(calls = 10, sendingAttempts = 10, enqueued = 10, dequeued = 10, sent = 10, unsent = 0) + } + + @Test + fun `given bad response, QueuedAgentMessageSender shouldn't send messages`() { + every { messageTransportSending() } returns ResponseStatus(false) + + repeat(10) { + sender.send(AgentMessageDestination("TYPE", "target-$it"), TestAgentMessage("message-$it"), TestAgentMessage.serializer()) + } + + verifyMethodCalls(calls = 10, sendingAttempts = 50, enqueued = 10, dequeued = 10, sent = 0, unsent = 10) + } + + @Test + fun `given shutdown state, QueuedAgentMessageSender shouldn't add messages to queue`() { + every { messageTransportSending() } returns ResponseStatus(true) + + sender.shutdown() //shutdown before sending messages + repeat(10) { + sender.send(AgentMessageDestination("TYPE", "target-$it"), TestAgentMessage("message-$it"), TestAgentMessage.serializer()) + } + + verifyMethodCalls(calls = 10, sendingAttempts = 0, enqueued = 0, dequeued = 0, sent = 0, unsent = 10) + } + + @Test + fun `given limit on queue capacity, QueuedAgentMessageSender shouldn't add messages to queue`() { + every { messageTransportSending() } returns ResponseStatus(true) + every { messageQueue.offer(any()) } returns false //queue is full + + repeat(10) { + sender.send(AgentMessageDestination("TYPE", "target-$it"), TestAgentMessage("message-$it"), TestAgentMessage.serializer()) + } + + verifyMethodCalls(calls = 10, sendingAttempts = 0, enqueued = 0, dequeued = 0, sent = 0, unsent = 10) + } + + private fun verifyMethodCalls( + calls: Int? = null, + sendingAttempts: Int? = null, + enqueued: Int? = null, + dequeued: Int? = null, + sent: Int? = null, + unsent: Int? = null + ) { + calls?.waitFor { verify(exactly = it) { messageSerializer.serialize(any(), TestAgentMessage.serializer()) } } + calls?.waitFor { verify(exactly = it) { destinationMapper.map(any()) } } + enqueued?.waitFor { + assertEquals(it, queueOffers.filter { o -> o }.size) + } + dequeued?.waitFor { + assertEquals(it, queuePolls.filterNotNull().size) + } + + sendingAttempts?.waitFor { verify(exactly = it) { messageTransport.send(any(), any(), any()) } } + sent?.waitFor { verify(exactly = it) { messageSendingListener.onSent(any(), any()) } } + unsent?.waitFor { verify(exactly = it) { messageSendingListener.onUnsent(any(), any()) } } + } + + private fun T.waitFor(timeout: Long = 1000, block: (T) -> Unit) { + val start = System.currentTimeMillis() + val timeIsOut = { System.currentTimeMillis() - start > timeout } + var error: Throwable? = null + while (runCatching { block(this) } + .onFailure { error = it } + .onSuccess { error = null } + .isFailure && !timeIsOut()) { + sleep(10) + } + error?.let { throw it } + } + + private fun MockKMatcherScope.messageTransportSending() = messageTransport.send( + capture(toSendDestinations), + capture(toSendMessages), + capture(toSendContentTypes) + ) + + private class StubExponentialBackoff : ExponentialBackoff { + override fun tryWithExponentialBackoff( + initDelay: Long, + baseDelay: Long, + maxDelay: Long, + factor: Double, + maxRetries: Int, + onSleep: (Long) -> Unit, + operation: (Int, Long) -> Boolean + ): Boolean { + var attempts = 0 + var result: Boolean + do { + result = operation(0, 0) + attempts++ + } while (attempts < maxRetries && !result) + return result + } + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/SimpleExponentialBackoffTest.kt b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/SimpleExponentialBackoffTest.kt new file mode 100644 index 00000000..e9befe91 --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/SimpleExponentialBackoffTest.kt @@ -0,0 +1,93 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport + +import kotlin.math.max +import kotlin.test.Test +import kotlin.test.assertTrue +import kotlin.test.assertFalse + +class SimpleExponentialBackoffTest { + + @Test + fun `given stable operation, tryWithExponentialBackoff should end in success on first attempt`() { + val backoff = SimpleExponentialBackoff() + val operation = { _: Int, _: Long -> true } + val result = backoff.tryWithExponentialBackoff( + onSleep = {}, + operation = operation + ) + assertTrue(result) + } + + @Test + fun `given unstable operation, tryWithExponentialBackoff should retry operation until success`() { + val backoff = SimpleExponentialBackoff() + var attemptCount = 0 + val operation = { _: Int, _: Long -> ++attemptCount == 3 } + val result = backoff.tryWithExponentialBackoff( + maxRetries = 5, + onSleep = {}, + operation = operation + ) + assertTrue(result) + } + + @Test + fun `given broken operation, tryWithExponentialBackoff should end with negative result`() { + val backoff = SimpleExponentialBackoff() + var attemptCount = 0 + val operation = { _: Int, _: Long -> ++attemptCount; false } + val result = backoff.tryWithExponentialBackoff( + maxRetries = 5, + onSleep = {}, + operation = operation + ) + assertFalse(result) + } + + @Test + fun `given maxDelay, tryWithExponentialBackoff shouldn't exceed it`() { + val backoff = SimpleExponentialBackoff() + var maxDelay = 0L + val operation = { _: Int, _: Long -> false } + backoff.tryWithExponentialBackoff( + initDelay = 0L, + baseDelay = 1000L, + maxDelay = 5000L, + factor = 2.0, + maxRetries = 5, + onSleep = { + maxDelay = max(maxDelay, it) + }, + operation = operation + ) + assertTrue(maxDelay <= 5000L) + } + + @Test + fun `given maxRetries, tryWithExponentialBackoff shouldn't exceed it`() { + val backoff = SimpleExponentialBackoff() + var attemptCount = 0 + val operation = { _: Int, _: Long -> ++attemptCount; false } + val result = backoff.tryWithExponentialBackoff( + maxRetries = 5, + onSleep = {}, + operation = operation + ) + assertTrue(attemptCount <= 5) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageTransportTest.kt b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageTransportTest.kt new file mode 100644 index 00000000..6f7a708a --- /dev/null +++ b/lib-jvm-shared/agent-transport/src/jvmTest/kotlin/com/epam/drill/agent/transport/http/HttpAgentMessageTransportTest.kt @@ -0,0 +1,122 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.transport.http + +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockkStatic +import io.mockk.verify +import org.apache.hc.client5.http.classic.methods.HttpGet +import org.apache.hc.client5.http.classic.methods.HttpPost +import org.apache.hc.client5.http.classic.methods.HttpPut +import org.apache.hc.client5.http.entity.GzipCompressingEntity +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder +import org.apache.hc.core5.http.ClassicHttpRequest +import org.apache.hc.core5.http.HttpHeaders +import org.apache.hc.core5.http.io.HttpClientResponseHandler +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.common.transport.ResponseStatus +import io.mockk.slot +import kotlin.test.* + +class HttpAgentMessageTransportTest { + + @MockK + private lateinit var clientBuilder: HttpClientBuilder + @MockK + private lateinit var closeableHttpClient: CloseableHttpClient + + private val request = slot() + + @BeforeTest + fun setup() = MockKAnnotations.init(this).also { + every { clientBuilder.build() } returns closeableHttpClient + every { closeableHttpClient.close() } returns Unit + every { + closeableHttpClient.execute(capture(request), any>>()) + } returns ResponseStatus(true) + } + + @Test + fun `successful GET`() = withHttpClientBuilder { + val transport = HttpAgentMessageTransport("http://someadmin", "") + val destination = AgentMessageDestination("GET", "somepath") + val status = transport.send(destination, ByteArray(2), "mime/type").success + + assertTrue(status) + verifyClassicHttpRequest("http://someadmin/somepath", "mime/type") + } + + @Test + fun `successful POST`() = withHttpClientBuilder { + val transport = HttpAgentMessageTransport("http://someadmin", "") + val destination = AgentMessageDestination("POST", "somepath") + val status = transport.send(destination, ByteArray(2), "mime/type").success + + assertTrue(status) + verifyClassicHttpRequest("http://someadmin/somepath", "mime/type") + } + + @Test + fun `successful PUT`() = withHttpClientBuilder { + val transport = HttpAgentMessageTransport("http://someadmin", "") + val destination = AgentMessageDestination("PUT", "somepath") + val status = transport.send(destination, ByteArray(2), "mime/type").success + + assertTrue(status) + verifyClassicHttpRequest("http://someadmin/somepath", "mime/type") + } + + @Test(expected = IllegalArgumentException::class) + fun `unknown HTTP method`() = withHttpClientBuilder { + val transport = HttpAgentMessageTransport("http://someadmin", "") + val destination = AgentMessageDestination("SOME", "somepath") + transport.send(destination, ByteArray(2)) + } + + @Test + fun `default content type`() = withHttpClientBuilder { + val transport = HttpAgentMessageTransport("http://someadmin", "") + val destination = AgentMessageDestination("POST", "somepath") + val status = transport.send(destination, ByteArray(2)).success + + assertTrue(status) + verifyClassicHttpRequest("http://someadmin/somepath", "*/*") + } + + private inline fun withHttpClientBuilder(block: () -> Unit) = mockkStatic(HttpClientBuilder::class) { + every { HttpClientBuilder.create() } returns clientBuilder + block() + } + + private inline fun verifyClassicHttpRequest(uri: String, contentType: String) { + verify(exactly = 1) { + closeableHttpClient.execute(any(), any>>()) + } + verify(exactly = 1) { + closeableHttpClient.execute(request.captured, any>>()) + } + assertIs(request.captured) + assertIs(request.captured.entity) + assertEquals(uri, request.captured.uri.toString()) + assertEquals(contentType, request.captured.getHeader(HttpHeaders.CONTENT_TYPE).value) + assertEquals(contentType, request.captured.entity.contentType) + assertEquals("gzip", request.captured.entity.contentEncoding) + } + +} diff --git a/lib-jvm-shared/build.gradle.kts b/lib-jvm-shared/build.gradle.kts new file mode 100644 index 00000000..e41289aa --- /dev/null +++ b/lib-jvm-shared/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + `kotlin-dsl-base`.apply(false) + `kotlin-dsl`.apply(false) + kotlin("jvm").apply(false) + kotlin("multiplatform").apply(false) + kotlin("plugin.allopen").apply(false) + kotlin("plugin.noarg").apply(false) + kotlin("plugin.serialization").apply(false) + id("kotlinx-atomicfu").apply(false) + id("org.jetbrains.kotlinx.benchmark").apply(false) + id("com.github.hierynomus.license").apply(false) + id("com.google.protobuf").apply(false) +} + +group = "com.epam.drill.agent" + +repositories { + mavenCentral() +} diff --git a/lib-jvm-shared/common/CODE_OF_CONDUCT.md b/lib-jvm-shared/common/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..ae731dda --- /dev/null +++ b/lib-jvm-shared/common/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ProjectEPM-D4J@epam.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/lib-jvm-shared/common/LICENSE b/lib-jvm-shared/common/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/common/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/common/README.md b/lib-jvm-shared/common/README.md new file mode 100644 index 00000000..103f6bff --- /dev/null +++ b/lib-jvm-shared/common/README.md @@ -0,0 +1,16 @@ +# Drill common back-end API module + +This repo contains common back-end related Drill4J project parts (common part). + +This API using +- Core part: + - https://github.com/Drill4J/admin + - https://github.com/Drill4J/agent + - https://github.com/Drill4J/native-agent-connector +- Agents + - https://github.com/Drill4J/java-agent + - js-agent? https://github.com/Drill4J/js-agent + - .Net agent? https://github.com/Drill4J/agent-dotnet +- Plugins: + - https://github.com/Drill4J/state-watcher-plugin + - https://github.com/Drill4J/test2code-plugin diff --git a/lib-jvm-shared/common/build.gradle.kts b/lib-jvm-shared/common/build.gradle.kts new file mode 100644 index 00000000..b503e161 --- /dev/null +++ b/lib-jvm-shared/common/build.gradle.kts @@ -0,0 +1,75 @@ +import java.net.URI +import java.util.Properties +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} + +val kotlinxSerializationVersion: String by parent!!.extra +val macosLd64: String by parent!!.extra + +repositories { + mavenCentral() +} + +kotlin { + jvm() + linuxX64() + mingwX64() + macosArm64().apply { + if (macosLd64.toBoolean()) { + binaries.all { + linkerOpts("-ld64") + } + } + } + macosX64().apply { + if(macosLd64.toBoolean()){ + binaries.all { + linkerOpts("-ld64") + } + } + } + @Suppress("UNUSED_VARIABLE") + sourceSets { + all { + languageSettings.optIn("kotlin.time.ExperimentalTime") + } + val commonMain by getting { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinxSerializationVersion") + implementation(project(":konform")) + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/AgentContext.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/AgentContext.kt new file mode 100644 index 00000000..f3f8e1d6 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/AgentContext.kt @@ -0,0 +1,21 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common + +interface AgentContext { + operator fun invoke(): String? + operator fun get(key: String): String? +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/classloading/ClassSource.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/classloading/ClassSource.kt new file mode 100644 index 00000000..0cb600fa --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/classloading/ClassSource.kt @@ -0,0 +1,53 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.classloading + +private const val SUBCLASS_OF = "!subclassOf:" + +data class ClassSource( + private val entityName: String, + private val superName: String? = null, + private val bytes: ByteArray = byteArrayOf(), +) : EntitySource { + + override fun entityName() = entityName + + override fun bytes() = bytes + + override fun toString() = "$entityName: ${this::class.simpleName}" + + override fun equals(other: Any?) = other is ClassSource && entityName == other.entityName + + override fun hashCode() = entityName.hashCode() + + fun prefixMatches(prefixes: Iterable, offset: Int = 0): Boolean { + val isMatchPrefix: () -> Boolean = { + prefixes.any { entityName.regionMatches(offset, it, 0, it.length) } + } + val isNotExcluded: () -> Boolean = { + prefixes.none { it.startsWith('!') && entityName.regionMatches(offset, it, 1, it.length - 1) } + } + val isNotSubclass: () -> Boolean = { + superName == null || prefixes.none { + it.startsWith(SUBCLASS_OF) && + superName.isNotBlank() && + superName.regionMatches(offset, it, SUBCLASS_OF.length, it.length - SUBCLASS_OF.length) + } + } + return isMatchPrefix() && isNotExcluded() && isNotSubclass() + } + +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/classloading/EntitySource.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/classloading/EntitySource.kt new file mode 100644 index 00000000..fdc44c1e --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/classloading/EntitySource.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.classloading + +/** + * Class containing information about entity/class name and bytes + */ +interface EntitySource { + /** + * @return the name of the class of the source + */ + fun entityName(): String + + /** + * @return bytes of the source + */ + fun bytes(): ByteArray +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentConfiguration.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentConfiguration.kt new file mode 100644 index 00000000..042a7bf7 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentConfiguration.kt @@ -0,0 +1,21 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +interface AgentConfiguration { + val agentMetadata: AgentMetadata + val parameters: AgentParameters +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentMetadata.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentMetadata.kt new file mode 100644 index 00000000..2b957095 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentMetadata.kt @@ -0,0 +1,31 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +import kotlinx.serialization.Serializable +import com.epam.drill.agent.common.transport.AgentMessage +import kotlinx.serialization.Transient + +@Serializable +data class AgentMetadata( + val groupId: String, + val appId: String, + val instanceId: String, + val commitSha: String? = null, + val buildVersion: String? = null, + val envId: String? = null, + @Transient val packagesPrefixes: List = emptyList() +) : AgentMessage() diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameterDefinition.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameterDefinition.kt new file mode 100644 index 00000000..471155b4 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameterDefinition.kt @@ -0,0 +1,158 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +import com.epam.drill.agent.konform.validation.Valid +import com.epam.drill.agent.konform.validation.Validation +import com.epam.drill.agent.konform.validation.ValidationBuilder +import com.epam.drill.agent.konform.validation.ValidationResult +import kotlin.String +import kotlin.reflect.KClass +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +class AgentParameterDefinition( + name: String, + description: String? = null, + type: KClass, + val defaultValue: T? = null, + parser: (String) -> T, + validation: ValidationType = ValidationType.STRICT, + validator: (T?, AgentParameters) -> ValidationResult<*> +) : BaseAgentParameterDefinition( + name = name, + description = description, + type = type, + parser = parser, + validation = validation, + validator = validator, +) { + + companion object { + + fun forString( + name: String, + description: String? = null, + defaultValue: String? = null, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> String = { it }, + validator: ValidationBuilder.() -> Unit = {} + ) = AgentParameterDefinition( + name, + description, + String::class, + defaultValue, + parser, + validation, + validator = { it, _ -> validate( it, validator) } + ) + + fun forBoolean( + name: String, + description: String? = null, + defaultValue: Boolean? = null, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Boolean = { it.toBoolean() }, + validator: ValidationBuilder.() -> Unit = {} + ) = AgentParameterDefinition( + name, + description, + Boolean::class, + defaultValue, + parser, + validation, + validator = { it, _ -> validate( it, validator) } + ) + + fun forInt( + name: String, + description: String? = null, + defaultValue: Int? = null, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Int = { it.toInt() }, + validator: ValidationBuilder.() -> Unit = {} + ) = AgentParameterDefinition( + name, + description, + Int::class, + defaultValue, + parser, + validation, + validator = { it, _ -> validate( it, validator) } + ) + + fun forLong( + name: String, + description: String? = null, + defaultValue: Long? = null, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Long = { it.toLong() }, + validator: ValidationBuilder.() -> Unit = {} + ) = AgentParameterDefinition( + name, + description, + Long::class, + defaultValue, + parser, + validation, + validator = { it, _ -> validate( it, validator) } + ) + + fun forDuration( + name: String, + description: String? = null, + defaultValue: Duration? = null, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Duration = { it.toLong().toDuration(DurationUnit.MILLISECONDS) }, + validator: ValidationBuilder.() -> Unit = {} + ) = AgentParameterDefinition( + name, + description, + Duration::class, + defaultValue, + parser, + validation, + validator = { it, _ -> validate( it, validator) } + ) + + fun forList( + name: String, + description: String? = null, + requiredIf: (AgentParameters) -> Boolean = { false }, + defaultValue: List = emptyList(), + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> List = { it.split(";") }, + listValidator: ValidationBuilder>.() -> Unit = {}, + itemValidator: ValidationBuilder.() -> Unit = {} + ) = AgentParameterDefinition( + name = name, + description = description, + type = List::class, + defaultValue = defaultValue, + parser = parser, + validation = validation, + validator = { value, params -> + class Typed(val value: List) + Validation { + if (requiredIf(params)) + Typed::value required listValidator + Typed::value onEach itemValidator + }.validate(Typed(value?.filterIsInstance() ?: emptyList())) + } + ) + } +} \ No newline at end of file diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameterDefinitionCollection.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameterDefinitionCollection.kt new file mode 100644 index 00000000..10ce1407 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameterDefinitionCollection.kt @@ -0,0 +1,27 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +abstract class AgentParameterDefinitionCollection(private val definitions: MutableSet> = mutableSetOf()) { + + fun > D.register(): D { + definitions.add(this) + return this + } + + fun getAll(): Set> = definitions + +} \ No newline at end of file diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameters.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameters.kt new file mode 100644 index 00000000..23b5a230 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentParameters.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +import kotlin.reflect.KProperty + +interface AgentParameters { + operator fun get(name: String): T? + operator fun get(definition: AgentParameterDefinition): T + operator fun getValue(ref: Any?, property: KProperty<*>): T? + operator fun get(definition: NullableAgentParameterDefinition): T? + fun define(vararg definitions: BaseAgentParameterDefinition<*>): List> +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentType.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentType.kt new file mode 100644 index 00000000..07536489 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/AgentType.kt @@ -0,0 +1,22 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +enum class AgentType(val notation: String) { + JAVA("Java"), + DOTNET(".NET"), + JAVASCRIPT("JavaScript") +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/BaseAgentParameterDefinition.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/BaseAgentParameterDefinition.kt new file mode 100644 index 00000000..a2572965 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/BaseAgentParameterDefinition.kt @@ -0,0 +1,53 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +import com.epam.drill.agent.konform.validation.Validation +import com.epam.drill.agent.konform.validation.ValidationBuilder +import com.epam.drill.agent.konform.validation.ValidationResult +import kotlin.reflect.KClass + +sealed class BaseAgentParameterDefinition( + val name: String, + val description: String? = null, + val type: KClass, + val parser: (String) -> T, + val validation: ValidationType = ValidationType.STRICT, + val validator: (T?, AgentParameters) -> ValidationResult<*> +) { + fun isSoftValidation() = validation == ValidationType.SOFT + fun isStrictValidation() = validation == ValidationType.STRICT +} + +enum class ValidationType { + STRICT, + SOFT +} + +internal fun validateIfPresent(value: T?, validator: ValidationBuilder.() -> Unit): ValidationResult<*> { + class Typed(val value: T?) + return Validation { + Typed::value ifPresent validator + }.validate(Typed(value)) +} + +internal fun validate(value: T?, validator: ValidationBuilder.() -> Unit): ValidationResult<*> { + class Typed(val value: T?) + return Validation { + Typed::value required validator + }.validate(Typed(value)) +} + diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/NullableAgentParameterDefinition.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/NullableAgentParameterDefinition.kt new file mode 100644 index 00000000..f5d3cf9a --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/NullableAgentParameterDefinition.kt @@ -0,0 +1,135 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +import com.epam.drill.agent.konform.validation.ValidationBuilder +import com.epam.drill.agent.konform.validation.ValidationResult +import kotlin.reflect.KClass +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +class NullableAgentParameterDefinition( + name: String, + description: String? = null, + type: KClass, + parser: (String) -> T, + validation: ValidationType = ValidationType.STRICT, + validator: (T?, AgentParameters) -> ValidationResult<*> +) : BaseAgentParameterDefinition( + name, + description, + type, + parser, + validation, + validator +) { + + companion object { + + fun forString( + name: String, + description: String? = null, + requiredIf: (AgentParameters) -> Boolean = { false }, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> String = { it }, + validator: ValidationBuilder.() -> Unit = {} + ) = NullableAgentParameterDefinition( + name = name, + description = description, + type = String::class, + parser = parser, + validation = validation, + validator = validateIfRequired(requiredIf, validator) + ) + + fun forBoolean( + name: String, + description: String? = null, + requiredIf: (AgentParameters) -> Boolean = { false }, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Boolean = { it.toBoolean() }, + validator: ValidationBuilder.() -> Unit = {} + ) = NullableAgentParameterDefinition( + name = name, + description = description, + type = Boolean::class, + parser = parser, + validation = validation, + validator = validateIfRequired(requiredIf, validator) + ) + + fun forInt( + name: String, + description: String? = null, + requiredIf: (AgentParameters) -> Boolean = { false }, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Int = { it.toInt() }, + validator: ValidationBuilder.() -> Unit = {}, + ) = NullableAgentParameterDefinition( + name = name, + description = description, + type = Int::class, + parser = parser, + validation = validation, + validator = validateIfRequired(requiredIf, validator) + ) + + fun forLong( + name: String, + description: String? = null, + requiredIf: (AgentParameters) -> Boolean = { false }, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Long = { it.toLong() }, + validator: ValidationBuilder.() -> Unit = {} + ) = NullableAgentParameterDefinition( + name = name, + description = description, + type = Long::class, + parser = parser, + validation = validation, + validator = validateIfRequired(requiredIf, validator) + ) + + fun forDuration( + name: String, + description: String? = null, + requiredIf: (AgentParameters) -> Boolean = { false }, + validation: ValidationType = ValidationType.STRICT, + parser: (String) -> Duration = { it.toLong().toDuration(DurationUnit.MILLISECONDS) }, + validator: ValidationBuilder.() -> Unit = {} + ) = NullableAgentParameterDefinition( + name = name, + description = description, + type = Duration::class, + parser = parser, + validation = validation, + validator = validateIfRequired(requiredIf, validator) + ) + + private fun validateIfRequired( + requiredIf: (AgentParameters) -> Boolean, + validator: ValidationBuilder.() -> Unit + ): (T?, AgentParameters) -> ValidationResult<*> = { value, parameters -> + if (requiredIf(parameters)) + validate(value, validator) + else + validateIfPresent(value, validator) + } + + } + +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/ValidationError.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/ValidationError.kt new file mode 100644 index 00000000..39d85d98 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/configuration/ValidationError.kt @@ -0,0 +1,21 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.configuration + +class ValidationError( + val definition: BaseAgentParameterDefinition, + val messages: List, +) \ No newline at end of file diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/AgentModule.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/AgentModule.kt new file mode 100644 index 00000000..3b9a830d --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/AgentModule.kt @@ -0,0 +1,30 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.module + +import com.epam.drill.agent.common.AgentContext +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.transport.AgentMessageSender + +abstract class AgentModule( + val id: String, + val context: AgentContext, + protected val sender: AgentMessageSender, + protected val configuration: AgentConfiguration +) { + abstract fun load() + open fun onConnect() = Unit +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/ClassScanner.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/ClassScanner.kt new file mode 100644 index 00000000..51c9b940 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/ClassScanner.kt @@ -0,0 +1,29 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.module + +import com.epam.drill.agent.common.classloading.EntitySource + +/** + * Service for scanning classes of the application under test + */ +interface ClassScanner { + /** + * Scan target classes of the application under test + * @param consumer the function for processing chunks of scanned classes + */ + fun scanClasses(consumer: (Set) -> Unit) +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/Instrumenter.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/Instrumenter.kt new file mode 100644 index 00000000..df4a4271 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/module/Instrumenter.kt @@ -0,0 +1,20 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.module + +interface Instrumenter { + fun instrument(className: String, initialBytes: ByteArray): ByteArray? +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/DrillInitialContext.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/DrillInitialContext.kt new file mode 100644 index 00000000..50c0a589 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/DrillInitialContext.kt @@ -0,0 +1,41 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.request + +object DrillInitialContext { + private val context: MutableMap = mutableMapOf() + + fun add(key: String, value: String) { + context[key] = value + } + + fun remove(key: String) { + context.remove(key) + } + + fun get(key: String): String? = context[key] + + fun getAll(): Map = context.toMap() + + fun getDrillRequest(): DrillRequest? { + return get("drill-session-id").takeIf { !it.isNullOrBlank() }?.let { testSessionId -> + DrillRequest( + drillSessionId = testSessionId, + headers = getAll().filter { it.key != "drill-session-id" }.toMap(), + ) + } + } +} \ No newline at end of file diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/DrillRequest.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/DrillRequest.kt new file mode 100644 index 00000000..41074cdc --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/DrillRequest.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.request + +import kotlinx.serialization.Serializable + +@Serializable +data class DrillRequest( + val drillSessionId: String, + val headers: Map = emptyMap() +) diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/HeadersRetriever.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/HeadersRetriever.kt new file mode 100644 index 00000000..d6dc4708 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/HeadersRetriever.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.request + +interface HeadersRetriever { + fun adminAddressHeader(): String + fun adminAddressValue(): String + fun sessionHeader(): String + fun agentIdHeader(): String + fun agentIdHeaderValue(): String +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/RequestHolder.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/RequestHolder.kt new file mode 100644 index 00000000..f627e987 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/RequestHolder.kt @@ -0,0 +1,22 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.request + +interface RequestHolder { + fun store(drillRequest: DrillRequest) + fun retrieve(): DrillRequest? + fun remove() +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/RequestProcessor.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/RequestProcessor.kt new file mode 100644 index 00000000..dc187159 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/request/RequestProcessor.kt @@ -0,0 +1,21 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.request + +interface RequestProcessor { + fun processServerRequest() + fun processServerResponse() +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessage.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessage.kt new file mode 100644 index 00000000..718ed733 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessage.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.transport + +import kotlinx.serialization.Serializable + +/** + * Abstraction for messages of any type. + * + * It's used in [AgentMessageSender]. + * + * @see [AgentMessageSender] + */ +@Serializable +open class AgentMessage diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageDestination.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageDestination.kt new file mode 100644 index 00000000..46d4a0d2 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageDestination.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.transport + +/** + * Simple data class to hold information about message destination. + * + * It's used with [AgentMessageSender]. + * + * @see [AgentMessageSender] + */ +data class AgentMessageDestination( + val type: String, + val target: String +) diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageReceiver.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageReceiver.kt new file mode 100644 index 00000000..7787c62a --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageReceiver.kt @@ -0,0 +1,22 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.transport + +import kotlin.reflect.KClass + +interface AgentMessageReceiver { + fun receive(destination: AgentMessageDestination, clazz: KClass): T +} \ No newline at end of file diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageSender.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageSender.kt new file mode 100644 index 00000000..9c8e6f42 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentMessageSender.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.transport + +import kotlinx.serialization.KSerializer + +/** + * An interface to send [AgentMessage] objects to [AgentMessageDestination]. + * It has [available] property to indicate transport state. + * + * It should be provided to all agent message producers. + * + * @see [AgentMessage] + * @see [AgentMessageDestination] + */ +interface AgentMessageSender { + fun send(destination: AgentMessageDestination, message: T, serializer: KSerializer) + fun shutdown() {} +} diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentReference.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentReference.kt new file mode 100644 index 00000000..69e8282b --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/AgentReference.kt @@ -0,0 +1,26 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.transport + +import kotlinx.serialization.Serializable + +@Serializable +data class AgentReference( + val instanceId: String, + val appId: String, + val groupId: String, + val buildVersion: String +) diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/ReferencingAgentMessage.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/ReferencingAgentMessage.kt new file mode 100644 index 00000000..c590dd2b --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/ReferencingAgentMessage.kt @@ -0,0 +1,24 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.transport + +import kotlinx.serialization.Serializable + +@Serializable +open class ReferencingAgentMessage( + open var agentReference: AgentReference?, + open val agentMessage: T +): AgentMessage() diff --git a/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/ResponseStatus.kt b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/ResponseStatus.kt new file mode 100644 index 00000000..1b9aab48 --- /dev/null +++ b/lib-jvm-shared/common/src/commonMain/kotlin/com/epam/drill/agent/common/transport/ResponseStatus.kt @@ -0,0 +1,49 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.transport + +/** + * Abstraction for response of any type. + * + * It's used in [AgentMessageSender]. + * + * @see [AgentMessageSender] + */ +class ResponseStatus( + val success: Boolean, + val content: T? = null, + val errorContent: String? = null +) { + fun onError(block: (String?) -> Unit): ResponseStatus { + if (!success) + block(errorContent) + return this + } + + fun onSuccess(block: (T?) -> Unit): ResponseStatus { + if (success) + block(content) + return this + } + + fun mapContent(block: (T) -> M): ResponseStatus { + return ResponseStatus( + success = success, + content = content.takeIf { it != null }?.let { block(it) }, + errorContent = errorContent + ) + } +} diff --git a/lib-jvm-shared/common/src/commonTest/kotlin/com/epam/drill/agent/common/classloading/ClassSourceTest.kt b/lib-jvm-shared/common/src/commonTest/kotlin/com/epam/drill/agent/common/classloading/ClassSourceTest.kt new file mode 100644 index 00000000..b4475d1e --- /dev/null +++ b/lib-jvm-shared/common/src/commonTest/kotlin/com/epam/drill/agent/common/classloading/ClassSourceTest.kt @@ -0,0 +1,47 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.common.classloading + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class ClassSourceTest { + + @Test + fun `prefix matching`() { + val prefixes = listOf("foo/bar", "!foo/bar/Bar", "!subclassOf:foo/bar/Foo") + assertTrue { "foo/bar/Baz.class".let(::ClassSource).prefixMatches(prefixes) } + assertTrue { "Lfoo/bar/Baz.class".let(::ClassSource).prefixMatches(prefixes, 1) } + assertFalse { "foo/baz/Baz.class".let(::ClassSource).prefixMatches(prefixes) } + assertFalse { "Lfoo/baz/Baz.class".let(::ClassSource).prefixMatches(prefixes, 1) } + assertFalse { "foo/bar/Bar.class".let(::ClassSource).prefixMatches(prefixes) } + assertFalse { "Lfoo/bar/Bar.class".let(::ClassSource).prefixMatches(prefixes, 1) } + assertFalse { + ClassSource( + entityName = "foo/bar/Baz.class", + superName = "foo/bar/Foo.class" + ).prefixMatches(prefixes) + } + assertFalse { + ClassSource( + entityName = "Lfoo/bar/Baz.class", + superName = "Lfoo/bar/Foo.class" + ).prefixMatches(prefixes, 1) + } + } + +} diff --git a/lib-jvm-shared/gradle.properties b/lib-jvm-shared/gradle.properties new file mode 100644 index 00000000..fe315e12 --- /dev/null +++ b/lib-jvm-shared/gradle.properties @@ -0,0 +1,42 @@ +kotlinVersion = 1.9.20 +licenseVersion = 0.16.1 +publishVersion = 1.1.0 +kotlinxCollectionsVersion = 0.3.5 +kotlinxCoroutinesVersion = 1.5.2-native-mt +kotlinxSerializationVersion = 1.3.1 +kotlinxBenchmarkVersion = 0.3.1 +exposedVersion = 0.36.2 +atomicfuVersion = 0.16.3 +ktorVersion = 1.6.8 +bcelVersion = 6.6.0 +kotlinPoetVersion = 1.9.0 +javassistVersion = 3.30.2-GA +uuidVersion = 0.3.1 +protobufVersion = 0.8.17 +microutilsLoggingVersion = 2.1.23 +postgresSqlVersion = 42.3.1 +testContainersVersion = 1.16.2 +zaxxerHikaricpVersion = 4.0.3 +webjarsSwaggerUiVersion = 3.23.8 +googleGsonVersion = 2.8.6 +ajaltCliktVersion = 3.0.1 +logbackVersion = 1.2.12 +apacheHttpClientVersion = 5.2.3 +transmittableThreadLocalVersion = 2.12.2 +shadowPluginVersion = 6.1.0 +bytebuddyVersion = 1.14.11 + +nativeAgentLibName = drill-agent +macosLd64 = false + +org.gradle.jvmargs = -Xmx6144m -XX:MaxPermSize=2048m +org.gradle.daemon = false +org.gradle.parallel = true +org.gradle.workers.max = 4 + +kotlin.code.style = official +kotlin.native.enableParallelExecutionCheck = false +kotlin.native.ignoreDisabledTargets = true +kotlin.mpp.stability.nowarn = true +kotlin.mpp.enableCInteropCommonization=true +kotlin.mpp.applyDefaultHierarchyTemplate=false diff --git a/lib-jvm-shared/gradle/wrapper/gradle-wrapper.jar b/lib-jvm-shared/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..7454180f Binary files /dev/null and b/lib-jvm-shared/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lib-jvm-shared/gradle/wrapper/gradle-wrapper.properties b/lib-jvm-shared/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..aa991fce --- /dev/null +++ b/lib-jvm-shared/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lib-jvm-shared/gradlew b/lib-jvm-shared/gradlew new file mode 100644 index 00000000..c53aefaa --- /dev/null +++ b/lib-jvm-shared/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/lib-jvm-shared/gradlew.bat b/lib-jvm-shared/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/lib-jvm-shared/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib-jvm-shared/interceptor-hook/LICENSE b/lib-jvm-shared/interceptor-hook/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/interceptor-hook/README.md b/lib-jvm-shared/interceptor-hook/README.md new file mode 100644 index 00000000..248e4feb --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/README.md @@ -0,0 +1,3 @@ +# Drill system calls interceptor library + +Library for intercepting system functions e.g. send, write, etc. diff --git a/lib-jvm-shared/interceptor-hook/build.gradle.kts b/lib-jvm-shared/interceptor-hook/build.gradle.kts new file mode 100644 index 00000000..fdb014e4 --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/build.gradle.kts @@ -0,0 +1,73 @@ +import java.net.URI +import java.util.Properties +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} + +val kotlinxCollectionsVersion: String by extra +val atomicfuVersion: String by extra + +repositories { + mavenCentral() +} + +kotlin { + val configureCInterop: KotlinNativeTarget.() -> Unit = { + compilations["main"].cinterops.create("hook_bindings") { + includeDirs("src/nativeInterop/cinterop/$targetName") + } + } + linuxX64(configure = configureCInterop) + mingwX64(configure = configureCInterop) + @Suppress("UNUSED_VARIABLE") + sourceSets { + all { + languageSettings.optIn("kotlin.ExperimentalUnsignedTypes") + } + val commonMain by getting { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:$kotlinxCollectionsVersion") + implementation("org.jetbrains.kotlinx:atomicfu:$atomicfuVersion") + } + } + val nativeMain by creating { + dependsOn(commonMain) + } + val posixMain by creating { + dependsOn(nativeMain) + } + val mingwX64Main by getting { + dependsOn(nativeMain) + } + val linuxX64Main by getting { + dependsOn(posixMain) + } + } + +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } +} diff --git a/lib-jvm-shared/interceptor-hook/src/mingwX64Main/kotlin/com/epam/drill/hook/io/Butyfier.kt b/lib-jvm-shared/interceptor-hook/src/mingwX64Main/kotlin/com/epam/drill/hook/io/Butyfier.kt new file mode 100644 index 00000000..a12d5615 --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/mingwX64Main/kotlin/com/epam/drill/hook/io/Butyfier.kt @@ -0,0 +1,39 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.hook.io + +import com.epam.drill.hook.gen.* +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +val nativeRead + get() = read_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeWrite + get() = write_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeSend + get() = send_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeRecv + get() = recv_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeAccept + get() = accept_func!! diff --git a/lib-jvm-shared/interceptor-hook/src/mingwX64Main/kotlin/com/epam/drill/hook/io/Drillo.kt b/lib-jvm-shared/interceptor-hook/src/mingwX64Main/kotlin/com/epam/drill/hook/io/Drillo.kt new file mode 100644 index 00000000..0cc6ac9f --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/mingwX64Main/kotlin/com/epam/drill/hook/io/Drillo.kt @@ -0,0 +1,143 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:Suppress("ObjectPropertyName") + +package com.epam.drill.hook.io + + +import com.epam.drill.hook.gen.* +import kotlinx.atomicfu.* +import kotlinx.cinterop.* +import kotlinx.collections.immutable.* +import platform.posix.* +import platform.windows.LPDWORD +import platform.windows._OVERLAPPED + +@OptIn(ExperimentalForeignApi::class) +actual val tcpInitializer = run { + val socketHook = funchook_create() + funchook_prepare(socketHook, close_func_point, staticCFunction(::drillClose)).check("prepare close_func_point") + funchook_prepare( + socketHook, + connect_func_point, + staticCFunction(::drillConnect) + ).check("prepare connect_func_point") + funchook_prepare(socketHook, accept_func_point, staticCFunction(::drillAccept)).check("prepare accept_func_point") + funchook_install(socketHook, 0).check("funchook_install") +} + +@OptIn(ExperimentalForeignApi::class) +actual fun drillRead(fd: DRILL_SOCKET, buf: CPointer>?, size: size_t): ssize_t { + initRuntimeIfNeeded() + val read = nativeRead(fd.convert(), buf, size.convert()) + if (read > 0 && (accepts.contains(fd.convert()) || connects.contains(fd.convert()))) + tryDetectProtocol(fd.convert(), buf, read.convert()) + return read.convert() +} + +@OptIn(ExperimentalForeignApi::class) +actual fun drillWrite(fd: DRILL_SOCKET, buf: CPointer>?, size: size_t): ssize_t { + initRuntimeIfNeeded() + + return memScoped { + val (finalBuf, finalSize, ll) = + if (accepts.contains(fd.convert()) || connects.contains(fd.convert())) + processWriteEvent(fd.convert(), buf, size.convert()) + else TcpFinalData(buf, size.convert()) + (nativeWrite(fd.convert(), finalBuf, (finalSize).convert()) - ll).convert() + } +} + + +@OptIn(ExperimentalForeignApi::class) +actual fun drillSend(fd: DRILL_SOCKET, buf: CPointer>?, size: Int, flags: Int): Int { + initRuntimeIfNeeded() + return memScoped { + val (finalBuf, finalSize, ll) = processWriteEvent(fd, buf, size.convert()) + (nativeSend(fd.convert(), finalBuf, (finalSize).convert(), flags) - ll).convert() + } +} + +@OptIn(ExperimentalForeignApi::class) +actual fun drillRecv(fd: DRILL_SOCKET, buf: CPointer>?, size: Int, flags: Int): Int { + initRuntimeIfNeeded() + val read = nativeRecv(fd, buf, size.convert(), flags) + tryDetectProtocol(fd, buf, read.convert()) + return read.convert() +} + +@OptIn(ExperimentalForeignApi::class) +fun drillAccept( + fd: DRILL_SOCKET, + addr: CPointer?, + socklen: CPointer?, +): DRILL_SOCKET { + initRuntimeIfNeeded() + val socket = nativeAccept(fd, addr, socklen) + if (isValidSocket(socket) == 0) + _accepts.update { it + socket } + return socket +} + +@OptIn(ExperimentalForeignApi::class) +actual fun configureTcpHooks() { + configureTcpHooksBuild { + println("Configuration for mingw") + funchook_prepare(tcpHook, wsaSend_func_point, staticCFunction(::drillWsaSend)) + funchook_prepare(tcpHook, wsaRecv_func_point, staticCFunction(::drillWsaRecv)) + } +} + +@OptIn(ExperimentalForeignApi::class) +private fun drillWsaSend( + fd: SOCKET, + buff: CPointer<_WSABUF>?, + buffersSize: UInt, + written: LPDWORD?, + p5: UInt, + p6: CPointer<_OVERLAPPED>?, + p7: LPWSAOVERLAPPED_COMPLETION_ROUTINE?, +): Int { + initRuntimeIfNeeded() + val buffer = buff!![0] + val size = buffer.len + val buf = buffer.buf + return memScoped { + val (finalBuf, finalSize, injectedSize) = processWriteEvent(fd.convert(), buf, size.convert()) + buff[0].buf = finalBuf + buff[0].len = finalSize.convert() + val wsasendFunc = wsaSend_func!!(fd, buff, buffersSize, written, p5, p6, p7) + written!!.pointed.value -= injectedSize.convert() + (wsasendFunc).convert() + } +} + +@OptIn(ExperimentalForeignApi::class) +private fun drillWsaRecv( + fd: SOCKET, + buff: CPointer<_WSABUF>?, + p3: UInt, + read: LPDWORD?, + p5: LPDWORD?, + p6: CPointer<_OVERLAPPED>?, + p7: LPWSAOVERLAPPED_COMPLETION_ROUTINE?, +): Int { + initRuntimeIfNeeded() + val wsarecvFunc: Int = wsaRecv_func!!(fd, buff, p3, read, p5, p6, p7) + val finalBuf = buff!!.pointed + tryDetectProtocol(fd, finalBuf.buf, read!!.pointed.value.convert()) + return wsarecvFunc +} diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/hook_bindings.def b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/hook_bindings.def new file mode 100644 index 00000000..2cddf115 --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/hook_bindings.def @@ -0,0 +1,153 @@ +package = com.epam.drill.hook.gen +headers = funchook.h +compilerOpts = -std=c99 +compilerOpts.mingw_x64 = -DMINGW +staticLibraries.mingw = hook.lib +staticLibraries.linux = libdistorm.a hook.a +staticLibraries.osx = libdistorm.a hook.a +libraryPaths.mingw = src/nativeInterop/cinterop/mingwX64 +libraryPaths.linux = src/nativeInterop/cinterop/linuxX64 +libraryPaths.osx = src/nativeInterop/cinterop/macosX64 + +--- + +#if defined(__linux__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#ifdef MINGW +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef MINGW +#include +#include +#include +#define mode_t int +#define ssize_t int +#define open _open +/* cast the third argument of _read to suppress warning C4267 */ +#define read(fd, buf, count) _read((fd), (buf), (unsigned int)(count)) +/* cast the second argument of fgets to suppress warning C4267 */ +#define fgets(s, size, fp) fgets((s), (int)(size), (fp)) +#define close _close +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "psapi.lib") +typedef SOCKET DRILL_SOCKET; +typedef int drill_sock_len; +int (*wsaSend_func)(SOCKET, LPWSABUF, DWORD, LPDWORD, DWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE) = WSASend; +static void **wsaSend_func_point = (void **) &wsaSend_func; + +int (*wsaRecv_func)(SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE) = WSARecv; +static void **wsaRecv_func_point = (void **) &wsaRecv_func; + + +int isValidSocket(DRILL_SOCKET soc){ + if(soc == INVALID_SOCKET){ + return -1; + } else { + return 0; + } +} + + +#else +#include +#include +#include +#include + +typedef int DRILL_SOCKET; +typedef socklen_t drill_sock_len; + +ssize_t (*writev_func)(int, const struct iovec *, int) = writev; +static void **writev_func_point = (void **) &writev_func; + +ssize_t (*readv_func)(int, const struct iovec *, int) = readv; +static void **readv_func_point = (void **) &readv_func; +int isValidSocket(DRILL_SOCKET soc){ + if(soc == -1){ + return -1; + } else { + return 0; + } +} + +#endif + +#include + +#ifdef MINGW +//#define DLLEXPORT __declspec(dllexport) +#else +//#define DLLEXPORT +#endif + +#ifdef __GNUC__ +#define NOINLINE __attribute__((noinline)) +#endif +#ifdef _MSC_VER +#define NOINLINE __declspec(noinline) +#endif + +#if defined(__APPLE__) && defined(__clang_major__) && __clang_major__ >= 11 +#define SKIP_TESTS_CHANGING_EXE +#endif + +typedef int (*int_func_t)(void); + + +#if defined(WIN32) || defined(__APPLE__) + +//extern void set_int_val(int val); + +#else +#define set_int_val(val) do {} while(0) +#endif + +#ifdef _MSC_VER +int reset_retval() +{ + return 0; +} +#endif + +#if defined(WIN32) +//__declspec(dllexport) int int_val = 0xbaceba11; +#else +int int_val = 0xbaceba11; +#endif + + +//____________________________________________________________________________sockets +int (*connect_func)(DRILL_SOCKET, const struct sockaddr *, drill_sock_len) = connect; +static void **connect_func_point = (void **) &connect_func; + +DRILL_SOCKET (*accept_func)(DRILL_SOCKET, struct sockaddr *, drill_sock_len *) = accept; +static void **accept_func_point = (void **) &accept_func; + +int (*close_func)(DRILL_SOCKET) = close; +static void **close_func_point = (void **) &close_func; + + +ssize_t (*send_func)(DRILL_SOCKET, const void *, size_t, int) = send; +static void **send_func_point = (void **) &send_func; + +ssize_t (*recv_func)(DRILL_SOCKET, void *, size_t, int) = recv; +static void **recv_func_point = (void **) &recv_func; + +//____________________________________________________________________________sockets + + +static ssize_t (*read_func)(int, void *, size_t) = read; +static void **read_func_point = (void **) &read_func; + +static ssize_t (*write_func)(int, const void *, size_t) = write; +static void **write_func_point = (void **) &write_func; + diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/funchook.h b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/funchook.h new file mode 100644 index 00000000..228d60cd --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/funchook.h @@ -0,0 +1,122 @@ +/* + * This file is part of Funchook. + * https://github.com/kubo/funchook + * + * Funchook is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * As a special exception, the copyright holders of this library give you + * permission to link this library with independent modules to produce an + * executable, regardless of the license terms of these independent + * modules, and to copy and distribute the resulting executable under + * terms of your choice, provided that you also meet, for each linked + * independent module, the terms and conditions of the license of that + * module. An independent module is a module which is not derived from or + * based on this library. If you modify this library, you may extend this + * exception to your version of the library, but you are not obliged to + * do so. If you do not wish to do so, delete this exception statement + * from your version. + * + * Funchook is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with Funchook. If not, see . + */ +#ifndef FUNCHOOK_H +#define FUNCHOOK_H 1 + +/* + * Only functions with FUNCHOOK_EXPORT are visible from outside of funchook.dll + * or libfunchook.so. Others are invisible. + */ +#ifdef FUNCHOOK_EXPORTS +#if defined(WIN32) +#define FUNCHOOK_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +#define FUNCHOOK_EXPORT __attribute__((visibility("default"))) +#endif +#endif /* FUNCHOOK_EXPORTS */ +#ifndef FUNCHOOK_EXPORT +#define FUNCHOOK_EXPORT +#endif + +typedef struct funchook funchook_t; + +#define FUNCHOOK_ERROR_INTERNAL_ERROR -1 +#define FUNCHOOK_ERROR_SUCCESS 0 +#define FUNCHOOK_ERROR_OUT_OF_MEMORY 1 +#define FUNCHOOK_ERROR_ALREADY_INSTALLED 2 +#define FUNCHOOK_ERROR_DISASSEMBLY 3 +#define FUNCHOOK_ERROR_IP_RELATIVE_OFFSET 4 +#define FUNCHOOK_ERROR_CANNOT_FIX_IP_RELATIVE 5 +#define FUNCHOOK_ERROR_FOUND_BACK_JUMP 6 +#define FUNCHOOK_ERROR_TOO_SHORT_INSTRUCTIONS 7 +#define FUNCHOOK_ERROR_MEMORY_ALLOCATION 8 /* memory allocation error */ +#define FUNCHOOK_ERROR_MEMORY_FUNCTION 9 /* other memory function errors */ +#define FUNCHOOK_ERROR_NOT_INSTALLED 10 + +/** + * Create a funchook handle + * + * @return allocated funchook handle. NULL when out-of-memory. + */ +FUNCHOOK_EXPORT funchook_t *funchook_create(void); + +/** + * Prepare hooking + * + * @param funchook a funchook handle created by funchook_create() + * @param target_func function pointer to be intercepted. The pointer to trampoline function is set on success. + * @param hook_func function pointer which is called istead of target_func + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_prepare(funchook_t *funchook, void **target_func, void *hook_func); + +/** + * Install hooks prepared by funchook_prepare(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_install(funchook_t *funchook, int flags); + +/** + * Uninstall hooks installed by funchook_install(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_uninstall(funchook_t *funchook, int flags); + +/** + * Destroy a funchook handle + * + * @param funchook a funchook handle created by funchook_create() + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_destroy(funchook_t *funchook); + +/** + * Get error message + * + * @param funchook a funchook handle created by funchook_create() + * @return pointer to buffer containing error message + */ +FUNCHOOK_EXPORT const char *funchook_error_message(const funchook_t *funchook); + +/** + * Set log file name to debug funchook itself. + * + * @param name log file name + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_set_debug_file(const char *name); + +#endif diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/hook.a b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/hook.a new file mode 100644 index 00000000..3ddfd906 Binary files /dev/null and b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/hook.a differ diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/libdistorm.a b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/libdistorm.a new file mode 100644 index 00000000..bf82ae98 Binary files /dev/null and b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/linuxX64/libdistorm.a differ diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/funchook.h b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/funchook.h new file mode 100644 index 00000000..228d60cd --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/funchook.h @@ -0,0 +1,122 @@ +/* + * This file is part of Funchook. + * https://github.com/kubo/funchook + * + * Funchook is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * As a special exception, the copyright holders of this library give you + * permission to link this library with independent modules to produce an + * executable, regardless of the license terms of these independent + * modules, and to copy and distribute the resulting executable under + * terms of your choice, provided that you also meet, for each linked + * independent module, the terms and conditions of the license of that + * module. An independent module is a module which is not derived from or + * based on this library. If you modify this library, you may extend this + * exception to your version of the library, but you are not obliged to + * do so. If you do not wish to do so, delete this exception statement + * from your version. + * + * Funchook is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with Funchook. If not, see . + */ +#ifndef FUNCHOOK_H +#define FUNCHOOK_H 1 + +/* + * Only functions with FUNCHOOK_EXPORT are visible from outside of funchook.dll + * or libfunchook.so. Others are invisible. + */ +#ifdef FUNCHOOK_EXPORTS +#if defined(WIN32) +#define FUNCHOOK_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +#define FUNCHOOK_EXPORT __attribute__((visibility("default"))) +#endif +#endif /* FUNCHOOK_EXPORTS */ +#ifndef FUNCHOOK_EXPORT +#define FUNCHOOK_EXPORT +#endif + +typedef struct funchook funchook_t; + +#define FUNCHOOK_ERROR_INTERNAL_ERROR -1 +#define FUNCHOOK_ERROR_SUCCESS 0 +#define FUNCHOOK_ERROR_OUT_OF_MEMORY 1 +#define FUNCHOOK_ERROR_ALREADY_INSTALLED 2 +#define FUNCHOOK_ERROR_DISASSEMBLY 3 +#define FUNCHOOK_ERROR_IP_RELATIVE_OFFSET 4 +#define FUNCHOOK_ERROR_CANNOT_FIX_IP_RELATIVE 5 +#define FUNCHOOK_ERROR_FOUND_BACK_JUMP 6 +#define FUNCHOOK_ERROR_TOO_SHORT_INSTRUCTIONS 7 +#define FUNCHOOK_ERROR_MEMORY_ALLOCATION 8 /* memory allocation error */ +#define FUNCHOOK_ERROR_MEMORY_FUNCTION 9 /* other memory function errors */ +#define FUNCHOOK_ERROR_NOT_INSTALLED 10 + +/** + * Create a funchook handle + * + * @return allocated funchook handle. NULL when out-of-memory. + */ +FUNCHOOK_EXPORT funchook_t *funchook_create(void); + +/** + * Prepare hooking + * + * @param funchook a funchook handle created by funchook_create() + * @param target_func function pointer to be intercepted. The pointer to trampoline function is set on success. + * @param hook_func function pointer which is called istead of target_func + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_prepare(funchook_t *funchook, void **target_func, void *hook_func); + +/** + * Install hooks prepared by funchook_prepare(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_install(funchook_t *funchook, int flags); + +/** + * Uninstall hooks installed by funchook_install(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_uninstall(funchook_t *funchook, int flags); + +/** + * Destroy a funchook handle + * + * @param funchook a funchook handle created by funchook_create() + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_destroy(funchook_t *funchook); + +/** + * Get error message + * + * @param funchook a funchook handle created by funchook_create() + * @return pointer to buffer containing error message + */ +FUNCHOOK_EXPORT const char *funchook_error_message(const funchook_t *funchook); + +/** + * Set log file name to debug funchook itself. + * + * @param name log file name + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_set_debug_file(const char *name); + +#endif diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/hook.a b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/hook.a new file mode 100644 index 00000000..ce76ed7c Binary files /dev/null and b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/hook.a differ diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/libdistorm.a b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/libdistorm.a new file mode 100644 index 00000000..daf5aec1 Binary files /dev/null and b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/macosX64/libdistorm.a differ diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/mingwX64/funchook.h b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/mingwX64/funchook.h new file mode 100644 index 00000000..228d60cd --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/mingwX64/funchook.h @@ -0,0 +1,122 @@ +/* + * This file is part of Funchook. + * https://github.com/kubo/funchook + * + * Funchook is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * As a special exception, the copyright holders of this library give you + * permission to link this library with independent modules to produce an + * executable, regardless of the license terms of these independent + * modules, and to copy and distribute the resulting executable under + * terms of your choice, provided that you also meet, for each linked + * independent module, the terms and conditions of the license of that + * module. An independent module is a module which is not derived from or + * based on this library. If you modify this library, you may extend this + * exception to your version of the library, but you are not obliged to + * do so. If you do not wish to do so, delete this exception statement + * from your version. + * + * Funchook is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with Funchook. If not, see . + */ +#ifndef FUNCHOOK_H +#define FUNCHOOK_H 1 + +/* + * Only functions with FUNCHOOK_EXPORT are visible from outside of funchook.dll + * or libfunchook.so. Others are invisible. + */ +#ifdef FUNCHOOK_EXPORTS +#if defined(WIN32) +#define FUNCHOOK_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +#define FUNCHOOK_EXPORT __attribute__((visibility("default"))) +#endif +#endif /* FUNCHOOK_EXPORTS */ +#ifndef FUNCHOOK_EXPORT +#define FUNCHOOK_EXPORT +#endif + +typedef struct funchook funchook_t; + +#define FUNCHOOK_ERROR_INTERNAL_ERROR -1 +#define FUNCHOOK_ERROR_SUCCESS 0 +#define FUNCHOOK_ERROR_OUT_OF_MEMORY 1 +#define FUNCHOOK_ERROR_ALREADY_INSTALLED 2 +#define FUNCHOOK_ERROR_DISASSEMBLY 3 +#define FUNCHOOK_ERROR_IP_RELATIVE_OFFSET 4 +#define FUNCHOOK_ERROR_CANNOT_FIX_IP_RELATIVE 5 +#define FUNCHOOK_ERROR_FOUND_BACK_JUMP 6 +#define FUNCHOOK_ERROR_TOO_SHORT_INSTRUCTIONS 7 +#define FUNCHOOK_ERROR_MEMORY_ALLOCATION 8 /* memory allocation error */ +#define FUNCHOOK_ERROR_MEMORY_FUNCTION 9 /* other memory function errors */ +#define FUNCHOOK_ERROR_NOT_INSTALLED 10 + +/** + * Create a funchook handle + * + * @return allocated funchook handle. NULL when out-of-memory. + */ +FUNCHOOK_EXPORT funchook_t *funchook_create(void); + +/** + * Prepare hooking + * + * @param funchook a funchook handle created by funchook_create() + * @param target_func function pointer to be intercepted. The pointer to trampoline function is set on success. + * @param hook_func function pointer which is called istead of target_func + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_prepare(funchook_t *funchook, void **target_func, void *hook_func); + +/** + * Install hooks prepared by funchook_prepare(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_install(funchook_t *funchook, int flags); + +/** + * Uninstall hooks installed by funchook_install(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_uninstall(funchook_t *funchook, int flags); + +/** + * Destroy a funchook handle + * + * @param funchook a funchook handle created by funchook_create() + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_destroy(funchook_t *funchook); + +/** + * Get error message + * + * @param funchook a funchook handle created by funchook_create() + * @return pointer to buffer containing error message + */ +FUNCHOOK_EXPORT const char *funchook_error_message(const funchook_t *funchook); + +/** + * Set log file name to debug funchook itself. + * + * @param name log file name + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_set_debug_file(const char *name); + +#endif diff --git a/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/mingwX64/hook.lib b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/mingwX64/hook.lib new file mode 100644 index 00000000..ae802029 Binary files /dev/null and b/lib-jvm-shared/interceptor-hook/src/nativeInterop/cinterop/mingwX64/hook.lib differ diff --git a/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/Butyfier.kt b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/Butyfier.kt new file mode 100644 index 00000000..c79e2d8d --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/Butyfier.kt @@ -0,0 +1,35 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.hook.io + +import com.epam.drill.hook.gen.* +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +val nativeConnect + get() = connect_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeClose + get() = close_func!! + + +@Suppress("NOTHING_TO_INLINE") +inline fun Int.check(message: String) { + if(this < 0) + println("Hook operation '$message' failed") + return +} diff --git a/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/DrillIo.kt b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/DrillIo.kt new file mode 100644 index 00000000..3a77c003 --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/DrillIo.kt @@ -0,0 +1,117 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:Suppress("ObjectPropertyName") + +package com.epam.drill.hook.io + +import com.epam.drill.hook.gen.* +import kotlinx.atomicfu.* +import kotlinx.cinterop.* +import kotlinx.collections.immutable.* +import platform.posix.* +import kotlin.native.SharedImmutable +import kotlin.native.ThreadLocal +import kotlin.native.concurrent.* + +@Suppress("unused") +@SharedImmutable +expect val tcpInitializer: Unit + +@SharedImmutable +private val accessThread = Worker.start(true) + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val _connects = atomic(persistentHashSetOf()) + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val _accepts = atomic(persistentHashSetOf()) + +@OptIn(ExperimentalForeignApi::class) +val connects + get() = _connects.value + +@OptIn(ExperimentalForeignApi::class) +val accepts + get() = _accepts.value + +@OptIn(ExperimentalForeignApi::class) +@ThreadLocal +private var _tcpHook: CPointer? = null + +@OptIn(ExperimentalForeignApi::class, ObsoleteWorkersApi::class) +var tcpHook + get() = accessThread.execute(TransferMode.UNSAFE, {}) { _tcpHook }.result + set(value) = accessThread.execute(TransferMode.UNSAFE, { value }) { _tcpHook = it }.result + +expect fun configureTcpHooks() + +@OptIn(ExperimentalForeignApi::class) +fun configureTcpHooksBuild(block: () -> Unit) = if (tcpHook != null) { + funchook_install(tcpHook, 0).check("funchook_install") +} else { + tcpHook = funchook_create() ?: run { + println("Failed to create hook") + return + } + funchook_prepare(tcpHook, read_func_point, staticCFunction(::drillRead)).check("prepare read_func_point") + funchook_prepare(tcpHook, write_func_point, staticCFunction(::drillWrite)).check("prepare write_func_point") + funchook_prepare(tcpHook, send_func_point, staticCFunction(::drillSend)).check("prepare send_func_point") + funchook_prepare(tcpHook, recv_func_point, staticCFunction(::drillRecv)).check("prepare recv_func_point") + block() + funchook_install(tcpHook, 0).check("funchook_install") +} + + +@OptIn(ExperimentalForeignApi::class) +fun removeTcpHook() { + funchook_uninstall(tcpHook, 0).check("funchook_uninstall") +} + +//TODO EPMDJ-8696 Move back to common module + +@OptIn(ExperimentalForeignApi::class) +expect fun drillRead(fd: DRILL_SOCKET, buf: CPointer>?, size: size_t): ssize_t + +@OptIn(ExperimentalForeignApi::class) +expect fun drillWrite(fd: DRILL_SOCKET, buf: CPointer>?, size: size_t): ssize_t + +@OptIn(ExperimentalForeignApi::class) +expect fun drillSend(fd: DRILL_SOCKET, buf: CPointer>?, size: Int, flags: Int): Int + +@OptIn(ExperimentalForeignApi::class) +expect fun drillRecv(fd: DRILL_SOCKET, buf: CPointer>?, size: Int, flags: Int): Int + +@OptIn(ExperimentalForeignApi::class) +internal fun drillConnect(fd: DRILL_SOCKET, addr: CPointer?, socklen: drill_sock_len): Int { + initRuntimeIfNeeded() + val connectStatus = nativeConnect(fd, addr, socklen).convert() + if (0 == connectStatus) _connects.update { it + fd } + return connectStatus +} + +@OptIn(ExperimentalForeignApi::class) +fun drillClose(fd: DRILL_SOCKET): Int { + initRuntimeIfNeeded() + val result = nativeClose(fd) + if (result == 0) { + _accepts.update { it - fd } + _connects.update { it - fd } + com.epam.drill.hook.io.close(fd) + } + return result +} diff --git a/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/Hooks.kt b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/Hooks.kt new file mode 100644 index 00000000..1fcec46a --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/Hooks.kt @@ -0,0 +1,125 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.hook.io + +import com.epam.drill.hook.gen.DRILL_SOCKET +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.update +import kotlinx.cinterop.ByteVarOf +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.MemScope +import kotlinx.cinterop.memScoped +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.plus +import kotlin.native.concurrent.freeze + + +@SharedImmutable +private val _interceptors = atomic(persistentListOf()) + +val interceptors: List + get() = _interceptors.value + +fun addInterceptor(interceptor: Interceptor) { + _interceptors.update { it + interceptor } +} + +interface ReadInterceptor { + @OptIn(ExperimentalForeignApi::class) + fun MemScope.interceptRead(fd: DRILL_SOCKET, bytes: CPointer>, size: Int) +} + +interface WriteInterceptor { + @OptIn(ExperimentalForeignApi::class) + fun MemScope.interceptWrite(fd: DRILL_SOCKET, bytes: CPointer>, size: Int): TcpFinalData +} + +interface Interceptor : ReadInterceptor, WriteInterceptor { + @OptIn(ExperimentalForeignApi::class) + fun isSuitableByteStream(fd: DRILL_SOCKET, bytes: CPointer>): Boolean + @OptIn(ExperimentalForeignApi::class) + fun close(fd: DRILL_SOCKET) +} + + +@OptIn(ExperimentalForeignApi::class) +fun tryDetectProtocol(fd: DRILL_SOCKET, buf: CPointer>?, size: Int) { + buf?.let { byteBuf -> + interceptors.forEach { + it.let { + if (it.isSuitableByteStream(fd, byteBuf)) { + memScoped { + with(it) { + interceptRead(fd, buf, size) + } + } + } + + } + } + } +} + +@OptIn(ExperimentalForeignApi::class) +fun close(fd: DRILL_SOCKET) { + interceptors.forEach { + it.close(fd) + } +} + +@OptIn(ExperimentalForeignApi::class) +fun MemScope.processWriteEvent(fd: DRILL_SOCKET, buf: CPointer>?, size: Int): TcpFinalData { + return buf?.let { byteBuf -> + interceptors.forEach { + it.let { + if (it.isSuitableByteStream(fd, byteBuf)) + return with(it) { + interceptWrite(fd, buf, size) + } + else TcpFinalData(buf, size) + } + } + TcpFinalData(buf, size) + } ?: TcpFinalData(buf, size) + + +} + +@SharedImmutable +val CR_LF = "\r\n" + +@SharedImmutable +val CR_LF_BYTES = CR_LF.encodeToByteArray() + +@SharedImmutable +val HEADERS_DELIMITER = CR_LF_BYTES + CR_LF_BYTES + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val injectedHeaders = atomic({ emptyMap() }.freeze()).freeze() + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val readHeaders = atomic({ _: Map -> }.freeze()).freeze() + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val readCallback = atomic({ _: ByteArray -> }.freeze()).freeze() + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val writeCallback = atomic({ _: ByteArray -> }.freeze()).freeze() \ No newline at end of file diff --git a/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/TcpFinalData.kt b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/TcpFinalData.kt new file mode 100644 index 00000000..d94cc589 --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/nativeMain/kotlin/com/epam/drill/hook/io/TcpFinalData.kt @@ -0,0 +1,22 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.hook.io + +import kotlinx.cinterop.ByteVarOf +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.ExperimentalForeignApi + +data class TcpFinalData @OptIn(ExperimentalForeignApi::class) constructor(val buf: CPointer>?, val size: Int, val dif: Int = 0) \ No newline at end of file diff --git a/lib-jvm-shared/interceptor-hook/src/posixMain/kotlin/com/epam/drill/hook/io/Butyfier.kt b/lib-jvm-shared/interceptor-hook/src/posixMain/kotlin/com/epam/drill/hook/io/Butyfier.kt new file mode 100644 index 00000000..a12d5615 --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/posixMain/kotlin/com/epam/drill/hook/io/Butyfier.kt @@ -0,0 +1,39 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.hook.io + +import com.epam.drill.hook.gen.* +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +val nativeRead + get() = read_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeWrite + get() = write_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeSend + get() = send_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeRecv + get() = recv_func!! + +@OptIn(ExperimentalForeignApi::class) +val nativeAccept + get() = accept_func!! diff --git a/lib-jvm-shared/interceptor-hook/src/posixMain/kotlin/com/epam/drill/hook/io/Drillo.kt b/lib-jvm-shared/interceptor-hook/src/posixMain/kotlin/com/epam/drill/hook/io/Drillo.kt new file mode 100644 index 00000000..fedcad46 --- /dev/null +++ b/lib-jvm-shared/interceptor-hook/src/posixMain/kotlin/com/epam/drill/hook/io/Drillo.kt @@ -0,0 +1,121 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:Suppress("ObjectPropertyName") + +package com.epam.drill.hook.io + +import com.epam.drill.hook.gen.* +import kotlinx.atomicfu.* +import kotlinx.cinterop.* +import kotlinx.collections.immutable.* +import platform.posix.* + + +@OptIn(ExperimentalForeignApi::class) +actual val tcpInitializer = run { + val socketHook = funchook_create() + funchook_prepare(socketHook, close_func_point, staticCFunction(::drillClose)).check("prepare close_func_point") + funchook_prepare( + socketHook, + connect_func_point, + staticCFunction(::drillConnect) + ).check("prepare connect_func_point") + funchook_prepare(socketHook, accept_func_point, staticCFunction(::drillAccept)).check("prepare accept_func_point") + funchook_install(socketHook, 0).check("funchook_install") +} + + +@OptIn(ExperimentalForeignApi::class) +actual fun drillRead(fd: DRILL_SOCKET, buf: CPointer>?, size: size_t): ssize_t { + initRuntimeIfNeeded() + val read = nativeRead(fd.convert(), buf, size.convert()) + if (read > 0 && (accepts.contains(fd.convert()) || connects.contains(fd.convert()))) + tryDetectProtocol(fd.convert(), buf, read.convert()) + return read.convert() +} + +@OptIn(ExperimentalForeignApi::class) +actual fun drillWrite(fd: DRILL_SOCKET, buf: CPointer>?, size: size_t): ssize_t { + initRuntimeIfNeeded() + + return memScoped { + val (finalBuf, finalSize, ll) = + if (accepts.contains(fd.convert()) || connects.contains(fd.convert())) + processWriteEvent(fd.convert(), buf, size.convert()) + else TcpFinalData(buf, size.convert()) + (nativeWrite(fd.convert(), finalBuf, (finalSize).convert()) - ll).convert() + } +} + + +@OptIn(ExperimentalForeignApi::class) +actual fun drillSend(fd: DRILL_SOCKET, buf: CPointer>?, size: Int, flags: Int): Int { + initRuntimeIfNeeded() + return memScoped { + val (finalBuf, finalSize, ll) = processWriteEvent(fd, buf, size.convert()) + (nativeSend(fd.convert(), finalBuf, (finalSize).convert(), flags) - ll).convert() + } +} + +@OptIn(ExperimentalForeignApi::class) +actual fun drillRecv(fd: DRILL_SOCKET, buf: CPointer>?, size: Int, flags: Int): Int { + initRuntimeIfNeeded() + val read = nativeRecv(fd, buf, size.convert(), flags) + tryDetectProtocol(fd, buf, read.convert()) + return read.convert() +} + +@OptIn(ExperimentalForeignApi::class) +fun drillAccept( + fd: DRILL_SOCKET, + addr: CPointer?, + socklen: CPointer?, +): DRILL_SOCKET { + initRuntimeIfNeeded() + val socket = nativeAccept(fd, addr, socklen) + if (isValidSocket(socket) == 0) + _accepts.update { it + socket } + return socket +} + +@OptIn(ExperimentalForeignApi::class) +actual fun configureTcpHooks() = configureTcpHooksBuild { + println("Configuration for unix") + funchook_prepare(tcpHook, writev_func_point, staticCFunction(::writevDrill)) + funchook_prepare(tcpHook, readv_func_point, staticCFunction(::readvDrill)) +} + +@OptIn(ExperimentalForeignApi::class) +private fun readvDrill(fd: Int, iovec: CPointer?, size: Int): ssize_t { + val convert = readv_func!!(fd, iovec, size).convert() + println("readv intercepted do not implemented now. If you see this message, please put issue to https://github.com/Drill4J/Drill4J") + return convert +} + + +@OptIn(ExperimentalForeignApi::class) +private fun writevDrill(fd: Int, iovec: CPointer?, size: Int): ssize_t { + return memScoped { + //todo I think we(headers) should be in the first buffer + val iovecs = iovec!![0] + val iovLen = iovecs.iov_len + val base = iovecs.iov_base!!.reinterpret>() + val (finalBuf, finalSize, injectedSize) = processWriteEvent(fd.convert(), base, iovLen.convert()) + iovec[0].iov_base = finalBuf + iovec[0].iov_len = finalSize.convert() + (writev_func!!(fd, iovec, size) - injectedSize).convert() + } +} diff --git a/lib-jvm-shared/interceptor-http/LICENSE b/lib-jvm-shared/interceptor-http/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/interceptor-http/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/interceptor-http/README.md b/lib-jvm-shared/interceptor-http/README.md new file mode 100644 index 00000000..bde924a2 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/README.md @@ -0,0 +1,3 @@ +# Drill http protocols interceptor library + +Library for intercepting http protocols calls, based on drill-hook diff --git a/lib-jvm-shared/interceptor-http/build.gradle.kts b/lib-jvm-shared/interceptor-http/build.gradle.kts new file mode 100644 index 00000000..1492eac9 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/build.gradle.kts @@ -0,0 +1,137 @@ +import java.net.URI +import java.util.Properties +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.presetName +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} + +val logbackVersion: String by parent!!.extra +val nativeAgentLibName: String by parent!!.extra +val microutilsLoggingVersion: String by parent!!.extra + +repositories { + mavenCentral() +} + +kotlin { + val configureIntTestTarget: KotlinTarget.() -> Unit = { + compilations.create("intTest").associateWith(compilations["main"]) + (this as? KotlinNativeTarget)?.binaries?.sharedLib(nativeAgentLibName, setOf(DEBUG)) { + compilation = compilations["intTest"] + } + } + targets { + jvm(configure = configureIntTestTarget) + linuxX64(configure = configureIntTestTarget) + mingwX64(configure = configureIntTestTarget).apply { + binaries.all { + linkerOpts("-lpsapi", "-lwsock32", "-lws2_32", "-lmswsock") + } + } + } + @Suppress("UNUSED_VARIABLE") + sourceSets { + all { + languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi") + } + targets.withType().findByName(HostManager.host.presetName)?.compilations?.forEach { + it.defaultSourceSet.kotlin.srcDir("src/native${it.name.capitalize()}/kotlin") + it.defaultSourceSet.resources.srcDir("src/native${it.name.capitalize()}/resources") + } + val jvmIntTest by getting { + dependencies { + implementation(kotlin("test-junit")) + implementation(project(":common")) + implementation("org.apache.tomcat.embed:tomcat-embed-core:10.0.27") + implementation("org.eclipse.jetty:jetty-server:9.4.26.v20200117") + implementation("io.undertow:undertow-core:2.0.29.Final") + implementation("ch.qos.logback:logback-classic:$logbackVersion") + } + } + val configureNativeMainDependencies: KotlinSourceSet.() -> Unit = { + dependencies { + api("io.github.microutils:kotlin-logging:$microutilsLoggingVersion") + implementation(project(":common")) + implementation(project(":interceptor-hook")) + } + } + val configureNativeIntTestDependencies: KotlinSourceSet.() -> Unit = { + dependencies { + implementation(project(":jvmapi")) + } + } + val linuxX64Main by getting(configuration = configureNativeMainDependencies) + val mingwX64Main by getting(configuration = configureNativeMainDependencies) + val linuxX64IntTest by getting(configuration = configureNativeIntTestDependencies) + val mingwX64IntTest by getting(configuration = configureNativeIntTestDependencies) + } + tasks { + val filterOutCurrentPlatform: (KotlinNativeTarget) -> Boolean = { + it.targetName != HostManager.host.presetName + } + val copyNativeClassesTask: (KotlinCompilation<*>) -> Unit = { + val taskName = "copyNativeClasses${it.target.targetName.capitalize()}${it.compilationName.capitalize()}" + val copyNativeClasses: TaskProvider = register(taskName, Copy::class) { + group = "build" + from("src/native${it.compilationName.capitalize()}/kotlin") + into("src/${it.target.targetName}${it.compilationName.capitalize()}/kotlin/gen") + } + it.compileKotlinTask.dependsOn(copyNativeClasses.get()) + } + val cleanNativeClassesTask: (KotlinCompilation<*>) -> Unit = { + val taskName = "cleanNativeClasses${it.target.targetName.capitalize()}${it.compilationName.capitalize()}" + val cleanNativeClasses: TaskProvider = register(taskName, Delete::class) { + group = "build" + delete("src/${it.target.targetName}${it.compilationName.capitalize()}/kotlin/gen") + } + clean.get().dependsOn(cleanNativeClasses.get()) + } + targets.withType().filter(filterOutCurrentPlatform) + .flatMap(KotlinNativeTarget::compilations) + .onEach(copyNativeClassesTask) + .onEach(cleanNativeClassesTask) + register("integrationTest", Test::class) { + val intTestAgentLib = targets.withType()[HostManager.host.presetName] + .binaries.getSharedLib(nativeAgentLibName, NativeBuildType.DEBUG) + val intTestCompilation = targets.withType()["jvm"].compilations["intTest"] + description = "Runs the integration tests using simple native agent" + group = "verification" + testClassesDirs = intTestCompilation.output.classesDirs + classpath = intTestCompilation.runtimeDependencyFiles + intTestCompilation.output.allOutputs + jvmArgs = listOf("-agentpath:${intTestAgentLib.outputFile.path}") + dependsOn(intTestAgentLib.linkTask) + } + } +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } +} diff --git a/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/AbstractHttpInterceptorTest.kt b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/AbstractHttpInterceptorTest.kt new file mode 100644 index 00000000..1d09f076 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/AbstractHttpInterceptorTest.kt @@ -0,0 +1,78 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import kotlin.test.Test +import kotlin.test.assertEquals +import java.net.HttpURLConnection +import java.net.URL + +@Suppress("FunctionName") +abstract class AbstractHttpInterceptorTest { + + @Test + fun `test with empty headers request`() = withHttpServer { + val response = callHttpEndpoint(it) + val responseHeaders = response.first + val responseBody = response.second + assertEquals("test-agent", responseHeaders["drill-agent-id"]) + assertEquals("test-admin:8080", responseHeaders["drill-admin-url"]) + assertEquals("test-request", responseBody) + } + + @Test + fun `test with session headers request`() = withHttpServer { + val requestHeaders = mapOf( + "drill-session-id" to "123", + "drill-session-data" to "data-123" + ) + val response = callHttpEndpoint(it, requestHeaders) + val responseHeaders = response.first + val responseBody = response.second + assertEquals("test-agent", responseHeaders["drill-agent-id"]) + assertEquals("test-admin:8080", responseHeaders["drill-admin-url"]) + assertEquals("123", responseHeaders["drill-session-id"]) + assertEquals("data-123", responseHeaders["drill-session-data"]) + assertEquals("test-request", responseBody) + } + + protected abstract fun withHttpServer(block: (String) -> Unit) + + private fun callHttpEndpoint( + endpoint: String, + headers: Map = emptyMap(), + request: String = "test-request" + ): Pair, String> { + lateinit var connection: HttpURLConnection + try { + connection = URL(endpoint).openConnection() as HttpURLConnection + connection.requestMethod = "POST" + headers.entries.forEach { + connection.setRequestProperty(it.key, it.value) + } + connection.doOutput = true + connection.outputStream.write(request.encodeToByteArray()) + connection.outputStream.close() + val responseHeaders = connection.headerFields.mapValues { it.value.joinToString(",") } + val responseBody = connection.inputStream.readBytes().decodeToString() + connection.inputStream.close() + return responseHeaders to responseBody + } finally { + connection.disconnect() + } + } + +} diff --git a/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/JettyServerHttpInterceptorTest.kt b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/JettyServerHttpInterceptorTest.kt new file mode 100644 index 00000000..c482e357 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/JettyServerHttpInterceptorTest.kt @@ -0,0 +1,55 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse +import org.eclipse.jetty.server.Request +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.server.ServerConnector +import org.eclipse.jetty.server.handler.AbstractHandler + +class JettyServerHttpInterceptorTest : AbstractHttpInterceptorTest() { + + override fun withHttpServer(block: (String) -> Unit) = Server().run { + try { + val connector = ServerConnector(this) + this.connectors = arrayOf(connector) + this.handler = TestRequestHandler + this.start() + block("http://localhost:${connector.localPort}") + } finally { + this.stop() + } + } + + @Suppress("VulnerableCodeUsages") + private object TestRequestHandler : AbstractHandler() { + override fun handle( + target: String, + baseRequest: Request, + request: HttpServletRequest, + response: HttpServletResponse + ) { + val requestBody = request.inputStream.readBytes() + response.status = 200 + response.setContentLength(requestBody.size) + response.outputStream.write(requestBody) + response.outputStream.close() + } + } + +} diff --git a/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/SunHttpServerHttpInterceptorTest.kt b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/SunHttpServerHttpInterceptorTest.kt new file mode 100644 index 00000000..8e24d003 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/SunHttpServerHttpInterceptorTest.kt @@ -0,0 +1,42 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import java.net.InetSocketAddress +import com.sun.net.httpserver.HttpExchange +import com.sun.net.httpserver.HttpServer + +class SunHttpServerHttpInterceptorTest : AbstractHttpInterceptorTest() { + + override fun withHttpServer(block: (String) -> Unit) = HttpServer.create().run { + try { + this.bind(InetSocketAddress(0), 0) + this.createContext("/", ::testRequestHandler) + this.start() + block("http://localhost:${this.address.port}") + } finally { + this.stop(2) + } + } + + private fun testRequestHandler(exchange: HttpExchange) { + val requestBody = exchange.requestBody.readBytes() + exchange.sendResponseHeaders(200, requestBody.size.toLong()) + exchange.responseBody.write(requestBody) + exchange.responseBody.close() + } + +} diff --git a/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/TomcatHttpInterceptorTest.kt b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/TomcatHttpInterceptorTest.kt new file mode 100644 index 00000000..d898bf64 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/TomcatHttpInterceptorTest.kt @@ -0,0 +1,51 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import java.util.logging.LogManager +import org.apache.catalina.startup.Tomcat +import jakarta.servlet.http.HttpServlet +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse + +class TomcatHttpInterceptorTest : AbstractHttpInterceptorTest() { + + override fun withHttpServer(block: (String) -> Unit) = Tomcat().run { + try { + LogManager.getLogManager().readConfiguration(ClassLoader.getSystemResourceAsStream("logging.properties")) + this.setBaseDir("./build") + this.setPort(0) + val context = this.addContext("", null) + this.addServlet(context.path, TestRequestServlet::class.simpleName, TestRequestServlet) + context.addServletMappingDecoded("/", TestRequestServlet::class.simpleName) + this.start() + block("http://localhost:${connector.localPort}") + } finally { + this.stop() + } + } + + private object TestRequestServlet : HttpServlet() { + override fun doPost(request: HttpServletRequest, response: HttpServletResponse) { + val requestBody = request.inputStream.readBytes() + response.status = 200 + response.setContentLength(requestBody.size) + response.outputStream.write(requestBody) + response.outputStream.close() + } + } + +} diff --git a/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/UndertowHttpInterceptorTest.kt b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/UndertowHttpInterceptorTest.kt new file mode 100644 index 00000000..25e509d3 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/jvmIntTest/kotlin/com/epam/drill/agent/interceptor/UndertowHttpInterceptorTest.kt @@ -0,0 +1,45 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import java.net.InetSocketAddress +import io.undertow.Undertow +import io.undertow.server.HttpServerExchange + +class UndertowHttpInterceptorTest : AbstractHttpInterceptorTest() { + + override fun withHttpServer(block: (String) -> Unit) = Undertow.builder() + .addHttpListener(0, "localhost") + .setHandler(::testRequestHandler) + .build().run { + try { + this.start() + block("http://localhost:${(this.listenerInfo[0].address as InetSocketAddress).port}") + } finally { + this.stop() + } + } + + private fun testRequestHandler(exchange: HttpServerExchange) { + lateinit var requestBody: ByteArray + exchange.requestReceiver.receiveFullBytes { _, body -> requestBody = body} + exchange.statusCode = 200 + exchange.responseContentLength = requestBody.size.toLong() + exchange.responseSender.send(requestBody.decodeToString()) + exchange.responseSender.close() + } + +} diff --git a/lib-jvm-shared/interceptor-http/src/jvmIntTest/resources/logback.xml b/lib-jvm-shared/interceptor-http/src/jvmIntTest/resources/logback.xml new file mode 100644 index 00000000..d330efa9 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/jvmIntTest/resources/logback.xml @@ -0,0 +1,10 @@ + + + + %date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%logger] %message%n%throwable + + + + + + diff --git a/lib-jvm-shared/interceptor-http/src/jvmIntTest/resources/logging.properties b/lib-jvm-shared/interceptor-http/src/jvmIntTest/resources/logging.properties new file mode 100644 index 00000000..e3450d7f --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/jvmIntTest/resources/logging.properties @@ -0,0 +1,6 @@ +handlers=java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level=INFO +java.util.logging.ConsoleHandler.formatter=org.apache.juli.OneLineFormatter + +org.apache.juli.OneLineFormatter.timeFormat=yyyy-MM-dd HH:mm:ss.SSS diff --git a/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/JvmtiLoad.kt b/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/JvmtiLoad.kt new file mode 100644 index 00000000..44c330a3 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/JvmtiLoad.kt @@ -0,0 +1,72 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:Suppress("unused") +package com.epam.drill.agent.interceptor + +import kotlin.native.concurrent.freeze +import kotlinx.cinterop.* +import com.epam.drill.agent.jvmapi.checkEx +import com.epam.drill.agent.jvmapi.env +import com.epam.drill.agent.jvmapi.gen.* +import com.epam.drill.agent.jvmapi.jvmti +import com.epam.drill.agent.jvmapi.vmGlobal +import com.epam.drill.logging.LoggingConfiguration +import kotlinx.cinterop.ExperimentalForeignApi +import kotlin.experimental.ExperimentalNativeApi + +@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) +@Suppress("unused_parameter") +@CName("Agent_OnLoad") +fun agentOnLoad(vmPointer: CPointer, options: String, reservedPtr: Long): Int = memScoped { + vmGlobal.value = vmPointer.freeze() + val vm = vmPointer.pointed + val jvmtiEnvPtr = alloc>() + vm.value!!.pointed.GetEnv!!(vm.ptr, jvmtiEnvPtr.ptr.reinterpret(), JVMTI_VERSION.convert()) + jvmti.value = jvmtiEnvPtr.value.freeze() + + LoggingConfiguration.readDefaultConfiguration() + LoggingConfiguration.setLoggingLevels("TRACE") + LoggingConfiguration.setLogMessageLimit(524288) + + val alloc = alloc() + alloc.VMInit = staticCFunction(::vmInitEvent) + SetEventCallbacks(alloc.ptr, sizeOf().toInt()) + SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, null) + + JNI_OK +} + +@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) +@Suppress("unused_parameter") +fun vmInitEvent(env: CPointer?, jniEnv: CPointer?, thread: jthread?) { + HttpInterceptorConfigurer(TestHeadersRetriever, TestRequestHolder) +} + +@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) +@CName("checkEx") +fun checkEx(errCode: jvmtiError, funName: String) = checkEx(errCode, funName) + +@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) +@CName("currentEnvs") +fun currentEnvs() = env + +@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) +@CName("jvmtii") +fun jvmtii() = jvmti.value + +@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) +@CName("getJvm") +fun getJvm() = vmGlobal.value diff --git a/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/TestHeadersRetriever.kt b/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/TestHeadersRetriever.kt new file mode 100644 index 00000000..c1147dcc --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/TestHeadersRetriever.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import com.epam.drill.agent.common.request.HeadersRetriever + +object TestHeadersRetriever : HeadersRetriever { + + override fun adminAddressHeader() = "drill-admin-url" + + override fun adminAddressValue() = "test-admin:8080" + + override fun sessionHeader() = "drill-session-id" + + override fun agentIdHeader() = "drill-agent-id" + + override fun agentIdHeaderValue() = "test-agent" + +} diff --git a/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/TestRequestHolder.kt b/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/TestRequestHolder.kt new file mode 100644 index 00000000..1fd21abe --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/nativeIntTest/kotlin/com/epam/drill/agent/interceptor/TestRequestHolder.kt @@ -0,0 +1,32 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.RequestHolder + +@ThreadLocal +private var localRequest: DrillRequest? = null + +object TestRequestHolder : RequestHolder { + + override fun remove() { localRequest = null } + + override fun retrieve(): DrillRequest? = localRequest + + override fun store(drillRequest: DrillRequest) { localRequest = drillRequest } + +} diff --git a/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptor.kt b/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptor.kt new file mode 100644 index 00000000..f0a94941 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptor.kt @@ -0,0 +1,181 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.set +import kotlinx.cinterop.ByteVarOf +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.MemScope +import kotlinx.cinterop.readBytes +import kotlinx.cinterop.toCValues +import mu.KotlinLogging +import com.epam.drill.hook.gen.DRILL_SOCKET +import com.epam.drill.hook.io.CR_LF +import com.epam.drill.hook.io.HEADERS_DELIMITER +import com.epam.drill.hook.io.Interceptor +import com.epam.drill.hook.io.TcpFinalData +import com.epam.drill.hook.io.injectedHeaders +import com.epam.drill.hook.io.readCallback +import com.epam.drill.hook.io.readHeaders +import com.epam.drill.hook.io.writeCallback +import kotlinx.cinterop.ExperimentalForeignApi + +private const val HTTP_DETECTOR_BYTES_COUNT = 8 +private const val HTTP_RESPONSE_MARKER = "HTTP" +private const val HTTP_HEADER_DRILL_INTERNAL = "drill-internal: true" + +@ThreadLocal +@OptIn(ExperimentalForeignApi::class) +private val localRequests = mutableMapOf() + +@ThreadLocal +@OptIn(ExperimentalForeignApi::class) +private val localResponses = mutableMapOf() + +class HttpInterceptor : Interceptor { + + private val httpVerbs = setOf( + "OPTIONS", "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", "CONNECT", "PRI", HTTP_RESPONSE_MARKER + ) + private val drillInternalHeader = HTTP_HEADER_DRILL_INTERNAL.encodeToByteArray() + private val logger = KotlinLogging.logger("com.epam.drill.agent.interceptor.HttpInterceptor") + + @OptIn(ExperimentalForeignApi::class) + override fun MemScope.interceptRead(fd: DRILL_SOCKET, bytes: CPointer>, size: Int) = try { + val prefix = bytes.readBytes(HTTP_DETECTOR_BYTES_COUNT).decodeToString() + val readBytes by lazy { bytes.readBytes(size) } + when { + httpVerbs.any(prefix::startsWith) -> { + when { + containsDrillInternalHeader(readBytes) -> Unit + containsFullHeadersPart(readBytes) -> readHeaders(fd, readBytes) + else -> localRequests[fd] = readBytes + } + } + localRequests[fd] != null -> { + val total = localRequests[fd]!! + readBytes + when { + containsDrillInternalHeader(total) -> Unit.also { localRequests.remove(fd) } + containsFullHeadersPart(readBytes) -> readHeaders(fd, total).also { localRequests.remove(fd) } + else -> localRequests[fd] = total + } + } + else -> Unit + } + } catch (e: Exception) { + logger.error(e) { "interceptRead: $e" } + } + + @OptIn(ExperimentalForeignApi::class) + override fun MemScope.interceptWrite(fd: DRILL_SOCKET, bytes: CPointer>, size: Int) = try { + val prefix = bytes.readBytes(HTTP_DETECTOR_BYTES_COUNT).decodeToString() + val readBytes by lazy { bytes.readBytes(size) } + val headersDelimiterIndex by lazy { readBytes.indexOf(HEADERS_DELIMITER) } + val writeHeaders by lazy { injectedHeaders.value() } + val toTcpFinalData: (ByteArray) -> TcpFinalData = { + TcpFinalData(it.toCValues().getPointer(this), it.size, it.size - size) + } + when { + prefix.startsWith("PRI") -> { + TcpFinalData(bytes, size) + } + httpVerbs.any(prefix::startsWith) -> { + when { + containsDrillInternalHeader(readBytes) -> TcpFinalData(bytes, size) + containsHeaders(readBytes, writeHeaders) -> TcpFinalData(bytes, size) + headersDelimiterIndex >= 0 -> writeHeaders(fd, readBytes, headersDelimiterIndex, writeHeaders) + .let(toTcpFinalData) + else -> TcpFinalData(bytes, size).also { localResponses[fd] = readBytes } + } + } + localResponses[fd] != null -> { + val total = localResponses[fd]!! + readBytes + when { + containsDrillInternalHeader(total) -> TcpFinalData(bytes, size).also { localResponses.remove(fd) } + containsHeaders(total, writeHeaders) -> TcpFinalData(bytes, size).also { localResponses.remove(fd) } + headersDelimiterIndex >= 0 -> writeHeaders(fd, readBytes, headersDelimiterIndex, writeHeaders) + .let(toTcpFinalData).also { localResponses.remove(fd) } + else -> TcpFinalData(bytes, size).also { localResponses[fd] = total } + } + } + else -> TcpFinalData(bytes, size) + } + } catch (e: Exception) { + logger.error(e) { "interceptWrite: $e" } + TcpFinalData(bytes, size) + } + + @OptIn(ExperimentalForeignApi::class) + override fun close(fd: DRILL_SOCKET) { + localRequests.remove(fd) + localResponses.remove(fd) + } + + @OptIn(ExperimentalForeignApi::class) + override fun isSuitableByteStream(fd: DRILL_SOCKET, bytes: CPointer>) = + bytes.readBytes(HTTP_DETECTOR_BYTES_COUNT).decodeToString().let { httpVerbs.any(it::startsWith) } + || localRequests.containsKey(fd) + || localResponses.containsKey(fd) + + @OptIn(ExperimentalForeignApi::class) + private fun readHeaders(fd: DRILL_SOCKET, bytes: ByteArray) { + val decoded = bytes.decodeToString() + logger.trace { "readHeaders: Reading HTTP request from fd=$fd:\n${decoded.prependIndent("\t")}" } + readHeaders.value( + decoded.subSequence(decoded.indexOf('\r'), decoded.indexOf(CR_LF + CR_LF)) + .split(CR_LF) + .filter(String::isNotBlank) + .map { it.split(":", limit = 2).map(String::trim) } + .onEach { logger.trace { "readHeaders: Read HTTP header from fd=$fd: ${it[0]}=${it[1]}" } } + .associate { it[0].encodeToByteArray() to it[1].encodeToByteArray() } + ) + readCallback.value(bytes) + } + + @OptIn(ExperimentalForeignApi::class) + private fun writeHeaders(fd: DRILL_SOCKET, bytes: ByteArray, index: Int, headers: Map): ByteArray { + headers.entries.forEach { logger.trace { "writeHeaders: Writing HTTP header to fd=$fd: ${it.key}=${it.value}" } } + val responseHead = bytes.copyOfRange(0, index) + val injectedHeaders = headersToBytes(headers) + val responseTail = bytes.copyOfRange(index, bytes.size) + val modified = responseHead + injectedHeaders + responseTail + logger.trace { "writeHeaders: Written HTTP headers to fd=$fd:\n${modified.decodeToString().prependIndent("\t")}" } + writeCallback.value(modified) + return modified + } + + private fun containsDrillInternalHeader(bytes: ByteArray) = bytes.indexOf(drillInternalHeader) != -1 + + private fun containsFullHeadersPart(bytes: ByteArray) = bytes.indexOf(HEADERS_DELIMITER) != -1 + + private fun containsHeaders(bytes: ByteArray, headers: Map) = + headers.isNotEmpty() && bytes.indexOf(headers.keys.first().encodeToByteArray()) != -1 + + private fun headersToBytes(headers: Map) = headers.map { (k, v) -> "$k: $v" } + .joinToString(CR_LF, CR_LF) + .encodeToByteArray() + + private fun ByteArray.indexOf(bytes: ByteArray): Int { + for (thisIndex in IntRange(0, lastIndex - bytes.lastIndex)) { + val regionMatches = bytes.foldIndexed(true) { index, acc, byte -> acc && this[thisIndex + index] == byte } + if (regionMatches) return thisIndex + } + return -1 + } + +} diff --git a/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptorCallbacks.kt b/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptorCallbacks.kt new file mode 100644 index 00000000..c1847215 --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptorCallbacks.kt @@ -0,0 +1,47 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import com.epam.drill.agent.common.request.DrillRequest +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.common.request.RequestHolder + +class HttpInterceptorCallbacks( + private val headersRetriever: HeadersRetriever, + private val requestHolder: RequestHolder +) { + + private val agentIdPair = headersRetriever.agentIdHeader() to headersRetriever.agentIdHeaderValue() + private val adminAddressPair = headersRetriever.adminAddressHeader() to headersRetriever.adminAddressValue() + + fun readHeaders(headers: Map) { + val decoded = headers.entries.associate { (k, v) -> k.decodeToString() to v.decodeToString() } + decoded[headersRetriever.sessionHeader()]?.also { requestHolder.store(DrillRequest(it, decoded)) } + } + + @Suppress("unused_parameter") + fun writeCallback(bytes: ByteArray) { + requestHolder.remove() + } + + fun injectedHeaders(): Map { + val existing = requestHolder.retrieve()?.headers + ?.filterKeys { it.startsWith("drill-") } + ?: emptyMap() + return existing + mapOf(agentIdPair, adminAddressPair) + } + +} diff --git a/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptorConfigurer.kt b/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptorConfigurer.kt new file mode 100644 index 00000000..c391acfb --- /dev/null +++ b/lib-jvm-shared/interceptor-http/src/nativeMain/kotlin/com/epam/drill/agent/interceptor/HttpInterceptorConfigurer.kt @@ -0,0 +1,52 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.interceptor + +import kotlin.native.concurrent.freeze +import mu.KotlinLogging +import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.common.request.RequestHolder +import com.epam.drill.hook.io.addInterceptor +import com.epam.drill.hook.io.configureTcpHooks +import com.epam.drill.hook.io.injectedHeaders +import com.epam.drill.hook.io.readHeaders +import com.epam.drill.hook.io.writeCallback + +@Suppress("unused") +object HttpInterceptorConfigurer { + + private val logger = KotlinLogging.logger("com.epam.drill.agent.interceptor.HttpInterceptorConfigurer") + + const val enabled = true + + operator fun invoke(headersRetriever: HeadersRetriever, requestHolder: RequestHolder) { + logger.debug { "invoke: Creating HTTP interceptor object..." } + val interceptor = HttpInterceptor() + + logger.debug { "invoke: Creating HTTP interceptor callbacks..." } + val callbacks = HttpInterceptorCallbacks(headersRetriever, requestHolder) + + logger.debug { "invoke: Configuring TCP hook..." } + configureTcpHooks() + addInterceptor(interceptor.freeze()) + + logger.debug { "invoke: Configuring TCP hook callbacks..." } + injectedHeaders.value = callbacks::injectedHeaders.freeze() + readHeaders.value = callbacks::readHeaders.freeze() + writeCallback.value = callbacks::writeCallback.freeze() + } + +} diff --git a/lib-jvm-shared/interceptor-stub/LICENSE b/lib-jvm-shared/interceptor-stub/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/interceptor-stub/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/interceptor-stub/README.md b/lib-jvm-shared/interceptor-stub/README.md new file mode 100644 index 00000000..bde924a2 --- /dev/null +++ b/lib-jvm-shared/interceptor-stub/README.md @@ -0,0 +1,3 @@ +# Drill http protocols interceptor library + +Library for intercepting http protocols calls, based on drill-hook diff --git a/lib-jvm-shared/interceptor-stub/build.gradle.kts b/lib-jvm-shared/interceptor-stub/build.gradle.kts new file mode 100644 index 00000000..b0d98a01 --- /dev/null +++ b/lib-jvm-shared/interceptor-stub/build.gradle.kts @@ -0,0 +1,76 @@ +import java.net.URI +import java.util.Properties +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} +val macosLd64: String by parent!!.extra + +repositories { + mavenCentral() +} + +kotlin { + linuxX64() + mingwX64() + macosX64().apply { + if(macosLd64.toBoolean()){ + binaries.all { + linkerOpts("-ld64") + } + } + } + macosArm64().apply { + if (macosLd64.toBoolean()) { + binaries.all { + linkerOpts("-ld64") + } + } + } + @Suppress("UNUSED_VARIABLE") + sourceSets { + val commonMain by getting + val nativeMain by creating { + dependsOn(commonMain) + dependencies { + implementation(project(":common")) + } + } + val linuxX64Main by getting { + dependsOn(nativeMain) + } + val mingwX64Main by getting{ + dependsOn(nativeMain) + } + val macosX64Main by getting{ + dependsOn(nativeMain) + } + val macosArm64Main by getting{ + dependsOn(nativeMain) + } + } +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } +} diff --git a/lib-jvm-shared/jvmapi/LICENSE b/lib-jvm-shared/jvmapi/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/jvmapi/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/jvmapi/README.md b/lib-jvm-shared/jvmapi/README.md new file mode 100644 index 00000000..8f554c25 --- /dev/null +++ b/lib-jvm-shared/jvmapi/README.md @@ -0,0 +1,6 @@ +# Drill JVM API + +This repo contains the [JVM API](https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html) used by Drill4J core. + +The main goal of this repository is to generate functions described in jvmti.def file using .h files for each platform. +These functions are required to communicate with the Java Virtual Machine. diff --git a/lib-jvm-shared/jvmapi/build.gradle.kts b/lib-jvm-shared/jvmapi/build.gradle.kts new file mode 100644 index 00000000..d1d7440f --- /dev/null +++ b/lib-jvm-shared/jvmapi/build.gradle.kts @@ -0,0 +1,76 @@ +import java.net.URI +import java.util.Properties +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import com.hierynomus.gradle.license.tasks.LicenseCheck +import com.hierynomus.gradle.license.tasks.LicenseFormat + +plugins { + kotlin("multiplatform") + id("com.github.hierynomus.license") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} + +repositories { + mavenCentral() +} + +val microutilsLoggingVersion: String by parent!!.extra + +kotlin { + val configureCInterop: KotlinNativeTarget.() -> Unit = { + compilations["main"].cinterops.create("jvmti") { + includeDirs("src/nativeInterop/cinterop/$targetName") + } + } + jvm() + linuxX64(configure = configureCInterop) + mingwX64(configure = configureCInterop) + macosX64(configure = configureCInterop) + macosArm64(configure = configureCInterop) + @Suppress("UNUSED_VARIABLE") + sourceSets { + all { + languageSettings.optIn("kotlin.ExperimentalUnsignedTypes") + } + val commonMain by getting { + dependencies { + api("io.github.microutils:kotlin-logging:$microutilsLoggingVersion") + } + } + val nativeMain by creating { + dependsOn(commonMain) + } + val linuxX64Main by getting { + dependsOn(nativeMain) + } + val mingwX64Main by getting { + dependsOn(nativeMain) + } + val macosX64Main by getting { + dependsOn(nativeMain) + } + val macosArm64Main by getting { + dependsOn(nativeMain) + } + } +} + +@Suppress("UNUSED_VARIABLE") +license { + headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") + val licenseFormatSources by tasks.registering(LicenseFormat::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } + val licenseCheckSources by tasks.registering(LicenseCheck::class) { + source = fileTree("$projectDir/src").also { + include("**/*.kt", "**/*.java", "**/*.groovy") + } + } +} diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/jvmti.def b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/jvmti.def new file mode 100644 index 00000000..ef27626c --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/jvmti.def @@ -0,0 +1,1928 @@ +package = com.epam.drill.agent.jvmapi.gen +headers = jvmti.h stdbool.h +excludedFunctions = JNI_OnLoad Agent_OnLoad Agent_OnAttach Agent_OnUnload + +--- + +//////////////////////////////////////JVMTI FUNCTIONS/////////////////////////////////////// +extern jvmtiError checkEx(jvmtiError errCode, char* fname); +extern jvmtiEnv* jvmtii(); + +jvmtiCapabilities GetPotentialCapabilities() { + jvmtiCapabilities capabilities; + (void)memset(&capabilities, 0, sizeof(capabilities)); + (*jvmtii())->GetPotentialCapabilities(jvmtii(), &capabilities); + return capabilities; +} +jvmtiError Allocate(jlong size, unsigned char** mem_ptr) { + return checkEx((*jvmtii())->Allocate(jvmtii(), size, mem_ptr), "Allocate"); +} +jvmtiError GetClassSignature(jclass klass, char** signature_ptr, char** generic_ptr) { + return checkEx((*jvmtii())->GetClassSignature(jvmtii(), klass, signature_ptr, generic_ptr), "GetClassSignature"); +} +jvmtiError Deallocate(unsigned char* mem) { + return checkEx((*jvmtii())->Deallocate(jvmtii(), mem), "Deallocate"); +} +jvmtiError GetThreadState(jthread thread, jint* thread_state_ptr) { + return checkEx((*jvmtii())->GetThreadState(jvmtii(), thread, thread_state_ptr), "GetThreadState"); +} +jvmtiError GetCurrentThread(jthread* thread_ptr) { + return checkEx((*jvmtii())->GetCurrentThread(jvmtii(), thread_ptr), "GetCurrentThread"); +} +jvmtiError GetAllThreads(jint* threads_count_ptr, jthread** threads_ptr) { + return checkEx((*jvmtii())->GetAllThreads(jvmtii(), threads_count_ptr, threads_ptr), "GetAllThreads"); +} +jvmtiError SuspendThread(jthread thread) { + return checkEx((*jvmtii())->SuspendThread(jvmtii(), thread), "SuspendThread"); +} +jvmtiError SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) { + return checkEx((*jvmtii())->SuspendThreadList(jvmtii(), request_count, request_list, results), "SuspendThreadList"); +} +jvmtiError ResumeThread(jthread thread) { + return checkEx((*jvmtii())->ResumeThread(jvmtii(), thread), "ResumeThread"); +} +jvmtiError ResumeThreadList(jint request_count, const jthread* request_list, jvmtiError* results) { + return checkEx((*jvmtii())->ResumeThreadList(jvmtii(), request_count, request_list, results), "ResumeThreadList"); +} +jvmtiError StopThread(jthread thread, jobject exception) { + return checkEx((*jvmtii())->StopThread(jvmtii(), thread, exception), "StopThread"); +} +jvmtiError InterruptThread(jthread thread) { + return checkEx((*jvmtii())->InterruptThread(jvmtii(), thread), "InterruptThread"); +} +jvmtiError GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) { + return checkEx((*jvmtii())->GetThreadInfo(jvmtii(), thread, info_ptr), "GetThreadInfo"); +} +jint HJNI_CreateJavaVM(JavaVM** pvm, void** penv, void* args) { + return JNI_CreateJavaVM(pvm, penv, args); +} +jvmtiError GetOwnedMonitorInfo(jthread thread, jint* owned_monitor_count_ptr, jobject** owned_monitors_ptr) { + return checkEx((*jvmtii())->GetOwnedMonitorInfo(jvmtii(), thread, owned_monitor_count_ptr, owned_monitors_ptr), "GetOwnedMonitorInfo"); +} +jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread, jint* monitor_info_count_ptr, jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + return checkEx((*jvmtii())->GetOwnedMonitorStackDepthInfo(jvmtii(), thread, monitor_info_count_ptr, monitor_info_ptr), "GetOwnedMonitorStackDepthInfo"); +} +jvmtiError GetCurrentContendedMonitor(jthread thread, jobject* monitor_ptr) { + return checkEx((*jvmtii())->GetCurrentContendedMonitor(jvmtii(), thread, monitor_ptr), "GetCurrentContendedMonitor"); +} +jvmtiError RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* arg, jint priority) { + return checkEx((*jvmtii())->RunAgentThread(jvmtii(), thread, proc, arg, priority), "RunAgentThread"); +} +jvmtiError SetThreadLocalStorage(jthread thread, const void* data) { + return checkEx((*jvmtii())->SetThreadLocalStorage(jvmtii(), thread, data), "SetThreadLocalStorage"); +} +jvmtiError GetThreadLocalStorage(jthread thread, void** data_ptr) { + return checkEx((*jvmtii())->GetThreadLocalStorage(jvmtii(), thread, data_ptr), "GetThreadLocalStorage"); +} +jvmtiError GetTopThreadGroups(jint* group_count_ptr, jthreadGroup** groups_ptr) { + return checkEx((*jvmtii())->GetTopThreadGroups(jvmtii(), group_count_ptr, groups_ptr), "GetTopThreadGroups"); +} +jvmtiError GetThreadGroupInfo(jthreadGroup group, jvmtiThreadGroupInfo* info_ptr) { + return checkEx((*jvmtii())->GetThreadGroupInfo(jvmtii(), group, info_ptr), "GetThreadGroupInfo"); +} +jvmtiError GetThreadGroupChildren(jthreadGroup group, jint* thread_count_ptr, jthread** threads_ptr, jint* group_count_ptr, jthreadGroup** groups_ptr) { + return checkEx((*jvmtii())->GetThreadGroupChildren(jvmtii(), group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr), "GetThreadGroupChildren"); +} +jvmtiError GetStackTrace(jthread thread, jint start_depth, jint max_frame_count, jvmtiFrameInfo* frame_buffer, jint* count_ptr) { + return checkEx((*jvmtii())->GetStackTrace(jvmtii(), thread, start_depth, max_frame_count, frame_buffer, count_ptr), "GetStackTrace"); +} +jvmtiError GetAllStackTraces(jint max_frame_count, jvmtiStackInfo** stack_info_ptr, jint* thread_count_ptr) { + return checkEx((*jvmtii())->GetAllStackTraces(jvmtii(), max_frame_count, stack_info_ptr, thread_count_ptr), "GetAllStackTraces"); +} +jvmtiError GetThreadListStackTraces(jint thread_count, const jthread* thread_list, jint max_frame_count, jvmtiStackInfo** stack_info_ptr) { + return checkEx((*jvmtii())->GetThreadListStackTraces(jvmtii(), thread_count, thread_list, max_frame_count, stack_info_ptr), "GetThreadListStackTraces"); +} +jvmtiError GetFrameCount(jthread thread, jint* count_ptr) { + return checkEx((*jvmtii())->GetFrameCount(jvmtii(), thread, count_ptr), "GetFrameCount"); +} +jvmtiError PopFrame(jthread thread) { + return checkEx((*jvmtii())->PopFrame(jvmtii(), thread), "PopFrame"); +} +jvmtiError GetFrameLocation(jthread thread, jint depth, jmethodID* method_ptr, jlocation* location_ptr) { + return checkEx((*jvmtii())->GetFrameLocation(jvmtii(), thread, depth, method_ptr, location_ptr), "GetFrameLocation"); +} +jvmtiError NotifyFramePop(jthread thread, jint depth) { + return checkEx((*jvmtii())->NotifyFramePop(jvmtii(), thread, depth), "NotifyFramePop"); +} +jvmtiError ForceEarlyReturnObject(jthread thread, jobject value) { + return checkEx((*jvmtii())->ForceEarlyReturnObject(jvmtii(), thread, value), "ForceEarlyReturnObject"); +} +jvmtiError ForceEarlyReturnInt(jthread thread, jint value) { + return checkEx((*jvmtii())->ForceEarlyReturnInt(jvmtii(), thread, value), "ForceEarlyReturnInt"); +} +jvmtiError ForceEarlyReturnLong(jthread thread, jlong value) { + return checkEx((*jvmtii())->ForceEarlyReturnLong(jvmtii(), thread, value), "ForceEarlyReturnLong"); +} +jvmtiError ForceEarlyReturnFloat(jthread thread, jfloat value) { + return checkEx((*jvmtii())->ForceEarlyReturnFloat(jvmtii(), thread, value), "ForceEarlyReturnFloat"); +} +jvmtiError ForceEarlyReturnDouble(jthread thread, jdouble value) { + return checkEx((*jvmtii())->ForceEarlyReturnDouble(jvmtii(), thread, value), "ForceEarlyReturnDouble"); +} +jvmtiError ForceEarlyReturnVoid(jthread thread) { + return checkEx((*jvmtii())->ForceEarlyReturnVoid(jvmtii(), thread), "ForceEarlyReturnVoid"); +} +jvmtiError FollowReferences(jint heap_filter, jclass klass, jobject initial_object, const jvmtiHeapCallbacks* callbacks, const void* user_data) { + return checkEx((*jvmtii())->FollowReferences(jvmtii(), heap_filter, klass, initial_object, callbacks, user_data), "FollowReferences"); +} +jvmtiError IterateThroughHeap(jint heap_filter, jclass klass, const jvmtiHeapCallbacks* callbacks, const void* user_data) { + return checkEx((*jvmtii())->IterateThroughHeap(jvmtii(), heap_filter, klass, callbacks, user_data), "IterateThroughHeap"); +} +jvmtiError GetTag(jobject object, jlong* tag_ptr) { + return checkEx((*jvmtii())->GetTag(jvmtii(), object, tag_ptr), "GetTag"); +} +jvmtiError SetTag(jobject object, jlong tag) { + return checkEx((*jvmtii())->SetTag(jvmtii(), object, tag), "SetTag"); +} +jvmtiError GetObjectsWithTags(jint tag_count, const jlong* tags, jint* count_ptr, jobject** object_result_ptr, jlong** tag_result_ptr) { + return checkEx((*jvmtii())->GetObjectsWithTags(jvmtii(), tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr), "GetObjectsWithTags"); +} +jvmtiError ForceGarbageCollection() { + return checkEx((*jvmtii())->ForceGarbageCollection(jvmtii()), "ForceGarbageCollection(jvmti"); +} +jvmtiError IterateOverObjectsReachableFromObject(jobject object, jvmtiObjectReferenceCallback object_reference_callback, const void* user_data) { + return checkEx((*jvmtii())->IterateOverObjectsReachableFromObject(jvmtii(), object, object_reference_callback, user_data), "IterateOverObjectsReachableFromObject"); +} +jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback, jvmtiStackReferenceCallback stack_ref_callback, jvmtiObjectReferenceCallback object_ref_callback, const void* user_data) { + return checkEx((*jvmtii())->IterateOverReachableObjects(jvmtii(), heap_root_callback, stack_ref_callback, object_ref_callback, user_data), "IterateOverReachableObjects"); +} +jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter, jvmtiHeapObjectCallback heap_object_callback, const void* user_data) { + return checkEx((*jvmtii())->IterateOverHeap(jvmtii(), object_filter, heap_object_callback, user_data), "IterateOverHeap"); +} +jvmtiError IterateOverInstancesOfClass(jclass klass, jvmtiHeapObjectFilter object_filter, jvmtiHeapObjectCallback heap_object_callback, const void* user_data) { + return checkEx((*jvmtii())->IterateOverInstancesOfClass(jvmtii(), klass, object_filter, heap_object_callback, user_data), "IterateOverInstancesOfClass"); +} +jvmtiError GetLocalObject(jthread thread, jint depth, jint slot, jobject* value_ptr) { + return checkEx((*jvmtii())->GetLocalObject(jvmtii(), thread, depth, slot, value_ptr), "GetLocalObject"); +} +jvmtiError GetLocalInstance(jthread thread, jint depth, jobject* value_ptr) { + return checkEx((*jvmtii())->GetLocalInstance(jvmtii(), thread, depth, value_ptr), "GetLocalInstance"); +} +jvmtiError GetLocalInt(jthread thread, jint depth, jint slot, jint* value_ptr) { + return checkEx((*jvmtii())->GetLocalInt(jvmtii(), thread, depth, slot, value_ptr), "GetLocalInt"); +} +jvmtiError GetLocalLong(jthread thread, jint depth, jint slot, jlong* value_ptr) { + return checkEx((*jvmtii())->GetLocalLong(jvmtii(), thread, depth, slot, value_ptr), "GetLocalLong"); +} +jvmtiError GetLocalFloat(jthread thread, jint depth, jint slot, jfloat* value_ptr) { + return checkEx((*jvmtii())->GetLocalFloat(jvmtii(), thread, depth, slot, value_ptr), "GetLocalFloat"); +} +jvmtiError GetLocalDouble(jthread thread, jint depth, jint slot, jdouble* value_ptr) { + return checkEx((*jvmtii())->GetLocalDouble(jvmtii(), thread, depth, slot, value_ptr), "GetLocalDouble"); +} +jvmtiError SetLocalObject(jthread thread, jint depth, jint slot, jobject value) { + return checkEx((*jvmtii())->SetLocalObject(jvmtii(), thread, depth, slot, value), "SetLocalObject"); +} +jvmtiError SetLocalInt(jthread thread, jint depth, jint slot, jint value) { + return checkEx((*jvmtii())->SetLocalInt(jvmtii(), thread, depth, slot, value), "SetLocalInt"); +} +jvmtiError SetLocalLong(jthread thread, jint depth, jint slot, jlong value) { + return checkEx((*jvmtii())->SetLocalLong(jvmtii(), thread, depth, slot, value), "SetLocalLong"); +} +jvmtiError SetLocalFloat(jthread thread, jint depth, jint slot, jfloat value) { + return checkEx((*jvmtii())->SetLocalFloat(jvmtii(), thread, depth, slot, value), "SetLocalFloat"); +} +jvmtiError SetLocalDouble(jthread thread, jint depth, jint slot, jdouble value) { + return checkEx((*jvmtii())->SetLocalDouble(jvmtii(), thread, depth, slot, value), "SetLocalDouble"); +} +jvmtiError SetBreakpoint(jmethodID method, jlocation location) { + return checkEx((*jvmtii())->SetBreakpoint(jvmtii(), method, location), "SetBreakpoint"); +} +jvmtiError ClearBreakpoint(jmethodID method, jlocation location) { + return checkEx((*jvmtii())->ClearBreakpoint(jvmtii(), method, location), "ClearBreakpoint"); +} +jvmtiError SetFieldAccessWatch(jclass klass, jfieldID field) { + return checkEx((*jvmtii())->SetFieldAccessWatch(jvmtii(), klass, field), "SetFieldAccessWatch"); +} +jvmtiError ClearFieldAccessWatch(jclass klass, jfieldID field) { + return checkEx((*jvmtii())->ClearFieldAccessWatch(jvmtii(), klass, field), "ClearFieldAccessWatch"); +} +jvmtiError SetFieldModificationWatch(jclass klass, jfieldID field) { + return checkEx((*jvmtii())->SetFieldModificationWatch(jvmtii(), klass, field), "SetFieldModificationWatch"); +} +jvmtiError ClearFieldModificationWatch(jclass klass, jfieldID field) { + return checkEx((*jvmtii())->ClearFieldModificationWatch(jvmtii(), klass, field), "ClearFieldModificationWatch"); +} +jvmtiError GetLoadedClasses(jint* class_count_ptr, jclass** classes_ptr) { + return checkEx((*jvmtii())->GetLoadedClasses(jvmtii(), class_count_ptr, classes_ptr), "GetLoadedClasses"); +} +jvmtiError GetClassLoaderClasses(jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) { + return checkEx((*jvmtii())->GetClassLoaderClasses(jvmtii(), initiating_loader, class_count_ptr, classes_ptr), "GetClassLoaderClasses"); +} +jvmtiError GetClassStatus(jclass klass, jint* status_ptr) { + return checkEx((*jvmtii())->GetClassStatus(jvmtii(), klass, status_ptr), "GetClassStatus"); +} +jvmtiError GetSourceFileName(jclass klass, char** source_name_ptr) { + return checkEx((*jvmtii())->GetSourceFileName(jvmtii(), klass, source_name_ptr), "GetSourceFileName"); +} +jvmtiError GetClassModifiers(jclass klass, jint* modifiers_ptr) { + return checkEx((*jvmtii())->GetClassModifiers(jvmtii(), klass, modifiers_ptr), "GetClassModifiers"); +} +jvmtiError GetClassMethods(jclass klass, jint* method_count_ptr, jmethodID** methods_ptr) { + return checkEx((*jvmtii())->GetClassMethods(jvmtii(), klass, method_count_ptr, methods_ptr), "GetClassMethods"); +} +jvmtiError GetClassFields(jclass klass, jint* field_count_ptr, jfieldID** fields_ptr) { + return checkEx((*jvmtii())->GetClassFields(jvmtii(), klass, field_count_ptr, fields_ptr), "GetClassFields"); +} +jvmtiError GetImplementedInterfaces(jclass klass, jint* interface_count_ptr, jclass** interfaces_ptr) { + return checkEx((*jvmtii())->GetImplementedInterfaces(jvmtii(), klass, interface_count_ptr, interfaces_ptr), "GetImplementedInterfaces"); +} +jvmtiError GetClassVersionNumbers(jclass klass, jint* minor_version_ptr, jint* major_version_ptr) { + return checkEx((*jvmtii())->GetClassVersionNumbers(jvmtii(), klass, minor_version_ptr, major_version_ptr), "GetClassVersionNumbers"); +} +jvmtiError GetConstantPool(jclass klass, jint* constant_pool_count_ptr, jint* constant_pool_byte_count_ptr, unsigned char** constant_pool_bytes_ptr) { + return checkEx((*jvmtii())->GetConstantPool(jvmtii(), klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr), "GetConstantPool"); +} +jvmtiError IsInterface(jclass klass, jboolean* is_interface_ptr) { + return checkEx((*jvmtii())->IsInterface(jvmtii(), klass, is_interface_ptr), "IsInterface"); +} +jvmtiError IsArrayClass(jclass klass, jboolean* is_array_class_ptr) { + return checkEx((*jvmtii())->IsArrayClass(jvmtii(), klass, is_array_class_ptr), "IsArrayClass"); +} +jvmtiError IsModifiableClass(jclass klass, jboolean* is_modifiable_class_ptr) { + return checkEx((*jvmtii())->IsModifiableClass(jvmtii(), klass, is_modifiable_class_ptr), "IsModifiableClass"); +} +jvmtiError GetClassLoader(jclass klass, jobject* classloader_ptr) { + return checkEx((*jvmtii())->GetClassLoader(jvmtii(), klass, classloader_ptr), "GetClassLoader"); +} +jvmtiError GetSourceDebugExtension(jclass klass, char** source_debug_extension_ptr) { + return checkEx((*jvmtii())->GetSourceDebugExtension(jvmtii(), klass, source_debug_extension_ptr), "GetSourceDebugExtension"); +} +jvmtiError RetransformClasses(jint class_count, const jclass* classes) { + return checkEx((*jvmtii())->RetransformClasses(jvmtii(), class_count, classes), "RetransformClasses"); +} +jvmtiError RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) { + return checkEx((*jvmtii())->RedefineClasses(jvmtii(), class_count, class_definitions), "RedefineClasses"); +} +jvmtiError GetObjectSize(jobject object, jlong* size_ptr) { + return checkEx((*jvmtii())->GetObjectSize(jvmtii(), object, size_ptr), "GetObjectSize"); +} +jvmtiError GetObjectHashCode(jobject object, jint* hash_code_ptr) { + return checkEx((*jvmtii())->GetObjectHashCode(jvmtii(), object, hash_code_ptr), "GetObjectHashCode"); +} +jvmtiError GetObjectMonitorUsage(jobject object, jvmtiMonitorUsage* info_ptr) { + return checkEx((*jvmtii())->GetObjectMonitorUsage(jvmtii(), object, info_ptr), "GetObjectMonitorUsage"); +} +jvmtiError GetFieldName(jclass klass, jfieldID field, char** name_ptr, char** signature_ptr, char** generic_ptr) { + return checkEx((*jvmtii())->GetFieldName(jvmtii(), klass, field, name_ptr, signature_ptr, generic_ptr), "GetFieldName"); +} +jvmtiError GetFieldDeclaringClass(jclass klass, jfieldID field, jclass* declaring_class_ptr) { + return checkEx((*jvmtii())->GetFieldDeclaringClass(jvmtii(), klass, field, declaring_class_ptr), "GetFieldDeclaringClass"); +} +jvmtiError GetFieldModifiers(jclass klass, jfieldID field, jint* modifiers_ptr) { + return checkEx((*jvmtii())->GetFieldModifiers(jvmtii(), klass, field, modifiers_ptr), "GetFieldModifiers"); +} +jvmtiError IsFieldSynthetic(jclass klass, jfieldID field, jboolean* is_synthetic_ptr) { + return checkEx((*jvmtii())->IsFieldSynthetic(jvmtii(), klass, field, is_synthetic_ptr), "IsFieldSynthetic"); +} +jvmtiError GetMethodName(jmethodID method, char** name_ptr, char** signature_ptr, char** generic_ptr) { + return checkEx((*jvmtii())->GetMethodName(jvmtii(), method, name_ptr, signature_ptr, generic_ptr), "GetMethodName"); +} +jvmtiError GetMethodDeclaringClass(jmethodID method, jclass* declaring_class_ptr) { + return checkEx((*jvmtii())->GetMethodDeclaringClass(jvmtii(), method, declaring_class_ptr), "GetMethodDeclaringClass"); +} +jvmtiError GetMethodModifiers(jmethodID method, jint* modifiers_ptr) { + return checkEx((*jvmtii())->GetMethodModifiers(jvmtii(), method, modifiers_ptr), "GetMethodModifiers"); +} +jvmtiError GetMaxLocals(jmethodID method, jint* max_ptr) { + return checkEx((*jvmtii())->GetMaxLocals(jvmtii(), method, max_ptr), "GetMaxLocals"); +} +jvmtiError GetArgumentsSize(jmethodID method, jint* size_ptr) { + return checkEx((*jvmtii())->GetArgumentsSize(jvmtii(), method, size_ptr), "GetArgumentsSize"); +} +jvmtiError GetLineNumberTable(jmethodID method, jint* entry_count_ptr, jvmtiLineNumberEntry** table_ptr) { + return checkEx((*jvmtii())->GetLineNumberTable(jvmtii(), method, entry_count_ptr, table_ptr), "GetLineNumberTable"); +} +jvmtiError GetMethodLocation(jmethodID method, jlocation* start_location_ptr, jlocation* end_location_ptr) { + return checkEx((*jvmtii())->GetMethodLocation(jvmtii(), method, start_location_ptr, end_location_ptr), "GetMethodLocation"); +} +jvmtiError GetLocalVariableTable(jmethodID method, jint* entry_count_ptr, jvmtiLocalVariableEntry** table_ptr) { + return checkEx((*jvmtii())->GetLocalVariableTable(jvmtii(), method, entry_count_ptr, table_ptr), "GetLocalVariableTable"); +} +jvmtiError GetBytecodes(jmethodID method, jint* bytecode_count_ptr, unsigned char** bytecodes_ptr) { + return checkEx((*jvmtii())->GetBytecodes(jvmtii(), method, bytecode_count_ptr, bytecodes_ptr), "GetBytecodes"); +} +jvmtiError IsMethodNative(jmethodID method, jboolean* is_native_ptr) { + return checkEx((*jvmtii())->IsMethodNative(jvmtii(), method, is_native_ptr), "IsMethodNative"); +} +jvmtiError IsMethodSynthetic(jmethodID method, jboolean* is_synthetic_ptr) { + return checkEx((*jvmtii())->IsMethodSynthetic(jvmtii(), method, is_synthetic_ptr), "IsMethodSynthetic"); +} +jvmtiError IsMethodObsolete(jmethodID method, jboolean* is_obsolete_ptr) { + return checkEx((*jvmtii())->IsMethodObsolete(jvmtii(), method, is_obsolete_ptr), "IsMethodObsolete"); +} +jvmtiError SetNativeMethodPrefix(const char* prefix) { + return checkEx((*jvmtii())->SetNativeMethodPrefix(jvmtii(), prefix), "SetNativeMethodPrefix"); +} +jvmtiError SetNativeMethodPrefixes(jint prefix_count, char** prefixes) { + return checkEx((*jvmtii())->SetNativeMethodPrefixes(jvmtii(), prefix_count, prefixes), "SetNativeMethodPrefixes"); +} +jvmtiError CreateRawMonitor(const char* name, jrawMonitorID* monitor_ptr) { + return checkEx((*jvmtii())->CreateRawMonitor(jvmtii(), name, monitor_ptr), "CreateRawMonitor"); +} +jvmtiError DestroyRawMonitor(jrawMonitorID monitor) { + return checkEx((*jvmtii())->DestroyRawMonitor(jvmtii(), monitor), "DestroyRawMonitor"); +} +jvmtiError RawMonitorEnter(jrawMonitorID monitor) { + return checkEx((*jvmtii())->RawMonitorEnter(jvmtii(), monitor), "RawMonitorEnter"); +} +jvmtiError RawMonitorExit(jrawMonitorID monitor) { + return checkEx((*jvmtii())->RawMonitorExit(jvmtii(), monitor), "RawMonitorExit"); +} +jvmtiError RawMonitorWait(jrawMonitorID monitor, jlong millis) { + return checkEx((*jvmtii())->RawMonitorWait(jvmtii(), monitor, millis), "RawMonitorWait"); +} +jvmtiError RawMonitorNotify(jrawMonitorID monitor) { + return checkEx((*jvmtii())->RawMonitorNotify(jvmtii(), monitor), "RawMonitorNotify"); +} +jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) { + return checkEx((*jvmtii())->RawMonitorNotifyAll(jvmtii(), monitor), "RawMonitorNotifyAll"); +} +jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) { + return checkEx((*jvmtii())->SetJNIFunctionTable(jvmtii(), function_table), "SetJNIFunctionTable"); +} +jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) { + return checkEx((*jvmtii())->GetJNIFunctionTable(jvmtii(), function_table), "GetJNIFunctionTable"); +} +jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_callbacks) { + return checkEx((*jvmtii())->SetEventCallbacks(jvmtii(), callbacks, size_of_callbacks), "SetEventCallbacks"); +} +jvmtiError SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, jthread event_thread, ...) { + return checkEx((*jvmtii())->SetEventNotificationMode(jvmtii(), mode, event_type, event_thread), "SetEventNotificationMode"); +} +jvmtiError GenerateEvents(jvmtiEvent event_type) { + return checkEx((*jvmtii())->GenerateEvents(jvmtii(), event_type), "GenerateEvents"); +} +jvmtiError GetExtensionFunctions(jint* extension_count_ptr, jvmtiExtensionFunctionInfo** extensions) { + return checkEx((*jvmtii())->GetExtensionFunctions(jvmtii(), extension_count_ptr, extensions), "GetExtensionFunctions"); +} +jvmtiError GetExtensionEvents(jint* extension_count_ptr, jvmtiExtensionEventInfo** extensions) { + return checkEx((*jvmtii())->GetExtensionEvents(jvmtii(), extension_count_ptr, extensions), "GetExtensionEvents"); +} +jvmtiError SetExtensionEventCallback(jint extension_event_index, jvmtiExtensionEvent callback) { + return checkEx((*jvmtii())->SetExtensionEventCallback(jvmtii(), extension_event_index, callback), "SetExtensionEventCallback"); +} +jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return checkEx((*jvmtii())->AddCapabilities(jvmtii(), capabilities_ptr), "AddCapabilities"); +} +jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return checkEx((*jvmtii())->RelinquishCapabilities(jvmtii(), capabilities_ptr), "RelinquishCapabilities"); +} +jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) { + return checkEx((*jvmtii())->GetCapabilities(jvmtii(), capabilities_ptr), "GetCapabilities"); +} +jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return checkEx((*jvmtii())->GetCurrentThreadCpuTimerInfo(jvmtii(), info_ptr), "GetCurrentThreadCpuTimerInfo"); +} +jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) { + return checkEx((*jvmtii())->GetCurrentThreadCpuTime(jvmtii(), nanos_ptr), "GetCurrentThreadCpuTime"); +} +jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return checkEx((*jvmtii())->GetThreadCpuTimerInfo(jvmtii(), info_ptr), "GetThreadCpuTimerInfo"); +} +jvmtiError GetThreadCpuTime(jthread thread, jlong* nanos_ptr) { + return checkEx((*jvmtii())->GetThreadCpuTime(jvmtii(), thread, nanos_ptr), "GetThreadCpuTime"); +} +jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) { + return checkEx((*jvmtii())->GetTimerInfo(jvmtii(), info_ptr), "GetTimerInfo"); +} +jvmtiError GetTime(jlong* nanos_ptr) { + return checkEx((*jvmtii())->GetTime(jvmtii(), nanos_ptr), "GetTime"); +} +jvmtiError GetAvailableProcessors(jint* processor_count_ptr) { + return checkEx((*jvmtii())->GetAvailableProcessors(jvmtii(), processor_count_ptr), "GetAvailableProcessors"); +} +jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) { + return checkEx((*jvmtii())->AddToBootstrapClassLoaderSearch(jvmtii(), segment), "AddToBootstrapClassLoaderSearch"); +} +jvmtiError AddToSystemClassLoaderSearch(const char* segment) { + return checkEx((*jvmtii())->AddToSystemClassLoaderSearch(jvmtii(), segment), "AddToSystemClassLoaderSearch"); +} +jvmtiError GetSystemProperties(jint* count_ptr, char*** property_ptr) { + return checkEx((*jvmtii())->GetSystemProperties(jvmtii(), count_ptr, property_ptr), "GetSystemProperties"); +} +jvmtiError GetSystemProperty(const char* property, char** value_ptr) { + return checkEx((*jvmtii())->GetSystemProperty(jvmtii(), property, value_ptr), "GetSystemProperty"); +} +jvmtiError SetSystemProperty(const char* property, const char* value) { + return checkEx((*jvmtii())->SetSystemProperty(jvmtii(), property, value), "SetSystemProperty"); +} +jvmtiError GetPhase(jvmtiPhase* phase_ptr) { + return checkEx((*jvmtii())->GetPhase(jvmtii(), phase_ptr), "GetPhase"); +} +jvmtiError DisposeEnvironment() { + return checkEx((*jvmtii())->DisposeEnvironment(jvmtii()), "DisposeEnvironment(jvmti"); +} +jvmtiError SetEnvironmentLocalStorage(const void* data) { + return checkEx((*jvmtii())->SetEnvironmentLocalStorage(jvmtii(), data), "SetEnvironmentLocalStorage"); +} +jvmtiError GetEnvironmentLocalStorage(void** data_ptr) { + return checkEx((*jvmtii())->GetEnvironmentLocalStorage(jvmtii(), data_ptr), "GetEnvironmentLocalStorage"); +} +jvmtiError GetVersionNumber(jint* version_ptr) { + return checkEx((*jvmtii())->GetVersionNumber(jvmtii(), version_ptr), "GetVersionNumber"); +} +jvmtiError GetErrorName(jvmtiError error, char** name_ptr) { + return checkEx((*jvmtii())->GetErrorName(jvmtii(), error, name_ptr), "GetErrorName"); +} +jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag, jboolean value) { + return checkEx((*jvmtii())->SetVerboseFlag(jvmtii(), flag, value), "SetVerboseFlag"); +} +jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { + return checkEx((*jvmtii())->GetJLocationFormat(jvmtii(), format_ptr), "GetJLocationFormat"); +} + +//////////////////////////////////////JNI FUNCTIONS/////////////////////////////////////// +extern JNIEnv* currentEnvs(); +extern JavaVM* getJvm(); + +jint GetVersion() { + jint variable = (*currentEnvs())->GetVersion(currentEnvs()); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jclass DefineClass(const char* name, jobject loader, const jbyte* buf, jsize len) { + jclass variable = (*currentEnvs())->DefineClass(currentEnvs(), name, loader, buf, len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jclass FindClass(const char* name) { + jclass variable = (*currentEnvs())->FindClass(currentEnvs(), name); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jmethodID FromReflectedMethod(jobject method) { + jmethodID variable = (*currentEnvs())->FromReflectedMethod(currentEnvs(), method); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfieldID FromReflectedField(jobject field) { + jfieldID variable = (*currentEnvs())->FromReflectedField(currentEnvs(), field); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + jobject variable = (*currentEnvs())->ToReflectedMethod(currentEnvs(), cls, methodID, isStatic); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jclass GetSuperclass(jclass sub) { + jclass variable = (*currentEnvs())->GetSuperclass(currentEnvs(), sub); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean IsAssignableFrom(jclass sub, jclass sup) { + jboolean variable = (*currentEnvs())->IsAssignableFrom(currentEnvs(), sub, sup); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + jobject variable = (*currentEnvs())->ToReflectedField(currentEnvs(), cls, fieldID, isStatic); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint Throw(jthrowable obj) { + jint variable = (*currentEnvs())->Throw(currentEnvs(), obj); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint ThrowNew(jclass clazz, const char* msg) { + jint variable = (*currentEnvs())->ThrowNew(currentEnvs(), clazz, msg); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jthrowable ExceptionOccurred() { + jthrowable variable = (*currentEnvs())->ExceptionOccurred(currentEnvs()); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint PushLocalFrame(jint capacity) { + jint variable = (*currentEnvs())->PushLocalFrame(currentEnvs(), capacity); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject PopLocalFrame(jobject result) { + jobject variable = (*currentEnvs())->PopLocalFrame(currentEnvs(), result); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject NewGlobalRef(jobject lobj) { + jobject variable = (*currentEnvs())->NewGlobalRef(currentEnvs(), lobj); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean IsSameObject(jobject obj1, jobject obj2) { + jboolean variable = (*currentEnvs())->IsSameObject(currentEnvs(), obj1, obj2); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject NewLocalRef(jobject ref) { + jobject variable = (*currentEnvs())->NewLocalRef(currentEnvs(), ref); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint EnsureLocalCapacity(jint capacity) { + jint variable = (*currentEnvs())->EnsureLocalCapacity(currentEnvs(), capacity); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject AllocObject(jclass clazz) { + jobject variable = (*currentEnvs())->AllocObject(currentEnvs(), clazz); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = (*currentEnvs())->NewObjectV(currentEnvs(), clazz, methodID, args); + va_end(args); + jobject variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args) { + jobject variable = (*currentEnvs())->NewObjectV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject NewObjectA(jclass clazz, jmethodID methodID, const jvalue* args) { + jobject variable = (*currentEnvs())->NewObjectA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jclass GetObjectClass(jobject obj) { + jclass variable = (*currentEnvs())->GetObjectClass(currentEnvs(), obj); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean IsInstanceOf(jobject obj, jclass clazz) { + jboolean variable = (*currentEnvs())->IsInstanceOf(currentEnvs(), obj, clazz); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) { + jmethodID variable = (*currentEnvs())->GetMethodID(currentEnvs(), clazz, name, sig); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = (*currentEnvs())->CallObjectMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jobject variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject CallObjectMethodV(jobject obj, jmethodID methodID, va_list args) { + jobject variable = (*currentEnvs())->CallObjectMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject CallObjectMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jobject variable = (*currentEnvs())->CallObjectMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean CallBooleanMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args, methodID); + result = (*currentEnvs())->CallBooleanMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jboolean variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, va_list args) { + jboolean variable = (*currentEnvs())->CallBooleanMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jboolean variable = (*currentEnvs())->CallBooleanMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args, methodID); + result = (*currentEnvs())->CallByteMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jbyte variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte CallByteMethodV(jobject obj, jmethodID methodID, va_list args) { + jbyte variable = (*currentEnvs())->CallByteMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte CallByteMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jbyte variable = (*currentEnvs())->CallByteMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args, methodID); + result = (*currentEnvs())->CallCharMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jchar variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar CallCharMethodV(jobject obj, jmethodID methodID, va_list args) { + jchar variable = (*currentEnvs())->CallCharMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar CallCharMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jchar variable = (*currentEnvs())->CallCharMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args, methodID); + result = (*currentEnvs())->CallShortMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jshort variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort CallShortMethodV(jobject obj, jmethodID methodID, va_list args) { + jshort variable = (*currentEnvs())->CallShortMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort CallShortMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jshort variable = (*currentEnvs())->CallShortMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args, methodID); + result = (*currentEnvs())->CallIntMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jint variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint CallIntMethodV(jobject obj, jmethodID methodID, va_list args) { + jint variable = (*currentEnvs())->CallIntMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint CallIntMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jint variable = (*currentEnvs())->CallIntMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args, methodID); + result = (*currentEnvs())->CallLongMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jlong variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong CallLongMethodV(jobject obj, jmethodID methodID, va_list args) { + jlong variable = (*currentEnvs())->CallLongMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong CallLongMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jlong variable = (*currentEnvs())->CallLongMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args, methodID); + result = (*currentEnvs())->CallFloatMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jfloat variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat CallFloatMethodV(jobject obj, jmethodID methodID, va_list args) { + jfloat variable = (*currentEnvs())->CallFloatMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat CallFloatMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jfloat variable = (*currentEnvs())->CallFloatMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args, methodID); + result = (*currentEnvs())->CallDoubleMethodV(currentEnvs(), obj, methodID, args); + va_end(args); + jdouble variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, va_list args) { + jdouble variable = (*currentEnvs())->CallDoubleMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + jdouble variable = (*currentEnvs())->CallDoubleMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualObjectMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jobject variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualObjectMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualObjectMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualBooleanMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jboolean variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualBooleanMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualBooleanMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualByteMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jbyte variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualByteMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualByteMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualCharMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jchar variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualCharMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualCharMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualShortMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jshort variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualShortMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualShortMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jint CallNonvirtualIntMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualIntMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jint variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualIntMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualIntMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualLongMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jlong variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualLongMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualLongMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualFloatMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jfloat variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualFloatMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualFloatMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args, methodID); + result = (*currentEnvs())->CallNonvirtualDoubleMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); + jdouble variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + return (*currentEnvs())->CallNonvirtualDoubleMethodV(currentEnvs(), obj, clazz, methodID, args); +} +jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + return (*currentEnvs())->CallNonvirtualDoubleMethodA(currentEnvs(), obj, clazz, methodID, args); +} +jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) { + jfieldID variable = (*currentEnvs())->GetFieldID(currentEnvs(), clazz, name, sig); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject GetObjectField(jobject obj, jfieldID fieldID) { + jobject variable = (*currentEnvs())->GetObjectField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + jboolean variable = (*currentEnvs())->GetBooleanField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte GetByteField(jobject obj, jfieldID fieldID) { + jbyte variable = (*currentEnvs())->GetByteField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar GetCharField(jobject obj, jfieldID fieldID) { + jchar variable = (*currentEnvs())->GetCharField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort GetShortField(jobject obj, jfieldID fieldID) { + jshort variable = (*currentEnvs())->GetShortField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint GetIntField(jobject obj, jfieldID fieldID) { + jint variable = (*currentEnvs())->GetIntField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong GetLongField(jobject obj, jfieldID fieldID) { + jlong variable = (*currentEnvs())->GetLongField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat GetFloatField(jobject obj, jfieldID fieldID) { + jfloat variable = (*currentEnvs())->GetFloatField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + jdouble variable = (*currentEnvs())->GetDoubleField(currentEnvs(), obj, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) { + jmethodID variable = (*currentEnvs())->GetStaticMethodID(currentEnvs(), clazz, name, sig); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticObjectMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + va_end(args); + jobject variable = result; + + return variable; +} +jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, va_list args) { + jobject variable = (*currentEnvs())->CallStaticObjectMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jobject variable = (*currentEnvs())->CallStaticObjectMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean CallStaticBooleanMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticBooleanMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jboolean variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean CallStaticBooleanMethodV(jclass clazz, jmethodID methodID, va_list args) { + jboolean variable = (*currentEnvs())->CallStaticBooleanMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean CallStaticBooleanMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jboolean variable = (*currentEnvs())->CallStaticBooleanMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte CallStaticByteMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticByteMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jbyte variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte CallStaticByteMethodV(jclass clazz, jmethodID methodID, va_list args) { + jbyte variable = (*currentEnvs())->CallStaticByteMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte CallStaticByteMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jbyte variable = (*currentEnvs())->CallStaticByteMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar CallStaticCharMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticCharMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jchar variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar CallStaticCharMethodV(jclass clazz, jmethodID methodID, va_list args) { + jchar variable = (*currentEnvs())->CallStaticCharMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar CallStaticCharMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jchar variable = (*currentEnvs())->CallStaticCharMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort CallStaticShortMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticShortMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jshort variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort CallStaticShortMethodV(jclass clazz, jmethodID methodID, va_list args) { + jshort variable = (*currentEnvs())->CallStaticShortMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort CallStaticShortMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jshort variable = (*currentEnvs())->CallStaticShortMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint CallStaticIntMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticIntMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jint variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint CallStaticIntMethodV(jclass clazz, jmethodID methodID, va_list args) { + jint variable = (*currentEnvs())->CallStaticIntMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint CallStaticIntMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jint variable = (*currentEnvs())->CallStaticIntMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong CallStaticLongMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticLongMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jlong variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong CallStaticLongMethodV(jclass clazz, jmethodID methodID, va_list args) { + jlong variable = (*currentEnvs())->CallStaticLongMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong CallStaticLongMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jlong variable = (*currentEnvs())->CallStaticLongMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat CallStaticFloatMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticFloatMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jfloat variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat CallStaticFloatMethodV(jclass clazz, jmethodID methodID, va_list args) { + jfloat variable = (*currentEnvs())->CallStaticFloatMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat CallStaticFloatMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jfloat variable = (*currentEnvs())->CallStaticFloatMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble CallStaticDoubleMethod(jclass clazz, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args, methodID); + result = (*currentEnvs())->CallStaticDoubleMethodV(currentEnvs(), clazz, methodID, args); + va_end(args); + jdouble variable = result; + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble CallStaticDoubleMethodV(jclass clazz, jmethodID methodID, va_list args) { + jdouble variable = (*currentEnvs())->CallStaticDoubleMethodV(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble CallStaticDoubleMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { + jdouble variable = (*currentEnvs())->CallStaticDoubleMethodA(currentEnvs(), clazz, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig) { + jfieldID variable = (*currentEnvs())->GetStaticFieldID(currentEnvs(), clazz, name, sig); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + jobject variable = (*currentEnvs())->GetStaticObjectField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + jboolean variable = (*currentEnvs())->GetStaticBooleanField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + jbyte variable = (*currentEnvs())->GetStaticByteField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + jchar variable = (*currentEnvs())->GetStaticCharField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + jshort variable = (*currentEnvs())->GetStaticShortField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + jint variable = (*currentEnvs())->GetStaticIntField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + jlong variable = (*currentEnvs())->GetStaticLongField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + jfloat variable = (*currentEnvs())->GetStaticFloatField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + jdouble variable = (*currentEnvs())->GetStaticDoubleField(currentEnvs(), clazz, fieldID); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jstring NewString(const jchar* unicode, jsize len) { + jstring variable = (*currentEnvs())->NewString(currentEnvs(), unicode, len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jsize GetStringLength(jstring str) { + jsize variable = (*currentEnvs())->GetStringLength(currentEnvs(), str); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +const jchar* GetStringChars(jstring str, jboolean* isCopy) { + const jchar variable = (*currentEnvs())->GetStringChars(currentEnvs(), str, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jstring NewStringUTF(const char* utf) { + jstring variable = (*currentEnvs())->NewStringUTF(currentEnvs(), utf); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jsize GetStringUTFLength(jstring str) { + jsize variable = (*currentEnvs())->GetStringUTFLength(currentEnvs(), str); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +const char* GetStringUTFChars(jstring str, jboolean* isCopy) { + const char* variable = (*currentEnvs())->GetStringUTFChars(currentEnvs(), str, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jsize GetArrayLength(jarray array) { + jsize variable = (*currentEnvs())->GetArrayLength(currentEnvs(), array); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobjectArray NewObjectArray(jsize len, jclass clazz, jobject init) { + jobjectArray variable = (*currentEnvs())->NewObjectArray(currentEnvs(), len, clazz, init); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject GetObjectArrayElement(jobjectArray array, jsize index) { + jobject variable = (*currentEnvs())->GetObjectArrayElement(currentEnvs(), array, index); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbooleanArray NewBooleanArray(jsize len) { + jbooleanArray variable = (*currentEnvs())->NewBooleanArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyteArray NewByteArray(jsize len) { + jbyteArray variable = (*currentEnvs())->NewByteArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jcharArray NewCharArray(jsize len) { + jcharArray variable = (*currentEnvs())->NewCharArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshortArray NewShortArray(jsize len) { + jshortArray variable = (*currentEnvs())->NewShortArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jintArray NewIntArray(jsize len) { + jintArray variable = (*currentEnvs())->NewIntArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlongArray NewLongArray(jsize len) { + jlongArray variable = (*currentEnvs())->NewLongArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloatArray NewFloatArray(jsize len) { + jfloatArray variable = (*currentEnvs())->NewFloatArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdoubleArray NewDoubleArray(jsize len) { + jdoubleArray variable = (*currentEnvs())->NewDoubleArray(currentEnvs(), len); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy) { + jboolean* variable = (*currentEnvs())->GetBooleanArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy) { + jbyte* variable = (*currentEnvs())->GetByteArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy) { + jchar* variable = (*currentEnvs())->GetCharArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy) { + jshort* variable = (*currentEnvs())->GetShortArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint* GetIntArrayElements(jintArray array, jboolean* isCopy) { + jint* variable = (*currentEnvs())->GetIntArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy) { + jlong* variable = (*currentEnvs())->GetLongArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy) { + jfloat* variable = (*currentEnvs())->GetFloatArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy) { + jdouble* variable = (*currentEnvs())->GetDoubleArrayElements(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods) { + jint variable = (*currentEnvs())->RegisterNatives(currentEnvs(), clazz, methods, nMethods); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint UnregisterNatives(jclass clazz) { + jint variable = (*currentEnvs())->UnregisterNatives(currentEnvs(), clazz); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint MonitorEnter(jobject obj) { + jint variable = (*currentEnvs())->MonitorEnter(currentEnvs(), obj); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint MonitorExit(jobject obj) { + jint variable = (*currentEnvs())->MonitorExit(currentEnvs(), obj); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint GetJavaVM(JavaVM** vm) { + jint variable = (*currentEnvs())->GetJavaVM(currentEnvs(), vm); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy) { + void* variable = (*currentEnvs())->GetPrimitiveArrayCritical(currentEnvs(), array, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +const jchar* GetStringCritical(jstring string, jboolean* isCopy) { + const jchar* variable = (*currentEnvs())->GetStringCritical(currentEnvs(), string, isCopy); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jweak NewWeakGlobalRef(jobject obj) { + jweak variable = (*currentEnvs())->NewWeakGlobalRef(currentEnvs(), obj); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jboolean ExceptionCheck() { + jboolean variable = (*currentEnvs())->ExceptionCheck(currentEnvs()); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobject NewDirectByteBuffer(void* address, jlong capacity) { + jobject variable = (*currentEnvs())->NewDirectByteBuffer(currentEnvs(), address, capacity); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +void* GetDirectBufferAddress(jobject buf) { + void* variable = (*currentEnvs())->GetDirectBufferAddress(currentEnvs(), buf); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jlong GetDirectBufferCapacity(jobject buf) { + jlong variable = (*currentEnvs())->GetDirectBufferCapacity(currentEnvs(), buf); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jobjectRefType GetObjectRefType(jobject obj) { + jobjectRefType variable = (*currentEnvs())->GetObjectRefType(currentEnvs(), obj); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint DestroyJavaVM() { + jint variable = (*getJvm())->DestroyJavaVM(getJvm()); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint AttachCurrentThread(void** penv, void* args) { + jint variable = (*getJvm())->AttachCurrentThread(getJvm(), penv, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint DetachCurrentThread() { + jint variable = (*getJvm())->DetachCurrentThread(getJvm()); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint GetEnv(void** penv, jint version) { + jint variable = (*getJvm())->GetEnv(getJvm(), penv, version); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +jint AttachCurrentThreadAsDaemon(void** penv, void* args) { + jint variable = (*getJvm())->AttachCurrentThreadAsDaemon(getJvm(), penv, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + return variable; +} +void ExceptionDescribe() { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); +} +void ExceptionClear() { + (*currentEnvs())->ExceptionClear(currentEnvs()); +} +void FatalError(const char* msg) { + (*currentEnvs())->FatalError(currentEnvs(), msg); +} +void DeleteGlobalRef(jobject gref) { + (*currentEnvs())->DeleteGlobalRef(currentEnvs(), gref); +} +void DeleteLocalRef(jobject obj) { + (*currentEnvs())->DeleteLocalRef(currentEnvs(), obj); +} +void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + (*currentEnvs())->CallVoidMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + va_end(args); +} +void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args) { + (*currentEnvs())->CallVoidMethodV(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void CallVoidMethodA(jobject obj, jmethodID methodID, const jvalue* args) { + (*currentEnvs())->CallVoidMethodA(currentEnvs(), obj, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + (*currentEnvs())->CallNonvirtualVoidMethodV(currentEnvs(), obj, clazz, methodID, args); + va_end(args); +} +void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { + (*currentEnvs())->CallNonvirtualVoidMethodV(currentEnvs(), obj, clazz, methodID, args); +} +void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + (*currentEnvs())->CallNonvirtualVoidMethodA(currentEnvs(), obj, clazz, methodID, args); +} +void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + (*currentEnvs())->SetObjectField(currentEnvs(), obj, fieldID, val); +} +void SetBooleanField(jobject obj, jfieldID fieldID, jboolean val) { + (*currentEnvs())->SetBooleanField(currentEnvs(), obj, fieldID, val); +} +void SetByteField(jobject obj, jfieldID fieldID, jbyte val) { + (*currentEnvs())->SetByteField(currentEnvs(), obj, fieldID, val); +} +void SetCharField(jobject obj, jfieldID fieldID, jchar val) { + (*currentEnvs())->SetCharField(currentEnvs(), obj, fieldID, val); +} +void SetShortField(jobject obj, jfieldID fieldID, jshort val) { + (*currentEnvs())->SetShortField(currentEnvs(), obj, fieldID, val); +} +void SetIntField(jobject obj, jfieldID fieldID, jint val) { + (*currentEnvs())->SetIntField(currentEnvs(), obj, fieldID, val); +} +void SetLongField(jobject obj, jfieldID fieldID, jlong val) { + (*currentEnvs())->SetLongField(currentEnvs(), obj, fieldID, val); +} +void SetFloatField(jobject obj, jfieldID fieldID, jfloat val) { + (*currentEnvs())->SetFloatField(currentEnvs(), obj, fieldID, val); +} +void SetDoubleField(jobject obj, jfieldID fieldID, jdouble val) { + (*currentEnvs())->SetDoubleField(currentEnvs(), obj, fieldID, val); +} +void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + (*currentEnvs())->CallStaticVoidMethodV(currentEnvs(), cls, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } + va_end(args); +} +void CallStaticVoidMethodV(jclass cls, jmethodID methodID, va_list args) { + (*currentEnvs())->CallStaticVoidMethodV(currentEnvs(), cls, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void CallStaticVoidMethodA(jclass cls, jmethodID methodID, const jvalue* args) { + (*currentEnvs())->CallStaticVoidMethodA(currentEnvs(), cls, methodID, args); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value) { + (*currentEnvs())->SetStaticObjectField(currentEnvs(), clazz, fieldID, value); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) { + (*currentEnvs())->SetStaticBooleanField(currentEnvs(), clazz, fieldID, value); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) { + (*currentEnvs())->SetStaticByteField(currentEnvs(), clazz, fieldID, value); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) { + (*currentEnvs())->SetStaticCharField(currentEnvs(), clazz, fieldID, value); + if((*currentEnvs())->ExceptionCheck(currentEnvs())) { + (*currentEnvs())->ExceptionDescribe(currentEnvs()); + } +} +void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) { + (*currentEnvs())->SetStaticShortField(currentEnvs(), clazz, fieldID, value); +} +void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) { + (*currentEnvs())->SetStaticIntField(currentEnvs(), clazz, fieldID, value); +} +void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) { + (*currentEnvs())->SetStaticLongField(currentEnvs(), clazz, fieldID, value); +} +void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) { + (*currentEnvs())->SetStaticFloatField(currentEnvs(), clazz, fieldID, value); +} +void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value) { + (*currentEnvs())->SetStaticDoubleField(currentEnvs(), clazz, fieldID, value); +} +void ReleaseStringChars(jstring str, const jchar* chars) { + (*currentEnvs())->ReleaseStringChars(currentEnvs(), str, chars); +} +void ReleaseStringUTFChars(jstring str, const char* chars) { + (*currentEnvs())->ReleaseStringUTFChars(currentEnvs(), str, chars); +} +void SetObjectArrayElement(jobjectArray array, jsize index, jobject val) { + (*currentEnvs())->SetObjectArrayElement(currentEnvs(), array, index, val); +} +void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode) { + (*currentEnvs())->ReleaseBooleanArrayElements(currentEnvs(), array, elems, mode); +} +void ReleaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode) { + (*currentEnvs())->ReleaseByteArrayElements(currentEnvs(), array, elems, mode); +} +void ReleaseCharArrayElements(jcharArray array, jchar* elems, jint mode) { + (*currentEnvs())->ReleaseCharArrayElements(currentEnvs(), array, elems, mode); +} +void ReleaseShortArrayElements(jshortArray array, jshort* elems, jint mode) { + (*currentEnvs())->ReleaseShortArrayElements(currentEnvs(), array, elems, mode); +} +void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode) { + (*currentEnvs())->ReleaseIntArrayElements(currentEnvs(), array, elems, mode); +} +void ReleaseLongArrayElements(jlongArray array, jlong* elems, jint mode) { + (*currentEnvs())->ReleaseLongArrayElements(currentEnvs(), array, elems, mode); +} +void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode) { + (*currentEnvs())->ReleaseFloatArrayElements(currentEnvs(), array, elems, mode); +} +void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode) { + (*currentEnvs())->ReleaseDoubleArrayElements(currentEnvs(), array, elems, mode); +} +void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf) { + (*currentEnvs())->GetBooleanArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf) { + (*currentEnvs())->GetByteArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* buf) { + (*currentEnvs())->GetCharArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf) { + (*currentEnvs())->GetShortArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf) { + (*currentEnvs())->GetIntArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf) { + (*currentEnvs())->GetLongArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf) { + (*currentEnvs())->GetFloatArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf) { + (*currentEnvs())->GetDoubleArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean* buf) { + (*currentEnvs())->SetBooleanArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte* buf) { + (*currentEnvs())->SetByteArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar* buf) { + (*currentEnvs())->SetCharArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort* buf) { + (*currentEnvs())->SetShortArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint* buf) { + (*currentEnvs())->SetIntArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong* buf) { + (*currentEnvs())->SetLongArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat* buf) { + (*currentEnvs())->SetFloatArrayRegion(currentEnvs(), array, start, len, buf); +} +void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble* buf) { + (*currentEnvs())->SetDoubleArrayRegion(currentEnvs(), array, start, len, buf); +} +void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf) { + (*currentEnvs())->GetStringRegion(currentEnvs(), str, start, len, buf); +} +void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf) { + (*currentEnvs())->GetStringUTFRegion(currentEnvs(), str, start, len, buf); +} +void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode) { + (*currentEnvs())->ReleasePrimitiveArrayCritical(currentEnvs(), array, carray, mode); +} +void ReleaseStringCritical(jstring string, const jchar* cstring) { + (*currentEnvs())->ReleaseStringCritical(currentEnvs(), string, cstring); +} +void DeleteWeakGlobalRef(jweak ref) { + (*currentEnvs())->DeleteWeakGlobalRef(currentEnvs(), ref); +} diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jni.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jni.h new file mode 100644 index 00000000..0ffe244b --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jni.h @@ -0,0 +1,1960 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + +#endif /* __cplusplus */ +}; + +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jni_md.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jni_md.h new file mode 100644 index 00000000..ff30a32c --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jni_md.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif +#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #define JNIEXPORT __attribute__((visibility("default"))) + #define JNIIMPORT __attribute__((visibility("default"))) +#else + #define JNIEXPORT + #define JNIIMPORT +#endif + +#define JNICALL + +typedef int jint; +#ifdef _LP64 /* 64-bit Solaris */ +typedef long jlong; +#else +typedef long long jlong; +#endif + +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jvmti.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jvmti.h new file mode 100644 index 00000000..74243f54 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/linuxX64/jvmti.h @@ -0,0 +1,2534 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + + /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */ + + + /* Include file for the Java(tm) Virtual Machine Tool Interface */ + +#ifndef _JAVA_JVMTI_H_ +#define _JAVA_JVMTI_H_ + +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + JVMTI_VERSION_1 = 0x30010000, + JVMTI_VERSION_1_0 = 0x30010000, + JVMTI_VERSION_1_1 = 0x30010100, + JVMTI_VERSION_1_2 = 0x30010200, + + JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (2 * 0x100) + 1 /* version: 1.2.1 */ +}; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved); + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM* vm, char* options, void* reserved); + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm); + + /* Forward declaration of the environment */ + +struct _jvmtiEnv; + +struct jvmtiInterface_1_; + +#ifdef __cplusplus +typedef _jvmtiEnv jvmtiEnv; +#else +typedef const struct jvmtiInterface_1_ *jvmtiEnv; +#endif /* __cplusplus */ + +/* Derived Base Types */ + +typedef jobject jthread; +typedef jobject jthreadGroup; +typedef jlong jlocation; +struct _jrawMonitorID; +typedef struct _jrawMonitorID *jrawMonitorID; +typedef struct JNINativeInterface_ jniNativeInterface; + + /* Constants */ + + + /* Thread State Flags */ + +enum { + JVMTI_THREAD_STATE_ALIVE = 0x0001, + JVMTI_THREAD_STATE_TERMINATED = 0x0002, + JVMTI_THREAD_STATE_RUNNABLE = 0x0004, + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400, + JVMTI_THREAD_STATE_WAITING = 0x0080, + JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010, + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020, + JVMTI_THREAD_STATE_SLEEPING = 0x0040, + JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100, + JVMTI_THREAD_STATE_PARKED = 0x0200, + JVMTI_THREAD_STATE_SUSPENDED = 0x100000, + JVMTI_THREAD_STATE_INTERRUPTED = 0x200000, + JVMTI_THREAD_STATE_IN_NATIVE = 0x400000, + JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000, + JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000, + JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000 +}; + + /* java.lang.Thread.State Conversion Masks */ + +enum { + JVMTI_JAVA_LANG_THREAD_STATE_MASK = JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT, + JVMTI_JAVA_LANG_THREAD_STATE_NEW = 0, + JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED = JVMTI_THREAD_STATE_TERMINATED, + JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE, + JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, + JVMTI_JAVA_LANG_THREAD_STATE_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY, + JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +}; + + /* Thread Priority Constants */ + +enum { + JVMTI_THREAD_MIN_PRIORITY = 1, + JVMTI_THREAD_NORM_PRIORITY = 5, + JVMTI_THREAD_MAX_PRIORITY = 10 +}; + + /* Heap Filter Flags */ + +enum { + JVMTI_HEAP_FILTER_TAGGED = 0x4, + JVMTI_HEAP_FILTER_UNTAGGED = 0x8, + JVMTI_HEAP_FILTER_CLASS_TAGGED = 0x10, + JVMTI_HEAP_FILTER_CLASS_UNTAGGED = 0x20 +}; + + /* Heap Visit Control Flags */ + +enum { + JVMTI_VISIT_OBJECTS = 0x100, + JVMTI_VISIT_ABORT = 0x8000 +}; + + /* Heap Reference Enumeration */ + +typedef enum { + JVMTI_HEAP_REFERENCE_CLASS = 1, + JVMTI_HEAP_REFERENCE_FIELD = 2, + JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_HEAP_REFERENCE_CLASS_LOADER = 4, + JVMTI_HEAP_REFERENCE_SIGNERS = 5, + JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_HEAP_REFERENCE_INTERFACE = 7, + JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8, + JVMTI_HEAP_REFERENCE_CONSTANT_POOL = 9, + JVMTI_HEAP_REFERENCE_SUPERCLASS = 10, + JVMTI_HEAP_REFERENCE_JNI_GLOBAL = 21, + JVMTI_HEAP_REFERENCE_SYSTEM_CLASS = 22, + JVMTI_HEAP_REFERENCE_MONITOR = 23, + JVMTI_HEAP_REFERENCE_STACK_LOCAL = 24, + JVMTI_HEAP_REFERENCE_JNI_LOCAL = 25, + JVMTI_HEAP_REFERENCE_THREAD = 26, + JVMTI_HEAP_REFERENCE_OTHER = 27 +} jvmtiHeapReferenceKind; + + /* Primitive Type Enumeration */ + +typedef enum { + JVMTI_PRIMITIVE_TYPE_BOOLEAN = 90, + JVMTI_PRIMITIVE_TYPE_BYTE = 66, + JVMTI_PRIMITIVE_TYPE_CHAR = 67, + JVMTI_PRIMITIVE_TYPE_SHORT = 83, + JVMTI_PRIMITIVE_TYPE_INT = 73, + JVMTI_PRIMITIVE_TYPE_LONG = 74, + JVMTI_PRIMITIVE_TYPE_FLOAT = 70, + JVMTI_PRIMITIVE_TYPE_DOUBLE = 68 +} jvmtiPrimitiveType; + + /* Heap Object Filter Enumeration */ + +typedef enum { + JVMTI_HEAP_OBJECT_TAGGED = 1, + JVMTI_HEAP_OBJECT_UNTAGGED = 2, + JVMTI_HEAP_OBJECT_EITHER = 3 +} jvmtiHeapObjectFilter; + + /* Heap Root Kind Enumeration */ + +typedef enum { + JVMTI_HEAP_ROOT_JNI_GLOBAL = 1, + JVMTI_HEAP_ROOT_SYSTEM_CLASS = 2, + JVMTI_HEAP_ROOT_MONITOR = 3, + JVMTI_HEAP_ROOT_STACK_LOCAL = 4, + JVMTI_HEAP_ROOT_JNI_LOCAL = 5, + JVMTI_HEAP_ROOT_THREAD = 6, + JVMTI_HEAP_ROOT_OTHER = 7 +} jvmtiHeapRootKind; + + /* Object Reference Enumeration */ + +typedef enum { + JVMTI_REFERENCE_CLASS = 1, + JVMTI_REFERENCE_FIELD = 2, + JVMTI_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_REFERENCE_CLASS_LOADER = 4, + JVMTI_REFERENCE_SIGNERS = 5, + JVMTI_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_REFERENCE_INTERFACE = 7, + JVMTI_REFERENCE_STATIC_FIELD = 8, + JVMTI_REFERENCE_CONSTANT_POOL = 9 +} jvmtiObjectReferenceKind; + + /* Iteration Control Enumeration */ + +typedef enum { + JVMTI_ITERATION_CONTINUE = 1, + JVMTI_ITERATION_IGNORE = 2, + JVMTI_ITERATION_ABORT = 0 +} jvmtiIterationControl; + + /* Class Status Flags */ + +enum { + JVMTI_CLASS_STATUS_VERIFIED = 1, + JVMTI_CLASS_STATUS_PREPARED = 2, + JVMTI_CLASS_STATUS_INITIALIZED = 4, + JVMTI_CLASS_STATUS_ERROR = 8, + JVMTI_CLASS_STATUS_ARRAY = 16, + JVMTI_CLASS_STATUS_PRIMITIVE = 32 +}; + + /* Event Enable/Disable */ + +typedef enum { + JVMTI_ENABLE = 1, + JVMTI_DISABLE = 0 +} jvmtiEventMode; + + /* Extension Function/Event Parameter Types */ + +typedef enum { + JVMTI_TYPE_JBYTE = 101, + JVMTI_TYPE_JCHAR = 102, + JVMTI_TYPE_JSHORT = 103, + JVMTI_TYPE_JINT = 104, + JVMTI_TYPE_JLONG = 105, + JVMTI_TYPE_JFLOAT = 106, + JVMTI_TYPE_JDOUBLE = 107, + JVMTI_TYPE_JBOOLEAN = 108, + JVMTI_TYPE_JOBJECT = 109, + JVMTI_TYPE_JTHREAD = 110, + JVMTI_TYPE_JCLASS = 111, + JVMTI_TYPE_JVALUE = 112, + JVMTI_TYPE_JFIELDID = 113, + JVMTI_TYPE_JMETHODID = 114, + JVMTI_TYPE_CCHAR = 115, + JVMTI_TYPE_CVOID = 116, + JVMTI_TYPE_JNIENV = 117 +} jvmtiParamTypes; + + /* Extension Function/Event Parameter Kinds */ + +typedef enum { + JVMTI_KIND_IN = 91, + JVMTI_KIND_IN_PTR = 92, + JVMTI_KIND_IN_BUF = 93, + JVMTI_KIND_ALLOC_BUF = 94, + JVMTI_KIND_ALLOC_ALLOC_BUF = 95, + JVMTI_KIND_OUT = 96, + JVMTI_KIND_OUT_BUF = 97 +} jvmtiParamKind; + + /* Timer Kinds */ + +typedef enum { + JVMTI_TIMER_USER_CPU = 30, + JVMTI_TIMER_TOTAL_CPU = 31, + JVMTI_TIMER_ELAPSED = 32 +} jvmtiTimerKind; + + /* Phases of execution */ + +typedef enum { + JVMTI_PHASE_ONLOAD = 1, + JVMTI_PHASE_PRIMORDIAL = 2, + JVMTI_PHASE_START = 6, + JVMTI_PHASE_LIVE = 4, + JVMTI_PHASE_DEAD = 8 +} jvmtiPhase; + + /* Version Interface Types */ + +enum { + JVMTI_VERSION_INTERFACE_JNI = 0x00000000, + JVMTI_VERSION_INTERFACE_JVMTI = 0x30000000 +}; + + /* Version Masks */ + +enum { + JVMTI_VERSION_MASK_INTERFACE_TYPE = 0x70000000, + JVMTI_VERSION_MASK_MAJOR = 0x0FFF0000, + JVMTI_VERSION_MASK_MINOR = 0x0000FF00, + JVMTI_VERSION_MASK_MICRO = 0x000000FF +}; + + /* Version Shifts */ + +enum { + JVMTI_VERSION_SHIFT_MAJOR = 16, + JVMTI_VERSION_SHIFT_MINOR = 8, + JVMTI_VERSION_SHIFT_MICRO = 0 +}; + + /* Verbose Flag Enumeration */ + +typedef enum { + JVMTI_VERBOSE_OTHER = 0, + JVMTI_VERBOSE_GC = 1, + JVMTI_VERBOSE_CLASS = 2, + JVMTI_VERBOSE_JNI = 4 +} jvmtiVerboseFlag; + + /* JLocation Format Enumeration */ + +typedef enum { + JVMTI_JLOCATION_JVMBCI = 1, + JVMTI_JLOCATION_MACHINEPC = 2, + JVMTI_JLOCATION_OTHER = 0 +} jvmtiJlocationFormat; + + /* Resource Exhaustion Flags */ + +enum { + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 0x0001, + JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 0x0002, + JVMTI_RESOURCE_EXHAUSTED_THREADS = 0x0004 +}; + + /* Errors */ + +typedef enum { + JVMTI_ERROR_NONE = 0, + JVMTI_ERROR_INVALID_THREAD = 10, + JVMTI_ERROR_INVALID_THREAD_GROUP = 11, + JVMTI_ERROR_INVALID_PRIORITY = 12, + JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13, + JVMTI_ERROR_THREAD_SUSPENDED = 14, + JVMTI_ERROR_THREAD_NOT_ALIVE = 15, + JVMTI_ERROR_INVALID_OBJECT = 20, + JVMTI_ERROR_INVALID_CLASS = 21, + JVMTI_ERROR_CLASS_NOT_PREPARED = 22, + JVMTI_ERROR_INVALID_METHODID = 23, + JVMTI_ERROR_INVALID_LOCATION = 24, + JVMTI_ERROR_INVALID_FIELDID = 25, + JVMTI_ERROR_NO_MORE_FRAMES = 31, + JVMTI_ERROR_OPAQUE_FRAME = 32, + JVMTI_ERROR_TYPE_MISMATCH = 34, + JVMTI_ERROR_INVALID_SLOT = 35, + JVMTI_ERROR_DUPLICATE = 40, + JVMTI_ERROR_NOT_FOUND = 41, + JVMTI_ERROR_INVALID_MONITOR = 50, + JVMTI_ERROR_NOT_MONITOR_OWNER = 51, + JVMTI_ERROR_INTERRUPT = 52, + JVMTI_ERROR_INVALID_CLASS_FORMAT = 60, + JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION = 61, + JVMTI_ERROR_FAILS_VERIFICATION = 62, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED = 63, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED = 64, + JVMTI_ERROR_INVALID_TYPESTATE = 65, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED = 66, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED = 67, + JVMTI_ERROR_UNSUPPORTED_VERSION = 68, + JVMTI_ERROR_NAMES_DONT_MATCH = 69, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71, + JVMTI_ERROR_UNMODIFIABLE_CLASS = 79, + JVMTI_ERROR_NOT_AVAILABLE = 98, + JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99, + JVMTI_ERROR_NULL_POINTER = 100, + JVMTI_ERROR_ABSENT_INFORMATION = 101, + JVMTI_ERROR_INVALID_EVENT_TYPE = 102, + JVMTI_ERROR_ILLEGAL_ARGUMENT = 103, + JVMTI_ERROR_NATIVE_METHOD = 104, + JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED = 106, + JVMTI_ERROR_OUT_OF_MEMORY = 110, + JVMTI_ERROR_ACCESS_DENIED = 111, + JVMTI_ERROR_WRONG_PHASE = 112, + JVMTI_ERROR_INTERNAL = 113, + JVMTI_ERROR_UNATTACHED_THREAD = 115, + JVMTI_ERROR_INVALID_ENVIRONMENT = 116, + JVMTI_ERROR_MAX = 116 +} jvmtiError; + + /* Event IDs */ + +typedef enum { + JVMTI_MIN_EVENT_TYPE_VAL = 50, + JVMTI_EVENT_VM_INIT = 50, + JVMTI_EVENT_VM_DEATH = 51, + JVMTI_EVENT_THREAD_START = 52, + JVMTI_EVENT_THREAD_END = 53, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54, + JVMTI_EVENT_CLASS_LOAD = 55, + JVMTI_EVENT_CLASS_PREPARE = 56, + JVMTI_EVENT_VM_START = 57, + JVMTI_EVENT_EXCEPTION = 58, + JVMTI_EVENT_EXCEPTION_CATCH = 59, + JVMTI_EVENT_SINGLE_STEP = 60, + JVMTI_EVENT_FRAME_POP = 61, + JVMTI_EVENT_BREAKPOINT = 62, + JVMTI_EVENT_FIELD_ACCESS = 63, + JVMTI_EVENT_FIELD_MODIFICATION = 64, + JVMTI_EVENT_METHOD_ENTRY = 65, + JVMTI_EVENT_METHOD_EXIT = 66, + JVMTI_EVENT_NATIVE_METHOD_BIND = 67, + JVMTI_EVENT_COMPILED_METHOD_LOAD = 68, + JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69, + JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70, + JVMTI_EVENT_DATA_DUMP_REQUEST = 71, + JVMTI_EVENT_MONITOR_WAIT = 73, + JVMTI_EVENT_MONITOR_WAITED = 74, + JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75, + JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76, + JVMTI_EVENT_RESOURCE_EXHAUSTED = 80, + JVMTI_EVENT_GARBAGE_COLLECTION_START = 81, + JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82, + JVMTI_EVENT_OBJECT_FREE = 83, + JVMTI_EVENT_VM_OBJECT_ALLOC = 84, + JVMTI_MAX_EVENT_TYPE_VAL = 84 +} jvmtiEvent; + + + /* Pre-Declarations */ +struct _jvmtiThreadInfo; +typedef struct _jvmtiThreadInfo jvmtiThreadInfo; +struct _jvmtiMonitorStackDepthInfo; +typedef struct _jvmtiMonitorStackDepthInfo jvmtiMonitorStackDepthInfo; +struct _jvmtiThreadGroupInfo; +typedef struct _jvmtiThreadGroupInfo jvmtiThreadGroupInfo; +struct _jvmtiFrameInfo; +typedef struct _jvmtiFrameInfo jvmtiFrameInfo; +struct _jvmtiStackInfo; +typedef struct _jvmtiStackInfo jvmtiStackInfo; +struct _jvmtiHeapReferenceInfoField; +typedef struct _jvmtiHeapReferenceInfoField jvmtiHeapReferenceInfoField; +struct _jvmtiHeapReferenceInfoArray; +typedef struct _jvmtiHeapReferenceInfoArray jvmtiHeapReferenceInfoArray; +struct _jvmtiHeapReferenceInfoConstantPool; +typedef struct _jvmtiHeapReferenceInfoConstantPool jvmtiHeapReferenceInfoConstantPool; +struct _jvmtiHeapReferenceInfoStackLocal; +typedef struct _jvmtiHeapReferenceInfoStackLocal jvmtiHeapReferenceInfoStackLocal; +struct _jvmtiHeapReferenceInfoJniLocal; +typedef struct _jvmtiHeapReferenceInfoJniLocal jvmtiHeapReferenceInfoJniLocal; +struct _jvmtiHeapReferenceInfoReserved; +typedef struct _jvmtiHeapReferenceInfoReserved jvmtiHeapReferenceInfoReserved; +union _jvmtiHeapReferenceInfo; +typedef union _jvmtiHeapReferenceInfo jvmtiHeapReferenceInfo; +struct _jvmtiHeapCallbacks; +typedef struct _jvmtiHeapCallbacks jvmtiHeapCallbacks; +struct _jvmtiClassDefinition; +typedef struct _jvmtiClassDefinition jvmtiClassDefinition; +struct _jvmtiMonitorUsage; +typedef struct _jvmtiMonitorUsage jvmtiMonitorUsage; +struct _jvmtiLineNumberEntry; +typedef struct _jvmtiLineNumberEntry jvmtiLineNumberEntry; +struct _jvmtiLocalVariableEntry; +typedef struct _jvmtiLocalVariableEntry jvmtiLocalVariableEntry; +struct _jvmtiParamInfo; +typedef struct _jvmtiParamInfo jvmtiParamInfo; +struct _jvmtiExtensionFunctionInfo; +typedef struct _jvmtiExtensionFunctionInfo jvmtiExtensionFunctionInfo; +struct _jvmtiExtensionEventInfo; +typedef struct _jvmtiExtensionEventInfo jvmtiExtensionEventInfo; +struct _jvmtiTimerInfo; +typedef struct _jvmtiTimerInfo jvmtiTimerInfo; +struct _jvmtiAddrLocationMap; +typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap; + + /* Function Types */ + +typedef void (JNICALL *jvmtiStartFunction) + (jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg); + +typedef jint (JNICALL *jvmtiHeapIterationCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiHeapReferenceCallback) + (jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiPrimitiveFieldCallback) + (jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong object_class_tag, jlong* object_tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data); + +typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data); + +typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data); + +typedef jint (JNICALL *jvmtiReservedCallback) + (); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback) + (jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void* user_data); + +typedef jvmtiError (JNICALL *jvmtiExtensionFunction) + (jvmtiEnv* jvmti_env, ...); + +typedef void (JNICALL *jvmtiExtensionEvent) + (jvmtiEnv* jvmti_env, ...); + + + /* Structure Types */ +struct _jvmtiThreadInfo { + char* name; + jint priority; + jboolean is_daemon; + jthreadGroup thread_group; + jobject context_class_loader; +}; +struct _jvmtiMonitorStackDepthInfo { + jobject monitor; + jint stack_depth; +}; +struct _jvmtiThreadGroupInfo { + jthreadGroup parent; + char* name; + jint max_priority; + jboolean is_daemon; +}; +struct _jvmtiFrameInfo { + jmethodID method; + jlocation location; +}; +struct _jvmtiStackInfo { + jthread thread; + jint state; + jvmtiFrameInfo* frame_buffer; + jint frame_count; +}; +struct _jvmtiHeapReferenceInfoField { + jint index; +}; +struct _jvmtiHeapReferenceInfoArray { + jint index; +}; +struct _jvmtiHeapReferenceInfoConstantPool { + jint index; +}; +struct _jvmtiHeapReferenceInfoStackLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; + jlocation location; + jint slot; +}; +struct _jvmtiHeapReferenceInfoJniLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; +}; +struct _jvmtiHeapReferenceInfoReserved { + jlong reserved1; + jlong reserved2; + jlong reserved3; + jlong reserved4; + jlong reserved5; + jlong reserved6; + jlong reserved7; + jlong reserved8; +}; +union _jvmtiHeapReferenceInfo { + jvmtiHeapReferenceInfoField field; + jvmtiHeapReferenceInfoArray array; + jvmtiHeapReferenceInfoConstantPool constant_pool; + jvmtiHeapReferenceInfoStackLocal stack_local; + jvmtiHeapReferenceInfoJniLocal jni_local; + jvmtiHeapReferenceInfoReserved other; +}; +struct _jvmtiHeapCallbacks { + jvmtiHeapIterationCallback heap_iteration_callback; + jvmtiHeapReferenceCallback heap_reference_callback; + jvmtiPrimitiveFieldCallback primitive_field_callback; + jvmtiArrayPrimitiveValueCallback array_primitive_value_callback; + jvmtiStringPrimitiveValueCallback string_primitive_value_callback; + jvmtiReservedCallback reserved5; + jvmtiReservedCallback reserved6; + jvmtiReservedCallback reserved7; + jvmtiReservedCallback reserved8; + jvmtiReservedCallback reserved9; + jvmtiReservedCallback reserved10; + jvmtiReservedCallback reserved11; + jvmtiReservedCallback reserved12; + jvmtiReservedCallback reserved13; + jvmtiReservedCallback reserved14; + jvmtiReservedCallback reserved15; +}; +struct _jvmtiClassDefinition { + jclass klass; + jint class_byte_count; + const unsigned char* class_bytes; +}; +struct _jvmtiMonitorUsage { + jthread owner; + jint entry_count; + jint waiter_count; + jthread* waiters; + jint notify_waiter_count; + jthread* notify_waiters; +}; +struct _jvmtiLineNumberEntry { + jlocation start_location; + jint line_number; +}; +struct _jvmtiLocalVariableEntry { + jlocation start_location; + jint length; + char* name; + char* signature; + char* generic_signature; + jint slot; +}; +struct _jvmtiParamInfo { + char* name; + jvmtiParamKind kind; + jvmtiParamTypes base_type; + jboolean null_ok; +}; +struct _jvmtiExtensionFunctionInfo { + jvmtiExtensionFunction func; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; + jint error_count; + jvmtiError* errors; +}; +struct _jvmtiExtensionEventInfo { + jint extension_event_index; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; +}; +struct _jvmtiTimerInfo { + jlong max_value; + jboolean may_skip_forward; + jboolean may_skip_backward; + jvmtiTimerKind kind; + jlong reserved1; + jlong reserved2; +}; +struct _jvmtiAddrLocationMap { + const void* start_address; + jlocation location; +}; + +typedef struct { + unsigned int can_tag_objects : 1; + unsigned int can_generate_field_modification_events : 1; + unsigned int can_generate_field_access_events : 1; + unsigned int can_get_bytecodes : 1; + unsigned int can_get_synthetic_attribute : 1; + unsigned int can_get_owned_monitor_info : 1; + unsigned int can_get_current_contended_monitor : 1; + unsigned int can_get_monitor_info : 1; + unsigned int can_pop_frame : 1; + unsigned int can_redefine_classes : 1; + unsigned int can_signal_thread : 1; + unsigned int can_get_source_file_name : 1; + unsigned int can_get_line_numbers : 1; + unsigned int can_get_source_debug_extension : 1; + unsigned int can_access_local_variables : 1; + unsigned int can_maintain_original_method_order : 1; + unsigned int can_generate_single_step_events : 1; + unsigned int can_generate_exception_events : 1; + unsigned int can_generate_frame_pop_events : 1; + unsigned int can_generate_breakpoint_events : 1; + unsigned int can_suspend : 1; + unsigned int can_redefine_any_class : 1; + unsigned int can_get_current_thread_cpu_time : 1; + unsigned int can_get_thread_cpu_time : 1; + unsigned int can_generate_method_entry_events : 1; + unsigned int can_generate_method_exit_events : 1; + unsigned int can_generate_all_class_hook_events : 1; + unsigned int can_generate_compiled_method_load_events : 1; + unsigned int can_generate_monitor_events : 1; + unsigned int can_generate_vm_object_alloc_events : 1; + unsigned int can_generate_native_method_bind_events : 1; + unsigned int can_generate_garbage_collection_events : 1; + unsigned int can_generate_object_free_events : 1; + unsigned int can_force_early_return : 1; + unsigned int can_get_owned_monitor_stack_depth_info : 1; + unsigned int can_get_constant_pool : 1; + unsigned int can_set_native_method_prefix : 1; + unsigned int can_retransform_classes : 1; + unsigned int can_retransform_any_class : 1; + unsigned int can_generate_resource_exhaustion_heap_events : 1; + unsigned int can_generate_resource_exhaustion_threads_events : 1; + unsigned int : 7; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; +} jvmtiCapabilities; + + + /* Event Definitions */ + +typedef void (JNICALL *jvmtiEventReserved)(void); + + +typedef void (JNICALL *jvmtiEventBreakpoint) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventClassFileLoadHook) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data); + +typedef void (JNICALL *jvmtiEventClassLoad) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventClassPrepare) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventCompiledMethodLoad) + (jvmtiEnv *jvmti_env, + jmethodID method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info); + +typedef void (JNICALL *jvmtiEventCompiledMethodUnload) + (jvmtiEnv *jvmti_env, + jmethodID method, + const void* code_addr); + +typedef void (JNICALL *jvmtiEventDataDumpRequest) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventDynamicCodeGenerated) + (jvmtiEnv *jvmti_env, + const char* name, + const void* address, + jint length); + +typedef void (JNICALL *jvmtiEventException) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception, + jmethodID catch_method, + jlocation catch_location); + +typedef void (JNICALL *jvmtiEventExceptionCatch) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception); + +typedef void (JNICALL *jvmtiEventFieldAccess) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field); + +typedef void (JNICALL *jvmtiEventFieldModification) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field, + char signature_type, + jvalue new_value); + +typedef void (JNICALL *jvmtiEventFramePop) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception); + +typedef void (JNICALL *jvmtiEventGarbageCollectionFinish) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventGarbageCollectionStart) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventMethodEntry) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method); + +typedef void (JNICALL *jvmtiEventMethodExit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception, + jvalue return_value); + +typedef void (JNICALL *jvmtiEventMonitorContendedEnter) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorContendedEntered) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorWait) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jlong timeout); + +typedef void (JNICALL *jvmtiEventMonitorWaited) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jboolean timed_out); + +typedef void (JNICALL *jvmtiEventNativeMethodBind) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + void* address, + void** new_address_ptr); + +typedef void (JNICALL *jvmtiEventObjectFree) + (jvmtiEnv *jvmti_env, + jlong tag); + +typedef void (JNICALL *jvmtiEventResourceExhausted) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint flags, + const void* reserved, + const char* description); + +typedef void (JNICALL *jvmtiEventSingleStep) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventThreadEnd) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventThreadStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMDeath) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + +typedef void (JNICALL *jvmtiEventVMInit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMObjectAlloc) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jclass object_klass, + jlong size); + +typedef void (JNICALL *jvmtiEventVMStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + + /* Event Callback Structure */ + +typedef struct { + /* 50 : VM Initialization Event */ + jvmtiEventVMInit VMInit; + /* 51 : VM Death Event */ + jvmtiEventVMDeath VMDeath; + /* 52 : Thread Start */ + jvmtiEventThreadStart ThreadStart; + /* 53 : Thread End */ + jvmtiEventThreadEnd ThreadEnd; + /* 54 : Class File Load Hook */ + jvmtiEventClassFileLoadHook ClassFileLoadHook; + /* 55 : Class Load */ + jvmtiEventClassLoad ClassLoad; + /* 56 : Class Prepare */ + jvmtiEventClassPrepare ClassPrepare; + /* 57 : VM Start Event */ + jvmtiEventVMStart VMStart; + /* 58 : Exception */ + jvmtiEventException Exception; + /* 59 : Exception Catch */ + jvmtiEventExceptionCatch ExceptionCatch; + /* 60 : Single Step */ + jvmtiEventSingleStep SingleStep; + /* 61 : Frame Pop */ + jvmtiEventFramePop FramePop; + /* 62 : Breakpoint */ + jvmtiEventBreakpoint Breakpoint; + /* 63 : Field Access */ + jvmtiEventFieldAccess FieldAccess; + /* 64 : Field Modification */ + jvmtiEventFieldModification FieldModification; + /* 65 : Method Entry */ + jvmtiEventMethodEntry MethodEntry; + /* 66 : Method Exit */ + jvmtiEventMethodExit MethodExit; + /* 67 : Native Method Bind */ + jvmtiEventNativeMethodBind NativeMethodBind; + /* 68 : Compiled Method Load */ + jvmtiEventCompiledMethodLoad CompiledMethodLoad; + /* 69 : Compiled Method Unload */ + jvmtiEventCompiledMethodUnload CompiledMethodUnload; + /* 70 : Dynamic Code Generated */ + jvmtiEventDynamicCodeGenerated DynamicCodeGenerated; + /* 71 : Data Dump Request */ + jvmtiEventDataDumpRequest DataDumpRequest; + /* 72 */ + jvmtiEventReserved reserved72; + /* 73 : Monitor Wait */ + jvmtiEventMonitorWait MonitorWait; + /* 74 : Monitor Waited */ + jvmtiEventMonitorWaited MonitorWaited; + /* 75 : Monitor Contended Enter */ + jvmtiEventMonitorContendedEnter MonitorContendedEnter; + /* 76 : Monitor Contended Entered */ + jvmtiEventMonitorContendedEntered MonitorContendedEntered; + /* 77 */ + jvmtiEventReserved reserved77; + /* 78 */ + jvmtiEventReserved reserved78; + /* 79 */ + jvmtiEventReserved reserved79; + /* 80 : Resource Exhausted */ + jvmtiEventResourceExhausted ResourceExhausted; + /* 81 : Garbage Collection Start */ + jvmtiEventGarbageCollectionStart GarbageCollectionStart; + /* 82 : Garbage Collection Finish */ + jvmtiEventGarbageCollectionFinish GarbageCollectionFinish; + /* 83 : Object Free */ + jvmtiEventObjectFree ObjectFree; + /* 84 : VM Object Allocation */ + jvmtiEventVMObjectAlloc VMObjectAlloc; +} jvmtiEventCallbacks; + + + /* Function Interface */ + +typedef struct jvmtiInterface_1_ { + + /* 1 : RESERVED */ + void *reserved1; + + /* 2 : Set Event Notification Mode */ + jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env, + jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...); + + /* 3 : RESERVED */ + void *reserved3; + + /* 4 : Get All Threads */ + jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env, + jint* threads_count_ptr, + jthread** threads_ptr); + + /* 5 : Suspend Thread */ + jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env, + jthread thread); + + /* 6 : Resume Thread */ + jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env, + jthread thread); + + /* 7 : Stop Thread */ + jvmtiError (JNICALL *StopThread) (jvmtiEnv* env, + jthread thread, + jobject exception); + + /* 8 : Interrupt Thread */ + jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env, + jthread thread); + + /* 9 : Get Thread Info */ + jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env, + jthread thread, + jvmtiThreadInfo* info_ptr); + + /* 10 : Get Owned Monitor Info */ + jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env, + jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr); + + /* 11 : Get Current Contended Monitor */ + jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env, + jthread thread, + jobject* monitor_ptr); + + /* 12 : Run Agent Thread */ + jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env, + jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority); + + /* 13 : Get Top Thread Groups */ + jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 14 : Get Thread Group Info */ + jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + /* 15 : Get Thread Group Children */ + jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 16 : Get Frame Count */ + jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env, + jthread thread, + jint* count_ptr); + + /* 17 : Get Thread State */ + jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env, + jthread thread, + jint* thread_state_ptr); + + /* 18 : Get Current Thread */ + jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env, + jthread* thread_ptr); + + /* 19 : Get Frame Location */ + jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env, + jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr); + + /* 20 : Notify Frame Pop */ + jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env, + jthread thread, + jint depth); + + /* 21 : Get Local Variable - Object */ + jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject* value_ptr); + + /* 22 : Get Local Variable - Int */ + jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint* value_ptr); + + /* 23 : Get Local Variable - Long */ + jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong* value_ptr); + + /* 24 : Get Local Variable - Float */ + jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat* value_ptr); + + /* 25 : Get Local Variable - Double */ + jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble* value_ptr); + + /* 26 : Set Local Variable - Object */ + jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject value); + + /* 27 : Set Local Variable - Int */ + jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint value); + + /* 28 : Set Local Variable - Long */ + jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong value); + + /* 29 : Set Local Variable - Float */ + jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat value); + + /* 30 : Set Local Variable - Double */ + jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble value); + + /* 31 : Create Raw Monitor */ + jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env, + const char* name, + jrawMonitorID* monitor_ptr); + + /* 32 : Destroy Raw Monitor */ + jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 33 : Raw Monitor Enter */ + jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 34 : Raw Monitor Exit */ + jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 35 : Raw Monitor Wait */ + jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env, + jrawMonitorID monitor, + jlong millis); + + /* 36 : Raw Monitor Notify */ + jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 37 : Raw Monitor Notify All */ + jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 38 : Set Breakpoint */ + jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 39 : Clear Breakpoint */ + jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 40 : RESERVED */ + void *reserved40; + + /* 41 : Set Field Access Watch */ + jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 42 : Clear Field Access Watch */ + jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 43 : Set Field Modification Watch */ + jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 44 : Clear Field Modification Watch */ + jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 45 : Is Modifiable Class */ + jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_modifiable_class_ptr); + + /* 46 : Allocate */ + jvmtiError (JNICALL *Allocate) (jvmtiEnv* env, + jlong size, + unsigned char** mem_ptr); + + /* 47 : Deallocate */ + jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env, + unsigned char* mem); + + /* 48 : Get Class Signature */ + jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env, + jclass klass, + char** signature_ptr, + char** generic_ptr); + + /* 49 : Get Class Status */ + jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env, + jclass klass, + jint* status_ptr); + + /* 50 : Get Source File Name */ + jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env, + jclass klass, + char** source_name_ptr); + + /* 51 : Get Class Modifiers */ + jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env, + jclass klass, + jint* modifiers_ptr); + + /* 52 : Get Class Methods */ + jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env, + jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr); + + /* 53 : Get Class Fields */ + jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env, + jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr); + + /* 54 : Get Implemented Interfaces */ + jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env, + jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr); + + /* 55 : Is Interface */ + jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env, + jclass klass, + jboolean* is_interface_ptr); + + /* 56 : Is Array Class */ + jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_array_class_ptr); + + /* 57 : Get Class Loader */ + jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env, + jclass klass, + jobject* classloader_ptr); + + /* 58 : Get Object Hash Code */ + jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env, + jobject object, + jint* hash_code_ptr); + + /* 59 : Get Object Monitor Usage */ + jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env, + jobject object, + jvmtiMonitorUsage* info_ptr); + + /* 60 : Get Field Name (and Signature) */ + jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env, + jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 61 : Get Field Declaring Class */ + jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jclass* declaring_class_ptr); + + /* 62 : Get Field Modifiers */ + jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jint* modifiers_ptr); + + /* 63 : Is Field Synthetic */ + jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr); + + /* 64 : Get Method Name (and Signature) */ + jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env, + jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 65 : Get Method Declaring Class */ + jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env, + jmethodID method, + jclass* declaring_class_ptr); + + /* 66 : Get Method Modifiers */ + jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env, + jmethodID method, + jint* modifiers_ptr); + + /* 67 : RESERVED */ + void *reserved67; + + /* 68 : Get Max Locals */ + jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env, + jmethodID method, + jint* max_ptr); + + /* 69 : Get Arguments Size */ + jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env, + jmethodID method, + jint* size_ptr); + + /* 70 : Get Line Number Table */ + jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr); + + /* 71 : Get Method Location */ + jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env, + jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr); + + /* 72 : Get Local Variable Table */ + jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr); + + /* 73 : Set Native Method Prefix */ + jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env, + const char* prefix); + + /* 74 : Set Native Method Prefixes */ + jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env, + jint prefix_count, + char** prefixes); + + /* 75 : Get Bytecodes */ + jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env, + jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr); + + /* 76 : Is Method Native */ + jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env, + jmethodID method, + jboolean* is_native_ptr); + + /* 77 : Is Method Synthetic */ + jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env, + jmethodID method, + jboolean* is_synthetic_ptr); + + /* 78 : Get Loaded Classes */ + jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 79 : Get Classloader Classes */ + jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 80 : Pop Frame */ + jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env, + jthread thread); + + /* 81 : Force Early Return - Object */ + jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env, + jthread thread, + jobject value); + + /* 82 : Force Early Return - Int */ + jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env, + jthread thread, + jint value); + + /* 83 : Force Early Return - Long */ + jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env, + jthread thread, + jlong value); + + /* 84 : Force Early Return - Float */ + jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env, + jthread thread, + jfloat value); + + /* 85 : Force Early Return - Double */ + jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env, + jthread thread, + jdouble value); + + /* 86 : Force Early Return - Void */ + jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env, + jthread thread); + + /* 87 : Redefine Classes */ + jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env, + jint class_count, + const jvmtiClassDefinition* class_definitions); + + /* 88 : Get Version Number */ + jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env, + jint* version_ptr); + + /* 89 : Get Capabilities */ + jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 90 : Get Source Debug Extension */ + jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env, + jclass klass, + char** source_debug_extension_ptr); + + /* 91 : Is Method Obsolete */ + jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env, + jmethodID method, + jboolean* is_obsolete_ptr); + + /* 92 : Suspend Thread List */ + jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 93 : Resume Thread List */ + jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 94 : RESERVED */ + void *reserved94; + + /* 95 : RESERVED */ + void *reserved95; + + /* 96 : RESERVED */ + void *reserved96; + + /* 97 : RESERVED */ + void *reserved97; + + /* 98 : RESERVED */ + void *reserved98; + + /* 99 : RESERVED */ + void *reserved99; + + /* 100 : Get All Stack Traces */ + jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr); + + /* 101 : Get Thread List Stack Traces */ + jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env, + jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr); + + /* 102 : Get Thread Local Storage */ + jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + void** data_ptr); + + /* 103 : Set Thread Local Storage */ + jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + const void* data); + + /* 104 : Get Stack Trace */ + jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env, + jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr); + + /* 105 : RESERVED */ + void *reserved105; + + /* 106 : Get Tag */ + jvmtiError (JNICALL *GetTag) (jvmtiEnv* env, + jobject object, + jlong* tag_ptr); + + /* 107 : Set Tag */ + jvmtiError (JNICALL *SetTag) (jvmtiEnv* env, + jobject object, + jlong tag); + + /* 108 : Force Garbage Collection */ + jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env); + + /* 109 : Iterate Over Objects Reachable From Object */ + jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env, + jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data); + + /* 110 : Iterate Over Reachable Objects */ + jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env, + jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data); + + /* 111 : Iterate Over Heap */ + jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 112 : Iterate Over Instances Of Class */ + jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env, + jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 113 : RESERVED */ + void *reserved113; + + /* 114 : Get Objects With Tags */ + jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env, + jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr); + + /* 115 : Follow References */ + jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 116 : Iterate Through Heap */ + jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 117 : RESERVED */ + void *reserved117; + + /* 118 : RESERVED */ + void *reserved118; + + /* 119 : RESERVED */ + void *reserved119; + + /* 120 : Set JNI Function Table */ + jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env, + const jniNativeInterface* function_table); + + /* 121 : Get JNI Function Table */ + jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env, + jniNativeInterface** function_table); + + /* 122 : Set Event Callbacks */ + jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks); + + /* 123 : Generate Events */ + jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env, + jvmtiEvent event_type); + + /* 124 : Get Extension Functions */ + jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions); + + /* 125 : Get Extension Events */ + jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions); + + /* 126 : Set Extension Event Callback */ + jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback); + + /* 127 : Dispose Environment */ + jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env); + + /* 128 : Get Error Name */ + jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env, + jvmtiError error, + char** name_ptr); + + /* 129 : Get JLocation Format */ + jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env, + jvmtiJlocationFormat* format_ptr); + + /* 130 : Get System Properties */ + jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env, + jint* count_ptr, + char*** property_ptr); + + /* 131 : Get System Property */ + jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env, + const char* property, + char** value_ptr); + + /* 132 : Set System Property */ + jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env, + const char* property, + const char* value); + + /* 133 : Get Phase */ + jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env, + jvmtiPhase* phase_ptr); + + /* 134 : Get Current Thread CPU Timer Information */ + jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 135 : Get Current Thread CPU Time */ + jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 136 : Get Thread CPU Timer Information */ + jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 137 : Get Thread CPU Time */ + jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env, + jthread thread, + jlong* nanos_ptr); + + /* 138 : Get Timer Information */ + jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 139 : Get Time */ + jvmtiError (JNICALL *GetTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 140 : Get Potential Capabilities */ + jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 141 : RESERVED */ + void *reserved141; + + /* 142 : Add Capabilities */ + jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 143 : Relinquish Capabilities */ + jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 144 : Get Available Processors */ + jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env, + jint* processor_count_ptr); + + /* 145 : Get Class Version Numbers */ + jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env, + jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr); + + /* 146 : Get Constant Pool */ + jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env, + jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr); + + /* 147 : Get Environment Local Storage */ + jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env, + void** data_ptr); + + /* 148 : Set Environment Local Storage */ + jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env, + const void* data); + + /* 149 : Add To Bootstrap Class Loader Search */ + jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 150 : Set Verbose Flag */ + jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env, + jvmtiVerboseFlag flag, + jboolean value); + + /* 151 : Add To System Class Loader Search */ + jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 152 : Retransform Classes */ + jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env, + jint class_count, + const jclass* classes); + + /* 153 : Get Owned Monitor Stack Depth Info */ + jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env, + jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr); + + /* 154 : Get Object Size */ + jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env, + jobject object, + jlong* size_ptr); + + /* 155 : Get Local Instance */ + jvmtiError (JNICALL *GetLocalInstance) (jvmtiEnv* env, + jthread thread, + jint depth, + jobject* value_ptr); + +} jvmtiInterface_1; + +struct _jvmtiEnv { + const struct jvmtiInterface_1_ *functions; +#ifdef __cplusplus + + + jvmtiError Allocate(jlong size, + unsigned char** mem_ptr) { + return functions->Allocate(this, size, mem_ptr); + } + + jvmtiError Deallocate(unsigned char* mem) { + return functions->Deallocate(this, mem); + } + + jvmtiError GetThreadState(jthread thread, + jint* thread_state_ptr) { + return functions->GetThreadState(this, thread, thread_state_ptr); + } + + jvmtiError GetCurrentThread(jthread* thread_ptr) { + return functions->GetCurrentThread(this, thread_ptr); + } + + jvmtiError GetAllThreads(jint* threads_count_ptr, + jthread** threads_ptr) { + return functions->GetAllThreads(this, threads_count_ptr, threads_ptr); + } + + jvmtiError SuspendThread(jthread thread) { + return functions->SuspendThread(this, thread); + } + + jvmtiError SuspendThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->SuspendThreadList(this, request_count, request_list, results); + } + + jvmtiError ResumeThread(jthread thread) { + return functions->ResumeThread(this, thread); + } + + jvmtiError ResumeThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->ResumeThreadList(this, request_count, request_list, results); + } + + jvmtiError StopThread(jthread thread, + jobject exception) { + return functions->StopThread(this, thread, exception); + } + + jvmtiError InterruptThread(jthread thread) { + return functions->InterruptThread(this, thread); + } + + jvmtiError GetThreadInfo(jthread thread, + jvmtiThreadInfo* info_ptr) { + return functions->GetThreadInfo(this, thread, info_ptr); + } + + jvmtiError GetOwnedMonitorInfo(jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr) { + return functions->GetOwnedMonitorInfo(this, thread, owned_monitor_count_ptr, owned_monitors_ptr); + } + + jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + return functions->GetOwnedMonitorStackDepthInfo(this, thread, monitor_info_count_ptr, monitor_info_ptr); + } + + jvmtiError GetCurrentContendedMonitor(jthread thread, + jobject* monitor_ptr) { + return functions->GetCurrentContendedMonitor(this, thread, monitor_ptr); + } + + jvmtiError RunAgentThread(jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority) { + return functions->RunAgentThread(this, thread, proc, arg, priority); + } + + jvmtiError SetThreadLocalStorage(jthread thread, + const void* data) { + return functions->SetThreadLocalStorage(this, thread, data); + } + + jvmtiError GetThreadLocalStorage(jthread thread, + void** data_ptr) { + return functions->GetThreadLocalStorage(this, thread, data_ptr); + } + + jvmtiError GetTopThreadGroups(jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetTopThreadGroups(this, group_count_ptr, groups_ptr); + } + + jvmtiError GetThreadGroupInfo(jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + return functions->GetThreadGroupInfo(this, group, info_ptr); + } + + jvmtiError GetThreadGroupChildren(jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetThreadGroupChildren(this, group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr); + } + + jvmtiError GetStackTrace(jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr) { + return functions->GetStackTrace(this, thread, start_depth, max_frame_count, frame_buffer, count_ptr); + } + + jvmtiError GetAllStackTraces(jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr) { + return functions->GetAllStackTraces(this, max_frame_count, stack_info_ptr, thread_count_ptr); + } + + jvmtiError GetThreadListStackTraces(jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr) { + return functions->GetThreadListStackTraces(this, thread_count, thread_list, max_frame_count, stack_info_ptr); + } + + jvmtiError GetFrameCount(jthread thread, + jint* count_ptr) { + return functions->GetFrameCount(this, thread, count_ptr); + } + + jvmtiError PopFrame(jthread thread) { + return functions->PopFrame(this, thread); + } + + jvmtiError GetFrameLocation(jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr) { + return functions->GetFrameLocation(this, thread, depth, method_ptr, location_ptr); + } + + jvmtiError NotifyFramePop(jthread thread, + jint depth) { + return functions->NotifyFramePop(this, thread, depth); + } + + jvmtiError ForceEarlyReturnObject(jthread thread, + jobject value) { + return functions->ForceEarlyReturnObject(this, thread, value); + } + + jvmtiError ForceEarlyReturnInt(jthread thread, + jint value) { + return functions->ForceEarlyReturnInt(this, thread, value); + } + + jvmtiError ForceEarlyReturnLong(jthread thread, + jlong value) { + return functions->ForceEarlyReturnLong(this, thread, value); + } + + jvmtiError ForceEarlyReturnFloat(jthread thread, + jfloat value) { + return functions->ForceEarlyReturnFloat(this, thread, value); + } + + jvmtiError ForceEarlyReturnDouble(jthread thread, + jdouble value) { + return functions->ForceEarlyReturnDouble(this, thread, value); + } + + jvmtiError ForceEarlyReturnVoid(jthread thread) { + return functions->ForceEarlyReturnVoid(this, thread); + } + + jvmtiError FollowReferences(jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->FollowReferences(this, heap_filter, klass, initial_object, callbacks, user_data); + } + + jvmtiError IterateThroughHeap(jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->IterateThroughHeap(this, heap_filter, klass, callbacks, user_data); + } + + jvmtiError GetTag(jobject object, + jlong* tag_ptr) { + return functions->GetTag(this, object, tag_ptr); + } + + jvmtiError SetTag(jobject object, + jlong tag) { + return functions->SetTag(this, object, tag); + } + + jvmtiError GetObjectsWithTags(jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr) { + return functions->GetObjectsWithTags(this, tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr); + } + + jvmtiError ForceGarbageCollection() { + return functions->ForceGarbageCollection(this); + } + + jvmtiError IterateOverObjectsReachableFromObject(jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data) { + return functions->IterateOverObjectsReachableFromObject(this, object, object_reference_callback, user_data); + } + + jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data) { + return functions->IterateOverReachableObjects(this, heap_root_callback, stack_ref_callback, object_ref_callback, user_data); + } + + jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverHeap(this, object_filter, heap_object_callback, user_data); + } + + jvmtiError IterateOverInstancesOfClass(jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverInstancesOfClass(this, klass, object_filter, heap_object_callback, user_data); + } + + jvmtiError GetLocalObject(jthread thread, + jint depth, + jint slot, + jobject* value_ptr) { + return functions->GetLocalObject(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalInstance(jthread thread, + jint depth, + jobject* value_ptr) { + return functions->GetLocalInstance(this, thread, depth, value_ptr); + } + + jvmtiError GetLocalInt(jthread thread, + jint depth, + jint slot, + jint* value_ptr) { + return functions->GetLocalInt(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalLong(jthread thread, + jint depth, + jint slot, + jlong* value_ptr) { + return functions->GetLocalLong(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat* value_ptr) { + return functions->GetLocalFloat(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble* value_ptr) { + return functions->GetLocalDouble(this, thread, depth, slot, value_ptr); + } + + jvmtiError SetLocalObject(jthread thread, + jint depth, + jint slot, + jobject value) { + return functions->SetLocalObject(this, thread, depth, slot, value); + } + + jvmtiError SetLocalInt(jthread thread, + jint depth, + jint slot, + jint value) { + return functions->SetLocalInt(this, thread, depth, slot, value); + } + + jvmtiError SetLocalLong(jthread thread, + jint depth, + jint slot, + jlong value) { + return functions->SetLocalLong(this, thread, depth, slot, value); + } + + jvmtiError SetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat value) { + return functions->SetLocalFloat(this, thread, depth, slot, value); + } + + jvmtiError SetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble value) { + return functions->SetLocalDouble(this, thread, depth, slot, value); + } + + jvmtiError SetBreakpoint(jmethodID method, + jlocation location) { + return functions->SetBreakpoint(this, method, location); + } + + jvmtiError ClearBreakpoint(jmethodID method, + jlocation location) { + return functions->ClearBreakpoint(this, method, location); + } + + jvmtiError SetFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->SetFieldAccessWatch(this, klass, field); + } + + jvmtiError ClearFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldAccessWatch(this, klass, field); + } + + jvmtiError SetFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->SetFieldModificationWatch(this, klass, field); + } + + jvmtiError ClearFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldModificationWatch(this, klass, field); + } + + jvmtiError GetLoadedClasses(jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassLoaderClasses(jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetClassLoaderClasses(this, initiating_loader, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassSignature(jclass klass, + char** signature_ptr, + char** generic_ptr) { + return functions->GetClassSignature(this, klass, signature_ptr, generic_ptr); + } + + jvmtiError GetClassStatus(jclass klass, + jint* status_ptr) { + return functions->GetClassStatus(this, klass, status_ptr); + } + + jvmtiError GetSourceFileName(jclass klass, + char** source_name_ptr) { + return functions->GetSourceFileName(this, klass, source_name_ptr); + } + + jvmtiError GetClassModifiers(jclass klass, + jint* modifiers_ptr) { + return functions->GetClassModifiers(this, klass, modifiers_ptr); + } + + jvmtiError GetClassMethods(jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr) { + return functions->GetClassMethods(this, klass, method_count_ptr, methods_ptr); + } + + jvmtiError GetClassFields(jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr) { + return functions->GetClassFields(this, klass, field_count_ptr, fields_ptr); + } + + jvmtiError GetImplementedInterfaces(jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr) { + return functions->GetImplementedInterfaces(this, klass, interface_count_ptr, interfaces_ptr); + } + + jvmtiError GetClassVersionNumbers(jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr) { + return functions->GetClassVersionNumbers(this, klass, minor_version_ptr, major_version_ptr); + } + + jvmtiError GetConstantPool(jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr) { + return functions->GetConstantPool(this, klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr); + } + + jvmtiError IsInterface(jclass klass, + jboolean* is_interface_ptr) { + return functions->IsInterface(this, klass, is_interface_ptr); + } + + jvmtiError IsArrayClass(jclass klass, + jboolean* is_array_class_ptr) { + return functions->IsArrayClass(this, klass, is_array_class_ptr); + } + + jvmtiError IsModifiableClass(jclass klass, + jboolean* is_modifiable_class_ptr) { + return functions->IsModifiableClass(this, klass, is_modifiable_class_ptr); + } + + jvmtiError GetClassLoader(jclass klass, + jobject* classloader_ptr) { + return functions->GetClassLoader(this, klass, classloader_ptr); + } + + jvmtiError GetSourceDebugExtension(jclass klass, + char** source_debug_extension_ptr) { + return functions->GetSourceDebugExtension(this, klass, source_debug_extension_ptr); + } + + jvmtiError RetransformClasses(jint class_count, + const jclass* classes) { + return functions->RetransformClasses(this, class_count, classes); + } + + jvmtiError RedefineClasses(jint class_count, + const jvmtiClassDefinition* class_definitions) { + return functions->RedefineClasses(this, class_count, class_definitions); + } + + jvmtiError GetObjectSize(jobject object, + jlong* size_ptr) { + return functions->GetObjectSize(this, object, size_ptr); + } + + jvmtiError GetObjectHashCode(jobject object, + jint* hash_code_ptr) { + return functions->GetObjectHashCode(this, object, hash_code_ptr); + } + + jvmtiError GetObjectMonitorUsage(jobject object, + jvmtiMonitorUsage* info_ptr) { + return functions->GetObjectMonitorUsage(this, object, info_ptr); + } + + jvmtiError GetFieldName(jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetFieldName(this, klass, field, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetFieldDeclaringClass(jclass klass, + jfieldID field, + jclass* declaring_class_ptr) { + return functions->GetFieldDeclaringClass(this, klass, field, declaring_class_ptr); + } + + jvmtiError GetFieldModifiers(jclass klass, + jfieldID field, + jint* modifiers_ptr) { + return functions->GetFieldModifiers(this, klass, field, modifiers_ptr); + } + + jvmtiError IsFieldSynthetic(jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr) { + return functions->IsFieldSynthetic(this, klass, field, is_synthetic_ptr); + } + + jvmtiError GetMethodName(jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetMethodName(this, method, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetMethodDeclaringClass(jmethodID method, + jclass* declaring_class_ptr) { + return functions->GetMethodDeclaringClass(this, method, declaring_class_ptr); + } + + jvmtiError GetMethodModifiers(jmethodID method, + jint* modifiers_ptr) { + return functions->GetMethodModifiers(this, method, modifiers_ptr); + } + + jvmtiError GetMaxLocals(jmethodID method, + jint* max_ptr) { + return functions->GetMaxLocals(this, method, max_ptr); + } + + jvmtiError GetArgumentsSize(jmethodID method, + jint* size_ptr) { + return functions->GetArgumentsSize(this, method, size_ptr); + } + + jvmtiError GetLineNumberTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr) { + return functions->GetLineNumberTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetMethodLocation(jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr) { + return functions->GetMethodLocation(this, method, start_location_ptr, end_location_ptr); + } + + jvmtiError GetLocalVariableTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr) { + return functions->GetLocalVariableTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetBytecodes(jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr) { + return functions->GetBytecodes(this, method, bytecode_count_ptr, bytecodes_ptr); + } + + jvmtiError IsMethodNative(jmethodID method, + jboolean* is_native_ptr) { + return functions->IsMethodNative(this, method, is_native_ptr); + } + + jvmtiError IsMethodSynthetic(jmethodID method, + jboolean* is_synthetic_ptr) { + return functions->IsMethodSynthetic(this, method, is_synthetic_ptr); + } + + jvmtiError IsMethodObsolete(jmethodID method, + jboolean* is_obsolete_ptr) { + return functions->IsMethodObsolete(this, method, is_obsolete_ptr); + } + + jvmtiError SetNativeMethodPrefix(const char* prefix) { + return functions->SetNativeMethodPrefix(this, prefix); + } + + jvmtiError SetNativeMethodPrefixes(jint prefix_count, + char** prefixes) { + return functions->SetNativeMethodPrefixes(this, prefix_count, prefixes); + } + + jvmtiError CreateRawMonitor(const char* name, + jrawMonitorID* monitor_ptr) { + return functions->CreateRawMonitor(this, name, monitor_ptr); + } + + jvmtiError DestroyRawMonitor(jrawMonitorID monitor) { + return functions->DestroyRawMonitor(this, monitor); + } + + jvmtiError RawMonitorEnter(jrawMonitorID monitor) { + return functions->RawMonitorEnter(this, monitor); + } + + jvmtiError RawMonitorExit(jrawMonitorID monitor) { + return functions->RawMonitorExit(this, monitor); + } + + jvmtiError RawMonitorWait(jrawMonitorID monitor, + jlong millis) { + return functions->RawMonitorWait(this, monitor, millis); + } + + jvmtiError RawMonitorNotify(jrawMonitorID monitor) { + return functions->RawMonitorNotify(this, monitor); + } + + jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) { + return functions->RawMonitorNotifyAll(this, monitor); + } + + jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) { + return functions->SetJNIFunctionTable(this, function_table); + } + + jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) { + return functions->GetJNIFunctionTable(this, function_table); + } + + jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + return functions->SetEventCallbacks(this, callbacks, size_of_callbacks); + } + + jvmtiError SetEventNotificationMode(jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...) { + return functions->SetEventNotificationMode(this, mode, event_type, event_thread); + } + + jvmtiError GenerateEvents(jvmtiEvent event_type) { + return functions->GenerateEvents(this, event_type); + } + + jvmtiError GetExtensionFunctions(jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions) { + return functions->GetExtensionFunctions(this, extension_count_ptr, extensions); + } + + jvmtiError GetExtensionEvents(jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions) { + return functions->GetExtensionEvents(this, extension_count_ptr, extensions); + } + + jvmtiError SetExtensionEventCallback(jint extension_event_index, + jvmtiExtensionEvent callback) { + return functions->SetExtensionEventCallback(this, extension_event_index, callback); + } + + jvmtiError GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetPotentialCapabilities(this, capabilities_ptr); + } + + jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->AddCapabilities(this, capabilities_ptr); + } + + jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->RelinquishCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetCurrentThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) { + return functions->GetCurrentThreadCpuTime(this, nanos_ptr); + } + + jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetThreadCpuTime(jthread thread, + jlong* nanos_ptr) { + return functions->GetThreadCpuTime(this, thread, nanos_ptr); + } + + jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetTimerInfo(this, info_ptr); + } + + jvmtiError GetTime(jlong* nanos_ptr) { + return functions->GetTime(this, nanos_ptr); + } + + jvmtiError GetAvailableProcessors(jint* processor_count_ptr) { + return functions->GetAvailableProcessors(this, processor_count_ptr); + } + + jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) { + return functions->AddToBootstrapClassLoaderSearch(this, segment); + } + + jvmtiError AddToSystemClassLoaderSearch(const char* segment) { + return functions->AddToSystemClassLoaderSearch(this, segment); + } + + jvmtiError GetSystemProperties(jint* count_ptr, + char*** property_ptr) { + return functions->GetSystemProperties(this, count_ptr, property_ptr); + } + + jvmtiError GetSystemProperty(const char* property, + char** value_ptr) { + return functions->GetSystemProperty(this, property, value_ptr); + } + + jvmtiError SetSystemProperty(const char* property, + const char* value) { + return functions->SetSystemProperty(this, property, value); + } + + jvmtiError GetPhase(jvmtiPhase* phase_ptr) { + return functions->GetPhase(this, phase_ptr); + } + + jvmtiError DisposeEnvironment() { + return functions->DisposeEnvironment(this); + } + + jvmtiError SetEnvironmentLocalStorage(const void* data) { + return functions->SetEnvironmentLocalStorage(this, data); + } + + jvmtiError GetEnvironmentLocalStorage(void** data_ptr) { + return functions->GetEnvironmentLocalStorage(this, data_ptr); + } + + jvmtiError GetVersionNumber(jint* version_ptr) { + return functions->GetVersionNumber(this, version_ptr); + } + + jvmtiError GetErrorName(jvmtiError error, + char** name_ptr) { + return functions->GetErrorName(this, error, name_ptr); + } + + jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag, + jboolean value) { + return functions->SetVerboseFlag(this, flag, value); + } + + jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { + return functions->GetJLocationFormat(this, format_ptr); + } + +#endif /* __cplusplus */ +}; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVA_JVMTI_H_ */ + diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jni.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jni.h new file mode 100644 index 00000000..91944ac5 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jni.h @@ -0,0 +1,1973 @@ +/* + * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); + + /* Module Features */ + + jobject (JNICALL *GetModule) + (JNIEnv* env, jclass clazz); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + + /* Module Features */ + + jobject GetModule(jclass clazz) { + return functions->GetModule(this, clazz); + } + +#endif /* __cplusplus */ +}; + +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 +#define JNI_VERSION_9 0x00090000 +#define JNI_VERSION_10 0x000a0000 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jni_md.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jni_md.h new file mode 100644 index 00000000..c4d8a316 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jni_md.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif +#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #ifdef ARM + #define JNIEXPORT __attribute__((externally_visible,visibility("default"))) + #define JNIIMPORT __attribute__((externally_visible,visibility("default"))) + #else + #define JNIEXPORT __attribute__((visibility("default"))) + #define JNIIMPORT __attribute__((visibility("default"))) + #endif +#else + #define JNIEXPORT + #define JNIIMPORT +#endif + +#define JNICALL + +typedef int jint; +#ifdef _LP64 +typedef long jlong; +#else +typedef long long jlong; +#endif + +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jvmti.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jvmti.h new file mode 100644 index 00000000..23cc0e83 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosArm64/jvmti.h @@ -0,0 +1,2624 @@ +/* + * * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + + /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */ + + /* Include file for the Java(tm) Virtual Machine Tool Interface */ + +#ifndef _JAVA_JVMTI_H_ +#define _JAVA_JVMTI_H_ + +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + JVMTI_VERSION_1 = 0x30010000, + JVMTI_VERSION_1_0 = 0x30010000, + JVMTI_VERSION_1_1 = 0x30010100, + JVMTI_VERSION_1_2 = 0x30010200, + JVMTI_VERSION_9 = 0x30090000, + JVMTI_VERSION_11 = 0x300B0000, + + JVMTI_VERSION = 0x30000000 + (11 * 0x10000) + (0 * 0x100) + 0 /* version: 11.0.0 */ +}; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved); + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM* vm, char* options, void* reserved); + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm); + + /* Forward declaration of the environment */ + +struct _jvmtiEnv; + +struct jvmtiInterface_1_; + +#ifdef __cplusplus +typedef _jvmtiEnv jvmtiEnv; +#else +typedef const struct jvmtiInterface_1_ *jvmtiEnv; +#endif /* __cplusplus */ + +/* Derived Base Types */ + +typedef jobject jthread; +typedef jobject jthreadGroup; +typedef jlong jlocation; +struct _jrawMonitorID; +typedef struct _jrawMonitorID *jrawMonitorID; +typedef struct JNINativeInterface_ jniNativeInterface; + + /* Constants */ + + + /* Thread State Flags */ + +enum { + JVMTI_THREAD_STATE_ALIVE = 0x0001, + JVMTI_THREAD_STATE_TERMINATED = 0x0002, + JVMTI_THREAD_STATE_RUNNABLE = 0x0004, + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400, + JVMTI_THREAD_STATE_WAITING = 0x0080, + JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010, + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020, + JVMTI_THREAD_STATE_SLEEPING = 0x0040, + JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100, + JVMTI_THREAD_STATE_PARKED = 0x0200, + JVMTI_THREAD_STATE_SUSPENDED = 0x100000, + JVMTI_THREAD_STATE_INTERRUPTED = 0x200000, + JVMTI_THREAD_STATE_IN_NATIVE = 0x400000, + JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000, + JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000, + JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000 +}; + + /* java.lang.Thread.State Conversion Masks */ + +enum { + JVMTI_JAVA_LANG_THREAD_STATE_MASK = JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT, + JVMTI_JAVA_LANG_THREAD_STATE_NEW = 0, + JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED = JVMTI_THREAD_STATE_TERMINATED, + JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE, + JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, + JVMTI_JAVA_LANG_THREAD_STATE_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY, + JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +}; + + /* Thread Priority Constants */ + +enum { + JVMTI_THREAD_MIN_PRIORITY = 1, + JVMTI_THREAD_NORM_PRIORITY = 5, + JVMTI_THREAD_MAX_PRIORITY = 10 +}; + + /* Heap Filter Flags */ + +enum { + JVMTI_HEAP_FILTER_TAGGED = 0x4, + JVMTI_HEAP_FILTER_UNTAGGED = 0x8, + JVMTI_HEAP_FILTER_CLASS_TAGGED = 0x10, + JVMTI_HEAP_FILTER_CLASS_UNTAGGED = 0x20 +}; + + /* Heap Visit Control Flags */ + +enum { + JVMTI_VISIT_OBJECTS = 0x100, + JVMTI_VISIT_ABORT = 0x8000 +}; + + /* Heap Reference Enumeration */ + +typedef enum { + JVMTI_HEAP_REFERENCE_CLASS = 1, + JVMTI_HEAP_REFERENCE_FIELD = 2, + JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_HEAP_REFERENCE_CLASS_LOADER = 4, + JVMTI_HEAP_REFERENCE_SIGNERS = 5, + JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_HEAP_REFERENCE_INTERFACE = 7, + JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8, + JVMTI_HEAP_REFERENCE_CONSTANT_POOL = 9, + JVMTI_HEAP_REFERENCE_SUPERCLASS = 10, + JVMTI_HEAP_REFERENCE_JNI_GLOBAL = 21, + JVMTI_HEAP_REFERENCE_SYSTEM_CLASS = 22, + JVMTI_HEAP_REFERENCE_MONITOR = 23, + JVMTI_HEAP_REFERENCE_STACK_LOCAL = 24, + JVMTI_HEAP_REFERENCE_JNI_LOCAL = 25, + JVMTI_HEAP_REFERENCE_THREAD = 26, + JVMTI_HEAP_REFERENCE_OTHER = 27 +} jvmtiHeapReferenceKind; + + /* Primitive Type Enumeration */ + +typedef enum { + JVMTI_PRIMITIVE_TYPE_BOOLEAN = 90, + JVMTI_PRIMITIVE_TYPE_BYTE = 66, + JVMTI_PRIMITIVE_TYPE_CHAR = 67, + JVMTI_PRIMITIVE_TYPE_SHORT = 83, + JVMTI_PRIMITIVE_TYPE_INT = 73, + JVMTI_PRIMITIVE_TYPE_LONG = 74, + JVMTI_PRIMITIVE_TYPE_FLOAT = 70, + JVMTI_PRIMITIVE_TYPE_DOUBLE = 68 +} jvmtiPrimitiveType; + + /* Heap Object Filter Enumeration */ + +typedef enum { + JVMTI_HEAP_OBJECT_TAGGED = 1, + JVMTI_HEAP_OBJECT_UNTAGGED = 2, + JVMTI_HEAP_OBJECT_EITHER = 3 +} jvmtiHeapObjectFilter; + + /* Heap Root Kind Enumeration */ + +typedef enum { + JVMTI_HEAP_ROOT_JNI_GLOBAL = 1, + JVMTI_HEAP_ROOT_SYSTEM_CLASS = 2, + JVMTI_HEAP_ROOT_MONITOR = 3, + JVMTI_HEAP_ROOT_STACK_LOCAL = 4, + JVMTI_HEAP_ROOT_JNI_LOCAL = 5, + JVMTI_HEAP_ROOT_THREAD = 6, + JVMTI_HEAP_ROOT_OTHER = 7 +} jvmtiHeapRootKind; + + /* Object Reference Enumeration */ + +typedef enum { + JVMTI_REFERENCE_CLASS = 1, + JVMTI_REFERENCE_FIELD = 2, + JVMTI_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_REFERENCE_CLASS_LOADER = 4, + JVMTI_REFERENCE_SIGNERS = 5, + JVMTI_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_REFERENCE_INTERFACE = 7, + JVMTI_REFERENCE_STATIC_FIELD = 8, + JVMTI_REFERENCE_CONSTANT_POOL = 9 +} jvmtiObjectReferenceKind; + + /* Iteration Control Enumeration */ + +typedef enum { + JVMTI_ITERATION_CONTINUE = 1, + JVMTI_ITERATION_IGNORE = 2, + JVMTI_ITERATION_ABORT = 0 +} jvmtiIterationControl; + + /* Class Status Flags */ + +enum { + JVMTI_CLASS_STATUS_VERIFIED = 1, + JVMTI_CLASS_STATUS_PREPARED = 2, + JVMTI_CLASS_STATUS_INITIALIZED = 4, + JVMTI_CLASS_STATUS_ERROR = 8, + JVMTI_CLASS_STATUS_ARRAY = 16, + JVMTI_CLASS_STATUS_PRIMITIVE = 32 +}; + + /* Event Enable/Disable */ + +typedef enum { + JVMTI_ENABLE = 1, + JVMTI_DISABLE = 0 +} jvmtiEventMode; + + /* Extension Function/Event Parameter Types */ + +typedef enum { + JVMTI_TYPE_JBYTE = 101, + JVMTI_TYPE_JCHAR = 102, + JVMTI_TYPE_JSHORT = 103, + JVMTI_TYPE_JINT = 104, + JVMTI_TYPE_JLONG = 105, + JVMTI_TYPE_JFLOAT = 106, + JVMTI_TYPE_JDOUBLE = 107, + JVMTI_TYPE_JBOOLEAN = 108, + JVMTI_TYPE_JOBJECT = 109, + JVMTI_TYPE_JTHREAD = 110, + JVMTI_TYPE_JCLASS = 111, + JVMTI_TYPE_JVALUE = 112, + JVMTI_TYPE_JFIELDID = 113, + JVMTI_TYPE_JMETHODID = 114, + JVMTI_TYPE_CCHAR = 115, + JVMTI_TYPE_CVOID = 116, + JVMTI_TYPE_JNIENV = 117 +} jvmtiParamTypes; + + /* Extension Function/Event Parameter Kinds */ + +typedef enum { + JVMTI_KIND_IN = 91, + JVMTI_KIND_IN_PTR = 92, + JVMTI_KIND_IN_BUF = 93, + JVMTI_KIND_ALLOC_BUF = 94, + JVMTI_KIND_ALLOC_ALLOC_BUF = 95, + JVMTI_KIND_OUT = 96, + JVMTI_KIND_OUT_BUF = 97 +} jvmtiParamKind; + + /* Timer Kinds */ + +typedef enum { + JVMTI_TIMER_USER_CPU = 30, + JVMTI_TIMER_TOTAL_CPU = 31, + JVMTI_TIMER_ELAPSED = 32 +} jvmtiTimerKind; + + /* Phases of execution */ + +typedef enum { + JVMTI_PHASE_ONLOAD = 1, + JVMTI_PHASE_PRIMORDIAL = 2, + JVMTI_PHASE_START = 6, + JVMTI_PHASE_LIVE = 4, + JVMTI_PHASE_DEAD = 8 +} jvmtiPhase; + + /* Version Interface Types */ + +enum { + JVMTI_VERSION_INTERFACE_JNI = 0x00000000, + JVMTI_VERSION_INTERFACE_JVMTI = 0x30000000 +}; + + /* Version Masks */ + +enum { + JVMTI_VERSION_MASK_INTERFACE_TYPE = 0x70000000, + JVMTI_VERSION_MASK_MAJOR = 0x0FFF0000, + JVMTI_VERSION_MASK_MINOR = 0x0000FF00, + JVMTI_VERSION_MASK_MICRO = 0x000000FF +}; + + /* Version Shifts */ + +enum { + JVMTI_VERSION_SHIFT_MAJOR = 16, + JVMTI_VERSION_SHIFT_MINOR = 8, + JVMTI_VERSION_SHIFT_MICRO = 0 +}; + + /* Verbose Flag Enumeration */ + +typedef enum { + JVMTI_VERBOSE_OTHER = 0, + JVMTI_VERBOSE_GC = 1, + JVMTI_VERBOSE_CLASS = 2, + JVMTI_VERBOSE_JNI = 4 +} jvmtiVerboseFlag; + + /* JLocation Format Enumeration */ + +typedef enum { + JVMTI_JLOCATION_JVMBCI = 1, + JVMTI_JLOCATION_MACHINEPC = 2, + JVMTI_JLOCATION_OTHER = 0 +} jvmtiJlocationFormat; + + /* Resource Exhaustion Flags */ + +enum { + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 0x0001, + JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 0x0002, + JVMTI_RESOURCE_EXHAUSTED_THREADS = 0x0004 +}; + + /* Errors */ + +typedef enum { + JVMTI_ERROR_NONE = 0, + JVMTI_ERROR_INVALID_THREAD = 10, + JVMTI_ERROR_INVALID_THREAD_GROUP = 11, + JVMTI_ERROR_INVALID_PRIORITY = 12, + JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13, + JVMTI_ERROR_THREAD_SUSPENDED = 14, + JVMTI_ERROR_THREAD_NOT_ALIVE = 15, + JVMTI_ERROR_INVALID_OBJECT = 20, + JVMTI_ERROR_INVALID_CLASS = 21, + JVMTI_ERROR_CLASS_NOT_PREPARED = 22, + JVMTI_ERROR_INVALID_METHODID = 23, + JVMTI_ERROR_INVALID_LOCATION = 24, + JVMTI_ERROR_INVALID_FIELDID = 25, + JVMTI_ERROR_INVALID_MODULE = 26, + JVMTI_ERROR_NO_MORE_FRAMES = 31, + JVMTI_ERROR_OPAQUE_FRAME = 32, + JVMTI_ERROR_TYPE_MISMATCH = 34, + JVMTI_ERROR_INVALID_SLOT = 35, + JVMTI_ERROR_DUPLICATE = 40, + JVMTI_ERROR_NOT_FOUND = 41, + JVMTI_ERROR_INVALID_MONITOR = 50, + JVMTI_ERROR_NOT_MONITOR_OWNER = 51, + JVMTI_ERROR_INTERRUPT = 52, + JVMTI_ERROR_INVALID_CLASS_FORMAT = 60, + JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION = 61, + JVMTI_ERROR_FAILS_VERIFICATION = 62, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED = 63, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED = 64, + JVMTI_ERROR_INVALID_TYPESTATE = 65, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED = 66, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED = 67, + JVMTI_ERROR_UNSUPPORTED_VERSION = 68, + JVMTI_ERROR_NAMES_DONT_MATCH = 69, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED = 72, + JVMTI_ERROR_UNMODIFIABLE_CLASS = 79, + JVMTI_ERROR_UNMODIFIABLE_MODULE = 80, + JVMTI_ERROR_NOT_AVAILABLE = 98, + JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99, + JVMTI_ERROR_NULL_POINTER = 100, + JVMTI_ERROR_ABSENT_INFORMATION = 101, + JVMTI_ERROR_INVALID_EVENT_TYPE = 102, + JVMTI_ERROR_ILLEGAL_ARGUMENT = 103, + JVMTI_ERROR_NATIVE_METHOD = 104, + JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED = 106, + JVMTI_ERROR_OUT_OF_MEMORY = 110, + JVMTI_ERROR_ACCESS_DENIED = 111, + JVMTI_ERROR_WRONG_PHASE = 112, + JVMTI_ERROR_INTERNAL = 113, + JVMTI_ERROR_UNATTACHED_THREAD = 115, + JVMTI_ERROR_INVALID_ENVIRONMENT = 116, + JVMTI_ERROR_MAX = 116 +} jvmtiError; + + /* Event IDs */ + +typedef enum { + JVMTI_MIN_EVENT_TYPE_VAL = 50, + JVMTI_EVENT_VM_INIT = 50, + JVMTI_EVENT_VM_DEATH = 51, + JVMTI_EVENT_THREAD_START = 52, + JVMTI_EVENT_THREAD_END = 53, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54, + JVMTI_EVENT_CLASS_LOAD = 55, + JVMTI_EVENT_CLASS_PREPARE = 56, + JVMTI_EVENT_VM_START = 57, + JVMTI_EVENT_EXCEPTION = 58, + JVMTI_EVENT_EXCEPTION_CATCH = 59, + JVMTI_EVENT_SINGLE_STEP = 60, + JVMTI_EVENT_FRAME_POP = 61, + JVMTI_EVENT_BREAKPOINT = 62, + JVMTI_EVENT_FIELD_ACCESS = 63, + JVMTI_EVENT_FIELD_MODIFICATION = 64, + JVMTI_EVENT_METHOD_ENTRY = 65, + JVMTI_EVENT_METHOD_EXIT = 66, + JVMTI_EVENT_NATIVE_METHOD_BIND = 67, + JVMTI_EVENT_COMPILED_METHOD_LOAD = 68, + JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69, + JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70, + JVMTI_EVENT_DATA_DUMP_REQUEST = 71, + JVMTI_EVENT_MONITOR_WAIT = 73, + JVMTI_EVENT_MONITOR_WAITED = 74, + JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75, + JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76, + JVMTI_EVENT_RESOURCE_EXHAUSTED = 80, + JVMTI_EVENT_GARBAGE_COLLECTION_START = 81, + JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82, + JVMTI_EVENT_OBJECT_FREE = 83, + JVMTI_EVENT_VM_OBJECT_ALLOC = 84, + JVMTI_EVENT_SAMPLED_OBJECT_ALLOC = 86, + JVMTI_MAX_EVENT_TYPE_VAL = 86 +} jvmtiEvent; + + + /* Pre-Declarations */ +struct _jvmtiThreadInfo; +typedef struct _jvmtiThreadInfo jvmtiThreadInfo; +struct _jvmtiMonitorStackDepthInfo; +typedef struct _jvmtiMonitorStackDepthInfo jvmtiMonitorStackDepthInfo; +struct _jvmtiThreadGroupInfo; +typedef struct _jvmtiThreadGroupInfo jvmtiThreadGroupInfo; +struct _jvmtiFrameInfo; +typedef struct _jvmtiFrameInfo jvmtiFrameInfo; +struct _jvmtiStackInfo; +typedef struct _jvmtiStackInfo jvmtiStackInfo; +struct _jvmtiHeapReferenceInfoField; +typedef struct _jvmtiHeapReferenceInfoField jvmtiHeapReferenceInfoField; +struct _jvmtiHeapReferenceInfoArray; +typedef struct _jvmtiHeapReferenceInfoArray jvmtiHeapReferenceInfoArray; +struct _jvmtiHeapReferenceInfoConstantPool; +typedef struct _jvmtiHeapReferenceInfoConstantPool jvmtiHeapReferenceInfoConstantPool; +struct _jvmtiHeapReferenceInfoStackLocal; +typedef struct _jvmtiHeapReferenceInfoStackLocal jvmtiHeapReferenceInfoStackLocal; +struct _jvmtiHeapReferenceInfoJniLocal; +typedef struct _jvmtiHeapReferenceInfoJniLocal jvmtiHeapReferenceInfoJniLocal; +struct _jvmtiHeapReferenceInfoReserved; +typedef struct _jvmtiHeapReferenceInfoReserved jvmtiHeapReferenceInfoReserved; +union _jvmtiHeapReferenceInfo; +typedef union _jvmtiHeapReferenceInfo jvmtiHeapReferenceInfo; +struct _jvmtiHeapCallbacks; +typedef struct _jvmtiHeapCallbacks jvmtiHeapCallbacks; +struct _jvmtiClassDefinition; +typedef struct _jvmtiClassDefinition jvmtiClassDefinition; +struct _jvmtiMonitorUsage; +typedef struct _jvmtiMonitorUsage jvmtiMonitorUsage; +struct _jvmtiLineNumberEntry; +typedef struct _jvmtiLineNumberEntry jvmtiLineNumberEntry; +struct _jvmtiLocalVariableEntry; +typedef struct _jvmtiLocalVariableEntry jvmtiLocalVariableEntry; +struct _jvmtiParamInfo; +typedef struct _jvmtiParamInfo jvmtiParamInfo; +struct _jvmtiExtensionFunctionInfo; +typedef struct _jvmtiExtensionFunctionInfo jvmtiExtensionFunctionInfo; +struct _jvmtiExtensionEventInfo; +typedef struct _jvmtiExtensionEventInfo jvmtiExtensionEventInfo; +struct _jvmtiTimerInfo; +typedef struct _jvmtiTimerInfo jvmtiTimerInfo; +struct _jvmtiAddrLocationMap; +typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap; + + /* Function Types */ + +typedef void (JNICALL *jvmtiStartFunction) + (jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg); + +typedef jint (JNICALL *jvmtiHeapIterationCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiHeapReferenceCallback) + (jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiPrimitiveFieldCallback) + (jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong object_class_tag, jlong* object_tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data); + +typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data); + +typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data); + +typedef jint (JNICALL *jvmtiReservedCallback) + (); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback) + (jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void* user_data); + +typedef jvmtiError (JNICALL *jvmtiExtensionFunction) + (jvmtiEnv* jvmti_env, ...); + +typedef void (JNICALL *jvmtiExtensionEvent) + (jvmtiEnv* jvmti_env, ...); + + + /* Structure Types */ +struct _jvmtiThreadInfo { + char* name; + jint priority; + jboolean is_daemon; + jthreadGroup thread_group; + jobject context_class_loader; +}; +struct _jvmtiMonitorStackDepthInfo { + jobject monitor; + jint stack_depth; +}; +struct _jvmtiThreadGroupInfo { + jthreadGroup parent; + char* name; + jint max_priority; + jboolean is_daemon; +}; +struct _jvmtiFrameInfo { + jmethodID method; + jlocation location; +}; +struct _jvmtiStackInfo { + jthread thread; + jint state; + jvmtiFrameInfo* frame_buffer; + jint frame_count; +}; +struct _jvmtiHeapReferenceInfoField { + jint index; +}; +struct _jvmtiHeapReferenceInfoArray { + jint index; +}; +struct _jvmtiHeapReferenceInfoConstantPool { + jint index; +}; +struct _jvmtiHeapReferenceInfoStackLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; + jlocation location; + jint slot; +}; +struct _jvmtiHeapReferenceInfoJniLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; +}; +struct _jvmtiHeapReferenceInfoReserved { + jlong reserved1; + jlong reserved2; + jlong reserved3; + jlong reserved4; + jlong reserved5; + jlong reserved6; + jlong reserved7; + jlong reserved8; +}; +union _jvmtiHeapReferenceInfo { + jvmtiHeapReferenceInfoField field; + jvmtiHeapReferenceInfoArray array; + jvmtiHeapReferenceInfoConstantPool constant_pool; + jvmtiHeapReferenceInfoStackLocal stack_local; + jvmtiHeapReferenceInfoJniLocal jni_local; + jvmtiHeapReferenceInfoReserved other; +}; +struct _jvmtiHeapCallbacks { + jvmtiHeapIterationCallback heap_iteration_callback; + jvmtiHeapReferenceCallback heap_reference_callback; + jvmtiPrimitiveFieldCallback primitive_field_callback; + jvmtiArrayPrimitiveValueCallback array_primitive_value_callback; + jvmtiStringPrimitiveValueCallback string_primitive_value_callback; + jvmtiReservedCallback reserved5; + jvmtiReservedCallback reserved6; + jvmtiReservedCallback reserved7; + jvmtiReservedCallback reserved8; + jvmtiReservedCallback reserved9; + jvmtiReservedCallback reserved10; + jvmtiReservedCallback reserved11; + jvmtiReservedCallback reserved12; + jvmtiReservedCallback reserved13; + jvmtiReservedCallback reserved14; + jvmtiReservedCallback reserved15; +}; +struct _jvmtiClassDefinition { + jclass klass; + jint class_byte_count; + const unsigned char* class_bytes; +}; +struct _jvmtiMonitorUsage { + jthread owner; + jint entry_count; + jint waiter_count; + jthread* waiters; + jint notify_waiter_count; + jthread* notify_waiters; +}; +struct _jvmtiLineNumberEntry { + jlocation start_location; + jint line_number; +}; +struct _jvmtiLocalVariableEntry { + jlocation start_location; + jint length; + char* name; + char* signature; + char* generic_signature; + jint slot; +}; +struct _jvmtiParamInfo { + char* name; + jvmtiParamKind kind; + jvmtiParamTypes base_type; + jboolean null_ok; +}; +struct _jvmtiExtensionFunctionInfo { + jvmtiExtensionFunction func; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; + jint error_count; + jvmtiError* errors; +}; +struct _jvmtiExtensionEventInfo { + jint extension_event_index; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; +}; +struct _jvmtiTimerInfo { + jlong max_value; + jboolean may_skip_forward; + jboolean may_skip_backward; + jvmtiTimerKind kind; + jlong reserved1; + jlong reserved2; +}; +struct _jvmtiAddrLocationMap { + const void* start_address; + jlocation location; +}; + +typedef struct { + unsigned int can_tag_objects : 1; + unsigned int can_generate_field_modification_events : 1; + unsigned int can_generate_field_access_events : 1; + unsigned int can_get_bytecodes : 1; + unsigned int can_get_synthetic_attribute : 1; + unsigned int can_get_owned_monitor_info : 1; + unsigned int can_get_current_contended_monitor : 1; + unsigned int can_get_monitor_info : 1; + unsigned int can_pop_frame : 1; + unsigned int can_redefine_classes : 1; + unsigned int can_signal_thread : 1; + unsigned int can_get_source_file_name : 1; + unsigned int can_get_line_numbers : 1; + unsigned int can_get_source_debug_extension : 1; + unsigned int can_access_local_variables : 1; + unsigned int can_maintain_original_method_order : 1; + unsigned int can_generate_single_step_events : 1; + unsigned int can_generate_exception_events : 1; + unsigned int can_generate_frame_pop_events : 1; + unsigned int can_generate_breakpoint_events : 1; + unsigned int can_suspend : 1; + unsigned int can_redefine_any_class : 1; + unsigned int can_get_current_thread_cpu_time : 1; + unsigned int can_get_thread_cpu_time : 1; + unsigned int can_generate_method_entry_events : 1; + unsigned int can_generate_method_exit_events : 1; + unsigned int can_generate_all_class_hook_events : 1; + unsigned int can_generate_compiled_method_load_events : 1; + unsigned int can_generate_monitor_events : 1; + unsigned int can_generate_vm_object_alloc_events : 1; + unsigned int can_generate_native_method_bind_events : 1; + unsigned int can_generate_garbage_collection_events : 1; + unsigned int can_generate_object_free_events : 1; + unsigned int can_force_early_return : 1; + unsigned int can_get_owned_monitor_stack_depth_info : 1; + unsigned int can_get_constant_pool : 1; + unsigned int can_set_native_method_prefix : 1; + unsigned int can_retransform_classes : 1; + unsigned int can_retransform_any_class : 1; + unsigned int can_generate_resource_exhaustion_heap_events : 1; + unsigned int can_generate_resource_exhaustion_threads_events : 1; + unsigned int can_generate_early_vmstart : 1; + unsigned int can_generate_early_class_hook_events : 1; + unsigned int can_generate_sampled_object_alloc_events : 1; + unsigned int : 4; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; +} jvmtiCapabilities; + + + /* Event Definitions */ + +typedef void (JNICALL *jvmtiEventReserved)(void); + + +typedef void (JNICALL *jvmtiEventBreakpoint) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventClassFileLoadHook) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data); + +typedef void (JNICALL *jvmtiEventClassLoad) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventClassPrepare) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventCompiledMethodLoad) + (jvmtiEnv *jvmti_env, + jmethodID method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info); + +typedef void (JNICALL *jvmtiEventCompiledMethodUnload) + (jvmtiEnv *jvmti_env, + jmethodID method, + const void* code_addr); + +typedef void (JNICALL *jvmtiEventDataDumpRequest) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventDynamicCodeGenerated) + (jvmtiEnv *jvmti_env, + const char* name, + const void* address, + jint length); + +typedef void (JNICALL *jvmtiEventException) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception, + jmethodID catch_method, + jlocation catch_location); + +typedef void (JNICALL *jvmtiEventExceptionCatch) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception); + +typedef void (JNICALL *jvmtiEventFieldAccess) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field); + +typedef void (JNICALL *jvmtiEventFieldModification) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field, + char signature_type, + jvalue new_value); + +typedef void (JNICALL *jvmtiEventFramePop) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception); + +typedef void (JNICALL *jvmtiEventGarbageCollectionFinish) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventGarbageCollectionStart) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventMethodEntry) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method); + +typedef void (JNICALL *jvmtiEventMethodExit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception, + jvalue return_value); + +typedef void (JNICALL *jvmtiEventMonitorContendedEnter) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorContendedEntered) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorWait) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jlong timeout); + +typedef void (JNICALL *jvmtiEventMonitorWaited) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jboolean timed_out); + +typedef void (JNICALL *jvmtiEventNativeMethodBind) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + void* address, + void** new_address_ptr); + +typedef void (JNICALL *jvmtiEventObjectFree) + (jvmtiEnv *jvmti_env, + jlong tag); + +typedef void (JNICALL *jvmtiEventResourceExhausted) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint flags, + const void* reserved, + const char* description); + +typedef void (JNICALL *jvmtiEventSampledObjectAlloc) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jclass object_klass, + jlong size); + +typedef void (JNICALL *jvmtiEventSingleStep) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventThreadEnd) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventThreadStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMDeath) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + +typedef void (JNICALL *jvmtiEventVMInit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMObjectAlloc) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jclass object_klass, + jlong size); + +typedef void (JNICALL *jvmtiEventVMStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + + /* Event Callback Structure */ + +typedef struct { + /* 50 : VM Initialization Event */ + jvmtiEventVMInit VMInit; + /* 51 : VM Death Event */ + jvmtiEventVMDeath VMDeath; + /* 52 : Thread Start */ + jvmtiEventThreadStart ThreadStart; + /* 53 : Thread End */ + jvmtiEventThreadEnd ThreadEnd; + /* 54 : Class File Load Hook */ + jvmtiEventClassFileLoadHook ClassFileLoadHook; + /* 55 : Class Load */ + jvmtiEventClassLoad ClassLoad; + /* 56 : Class Prepare */ + jvmtiEventClassPrepare ClassPrepare; + /* 57 : VM Start Event */ + jvmtiEventVMStart VMStart; + /* 58 : Exception */ + jvmtiEventException Exception; + /* 59 : Exception Catch */ + jvmtiEventExceptionCatch ExceptionCatch; + /* 60 : Single Step */ + jvmtiEventSingleStep SingleStep; + /* 61 : Frame Pop */ + jvmtiEventFramePop FramePop; + /* 62 : Breakpoint */ + jvmtiEventBreakpoint Breakpoint; + /* 63 : Field Access */ + jvmtiEventFieldAccess FieldAccess; + /* 64 : Field Modification */ + jvmtiEventFieldModification FieldModification; + /* 65 : Method Entry */ + jvmtiEventMethodEntry MethodEntry; + /* 66 : Method Exit */ + jvmtiEventMethodExit MethodExit; + /* 67 : Native Method Bind */ + jvmtiEventNativeMethodBind NativeMethodBind; + /* 68 : Compiled Method Load */ + jvmtiEventCompiledMethodLoad CompiledMethodLoad; + /* 69 : Compiled Method Unload */ + jvmtiEventCompiledMethodUnload CompiledMethodUnload; + /* 70 : Dynamic Code Generated */ + jvmtiEventDynamicCodeGenerated DynamicCodeGenerated; + /* 71 : Data Dump Request */ + jvmtiEventDataDumpRequest DataDumpRequest; + /* 72 */ + jvmtiEventReserved reserved72; + /* 73 : Monitor Wait */ + jvmtiEventMonitorWait MonitorWait; + /* 74 : Monitor Waited */ + jvmtiEventMonitorWaited MonitorWaited; + /* 75 : Monitor Contended Enter */ + jvmtiEventMonitorContendedEnter MonitorContendedEnter; + /* 76 : Monitor Contended Entered */ + jvmtiEventMonitorContendedEntered MonitorContendedEntered; + /* 77 */ + jvmtiEventReserved reserved77; + /* 78 */ + jvmtiEventReserved reserved78; + /* 79 */ + jvmtiEventReserved reserved79; + /* 80 : Resource Exhausted */ + jvmtiEventResourceExhausted ResourceExhausted; + /* 81 : Garbage Collection Start */ + jvmtiEventGarbageCollectionStart GarbageCollectionStart; + /* 82 : Garbage Collection Finish */ + jvmtiEventGarbageCollectionFinish GarbageCollectionFinish; + /* 83 : Object Free */ + jvmtiEventObjectFree ObjectFree; + /* 84 : VM Object Allocation */ + jvmtiEventVMObjectAlloc VMObjectAlloc; + /* 85 */ + jvmtiEventReserved reserved85; + /* 86 : Sampled Object Allocation */ + jvmtiEventSampledObjectAlloc SampledObjectAlloc; +} jvmtiEventCallbacks; + + + /* Function Interface */ + +typedef struct jvmtiInterface_1_ { + + /* 1 : RESERVED */ + void *reserved1; + + /* 2 : Set Event Notification Mode */ + jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env, + jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...); + + /* 3 : Get All Modules */ + jvmtiError (JNICALL *GetAllModules) (jvmtiEnv* env, + jint* module_count_ptr, + jobject** modules_ptr); + + /* 4 : Get All Threads */ + jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env, + jint* threads_count_ptr, + jthread** threads_ptr); + + /* 5 : Suspend Thread */ + jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env, + jthread thread); + + /* 6 : Resume Thread */ + jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env, + jthread thread); + + /* 7 : Stop Thread */ + jvmtiError (JNICALL *StopThread) (jvmtiEnv* env, + jthread thread, + jobject exception); + + /* 8 : Interrupt Thread */ + jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env, + jthread thread); + + /* 9 : Get Thread Info */ + jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env, + jthread thread, + jvmtiThreadInfo* info_ptr); + + /* 10 : Get Owned Monitor Info */ + jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env, + jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr); + + /* 11 : Get Current Contended Monitor */ + jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env, + jthread thread, + jobject* monitor_ptr); + + /* 12 : Run Agent Thread */ + jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env, + jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority); + + /* 13 : Get Top Thread Groups */ + jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 14 : Get Thread Group Info */ + jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + /* 15 : Get Thread Group Children */ + jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 16 : Get Frame Count */ + jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env, + jthread thread, + jint* count_ptr); + + /* 17 : Get Thread State */ + jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env, + jthread thread, + jint* thread_state_ptr); + + /* 18 : Get Current Thread */ + jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env, + jthread* thread_ptr); + + /* 19 : Get Frame Location */ + jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env, + jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr); + + /* 20 : Notify Frame Pop */ + jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env, + jthread thread, + jint depth); + + /* 21 : Get Local Variable - Object */ + jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject* value_ptr); + + /* 22 : Get Local Variable - Int */ + jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint* value_ptr); + + /* 23 : Get Local Variable - Long */ + jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong* value_ptr); + + /* 24 : Get Local Variable - Float */ + jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat* value_ptr); + + /* 25 : Get Local Variable - Double */ + jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble* value_ptr); + + /* 26 : Set Local Variable - Object */ + jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject value); + + /* 27 : Set Local Variable - Int */ + jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint value); + + /* 28 : Set Local Variable - Long */ + jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong value); + + /* 29 : Set Local Variable - Float */ + jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat value); + + /* 30 : Set Local Variable - Double */ + jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble value); + + /* 31 : Create Raw Monitor */ + jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env, + const char* name, + jrawMonitorID* monitor_ptr); + + /* 32 : Destroy Raw Monitor */ + jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 33 : Raw Monitor Enter */ + jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 34 : Raw Monitor Exit */ + jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 35 : Raw Monitor Wait */ + jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env, + jrawMonitorID monitor, + jlong millis); + + /* 36 : Raw Monitor Notify */ + jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 37 : Raw Monitor Notify All */ + jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 38 : Set Breakpoint */ + jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 39 : Clear Breakpoint */ + jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 40 : Get Named Module */ + jvmtiError (JNICALL *GetNamedModule) (jvmtiEnv* env, + jobject class_loader, + const char* package_name, + jobject* module_ptr); + + /* 41 : Set Field Access Watch */ + jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 42 : Clear Field Access Watch */ + jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 43 : Set Field Modification Watch */ + jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 44 : Clear Field Modification Watch */ + jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 45 : Is Modifiable Class */ + jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_modifiable_class_ptr); + + /* 46 : Allocate */ + jvmtiError (JNICALL *Allocate) (jvmtiEnv* env, + jlong size, + unsigned char** mem_ptr); + + /* 47 : Deallocate */ + jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env, + unsigned char* mem); + + /* 48 : Get Class Signature */ + jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env, + jclass klass, + char** signature_ptr, + char** generic_ptr); + + /* 49 : Get Class Status */ + jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env, + jclass klass, + jint* status_ptr); + + /* 50 : Get Source File Name */ + jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env, + jclass klass, + char** source_name_ptr); + + /* 51 : Get Class Modifiers */ + jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env, + jclass klass, + jint* modifiers_ptr); + + /* 52 : Get Class Methods */ + jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env, + jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr); + + /* 53 : Get Class Fields */ + jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env, + jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr); + + /* 54 : Get Implemented Interfaces */ + jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env, + jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr); + + /* 55 : Is Interface */ + jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env, + jclass klass, + jboolean* is_interface_ptr); + + /* 56 : Is Array Class */ + jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_array_class_ptr); + + /* 57 : Get Class Loader */ + jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env, + jclass klass, + jobject* classloader_ptr); + + /* 58 : Get Object Hash Code */ + jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env, + jobject object, + jint* hash_code_ptr); + + /* 59 : Get Object Monitor Usage */ + jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env, + jobject object, + jvmtiMonitorUsage* info_ptr); + + /* 60 : Get Field Name (and Signature) */ + jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env, + jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 61 : Get Field Declaring Class */ + jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jclass* declaring_class_ptr); + + /* 62 : Get Field Modifiers */ + jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jint* modifiers_ptr); + + /* 63 : Is Field Synthetic */ + jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr); + + /* 64 : Get Method Name (and Signature) */ + jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env, + jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 65 : Get Method Declaring Class */ + jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env, + jmethodID method, + jclass* declaring_class_ptr); + + /* 66 : Get Method Modifiers */ + jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env, + jmethodID method, + jint* modifiers_ptr); + + /* 67 : RESERVED */ + void *reserved67; + + /* 68 : Get Max Locals */ + jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env, + jmethodID method, + jint* max_ptr); + + /* 69 : Get Arguments Size */ + jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env, + jmethodID method, + jint* size_ptr); + + /* 70 : Get Line Number Table */ + jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr); + + /* 71 : Get Method Location */ + jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env, + jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr); + + /* 72 : Get Local Variable Table */ + jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr); + + /* 73 : Set Native Method Prefix */ + jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env, + const char* prefix); + + /* 74 : Set Native Method Prefixes */ + jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env, + jint prefix_count, + char** prefixes); + + /* 75 : Get Bytecodes */ + jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env, + jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr); + + /* 76 : Is Method Native */ + jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env, + jmethodID method, + jboolean* is_native_ptr); + + /* 77 : Is Method Synthetic */ + jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env, + jmethodID method, + jboolean* is_synthetic_ptr); + + /* 78 : Get Loaded Classes */ + jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 79 : Get Classloader Classes */ + jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 80 : Pop Frame */ + jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env, + jthread thread); + + /* 81 : Force Early Return - Object */ + jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env, + jthread thread, + jobject value); + + /* 82 : Force Early Return - Int */ + jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env, + jthread thread, + jint value); + + /* 83 : Force Early Return - Long */ + jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env, + jthread thread, + jlong value); + + /* 84 : Force Early Return - Float */ + jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env, + jthread thread, + jfloat value); + + /* 85 : Force Early Return - Double */ + jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env, + jthread thread, + jdouble value); + + /* 86 : Force Early Return - Void */ + jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env, + jthread thread); + + /* 87 : Redefine Classes */ + jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env, + jint class_count, + const jvmtiClassDefinition* class_definitions); + + /* 88 : Get Version Number */ + jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env, + jint* version_ptr); + + /* 89 : Get Capabilities */ + jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 90 : Get Source Debug Extension */ + jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env, + jclass klass, + char** source_debug_extension_ptr); + + /* 91 : Is Method Obsolete */ + jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env, + jmethodID method, + jboolean* is_obsolete_ptr); + + /* 92 : Suspend Thread List */ + jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 93 : Resume Thread List */ + jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 94 : Add Module Reads */ + jvmtiError (JNICALL *AddModuleReads) (jvmtiEnv* env, + jobject module, + jobject to_module); + + /* 95 : Add Module Exports */ + jvmtiError (JNICALL *AddModuleExports) (jvmtiEnv* env, + jobject module, + const char* pkg_name, + jobject to_module); + + /* 96 : Add Module Opens */ + jvmtiError (JNICALL *AddModuleOpens) (jvmtiEnv* env, + jobject module, + const char* pkg_name, + jobject to_module); + + /* 97 : Add Module Uses */ + jvmtiError (JNICALL *AddModuleUses) (jvmtiEnv* env, + jobject module, + jclass service); + + /* 98 : Add Module Provides */ + jvmtiError (JNICALL *AddModuleProvides) (jvmtiEnv* env, + jobject module, + jclass service, + jclass impl_class); + + /* 99 : Is Modifiable Module */ + jvmtiError (JNICALL *IsModifiableModule) (jvmtiEnv* env, + jobject module, + jboolean* is_modifiable_module_ptr); + + /* 100 : Get All Stack Traces */ + jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr); + + /* 101 : Get Thread List Stack Traces */ + jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env, + jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr); + + /* 102 : Get Thread Local Storage */ + jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + void** data_ptr); + + /* 103 : Set Thread Local Storage */ + jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + const void* data); + + /* 104 : Get Stack Trace */ + jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env, + jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr); + + /* 105 : RESERVED */ + void *reserved105; + + /* 106 : Get Tag */ + jvmtiError (JNICALL *GetTag) (jvmtiEnv* env, + jobject object, + jlong* tag_ptr); + + /* 107 : Set Tag */ + jvmtiError (JNICALL *SetTag) (jvmtiEnv* env, + jobject object, + jlong tag); + + /* 108 : Force Garbage Collection */ + jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env); + + /* 109 : Iterate Over Objects Reachable From Object */ + jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env, + jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data); + + /* 110 : Iterate Over Reachable Objects */ + jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env, + jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data); + + /* 111 : Iterate Over Heap */ + jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 112 : Iterate Over Instances Of Class */ + jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env, + jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 113 : RESERVED */ + void *reserved113; + + /* 114 : Get Objects With Tags */ + jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env, + jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr); + + /* 115 : Follow References */ + jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 116 : Iterate Through Heap */ + jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 117 : RESERVED */ + void *reserved117; + + /* 118 : RESERVED */ + void *reserved118; + + /* 119 : RESERVED */ + void *reserved119; + + /* 120 : Set JNI Function Table */ + jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env, + const jniNativeInterface* function_table); + + /* 121 : Get JNI Function Table */ + jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env, + jniNativeInterface** function_table); + + /* 122 : Set Event Callbacks */ + jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks); + + /* 123 : Generate Events */ + jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env, + jvmtiEvent event_type); + + /* 124 : Get Extension Functions */ + jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions); + + /* 125 : Get Extension Events */ + jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions); + + /* 126 : Set Extension Event Callback */ + jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback); + + /* 127 : Dispose Environment */ + jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env); + + /* 128 : Get Error Name */ + jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env, + jvmtiError error, + char** name_ptr); + + /* 129 : Get JLocation Format */ + jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env, + jvmtiJlocationFormat* format_ptr); + + /* 130 : Get System Properties */ + jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env, + jint* count_ptr, + char*** property_ptr); + + /* 131 : Get System Property */ + jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env, + const char* property, + char** value_ptr); + + /* 132 : Set System Property */ + jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env, + const char* property, + const char* value_ptr); + + /* 133 : Get Phase */ + jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env, + jvmtiPhase* phase_ptr); + + /* 134 : Get Current Thread CPU Timer Information */ + jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 135 : Get Current Thread CPU Time */ + jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 136 : Get Thread CPU Timer Information */ + jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 137 : Get Thread CPU Time */ + jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env, + jthread thread, + jlong* nanos_ptr); + + /* 138 : Get Timer Information */ + jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 139 : Get Time */ + jvmtiError (JNICALL *GetTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 140 : Get Potential Capabilities */ + jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 141 : RESERVED */ + void *reserved141; + + /* 142 : Add Capabilities */ + jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 143 : Relinquish Capabilities */ + jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 144 : Get Available Processors */ + jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env, + jint* processor_count_ptr); + + /* 145 : Get Class Version Numbers */ + jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env, + jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr); + + /* 146 : Get Constant Pool */ + jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env, + jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr); + + /* 147 : Get Environment Local Storage */ + jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env, + void** data_ptr); + + /* 148 : Set Environment Local Storage */ + jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env, + const void* data); + + /* 149 : Add To Bootstrap Class Loader Search */ + jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 150 : Set Verbose Flag */ + jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env, + jvmtiVerboseFlag flag, + jboolean value); + + /* 151 : Add To System Class Loader Search */ + jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 152 : Retransform Classes */ + jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env, + jint class_count, + const jclass* classes); + + /* 153 : Get Owned Monitor Stack Depth Info */ + jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env, + jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr); + + /* 154 : Get Object Size */ + jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env, + jobject object, + jlong* size_ptr); + + /* 155 : Get Local Instance */ + jvmtiError (JNICALL *GetLocalInstance) (jvmtiEnv* env, + jthread thread, + jint depth, + jobject* value_ptr); + + /* 156 : Set Heap Sampling Interval */ + jvmtiError (JNICALL *SetHeapSamplingInterval) (jvmtiEnv* env, + jint sampling_interval); + +} jvmtiInterface_1; + +struct _jvmtiEnv { + const struct jvmtiInterface_1_ *functions; +#ifdef __cplusplus + + + jvmtiError Allocate(jlong size, + unsigned char** mem_ptr) { + return functions->Allocate(this, size, mem_ptr); + } + + jvmtiError Deallocate(unsigned char* mem) { + return functions->Deallocate(this, mem); + } + + jvmtiError GetThreadState(jthread thread, + jint* thread_state_ptr) { + return functions->GetThreadState(this, thread, thread_state_ptr); + } + + jvmtiError GetCurrentThread(jthread* thread_ptr) { + return functions->GetCurrentThread(this, thread_ptr); + } + + jvmtiError GetAllThreads(jint* threads_count_ptr, + jthread** threads_ptr) { + return functions->GetAllThreads(this, threads_count_ptr, threads_ptr); + } + + jvmtiError SuspendThread(jthread thread) { + return functions->SuspendThread(this, thread); + } + + jvmtiError SuspendThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->SuspendThreadList(this, request_count, request_list, results); + } + + jvmtiError ResumeThread(jthread thread) { + return functions->ResumeThread(this, thread); + } + + jvmtiError ResumeThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->ResumeThreadList(this, request_count, request_list, results); + } + + jvmtiError StopThread(jthread thread, + jobject exception) { + return functions->StopThread(this, thread, exception); + } + + jvmtiError InterruptThread(jthread thread) { + return functions->InterruptThread(this, thread); + } + + jvmtiError GetThreadInfo(jthread thread, + jvmtiThreadInfo* info_ptr) { + return functions->GetThreadInfo(this, thread, info_ptr); + } + + jvmtiError GetOwnedMonitorInfo(jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr) { + return functions->GetOwnedMonitorInfo(this, thread, owned_monitor_count_ptr, owned_monitors_ptr); + } + + jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + return functions->GetOwnedMonitorStackDepthInfo(this, thread, monitor_info_count_ptr, monitor_info_ptr); + } + + jvmtiError GetCurrentContendedMonitor(jthread thread, + jobject* monitor_ptr) { + return functions->GetCurrentContendedMonitor(this, thread, monitor_ptr); + } + + jvmtiError RunAgentThread(jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority) { + return functions->RunAgentThread(this, thread, proc, arg, priority); + } + + jvmtiError SetThreadLocalStorage(jthread thread, + const void* data) { + return functions->SetThreadLocalStorage(this, thread, data); + } + + jvmtiError GetThreadLocalStorage(jthread thread, + void** data_ptr) { + return functions->GetThreadLocalStorage(this, thread, data_ptr); + } + + jvmtiError GetTopThreadGroups(jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetTopThreadGroups(this, group_count_ptr, groups_ptr); + } + + jvmtiError GetThreadGroupInfo(jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + return functions->GetThreadGroupInfo(this, group, info_ptr); + } + + jvmtiError GetThreadGroupChildren(jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetThreadGroupChildren(this, group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr); + } + + jvmtiError GetStackTrace(jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr) { + return functions->GetStackTrace(this, thread, start_depth, max_frame_count, frame_buffer, count_ptr); + } + + jvmtiError GetAllStackTraces(jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr) { + return functions->GetAllStackTraces(this, max_frame_count, stack_info_ptr, thread_count_ptr); + } + + jvmtiError GetThreadListStackTraces(jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr) { + return functions->GetThreadListStackTraces(this, thread_count, thread_list, max_frame_count, stack_info_ptr); + } + + jvmtiError GetFrameCount(jthread thread, + jint* count_ptr) { + return functions->GetFrameCount(this, thread, count_ptr); + } + + jvmtiError PopFrame(jthread thread) { + return functions->PopFrame(this, thread); + } + + jvmtiError GetFrameLocation(jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr) { + return functions->GetFrameLocation(this, thread, depth, method_ptr, location_ptr); + } + + jvmtiError NotifyFramePop(jthread thread, + jint depth) { + return functions->NotifyFramePop(this, thread, depth); + } + + jvmtiError ForceEarlyReturnObject(jthread thread, + jobject value) { + return functions->ForceEarlyReturnObject(this, thread, value); + } + + jvmtiError ForceEarlyReturnInt(jthread thread, + jint value) { + return functions->ForceEarlyReturnInt(this, thread, value); + } + + jvmtiError ForceEarlyReturnLong(jthread thread, + jlong value) { + return functions->ForceEarlyReturnLong(this, thread, value); + } + + jvmtiError ForceEarlyReturnFloat(jthread thread, + jfloat value) { + return functions->ForceEarlyReturnFloat(this, thread, value); + } + + jvmtiError ForceEarlyReturnDouble(jthread thread, + jdouble value) { + return functions->ForceEarlyReturnDouble(this, thread, value); + } + + jvmtiError ForceEarlyReturnVoid(jthread thread) { + return functions->ForceEarlyReturnVoid(this, thread); + } + + jvmtiError FollowReferences(jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->FollowReferences(this, heap_filter, klass, initial_object, callbacks, user_data); + } + + jvmtiError IterateThroughHeap(jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->IterateThroughHeap(this, heap_filter, klass, callbacks, user_data); + } + + jvmtiError GetTag(jobject object, + jlong* tag_ptr) { + return functions->GetTag(this, object, tag_ptr); + } + + jvmtiError SetTag(jobject object, + jlong tag) { + return functions->SetTag(this, object, tag); + } + + jvmtiError GetObjectsWithTags(jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr) { + return functions->GetObjectsWithTags(this, tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr); + } + + jvmtiError ForceGarbageCollection() { + return functions->ForceGarbageCollection(this); + } + + jvmtiError IterateOverObjectsReachableFromObject(jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data) { + return functions->IterateOverObjectsReachableFromObject(this, object, object_reference_callback, user_data); + } + + jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data) { + return functions->IterateOverReachableObjects(this, heap_root_callback, stack_ref_callback, object_ref_callback, user_data); + } + + jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverHeap(this, object_filter, heap_object_callback, user_data); + } + + jvmtiError IterateOverInstancesOfClass(jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverInstancesOfClass(this, klass, object_filter, heap_object_callback, user_data); + } + + jvmtiError GetLocalObject(jthread thread, + jint depth, + jint slot, + jobject* value_ptr) { + return functions->GetLocalObject(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalInstance(jthread thread, + jint depth, + jobject* value_ptr) { + return functions->GetLocalInstance(this, thread, depth, value_ptr); + } + + jvmtiError GetLocalInt(jthread thread, + jint depth, + jint slot, + jint* value_ptr) { + return functions->GetLocalInt(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalLong(jthread thread, + jint depth, + jint slot, + jlong* value_ptr) { + return functions->GetLocalLong(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat* value_ptr) { + return functions->GetLocalFloat(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble* value_ptr) { + return functions->GetLocalDouble(this, thread, depth, slot, value_ptr); + } + + jvmtiError SetLocalObject(jthread thread, + jint depth, + jint slot, + jobject value) { + return functions->SetLocalObject(this, thread, depth, slot, value); + } + + jvmtiError SetLocalInt(jthread thread, + jint depth, + jint slot, + jint value) { + return functions->SetLocalInt(this, thread, depth, slot, value); + } + + jvmtiError SetLocalLong(jthread thread, + jint depth, + jint slot, + jlong value) { + return functions->SetLocalLong(this, thread, depth, slot, value); + } + + jvmtiError SetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat value) { + return functions->SetLocalFloat(this, thread, depth, slot, value); + } + + jvmtiError SetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble value) { + return functions->SetLocalDouble(this, thread, depth, slot, value); + } + + jvmtiError SetBreakpoint(jmethodID method, + jlocation location) { + return functions->SetBreakpoint(this, method, location); + } + + jvmtiError ClearBreakpoint(jmethodID method, + jlocation location) { + return functions->ClearBreakpoint(this, method, location); + } + + jvmtiError SetFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->SetFieldAccessWatch(this, klass, field); + } + + jvmtiError ClearFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldAccessWatch(this, klass, field); + } + + jvmtiError SetFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->SetFieldModificationWatch(this, klass, field); + } + + jvmtiError ClearFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldModificationWatch(this, klass, field); + } + + jvmtiError GetAllModules(jint* module_count_ptr, + jobject** modules_ptr) { + return functions->GetAllModules(this, module_count_ptr, modules_ptr); + } + + jvmtiError GetNamedModule(jobject class_loader, + const char* package_name, + jobject* module_ptr) { + return functions->GetNamedModule(this, class_loader, package_name, module_ptr); + } + + jvmtiError AddModuleReads(jobject module, + jobject to_module) { + return functions->AddModuleReads(this, module, to_module); + } + + jvmtiError AddModuleExports(jobject module, + const char* pkg_name, + jobject to_module) { + return functions->AddModuleExports(this, module, pkg_name, to_module); + } + + jvmtiError AddModuleOpens(jobject module, + const char* pkg_name, + jobject to_module) { + return functions->AddModuleOpens(this, module, pkg_name, to_module); + } + + jvmtiError AddModuleUses(jobject module, + jclass service) { + return functions->AddModuleUses(this, module, service); + } + + jvmtiError AddModuleProvides(jobject module, + jclass service, + jclass impl_class) { + return functions->AddModuleProvides(this, module, service, impl_class); + } + + jvmtiError IsModifiableModule(jobject module, + jboolean* is_modifiable_module_ptr) { + return functions->IsModifiableModule(this, module, is_modifiable_module_ptr); + } + + jvmtiError GetLoadedClasses(jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassLoaderClasses(jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetClassLoaderClasses(this, initiating_loader, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassSignature(jclass klass, + char** signature_ptr, + char** generic_ptr) { + return functions->GetClassSignature(this, klass, signature_ptr, generic_ptr); + } + + jvmtiError GetClassStatus(jclass klass, + jint* status_ptr) { + return functions->GetClassStatus(this, klass, status_ptr); + } + + jvmtiError GetSourceFileName(jclass klass, + char** source_name_ptr) { + return functions->GetSourceFileName(this, klass, source_name_ptr); + } + + jvmtiError GetClassModifiers(jclass klass, + jint* modifiers_ptr) { + return functions->GetClassModifiers(this, klass, modifiers_ptr); + } + + jvmtiError GetClassMethods(jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr) { + return functions->GetClassMethods(this, klass, method_count_ptr, methods_ptr); + } + + jvmtiError GetClassFields(jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr) { + return functions->GetClassFields(this, klass, field_count_ptr, fields_ptr); + } + + jvmtiError GetImplementedInterfaces(jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr) { + return functions->GetImplementedInterfaces(this, klass, interface_count_ptr, interfaces_ptr); + } + + jvmtiError GetClassVersionNumbers(jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr) { + return functions->GetClassVersionNumbers(this, klass, minor_version_ptr, major_version_ptr); + } + + jvmtiError GetConstantPool(jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr) { + return functions->GetConstantPool(this, klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr); + } + + jvmtiError IsInterface(jclass klass, + jboolean* is_interface_ptr) { + return functions->IsInterface(this, klass, is_interface_ptr); + } + + jvmtiError IsArrayClass(jclass klass, + jboolean* is_array_class_ptr) { + return functions->IsArrayClass(this, klass, is_array_class_ptr); + } + + jvmtiError IsModifiableClass(jclass klass, + jboolean* is_modifiable_class_ptr) { + return functions->IsModifiableClass(this, klass, is_modifiable_class_ptr); + } + + jvmtiError GetClassLoader(jclass klass, + jobject* classloader_ptr) { + return functions->GetClassLoader(this, klass, classloader_ptr); + } + + jvmtiError GetSourceDebugExtension(jclass klass, + char** source_debug_extension_ptr) { + return functions->GetSourceDebugExtension(this, klass, source_debug_extension_ptr); + } + + jvmtiError RetransformClasses(jint class_count, + const jclass* classes) { + return functions->RetransformClasses(this, class_count, classes); + } + + jvmtiError RedefineClasses(jint class_count, + const jvmtiClassDefinition* class_definitions) { + return functions->RedefineClasses(this, class_count, class_definitions); + } + + jvmtiError GetObjectSize(jobject object, + jlong* size_ptr) { + return functions->GetObjectSize(this, object, size_ptr); + } + + jvmtiError GetObjectHashCode(jobject object, + jint* hash_code_ptr) { + return functions->GetObjectHashCode(this, object, hash_code_ptr); + } + + jvmtiError GetObjectMonitorUsage(jobject object, + jvmtiMonitorUsage* info_ptr) { + return functions->GetObjectMonitorUsage(this, object, info_ptr); + } + + jvmtiError GetFieldName(jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetFieldName(this, klass, field, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetFieldDeclaringClass(jclass klass, + jfieldID field, + jclass* declaring_class_ptr) { + return functions->GetFieldDeclaringClass(this, klass, field, declaring_class_ptr); + } + + jvmtiError GetFieldModifiers(jclass klass, + jfieldID field, + jint* modifiers_ptr) { + return functions->GetFieldModifiers(this, klass, field, modifiers_ptr); + } + + jvmtiError IsFieldSynthetic(jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr) { + return functions->IsFieldSynthetic(this, klass, field, is_synthetic_ptr); + } + + jvmtiError GetMethodName(jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetMethodName(this, method, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetMethodDeclaringClass(jmethodID method, + jclass* declaring_class_ptr) { + return functions->GetMethodDeclaringClass(this, method, declaring_class_ptr); + } + + jvmtiError GetMethodModifiers(jmethodID method, + jint* modifiers_ptr) { + return functions->GetMethodModifiers(this, method, modifiers_ptr); + } + + jvmtiError GetMaxLocals(jmethodID method, + jint* max_ptr) { + return functions->GetMaxLocals(this, method, max_ptr); + } + + jvmtiError GetArgumentsSize(jmethodID method, + jint* size_ptr) { + return functions->GetArgumentsSize(this, method, size_ptr); + } + + jvmtiError GetLineNumberTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr) { + return functions->GetLineNumberTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetMethodLocation(jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr) { + return functions->GetMethodLocation(this, method, start_location_ptr, end_location_ptr); + } + + jvmtiError GetLocalVariableTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr) { + return functions->GetLocalVariableTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetBytecodes(jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr) { + return functions->GetBytecodes(this, method, bytecode_count_ptr, bytecodes_ptr); + } + + jvmtiError IsMethodNative(jmethodID method, + jboolean* is_native_ptr) { + return functions->IsMethodNative(this, method, is_native_ptr); + } + + jvmtiError IsMethodSynthetic(jmethodID method, + jboolean* is_synthetic_ptr) { + return functions->IsMethodSynthetic(this, method, is_synthetic_ptr); + } + + jvmtiError IsMethodObsolete(jmethodID method, + jboolean* is_obsolete_ptr) { + return functions->IsMethodObsolete(this, method, is_obsolete_ptr); + } + + jvmtiError SetNativeMethodPrefix(const char* prefix) { + return functions->SetNativeMethodPrefix(this, prefix); + } + + jvmtiError SetNativeMethodPrefixes(jint prefix_count, + char** prefixes) { + return functions->SetNativeMethodPrefixes(this, prefix_count, prefixes); + } + + jvmtiError CreateRawMonitor(const char* name, + jrawMonitorID* monitor_ptr) { + return functions->CreateRawMonitor(this, name, monitor_ptr); + } + + jvmtiError DestroyRawMonitor(jrawMonitorID monitor) { + return functions->DestroyRawMonitor(this, monitor); + } + + jvmtiError RawMonitorEnter(jrawMonitorID monitor) { + return functions->RawMonitorEnter(this, monitor); + } + + jvmtiError RawMonitorExit(jrawMonitorID monitor) { + return functions->RawMonitorExit(this, monitor); + } + + jvmtiError RawMonitorWait(jrawMonitorID monitor, + jlong millis) { + return functions->RawMonitorWait(this, monitor, millis); + } + + jvmtiError RawMonitorNotify(jrawMonitorID monitor) { + return functions->RawMonitorNotify(this, monitor); + } + + jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) { + return functions->RawMonitorNotifyAll(this, monitor); + } + + jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) { + return functions->SetJNIFunctionTable(this, function_table); + } + + jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) { + return functions->GetJNIFunctionTable(this, function_table); + } + + jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + return functions->SetEventCallbacks(this, callbacks, size_of_callbacks); + } + + jvmtiError SetEventNotificationMode(jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...) { + return functions->SetEventNotificationMode(this, mode, event_type, event_thread); + } + + jvmtiError GenerateEvents(jvmtiEvent event_type) { + return functions->GenerateEvents(this, event_type); + } + + jvmtiError GetExtensionFunctions(jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions) { + return functions->GetExtensionFunctions(this, extension_count_ptr, extensions); + } + + jvmtiError GetExtensionEvents(jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions) { + return functions->GetExtensionEvents(this, extension_count_ptr, extensions); + } + + jvmtiError SetExtensionEventCallback(jint extension_event_index, + jvmtiExtensionEvent callback) { + return functions->SetExtensionEventCallback(this, extension_event_index, callback); + } + + jvmtiError GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetPotentialCapabilities(this, capabilities_ptr); + } + + jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->AddCapabilities(this, capabilities_ptr); + } + + jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->RelinquishCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetCurrentThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) { + return functions->GetCurrentThreadCpuTime(this, nanos_ptr); + } + + jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetThreadCpuTime(jthread thread, + jlong* nanos_ptr) { + return functions->GetThreadCpuTime(this, thread, nanos_ptr); + } + + jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetTimerInfo(this, info_ptr); + } + + jvmtiError GetTime(jlong* nanos_ptr) { + return functions->GetTime(this, nanos_ptr); + } + + jvmtiError GetAvailableProcessors(jint* processor_count_ptr) { + return functions->GetAvailableProcessors(this, processor_count_ptr); + } + + jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) { + return functions->AddToBootstrapClassLoaderSearch(this, segment); + } + + jvmtiError AddToSystemClassLoaderSearch(const char* segment) { + return functions->AddToSystemClassLoaderSearch(this, segment); + } + + jvmtiError GetSystemProperties(jint* count_ptr, + char*** property_ptr) { + return functions->GetSystemProperties(this, count_ptr, property_ptr); + } + + jvmtiError GetSystemProperty(const char* property, + char** value_ptr) { + return functions->GetSystemProperty(this, property, value_ptr); + } + + jvmtiError SetSystemProperty(const char* property, + const char* value_ptr) { + return functions->SetSystemProperty(this, property, value_ptr); + } + + jvmtiError GetPhase(jvmtiPhase* phase_ptr) { + return functions->GetPhase(this, phase_ptr); + } + + jvmtiError DisposeEnvironment() { + return functions->DisposeEnvironment(this); + } + + jvmtiError SetEnvironmentLocalStorage(const void* data) { + return functions->SetEnvironmentLocalStorage(this, data); + } + + jvmtiError GetEnvironmentLocalStorage(void** data_ptr) { + return functions->GetEnvironmentLocalStorage(this, data_ptr); + } + + jvmtiError GetVersionNumber(jint* version_ptr) { + return functions->GetVersionNumber(this, version_ptr); + } + + jvmtiError GetErrorName(jvmtiError error, + char** name_ptr) { + return functions->GetErrorName(this, error, name_ptr); + } + + jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag, + jboolean value) { + return functions->SetVerboseFlag(this, flag, value); + } + + jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { + return functions->GetJLocationFormat(this, format_ptr); + } + + jvmtiError SetHeapSamplingInterval(jint sampling_interval) { + return functions->SetHeapSamplingInterval(this, sampling_interval); + } + +#endif /* __cplusplus */ +}; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVA_JVMTI_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jni.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jni.h new file mode 100644 index 00000000..0ffe244b --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jni.h @@ -0,0 +1,1960 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + +#endif /* __cplusplus */ +}; + +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jni_md.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jni_md.h new file mode 100644 index 00000000..d2b83307 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jni_md.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#define JNIEXPORT __attribute__((visibility("default"))) +#define JNIIMPORT __attribute__((visibility("default"))) +#define JNICALL + +typedef int jint; +#ifdef _LP64 /* 64-bit */ +typedef long jlong; +#else +typedef long long jlong; +#endif + +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jvmti.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jvmti.h new file mode 100644 index 00000000..74243f54 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/macosX64/jvmti.h @@ -0,0 +1,2534 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + + /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */ + + + /* Include file for the Java(tm) Virtual Machine Tool Interface */ + +#ifndef _JAVA_JVMTI_H_ +#define _JAVA_JVMTI_H_ + +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + JVMTI_VERSION_1 = 0x30010000, + JVMTI_VERSION_1_0 = 0x30010000, + JVMTI_VERSION_1_1 = 0x30010100, + JVMTI_VERSION_1_2 = 0x30010200, + + JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (2 * 0x100) + 1 /* version: 1.2.1 */ +}; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved); + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM* vm, char* options, void* reserved); + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm); + + /* Forward declaration of the environment */ + +struct _jvmtiEnv; + +struct jvmtiInterface_1_; + +#ifdef __cplusplus +typedef _jvmtiEnv jvmtiEnv; +#else +typedef const struct jvmtiInterface_1_ *jvmtiEnv; +#endif /* __cplusplus */ + +/* Derived Base Types */ + +typedef jobject jthread; +typedef jobject jthreadGroup; +typedef jlong jlocation; +struct _jrawMonitorID; +typedef struct _jrawMonitorID *jrawMonitorID; +typedef struct JNINativeInterface_ jniNativeInterface; + + /* Constants */ + + + /* Thread State Flags */ + +enum { + JVMTI_THREAD_STATE_ALIVE = 0x0001, + JVMTI_THREAD_STATE_TERMINATED = 0x0002, + JVMTI_THREAD_STATE_RUNNABLE = 0x0004, + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400, + JVMTI_THREAD_STATE_WAITING = 0x0080, + JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010, + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020, + JVMTI_THREAD_STATE_SLEEPING = 0x0040, + JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100, + JVMTI_THREAD_STATE_PARKED = 0x0200, + JVMTI_THREAD_STATE_SUSPENDED = 0x100000, + JVMTI_THREAD_STATE_INTERRUPTED = 0x200000, + JVMTI_THREAD_STATE_IN_NATIVE = 0x400000, + JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000, + JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000, + JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000 +}; + + /* java.lang.Thread.State Conversion Masks */ + +enum { + JVMTI_JAVA_LANG_THREAD_STATE_MASK = JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT, + JVMTI_JAVA_LANG_THREAD_STATE_NEW = 0, + JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED = JVMTI_THREAD_STATE_TERMINATED, + JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE, + JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, + JVMTI_JAVA_LANG_THREAD_STATE_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY, + JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +}; + + /* Thread Priority Constants */ + +enum { + JVMTI_THREAD_MIN_PRIORITY = 1, + JVMTI_THREAD_NORM_PRIORITY = 5, + JVMTI_THREAD_MAX_PRIORITY = 10 +}; + + /* Heap Filter Flags */ + +enum { + JVMTI_HEAP_FILTER_TAGGED = 0x4, + JVMTI_HEAP_FILTER_UNTAGGED = 0x8, + JVMTI_HEAP_FILTER_CLASS_TAGGED = 0x10, + JVMTI_HEAP_FILTER_CLASS_UNTAGGED = 0x20 +}; + + /* Heap Visit Control Flags */ + +enum { + JVMTI_VISIT_OBJECTS = 0x100, + JVMTI_VISIT_ABORT = 0x8000 +}; + + /* Heap Reference Enumeration */ + +typedef enum { + JVMTI_HEAP_REFERENCE_CLASS = 1, + JVMTI_HEAP_REFERENCE_FIELD = 2, + JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_HEAP_REFERENCE_CLASS_LOADER = 4, + JVMTI_HEAP_REFERENCE_SIGNERS = 5, + JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_HEAP_REFERENCE_INTERFACE = 7, + JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8, + JVMTI_HEAP_REFERENCE_CONSTANT_POOL = 9, + JVMTI_HEAP_REFERENCE_SUPERCLASS = 10, + JVMTI_HEAP_REFERENCE_JNI_GLOBAL = 21, + JVMTI_HEAP_REFERENCE_SYSTEM_CLASS = 22, + JVMTI_HEAP_REFERENCE_MONITOR = 23, + JVMTI_HEAP_REFERENCE_STACK_LOCAL = 24, + JVMTI_HEAP_REFERENCE_JNI_LOCAL = 25, + JVMTI_HEAP_REFERENCE_THREAD = 26, + JVMTI_HEAP_REFERENCE_OTHER = 27 +} jvmtiHeapReferenceKind; + + /* Primitive Type Enumeration */ + +typedef enum { + JVMTI_PRIMITIVE_TYPE_BOOLEAN = 90, + JVMTI_PRIMITIVE_TYPE_BYTE = 66, + JVMTI_PRIMITIVE_TYPE_CHAR = 67, + JVMTI_PRIMITIVE_TYPE_SHORT = 83, + JVMTI_PRIMITIVE_TYPE_INT = 73, + JVMTI_PRIMITIVE_TYPE_LONG = 74, + JVMTI_PRIMITIVE_TYPE_FLOAT = 70, + JVMTI_PRIMITIVE_TYPE_DOUBLE = 68 +} jvmtiPrimitiveType; + + /* Heap Object Filter Enumeration */ + +typedef enum { + JVMTI_HEAP_OBJECT_TAGGED = 1, + JVMTI_HEAP_OBJECT_UNTAGGED = 2, + JVMTI_HEAP_OBJECT_EITHER = 3 +} jvmtiHeapObjectFilter; + + /* Heap Root Kind Enumeration */ + +typedef enum { + JVMTI_HEAP_ROOT_JNI_GLOBAL = 1, + JVMTI_HEAP_ROOT_SYSTEM_CLASS = 2, + JVMTI_HEAP_ROOT_MONITOR = 3, + JVMTI_HEAP_ROOT_STACK_LOCAL = 4, + JVMTI_HEAP_ROOT_JNI_LOCAL = 5, + JVMTI_HEAP_ROOT_THREAD = 6, + JVMTI_HEAP_ROOT_OTHER = 7 +} jvmtiHeapRootKind; + + /* Object Reference Enumeration */ + +typedef enum { + JVMTI_REFERENCE_CLASS = 1, + JVMTI_REFERENCE_FIELD = 2, + JVMTI_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_REFERENCE_CLASS_LOADER = 4, + JVMTI_REFERENCE_SIGNERS = 5, + JVMTI_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_REFERENCE_INTERFACE = 7, + JVMTI_REFERENCE_STATIC_FIELD = 8, + JVMTI_REFERENCE_CONSTANT_POOL = 9 +} jvmtiObjectReferenceKind; + + /* Iteration Control Enumeration */ + +typedef enum { + JVMTI_ITERATION_CONTINUE = 1, + JVMTI_ITERATION_IGNORE = 2, + JVMTI_ITERATION_ABORT = 0 +} jvmtiIterationControl; + + /* Class Status Flags */ + +enum { + JVMTI_CLASS_STATUS_VERIFIED = 1, + JVMTI_CLASS_STATUS_PREPARED = 2, + JVMTI_CLASS_STATUS_INITIALIZED = 4, + JVMTI_CLASS_STATUS_ERROR = 8, + JVMTI_CLASS_STATUS_ARRAY = 16, + JVMTI_CLASS_STATUS_PRIMITIVE = 32 +}; + + /* Event Enable/Disable */ + +typedef enum { + JVMTI_ENABLE = 1, + JVMTI_DISABLE = 0 +} jvmtiEventMode; + + /* Extension Function/Event Parameter Types */ + +typedef enum { + JVMTI_TYPE_JBYTE = 101, + JVMTI_TYPE_JCHAR = 102, + JVMTI_TYPE_JSHORT = 103, + JVMTI_TYPE_JINT = 104, + JVMTI_TYPE_JLONG = 105, + JVMTI_TYPE_JFLOAT = 106, + JVMTI_TYPE_JDOUBLE = 107, + JVMTI_TYPE_JBOOLEAN = 108, + JVMTI_TYPE_JOBJECT = 109, + JVMTI_TYPE_JTHREAD = 110, + JVMTI_TYPE_JCLASS = 111, + JVMTI_TYPE_JVALUE = 112, + JVMTI_TYPE_JFIELDID = 113, + JVMTI_TYPE_JMETHODID = 114, + JVMTI_TYPE_CCHAR = 115, + JVMTI_TYPE_CVOID = 116, + JVMTI_TYPE_JNIENV = 117 +} jvmtiParamTypes; + + /* Extension Function/Event Parameter Kinds */ + +typedef enum { + JVMTI_KIND_IN = 91, + JVMTI_KIND_IN_PTR = 92, + JVMTI_KIND_IN_BUF = 93, + JVMTI_KIND_ALLOC_BUF = 94, + JVMTI_KIND_ALLOC_ALLOC_BUF = 95, + JVMTI_KIND_OUT = 96, + JVMTI_KIND_OUT_BUF = 97 +} jvmtiParamKind; + + /* Timer Kinds */ + +typedef enum { + JVMTI_TIMER_USER_CPU = 30, + JVMTI_TIMER_TOTAL_CPU = 31, + JVMTI_TIMER_ELAPSED = 32 +} jvmtiTimerKind; + + /* Phases of execution */ + +typedef enum { + JVMTI_PHASE_ONLOAD = 1, + JVMTI_PHASE_PRIMORDIAL = 2, + JVMTI_PHASE_START = 6, + JVMTI_PHASE_LIVE = 4, + JVMTI_PHASE_DEAD = 8 +} jvmtiPhase; + + /* Version Interface Types */ + +enum { + JVMTI_VERSION_INTERFACE_JNI = 0x00000000, + JVMTI_VERSION_INTERFACE_JVMTI = 0x30000000 +}; + + /* Version Masks */ + +enum { + JVMTI_VERSION_MASK_INTERFACE_TYPE = 0x70000000, + JVMTI_VERSION_MASK_MAJOR = 0x0FFF0000, + JVMTI_VERSION_MASK_MINOR = 0x0000FF00, + JVMTI_VERSION_MASK_MICRO = 0x000000FF +}; + + /* Version Shifts */ + +enum { + JVMTI_VERSION_SHIFT_MAJOR = 16, + JVMTI_VERSION_SHIFT_MINOR = 8, + JVMTI_VERSION_SHIFT_MICRO = 0 +}; + + /* Verbose Flag Enumeration */ + +typedef enum { + JVMTI_VERBOSE_OTHER = 0, + JVMTI_VERBOSE_GC = 1, + JVMTI_VERBOSE_CLASS = 2, + JVMTI_VERBOSE_JNI = 4 +} jvmtiVerboseFlag; + + /* JLocation Format Enumeration */ + +typedef enum { + JVMTI_JLOCATION_JVMBCI = 1, + JVMTI_JLOCATION_MACHINEPC = 2, + JVMTI_JLOCATION_OTHER = 0 +} jvmtiJlocationFormat; + + /* Resource Exhaustion Flags */ + +enum { + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 0x0001, + JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 0x0002, + JVMTI_RESOURCE_EXHAUSTED_THREADS = 0x0004 +}; + + /* Errors */ + +typedef enum { + JVMTI_ERROR_NONE = 0, + JVMTI_ERROR_INVALID_THREAD = 10, + JVMTI_ERROR_INVALID_THREAD_GROUP = 11, + JVMTI_ERROR_INVALID_PRIORITY = 12, + JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13, + JVMTI_ERROR_THREAD_SUSPENDED = 14, + JVMTI_ERROR_THREAD_NOT_ALIVE = 15, + JVMTI_ERROR_INVALID_OBJECT = 20, + JVMTI_ERROR_INVALID_CLASS = 21, + JVMTI_ERROR_CLASS_NOT_PREPARED = 22, + JVMTI_ERROR_INVALID_METHODID = 23, + JVMTI_ERROR_INVALID_LOCATION = 24, + JVMTI_ERROR_INVALID_FIELDID = 25, + JVMTI_ERROR_NO_MORE_FRAMES = 31, + JVMTI_ERROR_OPAQUE_FRAME = 32, + JVMTI_ERROR_TYPE_MISMATCH = 34, + JVMTI_ERROR_INVALID_SLOT = 35, + JVMTI_ERROR_DUPLICATE = 40, + JVMTI_ERROR_NOT_FOUND = 41, + JVMTI_ERROR_INVALID_MONITOR = 50, + JVMTI_ERROR_NOT_MONITOR_OWNER = 51, + JVMTI_ERROR_INTERRUPT = 52, + JVMTI_ERROR_INVALID_CLASS_FORMAT = 60, + JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION = 61, + JVMTI_ERROR_FAILS_VERIFICATION = 62, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED = 63, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED = 64, + JVMTI_ERROR_INVALID_TYPESTATE = 65, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED = 66, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED = 67, + JVMTI_ERROR_UNSUPPORTED_VERSION = 68, + JVMTI_ERROR_NAMES_DONT_MATCH = 69, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71, + JVMTI_ERROR_UNMODIFIABLE_CLASS = 79, + JVMTI_ERROR_NOT_AVAILABLE = 98, + JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99, + JVMTI_ERROR_NULL_POINTER = 100, + JVMTI_ERROR_ABSENT_INFORMATION = 101, + JVMTI_ERROR_INVALID_EVENT_TYPE = 102, + JVMTI_ERROR_ILLEGAL_ARGUMENT = 103, + JVMTI_ERROR_NATIVE_METHOD = 104, + JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED = 106, + JVMTI_ERROR_OUT_OF_MEMORY = 110, + JVMTI_ERROR_ACCESS_DENIED = 111, + JVMTI_ERROR_WRONG_PHASE = 112, + JVMTI_ERROR_INTERNAL = 113, + JVMTI_ERROR_UNATTACHED_THREAD = 115, + JVMTI_ERROR_INVALID_ENVIRONMENT = 116, + JVMTI_ERROR_MAX = 116 +} jvmtiError; + + /* Event IDs */ + +typedef enum { + JVMTI_MIN_EVENT_TYPE_VAL = 50, + JVMTI_EVENT_VM_INIT = 50, + JVMTI_EVENT_VM_DEATH = 51, + JVMTI_EVENT_THREAD_START = 52, + JVMTI_EVENT_THREAD_END = 53, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54, + JVMTI_EVENT_CLASS_LOAD = 55, + JVMTI_EVENT_CLASS_PREPARE = 56, + JVMTI_EVENT_VM_START = 57, + JVMTI_EVENT_EXCEPTION = 58, + JVMTI_EVENT_EXCEPTION_CATCH = 59, + JVMTI_EVENT_SINGLE_STEP = 60, + JVMTI_EVENT_FRAME_POP = 61, + JVMTI_EVENT_BREAKPOINT = 62, + JVMTI_EVENT_FIELD_ACCESS = 63, + JVMTI_EVENT_FIELD_MODIFICATION = 64, + JVMTI_EVENT_METHOD_ENTRY = 65, + JVMTI_EVENT_METHOD_EXIT = 66, + JVMTI_EVENT_NATIVE_METHOD_BIND = 67, + JVMTI_EVENT_COMPILED_METHOD_LOAD = 68, + JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69, + JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70, + JVMTI_EVENT_DATA_DUMP_REQUEST = 71, + JVMTI_EVENT_MONITOR_WAIT = 73, + JVMTI_EVENT_MONITOR_WAITED = 74, + JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75, + JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76, + JVMTI_EVENT_RESOURCE_EXHAUSTED = 80, + JVMTI_EVENT_GARBAGE_COLLECTION_START = 81, + JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82, + JVMTI_EVENT_OBJECT_FREE = 83, + JVMTI_EVENT_VM_OBJECT_ALLOC = 84, + JVMTI_MAX_EVENT_TYPE_VAL = 84 +} jvmtiEvent; + + + /* Pre-Declarations */ +struct _jvmtiThreadInfo; +typedef struct _jvmtiThreadInfo jvmtiThreadInfo; +struct _jvmtiMonitorStackDepthInfo; +typedef struct _jvmtiMonitorStackDepthInfo jvmtiMonitorStackDepthInfo; +struct _jvmtiThreadGroupInfo; +typedef struct _jvmtiThreadGroupInfo jvmtiThreadGroupInfo; +struct _jvmtiFrameInfo; +typedef struct _jvmtiFrameInfo jvmtiFrameInfo; +struct _jvmtiStackInfo; +typedef struct _jvmtiStackInfo jvmtiStackInfo; +struct _jvmtiHeapReferenceInfoField; +typedef struct _jvmtiHeapReferenceInfoField jvmtiHeapReferenceInfoField; +struct _jvmtiHeapReferenceInfoArray; +typedef struct _jvmtiHeapReferenceInfoArray jvmtiHeapReferenceInfoArray; +struct _jvmtiHeapReferenceInfoConstantPool; +typedef struct _jvmtiHeapReferenceInfoConstantPool jvmtiHeapReferenceInfoConstantPool; +struct _jvmtiHeapReferenceInfoStackLocal; +typedef struct _jvmtiHeapReferenceInfoStackLocal jvmtiHeapReferenceInfoStackLocal; +struct _jvmtiHeapReferenceInfoJniLocal; +typedef struct _jvmtiHeapReferenceInfoJniLocal jvmtiHeapReferenceInfoJniLocal; +struct _jvmtiHeapReferenceInfoReserved; +typedef struct _jvmtiHeapReferenceInfoReserved jvmtiHeapReferenceInfoReserved; +union _jvmtiHeapReferenceInfo; +typedef union _jvmtiHeapReferenceInfo jvmtiHeapReferenceInfo; +struct _jvmtiHeapCallbacks; +typedef struct _jvmtiHeapCallbacks jvmtiHeapCallbacks; +struct _jvmtiClassDefinition; +typedef struct _jvmtiClassDefinition jvmtiClassDefinition; +struct _jvmtiMonitorUsage; +typedef struct _jvmtiMonitorUsage jvmtiMonitorUsage; +struct _jvmtiLineNumberEntry; +typedef struct _jvmtiLineNumberEntry jvmtiLineNumberEntry; +struct _jvmtiLocalVariableEntry; +typedef struct _jvmtiLocalVariableEntry jvmtiLocalVariableEntry; +struct _jvmtiParamInfo; +typedef struct _jvmtiParamInfo jvmtiParamInfo; +struct _jvmtiExtensionFunctionInfo; +typedef struct _jvmtiExtensionFunctionInfo jvmtiExtensionFunctionInfo; +struct _jvmtiExtensionEventInfo; +typedef struct _jvmtiExtensionEventInfo jvmtiExtensionEventInfo; +struct _jvmtiTimerInfo; +typedef struct _jvmtiTimerInfo jvmtiTimerInfo; +struct _jvmtiAddrLocationMap; +typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap; + + /* Function Types */ + +typedef void (JNICALL *jvmtiStartFunction) + (jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg); + +typedef jint (JNICALL *jvmtiHeapIterationCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiHeapReferenceCallback) + (jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiPrimitiveFieldCallback) + (jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong object_class_tag, jlong* object_tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data); + +typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data); + +typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data); + +typedef jint (JNICALL *jvmtiReservedCallback) + (); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback) + (jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void* user_data); + +typedef jvmtiError (JNICALL *jvmtiExtensionFunction) + (jvmtiEnv* jvmti_env, ...); + +typedef void (JNICALL *jvmtiExtensionEvent) + (jvmtiEnv* jvmti_env, ...); + + + /* Structure Types */ +struct _jvmtiThreadInfo { + char* name; + jint priority; + jboolean is_daemon; + jthreadGroup thread_group; + jobject context_class_loader; +}; +struct _jvmtiMonitorStackDepthInfo { + jobject monitor; + jint stack_depth; +}; +struct _jvmtiThreadGroupInfo { + jthreadGroup parent; + char* name; + jint max_priority; + jboolean is_daemon; +}; +struct _jvmtiFrameInfo { + jmethodID method; + jlocation location; +}; +struct _jvmtiStackInfo { + jthread thread; + jint state; + jvmtiFrameInfo* frame_buffer; + jint frame_count; +}; +struct _jvmtiHeapReferenceInfoField { + jint index; +}; +struct _jvmtiHeapReferenceInfoArray { + jint index; +}; +struct _jvmtiHeapReferenceInfoConstantPool { + jint index; +}; +struct _jvmtiHeapReferenceInfoStackLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; + jlocation location; + jint slot; +}; +struct _jvmtiHeapReferenceInfoJniLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; +}; +struct _jvmtiHeapReferenceInfoReserved { + jlong reserved1; + jlong reserved2; + jlong reserved3; + jlong reserved4; + jlong reserved5; + jlong reserved6; + jlong reserved7; + jlong reserved8; +}; +union _jvmtiHeapReferenceInfo { + jvmtiHeapReferenceInfoField field; + jvmtiHeapReferenceInfoArray array; + jvmtiHeapReferenceInfoConstantPool constant_pool; + jvmtiHeapReferenceInfoStackLocal stack_local; + jvmtiHeapReferenceInfoJniLocal jni_local; + jvmtiHeapReferenceInfoReserved other; +}; +struct _jvmtiHeapCallbacks { + jvmtiHeapIterationCallback heap_iteration_callback; + jvmtiHeapReferenceCallback heap_reference_callback; + jvmtiPrimitiveFieldCallback primitive_field_callback; + jvmtiArrayPrimitiveValueCallback array_primitive_value_callback; + jvmtiStringPrimitiveValueCallback string_primitive_value_callback; + jvmtiReservedCallback reserved5; + jvmtiReservedCallback reserved6; + jvmtiReservedCallback reserved7; + jvmtiReservedCallback reserved8; + jvmtiReservedCallback reserved9; + jvmtiReservedCallback reserved10; + jvmtiReservedCallback reserved11; + jvmtiReservedCallback reserved12; + jvmtiReservedCallback reserved13; + jvmtiReservedCallback reserved14; + jvmtiReservedCallback reserved15; +}; +struct _jvmtiClassDefinition { + jclass klass; + jint class_byte_count; + const unsigned char* class_bytes; +}; +struct _jvmtiMonitorUsage { + jthread owner; + jint entry_count; + jint waiter_count; + jthread* waiters; + jint notify_waiter_count; + jthread* notify_waiters; +}; +struct _jvmtiLineNumberEntry { + jlocation start_location; + jint line_number; +}; +struct _jvmtiLocalVariableEntry { + jlocation start_location; + jint length; + char* name; + char* signature; + char* generic_signature; + jint slot; +}; +struct _jvmtiParamInfo { + char* name; + jvmtiParamKind kind; + jvmtiParamTypes base_type; + jboolean null_ok; +}; +struct _jvmtiExtensionFunctionInfo { + jvmtiExtensionFunction func; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; + jint error_count; + jvmtiError* errors; +}; +struct _jvmtiExtensionEventInfo { + jint extension_event_index; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; +}; +struct _jvmtiTimerInfo { + jlong max_value; + jboolean may_skip_forward; + jboolean may_skip_backward; + jvmtiTimerKind kind; + jlong reserved1; + jlong reserved2; +}; +struct _jvmtiAddrLocationMap { + const void* start_address; + jlocation location; +}; + +typedef struct { + unsigned int can_tag_objects : 1; + unsigned int can_generate_field_modification_events : 1; + unsigned int can_generate_field_access_events : 1; + unsigned int can_get_bytecodes : 1; + unsigned int can_get_synthetic_attribute : 1; + unsigned int can_get_owned_monitor_info : 1; + unsigned int can_get_current_contended_monitor : 1; + unsigned int can_get_monitor_info : 1; + unsigned int can_pop_frame : 1; + unsigned int can_redefine_classes : 1; + unsigned int can_signal_thread : 1; + unsigned int can_get_source_file_name : 1; + unsigned int can_get_line_numbers : 1; + unsigned int can_get_source_debug_extension : 1; + unsigned int can_access_local_variables : 1; + unsigned int can_maintain_original_method_order : 1; + unsigned int can_generate_single_step_events : 1; + unsigned int can_generate_exception_events : 1; + unsigned int can_generate_frame_pop_events : 1; + unsigned int can_generate_breakpoint_events : 1; + unsigned int can_suspend : 1; + unsigned int can_redefine_any_class : 1; + unsigned int can_get_current_thread_cpu_time : 1; + unsigned int can_get_thread_cpu_time : 1; + unsigned int can_generate_method_entry_events : 1; + unsigned int can_generate_method_exit_events : 1; + unsigned int can_generate_all_class_hook_events : 1; + unsigned int can_generate_compiled_method_load_events : 1; + unsigned int can_generate_monitor_events : 1; + unsigned int can_generate_vm_object_alloc_events : 1; + unsigned int can_generate_native_method_bind_events : 1; + unsigned int can_generate_garbage_collection_events : 1; + unsigned int can_generate_object_free_events : 1; + unsigned int can_force_early_return : 1; + unsigned int can_get_owned_monitor_stack_depth_info : 1; + unsigned int can_get_constant_pool : 1; + unsigned int can_set_native_method_prefix : 1; + unsigned int can_retransform_classes : 1; + unsigned int can_retransform_any_class : 1; + unsigned int can_generate_resource_exhaustion_heap_events : 1; + unsigned int can_generate_resource_exhaustion_threads_events : 1; + unsigned int : 7; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; +} jvmtiCapabilities; + + + /* Event Definitions */ + +typedef void (JNICALL *jvmtiEventReserved)(void); + + +typedef void (JNICALL *jvmtiEventBreakpoint) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventClassFileLoadHook) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data); + +typedef void (JNICALL *jvmtiEventClassLoad) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventClassPrepare) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventCompiledMethodLoad) + (jvmtiEnv *jvmti_env, + jmethodID method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info); + +typedef void (JNICALL *jvmtiEventCompiledMethodUnload) + (jvmtiEnv *jvmti_env, + jmethodID method, + const void* code_addr); + +typedef void (JNICALL *jvmtiEventDataDumpRequest) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventDynamicCodeGenerated) + (jvmtiEnv *jvmti_env, + const char* name, + const void* address, + jint length); + +typedef void (JNICALL *jvmtiEventException) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception, + jmethodID catch_method, + jlocation catch_location); + +typedef void (JNICALL *jvmtiEventExceptionCatch) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception); + +typedef void (JNICALL *jvmtiEventFieldAccess) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field); + +typedef void (JNICALL *jvmtiEventFieldModification) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field, + char signature_type, + jvalue new_value); + +typedef void (JNICALL *jvmtiEventFramePop) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception); + +typedef void (JNICALL *jvmtiEventGarbageCollectionFinish) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventGarbageCollectionStart) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventMethodEntry) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method); + +typedef void (JNICALL *jvmtiEventMethodExit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception, + jvalue return_value); + +typedef void (JNICALL *jvmtiEventMonitorContendedEnter) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorContendedEntered) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorWait) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jlong timeout); + +typedef void (JNICALL *jvmtiEventMonitorWaited) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jboolean timed_out); + +typedef void (JNICALL *jvmtiEventNativeMethodBind) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + void* address, + void** new_address_ptr); + +typedef void (JNICALL *jvmtiEventObjectFree) + (jvmtiEnv *jvmti_env, + jlong tag); + +typedef void (JNICALL *jvmtiEventResourceExhausted) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint flags, + const void* reserved, + const char* description); + +typedef void (JNICALL *jvmtiEventSingleStep) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventThreadEnd) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventThreadStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMDeath) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + +typedef void (JNICALL *jvmtiEventVMInit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMObjectAlloc) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jclass object_klass, + jlong size); + +typedef void (JNICALL *jvmtiEventVMStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + + /* Event Callback Structure */ + +typedef struct { + /* 50 : VM Initialization Event */ + jvmtiEventVMInit VMInit; + /* 51 : VM Death Event */ + jvmtiEventVMDeath VMDeath; + /* 52 : Thread Start */ + jvmtiEventThreadStart ThreadStart; + /* 53 : Thread End */ + jvmtiEventThreadEnd ThreadEnd; + /* 54 : Class File Load Hook */ + jvmtiEventClassFileLoadHook ClassFileLoadHook; + /* 55 : Class Load */ + jvmtiEventClassLoad ClassLoad; + /* 56 : Class Prepare */ + jvmtiEventClassPrepare ClassPrepare; + /* 57 : VM Start Event */ + jvmtiEventVMStart VMStart; + /* 58 : Exception */ + jvmtiEventException Exception; + /* 59 : Exception Catch */ + jvmtiEventExceptionCatch ExceptionCatch; + /* 60 : Single Step */ + jvmtiEventSingleStep SingleStep; + /* 61 : Frame Pop */ + jvmtiEventFramePop FramePop; + /* 62 : Breakpoint */ + jvmtiEventBreakpoint Breakpoint; + /* 63 : Field Access */ + jvmtiEventFieldAccess FieldAccess; + /* 64 : Field Modification */ + jvmtiEventFieldModification FieldModification; + /* 65 : Method Entry */ + jvmtiEventMethodEntry MethodEntry; + /* 66 : Method Exit */ + jvmtiEventMethodExit MethodExit; + /* 67 : Native Method Bind */ + jvmtiEventNativeMethodBind NativeMethodBind; + /* 68 : Compiled Method Load */ + jvmtiEventCompiledMethodLoad CompiledMethodLoad; + /* 69 : Compiled Method Unload */ + jvmtiEventCompiledMethodUnload CompiledMethodUnload; + /* 70 : Dynamic Code Generated */ + jvmtiEventDynamicCodeGenerated DynamicCodeGenerated; + /* 71 : Data Dump Request */ + jvmtiEventDataDumpRequest DataDumpRequest; + /* 72 */ + jvmtiEventReserved reserved72; + /* 73 : Monitor Wait */ + jvmtiEventMonitorWait MonitorWait; + /* 74 : Monitor Waited */ + jvmtiEventMonitorWaited MonitorWaited; + /* 75 : Monitor Contended Enter */ + jvmtiEventMonitorContendedEnter MonitorContendedEnter; + /* 76 : Monitor Contended Entered */ + jvmtiEventMonitorContendedEntered MonitorContendedEntered; + /* 77 */ + jvmtiEventReserved reserved77; + /* 78 */ + jvmtiEventReserved reserved78; + /* 79 */ + jvmtiEventReserved reserved79; + /* 80 : Resource Exhausted */ + jvmtiEventResourceExhausted ResourceExhausted; + /* 81 : Garbage Collection Start */ + jvmtiEventGarbageCollectionStart GarbageCollectionStart; + /* 82 : Garbage Collection Finish */ + jvmtiEventGarbageCollectionFinish GarbageCollectionFinish; + /* 83 : Object Free */ + jvmtiEventObjectFree ObjectFree; + /* 84 : VM Object Allocation */ + jvmtiEventVMObjectAlloc VMObjectAlloc; +} jvmtiEventCallbacks; + + + /* Function Interface */ + +typedef struct jvmtiInterface_1_ { + + /* 1 : RESERVED */ + void *reserved1; + + /* 2 : Set Event Notification Mode */ + jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env, + jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...); + + /* 3 : RESERVED */ + void *reserved3; + + /* 4 : Get All Threads */ + jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env, + jint* threads_count_ptr, + jthread** threads_ptr); + + /* 5 : Suspend Thread */ + jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env, + jthread thread); + + /* 6 : Resume Thread */ + jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env, + jthread thread); + + /* 7 : Stop Thread */ + jvmtiError (JNICALL *StopThread) (jvmtiEnv* env, + jthread thread, + jobject exception); + + /* 8 : Interrupt Thread */ + jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env, + jthread thread); + + /* 9 : Get Thread Info */ + jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env, + jthread thread, + jvmtiThreadInfo* info_ptr); + + /* 10 : Get Owned Monitor Info */ + jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env, + jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr); + + /* 11 : Get Current Contended Monitor */ + jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env, + jthread thread, + jobject* monitor_ptr); + + /* 12 : Run Agent Thread */ + jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env, + jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority); + + /* 13 : Get Top Thread Groups */ + jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 14 : Get Thread Group Info */ + jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + /* 15 : Get Thread Group Children */ + jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 16 : Get Frame Count */ + jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env, + jthread thread, + jint* count_ptr); + + /* 17 : Get Thread State */ + jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env, + jthread thread, + jint* thread_state_ptr); + + /* 18 : Get Current Thread */ + jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env, + jthread* thread_ptr); + + /* 19 : Get Frame Location */ + jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env, + jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr); + + /* 20 : Notify Frame Pop */ + jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env, + jthread thread, + jint depth); + + /* 21 : Get Local Variable - Object */ + jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject* value_ptr); + + /* 22 : Get Local Variable - Int */ + jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint* value_ptr); + + /* 23 : Get Local Variable - Long */ + jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong* value_ptr); + + /* 24 : Get Local Variable - Float */ + jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat* value_ptr); + + /* 25 : Get Local Variable - Double */ + jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble* value_ptr); + + /* 26 : Set Local Variable - Object */ + jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject value); + + /* 27 : Set Local Variable - Int */ + jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint value); + + /* 28 : Set Local Variable - Long */ + jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong value); + + /* 29 : Set Local Variable - Float */ + jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat value); + + /* 30 : Set Local Variable - Double */ + jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble value); + + /* 31 : Create Raw Monitor */ + jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env, + const char* name, + jrawMonitorID* monitor_ptr); + + /* 32 : Destroy Raw Monitor */ + jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 33 : Raw Monitor Enter */ + jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 34 : Raw Monitor Exit */ + jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 35 : Raw Monitor Wait */ + jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env, + jrawMonitorID monitor, + jlong millis); + + /* 36 : Raw Monitor Notify */ + jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 37 : Raw Monitor Notify All */ + jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 38 : Set Breakpoint */ + jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 39 : Clear Breakpoint */ + jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 40 : RESERVED */ + void *reserved40; + + /* 41 : Set Field Access Watch */ + jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 42 : Clear Field Access Watch */ + jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 43 : Set Field Modification Watch */ + jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 44 : Clear Field Modification Watch */ + jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 45 : Is Modifiable Class */ + jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_modifiable_class_ptr); + + /* 46 : Allocate */ + jvmtiError (JNICALL *Allocate) (jvmtiEnv* env, + jlong size, + unsigned char** mem_ptr); + + /* 47 : Deallocate */ + jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env, + unsigned char* mem); + + /* 48 : Get Class Signature */ + jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env, + jclass klass, + char** signature_ptr, + char** generic_ptr); + + /* 49 : Get Class Status */ + jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env, + jclass klass, + jint* status_ptr); + + /* 50 : Get Source File Name */ + jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env, + jclass klass, + char** source_name_ptr); + + /* 51 : Get Class Modifiers */ + jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env, + jclass klass, + jint* modifiers_ptr); + + /* 52 : Get Class Methods */ + jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env, + jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr); + + /* 53 : Get Class Fields */ + jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env, + jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr); + + /* 54 : Get Implemented Interfaces */ + jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env, + jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr); + + /* 55 : Is Interface */ + jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env, + jclass klass, + jboolean* is_interface_ptr); + + /* 56 : Is Array Class */ + jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_array_class_ptr); + + /* 57 : Get Class Loader */ + jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env, + jclass klass, + jobject* classloader_ptr); + + /* 58 : Get Object Hash Code */ + jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env, + jobject object, + jint* hash_code_ptr); + + /* 59 : Get Object Monitor Usage */ + jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env, + jobject object, + jvmtiMonitorUsage* info_ptr); + + /* 60 : Get Field Name (and Signature) */ + jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env, + jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 61 : Get Field Declaring Class */ + jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jclass* declaring_class_ptr); + + /* 62 : Get Field Modifiers */ + jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jint* modifiers_ptr); + + /* 63 : Is Field Synthetic */ + jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr); + + /* 64 : Get Method Name (and Signature) */ + jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env, + jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 65 : Get Method Declaring Class */ + jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env, + jmethodID method, + jclass* declaring_class_ptr); + + /* 66 : Get Method Modifiers */ + jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env, + jmethodID method, + jint* modifiers_ptr); + + /* 67 : RESERVED */ + void *reserved67; + + /* 68 : Get Max Locals */ + jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env, + jmethodID method, + jint* max_ptr); + + /* 69 : Get Arguments Size */ + jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env, + jmethodID method, + jint* size_ptr); + + /* 70 : Get Line Number Table */ + jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr); + + /* 71 : Get Method Location */ + jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env, + jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr); + + /* 72 : Get Local Variable Table */ + jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr); + + /* 73 : Set Native Method Prefix */ + jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env, + const char* prefix); + + /* 74 : Set Native Method Prefixes */ + jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env, + jint prefix_count, + char** prefixes); + + /* 75 : Get Bytecodes */ + jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env, + jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr); + + /* 76 : Is Method Native */ + jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env, + jmethodID method, + jboolean* is_native_ptr); + + /* 77 : Is Method Synthetic */ + jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env, + jmethodID method, + jboolean* is_synthetic_ptr); + + /* 78 : Get Loaded Classes */ + jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 79 : Get Classloader Classes */ + jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 80 : Pop Frame */ + jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env, + jthread thread); + + /* 81 : Force Early Return - Object */ + jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env, + jthread thread, + jobject value); + + /* 82 : Force Early Return - Int */ + jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env, + jthread thread, + jint value); + + /* 83 : Force Early Return - Long */ + jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env, + jthread thread, + jlong value); + + /* 84 : Force Early Return - Float */ + jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env, + jthread thread, + jfloat value); + + /* 85 : Force Early Return - Double */ + jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env, + jthread thread, + jdouble value); + + /* 86 : Force Early Return - Void */ + jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env, + jthread thread); + + /* 87 : Redefine Classes */ + jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env, + jint class_count, + const jvmtiClassDefinition* class_definitions); + + /* 88 : Get Version Number */ + jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env, + jint* version_ptr); + + /* 89 : Get Capabilities */ + jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 90 : Get Source Debug Extension */ + jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env, + jclass klass, + char** source_debug_extension_ptr); + + /* 91 : Is Method Obsolete */ + jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env, + jmethodID method, + jboolean* is_obsolete_ptr); + + /* 92 : Suspend Thread List */ + jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 93 : Resume Thread List */ + jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 94 : RESERVED */ + void *reserved94; + + /* 95 : RESERVED */ + void *reserved95; + + /* 96 : RESERVED */ + void *reserved96; + + /* 97 : RESERVED */ + void *reserved97; + + /* 98 : RESERVED */ + void *reserved98; + + /* 99 : RESERVED */ + void *reserved99; + + /* 100 : Get All Stack Traces */ + jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr); + + /* 101 : Get Thread List Stack Traces */ + jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env, + jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr); + + /* 102 : Get Thread Local Storage */ + jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + void** data_ptr); + + /* 103 : Set Thread Local Storage */ + jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + const void* data); + + /* 104 : Get Stack Trace */ + jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env, + jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr); + + /* 105 : RESERVED */ + void *reserved105; + + /* 106 : Get Tag */ + jvmtiError (JNICALL *GetTag) (jvmtiEnv* env, + jobject object, + jlong* tag_ptr); + + /* 107 : Set Tag */ + jvmtiError (JNICALL *SetTag) (jvmtiEnv* env, + jobject object, + jlong tag); + + /* 108 : Force Garbage Collection */ + jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env); + + /* 109 : Iterate Over Objects Reachable From Object */ + jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env, + jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data); + + /* 110 : Iterate Over Reachable Objects */ + jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env, + jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data); + + /* 111 : Iterate Over Heap */ + jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 112 : Iterate Over Instances Of Class */ + jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env, + jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 113 : RESERVED */ + void *reserved113; + + /* 114 : Get Objects With Tags */ + jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env, + jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr); + + /* 115 : Follow References */ + jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 116 : Iterate Through Heap */ + jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 117 : RESERVED */ + void *reserved117; + + /* 118 : RESERVED */ + void *reserved118; + + /* 119 : RESERVED */ + void *reserved119; + + /* 120 : Set JNI Function Table */ + jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env, + const jniNativeInterface* function_table); + + /* 121 : Get JNI Function Table */ + jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env, + jniNativeInterface** function_table); + + /* 122 : Set Event Callbacks */ + jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks); + + /* 123 : Generate Events */ + jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env, + jvmtiEvent event_type); + + /* 124 : Get Extension Functions */ + jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions); + + /* 125 : Get Extension Events */ + jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions); + + /* 126 : Set Extension Event Callback */ + jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback); + + /* 127 : Dispose Environment */ + jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env); + + /* 128 : Get Error Name */ + jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env, + jvmtiError error, + char** name_ptr); + + /* 129 : Get JLocation Format */ + jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env, + jvmtiJlocationFormat* format_ptr); + + /* 130 : Get System Properties */ + jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env, + jint* count_ptr, + char*** property_ptr); + + /* 131 : Get System Property */ + jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env, + const char* property, + char** value_ptr); + + /* 132 : Set System Property */ + jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env, + const char* property, + const char* value); + + /* 133 : Get Phase */ + jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env, + jvmtiPhase* phase_ptr); + + /* 134 : Get Current Thread CPU Timer Information */ + jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 135 : Get Current Thread CPU Time */ + jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 136 : Get Thread CPU Timer Information */ + jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 137 : Get Thread CPU Time */ + jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env, + jthread thread, + jlong* nanos_ptr); + + /* 138 : Get Timer Information */ + jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 139 : Get Time */ + jvmtiError (JNICALL *GetTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 140 : Get Potential Capabilities */ + jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 141 : RESERVED */ + void *reserved141; + + /* 142 : Add Capabilities */ + jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 143 : Relinquish Capabilities */ + jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 144 : Get Available Processors */ + jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env, + jint* processor_count_ptr); + + /* 145 : Get Class Version Numbers */ + jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env, + jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr); + + /* 146 : Get Constant Pool */ + jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env, + jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr); + + /* 147 : Get Environment Local Storage */ + jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env, + void** data_ptr); + + /* 148 : Set Environment Local Storage */ + jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env, + const void* data); + + /* 149 : Add To Bootstrap Class Loader Search */ + jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 150 : Set Verbose Flag */ + jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env, + jvmtiVerboseFlag flag, + jboolean value); + + /* 151 : Add To System Class Loader Search */ + jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 152 : Retransform Classes */ + jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env, + jint class_count, + const jclass* classes); + + /* 153 : Get Owned Monitor Stack Depth Info */ + jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env, + jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr); + + /* 154 : Get Object Size */ + jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env, + jobject object, + jlong* size_ptr); + + /* 155 : Get Local Instance */ + jvmtiError (JNICALL *GetLocalInstance) (jvmtiEnv* env, + jthread thread, + jint depth, + jobject* value_ptr); + +} jvmtiInterface_1; + +struct _jvmtiEnv { + const struct jvmtiInterface_1_ *functions; +#ifdef __cplusplus + + + jvmtiError Allocate(jlong size, + unsigned char** mem_ptr) { + return functions->Allocate(this, size, mem_ptr); + } + + jvmtiError Deallocate(unsigned char* mem) { + return functions->Deallocate(this, mem); + } + + jvmtiError GetThreadState(jthread thread, + jint* thread_state_ptr) { + return functions->GetThreadState(this, thread, thread_state_ptr); + } + + jvmtiError GetCurrentThread(jthread* thread_ptr) { + return functions->GetCurrentThread(this, thread_ptr); + } + + jvmtiError GetAllThreads(jint* threads_count_ptr, + jthread** threads_ptr) { + return functions->GetAllThreads(this, threads_count_ptr, threads_ptr); + } + + jvmtiError SuspendThread(jthread thread) { + return functions->SuspendThread(this, thread); + } + + jvmtiError SuspendThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->SuspendThreadList(this, request_count, request_list, results); + } + + jvmtiError ResumeThread(jthread thread) { + return functions->ResumeThread(this, thread); + } + + jvmtiError ResumeThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->ResumeThreadList(this, request_count, request_list, results); + } + + jvmtiError StopThread(jthread thread, + jobject exception) { + return functions->StopThread(this, thread, exception); + } + + jvmtiError InterruptThread(jthread thread) { + return functions->InterruptThread(this, thread); + } + + jvmtiError GetThreadInfo(jthread thread, + jvmtiThreadInfo* info_ptr) { + return functions->GetThreadInfo(this, thread, info_ptr); + } + + jvmtiError GetOwnedMonitorInfo(jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr) { + return functions->GetOwnedMonitorInfo(this, thread, owned_monitor_count_ptr, owned_monitors_ptr); + } + + jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + return functions->GetOwnedMonitorStackDepthInfo(this, thread, monitor_info_count_ptr, monitor_info_ptr); + } + + jvmtiError GetCurrentContendedMonitor(jthread thread, + jobject* monitor_ptr) { + return functions->GetCurrentContendedMonitor(this, thread, monitor_ptr); + } + + jvmtiError RunAgentThread(jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority) { + return functions->RunAgentThread(this, thread, proc, arg, priority); + } + + jvmtiError SetThreadLocalStorage(jthread thread, + const void* data) { + return functions->SetThreadLocalStorage(this, thread, data); + } + + jvmtiError GetThreadLocalStorage(jthread thread, + void** data_ptr) { + return functions->GetThreadLocalStorage(this, thread, data_ptr); + } + + jvmtiError GetTopThreadGroups(jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetTopThreadGroups(this, group_count_ptr, groups_ptr); + } + + jvmtiError GetThreadGroupInfo(jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + return functions->GetThreadGroupInfo(this, group, info_ptr); + } + + jvmtiError GetThreadGroupChildren(jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetThreadGroupChildren(this, group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr); + } + + jvmtiError GetStackTrace(jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr) { + return functions->GetStackTrace(this, thread, start_depth, max_frame_count, frame_buffer, count_ptr); + } + + jvmtiError GetAllStackTraces(jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr) { + return functions->GetAllStackTraces(this, max_frame_count, stack_info_ptr, thread_count_ptr); + } + + jvmtiError GetThreadListStackTraces(jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr) { + return functions->GetThreadListStackTraces(this, thread_count, thread_list, max_frame_count, stack_info_ptr); + } + + jvmtiError GetFrameCount(jthread thread, + jint* count_ptr) { + return functions->GetFrameCount(this, thread, count_ptr); + } + + jvmtiError PopFrame(jthread thread) { + return functions->PopFrame(this, thread); + } + + jvmtiError GetFrameLocation(jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr) { + return functions->GetFrameLocation(this, thread, depth, method_ptr, location_ptr); + } + + jvmtiError NotifyFramePop(jthread thread, + jint depth) { + return functions->NotifyFramePop(this, thread, depth); + } + + jvmtiError ForceEarlyReturnObject(jthread thread, + jobject value) { + return functions->ForceEarlyReturnObject(this, thread, value); + } + + jvmtiError ForceEarlyReturnInt(jthread thread, + jint value) { + return functions->ForceEarlyReturnInt(this, thread, value); + } + + jvmtiError ForceEarlyReturnLong(jthread thread, + jlong value) { + return functions->ForceEarlyReturnLong(this, thread, value); + } + + jvmtiError ForceEarlyReturnFloat(jthread thread, + jfloat value) { + return functions->ForceEarlyReturnFloat(this, thread, value); + } + + jvmtiError ForceEarlyReturnDouble(jthread thread, + jdouble value) { + return functions->ForceEarlyReturnDouble(this, thread, value); + } + + jvmtiError ForceEarlyReturnVoid(jthread thread) { + return functions->ForceEarlyReturnVoid(this, thread); + } + + jvmtiError FollowReferences(jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->FollowReferences(this, heap_filter, klass, initial_object, callbacks, user_data); + } + + jvmtiError IterateThroughHeap(jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->IterateThroughHeap(this, heap_filter, klass, callbacks, user_data); + } + + jvmtiError GetTag(jobject object, + jlong* tag_ptr) { + return functions->GetTag(this, object, tag_ptr); + } + + jvmtiError SetTag(jobject object, + jlong tag) { + return functions->SetTag(this, object, tag); + } + + jvmtiError GetObjectsWithTags(jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr) { + return functions->GetObjectsWithTags(this, tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr); + } + + jvmtiError ForceGarbageCollection() { + return functions->ForceGarbageCollection(this); + } + + jvmtiError IterateOverObjectsReachableFromObject(jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data) { + return functions->IterateOverObjectsReachableFromObject(this, object, object_reference_callback, user_data); + } + + jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data) { + return functions->IterateOverReachableObjects(this, heap_root_callback, stack_ref_callback, object_ref_callback, user_data); + } + + jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverHeap(this, object_filter, heap_object_callback, user_data); + } + + jvmtiError IterateOverInstancesOfClass(jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverInstancesOfClass(this, klass, object_filter, heap_object_callback, user_data); + } + + jvmtiError GetLocalObject(jthread thread, + jint depth, + jint slot, + jobject* value_ptr) { + return functions->GetLocalObject(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalInstance(jthread thread, + jint depth, + jobject* value_ptr) { + return functions->GetLocalInstance(this, thread, depth, value_ptr); + } + + jvmtiError GetLocalInt(jthread thread, + jint depth, + jint slot, + jint* value_ptr) { + return functions->GetLocalInt(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalLong(jthread thread, + jint depth, + jint slot, + jlong* value_ptr) { + return functions->GetLocalLong(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat* value_ptr) { + return functions->GetLocalFloat(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble* value_ptr) { + return functions->GetLocalDouble(this, thread, depth, slot, value_ptr); + } + + jvmtiError SetLocalObject(jthread thread, + jint depth, + jint slot, + jobject value) { + return functions->SetLocalObject(this, thread, depth, slot, value); + } + + jvmtiError SetLocalInt(jthread thread, + jint depth, + jint slot, + jint value) { + return functions->SetLocalInt(this, thread, depth, slot, value); + } + + jvmtiError SetLocalLong(jthread thread, + jint depth, + jint slot, + jlong value) { + return functions->SetLocalLong(this, thread, depth, slot, value); + } + + jvmtiError SetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat value) { + return functions->SetLocalFloat(this, thread, depth, slot, value); + } + + jvmtiError SetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble value) { + return functions->SetLocalDouble(this, thread, depth, slot, value); + } + + jvmtiError SetBreakpoint(jmethodID method, + jlocation location) { + return functions->SetBreakpoint(this, method, location); + } + + jvmtiError ClearBreakpoint(jmethodID method, + jlocation location) { + return functions->ClearBreakpoint(this, method, location); + } + + jvmtiError SetFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->SetFieldAccessWatch(this, klass, field); + } + + jvmtiError ClearFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldAccessWatch(this, klass, field); + } + + jvmtiError SetFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->SetFieldModificationWatch(this, klass, field); + } + + jvmtiError ClearFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldModificationWatch(this, klass, field); + } + + jvmtiError GetLoadedClasses(jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassLoaderClasses(jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetClassLoaderClasses(this, initiating_loader, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassSignature(jclass klass, + char** signature_ptr, + char** generic_ptr) { + return functions->GetClassSignature(this, klass, signature_ptr, generic_ptr); + } + + jvmtiError GetClassStatus(jclass klass, + jint* status_ptr) { + return functions->GetClassStatus(this, klass, status_ptr); + } + + jvmtiError GetSourceFileName(jclass klass, + char** source_name_ptr) { + return functions->GetSourceFileName(this, klass, source_name_ptr); + } + + jvmtiError GetClassModifiers(jclass klass, + jint* modifiers_ptr) { + return functions->GetClassModifiers(this, klass, modifiers_ptr); + } + + jvmtiError GetClassMethods(jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr) { + return functions->GetClassMethods(this, klass, method_count_ptr, methods_ptr); + } + + jvmtiError GetClassFields(jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr) { + return functions->GetClassFields(this, klass, field_count_ptr, fields_ptr); + } + + jvmtiError GetImplementedInterfaces(jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr) { + return functions->GetImplementedInterfaces(this, klass, interface_count_ptr, interfaces_ptr); + } + + jvmtiError GetClassVersionNumbers(jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr) { + return functions->GetClassVersionNumbers(this, klass, minor_version_ptr, major_version_ptr); + } + + jvmtiError GetConstantPool(jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr) { + return functions->GetConstantPool(this, klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr); + } + + jvmtiError IsInterface(jclass klass, + jboolean* is_interface_ptr) { + return functions->IsInterface(this, klass, is_interface_ptr); + } + + jvmtiError IsArrayClass(jclass klass, + jboolean* is_array_class_ptr) { + return functions->IsArrayClass(this, klass, is_array_class_ptr); + } + + jvmtiError IsModifiableClass(jclass klass, + jboolean* is_modifiable_class_ptr) { + return functions->IsModifiableClass(this, klass, is_modifiable_class_ptr); + } + + jvmtiError GetClassLoader(jclass klass, + jobject* classloader_ptr) { + return functions->GetClassLoader(this, klass, classloader_ptr); + } + + jvmtiError GetSourceDebugExtension(jclass klass, + char** source_debug_extension_ptr) { + return functions->GetSourceDebugExtension(this, klass, source_debug_extension_ptr); + } + + jvmtiError RetransformClasses(jint class_count, + const jclass* classes) { + return functions->RetransformClasses(this, class_count, classes); + } + + jvmtiError RedefineClasses(jint class_count, + const jvmtiClassDefinition* class_definitions) { + return functions->RedefineClasses(this, class_count, class_definitions); + } + + jvmtiError GetObjectSize(jobject object, + jlong* size_ptr) { + return functions->GetObjectSize(this, object, size_ptr); + } + + jvmtiError GetObjectHashCode(jobject object, + jint* hash_code_ptr) { + return functions->GetObjectHashCode(this, object, hash_code_ptr); + } + + jvmtiError GetObjectMonitorUsage(jobject object, + jvmtiMonitorUsage* info_ptr) { + return functions->GetObjectMonitorUsage(this, object, info_ptr); + } + + jvmtiError GetFieldName(jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetFieldName(this, klass, field, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetFieldDeclaringClass(jclass klass, + jfieldID field, + jclass* declaring_class_ptr) { + return functions->GetFieldDeclaringClass(this, klass, field, declaring_class_ptr); + } + + jvmtiError GetFieldModifiers(jclass klass, + jfieldID field, + jint* modifiers_ptr) { + return functions->GetFieldModifiers(this, klass, field, modifiers_ptr); + } + + jvmtiError IsFieldSynthetic(jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr) { + return functions->IsFieldSynthetic(this, klass, field, is_synthetic_ptr); + } + + jvmtiError GetMethodName(jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetMethodName(this, method, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetMethodDeclaringClass(jmethodID method, + jclass* declaring_class_ptr) { + return functions->GetMethodDeclaringClass(this, method, declaring_class_ptr); + } + + jvmtiError GetMethodModifiers(jmethodID method, + jint* modifiers_ptr) { + return functions->GetMethodModifiers(this, method, modifiers_ptr); + } + + jvmtiError GetMaxLocals(jmethodID method, + jint* max_ptr) { + return functions->GetMaxLocals(this, method, max_ptr); + } + + jvmtiError GetArgumentsSize(jmethodID method, + jint* size_ptr) { + return functions->GetArgumentsSize(this, method, size_ptr); + } + + jvmtiError GetLineNumberTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr) { + return functions->GetLineNumberTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetMethodLocation(jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr) { + return functions->GetMethodLocation(this, method, start_location_ptr, end_location_ptr); + } + + jvmtiError GetLocalVariableTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr) { + return functions->GetLocalVariableTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetBytecodes(jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr) { + return functions->GetBytecodes(this, method, bytecode_count_ptr, bytecodes_ptr); + } + + jvmtiError IsMethodNative(jmethodID method, + jboolean* is_native_ptr) { + return functions->IsMethodNative(this, method, is_native_ptr); + } + + jvmtiError IsMethodSynthetic(jmethodID method, + jboolean* is_synthetic_ptr) { + return functions->IsMethodSynthetic(this, method, is_synthetic_ptr); + } + + jvmtiError IsMethodObsolete(jmethodID method, + jboolean* is_obsolete_ptr) { + return functions->IsMethodObsolete(this, method, is_obsolete_ptr); + } + + jvmtiError SetNativeMethodPrefix(const char* prefix) { + return functions->SetNativeMethodPrefix(this, prefix); + } + + jvmtiError SetNativeMethodPrefixes(jint prefix_count, + char** prefixes) { + return functions->SetNativeMethodPrefixes(this, prefix_count, prefixes); + } + + jvmtiError CreateRawMonitor(const char* name, + jrawMonitorID* monitor_ptr) { + return functions->CreateRawMonitor(this, name, monitor_ptr); + } + + jvmtiError DestroyRawMonitor(jrawMonitorID monitor) { + return functions->DestroyRawMonitor(this, monitor); + } + + jvmtiError RawMonitorEnter(jrawMonitorID monitor) { + return functions->RawMonitorEnter(this, monitor); + } + + jvmtiError RawMonitorExit(jrawMonitorID monitor) { + return functions->RawMonitorExit(this, monitor); + } + + jvmtiError RawMonitorWait(jrawMonitorID monitor, + jlong millis) { + return functions->RawMonitorWait(this, monitor, millis); + } + + jvmtiError RawMonitorNotify(jrawMonitorID monitor) { + return functions->RawMonitorNotify(this, monitor); + } + + jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) { + return functions->RawMonitorNotifyAll(this, monitor); + } + + jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) { + return functions->SetJNIFunctionTable(this, function_table); + } + + jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) { + return functions->GetJNIFunctionTable(this, function_table); + } + + jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + return functions->SetEventCallbacks(this, callbacks, size_of_callbacks); + } + + jvmtiError SetEventNotificationMode(jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...) { + return functions->SetEventNotificationMode(this, mode, event_type, event_thread); + } + + jvmtiError GenerateEvents(jvmtiEvent event_type) { + return functions->GenerateEvents(this, event_type); + } + + jvmtiError GetExtensionFunctions(jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions) { + return functions->GetExtensionFunctions(this, extension_count_ptr, extensions); + } + + jvmtiError GetExtensionEvents(jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions) { + return functions->GetExtensionEvents(this, extension_count_ptr, extensions); + } + + jvmtiError SetExtensionEventCallback(jint extension_event_index, + jvmtiExtensionEvent callback) { + return functions->SetExtensionEventCallback(this, extension_event_index, callback); + } + + jvmtiError GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetPotentialCapabilities(this, capabilities_ptr); + } + + jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->AddCapabilities(this, capabilities_ptr); + } + + jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->RelinquishCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetCurrentThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) { + return functions->GetCurrentThreadCpuTime(this, nanos_ptr); + } + + jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetThreadCpuTime(jthread thread, + jlong* nanos_ptr) { + return functions->GetThreadCpuTime(this, thread, nanos_ptr); + } + + jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetTimerInfo(this, info_ptr); + } + + jvmtiError GetTime(jlong* nanos_ptr) { + return functions->GetTime(this, nanos_ptr); + } + + jvmtiError GetAvailableProcessors(jint* processor_count_ptr) { + return functions->GetAvailableProcessors(this, processor_count_ptr); + } + + jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) { + return functions->AddToBootstrapClassLoaderSearch(this, segment); + } + + jvmtiError AddToSystemClassLoaderSearch(const char* segment) { + return functions->AddToSystemClassLoaderSearch(this, segment); + } + + jvmtiError GetSystemProperties(jint* count_ptr, + char*** property_ptr) { + return functions->GetSystemProperties(this, count_ptr, property_ptr); + } + + jvmtiError GetSystemProperty(const char* property, + char** value_ptr) { + return functions->GetSystemProperty(this, property, value_ptr); + } + + jvmtiError SetSystemProperty(const char* property, + const char* value) { + return functions->SetSystemProperty(this, property, value); + } + + jvmtiError GetPhase(jvmtiPhase* phase_ptr) { + return functions->GetPhase(this, phase_ptr); + } + + jvmtiError DisposeEnvironment() { + return functions->DisposeEnvironment(this); + } + + jvmtiError SetEnvironmentLocalStorage(const void* data) { + return functions->SetEnvironmentLocalStorage(this, data); + } + + jvmtiError GetEnvironmentLocalStorage(void** data_ptr) { + return functions->GetEnvironmentLocalStorage(this, data_ptr); + } + + jvmtiError GetVersionNumber(jint* version_ptr) { + return functions->GetVersionNumber(this, version_ptr); + } + + jvmtiError GetErrorName(jvmtiError error, + char** name_ptr) { + return functions->GetErrorName(this, error, name_ptr); + } + + jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag, + jboolean value) { + return functions->SetVerboseFlag(this, flag, value); + } + + jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { + return functions->GetJLocationFormat(this, format_ptr); + } + +#endif /* __cplusplus */ +}; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVA_JVMTI_H_ */ + diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jni.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jni.h new file mode 100644 index 00000000..0ffe244b --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jni.h @@ -0,0 +1,1960 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + +#endif /* __cplusplus */ +}; + +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jni_md.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jni_md.h new file mode 100644 index 00000000..38080013 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jni_md.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#define JNIEXPORT __declspec(dllexport) +#define JNIIMPORT __declspec(dllimport) +#define JNICALL __stdcall + +typedef long jint; +typedef __int64 jlong; +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jvmti.h b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jvmti.h new file mode 100644 index 00000000..812ecd60 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeInterop/cinterop/mingwX64/jvmti.h @@ -0,0 +1,2534 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + + /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */ + + + /* Include file for the Java(tm) Virtual Machine Tool Interface */ + +#ifndef _JAVA_JVMTI_H_ +#define _JAVA_JVMTI_H_ + +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + JVMTI_VERSION_1 = 0x30010000, + JVMTI_VERSION_1_0 = 0x30010000, + JVMTI_VERSION_1_1 = 0x30010100, + JVMTI_VERSION_1_2 = 0x30010200, + + JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (2 * 0x100) + 1 /* version: 1.2.1 */ +}; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved); + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM* vm, char* options, void* reserved); + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm); + + /* Forward declaration of the environment */ + +struct _jvmtiEnv; + +struct jvmtiInterface_1_; + +#ifdef __cplusplus +typedef _jvmtiEnv jvmtiEnv; +#else +typedef const struct jvmtiInterface_1_ *jvmtiEnv; +#endif /* __cplusplus */ + +/* Derived Base Types */ + +typedef jobject jthread; +typedef jobject jthreadGroup; +typedef jlong jlocation; +struct _jrawMonitorID; +typedef struct _jrawMonitorID *jrawMonitorID; +typedef struct JNINativeInterface_ jniNativeInterface; + + /* Constants */ + + + /* Thread State Flags */ + +enum { + JVMTI_THREAD_STATE_ALIVE = 0x0001, + JVMTI_THREAD_STATE_TERMINATED = 0x0002, + JVMTI_THREAD_STATE_RUNNABLE = 0x0004, + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400, + JVMTI_THREAD_STATE_WAITING = 0x0080, + JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010, + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020, + JVMTI_THREAD_STATE_SLEEPING = 0x0040, + JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100, + JVMTI_THREAD_STATE_PARKED = 0x0200, + JVMTI_THREAD_STATE_SUSPENDED = 0x100000, + JVMTI_THREAD_STATE_INTERRUPTED = 0x200000, + JVMTI_THREAD_STATE_IN_NATIVE = 0x400000, + JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000, + JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000, + JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000 +}; + + /* java.lang.Thread.State Conversion Masks */ + +enum { + JVMTI_JAVA_LANG_THREAD_STATE_MASK = JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT, + JVMTI_JAVA_LANG_THREAD_STATE_NEW = 0, + JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED = JVMTI_THREAD_STATE_TERMINATED, + JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE, + JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, + JVMTI_JAVA_LANG_THREAD_STATE_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY, + JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +}; + + /* Thread Priority Constants */ + +enum { + JVMTI_THREAD_MIN_PRIORITY = 1, + JVMTI_THREAD_NORM_PRIORITY = 5, + JVMTI_THREAD_MAX_PRIORITY = 10 +}; + + /* Heap Filter Flags */ + +enum { + JVMTI_HEAP_FILTER_TAGGED = 0x4, + JVMTI_HEAP_FILTER_UNTAGGED = 0x8, + JVMTI_HEAP_FILTER_CLASS_TAGGED = 0x10, + JVMTI_HEAP_FILTER_CLASS_UNTAGGED = 0x20 +}; + + /* Heap Visit Control Flags */ + +enum { + JVMTI_VISIT_OBJECTS = 0x100, + JVMTI_VISIT_ABORT = 0x8000 +}; + + /* Heap Reference Enumeration */ + +typedef enum { + JVMTI_HEAP_REFERENCE_CLASS = 1, + JVMTI_HEAP_REFERENCE_FIELD = 2, + JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_HEAP_REFERENCE_CLASS_LOADER = 4, + JVMTI_HEAP_REFERENCE_SIGNERS = 5, + JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_HEAP_REFERENCE_INTERFACE = 7, + JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8, + JVMTI_HEAP_REFERENCE_CONSTANT_POOL = 9, + JVMTI_HEAP_REFERENCE_SUPERCLASS = 10, + JVMTI_HEAP_REFERENCE_JNI_GLOBAL = 21, + JVMTI_HEAP_REFERENCE_SYSTEM_CLASS = 22, + JVMTI_HEAP_REFERENCE_MONITOR = 23, + JVMTI_HEAP_REFERENCE_STACK_LOCAL = 24, + JVMTI_HEAP_REFERENCE_JNI_LOCAL = 25, + JVMTI_HEAP_REFERENCE_THREAD = 26, + JVMTI_HEAP_REFERENCE_OTHER = 27 +} jvmtiHeapReferenceKind; + + /* Primitive Type Enumeration */ + +typedef enum { + JVMTI_PRIMITIVE_TYPE_BOOLEAN = 90, + JVMTI_PRIMITIVE_TYPE_BYTE = 66, + JVMTI_PRIMITIVE_TYPE_CHAR = 67, + JVMTI_PRIMITIVE_TYPE_SHORT = 83, + JVMTI_PRIMITIVE_TYPE_INT = 73, + JVMTI_PRIMITIVE_TYPE_LONG = 74, + JVMTI_PRIMITIVE_TYPE_FLOAT = 70, + JVMTI_PRIMITIVE_TYPE_DOUBLE = 68 +} jvmtiPrimitiveType; + + /* Heap Object Filter Enumeration */ + +typedef enum { + JVMTI_HEAP_OBJECT_TAGGED = 1, + JVMTI_HEAP_OBJECT_UNTAGGED = 2, + JVMTI_HEAP_OBJECT_EITHER = 3 +} jvmtiHeapObjectFilter; + + /* Heap Root Kind Enumeration */ + +typedef enum { + JVMTI_HEAP_ROOT_JNI_GLOBAL = 1, + JVMTI_HEAP_ROOT_SYSTEM_CLASS = 2, + JVMTI_HEAP_ROOT_MONITOR = 3, + JVMTI_HEAP_ROOT_STACK_LOCAL = 4, + JVMTI_HEAP_ROOT_JNI_LOCAL = 5, + JVMTI_HEAP_ROOT_THREAD = 6, + JVMTI_HEAP_ROOT_OTHER = 7 +} jvmtiHeapRootKind; + + /* Object Reference Enumeration */ + +typedef enum { + JVMTI_REFERENCE_CLASS = 1, + JVMTI_REFERENCE_FIELD = 2, + JVMTI_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_REFERENCE_CLASS_LOADER = 4, + JVMTI_REFERENCE_SIGNERS = 5, + JVMTI_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_REFERENCE_INTERFACE = 7, + JVMTI_REFERENCE_STATIC_FIELD = 8, + JVMTI_REFERENCE_CONSTANT_POOL = 9 +} jvmtiObjectReferenceKind; + + /* Iteration Control Enumeration */ + +typedef enum { + JVMTI_ITERATION_CONTINUE = 1, + JVMTI_ITERATION_IGNORE = 2, + JVMTI_ITERATION_ABORT = 0 +} jvmtiIterationControl; + + /* Class Status Flags */ + +enum { + JVMTI_CLASS_STATUS_VERIFIED = 1, + JVMTI_CLASS_STATUS_PREPARED = 2, + JVMTI_CLASS_STATUS_INITIALIZED = 4, + JVMTI_CLASS_STATUS_ERROR = 8, + JVMTI_CLASS_STATUS_ARRAY = 16, + JVMTI_CLASS_STATUS_PRIMITIVE = 32 +}; + + /* Event Enable/Disable */ + +typedef enum { + JVMTI_ENABLE = 1, + JVMTI_DISABLE = 0 +} jvmtiEventMode; + + /* Extension Function/Event Parameter Types */ + +typedef enum { + JVMTI_TYPE_JBYTE = 101, + JVMTI_TYPE_JCHAR = 102, + JVMTI_TYPE_JSHORT = 103, + JVMTI_TYPE_JINT = 104, + JVMTI_TYPE_JLONG = 105, + JVMTI_TYPE_JFLOAT = 106, + JVMTI_TYPE_JDOUBLE = 107, + JVMTI_TYPE_JBOOLEAN = 108, + JVMTI_TYPE_JOBJECT = 109, + JVMTI_TYPE_JTHREAD = 110, + JVMTI_TYPE_JCLASS = 111, + JVMTI_TYPE_JVALUE = 112, + JVMTI_TYPE_JFIELDID = 113, + JVMTI_TYPE_JMETHODID = 114, + JVMTI_TYPE_CCHAR = 115, + JVMTI_TYPE_CVOID = 116, + JVMTI_TYPE_JNIENV = 117 +} jvmtiParamTypes; + + /* Extension Function/Event Parameter Kinds */ + +typedef enum { + JVMTI_KIND_IN = 91, + JVMTI_KIND_IN_PTR = 92, + JVMTI_KIND_IN_BUF = 93, + JVMTI_KIND_ALLOC_BUF = 94, + JVMTI_KIND_ALLOC_ALLOC_BUF = 95, + JVMTI_KIND_OUT = 96, + JVMTI_KIND_OUT_BUF = 97 +} jvmtiParamKind; + + /* Timer Kinds */ + +typedef enum { + JVMTI_TIMER_USER_CPU = 30, + JVMTI_TIMER_TOTAL_CPU = 31, + JVMTI_TIMER_ELAPSED = 32 +} jvmtiTimerKind; + + /* Phases of execution */ + +typedef enum { + JVMTI_PHASE_ONLOAD = 1, + JVMTI_PHASE_PRIMORDIAL = 2, + JVMTI_PHASE_START = 6, + JVMTI_PHASE_LIVE = 4, + JVMTI_PHASE_DEAD = 8 +} jvmtiPhase; + + /* Version Interface Types */ + +enum { + JVMTI_VERSION_INTERFACE_JNI = 0x00000000, + JVMTI_VERSION_INTERFACE_JVMTI = 0x30000000 +}; + + /* Version Masks */ + +enum { + JVMTI_VERSION_MASK_INTERFACE_TYPE = 0x70000000, + JVMTI_VERSION_MASK_MAJOR = 0x0FFF0000, + JVMTI_VERSION_MASK_MINOR = 0x0000FF00, + JVMTI_VERSION_MASK_MICRO = 0x000000FF +}; + + /* Version Shifts */ + +enum { + JVMTI_VERSION_SHIFT_MAJOR = 16, + JVMTI_VERSION_SHIFT_MINOR = 8, + JVMTI_VERSION_SHIFT_MICRO = 0 +}; + + /* Verbose Flag Enumeration */ + +typedef enum { + JVMTI_VERBOSE_OTHER = 0, + JVMTI_VERBOSE_GC = 1, + JVMTI_VERBOSE_CLASS = 2, + JVMTI_VERBOSE_JNI = 4 +} jvmtiVerboseFlag; + + /* JLocation Format Enumeration */ + +typedef enum { + JVMTI_JLOCATION_JVMBCI = 1, + JVMTI_JLOCATION_MACHINEPC = 2, + JVMTI_JLOCATION_OTHER = 0 +} jvmtiJlocationFormat; + + /* Resource Exhaustion Flags */ + +enum { + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 0x0001, + JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 0x0002, + JVMTI_RESOURCE_EXHAUSTED_THREADS = 0x0004 +}; + + /* Errors */ + +typedef enum { + JVMTI_ERROR_NONE = 0, + JVMTI_ERROR_INVALID_THREAD = 10, + JVMTI_ERROR_INVALID_THREAD_GROUP = 11, + JVMTI_ERROR_INVALID_PRIORITY = 12, + JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13, + JVMTI_ERROR_THREAD_SUSPENDED = 14, + JVMTI_ERROR_THREAD_NOT_ALIVE = 15, + JVMTI_ERROR_INVALID_OBJECT = 20, + JVMTI_ERROR_INVALID_CLASS = 21, + JVMTI_ERROR_CLASS_NOT_PREPARED = 22, + JVMTI_ERROR_INVALID_METHODID = 23, + JVMTI_ERROR_INVALID_LOCATION = 24, + JVMTI_ERROR_INVALID_FIELDID = 25, + JVMTI_ERROR_NO_MORE_FRAMES = 31, + JVMTI_ERROR_OPAQUE_FRAME = 32, + JVMTI_ERROR_TYPE_MISMATCH = 34, + JVMTI_ERROR_INVALID_SLOT = 35, + JVMTI_ERROR_DUPLICATE = 40, + JVMTI_ERROR_NOT_FOUND = 41, + JVMTI_ERROR_INVALID_MONITOR = 50, + JVMTI_ERROR_NOT_MONITOR_OWNER = 51, + JVMTI_ERROR_INTERRUPT = 52, + JVMTI_ERROR_INVALID_CLASS_FORMAT = 60, + JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION = 61, + JVMTI_ERROR_FAILS_VERIFICATION = 62, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED = 63, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED = 64, + JVMTI_ERROR_INVALID_TYPESTATE = 65, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED = 66, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED = 67, + JVMTI_ERROR_UNSUPPORTED_VERSION = 68, + JVMTI_ERROR_NAMES_DONT_MATCH = 69, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71, + JVMTI_ERROR_UNMODIFIABLE_CLASS = 79, + JVMTI_ERROR_NOT_AVAILABLE = 98, + JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99, + JVMTI_ERROR_NULL_POINTER = 100, + JVMTI_ERROR_ABSENT_INFORMATION = 101, + JVMTI_ERROR_INVALID_EVENT_TYPE = 102, + JVMTI_ERROR_ILLEGAL_ARGUMENT = 103, + JVMTI_ERROR_NATIVE_METHOD = 104, + JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED = 106, + JVMTI_ERROR_OUT_OF_MEMORY = 110, + JVMTI_ERROR_ACCESS_DENIED = 111, + JVMTI_ERROR_WRONG_PHASE = 112, + JVMTI_ERROR_INTERNAL = 113, + JVMTI_ERROR_UNATTACHED_THREAD = 115, + JVMTI_ERROR_INVALID_ENVIRONMENT = 116, + JVMTI_ERROR_MAX = 116 +} jvmtiError; + + /* Event IDs */ + +typedef enum { + JVMTI_MIN_EVENT_TYPE_VAL = 50, + JVMTI_EVENT_VM_INIT = 50, + JVMTI_EVENT_VM_DEATH = 51, + JVMTI_EVENT_THREAD_START = 52, + JVMTI_EVENT_THREAD_END = 53, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54, + JVMTI_EVENT_CLASS_LOAD = 55, + JVMTI_EVENT_CLASS_PREPARE = 56, + JVMTI_EVENT_VM_START = 57, + JVMTI_EVENT_EXCEPTION = 58, + JVMTI_EVENT_EXCEPTION_CATCH = 59, + JVMTI_EVENT_SINGLE_STEP = 60, + JVMTI_EVENT_FRAME_POP = 61, + JVMTI_EVENT_BREAKPOINT = 62, + JVMTI_EVENT_FIELD_ACCESS = 63, + JVMTI_EVENT_FIELD_MODIFICATION = 64, + JVMTI_EVENT_METHOD_ENTRY = 65, + JVMTI_EVENT_METHOD_EXIT = 66, + JVMTI_EVENT_NATIVE_METHOD_BIND = 67, + JVMTI_EVENT_COMPILED_METHOD_LOAD = 68, + JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69, + JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70, + JVMTI_EVENT_DATA_DUMP_REQUEST = 71, + JVMTI_EVENT_MONITOR_WAIT = 73, + JVMTI_EVENT_MONITOR_WAITED = 74, + JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75, + JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76, + JVMTI_EVENT_RESOURCE_EXHAUSTED = 80, + JVMTI_EVENT_GARBAGE_COLLECTION_START = 81, + JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82, + JVMTI_EVENT_OBJECT_FREE = 83, + JVMTI_EVENT_VM_OBJECT_ALLOC = 84, + JVMTI_MAX_EVENT_TYPE_VAL = 84 +} jvmtiEvent; + + + /* Pre-Declarations */ +struct _jvmtiThreadInfo; +typedef struct _jvmtiThreadInfo jvmtiThreadInfo; +struct _jvmtiMonitorStackDepthInfo; +typedef struct _jvmtiMonitorStackDepthInfo jvmtiMonitorStackDepthInfo; +struct _jvmtiThreadGroupInfo; +typedef struct _jvmtiThreadGroupInfo jvmtiThreadGroupInfo; +struct _jvmtiFrameInfo; +typedef struct _jvmtiFrameInfo jvmtiFrameInfo; +struct _jvmtiStackInfo; +typedef struct _jvmtiStackInfo jvmtiStackInfo; +struct _jvmtiHeapReferenceInfoField; +typedef struct _jvmtiHeapReferenceInfoField jvmtiHeapReferenceInfoField; +struct _jvmtiHeapReferenceInfoArray; +typedef struct _jvmtiHeapReferenceInfoArray jvmtiHeapReferenceInfoArray; +struct _jvmtiHeapReferenceInfoConstantPool; +typedef struct _jvmtiHeapReferenceInfoConstantPool jvmtiHeapReferenceInfoConstantPool; +struct _jvmtiHeapReferenceInfoStackLocal; +typedef struct _jvmtiHeapReferenceInfoStackLocal jvmtiHeapReferenceInfoStackLocal; +struct _jvmtiHeapReferenceInfoJniLocal; +typedef struct _jvmtiHeapReferenceInfoJniLocal jvmtiHeapReferenceInfoJniLocal; +struct _jvmtiHeapReferenceInfoReserved; +typedef struct _jvmtiHeapReferenceInfoReserved jvmtiHeapReferenceInfoReserved; +union _jvmtiHeapReferenceInfo; +typedef union _jvmtiHeapReferenceInfo jvmtiHeapReferenceInfo; +struct _jvmtiHeapCallbacks; +typedef struct _jvmtiHeapCallbacks jvmtiHeapCallbacks; +struct _jvmtiClassDefinition; +typedef struct _jvmtiClassDefinition jvmtiClassDefinition; +struct _jvmtiMonitorUsage; +typedef struct _jvmtiMonitorUsage jvmtiMonitorUsage; +struct _jvmtiLineNumberEntry; +typedef struct _jvmtiLineNumberEntry jvmtiLineNumberEntry; +struct _jvmtiLocalVariableEntry; +typedef struct _jvmtiLocalVariableEntry jvmtiLocalVariableEntry; +struct _jvmtiParamInfo; +typedef struct _jvmtiParamInfo jvmtiParamInfo; +struct _jvmtiExtensionFunctionInfo; +typedef struct _jvmtiExtensionFunctionInfo jvmtiExtensionFunctionInfo; +struct _jvmtiExtensionEventInfo; +typedef struct _jvmtiExtensionEventInfo jvmtiExtensionEventInfo; +struct _jvmtiTimerInfo; +typedef struct _jvmtiTimerInfo jvmtiTimerInfo; +struct _jvmtiAddrLocationMap; +typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap; + + /* Function Types */ + +typedef void (JNICALL *jvmtiStartFunction) + (jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg); + +typedef jint (JNICALL *jvmtiHeapIterationCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiHeapReferenceCallback) + (jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiPrimitiveFieldCallback) + (jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong object_class_tag, jlong* object_tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data); + +typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data); + +typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data); + +typedef jint (JNICALL *jvmtiReservedCallback) + (); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback) + (jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void* user_data); + +typedef jvmtiError (JNICALL *jvmtiExtensionFunction) + (jvmtiEnv* jvmti_env, ...); + +typedef void (JNICALL *jvmtiExtensionEvent) + (jvmtiEnv* jvmti_env, ...); + + + /* Structure Types */ +struct _jvmtiThreadInfo { + char* name; + jint priority; + jboolean is_daemon; + jthreadGroup thread_group; + jobject context_class_loader; +}; +struct _jvmtiMonitorStackDepthInfo { + jobject monitor; + jint stack_depth; +}; +struct _jvmtiThreadGroupInfo { + jthreadGroup parent; + char* name; + jint max_priority; + jboolean is_daemon; +}; +struct _jvmtiFrameInfo { + jmethodID method; + jlocation location; +}; +struct _jvmtiStackInfo { + jthread thread; + jint state; + jvmtiFrameInfo* frame_buffer; + jint frame_count; +}; +struct _jvmtiHeapReferenceInfoField { + jint index; +}; +struct _jvmtiHeapReferenceInfoArray { + jint index; +}; +struct _jvmtiHeapReferenceInfoConstantPool { + jint index; +}; +struct _jvmtiHeapReferenceInfoStackLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; + jlocation location; + jint slot; +}; +struct _jvmtiHeapReferenceInfoJniLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; +}; +struct _jvmtiHeapReferenceInfoReserved { + jlong reserved1; + jlong reserved2; + jlong reserved3; + jlong reserved4; + jlong reserved5; + jlong reserved6; + jlong reserved7; + jlong reserved8; +}; +union _jvmtiHeapReferenceInfo { + jvmtiHeapReferenceInfoField field; + jvmtiHeapReferenceInfoArray array; + jvmtiHeapReferenceInfoConstantPool constant_pool; + jvmtiHeapReferenceInfoStackLocal stack_local; + jvmtiHeapReferenceInfoJniLocal jni_local; + jvmtiHeapReferenceInfoReserved other; +}; +struct _jvmtiHeapCallbacks { + jvmtiHeapIterationCallback heap_iteration_callback; + jvmtiHeapReferenceCallback heap_reference_callback; + jvmtiPrimitiveFieldCallback primitive_field_callback; + jvmtiArrayPrimitiveValueCallback array_primitive_value_callback; + jvmtiStringPrimitiveValueCallback string_primitive_value_callback; + jvmtiReservedCallback reserved5; + jvmtiReservedCallback reserved6; + jvmtiReservedCallback reserved7; + jvmtiReservedCallback reserved8; + jvmtiReservedCallback reserved9; + jvmtiReservedCallback reserved10; + jvmtiReservedCallback reserved11; + jvmtiReservedCallback reserved12; + jvmtiReservedCallback reserved13; + jvmtiReservedCallback reserved14; + jvmtiReservedCallback reserved15; +}; +struct _jvmtiClassDefinition { + jclass klass; + jint class_byte_count; + const unsigned char* class_bytes; +}; +struct _jvmtiMonitorUsage { + jthread owner; + jint entry_count; + jint waiter_count; + jthread* waiters; + jint notify_waiter_count; + jthread* notify_waiters; +}; +struct _jvmtiLineNumberEntry { + jlocation start_location; + jint line_number; +}; +struct _jvmtiLocalVariableEntry { + jlocation start_location; + jint length; + char* name; + char* signature; + char* generic_signature; + jint slot; +}; +struct _jvmtiParamInfo { + char* name; + jvmtiParamKind kind; + jvmtiParamTypes base_type; + jboolean null_ok; +}; +struct _jvmtiExtensionFunctionInfo { + jvmtiExtensionFunction func; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; + jint error_count; + jvmtiError* errors; +}; +struct _jvmtiExtensionEventInfo { + jint extension_event_index; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; +}; +struct _jvmtiTimerInfo { + jlong max_value; + jboolean may_skip_forward; + jboolean may_skip_backward; + jvmtiTimerKind kind; + jlong reserved1; + jlong reserved2; +}; +struct _jvmtiAddrLocationMap { + const void* start_address; + jlocation location; +}; + +typedef struct { + unsigned int can_tag_objects : 1; + unsigned int can_generate_field_modification_events : 1; + unsigned int can_generate_field_access_events : 1; + unsigned int can_get_bytecodes : 1; + unsigned int can_get_synthetic_attribute : 1; + unsigned int can_get_owned_monitor_info : 1; + unsigned int can_get_current_contended_monitor : 1; + unsigned int can_get_monitor_info : 1; + unsigned int can_pop_frame : 1; + unsigned int can_redefine_classes : 1; + unsigned int can_signal_thread : 1; + unsigned int can_get_source_file_name : 1; + unsigned int can_get_line_numbers : 1; + unsigned int can_get_source_debug_extension : 1; + unsigned int can_access_local_variables : 1; + unsigned int can_maintain_original_method_order : 1; + unsigned int can_generate_single_step_events : 1; + unsigned int can_generate_exception_events : 1; + unsigned int can_generate_frame_pop_events : 1; + unsigned int can_generate_breakpoint_events : 1; + unsigned int can_suspend : 1; + unsigned int can_redefine_any_class : 1; + unsigned int can_get_current_thread_cpu_time : 1; + unsigned int can_get_thread_cpu_time : 1; + unsigned int can_generate_method_entry_events : 1; + unsigned int can_generate_method_exit_events : 1; + unsigned int can_generate_all_class_hook_events : 1; + unsigned int can_generate_compiled_method_load_events : 1; + unsigned int can_generate_monitor_events : 1; + unsigned int can_generate_vm_object_alloc_events : 1; + unsigned int can_generate_native_method_bind_events : 1; + unsigned int can_generate_garbage_collection_events : 1; + unsigned int can_generate_object_free_events : 1; + unsigned int can_force_early_return : 1; + unsigned int can_get_owned_monitor_stack_depth_info : 1; + unsigned int can_get_constant_pool : 1; + unsigned int can_set_native_method_prefix : 1; + unsigned int can_retransform_classes : 1; + unsigned int can_retransform_any_class : 1; + unsigned int can_generate_resource_exhaustion_heap_events : 1; + unsigned int can_generate_resource_exhaustion_threads_events : 1; + unsigned int : 7; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; +} jvmtiCapabilities; + + + /* Event Definitions */ + +typedef void (JNICALL *jvmtiEventReserved)(void); + + +typedef void (JNICALL *jvmtiEventBreakpoint) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventClassFileLoadHook) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data); + +typedef void (JNICALL *jvmtiEventClassLoad) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventClassPrepare) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventCompiledMethodLoad) + (jvmtiEnv *jvmti_env, + jmethodID method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info); + +typedef void (JNICALL *jvmtiEventCompiledMethodUnload) + (jvmtiEnv *jvmti_env, + jmethodID method, + const void* code_addr); + +typedef void (JNICALL *jvmtiEventDataDumpRequest) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventDynamicCodeGenerated) + (jvmtiEnv *jvmti_env, + const char* name, + const void* address, + jint length); + +typedef void (JNICALL *jvmtiEventException) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception, + jmethodID catch_method, + jlocation catch_location); + +typedef void (JNICALL *jvmtiEventExceptionCatch) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception); + +typedef void (JNICALL *jvmtiEventFieldAccess) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field); + +typedef void (JNICALL *jvmtiEventFieldModification) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field, + char signature_type, + jvalue new_value); + +typedef void (JNICALL *jvmtiEventFramePop) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception); + +typedef void (JNICALL *jvmtiEventGarbageCollectionFinish) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventGarbageCollectionStart) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventMethodEntry) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method); + +typedef void (JNICALL *jvmtiEventMethodExit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception, + jvalue return_value); + +typedef void (JNICALL *jvmtiEventMonitorContendedEnter) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorContendedEntered) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorWait) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jlong timeout); + +typedef void (JNICALL *jvmtiEventMonitorWaited) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jboolean timed_out); + +typedef void (JNICALL *jvmtiEventNativeMethodBind) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + void* address, + void** new_address_ptr); + +typedef void (JNICALL *jvmtiEventObjectFree) + (jvmtiEnv *jvmti_env, + jlong tag); + +typedef void (JNICALL *jvmtiEventResourceExhausted) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint flags, + const void* reserved, + const char* description); + +typedef void (JNICALL *jvmtiEventSingleStep) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventThreadEnd) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventThreadStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMDeath) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + +typedef void (JNICALL *jvmtiEventVMInit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMObjectAlloc) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jclass object_klass, + jlong size); + +typedef void (JNICALL *jvmtiEventVMStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + + /* Event Callback Structure */ + +typedef struct { + /* 50 : VM Initialization Event */ + jvmtiEventVMInit VMInit; + /* 51 : VM Death Event */ + jvmtiEventVMDeath VMDeath; + /* 52 : Thread Start */ + jvmtiEventThreadStart ThreadStart; + /* 53 : Thread End */ + jvmtiEventThreadEnd ThreadEnd; + /* 54 : Class File Load Hook */ + jvmtiEventClassFileLoadHook ClassFileLoadHook; + /* 55 : Class Load */ + jvmtiEventClassLoad ClassLoad; + /* 56 : Class Prepare */ + jvmtiEventClassPrepare ClassPrepare; + /* 57 : VM Start Event */ + jvmtiEventVMStart VMStart; + /* 58 : Exception */ + jvmtiEventException Exception; + /* 59 : Exception Catch */ + jvmtiEventExceptionCatch ExceptionCatch; + /* 60 : Single Step */ + jvmtiEventSingleStep SingleStep; + /* 61 : Frame Pop */ + jvmtiEventFramePop FramePop; + /* 62 : Breakpoint */ + jvmtiEventBreakpoint Breakpoint; + /* 63 : Field Access */ + jvmtiEventFieldAccess FieldAccess; + /* 64 : Field Modification */ + jvmtiEventFieldModification FieldModification; + /* 65 : Method Entry */ + jvmtiEventMethodEntry MethodEntry; + /* 66 : Method Exit */ + jvmtiEventMethodExit MethodExit; + /* 67 : Native Method Bind */ + jvmtiEventNativeMethodBind NativeMethodBind; + /* 68 : Compiled Method Load */ + jvmtiEventCompiledMethodLoad CompiledMethodLoad; + /* 69 : Compiled Method Unload */ + jvmtiEventCompiledMethodUnload CompiledMethodUnload; + /* 70 : Dynamic Code Generated */ + jvmtiEventDynamicCodeGenerated DynamicCodeGenerated; + /* 71 : Data Dump Request */ + jvmtiEventDataDumpRequest DataDumpRequest; + /* 72 */ + jvmtiEventReserved reserved72; + /* 73 : Monitor Wait */ + jvmtiEventMonitorWait MonitorWait; + /* 74 : Monitor Waited */ + jvmtiEventMonitorWaited MonitorWaited; + /* 75 : Monitor Contended Enter */ + jvmtiEventMonitorContendedEnter MonitorContendedEnter; + /* 76 : Monitor Contended Entered */ + jvmtiEventMonitorContendedEntered MonitorContendedEntered; + /* 77 */ + jvmtiEventReserved reserved77; + /* 78 */ + jvmtiEventReserved reserved78; + /* 79 */ + jvmtiEventReserved reserved79; + /* 80 : Resource Exhausted */ + jvmtiEventResourceExhausted ResourceExhausted; + /* 81 : Garbage Collection Start */ + jvmtiEventGarbageCollectionStart GarbageCollectionStart; + /* 82 : Garbage Collection Finish */ + jvmtiEventGarbageCollectionFinish GarbageCollectionFinish; + /* 83 : Object Free */ + jvmtiEventObjectFree ObjectFree; + /* 84 : VM Object Allocation */ + jvmtiEventVMObjectAlloc VMObjectAlloc; +} jvmtiEventCallbacks; + + + /* Function Interface */ + +typedef struct jvmtiInterface_1_ { + + /* 1 : RESERVED */ + void *reserved1; + + /* 2 : Set Event Notification Mode */ + jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env, + jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...); + + /* 3 : RESERVED */ + void *reserved3; + + /* 4 : Get All Threads */ + jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env, + jint* threads_count_ptr, + jthread** threads_ptr); + + /* 5 : Suspend Thread */ + jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env, + jthread thread); + + /* 6 : Resume Thread */ + jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env, + jthread thread); + + /* 7 : Stop Thread */ + jvmtiError (JNICALL *StopThread) (jvmtiEnv* env, + jthread thread, + jobject exception); + + /* 8 : Interrupt Thread */ + jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env, + jthread thread); + + /* 9 : Get Thread Info */ + jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env, + jthread thread, + jvmtiThreadInfo* info_ptr); + + /* 10 : Get Owned Monitor Info */ + jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env, + jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr); + + /* 11 : Get Current Contended Monitor */ + jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env, + jthread thread, + jobject* monitor_ptr); + + /* 12 : Run Agent Thread */ + jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env, + jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority); + + /* 13 : Get Top Thread Groups */ + jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 14 : Get Thread Group Info */ + jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + /* 15 : Get Thread Group Children */ + jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 16 : Get Frame Count */ + jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env, + jthread thread, + jint* count_ptr); + + /* 17 : Get Thread State */ + jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env, + jthread thread, + jint* thread_state_ptr); + + /* 18 : Get Current Thread */ + jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env, + jthread* thread_ptr); + + /* 19 : Get Frame Location */ + jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env, + jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr); + + /* 20 : Notify Frame Pop */ + jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env, + jthread thread, + jint depth); + + /* 21 : Get Local Variable - Object */ + jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject* value_ptr); + + /* 22 : Get Local Variable - Int */ + jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint* value_ptr); + + /* 23 : Get Local Variable - Long */ + jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong* value_ptr); + + /* 24 : Get Local Variable - Float */ + jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat* value_ptr); + + /* 25 : Get Local Variable - Double */ + jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble* value_ptr); + + /* 26 : Set Local Variable - Object */ + jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject value); + + /* 27 : Set Local Variable - Int */ + jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint value); + + /* 28 : Set Local Variable - Long */ + jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong value); + + /* 29 : Set Local Variable - Float */ + jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat value); + + /* 30 : Set Local Variable - Double */ + jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble value); + + /* 31 : Create Raw Monitor */ + jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env, + const char* name, + jrawMonitorID* monitor_ptr); + + /* 32 : Destroy Raw Monitor */ + jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 33 : Raw Monitor Enter */ + jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 34 : Raw Monitor Exit */ + jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 35 : Raw Monitor Wait */ + jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env, + jrawMonitorID monitor, + jlong millis); + + /* 36 : Raw Monitor Notify */ + jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 37 : Raw Monitor Notify All */ + jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 38 : Set Breakpoint */ + jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 39 : Clear Breakpoint */ + jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 40 : RESERVED */ + void *reserved40; + + /* 41 : Set Field Access Watch */ + jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 42 : Clear Field Access Watch */ + jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 43 : Set Field Modification Watch */ + jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 44 : Clear Field Modification Watch */ + jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 45 : Is Modifiable Class */ + jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_modifiable_class_ptr); + + /* 46 : Allocate */ + jvmtiError (JNICALL *Allocate) (jvmtiEnv* env, + jlong size, + unsigned char** mem_ptr); + + /* 47 : Deallocate */ + jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env, + unsigned char* mem); + + /* 48 : Get Class Signature */ + jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env, + jclass klass, + char** signature_ptr, + char** generic_ptr); + + /* 49 : Get Class Status */ + jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env, + jclass klass, + jint* status_ptr); + + /* 50 : Get Source File Name */ + jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env, + jclass klass, + char** source_name_ptr); + + /* 51 : Get Class Modifiers */ + jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env, + jclass klass, + jint* modifiers_ptr); + + /* 52 : Get Class Methods */ + jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env, + jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr); + + /* 53 : Get Class Fields */ + jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env, + jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr); + + /* 54 : Get Implemented Interfaces */ + jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env, + jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr); + + /* 55 : Is Interface */ + jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env, + jclass klass, + jboolean* is_interface_ptr); + + /* 56 : Is Array Class */ + jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_array_class_ptr); + + /* 57 : Get Class Loader */ + jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env, + jclass klass, + jobject* classloader_ptr); + + /* 58 : Get Object Hash Code */ + jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env, + jobject object, + jint* hash_code_ptr); + + /* 59 : Get Object Monitor Usage */ + jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env, + jobject object, + jvmtiMonitorUsage* info_ptr); + + /* 60 : Get Field Name (and Signature) */ + jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env, + jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 61 : Get Field Declaring Class */ + jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jclass* declaring_class_ptr); + + /* 62 : Get Field Modifiers */ + jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jint* modifiers_ptr); + + /* 63 : Is Field Synthetic */ + jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr); + + /* 64 : Get Method Name (and Signature) */ + jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env, + jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 65 : Get Method Declaring Class */ + jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env, + jmethodID method, + jclass* declaring_class_ptr); + + /* 66 : Get Method Modifiers */ + jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env, + jmethodID method, + jint* modifiers_ptr); + + /* 67 : RESERVED */ + void *reserved67; + + /* 68 : Get Max Locals */ + jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env, + jmethodID method, + jint* max_ptr); + + /* 69 : Get Arguments Size */ + jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env, + jmethodID method, + jint* size_ptr); + + /* 70 : Get Line Number Table */ + jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr); + + /* 71 : Get Method Location */ + jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env, + jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr); + + /* 72 : Get Local Variable Table */ + jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr); + + /* 73 : Set Native Method Prefix */ + jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env, + const char* prefix); + + /* 74 : Set Native Method Prefixes */ + jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env, + jint prefix_count, + char** prefixes); + + /* 75 : Get Bytecodes */ + jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env, + jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr); + + /* 76 : Is Method Native */ + jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env, + jmethodID method, + jboolean* is_native_ptr); + + /* 77 : Is Method Synthetic */ + jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env, + jmethodID method, + jboolean* is_synthetic_ptr); + + /* 78 : Get Loaded Classes */ + jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 79 : Get Classloader Classes */ + jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 80 : Pop Frame */ + jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env, + jthread thread); + + /* 81 : Force Early Return - Object */ + jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env, + jthread thread, + jobject value); + + /* 82 : Force Early Return - Int */ + jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env, + jthread thread, + jint value); + + /* 83 : Force Early Return - Long */ + jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env, + jthread thread, + jlong value); + + /* 84 : Force Early Return - Float */ + jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env, + jthread thread, + jfloat value); + + /* 85 : Force Early Return - Double */ + jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env, + jthread thread, + jdouble value); + + /* 86 : Force Early Return - Void */ + jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env, + jthread thread); + + /* 87 : Redefine Classes */ + jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env, + jint class_count, + const jvmtiClassDefinition* class_definitions); + + /* 88 : Get Version Number */ + jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env, + jint* version_ptr); + + /* 89 : Get Capabilities */ + jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 90 : Get Source Debug Extension */ + jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env, + jclass klass, + char** source_debug_extension_ptr); + + /* 91 : Is Method Obsolete */ + jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env, + jmethodID method, + jboolean* is_obsolete_ptr); + + /* 92 : Suspend Thread List */ + jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 93 : Resume Thread List */ + jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 94 : RESERVED */ + void *reserved94; + + /* 95 : RESERVED */ + void *reserved95; + + /* 96 : RESERVED */ + void *reserved96; + + /* 97 : RESERVED */ + void *reserved97; + + /* 98 : RESERVED */ + void *reserved98; + + /* 99 : RESERVED */ + void *reserved99; + + /* 100 : Get All Stack Traces */ + jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr); + + /* 101 : Get Thread List Stack Traces */ + jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env, + jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr); + + /* 102 : Get Thread Local Storage */ + jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + void** data_ptr); + + /* 103 : Set Thread Local Storage */ + jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + const void* data); + + /* 104 : Get Stack Trace */ + jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env, + jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr); + + /* 105 : RESERVED */ + void *reserved105; + + /* 106 : Get Tag */ + jvmtiError (JNICALL *GetTag) (jvmtiEnv* env, + jobject object, + jlong* tag_ptr); + + /* 107 : Set Tag */ + jvmtiError (JNICALL *SetTag) (jvmtiEnv* env, + jobject object, + jlong tag); + + /* 108 : Force Garbage Collection */ + jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env); + + /* 109 : Iterate Over Objects Reachable From Object */ + jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env, + jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data); + + /* 110 : Iterate Over Reachable Objects */ + jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env, + jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data); + + /* 111 : Iterate Over Heap */ + jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 112 : Iterate Over Instances Of Class */ + jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env, + jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 113 : RESERVED */ + void *reserved113; + + /* 114 : Get Objects With Tags */ + jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env, + jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr); + + /* 115 : Follow References */ + jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 116 : Iterate Through Heap */ + jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 117 : RESERVED */ + void *reserved117; + + /* 118 : RESERVED */ + void *reserved118; + + /* 119 : RESERVED */ + void *reserved119; + + /* 120 : Set JNI Function Table */ + jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env, + const jniNativeInterface* function_table); + + /* 121 : Get JNI Function Table */ + jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env, + jniNativeInterface** function_table); + + /* 122 : Set Event Callbacks */ + jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks); + + /* 123 : Generate Events */ + jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env, + jvmtiEvent event_type); + + /* 124 : Get Extension Functions */ + jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions); + + /* 125 : Get Extension Events */ + jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions); + + /* 126 : Set Extension Event Callback */ + jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback); + + /* 127 : Dispose Environment */ + jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env); + + /* 128 : Get Error Name */ + jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env, + jvmtiError error, + char** name_ptr); + + /* 129 : Get JLocation Format */ + jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env, + jvmtiJlocationFormat* format_ptr); + + /* 130 : Get System Properties */ + jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env, + jint* count_ptr, + char*** property_ptr); + + /* 131 : Get System Property */ + jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env, + const char* property, + char** value_ptr); + + /* 132 : Set System Property */ + jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env, + const char* property, + const char* value); + + /* 133 : Get Phase */ + jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env, + jvmtiPhase* phase_ptr); + + /* 134 : Get Current Thread CPU Timer Information */ + jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 135 : Get Current Thread CPU Time */ + jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 136 : Get Thread CPU Timer Information */ + jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 137 : Get Thread CPU Time */ + jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env, + jthread thread, + jlong* nanos_ptr); + + /* 138 : Get Timer Information */ + jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 139 : Get Time */ + jvmtiError (JNICALL *GetTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 140 : Get Potential Capabilities */ + jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 141 : RESERVED */ + void *reserved141; + + /* 142 : Add Capabilities */ + jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 143 : Relinquish Capabilities */ + jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 144 : Get Available Processors */ + jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env, + jint* processor_count_ptr); + + /* 145 : Get Class Version Numbers */ + jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env, + jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr); + + /* 146 : Get Constant Pool */ + jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env, + jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr); + + /* 147 : Get Environment Local Storage */ + jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env, + void** data_ptr); + + /* 148 : Set Environment Local Storage */ + jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env, + const void* data); + + /* 149 : Add To Bootstrap Class Loader Search */ + jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 150 : Set Verbose Flag */ + jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env, + jvmtiVerboseFlag flag, + jboolean value); + + /* 151 : Add To System Class Loader Search */ + jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 152 : Retransform Classes */ + jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env, + jint class_count, + const jclass* classes); + + /* 153 : Get Owned Monitor Stack Depth Info */ + jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env, + jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr); + + /* 154 : Get Object Size */ + jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env, + jobject object, + jlong* size_ptr); + + /* 155 : Get Local Instance */ + jvmtiError (JNICALL *GetLocalInstance) (jvmtiEnv* env, + jthread thread, + jint depth, + jobject* value_ptr); + +} jvmtiInterface_1; + +struct _jvmtiEnv { + const struct jvmtiInterface_1_ *functions; +#ifdef __cplusplus + + + jvmtiError Allocate(jlong size, + unsigned char** mem_ptr) { + return functions->Allocate(this, size, mem_ptr); + } + + jvmtiError Deallocate(unsigned char* mem) { + return functions->Deallocate(this, mem); + } + + jvmtiError GetThreadState(jthread thread, + jint* thread_state_ptr) { + return functions->GetThreadState(this, thread, thread_state_ptr); + } + + jvmtiError GetCurrentThread(jthread* thread_ptr) { + return functions->GetCurrentThread(this, thread_ptr); + } + + jvmtiError GetAllThreads(jint* threads_count_ptr, + jthread** threads_ptr) { + return functions->GetAllThreads(this, threads_count_ptr, threads_ptr); + } + + jvmtiError SuspendThread(jthread thread) { + return functions->SuspendThread(this, thread); + } + + jvmtiError SuspendThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->SuspendThreadList(this, request_count, request_list, results); + } + + jvmtiError ResumeThread(jthread thread) { + return functions->ResumeThread(this, thread); + } + + jvmtiError ResumeThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->ResumeThreadList(this, request_count, request_list, results); + } + + jvmtiError StopThread(jthread thread, + jobject exception) { + return functions->StopThread(this, thread, exception); + } + + jvmtiError InterruptThread(jthread thread) { + return functions->InterruptThread(this, thread); + } + + jvmtiError GetThreadInfo(jthread thread, + jvmtiThreadInfo* info_ptr) { + return functions->GetThreadInfo(this, thread, info_ptr); + } + + jvmtiError GetOwnedMonitorInfo(jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr) { + return functions->GetOwnedMonitorInfo(this, thread, owned_monitor_count_ptr, owned_monitors_ptr); + } + + jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + return functions->GetOwnedMonitorStackDepthInfo(this, thread, monitor_info_count_ptr, monitor_info_ptr); + } + + jvmtiError GetCurrentContendedMonitor(jthread thread, + jobject* monitor_ptr) { + return functions->GetCurrentContendedMonitor(this, thread, monitor_ptr); + } + + jvmtiError RunAgentThread(jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority) { + return functions->RunAgentThread(this, thread, proc, arg, priority); + } + + jvmtiError SetThreadLocalStorage(jthread thread, + const void* data) { + return functions->SetThreadLocalStorage(this, thread, data); + } + + jvmtiError GetThreadLocalStorage(jthread thread, + void** data_ptr) { + return functions->GetThreadLocalStorage(this, thread, data_ptr); + } + + jvmtiError GetTopThreadGroups(jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetTopThreadGroups(this, group_count_ptr, groups_ptr); + } + + jvmtiError GetThreadGroupInfo(jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + return functions->GetThreadGroupInfo(this, group, info_ptr); + } + + jvmtiError GetThreadGroupChildren(jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetThreadGroupChildren(this, group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr); + } + + jvmtiError GetStackTrace(jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr) { + return functions->GetStackTrace(this, thread, start_depth, max_frame_count, frame_buffer, count_ptr); + } + + jvmtiError GetAllStackTraces(jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr) { + return functions->GetAllStackTraces(this, max_frame_count, stack_info_ptr, thread_count_ptr); + } + + jvmtiError GetThreadListStackTraces(jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr) { + return functions->GetThreadListStackTraces(this, thread_count, thread_list, max_frame_count, stack_info_ptr); + } + + jvmtiError GetFrameCount(jthread thread, + jint* count_ptr) { + return functions->GetFrameCount(this, thread, count_ptr); + } + + jvmtiError PopFrame(jthread thread) { + return functions->PopFrame(this, thread); + } + + jvmtiError GetFrameLocation(jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr) { + return functions->GetFrameLocation(this, thread, depth, method_ptr, location_ptr); + } + + jvmtiError NotifyFramePop(jthread thread, + jint depth) { + return functions->NotifyFramePop(this, thread, depth); + } + + jvmtiError ForceEarlyReturnObject(jthread thread, + jobject value) { + return functions->ForceEarlyReturnObject(this, thread, value); + } + + jvmtiError ForceEarlyReturnInt(jthread thread, + jint value) { + return functions->ForceEarlyReturnInt(this, thread, value); + } + + jvmtiError ForceEarlyReturnLong(jthread thread, + jlong value) { + return functions->ForceEarlyReturnLong(this, thread, value); + } + + jvmtiError ForceEarlyReturnFloat(jthread thread, + jfloat value) { + return functions->ForceEarlyReturnFloat(this, thread, value); + } + + jvmtiError ForceEarlyReturnDouble(jthread thread, + jdouble value) { + return functions->ForceEarlyReturnDouble(this, thread, value); + } + + jvmtiError ForceEarlyReturnVoid(jthread thread) { + return functions->ForceEarlyReturnVoid(this, thread); + } + + jvmtiError FollowReferences(jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->FollowReferences(this, heap_filter, klass, initial_object, callbacks, user_data); + } + + jvmtiError IterateThroughHeap(jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->IterateThroughHeap(this, heap_filter, klass, callbacks, user_data); + } + + jvmtiError GetTag(jobject object, + jlong* tag_ptr) { + return functions->GetTag(this, object, tag_ptr); + } + + jvmtiError SetTag(jobject object, + jlong tag) { + return functions->SetTag(this, object, tag); + } + + jvmtiError GetObjectsWithTags(jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr) { + return functions->GetObjectsWithTags(this, tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr); + } + + jvmtiError ForceGarbageCollection() { + return functions->ForceGarbageCollection(this); + } + + jvmtiError IterateOverObjectsReachableFromObject(jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data) { + return functions->IterateOverObjectsReachableFromObject(this, object, object_reference_callback, user_data); + } + + jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data) { + return functions->IterateOverReachableObjects(this, heap_root_callback, stack_ref_callback, object_ref_callback, user_data); + } + + jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverHeap(this, object_filter, heap_object_callback, user_data); + } + + jvmtiError IterateOverInstancesOfClass(jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverInstancesOfClass(this, klass, object_filter, heap_object_callback, user_data); + } + + jvmtiError GetLocalObject(jthread thread, + jint depth, + jint slot, + jobject* value_ptr) { + return functions->GetLocalObject(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalInstance(jthread thread, + jint depth, + jobject* value_ptr) { + return functions->GetLocalInstance(this, thread, depth, value_ptr); + } + + jvmtiError GetLocalInt(jthread thread, + jint depth, + jint slot, + jint* value_ptr) { + return functions->GetLocalInt(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalLong(jthread thread, + jint depth, + jint slot, + jlong* value_ptr) { + return functions->GetLocalLong(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat* value_ptr) { + return functions->GetLocalFloat(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble* value_ptr) { + return functions->GetLocalDouble(this, thread, depth, slot, value_ptr); + } + + jvmtiError SetLocalObject(jthread thread, + jint depth, + jint slot, + jobject value) { + return functions->SetLocalObject(this, thread, depth, slot, value); + } + + jvmtiError SetLocalInt(jthread thread, + jint depth, + jint slot, + jint value) { + return functions->SetLocalInt(this, thread, depth, slot, value); + } + + jvmtiError SetLocalLong(jthread thread, + jint depth, + jint slot, + jlong value) { + return functions->SetLocalLong(this, thread, depth, slot, value); + } + + jvmtiError SetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat value) { + return functions->SetLocalFloat(this, thread, depth, slot, value); + } + + jvmtiError SetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble value) { + return functions->SetLocalDouble(this, thread, depth, slot, value); + } + + jvmtiError SetBreakpoint(jmethodID method, + jlocation location) { + return functions->SetBreakpoint(this, method, location); + } + + jvmtiError ClearBreakpoint(jmethodID method, + jlocation location) { + return functions->ClearBreakpoint(this, method, location); + } + + jvmtiError SetFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->SetFieldAccessWatch(this, klass, field); + } + + jvmtiError ClearFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldAccessWatch(this, klass, field); + } + + jvmtiError SetFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->SetFieldModificationWatch(this, klass, field); + } + + jvmtiError ClearFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldModificationWatch(this, klass, field); + } + + jvmtiError GetLoadedClasses(jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassLoaderClasses(jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetClassLoaderClasses(this, initiating_loader, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassSignature(jvmtiClassDefinitions klass, + char** signature_ptr, + char** generic_ptr) { + return functions->GetClassSignature(this, klass, signature_ptr, generic_ptr); + } + + jvmtiError GetClassStatus(jclass klass, + jint* status_ptr) { + return functions->GetClassStatus(this, klass, status_ptr); + } + + jvmtiError GetSourceFileName(jclass klass, + char** source_name_ptr) { + return functions->GetSourceFileName(this, klass, source_name_ptr); + } + + jvmtiError GetClassModifiers(jclass klass, + jint* modifiers_ptr) { + return functions->GetClassModifiers(this, klass, modifiers_ptr); + } + + jvmtiError GetClassMethods(jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr) { + return functions->GetClassMethods(this, klass, method_count_ptr, methods_ptr); + } + + jvmtiError GetClassFields(jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr) { + return functions->GetClassFields(this, klass, field_count_ptr, fields_ptr); + } + + jvmtiError GetImplementedInterfaces(jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr) { + return functions->GetImplementedInterfaces(this, klass, interface_count_ptr, interfaces_ptr); + } + + jvmtiError GetClassVersionNumbers(jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr) { + return functions->GetClassVersionNumbers(this, klass, minor_version_ptr, major_version_ptr); + } + + jvmtiError GetConstantPool(jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr) { + return functions->GetConstantPool(this, klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr); + } + + jvmtiError IsInterface(jclass klass, + jboolean* is_interface_ptr) { + return functions->IsInterface(this, klass, is_interface_ptr); + } + + jvmtiError IsArrayClass(jclass klass, + jboolean* is_array_class_ptr) { + return functions->IsArrayClass(this, klass, is_array_class_ptr); + } + + jvmtiError IsModifiableClass(jclass klass, + jboolean* is_modifiable_class_ptr) { + return functions->IsModifiableClass(this, klass, is_modifiable_class_ptr); + } + + jvmtiError GetClassLoader(jclass klass, + jobject* classloader_ptr) { + return functions->GetClassLoader(this, klass, classloader_ptr); + } + + jvmtiError GetSourceDebugExtension(jclass klass, + char** source_debug_extension_ptr) { + return functions->GetSourceDebugExtension(this, klass, source_debug_extension_ptr); + } + + jvmtiError RetransformClasses(jint class_count, + const jclass* classes) { + return functions->RetransformClasses(this, class_count, classes); + } + + jvmtiError RedefineClasses(jint class_count, + const jvmtiClassDefinition* class_definitions) { + return functions->RedefineClasses(this, class_count, class_definitions); + } + + jvmtiError GetObjectSize(jobject object, + jlong* size_ptr) { + return functions->GetObjectSize(this, object, size_ptr); + } + + jvmtiError GetObjectHashCode(jobject object, + jint* hash_code_ptr) { + return functions->GetObjectHashCode(this, object, hash_code_ptr); + } + + jvmtiError GetObjectMonitorUsage(jobject object, + jvmtiMonitorUsage* info_ptr) { + return functions->GetObjectMonitorUsage(this, object, info_ptr); + } + + jvmtiError GetFieldName(jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetFieldName(this, klass, field, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetFieldDeclaringClass(jclass klass, + jfieldID field, + jclass* declaring_class_ptr) { + return functions->GetFieldDeclaringClass(this, klass, field, declaring_class_ptr); + } + + jvmtiError GetFieldModifiers(jclass klass, + jfieldID field, + jint* modifiers_ptr) { + return functions->GetFieldModifiers(this, klass, field, modifiers_ptr); + } + + jvmtiError IsFieldSynthetic(jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr) { + return functions->IsFieldSynthetic(this, klass, field, is_synthetic_ptr); + } + + jvmtiError GetMethodName(jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetMethodName(this, method, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetMethodDeclaringClass(jmethodID method, + jclass* declaring_class_ptr) { + return functions->GetMethodDeclaringClass(this, method, declaring_class_ptr); + } + + jvmtiError GetMethodModifiers(jmethodID method, + jint* modifiers_ptr) { + return functions->GetMethodModifiers(this, method, modifiers_ptr); + } + + jvmtiError GetMaxLocals(jmethodID method, + jint* max_ptr) { + return functions->GetMaxLocals(this, method, max_ptr); + } + + jvmtiError GetArgumentsSize(jmethodID method, + jint* size_ptr) { + return functions->GetArgumentsSize(this, method, size_ptr); + } + + jvmtiError GetLineNumberTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr) { + return functions->GetLineNumberTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetMethodLocation(jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr) { + return functions->GetMethodLocation(this, method, start_location_ptr, end_location_ptr); + } + + jvmtiError GetLocalVariableTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr) { + return functions->GetLocalVariableTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetBytecodes(jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr) { + return functions->GetBytecodes(this, method, bytecode_count_ptr, bytecodes_ptr); + } + + jvmtiError IsMethodNative(jmethodID method, + jboolean* is_native_ptr) { + return functions->IsMethodNative(this, method, is_native_ptr); + } + + jvmtiError IsMethodSynthetic(jmethodID method, + jboolean* is_synthetic_ptr) { + return functions->IsMethodSynthetic(this, method, is_synthetic_ptr); + } + + jvmtiError IsMethodObsolete(jmethodID method, + jboolean* is_obsolete_ptr) { + return functions->IsMethodObsolete(this, method, is_obsolete_ptr); + } + + jvmtiError SetNativeMethodPrefix(const char* prefix) { + return functions->SetNativeMethodPrefix(this, prefix); + } + + jvmtiError SetNativeMethodPrefixes(jint prefix_count, + char** prefixes) { + return functions->SetNativeMethodPrefixes(this, prefix_count, prefixes); + } + + jvmtiError CreateRawMonitor(const char* name, + jrawMonitorID* monitor_ptr) { + return functions->CreateRawMonitor(this, name, monitor_ptr); + } + + jvmtiError DestroyRawMonitor(jrawMonitorID monitor) { + return functions->DestroyRawMonitor(this, monitor); + } + + jvmtiError RawMonitorEnter(jrawMonitorID monitor) { + return functions->RawMonitorEnter(this, monitor); + } + + jvmtiError RawMonitorExit(jrawMonitorID monitor) { + return functions->RawMonitorExit(this, monitor); + } + + jvmtiError RawMonitorWait(jrawMonitorID monitor, + jlong millis) { + return functions->RawMonitorWait(this, monitor, millis); + } + + jvmtiError RawMonitorNotify(jrawMonitorID monitor) { + return functions->RawMonitorNotify(this, monitor); + } + + jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) { + return functions->RawMonitorNotifyAll(this, monitor); + } + + jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) { + return functions->SetJNIFunctionTable(this, function_table); + } + + jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) { + return functions->GetJNIFunctionTable(this, function_table); + } + + jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + return functions->SetEventCallbacks(this, callbacks, size_of_callbacks); + } + + jvmtiError SetEventNotificationMode(jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...) { + return functions->SetEventNotificationMode(this, mode, event_type, event_thread); + } + + jvmtiError GenerateEvents(jvmtiEvent event_type) { + return functions->GenerateEvents(this, event_type); + } + + jvmtiError GetExtensionFunctions(jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions) { + return functions->GetExtensionFunctions(this, extension_count_ptr, extensions); + } + + jvmtiError GetExtensionEvents(jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions) { + return functions->GetExtensionEvents(this, extension_count_ptr, extensions); + } + + jvmtiError SetExtensionEventCallback(jint extension_event_index, + jvmtiExtensionEvent callback) { + return functions->SetExtensionEventCallback(this, extension_event_index, callback); + } + + jvmtiError GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetPotentialCapabilities(this, capabilities_ptr); + } + + jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->AddCapabilities(this, capabilities_ptr); + } + + jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->RelinquishCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetCurrentThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) { + return functions->GetCurrentThreadCpuTime(this, nanos_ptr); + } + + jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetThreadCpuTime(jthread thread, + jlong* nanos_ptr) { + return functions->GetThreadCpuTime(this, thread, nanos_ptr); + } + + jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetTimerInfo(this, info_ptr); + } + + jvmtiError GetTime(jlong* nanos_ptr) { + return functions->GetTime(this, nanos_ptr); + } + + jvmtiError GetAvailableProcessors(jint* processor_count_ptr) { + return functions->GetAvailableProcessors(this, processor_count_ptr); + } + + jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) { + return functions->AddToBootstrapClassLoaderSearch(this, segment); + } + + jvmtiError AddToSystemClassLoaderSearch(const char* segment) { + return functions->AddToSystemClassLoaderSearch(this, segment); + } + + jvmtiError GetSystemProperties(jint* count_ptr, + char*** property_ptr) { + return functions->GetSystemProperties(this, count_ptr, property_ptr); + } + + jvmtiError GetSystemProperty(const char* property, + char** value_ptr) { + return functions->GetSystemProperty(this, property, value_ptr); + } + + jvmtiError SetSystemProperty(const char* property, + const char* value) { + return functions->SetSystemProperty(this, property, value); + } + + jvmtiError GetPhase(jvmtiPhase* phase_ptr) { + return functions->GetPhase(this, phase_ptr); + } + + jvmtiError DisposeEnvironment() { + return functions->DisposeEnvironment(this); + } + + jvmtiError SetEnvironmentLocalStorage(const void* data) { + return functions->SetEnvironmentLocalStorage(this, data); + } + + jvmtiError GetEnvironmentLocalStorage(void** data_ptr) { + return functions->GetEnvironmentLocalStorage(this, data_ptr); + } + + jvmtiError GetVersionNumber(jint* version_ptr) { + return functions->GetVersionNumber(this, version_ptr); + } + + jvmtiError GetErrorName(jvmtiError error, + char** name_ptr) { + return functions->GetErrorName(this, error, name_ptr); + } + + jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag, + jboolean value) { + return functions->SetVerboseFlag(this, flag, value); + } + + jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { + return functions->GetJLocationFormat(this, format_ptr); + } + +#endif /* __cplusplus */ +}; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVA_JVMTI_H_ */ + diff --git a/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/CheckEx.kt b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/CheckEx.kt new file mode 100644 index 00000000..396c850b --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/CheckEx.kt @@ -0,0 +1,87 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.jvmapi + +import mu.KotlinLogging +import com.epam.drill.agent.jvmapi.gen.* +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +fun checkEx(errCode: UInt, funName: String): UInt { + if (errCode == 0.toUInt()) { + return 0.toUInt() + } + val message = errorMapping[errCode] + if (message != null) + logger.warn { "$funName: $message" } + return errCode +} + +@SharedImmutable +private val logger = KotlinLogging.logger("com.epam.drill.agent.jvmapi.CheckEx") + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +private val errorMapping = mapOf( + JVMTI_ERROR_NULL_POINTER to "Pointer is unexpectedly NULL.", + JVMTI_ERROR_OUT_OF_MEMORY to "The function attempted to allocate memory and no more memory was available for allocation.", + JVMTI_ERROR_ACCESS_DENIED to "The desired functionality has not been enabled in this virtual machine.", + JVMTI_ERROR_UNATTACHED_THREAD to "The thread being used to call this function is not attached to the virtual machine. Calls must be made from attached threads. See AttachCurrentThread in the JNI invocation API.", + JVMTI_ERROR_INVALID_ENVIRONMENT to "The JVM TI environment provided is no longer connected or is not an environment.", + JVMTI_ERROR_WRONG_PHASE to "The desired functionality is not available in the current phase. Always returned if the virtual machine has completed running.", + JVMTI_ERROR_INTERNAL to "An unexpected internal error has occurred.", + JVMTI_ERROR_INVALID_PRIORITY to "Invalid priority.", + JVMTI_ERROR_THREAD_NOT_SUSPENDED to "Thread was not suspended.", + JVMTI_ERROR_THREAD_SUSPENDED to "Thread already suspended.", + JVMTI_ERROR_THREAD_NOT_ALIVE to "This operation requires the thread to be alive--that is, it must be started and not yet have died.", + JVMTI_ERROR_CLASS_NOT_PREPARED to "The class has been loaded but not yet prepared.", + JVMTI_ERROR_NO_MORE_FRAMES to "There are no Java programming language or JNI stack frames at the specified depth.", + JVMTI_ERROR_OPAQUE_FRAME to "Information about the frame is not available (e.g. for native frames).", + JVMTI_ERROR_DUPLICATE to "Item already set.", + JVMTI_ERROR_NOT_FOUND to "Desired element (e.g. field or breakpoint) not found", + JVMTI_ERROR_NOT_MONITOR_OWNER to "This thread doesn't own the raw monitor.", + JVMTI_ERROR_INTERRUPT to "The call has been interrupted before completion.", + JVMTI_ERROR_UNMODIFIABLE_CLASS to "The class cannot be modified.", + JVMTI_ERROR_NOT_AVAILABLE to "The functionality is not available in this virtual machine.", + JVMTI_ERROR_ABSENT_INFORMATION to "The requested information is not available.", + JVMTI_ERROR_INVALID_EVENT_TYPE to "The specified event type ID is not recognized.", + JVMTI_ERROR_NATIVE_METHOD to "The requested information is not available for native method.", + JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED to "The class loader does not support this operation.", + JVMTI_ERROR_INVALID_THREAD to "The passed thread is not a valid thread.", + JVMTI_ERROR_INVALID_FIELDID to "Invalid field.", + JVMTI_ERROR_INVALID_METHODID to "Invalid method.", + JVMTI_ERROR_INVALID_LOCATION to "Invalid location.", + JVMTI_ERROR_INVALID_OBJECT to "Invalid object.", + JVMTI_ERROR_INVALID_CLASS to "Invalid class.", + JVMTI_ERROR_TYPE_MISMATCH to "The variable is not an appropriate type for the function used.", + JVMTI_ERROR_INVALID_SLOT to "Invalid slot.", + JVMTI_ERROR_MUST_POSSESS_CAPABILITY to "The capability being used is false in this environment.", + JVMTI_ERROR_INVALID_THREAD_GROUP to "Thread group invalid.", + JVMTI_ERROR_INVALID_MONITOR to "Invalid raw monitor.", + JVMTI_ERROR_ILLEGAL_ARGUMENT to "Illegal argument.", + JVMTI_ERROR_INVALID_TYPESTATE to "The state of the thread has been modified, and is now inconsistent.", + JVMTI_ERROR_UNSUPPORTED_VERSION to "A new class file has a version number not supported by this VM.", + JVMTI_ERROR_INVALID_CLASS_FORMAT to "A new class file is malformed (the VM would return a ClassFormatError).", + JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION to "The new class file definitions would lead to a circular definition (the VM would return a ClassCircularityError).", + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED to "A new class file would require adding a method.", + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED to "A new class version changes a field.", + JVMTI_ERROR_FAILS_VERIFICATION to "The class bytes fail verification.", + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED to "A direct superclass is different for the new class version, or the set of directly implemented interfaces is different.", + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED to "A new class version does not declare a method declared in the old class version.", + JVMTI_ERROR_NAMES_DONT_MATCH to "The class name defined in the new class file is different from the name in the old class object.", + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED to "A new class version has different modifiers.", + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED to "A method in the new class version has different modifiers than its counterpart in the old class version." +) diff --git a/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/GlobalVariables.kt b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/GlobalVariables.kt new file mode 100644 index 00000000..75ef5e56 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/GlobalVariables.kt @@ -0,0 +1,53 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.jvmapi + +import kotlin.concurrent.AtomicReference +import kotlin.native.concurrent.freeze +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.CPointerVar +import kotlinx.cinterop.alloc +import kotlinx.cinterop.invoke +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.pointed +import kotlinx.cinterop.ptr +import kotlinx.cinterop.reinterpret +import kotlinx.cinterop.value +import com.epam.drill.agent.jvmapi.gen.JNIEnvVar +import com.epam.drill.agent.jvmapi.gen.JavaVMVar +import com.epam.drill.agent.jvmapi.gen.jvmtiEnvVar +import kotlinx.cinterop.ExperimentalForeignApi +import kotlin.native.concurrent.ThreadLocal + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val vmGlobal = AtomicReference?>(null).freeze() + +@OptIn(ExperimentalForeignApi::class) +@SharedImmutable +val jvmti = AtomicReference?>(null).freeze() + +@OptIn(ExperimentalForeignApi::class) +@ThreadLocal +val env: CPointer by lazy { + memScoped { + val vm = vmGlobal.value!! + val vmFns = vm.pointed.value!!.pointed + val jvmtiEnvPtr = alloc>() + vmFns.AttachCurrentThread!!(vm, jvmtiEnvPtr.ptr.reinterpret(), null) + jvmtiEnvPtr.value!! + } +} diff --git a/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmMethodCalls.kt b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmMethodCalls.kt new file mode 100644 index 00000000..191a0f40 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmMethodCalls.kt @@ -0,0 +1,109 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.jvmapi + +import kotlin.reflect.KCallable +import kotlin.reflect.KClass +import kotlinx.cinterop.toKString +import com.epam.drill.agent.jvmapi.gen.CallIntMethod +import com.epam.drill.agent.jvmapi.gen.CallObjectMethod +import com.epam.drill.agent.jvmapi.gen.CallVoidMethod +import com.epam.drill.agent.jvmapi.gen.GetStringUTFChars +import com.epam.drill.agent.jvmapi.gen.NewStringUTF +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +fun callObjectVoidMethod(clazz: KClass, method: String) = + getObjectVoidMethod(clazz, method).run { + CallVoidMethod(this.first, this.second) + } + +fun callObjectVoidMethod(clazz: KClass, method: KCallable) = + callObjectVoidMethod(clazz, method.name) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectVoidMethodWithBoolean(clazz: KClass, method: String, bool: Boolean) = + getObjectVoidMethodWithBoolean(clazz, method).run { + CallVoidMethod(this.first, this.second, bool) + } + +fun callObjectVoidMethodWithBoolean(clazz: KClass, method: KCallable, bool: Boolean) = + callObjectVoidMethodWithBoolean(clazz, method.name, bool) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectVoidMethodWithInt(clazz: KClass, method: String, int: Int) = + getObjectVoidMethodWithInt(clazz, method).run { + CallVoidMethod(this.first, this.second, int) + } + +fun callObjectVoidMethodWithInt(clazz: KClass, method: KCallable, int: Int) = + callObjectVoidMethodWithInt(clazz, method.name, int) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectVoidMethodWithString(clazz: KClass, method: String, string: String?) = + getObjectVoidMethodWithString(clazz, method).run { + CallVoidMethod(this.first, this.second, string?.let(::NewStringUTF)) + } + +fun callObjectVoidMethodWithString(clazz: KClass, method: KCallable, string: String?) = + callObjectVoidMethodWithString(clazz, method.name, string) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectVoidMethodWithByteArray(clazz: KClass, method: String, bytes: ByteArray) = + getObjectVoidMethodWithByteArray(clazz, method).run { + CallVoidMethod(this.first, this.second, toJByteArray(bytes)) + } + +fun callObjectVoidMethodWithByteArray(clazz: KClass, method: KCallable, bytes: ByteArray) = + callObjectVoidMethodWithByteArray(clazz, method.name, bytes) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectIntMethod(clazz: KClass, method: String) = + getObjectIntMethod(clazz, method).run { + CallIntMethod(this.first, this.second) + } + +fun callObjectIntMethod(clazz: KClass, method: KCallable) = + callObjectIntMethod(clazz, method.name) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectObjectMethodWithString(clazz: KClass, method: String, string: String?) = + getObjectObjectMethodWithString(clazz, method).run { + CallObjectMethod(this.first, this.second, string?.let(::NewStringUTF)) + } + +@OptIn(ExperimentalForeignApi::class) +@Suppress("unused") +fun callObjectObjectMethodWithString(clazz: KClass, method: KCallable, string: String?) = + callObjectObjectMethodWithString(clazz, method.name, string) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectStringMethod(clazz: KClass, method: String) = + getObjectStringMethod(clazz, method).run { + CallObjectMethod(this.first, this.second)?.let { GetStringUTFChars(it, null)?.toKString() } + } + +fun callObjectStringMethod(clazz: KClass, method: KCallable) = + callObjectStringMethod(clazz, method.name) + +@OptIn(ExperimentalForeignApi::class) +fun callObjectByteArrayMethod(clazz: KClass, method: String) = + getObjectByteArrayMethod(clazz, method).run { + CallObjectMethod(this.first, this.second)?.let(::toByteArray) + } + +fun callObjectByteArrayMethod(clazz: KClass, method: KCallable) = + callObjectByteArrayMethod(clazz, method.name) diff --git a/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmMethods.kt b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmMethods.kt new file mode 100644 index 00000000..4b86e28d --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmMethods.kt @@ -0,0 +1,69 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.jvmapi + +import kotlin.reflect.KClass +import com.epam.drill.agent.jvmapi.gen.FindClass +import com.epam.drill.agent.jvmapi.gen.GetMethodID +import com.epam.drill.agent.jvmapi.gen.GetStaticFieldID +import com.epam.drill.agent.jvmapi.gen.GetStaticObjectField +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +fun getObjectMethod(clazz: KClass, method: String, signature: String) = run { + val className = clazz.qualifiedName!!.replace(".", "/") + val classRef = FindClass(className) + val methodId = GetMethodID(classRef, method, signature) + val instanceId = GetStaticFieldID(classRef, "INSTANCE", "L$className;") + val instaceRef = GetStaticObjectField(classRef, instanceId) + instaceRef to methodId +} + +@OptIn(ExperimentalForeignApi::class) +fun getObjectVoidMethod(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "()V") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectVoidMethodWithBoolean(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "(Z)V") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectVoidMethodWithInt(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "(I)V") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectVoidMethodWithString(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "(Ljava/lang/String;)V") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectVoidMethodWithByteArray(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "([B)V") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectIntMethod(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "()I") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectObjectMethodWithString(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "(Ljava/lang/String;)Ljava/lang/Object;") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectStringMethod(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "()Ljava/lang/String;") + +@OptIn(ExperimentalForeignApi::class) +fun getObjectByteArrayMethod(clazz: KClass, method: String) = + getObjectMethod(clazz, method, "()[B") diff --git a/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmTypeConverters.kt b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmTypeConverters.kt new file mode 100644 index 00000000..93261f94 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/JvmTypeConverters.kt @@ -0,0 +1,48 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.jvmapi + +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.convert +import kotlinx.cinterop.usePinned +import platform.posix.memcpy +import com.epam.drill.agent.jvmapi.gen.* +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +fun toJByteArray(array: ByteArray) = NewByteArray(array.size)!!.apply { + array.usePinned { SetByteArrayRegion(this, 0, array.size, it.addressOf(0)) } +} + +@OptIn(ExperimentalForeignApi::class) +fun toByteArray(jarray: jobject) = ByteArray(GetArrayLength(jarray)).apply { + if (this.isEmpty()) return@apply + val buffer = GetPrimitiveArrayCritical(jarray, null) + try { + this.usePinned { memcpy(it.addressOf(0), buffer, this.size.convert()) } + } finally { + ReleasePrimitiveArrayCritical(jarray, buffer, JNI_ABORT) + } +} + +fun withStringsRelease(block: StringConverter.() -> R): R { + val stringConverter = StringConverter() + try { + return stringConverter.block() + } finally { + stringConverter.release() + } +} diff --git a/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/NativeMathodCalls.kt b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/NativeMathodCalls.kt new file mode 100644 index 00000000..c8a65b67 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/NativeMathodCalls.kt @@ -0,0 +1,50 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.jvmapi + +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.toByte +import com.epam.drill.agent.jvmapi.gen.JNIEnv +import com.epam.drill.agent.jvmapi.gen.NewStringUTF +import com.epam.drill.agent.jvmapi.gen.jobject +import com.epam.drill.agent.jvmapi.gen.jstring +import kotlinx.cinterop.ExperimentalForeignApi + +@OptIn(ExperimentalForeignApi::class) +@Suppress("UNUSED_PARAMETER") +fun callNativeVoidMethod(env: JNIEnv, thiz: jobject, method: () -> Unit) = memScoped { + method() +} + +@OptIn(ExperimentalForeignApi::class) +@Suppress("UNUSED_PARAMETER") +fun callNativeVoidMethodWithString(env: JNIEnv, thiz: jobject, method: (String?) -> Unit, string: jstring?) = memScoped { + withStringsRelease { + method(string?.let(this::toKString)) + } +} + +@OptIn(ExperimentalForeignApi::class) +@Suppress("UNUSED_PARAMETER") +fun callNativeBooleanMethod(env: JNIEnv, thiz: jobject, method: () -> Boolean?) = memScoped { + method()?.toByte()?.toUByte() +} + +@OptIn(ExperimentalForeignApi::class) +@Suppress("UNUSED_PARAMETER") +fun callNativeStringMethod(env: JNIEnv, thiz: jobject, method: () -> String?) = memScoped { + NewStringUTF(method()) +} diff --git a/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/StringConverter.kt b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/StringConverter.kt new file mode 100644 index 00000000..c45a4028 --- /dev/null +++ b/lib-jvm-shared/jvmapi/src/nativeMain/kotlin/com/epam/drill/agent/jvmapi/StringConverter.kt @@ -0,0 +1,41 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.jvmapi + +import kotlinx.cinterop.ByteVar +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.invoke +import kotlinx.cinterop.pointed +import kotlinx.cinterop.toKString +import com.epam.drill.agent.jvmapi.gen.jstring +import kotlinx.cinterop.ExperimentalForeignApi + +class StringConverter { + + @OptIn(ExperimentalForeignApi::class) + private val jni = env.pointed.pointed!! + @OptIn(ExperimentalForeignApi::class) + private val strings = mutableMapOf?>() + + @OptIn(ExperimentalForeignApi::class) + fun toKString(jstring: jstring) = jni.GetStringUTFChars!!(env, jstring, null) + .also { strings[jstring] = it } + ?.toKString()!! + + @OptIn(ExperimentalForeignApi::class) + fun release() = strings.forEach { jni.ReleaseStringUTFChars!!(env, it.key, it.value) } + +} diff --git a/lib-jvm-shared/knasm/LICENSE b/lib-jvm-shared/knasm/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/lib-jvm-shared/knasm/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib-jvm-shared/knasm/README.md b/lib-jvm-shared/knasm/README.md new file mode 100644 index 00000000..139ef028 --- /dev/null +++ b/lib-jvm-shared/knasm/README.md @@ -0,0 +1,5 @@ +# Drill KNASM + +The port of [Java ASM library](https://gitlab.ow2.org/asm/asm) to Kotlin native. Latest supported java version - 18. + +Forked from [commit](https://gitlab.ow2.org/asm/asm/-/commit/0dd78422eb156571a54638442a085b938476154e) diff --git a/lib-jvm-shared/knasm/build.gradle.kts b/lib-jvm-shared/knasm/build.gradle.kts new file mode 100644 index 00000000..ec4fd1ca --- /dev/null +++ b/lib-jvm-shared/knasm/build.gradle.kts @@ -0,0 +1,55 @@ +import java.util.Properties +import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest + +plugins { + kotlin("multiplatform") +} + +group = "com.epam.drill.agent" +version = Properties().run { + projectDir.parentFile.resolve("versions.properties").reader().use { load(it) } + getProperty("version.$name") ?: Project.DEFAULT_VERSION +} +val macosLd64: String by parent!!.extra + +repositories { + mavenCentral() +} + +kotlin { + jvm() + mingwX64() + linuxX64() + macosX64().apply { + if(macosLd64.toBoolean()){ + binaries.all { + linkerOpts("-ld64") + } + } + } + macosArm64().apply { + if (macosLd64.toBoolean()) { + binaries.all { + linkerOpts("-ld64") + } + } + } + @Suppress("UNUSED_VARIABLE") + sourceSets { + val jvmTest by getting { + dependencies { + implementation(kotlin("test-junit5")) + implementation("org.junit.jupiter:junit-jupiter:5.5.2") + } + } + } + @Suppress("UNUSED_VARIABLE") + tasks { + val jvmTest by getting(KotlinJvmTest::class) { + useJUnitPlatform() + } + } + tasks.withType { + options.compilerArgs = (options.compilerArgs + "-Xlint:none").toMutableList() + } +} diff --git a/lib-jvm-shared/knasm/src/commonMain/kotlin/System.kt b/lib-jvm-shared/knasm/src/commonMain/kotlin/System.kt new file mode 100644 index 00000000..589efb2e --- /dev/null +++ b/lib-jvm-shared/knasm/src/commonMain/kotlin/System.kt @@ -0,0 +1,16 @@ +object System { + + fun arraycopy(src: Array?, srcPos: Int, dest: Array, destPos: Int, length: Int) { + src?.copyInto(dest, destPos, srcPos, srcPos + length) + } + + fun arraycopy(src: ByteArray?, srcPos: Int, dest: ByteArray, destPos: Int, length: Int) { + src?.copyInto(dest, destPos, srcPos, srcPos + length) + } + + fun arraycopy(src: IntArray?, srcPos: Int, dest: IntArray, destPos: Int, length: Int) { + src?.copyInto(dest, destPos, srcPos, srcPos + length) + } + + +} diff --git a/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/AnnotationVisitor.kt b/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/AnnotationVisitor.kt new file mode 100644 index 00000000..21c1978a --- /dev/null +++ b/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/AnnotationVisitor.kt @@ -0,0 +1,140 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm + +import kotlin.jvm.* + +/** + * A visitor to visit a Java annotation. The methods of this class must be called in the following + * order: ( `visit` | `visitEnum` | `visitAnnotation` | `visitArray` )* + * `visitEnd`. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +abstract class AnnotationVisitor @JvmOverloads constructor(api: Int, annotationVisitor: AnnotationVisitor? = null) { + /** + * The ASM API version implemented by this visitor. The value of this field must be one of the + * `ASM`*x* values in [Opcodes]. + */ + protected val api: Int + + /** + * The annotation visitor to which this visitor must delegate method calls. May be null. + */ + protected var av: AnnotationVisitor? + + /** + * Visits a primitive value of the annotation. + * + * @param name the value name. + * @param value the actual value, whose type must be [Byte], [Boolean], [ ], [Short], [Integer] , [Long], [Float], [Double], + * [String] or [Type] of [Type.OBJECT] or [Type.ARRAY] sort. This + * value can also be an array of byte, boolean, short, char, int, long, float or double values + * (this is equivalent to using [.visitArray] and visiting each array element in turn, + * but is more convenient). + */ + open fun visit(name: String?, value: Any?) { + if (av != null) { + av!!.visit(name, value) + } + } + + /** + * Visits an enumeration value of the annotation. + * + * @param name the value name. + * @param descriptor the class descriptor of the enumeration class. + * @param value the actual enumeration value. + */ + open fun visitEnum(name: String?, descriptor: String?, value: String?) { + if (av != null) { + av!!.visitEnum(name, descriptor, value) + } + } + + /** + * Visits a nested annotation value of the annotation. + * + * @param name the value name. + * @param descriptor the class descriptor of the nested annotation class. + * @return a visitor to visit the actual nested annotation value, or null if this + * visitor is not interested in visiting this nested annotation. *The nested annotation + * value must be fully visited before calling other methods on this annotation visitor*. + */ + open fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor? { + return if (av != null) { + av!!.visitAnnotation(name, descriptor) + } else null + } + + /** + * Visits an array value of the annotation. Note that arrays of primitive values (such as byte, + * boolean, short, char, int, long, float or double) can be passed as value to [ visit][.visit]. This is what [ClassReader] does for non empty arrays of primitive values. + * + * @param name the value name. + * @return a visitor to visit the actual array value elements, or null if this visitor + * is not interested in visiting these values. The 'name' parameters passed to the methods of + * this visitor are ignored. *All the array values must be visited before calling other + * methods on this annotation visitor*. + */ + open fun visitArray(name: String?): AnnotationVisitor? { + return if (av != null) { + av!!.visitArray(name) + } else null + } + + /** Visits the end of the annotation. */ + open fun visitEnd() { + if (av != null) { + av!!.visitEnd() + } + } + /** + * Constructs a new [AnnotationVisitor]. + * + * @param api the ASM API version implemented by this visitor. Must be one of the `ASM`*x* values in [Opcodes]. + * @param annotationVisitor the annotation visitor to which this visitor must delegate method + * calls. May be null. + */ + /** + * Constructs a new [AnnotationVisitor]. + * + * @param api the ASM API version implemented by this visitor. Must be one of the `ASM`*x* values in [Opcodes]. + */ + init { + if (api != Opcodes.ASM9 && api != Opcodes.ASM8 && api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM10_EXPERIMENTAL) { + throw IllegalArgumentException("Unsupported api $api") + } + if (api == Opcodes.ASM10_EXPERIMENTAL) { + Constants.checkAsmExperimental(this) + } + this.api = api + av = annotationVisitor + } +} diff --git a/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/AnnotationWriter.kt b/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/AnnotationWriter.kt new file mode 100644 index 00000000..a0acce96 --- /dev/null +++ b/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/AnnotationWriter.kt @@ -0,0 +1,534 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm + +/** + * An [AnnotationVisitor] that generates a corresponding 'annotation' or 'type_annotation' + * structure, as defined in the Java Virtual Machine Specification (JVMS). AnnotationWriter + * instances can be chained in a doubly linked list, from which Runtime[In]Visible[Type]Annotations + * attributes can be generated with the [.putAnnotations] method. Similarly, arrays of such + * lists can be used to generate Runtime[In]VisibleParameterAnnotations attributes. + * + * @see [JVMS + * 4.7.16](https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html.jvms-4.7.16) + * + * @see [JVMS + * 4.7.20](https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html.jvms-4.7.20) + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +internal class AnnotationWriter( + symbolTable: SymbolTable, + useNamedValues: Boolean, + annotation: ByteVector, + previousAnnotation: AnnotationWriter? +) : AnnotationVisitor( /* latest api = */Opcodes.ASM9) { + /** Where the constants used in this AnnotationWriter must be stored. */ + private val symbolTable: SymbolTable + + /** + * Whether values are named or not. AnnotationWriter instances used for annotation default and + * annotation arrays use unnamed values (i.e. they generate an 'element_value' structure for each + * value, instead of an element_name_index followed by an element_value). + */ + private val useNamedValues: Boolean + + /** + * The 'annotation' or 'type_annotation' JVMS structure corresponding to the annotation values + * visited so far. All the fields of these structures, except the last one - the + * element_value_pairs array, must be set before this ByteVector is passed to the constructor + * (num_element_value_pairs can be set to 0, it is reset to the correct value in [ ][.visitEnd]). The element_value_pairs array is filled incrementally in the various visit() + * methods. + * + * + * Note: as an exception to the above rules, for AnnotationDefault attributes (which contain a + * single element_value by definition), this ByteVector is initially empty when passed to the + * constructor, and [.numElementValuePairsOffset] is set to -1. + */ + private val annotation: ByteVector + + /** + * The offset in [.annotation] where [.numElementValuePairs] must be stored (or -1 for + * the case of AnnotationDefault attributes). + */ + private val numElementValuePairsOffset: Int + + /** The number of element value pairs visited so far. */ + private var numElementValuePairs = 0 + + /** + * The previous AnnotationWriter. This field is used to store the list of annotations of a + * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations + * (annotation values of annotation type), or for AnnotationDefault attributes. + */ + private val previousAnnotation: AnnotationWriter? + + /** + * The next AnnotationWriter. This field is used to store the list of annotations of a + * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations + * (annotation values of annotation type), or for AnnotationDefault attributes. + */ + private var nextAnnotation: AnnotationWriter? = null + + // ----------------------------------------------------------------------------------------------- + // Implementation of the AnnotationVisitor abstract class + // ----------------------------------------------------------------------------------------------- + override fun visit(name: String?, value: Any?) { + // Case of an element_value with a const_value_index, class_info_index or array_index field. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. + ++numElementValuePairs + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)) + } + if (value is String) { + annotation.put12('s'.toInt(), symbolTable.addConstantUtf8(value)) + } else if (value is Byte) { + annotation.put12('B'.toInt(), symbolTable.addConstantInteger(value.toByte().toInt()).index) + } else if (value is Boolean) { + val booleanValue = if (value) 1 else 0 + annotation.put12('Z'.toInt(), symbolTable.addConstantInteger(booleanValue).index) + } else if (value is Char) { + annotation.put12('C'.toInt(), symbolTable.addConstantInteger(value.toChar().toInt()).index) + } else if (value is Short) { + annotation.put12('S'.toInt(), symbolTable.addConstantInteger(value.toShort().toInt()).index) + } else if (value is Type) { + annotation.put12('c'.toInt(), symbolTable.addConstantUtf8(value.descriptor)) + } else if (value is ByteArray) { + val byteArray = value + annotation.put12('['.toInt(), byteArray.size) + for (byteValue in byteArray) { + annotation.put12('B'.toInt(), symbolTable.addConstantInteger(byteValue.toInt()).index) + } + } else if (value is BooleanArray) { + val booleanArray = value + annotation.put12('['.toInt(), booleanArray.size) + for (booleanValue in booleanArray) { + annotation.put12('Z'.toInt(), symbolTable.addConstantInteger(if (booleanValue) 1 else 0).index) + } + } else if (value is ShortArray) { + val shortArray = value + annotation.put12('['.toInt(), shortArray.size) + for (shortValue in shortArray) { + annotation.put12('S'.toInt(), symbolTable.addConstantInteger(shortValue.toInt()).index) + } + } else if (value is CharArray) { + val charArray = value + annotation.put12('['.toInt(), charArray.size) + for (charValue in charArray) { + annotation.put12('C'.toInt(), symbolTable.addConstantInteger(charValue.toInt()).index) + } + } else if (value is IntArray) { + val intArray = value + annotation.put12('['.toInt(), intArray.size) + for (intValue in intArray) { + annotation.put12('I'.toInt(), symbolTable.addConstantInteger(intValue).index) + } + } else if (value is LongArray) { + val longArray = value + annotation.put12('['.toInt(), longArray.size) + for (longValue in longArray) { + annotation.put12('J'.toInt(), symbolTable.addConstantLong(longValue).index) + } + } else if (value is FloatArray) { + val floatArray = value + annotation.put12('['.toInt(), floatArray.size) + for (floatValue in floatArray) { + annotation.put12('F'.toInt(), symbolTable.addConstantFloat(floatValue).index) + } + } else if (value is DoubleArray) { + val doubleArray = value + annotation.put12('['.toInt(), doubleArray.size) + for (doubleValue in doubleArray) { + annotation.put12('D'.toInt(), symbolTable.addConstantDouble(doubleValue).index) + } + } else { + val symbol: Symbol = symbolTable.addConstant(value) + annotation.put12(".s.IFJDCS"[symbol.tag].toInt(), symbol.index) + } + } + + override fun visitEnum(name: String?, descriptor: String?, value: String?) { + // Case of an element_value with an enum_const_value field. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. + ++numElementValuePairs + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)) + } + annotation + .put12('e'.toInt(), symbolTable.addConstantUtf8(descriptor)) + .putShort(symbolTable.addConstantUtf8(value)) + } + + override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor { + // Case of an element_value with an annotation_value field. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. + ++numElementValuePairs + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)) + } + // Write tag and type_index, and reserve 2 bytes for num_element_value_pairs. + annotation.put12('@'.toInt(), symbolTable.addConstantUtf8(descriptor)).putShort(0) + return AnnotationWriter(symbolTable, /* useNamedValues = */true, annotation, null) + } + + override fun visitArray(name: String?): AnnotationVisitor { + // Case of an element_value with an array_value field. + // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1 + ++numElementValuePairs + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)) + } + // Write tag, and reserve 2 bytes for num_values. Here we take advantage of the fact that the + // end of an element_value of array type is similar to the end of an 'annotation' structure: an + // unsigned short num_values followed by num_values element_value, versus an unsigned short + // num_element_value_pairs, followed by num_element_value_pairs { element_name_index, + // element_value } tuples. This allows us to use an AnnotationWriter with unnamed values to + // visit the array elements. Its num_element_value_pairs will correspond to the number of array + // elements and will be stored in what is in fact num_values. + annotation.put12('['.toInt(), 0) + return AnnotationWriter(symbolTable, /* useNamedValues = */false, annotation, null) + } + + override fun visitEnd() { + if (numElementValuePairsOffset != -1) { + val data: ByteArray = annotation.data + data[numElementValuePairsOffset] = (numElementValuePairs ushr 8).toByte() + data[numElementValuePairsOffset + 1] = numElementValuePairs.toByte() + } + } + // ----------------------------------------------------------------------------------------------- + // Utility methods + // ----------------------------------------------------------------------------------------------- + /** + * Returns the size of a Runtime[In]Visible[Type]Annotations attribute containing this annotation + * and all its *predecessors* (see [.previousAnnotation]. Also adds the attribute name + * to the constant pool of the class (if not null). + * + * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or null. + * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this + * annotation and all its predecessors. This includes the size of the attribute_name_index and + * attribute_length fields. + */ + fun computeAnnotationsSize(attributeName: String?): Int { + if (attributeName != null) { + symbolTable.addConstantUtf8(attributeName) + } + // The attribute_name_index, attribute_length and num_annotations fields use 8 bytes. + var attributeSize = 8 + var annotationWriter:AnnotationWriter? = this + while (annotationWriter != null) { + attributeSize += annotationWriter.annotation.length + annotationWriter = annotationWriter.previousAnnotation + } + return attributeSize + } + + /** + * Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its + * *predecessors* (see [.previousAnnotation] in the given ByteVector. Annotations are + * put in the same order they have been visited. + * + * @param attributeNameIndex the constant pool index of the attribute name (one of + * "Runtime[In]Visible[Type]Annotations"). + * @param output where the attribute must be put. + */ + fun putAnnotations(attributeNameIndex: Int, output: ByteVector) { + var attributeLength = 2 // For num_annotations. + var numAnnotations = 0 + var annotationWriter: AnnotationWriter? = this + var firstAnnotation: AnnotationWriter? = null + while (annotationWriter != null) { + // In case the user forgot to call visitEnd(). + annotationWriter.visitEnd() + attributeLength += annotationWriter.annotation.length + numAnnotations++ + firstAnnotation = annotationWriter + annotationWriter = annotationWriter.previousAnnotation + } + output.putShort(attributeNameIndex) + output.putInt(attributeLength) + output.putShort(numAnnotations) + annotationWriter = firstAnnotation + while (annotationWriter != null) { + output.putByteArray(annotationWriter.annotation.data, 0, annotationWriter.annotation.length) + annotationWriter = annotationWriter.nextAnnotation + } + } + + companion object { + /** + * Creates a new [AnnotationWriter] using named values. + * + * @param symbolTable where the constants used in this AnnotationWriter must be stored. + * @param descriptor the class descriptor of the annotation class. + * @param previousAnnotation the previously visited annotation of the + * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or + * null in other cases (e.g. nested or array annotations). + * @return a new [AnnotationWriter] for the given annotation descriptor. + */ + fun create( + symbolTable: SymbolTable, + descriptor: String?, + previousAnnotation: AnnotationWriter? + ): AnnotationWriter { + // Create a ByteVector to hold an 'annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. + val annotation = ByteVector() + // Write type_index and reserve space for num_element_value_pairs. + annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0) + return AnnotationWriter( + symbolTable, /* useNamedValues = */true, annotation, previousAnnotation!!) + } + + /** + * Creates a new [AnnotationWriter] using named values. + * + * @param symbolTable where the constants used in this AnnotationWriter must be stored. + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * [TypeReference.CLASS_TYPE_PARAMETER], [ ][TypeReference.CLASS_TYPE_PARAMETER_BOUND] or [TypeReference.CLASS_EXTENDS]. See + * [TypeReference]. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be null if the annotation targets + * 'typeRef' as a whole. + * @param descriptor the class descriptor of the annotation class. + * @param previousAnnotation the previously visited annotation of the + * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or + * null in other cases (e.g. nested or array annotations). + * @return a new [AnnotationWriter] for the given type annotation reference and descriptor. + */ + fun create( + symbolTable: SymbolTable, + typeRef: Int, + typePath: TypePath?, + descriptor: String?, + previousAnnotation: AnnotationWriter? + ): AnnotationWriter { + // Create a ByteVector to hold a 'type_annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. + val typeAnnotation = ByteVector() + // Write target_type, target_info, and target_path. + TypeReference.putTarget(typeRef, typeAnnotation) + TypePath.put(typePath, typeAnnotation) + // Write type_index and reserve space for num_element_value_pairs. + typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0) + return AnnotationWriter( + symbolTable, /* useNamedValues = */true, typeAnnotation, previousAnnotation!!) + } + + /** + * Returns the size of the Runtime[In]Visible[Type]Annotations attributes containing the given + * annotations and all their *predecessors* (see [.previousAnnotation]. Also adds the + * attribute names to the constant pool of the class (if not null). + * + * @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or + * class. The previous ones can be accessed with the [.previousAnnotation] field. May be + * null. + * @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field, + * method or class. The previous ones can be accessed with the [.previousAnnotation] + * field. May be null. + * @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a + * field, method or class. The previous ones can be accessed with the [ ][.previousAnnotation] field. May be null. + * @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a + * field, method or class field. The previous ones can be accessed with the [ ][.previousAnnotation] field. May be null. + * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing the + * given annotations and all their predecessors. This includes the size of the + * attribute_name_index and attribute_length fields. + */ + fun computeAnnotationsSize( + lastRuntimeVisibleAnnotation: AnnotationWriter?, + lastRuntimeInvisibleAnnotation: AnnotationWriter?, + lastRuntimeVisibleTypeAnnotation: AnnotationWriter?, + lastRuntimeInvisibleTypeAnnotation: AnnotationWriter? + ): Int { + var size = 0 + if (lastRuntimeVisibleAnnotation != null) { + size += lastRuntimeVisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_ANNOTATIONS) + } + if (lastRuntimeInvisibleAnnotation != null) { + size += lastRuntimeInvisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_ANNOTATIONS) + } + if (lastRuntimeVisibleTypeAnnotation != null) { + size += lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS) + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + size += lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS) + } + return size + } + + /** + * Puts the Runtime[In]Visible[Type]Annotations attributes containing the given annotations and + * all their *predecessors* (see [.previousAnnotation] in the given ByteVector. + * Annotations are put in the same order they have been visited. + * + * @param symbolTable where the constants used in the AnnotationWriter instances are stored. + * @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or + * class. The previous ones can be accessed with the [.previousAnnotation] field. May be + * null. + * @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field, + * method or class. The previous ones can be accessed with the [.previousAnnotation] + * field. May be null. + * @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a + * field, method or class. The previous ones can be accessed with the [ ][.previousAnnotation] field. May be null. + * @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a + * field, method or class field. The previous ones can be accessed with the [ ][.previousAnnotation] field. May be null. + * @param output where the attributes must be put. + */ + fun putAnnotations( + symbolTable: SymbolTable, + lastRuntimeVisibleAnnotation: AnnotationWriter?, + lastRuntimeInvisibleAnnotation: AnnotationWriter?, + lastRuntimeVisibleTypeAnnotation: AnnotationWriter?, + lastRuntimeInvisibleTypeAnnotation: AnnotationWriter?, + output: ByteVector + ) { + lastRuntimeVisibleAnnotation?.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output) + lastRuntimeInvisibleAnnotation?.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output) + lastRuntimeVisibleTypeAnnotation?.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output) + lastRuntimeInvisibleTypeAnnotation?.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output) + } + + /** + * Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the + * annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the + * constant pool of the class. + * + * @param attributeName one of "Runtime[In]VisibleParameterAnnotations". + * @param annotationWriters an array of AnnotationWriter lists (designated by their *last* + * element). + * @param annotableParameterCount the number of elements in annotationWriters to take into account + * (elements [0..annotableParameterCount[ are taken into account). + * @return the size in bytes of a Runtime[In]VisibleParameterAnnotations attribute corresponding + * to the given sub-array of AnnotationWriter lists. This includes the size of the + * attribute_name_index and attribute_length fields. + */ + fun computeParameterAnnotationsSize( + attributeName: String?, + annotationWriters: Array, + annotableParameterCount: Int + ): Int { + // Note: attributeName is added to the constant pool by the call to computeAnnotationsSize + // below. This assumes that there is at least one non-null element in the annotationWriters + // sub-array (which is ensured by the lazy instantiation of this array in MethodWriter). + // The attribute_name_index, attribute_length and num_parameters fields use 7 bytes, and each + // element of the parameter_annotations array uses 2 bytes for its num_annotations field. + var attributeSize = 7 + 2 * annotableParameterCount + for (i in 0 until annotableParameterCount) { + val annotationWriter = annotationWriters[i] + attributeSize += if (annotationWriter == null) 0 else annotationWriter.computeAnnotationsSize( + attributeName) - 8 + } + return attributeSize + } + + /** + * Puts a Runtime[In]VisibleParameterAnnotations attribute containing all the annotation lists + * from the given AnnotationWriter sub-array in the given ByteVector. + * + * @param attributeNameIndex constant pool index of the attribute name (one of + * Runtime[In]VisibleParameterAnnotations). + * @param annotationWriters an array of AnnotationWriter lists (designated by their *last* + * element). + * @param annotableParameterCount the number of elements in annotationWriters to put (elements + * [0..annotableParameterCount[ are put). + * @param output where the attribute must be put. + */ + fun putParameterAnnotations( + attributeNameIndex: Int, + annotationWriters: Array, + annotableParameterCount: Int, + output: ByteVector + ) { + // The num_parameters field uses 1 byte, and each element of the parameter_annotations array + // uses 2 bytes for its num_annotations field. + var attributeLength = 1 + 2 * annotableParameterCount + for (i in 0 until annotableParameterCount) { + val annotationWriter = annotationWriters[i] + attributeLength += if (annotationWriter == null) 0 else annotationWriter.computeAnnotationsSize(null) - 8 + } + output.putShort(attributeNameIndex) + output.putInt(attributeLength) + output.putByte(annotableParameterCount) + for (i in 0 until annotableParameterCount) { + var annotationWriter = annotationWriters[i] + var firstAnnotation: AnnotationWriter? = null + var numAnnotations = 0 + while (annotationWriter != null) { + // In case user the forgot to call visitEnd(). + annotationWriter.visitEnd() + numAnnotations++ + firstAnnotation = annotationWriter + annotationWriter = annotationWriter.previousAnnotation + } + output.putShort(numAnnotations) + annotationWriter = firstAnnotation + while (annotationWriter != null) { + output.putByteArray( + annotationWriter.annotation.data, 0, annotationWriter.annotation.length) + annotationWriter = annotationWriter.nextAnnotation + } + } + } + } + // ----------------------------------------------------------------------------------------------- + // Constructors and factories + // ----------------------------------------------------------------------------------------------- + /** + * Constructs a new [AnnotationWriter]. + * + * @param symbolTable where the constants used in this AnnotationWriter must be stored. + * @param useNamedValues whether values are named or not. AnnotationDefault and annotation arrays + * use unnamed values. + * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to + * the visited content must be stored. This ByteVector must already contain all the fields of + * the structure except the last one (the element_value_pairs array). + * @param previousAnnotation the previously visited annotation of the + * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or + * null in other cases (e.g. nested or array annotations). + */ + init { + this.symbolTable = symbolTable + this.useNamedValues = useNamedValues + this.annotation = annotation + // By hypothesis, num_element_value_pairs is stored in the last unsigned short of 'annotation'. + numElementValuePairsOffset = if (annotation.length === 0) -1 else annotation.length - 2 + this.previousAnnotation = previousAnnotation + if (previousAnnotation != null) { + previousAnnotation.nextAnnotation = this + } + } +} diff --git a/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/Attribute.kt b/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/Attribute.kt new file mode 100644 index 00000000..b599087a --- /dev/null +++ b/lib-jvm-shared/knasm/src/commonMain/kotlin/org/objectweb/asm/Attribute.kt @@ -0,0 +1,394 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm + +/** + * A non standard class, field, method or Code attribute, as defined in the Java Virtual Machine + * Specification (JVMS). + * + * @see [JVMS + * 4.7](https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html.jvms-4.7) + * + * @see [JVMS + * 4.7.3](https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html.jvms-4.7.3) + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +open class Attribute +/** + * Constructs a new empty attribute. + * + * @param type the type of the attribute. + */( + /** The type of this attribute, also called its name in the JVMS. */ + val type: String? +) { + /** + * The raw content of this attribute, only used for unknown attributes (see [.isUnknown]). + * The 6 header bytes of the attribute (attribute_name_index and attribute_length) are *not* + * included. + */ + private lateinit var content: ByteArray + + /** + * The next attribute in this attribute list (Attribute instances can be linked via this field to + * store a list of class, field, method or Code attributes). May be null. + */ + var nextAttribute: Attribute? = null + + /** + * Returns true if this type of attribute is unknown. This means that the attribute + * content can't be parsed to extract constant pool references, labels, etc. Instead, the + * attribute content is read as an opaque byte array, and written back as is. This can lead to + * invalid attributes, if the content actually contains constant pool references, labels, or other + * symbolic references that need to be updated when there are changes to the constant pool, the + * method bytecode, etc. The default implementation of this method always returns true. + * + * @return true if this type of attribute is unknown. + */ + val isUnknown: Boolean + get() = true + + /** + * Returns true if this type of attribute is a Code attribute. + * + * @return true if this type of attribute is a Code attribute. + */ + val isCodeAttribute: Boolean + get() = false + + /** + * Returns the labels corresponding to this attribute. + * + * @return the labels corresponding to this attribute, or null if this attribute is not + * a Code attribute that contains labels. + */ + protected val labels: Array + protected get() = arrayOfNulls