Skip to content

Commit 3db26f5

Browse files
committed
New: Upgrade JMH test classes to Java 11.
This is needed to ensure we can test all the coverage approaches.
1 parent bbec4c4 commit 3db26f5

File tree

6 files changed

+238
-2
lines changed

6 files changed

+238
-2
lines changed

benchmarks/build.gradle

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import com.intellij.rt.coverage.utils.ClassFileUpgradeTask
2+
13
/*
24
* Copyright 2000-2018 JetBrains s.r.o.
35
*
@@ -34,3 +36,13 @@ dependencies {
3436
jmhImplementation 'junit:junit:4.13.1'
3537
jmhImplementation fileTree('lib')
3638
}
39+
40+
java {
41+
sourceCompatibility = JavaVersion.VERSION_11
42+
targetCompatibility = JavaVersion.VERSION_11
43+
}
44+
45+
task upgradeJmhClasspath(type: ClassFileUpgradeTask) {
46+
inputFiles = sourceSets.jmh.runtimeClasspath
47+
outputDirectory = layout.buildDirectory.dir("jmhClasspath").get()
48+
}

benchmarks/jmh.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ task NoAgent(type: JavaExec) {
4141
def configureBenchmark(JavaExec benchmark, Closure<List<String>> jvmArgs = {[]}) {
4242
benchmark.with {
4343
group = 'benchmarks'
44-
dependsOn ":benchmarks:jmhClasses"
44+
dependsOn(":benchmarks:jmhClasses", ":benchmarks:upgradeJmhClasspath")
4545
main = 'org.openjdk.jmh.Main'
4646
doFirst {
47-
classpath = project(":benchmarks").sourceSets.jmh.runtimeClasspath
47+
classpath = fileTree(project(":benchmarks").upgradeJmhClasspath.outputs.files.singleFile)
48+
classpath += project(":benchmarks").sourceSets.jmh.output.classesDirs
4849
args = [
4950
'-jvmArgs', '-Dfile.encoding=UTF-8',
5051
// benchmarks

buildSrc/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@ repositories {
2424

2525
dependencies {
2626
implementation("com.guardsquare:proguard-gradle:7.3.0")
27+
implementation("org.ow2.asm:asm-tree:9.7")
28+
implementation("org.ow2.asm:asm-commons:9.7")
29+
implementation("commons-io:commons-io:2.16.1")
2730
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2000-2024 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.intellij.rt.coverage.utils
18+
19+
import groovy.transform.CompileStatic
20+
import org.apache.commons.io.IOUtils
21+
import org.gradle.api.DefaultTask
22+
import org.gradle.api.file.Directory
23+
import org.gradle.api.file.FileCollection
24+
import org.gradle.api.tasks.InputFiles
25+
import org.gradle.api.tasks.OutputDirectory
26+
import org.gradle.api.tasks.TaskAction
27+
import org.objectweb.asm.ClassReader
28+
29+
import java.util.zip.ZipEntry
30+
import java.util.zip.ZipFile
31+
import java.util.zip.ZipOutputStream
32+
33+
@CompileStatic
34+
abstract class ClassFileUpgradeTask extends DefaultTask {
35+
@InputFiles
36+
FileCollection inputFiles
37+
38+
@OutputDirectory
39+
Directory outputDirectory
40+
41+
@TaskAction
42+
void transform() {
43+
def inputJars = inputFiles.files.findAll { it.name.endsWith(".jar") }
44+
List<ZipFile> zipFiles = []
45+
try {
46+
inputJars.each {
47+
zipFiles << new ZipFile(it)
48+
}
49+
process(zipFiles)
50+
} finally {
51+
zipFiles.each { it.close() }
52+
}
53+
}
54+
55+
private void process(List<ZipFile> zipFiles) {
56+
def superClasses = collectSuperClasses(zipFiles)
57+
transformJars(zipFiles, superClasses)
58+
}
59+
60+
private Map<String, String> collectSuperClasses(List<ZipFile> zipFiles) {
61+
Map<String, String> superClasses = [:]
62+
zipFiles.each { zip ->
63+
eachEntry(zip) { ZipEntry classEntry ->
64+
if (!classEntry.name.endsWith(".class")) {
65+
return
66+
}
67+
zip.getInputStream(classEntry).withStream { inputStream ->
68+
def reader = new ClassReader(inputStream)
69+
superClasses[reader.className] = reader.superName
70+
}
71+
}
72+
}
73+
return superClasses
74+
}
75+
76+
private void transformJars(List<ZipFile> zipFiles, Map<String, String> superClasses) {
77+
zipFiles.each { zip ->
78+
def outputFile = outputDirectory.file(new File(zip.name).name).asFile
79+
new ZipOutputStream(outputFile.newOutputStream()).withStream { output ->
80+
eachEntry(zip) { ZipEntry entry ->
81+
output.putNextEntry(new ZipEntry(entry.name))
82+
if (!entry.isDirectory()) {
83+
zip.getInputStream(entry).withStream { input ->
84+
if (entry.name.endsWith(".class")) {
85+
def bytes = transformClass(input, superClasses)
86+
IOUtils.write(bytes, output)
87+
} else {
88+
IOUtils.copy(input, output)
89+
}
90+
}
91+
}
92+
output.closeEntry()
93+
}
94+
}
95+
}
96+
}
97+
98+
private byte[] transformClass(InputStream stream, Map<String, String> superClasses) {
99+
def reader = new ClassReader(stream)
100+
def writer = new HierarchyClassWriter(superClasses)
101+
reader.accept(new UpgradingClassVisitor(writer), 0)
102+
return writer.toByteArray()
103+
}
104+
105+
private void eachEntry(ZipFile zip, Closure<?> callback) {
106+
zip.entries().iterator().each { ZipEntry entry ->
107+
callback(entry)
108+
}
109+
}
110+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2000-2024 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.intellij.rt.coverage.utils
18+
19+
import groovy.transform.CompileStatic
20+
import org.objectweb.asm.ClassWriter
21+
22+
@CompileStatic
23+
class HierarchyClassWriter extends ClassWriter {
24+
private final Map<String, String> superClasses
25+
private final Map<String, LinkedHashSet<String>> hierarchies = [:]
26+
27+
HierarchyClassWriter(Map<String, String> superClasses) {
28+
super(COMPUTE_FRAMES)
29+
this.superClasses = superClasses
30+
}
31+
32+
@Override
33+
protected String getCommonSuperClass(String type1, String type2) {
34+
def hierarchy1 = getHierarchy(type1)
35+
def hierarchy2 = getHierarchy(type2)
36+
return hierarchy1.find { it in hierarchy2 }
37+
}
38+
39+
private LinkedHashSet<String> getHierarchy(String type) {
40+
LinkedHashSet<String> result = hierarchies[type]
41+
if (result != null) {
42+
return result
43+
}
44+
result = []
45+
hierarchies[type] = result
46+
while (type != null) {
47+
result << type
48+
type = getSuperClass(type)
49+
}
50+
result << "java/lang/Object"
51+
return result
52+
}
53+
54+
private String getSuperClass(String type) {
55+
String known = superClasses[type]
56+
if (known != null) {
57+
return known
58+
}
59+
def clazz = Class.forName(type.replace('/', '.'), false, classLoader)
60+
return clazz.superclass?.name?.replace('.', '/')
61+
}
62+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2000-2024 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.intellij.rt.coverage.utils
18+
19+
import org.objectweb.asm.ClassVisitor
20+
import org.objectweb.asm.MethodVisitor
21+
import org.objectweb.asm.Opcodes
22+
import org.objectweb.asm.commons.JSRInlinerAdapter
23+
24+
class UpgradingClassVisitor extends ClassVisitor {
25+
private int version
26+
27+
protected UpgradingClassVisitor(ClassVisitor classVisitor) {
28+
super(Opcodes.ASM9, classVisitor)
29+
}
30+
31+
@Override
32+
void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
33+
this.version = version
34+
if (version < Opcodes.V11) {
35+
version = Opcodes.V11
36+
}
37+
super.visit(version, access, name, signature, superName, interfaces)
38+
}
39+
40+
@Override
41+
MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
42+
def mv = super.visitMethod(access, name, descriptor, signature, exceptions)
43+
if (version < Opcodes.V1_7) {
44+
return new JSRInlinerAdapter(mv, access, name, descriptor, signature, exceptions)
45+
}
46+
return mv
47+
}
48+
}

0 commit comments

Comments
 (0)