diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/Context.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/Context.java | 80 |
1 files changed, 75 insertions, 5 deletions
diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java index aa3306a4..23f0e3ab 100644 --- a/src/jdk/nashorn/internal/runtime/Context.java +++ b/src/jdk/nashorn/internal/runtime/Context.java @@ -33,13 +33,13 @@ import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import static jdk.nashorn.internal.runtime.Source.sourceFor; - import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.invoke.SwitchPoint; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.reflect.Field; @@ -127,6 +127,16 @@ public final class Context { private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); + /** + * Keeps track of which builtin prototypes and properties have been relinked + * Currently we are conservative and associate the name of a builtin class with all + * its properties, so it's enough to invalidate a property to break all assumptions + * about a prototype. This can be changed to a more fine grained approach, but no one + * ever needs this, given the very rare occurance of swapping out only parts of + * a builtin v.s. the entire builtin object + */ + private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>(); + /* Force DebuggerSupport to be loaded. */ static { DebuggerSupport.FORCELOAD = true; @@ -140,6 +150,13 @@ public final class Context { private final Context context; private final ScriptLoader loader; private final CodeSource codeSource; + private int usageCount = 0; + private int bytesDefined = 0; + + // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition + // will occur much earlier, the second is a safety measure for very large scripts/functions. + private final static int MAX_USAGES = 10; + private final static int MAX_BYTES_DEFINED = 200_000; private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) { this.context = context; @@ -148,7 +165,7 @@ public final class Context { } /** - * Return the context for this installer + * Return the script environment for this installer * @return ScriptEnvironment */ @Override @@ -158,6 +175,8 @@ public final class Context { @Override public Class<?> install(final String className, final byte[] bytecode) { + usageCount++; + bytesDefined += bytecode.length; final String binaryName = Compiler.binaryName(className); return loader.installClass(binaryName, bytecode, codeSource); } @@ -212,6 +231,24 @@ public final class Context { } return null; } + + @Override + public CodeInstaller<ScriptEnvironment> withNewLoader() { + // Reuse this installer if we're within our limits. + if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) { + return this; + } + return new ContextCodeInstaller(context, context.createNewLoader(), codeSource); + } + + @Override + public boolean isCompatibleWith(final CodeInstaller<ScriptEnvironment> other) { + if (other instanceof ContextCodeInstaller) { + final ContextCodeInstaller cci = (ContextCodeInstaller)other; + return cci.context == context && cci.codeSource == codeSource; + } + return false; + } } /** Is Context global debug mode enabled ? */ @@ -1113,6 +1150,9 @@ public final class Context { StoredScript storedScript = null; FunctionNode functionNode = null; + // We only use the code store here if optimistic types are disabled. With optimistic types, + // code is stored per function in RecompilableScriptFunctionData. + // TODO: This should really be triggered by lazy compilation, not optimistic types. final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types; final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null; @@ -1199,7 +1239,7 @@ public final class Context { final String mainClassName = storedScript.getMainClassName(); final byte[] mainClassBytes = classBytes.get(mainClassName); final Class<?> mainClass = installer.install(mainClassName, mainClassBytes); - final Map<Integer, FunctionInitializer> initialzers = storedScript.getInitializers(); + final Map<Integer, FunctionInitializer> initializers = storedScript.getInitializers(); installedClasses.put(mainClassName, mainClass); @@ -1219,8 +1259,8 @@ public final class Context { if (constant instanceof RecompilableScriptFunctionData) { final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant; data.initTransients(source, installer); - if (initialzers != null) { - final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId()); + final FunctionInitializer initializer = initializers.get(data.getFunctionNodeId()); + if (initializer != null) { initializer.setCode(installedClasses.get(initializer.getClassName())); data.initializeCode(initializer); } @@ -1371,4 +1411,34 @@ public final class Context { return null; } + /** + * This is a special kind of switchpoint used to guard builtin + * properties and prototypes. In the future it might contain + * logic to e.g. multiple switchpoint classes. + */ + public static final class BuiltinSwitchPoint extends SwitchPoint { + //empty + } + + /** + * Create a new builtin switchpoint and return it + * @param name key name + * @return new builtin switchpoint + */ + public SwitchPoint newBuiltinSwitchPoint(final String name) { + assert builtinSwitchPoints.get(name) == null; + final SwitchPoint sp = new BuiltinSwitchPoint(); + builtinSwitchPoints.put(name, sp); + return sp; + } + + /** + * Return the builtin switchpoint for a particular key name + * @param name key name + * @return builtin switchpoint or null if none + */ + public SwitchPoint getBuiltinSwitchPoint(final String name) { + return builtinSwitchPoints.get(name); + } + } |