8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
30 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
31 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
32 import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
33 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
34 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
35 import static jdk.nashorn.internal.runtime.Source.sourceFor;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.io.PrintWriter;
40 import java.lang.invoke.MethodHandle;
41 import java.lang.invoke.MethodHandles;
42 import java.lang.invoke.MethodType;
43 import java.lang.invoke.SwitchPoint;
44 import java.lang.ref.ReferenceQueue;
45 import java.lang.ref.SoftReference;
46 import java.lang.reflect.Field;
47 import java.lang.reflect.Modifier;
48 import java.net.MalformedURLException;
49 import java.net.URL;
50 import java.security.AccessControlContext;
51 import java.security.AccessController;
52 import java.security.CodeSigner;
53 import java.security.CodeSource;
54 import java.security.Permissions;
55 import java.security.PrivilegedAction;
56 import java.security.PrivilegedActionException;
57 import java.security.PrivilegedExceptionAction;
58 import java.security.ProtectionDomain;
59 import java.util.Collection;
60 import java.util.HashMap;
61 import java.util.LinkedHashMap;
62 import java.util.Map;
63 import java.util.Objects;
64 import java.util.concurrent.atomic.AtomicLong;
65 import java.util.concurrent.atomic.AtomicReference;
66 import java.util.function.Consumer;
67 import java.util.function.Supplier;
68 import java.util.logging.Level;
69 import javax.script.ScriptContext;
70 import javax.script.ScriptEngine;
71 import jdk.internal.org.objectweb.asm.ClassReader;
72 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
73 import jdk.nashorn.api.scripting.ClassFilter;
74 import jdk.nashorn.api.scripting.ScriptObjectMirror;
75 import jdk.nashorn.internal.codegen.Compiler;
76 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
77 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
78 import jdk.nashorn.internal.ir.FunctionNode;
79 import jdk.nashorn.internal.ir.debug.ASTWriter;
80 import jdk.nashorn.internal.ir.debug.PrintVisitor;
81 import jdk.nashorn.internal.lookup.MethodHandleFactory;
82 import jdk.nashorn.internal.objects.Global;
83 import jdk.nashorn.internal.parser.Parser;
84 import jdk.nashorn.internal.runtime.events.RuntimeEvent;
85 import jdk.nashorn.internal.runtime.logging.DebugLogger;
86 import jdk.nashorn.internal.runtime.logging.Loggable;
87 import jdk.nashorn.internal.runtime.logging.Logger;
88 import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
89 import jdk.nashorn.internal.runtime.options.Options;
90
91 /**
92 * This class manages the global state of execution. Context is immutable.
93 */
94 public final class Context {
95 // nashorn specific security runtime access permission names
96 /**
97 * Permission needed to pass arbitrary nashorn command line options when creating Context.
98 */
99 public static final String NASHORN_SET_CONFIG = "nashorn.setConfig";
100
101 /**
102 * Permission needed to create Nashorn Context instance.
103 */
104 public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext";
105
106 /**
107 * Permission needed to create Nashorn Global instance.
108 */
109 public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal";
111 /**
112 * Permission to get current Nashorn Context from thread local storage.
113 */
114 public static final String NASHORN_GET_CONTEXT = "nashorn.getContext";
115
116 /**
117 * Permission to use Java reflection/jsr292 from script code.
118 */
119 public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
120
121 /**
122 * Permission to enable nashorn debug mode.
123 */
124 public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode";
125
126 // nashorn load psuedo URL prefixes
127 private static final String LOAD_CLASSPATH = "classpath:";
128 private static final String LOAD_FX = "fx:";
129 private static final String LOAD_NASHORN = "nashorn:";
130
131 private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
132 private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
133
134 /**
135 * Should scripts use only object slots for fields, or dual long/object slots? The default
136 * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled
137 * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects"
138 * or "nashorn.fields.dual" system property.
139 */
140 private final FieldMode fieldMode;
141
142 private static enum FieldMode {
143 /** Value for automatic field representation depending on optimistic types setting */
144 AUTO,
145 /** Value for object field representation regardless of optimistic types setting */
146 OBJECTS,
147 /** Value for dual primitive/object field representation regardless of optimistic types setting */
148 DUAL
149 }
150
151 /**
152 * Keeps track of which builtin prototypes and properties have been relinked
153 * Currently we are conservative and associate the name of a builtin class with all
154 * its properties, so it's enough to invalidate a property to break all assumptions
155 * about a prototype. This can be changed to a more fine grained approach, but no one
156 * ever needs this, given the very rare occurrence of swapping out only parts of
157 * a builtin v.s. the entire builtin object
158 */
159 private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>();
160
161 /* Force DebuggerSupport to be loaded. */
162 static {
163 DebuggerSupport.FORCELOAD = true;
164 }
165
166 /**
167 * ContextCodeInstaller that has the privilege of installing classes in the Context.
168 * Can only be instantiated from inside the context and is opaque to other classes
169 */
170 public static class ContextCodeInstaller implements CodeInstaller {
171 private final Context context;
172 private final ScriptLoader loader;
173 private final CodeSource codeSource;
174 private int usageCount = 0;
175 private int bytesDefined = 0;
176
177 // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition
178 // will occur much earlier, the second is a safety measure for very large scripts/functions.
179 private final static int MAX_USAGES = 10;
180 private final static int MAX_BYTES_DEFINED = 200_000;
181
182 private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
183 this.context = context;
184 this.loader = loader;
185 this.codeSource = codeSource;
186 }
187
188 @Override
189 public Context getContext() {
190 return context;
191 }
192
193 @Override
194 public Class<?> install(final String className, final byte[] bytecode) {
195 usageCount++;
196 bytesDefined += bytecode.length;
197 final String binaryName = Compiler.binaryName(className);
198 return loader.installClass(binaryName, bytecode, codeSource);
199 }
200
201 @Override
202 public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
203 try {
204 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
205 @Override
206 public Void run() throws Exception {
207 for (final Class<?> clazz : classes) {
208 //use reflection to write source and constants table to installed classes
209 final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
210 sourceField.setAccessible(true);
211 sourceField.set(null, source);
212
213 final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
214 constantsField.setAccessible(true);
215 constantsField.set(null, constants);
216 }
217 return null;
218 }
219 });
220 } catch (final PrivilegedActionException e) {
221 throw new RuntimeException(e);
233 }
234
235 @Override
236 public void storeScript(final String cacheKey, final Source source, final String mainClassName,
237 final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers,
238 final Object[] constants, final int compilationId) {
239 if (context.codeStore != null) {
240 context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId);
241 }
242 }
243
244 @Override
245 public StoredScript loadScript(final Source source, final String functionKey) {
246 if (context.codeStore != null) {
247 return context.codeStore.load(source, functionKey);
248 }
249 return null;
250 }
251
252 @Override
253 public CodeInstaller withNewLoader() {
254 // Reuse this installer if we're within our limits.
255 if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) {
256 return this;
257 }
258 return new ContextCodeInstaller(context, context.createNewLoader(), codeSource);
259 }
260
261 @Override
262 public boolean isCompatibleWith(final CodeInstaller other) {
263 if (other instanceof ContextCodeInstaller) {
264 final ContextCodeInstaller cci = (ContextCodeInstaller)other;
265 return cci.context == context && cci.codeSource == codeSource;
266 }
267 return false;
268 }
269 }
270
271 /** Is Context global debug mode enabled ? */
272 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
273
274 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>();
275
276 // in-memory cache for loaded classes
277 private ClassCache classCache;
278
279 // persistent code store
280 private CodeStore codeStore;
281
282 // A factory for linking global properties as constant method handles. It is created when the first Global
283 // is created, and invalidated forever once the second global is created.
284 private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
285
286 /**
287 * Get the current global scope
631 * Interface to represent compiled code that can be re-used across many
632 * global scope instances
633 */
634 public static interface MultiGlobalCompiledScript {
635 /**
636 * Obtain script function object for a specific global scope object.
637 *
638 * @param newGlobal global scope for which function object is obtained
639 * @return script function for script level expressions
640 */
641 public ScriptFunction getFunction(final Global newGlobal);
642 }
643
644 /**
645 * Compile a top level script.
646 *
647 * @param source the script source
648 * @return reusable compiled script across many global scopes.
649 */
650 public MultiGlobalCompiledScript compileScript(final Source source) {
651 final Class<?> clazz = compile(source, this.errors, this._strict);
652 final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz);
653
654 return new MultiGlobalCompiledScript() {
655 @Override
656 public ScriptFunction getFunction(final Global newGlobal) {
657 return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal);
658 }
659 };
660 }
661
662 /**
663 * Entry point for {@code eval}
664 *
665 * @param initialScope The scope of this eval call
666 * @param string Evaluated code as a String
667 * @param callThis "this" to be passed to the evaluated code
668 * @param location location of the eval call
669 * @return the return value of the {@code eval}
670 */
671 public Object eval(final ScriptObject initialScope, final String string,
685 *
686 * @return the return value of the {@code eval}
687 */
688 public Object eval(final ScriptObject initialScope, final String string,
689 final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
690 final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString();
691 final Source source = sourceFor(file, string, evalCall);
692 // is this direct 'eval' builtin call?
693 final boolean directEval = evalCall && (location != UNDEFINED);
694 final Global global = Context.getGlobal();
695 ScriptObject scope = initialScope;
696
697 // ECMA section 10.1.1 point 2 says eval code is strict if it begins
698 // with "use strict" directive or eval direct call itself is made
699 // from from strict mode code. We are passed with caller's strict mode.
700 // Nashorn extension: any 'eval' is unconditionally strict when -strict is specified.
701 boolean strictFlag = strict || this._strict;
702
703 Class<?> clazz = null;
704 try {
705 clazz = compile(source, new ThrowErrorManager(), strictFlag);
706 } catch (final ParserException e) {
707 e.throwAsEcmaException(global);
708 return null;
709 }
710
711 if (!strictFlag) {
712 // We need to get strict mode flag from compiled class. This is
713 // because eval code may start with "use strict" directive.
714 try {
715 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
716 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
717 //ignored
718 strictFlag = false;
719 }
720 }
721
722 // In strict mode, eval does not instantiate variables and functions
723 // in the caller's environment. A new environment is created!
724 if (strictFlag) {
725 // Create a new scope object with given scope as its prototype
1231
1232 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) {
1233 try {
1234 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE);
1235 } catch (NoSuchMethodException | IllegalAccessException e) {
1236 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e);
1237 }
1238 }
1239
1240 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) {
1241 try {
1242 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope);
1243 } catch (final RuntimeException|Error e) {
1244 throw e;
1245 } catch (final Throwable t) {
1246 throw new AssertionError("Failed to create a program function", t);
1247 }
1248 }
1249
1250 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
1251 return getProgramFunction(compile(source, errMan, this._strict), scope);
1252 }
1253
1254 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) {
1255 // start with no errors, no warnings.
1256 errMan.reset();
1257
1258 Class<?> script = findCachedClass(source);
1259 if (script != null) {
1260 final DebugLogger log = getLogger(Compiler.class);
1261 if (log.isEnabled()) {
1262 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile.");
1263 }
1264 return script;
1265 }
1266
1267 StoredScript storedScript = null;
1268 FunctionNode functionNode = null;
1269 // Don't use code store if optimistic types is enabled but lazy compilation is not.
1270 // This would store a full script compilation with many wrong optimistic assumptions that would
1271 // do more harm than good on later runs with both optimistic types and lazy compilation enabled.
1272 final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation);
1273 final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null;
1274
1284 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
1285
1286 if (errMan.hasErrors()) {
1287 return null;
1288 }
1289
1290 if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
1291 getErr().println(new ASTWriter(functionNode));
1292 }
1293
1294 if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) {
1295 getErr().println(new PrintVisitor(functionNode, true, false));
1296 }
1297 }
1298
1299 if (env._parse_only) {
1300 return null;
1301 }
1302
1303 final URL url = source.getURL();
1304 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
1305 final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
1306 final CodeInstaller installer = new ContextCodeInstaller(this, loader, cs);
1307
1308 if (storedScript == null) {
1309 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
1310
1311 final Compiler compiler = Compiler.forInitialCompilation(
1312 installer,
1313 source,
1314 errMan,
1315 strict | functionNode.isStrict());
1316
1317 final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
1318 if (errMan.hasErrors()) {
1319 return null;
1320 }
1321 script = compiledFunction.getRootClass();
1322 compiler.persistClassInfo(cacheKey, compiledFunction);
1323 } else {
1324 Compiler.updateCompilationId(storedScript.getCompilationId());
1325 script = storedScript.installScript(source, installer);
1326 }
|
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
30 import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
31 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
32 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
33 import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
34 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
35 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
36 import static jdk.nashorn.internal.runtime.Source.sourceFor;
37
38 import java.io.File;
39 import java.io.IOException;
40 import java.io.PrintWriter;
41 import java.lang.invoke.MethodHandle;
42 import java.lang.invoke.MethodHandles;
43 import java.lang.invoke.MethodType;
44 import java.lang.invoke.SwitchPoint;
45 import java.lang.ref.Reference;
46 import java.lang.ref.ReferenceQueue;
47 import java.lang.ref.SoftReference;
48 import java.lang.ref.WeakReference;
49 import java.lang.reflect.Field;
50 import java.lang.reflect.Modifier;
51 import java.net.MalformedURLException;
52 import java.net.URL;
53 import java.security.AccessControlContext;
54 import java.security.AccessController;
55 import java.security.CodeSigner;
56 import java.security.CodeSource;
57 import java.security.Permissions;
58 import java.security.PrivilegedAction;
59 import java.security.PrivilegedActionException;
60 import java.security.PrivilegedExceptionAction;
61 import java.security.ProtectionDomain;
62 import java.util.Collection;
63 import java.util.HashMap;
64 import java.util.LinkedHashMap;
65 import java.util.Map;
66 import java.util.Objects;
67 import java.util.concurrent.atomic.AtomicLong;
68 import java.util.concurrent.atomic.AtomicReference;
69 import java.util.concurrent.atomic.LongAdder;
70 import java.util.function.Consumer;
71 import java.util.function.Supplier;
72 import java.util.logging.Level;
73 import javax.script.ScriptContext;
74 import javax.script.ScriptEngine;
75 import jdk.internal.org.objectweb.asm.ClassReader;
76 import jdk.internal.org.objectweb.asm.ClassWriter;
77 import jdk.internal.org.objectweb.asm.Opcodes;
78 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
79 import jdk.nashorn.api.scripting.ClassFilter;
80 import jdk.nashorn.api.scripting.ScriptObjectMirror;
81 import jdk.nashorn.internal.codegen.Compiler;
82 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
83 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
84 import jdk.nashorn.internal.ir.FunctionNode;
85 import jdk.nashorn.internal.ir.debug.ASTWriter;
86 import jdk.nashorn.internal.ir.debug.PrintVisitor;
87 import jdk.nashorn.internal.lookup.MethodHandleFactory;
88 import jdk.nashorn.internal.objects.Global;
89 import jdk.nashorn.internal.parser.Parser;
90 import jdk.nashorn.internal.runtime.events.RuntimeEvent;
91 import jdk.nashorn.internal.runtime.logging.DebugLogger;
92 import jdk.nashorn.internal.runtime.logging.Loggable;
93 import jdk.nashorn.internal.runtime.logging.Logger;
94 import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
95 import jdk.nashorn.internal.runtime.options.Options;
96 import sun.misc.Unsafe;
97
98 /**
99 * This class manages the global state of execution. Context is immutable.
100 */
101 public final class Context {
102 // nashorn specific security runtime access permission names
103 /**
104 * Permission needed to pass arbitrary nashorn command line options when creating Context.
105 */
106 public static final String NASHORN_SET_CONFIG = "nashorn.setConfig";
107
108 /**
109 * Permission needed to create Nashorn Context instance.
110 */
111 public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext";
112
113 /**
114 * Permission needed to create Nashorn Global instance.
115 */
116 public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal";
118 /**
119 * Permission to get current Nashorn Context from thread local storage.
120 */
121 public static final String NASHORN_GET_CONTEXT = "nashorn.getContext";
122
123 /**
124 * Permission to use Java reflection/jsr292 from script code.
125 */
126 public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
127
128 /**
129 * Permission to enable nashorn debug mode.
130 */
131 public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode";
132
133 // nashorn load psuedo URL prefixes
134 private static final String LOAD_CLASSPATH = "classpath:";
135 private static final String LOAD_FX = "fx:";
136 private static final String LOAD_NASHORN = "nashorn:";
137
138 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
139 private static final MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
140
141 private static final LongAdder NAMED_INSTALLED_SCRIPT_COUNT = new LongAdder();
142 private static final LongAdder ANONYMOUS_INSTALLED_SCRIPT_COUNT = new LongAdder();
143
144 /**
145 * Should scripts use only object slots for fields, or dual long/object slots? The default
146 * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled
147 * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects"
148 * or "nashorn.fields.dual" system property.
149 */
150 private final FieldMode fieldMode;
151
152 private static enum FieldMode {
153 /** Value for automatic field representation depending on optimistic types setting */
154 AUTO,
155 /** Value for object field representation regardless of optimistic types setting */
156 OBJECTS,
157 /** Value for dual primitive/object field representation regardless of optimistic types setting */
158 DUAL
159 }
160
161 /**
162 * Keeps track of which builtin prototypes and properties have been relinked
163 * Currently we are conservative and associate the name of a builtin class with all
164 * its properties, so it's enough to invalidate a property to break all assumptions
165 * about a prototype. This can be changed to a more fine grained approach, but no one
166 * ever needs this, given the very rare occurrence of swapping out only parts of
167 * a builtin v.s. the entire builtin object
168 */
169 private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>();
170
171 /* Force DebuggerSupport to be loaded. */
172 static {
173 DebuggerSupport.FORCELOAD = true;
174 }
175
176 static long getNamedInstalledScriptCount() {
177 return NAMED_INSTALLED_SCRIPT_COUNT.sum();
178 }
179
180 static long getAnonymousInstalledScriptCount() {
181 return ANONYMOUS_INSTALLED_SCRIPT_COUNT.sum();
182 }
183
184 /**
185 * ContextCodeInstaller that has the privilege of installing classes in the Context.
186 * Can only be instantiated from inside the context and is opaque to other classes
187 */
188 private abstract static class ContextCodeInstaller implements CodeInstaller {
189 final Context context;
190 final CodeSource codeSource;
191
192 ContextCodeInstaller(final Context context, final CodeSource codeSource) {
193 this.context = context;
194 this.codeSource = codeSource;
195 }
196
197 @Override
198 public Context getContext() {
199 return context;
200 }
201
202 @Override
203 public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
204 try {
205 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
206 @Override
207 public Void run() throws Exception {
208 for (final Class<?> clazz : classes) {
209 //use reflection to write source and constants table to installed classes
210 final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
211 sourceField.setAccessible(true);
212 sourceField.set(null, source);
213
214 final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
215 constantsField.setAccessible(true);
216 constantsField.set(null, constants);
217 }
218 return null;
219 }
220 });
221 } catch (final PrivilegedActionException e) {
222 throw new RuntimeException(e);
234 }
235
236 @Override
237 public void storeScript(final String cacheKey, final Source source, final String mainClassName,
238 final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers,
239 final Object[] constants, final int compilationId) {
240 if (context.codeStore != null) {
241 context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId);
242 }
243 }
244
245 @Override
246 public StoredScript loadScript(final Source source, final String functionKey) {
247 if (context.codeStore != null) {
248 return context.codeStore.load(source, functionKey);
249 }
250 return null;
251 }
252
253 @Override
254 public boolean isCompatibleWith(final CodeInstaller other) {
255 if (other instanceof ContextCodeInstaller) {
256 final ContextCodeInstaller cci = (ContextCodeInstaller)other;
257 return cci.context == context && cci.codeSource == codeSource;
258 }
259 return false;
260 }
261 }
262
263 private static class NamedContextCodeInstaller extends ContextCodeInstaller {
264 private final ScriptLoader loader;
265 private int usageCount = 0;
266 private int bytesDefined = 0;
267
268 // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition
269 // will occur much earlier, the second is a safety measure for very large scripts/functions.
270 private final static int MAX_USAGES = 10;
271 private final static int MAX_BYTES_DEFINED = 200_000;
272
273 private NamedContextCodeInstaller(final Context context, final CodeSource codeSource, final ScriptLoader loader) {
274 super(context, codeSource);
275 this.loader = loader;
276 }
277
278 @Override
279 public Class<?> install(final String className, final byte[] bytecode) {
280 usageCount++;
281 bytesDefined += bytecode.length;
282 NAMED_INSTALLED_SCRIPT_COUNT.increment();
283 return loader.installClass(Compiler.binaryName(className), bytecode, codeSource);
284 }
285
286 @Override
287 public CodeInstaller getOnDemandCompilationInstaller() {
288 // Reuse this installer if we're within our limits.
289 if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) {
290 return this;
291 }
292 return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
293 }
294
295 @Override
296 public CodeInstaller getMultiClassCodeInstaller() {
297 // This installer is perfectly suitable for installing multiple classes that reference each other
298 // as it produces classes with resolvable names, all defined in a single class loader.
299 return this;
300 }
301 }
302
303 private final Map<CodeSource, HostClassReference> anonymousHostClasses = new HashMap<>();
304 private final ReferenceQueue<Class<?>> anonymousHostClassesRefQueue = new ReferenceQueue<>();
305
306 private static class HostClassReference extends WeakReference<Class<?>> {
307 final CodeSource codeSource;
308
309 HostClassReference(final CodeSource codeSource, final Class<?> clazz, final ReferenceQueue<Class<?>> refQueue) {
310 super(clazz, refQueue);
311 this.codeSource = codeSource;
312 }
313 }
314
315 private synchronized Class<?> getAnonymousHostClass(final CodeSource codeSource) {
316 // Remove cleared entries
317 for(;;) {
318 final HostClassReference clearedRef = (HostClassReference)anonymousHostClassesRefQueue.poll();
319 if (clearedRef == null) {
320 break;
321 }
322 anonymousHostClasses.remove(clearedRef.codeSource, clearedRef);
323 }
324
325 // Try to find an existing host class
326 final Reference<Class<?>> ref = anonymousHostClasses.get(codeSource);
327 if (ref != null) {
328 final Class<?> existingHostClass = ref.get();
329 if (existingHostClass != null) {
330 return existingHostClass;
331 }
332 }
333
334 // Define a new host class if existing is not found
335 final Class<?> newHostClass = createNewLoader().installClass(
336 // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not
337 // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever
338 // invoked from AnonymousContextCodeInstaller, this is okay.
339 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME,
340 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, codeSource);
341 anonymousHostClasses.put(codeSource, new HostClassReference(codeSource, newHostClass, anonymousHostClassesRefQueue));
342 return newHostClass;
343 }
344
345 private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller {
346 private static final Unsafe UNSAFE = getUnsafe();
347 private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost";
348 private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes();
349
350 private final Class<?> hostClass;
351
352 private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) {
353 super(context, codeSource);
354 this.hostClass = hostClass;
355 }
356
357 @Override
358 public Class<?> install(final String className, final byte[] bytecode) {
359 ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment();
360 return UNSAFE.defineAnonymousClass(hostClass, bytecode, null);
361 }
362
363 @Override
364 public CodeInstaller getOnDemandCompilationInstaller() {
365 // This code loader can be indefinitely reused for on-demand recompilations for the same code source.
366 return this;
367 }
368
369 @Override
370 public CodeInstaller getMultiClassCodeInstaller() {
371 // This code loader can not be used to install multiple classes that reference each other, as they
372 // would have no resolvable names. Therefore, in such situation we must revert to an installer that
373 // produces named classes.
374 return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
375 }
376
377 private static final byte[] getAnonymousHostClassBytes() {
378 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
379 cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null);
380 cw.visitEnd();
381 return cw.toByteArray();
382 }
383
384 private static Unsafe getUnsafe() {
385 return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() {
386 @Override
387 public Unsafe run() {
388 try {
389 final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
390 theUnsafeField.setAccessible(true);
391 return (Unsafe)theUnsafeField.get(null);
392 } catch (final ReflectiveOperationException e) {
393 throw new RuntimeException(e);
394 }
395 }
396 });
397 }
398 }
399
400 /** Is Context global debug mode enabled ? */
401 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
402
403 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>();
404
405 // in-memory cache for loaded classes
406 private ClassCache classCache;
407
408 // persistent code store
409 private CodeStore codeStore;
410
411 // A factory for linking global properties as constant method handles. It is created when the first Global
412 // is created, and invalidated forever once the second global is created.
413 private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
414
415 /**
416 * Get the current global scope
760 * Interface to represent compiled code that can be re-used across many
761 * global scope instances
762 */
763 public static interface MultiGlobalCompiledScript {
764 /**
765 * Obtain script function object for a specific global scope object.
766 *
767 * @param newGlobal global scope for which function object is obtained
768 * @return script function for script level expressions
769 */
770 public ScriptFunction getFunction(final Global newGlobal);
771 }
772
773 /**
774 * Compile a top level script.
775 *
776 * @param source the script source
777 * @return reusable compiled script across many global scopes.
778 */
779 public MultiGlobalCompiledScript compileScript(final Source source) {
780 final Class<?> clazz = compile(source, this.errors, this._strict, false);
781 final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz);
782
783 return new MultiGlobalCompiledScript() {
784 @Override
785 public ScriptFunction getFunction(final Global newGlobal) {
786 return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal);
787 }
788 };
789 }
790
791 /**
792 * Entry point for {@code eval}
793 *
794 * @param initialScope The scope of this eval call
795 * @param string Evaluated code as a String
796 * @param callThis "this" to be passed to the evaluated code
797 * @param location location of the eval call
798 * @return the return value of the {@code eval}
799 */
800 public Object eval(final ScriptObject initialScope, final String string,
814 *
815 * @return the return value of the {@code eval}
816 */
817 public Object eval(final ScriptObject initialScope, final String string,
818 final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
819 final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString();
820 final Source source = sourceFor(file, string, evalCall);
821 // is this direct 'eval' builtin call?
822 final boolean directEval = evalCall && (location != UNDEFINED);
823 final Global global = Context.getGlobal();
824 ScriptObject scope = initialScope;
825
826 // ECMA section 10.1.1 point 2 says eval code is strict if it begins
827 // with "use strict" directive or eval direct call itself is made
828 // from from strict mode code. We are passed with caller's strict mode.
829 // Nashorn extension: any 'eval' is unconditionally strict when -strict is specified.
830 boolean strictFlag = strict || this._strict;
831
832 Class<?> clazz = null;
833 try {
834 clazz = compile(source, new ThrowErrorManager(), strictFlag, true);
835 } catch (final ParserException e) {
836 e.throwAsEcmaException(global);
837 return null;
838 }
839
840 if (!strictFlag) {
841 // We need to get strict mode flag from compiled class. This is
842 // because eval code may start with "use strict" directive.
843 try {
844 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
845 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
846 //ignored
847 strictFlag = false;
848 }
849 }
850
851 // In strict mode, eval does not instantiate variables and functions
852 // in the caller's environment. A new environment is created!
853 if (strictFlag) {
854 // Create a new scope object with given scope as its prototype
1360
1361 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) {
1362 try {
1363 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE);
1364 } catch (NoSuchMethodException | IllegalAccessException e) {
1365 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e);
1366 }
1367 }
1368
1369 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) {
1370 try {
1371 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope);
1372 } catch (final RuntimeException|Error e) {
1373 throw e;
1374 } catch (final Throwable t) {
1375 throw new AssertionError("Failed to create a program function", t);
1376 }
1377 }
1378
1379 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
1380 return getProgramFunction(compile(source, errMan, this._strict, false), scope);
1381 }
1382
1383 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict, final boolean isEval) {
1384 // start with no errors, no warnings.
1385 errMan.reset();
1386
1387 Class<?> script = findCachedClass(source);
1388 if (script != null) {
1389 final DebugLogger log = getLogger(Compiler.class);
1390 if (log.isEnabled()) {
1391 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile.");
1392 }
1393 return script;
1394 }
1395
1396 StoredScript storedScript = null;
1397 FunctionNode functionNode = null;
1398 // Don't use code store if optimistic types is enabled but lazy compilation is not.
1399 // This would store a full script compilation with many wrong optimistic assumptions that would
1400 // do more harm than good on later runs with both optimistic types and lazy compilation enabled.
1401 final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation);
1402 final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null;
1403
1413 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
1414
1415 if (errMan.hasErrors()) {
1416 return null;
1417 }
1418
1419 if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
1420 getErr().println(new ASTWriter(functionNode));
1421 }
1422
1423 if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) {
1424 getErr().println(new PrintVisitor(functionNode, true, false));
1425 }
1426 }
1427
1428 if (env._parse_only) {
1429 return null;
1430 }
1431
1432 final URL url = source.getURL();
1433 final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
1434 final CodeInstaller installer;
1435 if (!env.useAnonymousClasses(source.getLength()) || env._persistent_cache || !env._lazy_compilation) {
1436 // Persistent code cache and eager compilation preclude use of VM anonymous classes
1437 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
1438 installer = new NamedContextCodeInstaller(this, cs, loader);
1439 } else {
1440 installer = new AnonymousContextCodeInstaller(this, cs, getAnonymousHostClass(cs));
1441 }
1442
1443 if (storedScript == null) {
1444 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
1445
1446 final Compiler compiler = Compiler.forInitialCompilation(
1447 installer,
1448 source,
1449 errMan,
1450 strict | functionNode.isStrict());
1451
1452 final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
1453 if (errMan.hasErrors()) {
1454 return null;
1455 }
1456 script = compiledFunction.getRootClass();
1457 compiler.persistClassInfo(cacheKey, compiledFunction);
1458 } else {
1459 Compiler.updateCompilationId(storedScript.getCompilationId());
1460 script = storedScript.installScript(source, installer);
1461 }
|