< prev index next >
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java
Print this page
rev 58760 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: alanb, cjplummer, coleenp, dholmes, dlong, forax, jlahoda, psandoz, plevart, vromero
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com, jan.lahoda@oracle.com, amy.lu@oracle.com
@@ -23,11 +23,11 @@
* questions.
*/
package jdk.nashorn.internal.runtime;
-import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
@@ -39,10 +39,11 @@
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup.ClassOption;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.module.Configuration;
@@ -65,11 +66,10 @@
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -82,14 +82,16 @@
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.script.ScriptEngine;
+
import jdk.dynalink.DynamicLinker;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.WeakValueCache;
import jdk.nashorn.internal.codegen.Compiler;
@@ -106,11 +108,10 @@
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
import jdk.nashorn.internal.runtime.options.Options;
-import jdk.internal.misc.Unsafe;
/**
* This class manages the global state of execution. Context is immutable.
*/
public final class Context {
@@ -316,25 +317,64 @@
}
private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>();
private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller {
- private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost";
private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes();
- private final Class<?> hostClass;
+ private final MethodHandles.Lookup hostLookup;
private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) {
super(context, codeSource);
- this.hostClass = hostClass;
+ this.hostLookup = (MethodHandles.Lookup)staticFieldValue(hostClass, "LOOKUP");
+ }
+
+ private static Object staticFieldValue(Class<?> c, String name) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ Field f = c.getDeclaredField(name);
+ return f.get(null);
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw new InternalError(e.getCause());
+ }
}
@Override
public Class<?> install(final String className, final byte[] bytecode) {
+ try {
ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment();
- return UNSAFE.defineAnonymousClass(hostClass, bytecode, null);
+ // Workaround: define it as a hidden nestmate so that the hostLookup can find private members
+ return hostLookup.defineHiddenClass(bytecode, true, ClassOption.NESTMATE).lookupClass();
+ } catch (IllegalAccessException e) {
+ throw new InternalError(e);
+ }
+ }
+
+ @Override
+ public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ for (final Class<?> clazz : classes) {
+ try {
+ //use reflection to write source and constants table to installed classes
+ MethodHandle sourceField = hostLookup.findStaticSetter(clazz, SOURCE.symbolName(), Source.class);
+ sourceField.invokeExact(source);
+ MethodHandle constantsField = hostLookup.findStaticSetter(clazz, CONSTANTS.symbolName(), Object[].class);
+ constantsField.invokeExact(constants);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+ });
}
@Override
public CodeInstaller getOnDemandCompilationInstaller() {
// This code loader can be indefinitely reused for on-demand recompilations for the same code source.
@@ -348,12 +388,31 @@
// produces named classes.
return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
}
private static byte[] getAnonymousHostClassBytes() {
+ // Workaround: define a host class in a non-exported package.
+ // This should be replaced when there is a mechanism to define
+ // a hidden class in a given package of a given module.
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
- cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null);
+ final String cn = ANONYMOUS_HOST_CLASS_NAME.replace('.', '/');
+ cw.visit(V13, ACC_PUBLIC|ACC_INTERFACE |ACC_ABSTRACT, cn, null, "java/lang/Object", null);
+ {
+ FieldVisitor fv = cw.visitField(ACC_PUBLIC|ACC_STATIC|ACC_FINAL, "LOOKUP",
+ "Ljava/lang/invoke/MethodHandles$Lookup;", null, null);
+ fv.visitEnd();
+ }
+ {
+ MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+ mv.visitCode();
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup",
+ "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
+ mv.visitFieldInsn(PUTSTATIC, cn, "LOOKUP", "Ljava/lang/invoke/MethodHandles$Lookup;");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
cw.visitEnd();
return cw.toByteArray();
}
}
< prev index next >