23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package jdk.nashorn.internal.runtime; |
26 package jdk.nashorn.internal.runtime; |
27 |
27 |
|
28 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; |
28 import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT; |
29 import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT; |
|
30 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; |
29 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; |
31 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; |
30 import static jdk.nashorn.internal.lookup.Lookup.MH; |
32 import static jdk.nashorn.internal.lookup.Lookup.MH; |
31 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
33 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
32 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; |
34 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; |
33 |
35 |
36 import java.io.PrintWriter; |
38 import java.io.PrintWriter; |
37 import java.lang.invoke.MethodHandle; |
39 import java.lang.invoke.MethodHandle; |
38 import java.lang.invoke.MethodHandles; |
40 import java.lang.invoke.MethodHandles; |
39 import java.lang.ref.ReferenceQueue; |
41 import java.lang.ref.ReferenceQueue; |
40 import java.lang.ref.SoftReference; |
42 import java.lang.ref.SoftReference; |
|
43 import java.lang.reflect.Field; |
41 import java.lang.reflect.Modifier; |
44 import java.lang.reflect.Modifier; |
42 import java.net.MalformedURLException; |
45 import java.net.MalformedURLException; |
43 import java.net.URL; |
46 import java.net.URL; |
44 import java.security.AccessControlContext; |
47 import java.security.AccessControlContext; |
45 import java.security.AccessController; |
48 import java.security.AccessController; |
46 import java.security.CodeSigner; |
49 import java.security.CodeSigner; |
47 import java.security.CodeSource; |
50 import java.security.CodeSource; |
48 import java.security.Permissions; |
51 import java.security.Permissions; |
49 import java.security.PrivilegedAction; |
52 import java.security.PrivilegedAction; |
|
53 import java.security.PrivilegedActionException; |
|
54 import java.security.PrivilegedExceptionAction; |
50 import java.security.ProtectionDomain; |
55 import java.security.ProtectionDomain; |
|
56 import java.util.HashMap; |
51 import java.util.LinkedHashMap; |
57 import java.util.LinkedHashMap; |
52 import java.util.Map; |
58 import java.util.Map; |
53 import java.util.concurrent.atomic.AtomicLong; |
59 import java.util.concurrent.atomic.AtomicLong; |
54 import jdk.internal.org.objectweb.asm.ClassReader; |
60 import jdk.internal.org.objectweb.asm.ClassReader; |
55 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; |
61 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; |
131 public ScriptEnvironment getOwner() { |
137 public ScriptEnvironment getOwner() { |
132 return context.env; |
138 return context.env; |
133 } |
139 } |
134 |
140 |
135 @Override |
141 @Override |
136 public Class<?> install(final String className, final byte[] bytecode) { |
142 public Class<?> install(final String className, final byte[] bytecode, final Source source, final Object[] constants) { |
137 return loader.installClass(className, bytecode, codeSource); |
143 Compiler.LOG.fine("Installing class ", className); |
|
144 |
|
145 final String binaryName = Compiler.binaryName(className); |
|
146 final Class<?> clazz = loader.installClass(binaryName, bytecode, codeSource); |
|
147 |
|
148 try { |
|
149 // Need doPrivileged because these fields are private |
|
150 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { |
|
151 @Override |
|
152 public Void run() throws Exception { |
|
153 //use reflection to write source and constants table to installed classes |
|
154 final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); |
|
155 final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); |
|
156 sourceField.setAccessible(true); |
|
157 constantsField.setAccessible(true); |
|
158 sourceField.set(null, source); |
|
159 constantsField.set(null, constants); |
|
160 return null; |
|
161 } |
|
162 }); |
|
163 } catch (final PrivilegedActionException e) { |
|
164 throw new RuntimeException(e); |
|
165 } |
|
166 |
|
167 return clazz; |
138 } |
168 } |
139 |
169 |
140 @Override |
170 @Override |
141 public void verify(final byte[] code) { |
171 public void verify(final byte[] code) { |
142 context.verify(code); |
172 context.verify(code); |
149 |
179 |
150 @Override |
180 @Override |
151 public long getUniqueEvalId() { |
181 public long getUniqueEvalId() { |
152 return context.getUniqueEvalId(); |
182 return context.getUniqueEvalId(); |
153 } |
183 } |
|
184 |
|
185 @Override |
|
186 public void storeCompiledScript(final Source source, final String mainClassName, |
|
187 final Map<String, byte[]> classBytes, final Object[] constants) { |
|
188 if (context.codeStore != null) { |
|
189 try { |
|
190 context.codeStore.putScript(source, mainClassName, classBytes, constants); |
|
191 } catch (final IOException e) { |
|
192 throw new RuntimeException(e); |
|
193 } |
|
194 } |
|
195 } |
154 } |
196 } |
155 |
197 |
156 /** Is Context global debug mode enabled ? */ |
198 /** Is Context global debug mode enabled ? */ |
157 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); |
199 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); |
158 |
200 |
159 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); |
201 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); |
160 |
202 |
161 // class cache |
203 // in-memory cache for loaded classes |
162 private ClassCache classCache; |
204 private ClassCache classCache; |
|
205 |
|
206 // persistent code store |
|
207 private CodeStore codeStore; |
163 |
208 |
164 /** |
209 /** |
165 * Get the current global scope |
210 * Get the current global scope |
166 * @return the current global scope |
211 * @return the current global scope |
167 */ |
212 */ |
364 } |
409 } |
365 |
410 |
366 final int cacheSize = env._class_cache_size; |
411 final int cacheSize = env._class_cache_size; |
367 if (cacheSize > 0) { |
412 if (cacheSize > 0) { |
368 classCache = new ClassCache(cacheSize); |
413 classCache = new ClassCache(cacheSize); |
|
414 } |
|
415 |
|
416 if (env._persistent_cache) { |
|
417 if (env._lazy_compilation || env._specialize_calls != null) { |
|
418 getErr().println("Can not use persistent class caching with lazy compilation or call specialization."); |
|
419 } else { |
|
420 try { |
|
421 final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"); |
|
422 codeStore = new CodeStore(cacheDir); |
|
423 } catch (IOException e) { |
|
424 throw new RuntimeException("Error initializing code cache", e); |
|
425 } |
|
426 } |
369 } |
427 } |
370 |
428 |
371 // print version info if asked. |
429 // print version info if asked. |
372 if (env._version) { |
430 if (env._version) { |
373 getErr().println("nashorn " + Version.version()); |
431 getErr().println("nashorn " + Version.version()); |
930 if (script != null) { |
988 if (script != null) { |
931 Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile."); |
989 Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile."); |
932 return script; |
990 return script; |
933 } |
991 } |
934 |
992 |
935 final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse(); |
993 CompiledScript compiledScript = null; |
936 if (errors.hasErrors()) { |
994 FunctionNode functionNode = null; |
937 return null; |
995 |
938 } |
996 if (!env._parse_only && codeStore != null) { |
939 |
997 try { |
940 if (env._print_ast) { |
998 compiledScript = codeStore.getScript(source); |
941 getErr().println(new ASTWriter(functionNode)); |
999 } catch (IOException | ClassNotFoundException e) { |
942 } |
1000 Compiler.LOG.warning("Error loading ", source, " from cache: ", e); |
943 |
1001 // Fall back to normal compilation |
944 if (env._print_parse) { |
1002 } |
945 getErr().println(new PrintVisitor(functionNode)); |
1003 } |
|
1004 |
|
1005 if (compiledScript == null) { |
|
1006 functionNode = new Parser(env, source, errMan, strict).parse(); |
|
1007 |
|
1008 if (errors.hasErrors()) { |
|
1009 return null; |
|
1010 } |
|
1011 |
|
1012 if (env._print_ast) { |
|
1013 getErr().println(new ASTWriter(functionNode)); |
|
1014 } |
|
1015 |
|
1016 if (env._print_parse) { |
|
1017 getErr().println(new PrintVisitor(functionNode)); |
|
1018 } |
946 } |
1019 } |
947 |
1020 |
948 if (env._parse_only) { |
1021 if (env._parse_only) { |
949 return null; |
1022 return null; |
950 } |
1023 } |
952 final URL url = source.getURL(); |
1025 final URL url = source.getURL(); |
953 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; |
1026 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; |
954 final CodeSource cs = new CodeSource(url, (CodeSigner[])null); |
1027 final CodeSource cs = new CodeSource(url, (CodeSigner[])null); |
955 final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); |
1028 final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); |
956 |
1029 |
957 final Compiler compiler = new Compiler(installer, strict); |
1030 if (functionNode != null) { |
958 |
1031 final Compiler compiler = new Compiler(installer, strict); |
959 final FunctionNode newFunctionNode = compiler.compile(functionNode); |
1032 final FunctionNode newFunctionNode = compiler.compile(functionNode); |
960 script = compiler.install(newFunctionNode); |
1033 script = compiler.install(newFunctionNode); |
|
1034 } else { |
|
1035 script = install(compiledScript, installer); |
|
1036 } |
|
1037 |
961 cacheClass(source, script); |
1038 cacheClass(source, script); |
962 |
|
963 return script; |
1039 return script; |
964 } |
1040 } |
965 |
1041 |
966 private ScriptLoader createNewLoader() { |
1042 private ScriptLoader createNewLoader() { |
967 return AccessController.doPrivileged( |
1043 return AccessController.doPrivileged( |
979 |
1055 |
980 private long getUniqueScriptId() { |
1056 private long getUniqueScriptId() { |
981 return uniqueScriptId.getAndIncrement(); |
1057 return uniqueScriptId.getAndIncrement(); |
982 } |
1058 } |
983 |
1059 |
|
1060 |
|
1061 /** |
|
1062 * Install a previously compiled class from the code cache. |
|
1063 * |
|
1064 * @param compiledScript cached script containing class bytes and constants |
|
1065 * @return main script class |
|
1066 */ |
|
1067 private Class<?> install(final CompiledScript compiledScript, final CodeInstaller<ScriptEnvironment> installer) { |
|
1068 |
|
1069 final Map<String, Class<?>> installedClasses = new HashMap<>(); |
|
1070 final Source source = compiledScript.getSource(); |
|
1071 final Object[] constants = compiledScript.getConstants(); |
|
1072 final String rootClassName = compiledScript.getMainClassName(); |
|
1073 final byte[] rootByteCode = compiledScript.getClassBytes().get(rootClassName); |
|
1074 final Class<?> rootClass = installer.install(rootClassName, rootByteCode, source, constants); |
|
1075 |
|
1076 installedClasses.put(rootClassName, rootClass); |
|
1077 |
|
1078 for (final Map.Entry<String, byte[]> entry : compiledScript.getClassBytes().entrySet()) { |
|
1079 final String className = entry.getKey(); |
|
1080 if (className.equals(rootClassName)) { |
|
1081 continue; |
|
1082 } |
|
1083 final byte[] code = entry.getValue(); |
|
1084 |
|
1085 installedClasses.put(className, installer.install(className, code, source, constants)); |
|
1086 } |
|
1087 for (Object constant : constants) { |
|
1088 if (constant instanceof RecompilableScriptFunctionData) { |
|
1089 ((RecompilableScriptFunctionData) constant).setCodeAndSource(installedClasses, source); |
|
1090 } |
|
1091 } |
|
1092 |
|
1093 return rootClass; |
|
1094 } |
|
1095 |
984 /** |
1096 /** |
985 * Cache for compiled script classes. |
1097 * Cache for compiled script classes. |
986 */ |
1098 */ |
987 @SuppressWarnings("serial") |
1099 @SuppressWarnings("serial") |
988 private static class ClassCache extends LinkedHashMap<Source, ClassReference> { |
1100 private static class ClassCache extends LinkedHashMap<Source, ClassReference> { |