diff --git a/test/micro/org/openjdk/bench/java/security/ProtectionDomainBench.java b/test/micro/org/openjdk/bench/java/security/ProtectionDomainBench.java new file mode 100644 index 00000000000..83765805f77 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/security/ProtectionDomainBench.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.security; + +import java.security.*; +import java.net.*; +import java.io.*; + +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import org.openjdk.bench.util.InMemoryJavaCompiler; + +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 5, time = 2) +@Measurement(iterations = 5, time = 2) +@BenchmarkMode(Mode.Throughput) +public class ProtectionDomainBench { + + @Param({"100"}) + public int numberOfClasses; + + @Param({"10"}) + public int numberOfCodeSources; + + static byte[][] compiledClasses; + static Class[] loadedClasses; + static String[] classNames; + static int index = 0; + static CodeSource[] cs; + + static String B(int count) { + return "public class B" + count + " {" + + " static int intField;" + + " public static void compiledMethod() { " + + " intField++;" + + " }" + + "}"; + } + + @Setup(Level.Trial) + public void setupClasses() throws Exception { + compiledClasses = new byte[numberOfClasses][]; + loadedClasses = new Class[numberOfClasses]; + classNames = new String[numberOfClasses]; + cs = new CodeSource[numberOfCodeSources]; + + for (int i = 0; i < numberOfCodeSources; i++) { + URL u = new URL("file:/tmp/duke" + i); + cs[i] = new CodeSource(u, (java.security.cert.Certificate[]) null); + } + + for (int i = 0; i < numberOfClasses; i++) { + classNames[i] = "B" + i; + compiledClasses[i] = InMemoryJavaCompiler.compile(classNames[i], B(i)); + } + + } + + static class ProtectionDomainBenchLoader extends SecureClassLoader { + + ProtectionDomainBenchLoader() { + super(); + } + + ProtectionDomainBenchLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (name.equals(classNames[index] /* "B" + index */)) { + assert compiledClasses[index] != null; + return defineClass(name, compiledClasses[index] , 0, (compiledClasses[index]).length, cs[index % cs.length] ); + } else { + return super.findClass(name); + } + } + } + + void work() throws ClassNotFoundException { + ProtectionDomainBench.ProtectionDomainBenchLoader loader1 = new + ProtectionDomainBench.ProtectionDomainBenchLoader(); + + for (index = 0; index < compiledClasses.length; index++) { + Class c = loader1.findClass(classNames[index]); + loadedClasses[index] = c; + } + } + + @Benchmark + @Fork(value = 3, jvmArgsPrepend={"-Djava.security.manager=allow"}) + public void withSecurityManager() throws ClassNotFoundException { + work(); + } + + @Benchmark + @Fork(value = 3) + public void noSecurityManager() throws ClassNotFoundException { + work(); + } +} diff --git a/test/micro/org/openjdk/bench/util/InMemoryJavaCompiler.java b/test/micro/org/openjdk/bench/util/InMemoryJavaCompiler.java new file mode 100644 index 00000000000..5f1fafdf05c --- /dev/null +++ b/test/micro/org/openjdk/bench/util/InMemoryJavaCompiler.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.tools.ForwardingJavaFileManager; +import javax.tools.FileObject; +import javax.tools.JavaCompiler; +import javax.tools.JavaCompiler.CompilationTask; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; + +/** + * {@code InMemoryJavaCompiler} can be used for compiling a {@link + * CharSequence} to a {@code byte[]}. + * + * The compiler will not use the file system at all, instead using a {@link + * ByteArrayOutputStream} for storing the byte code. For the source code, any + * kind of {@link CharSequence} can be used, e.g. {@link String}, {@link + * StringBuffer} or {@link StringBuilder}. + * + * The {@code InMemoryCompiler} can easily be used together with a {@code + * ByteClassLoader} to easily compile and load source code in a {@link String}: + * + *
+ * {@code
+ * import jdk.test.lib.compiler.InMemoryJavaCompiler;
+ * import jdk.test.lib.ByteClassLoader;
+ *
+ * class Example {
+ *     public static void main(String[] args) {
+ *         String className = "Foo";
+ *         String sourceCode = "public class " + className + " {" +
+ *                             "    public void bar() {" +
+ *                             "        System.out.println("Hello from bar!");" +
+ *                             "    }" +
+ *                             "}";
+ *         byte[] byteCode = InMemoryJavaCompiler.compile(className, sourceCode);
+ *         Class fooClass = ByteClassLoader.load(className, byteCode);
+ *     }
+ * }
+ * }
+ * 
+ */ +public class InMemoryJavaCompiler { + private static class MemoryJavaFileObject extends SimpleJavaFileObject { + private final String className; + private final CharSequence sourceCode; + private final ByteArrayOutputStream byteCode; + + public MemoryJavaFileObject(String className, CharSequence sourceCode) { + super(URI.create("string:///" + className.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); + this.className = className; + this.sourceCode = sourceCode; + this.byteCode = new ByteArrayOutputStream(); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return sourceCode; + } + + @Override + public OutputStream openOutputStream() throws IOException { + return byteCode; + } + + public byte[] getByteCode() { + return byteCode.toByteArray(); + } + + public String getClassName() { + return className; + } + } + + private static class FileManagerWrapper extends ForwardingJavaFileManager { + private static final Location PATCH_LOCATION = new Location() { + @Override + public String getName() { + return "patch module location"; + } + + @Override + public boolean isOutputLocation() { + return false; + } + }; + private final MemoryJavaFileObject file; + private final String moduleOverride; + + public FileManagerWrapper(MemoryJavaFileObject file, String moduleOverride) { + super(getCompiler().getStandardFileManager(null, null, null)); + this.file = file; + this.moduleOverride = moduleOverride; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, + Kind kind, FileObject sibling) + throws IOException { + if (!file.getClassName().equals(className)) { + throw new IOException("Expected class with name " + file.getClassName() + + ", but got " + className); + } + return file; + } + + @Override + public Location getLocationForModule(Location location, JavaFileObject fo) throws IOException { + if (fo == file && moduleOverride != null) { + return PATCH_LOCATION; + } + return super.getLocationForModule(location, fo); + } + + @Override + public String inferModuleName(Location location) throws IOException { + if (location == PATCH_LOCATION) { + return moduleOverride; + } + return super.inferModuleName(location); + } + + @Override + public boolean hasLocation(Location location) { + return super.hasLocation(location) || location == StandardLocation.PATCH_MODULE_PATH; + } + + } + + /** + * Compiles the class with the given name and source code. + * + * @param className The name of the class + * @param sourceCode The source code for the class with name {@code className} + * @param options additional command line options + * @throws RuntimeException if the compilation did not succeed + * @return The resulting byte code from the compilation + */ + public static byte[] compile(String className, CharSequence sourceCode, String... options) { + MemoryJavaFileObject file = new MemoryJavaFileObject(className, sourceCode); + CompilationTask task = getCompilationTask(file, options); + + if(!task.call()) { + throw new RuntimeException("Could not compile " + className + " with source code " + sourceCode); + } + + return file.getByteCode(); + } + + private static JavaCompiler getCompiler() { + return ToolProvider.getSystemJavaCompiler(); + } + + private static CompilationTask getCompilationTask(MemoryJavaFileObject file, String... options) { + List opts = new ArrayList<>(); + String moduleOverride = null; + for (String opt : options) { + if (opt.startsWith("--patch-module=")) { + moduleOverride = opt.substring("--patch-module=".length()); + } else { + opts.add(opt); + } + } + return getCompiler().getTask(null, new FileManagerWrapper(file, moduleOverride), null, opts, null, Arrays.asList(file)); + } +}