src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java
Print this page
@@ -32,30 +32,34 @@
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.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
+import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ECMAErrors;
+import jdk.nashorn.internal.runtime.GlobalConstants;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
import jdk.nashorn.internal.runtime.PropertyDescriptor;
import jdk.nashorn.internal.runtime.PropertyMap;
@@ -67,10 +71,11 @@
import jdk.nashorn.internal.runtime.ScriptingFunctions;
import jdk.nashorn.internal.runtime.Specialization;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.regexp.RegExpResult;
import jdk.nashorn.internal.scripts.JO;
/**
* Representation of global scope.
@@ -413,12 +418,13 @@
private static final MethodHandle EVAL = findOwnMH_S("eval", Object.class, Object.class, Object.class);
private static final MethodHandle NO_SUCH_PROPERTY = findOwnMH_S(NO_SUCH_PROPERTY_NAME, Object.class, Object.class, Object.class);
private static final MethodHandle PRINT = findOwnMH_S("print", Object.class, Object.class, Object[].class);
private static final MethodHandle PRINTLN = findOwnMH_S("println", Object.class, Object.class, Object[].class);
private static final MethodHandle LOAD = findOwnMH_S("load", Object.class, Object.class, Object.class);
- private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
+ private static final MethodHandle LOAD_WITH_NEW_GLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class);
+ private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class);
// initialized by nasgen
private static PropertyMap $nasgenmap$;
// context to which this global belongs to
@@ -427,10 +433,16 @@
// current ScriptContext to use - can be null.
private ScriptContext scontext;
// current ScriptEngine associated - can be null.
private ScriptEngine engine;
+ // ES6 global lexical scope.
+ private final LexicalScope lexicalScope;
+
+ // Switchpoint for non-constant global callsites in the presence of ES6 lexical scope.
+ private SwitchPoint lexicalScopeSwitchPoint;
+
/**
* Set the current script context
* @param scontext script context
*/
public void setScriptContext(final ScriptContext scontext) {
@@ -464,10 +476,11 @@
*/
public Global(final Context context) {
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
+ this.lexicalScope = context.getEnv()._es6 ? new LexicalScope(this) : null;
}
/**
* Script access to "current" Global instance
*
@@ -1691,10 +1704,137 @@
@Override
public void setSplitState(final int state) {
splitState = state;
}
+ /**
+ * Return the ES6 global scope for lexically declared bindings.
+ * @return the ES6 lexical global scope.
+ */
+ public final ScriptObject getLexicalScope() {
+ assert context.getEnv()._es6;
+ return lexicalScope;
+ }
+
+ @Override
+ public void addBoundProperties(final ScriptObject source, final jdk.nashorn.internal.runtime.Property[] properties) {
+ PropertyMap ownMap = getMap();
+ LexicalScope lexicalScope = null;
+ PropertyMap lexicalMap = null;
+ boolean hasLexicalDefinitions = false;
+
+ if (context.getEnv()._es6) {
+ lexicalScope = (LexicalScope) getLexicalScope();
+ lexicalMap = lexicalScope.getMap();
+
+ for (final jdk.nashorn.internal.runtime.Property property : properties) {
+ if (property.isLexicalBinding()) {
+ hasLexicalDefinitions = true;
+ }
+ // ES6 15.1.8 steps 6. and 7.
+ final jdk.nashorn.internal.runtime.Property globalProperty = ownMap.findProperty(property.getKey());
+ if (globalProperty != null && !globalProperty.isConfigurable() && property.isLexicalBinding()) {
+ throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+ }
+ final jdk.nashorn.internal.runtime.Property lexicalProperty = lexicalMap.findProperty(property.getKey());
+ if (lexicalProperty != null && !property.isConfigurable()) {
+ throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+ }
+ }
+ }
+
+ for (final jdk.nashorn.internal.runtime.Property property : properties) {
+ if (property.isLexicalBinding()) {
+ assert lexicalScope != null;
+ lexicalMap = lexicalScope.addBoundProperty(lexicalMap, source, property);
+
+ if (ownMap.findProperty(property.getKey()) != null) {
+ // If property exists in the global object invalidate any global constant call sites.
+ invalidateGlobalConstant(property.getKey());
+ }
+ } else {
+ ownMap = addBoundProperty(ownMap, source, property);
+ }
+ }
+
+ setMap(ownMap);
+
+ if (hasLexicalDefinitions) {
+ lexicalScope.setMap(lexicalMap);
+ invalidateLexicalSwitchPoint();
+ }
+ }
+
+ @Override
+ public GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
+
+ if (lexicalScope != null && isScope && !NashornCallSiteDescriptor.isApplyToCall(desc)) {
+ if (lexicalScope.hasOwnProperty(name)) {
+ return lexicalScope.findGetMethod(desc, request, operator);
+ }
+ }
+
+ final GuardedInvocation invocation = super.findGetMethod(desc, request, operator);
+
+ // We want to avoid adding our generic lexical scope switchpoint to global constant invocations,
+ // because those are invalidated per-key in the addBoundProperties method above.
+ // We therefor check if the invocation does already have a switchpoint and the property is non-inherited,
+ // assuming this only applies to global constants. If other non-inherited properties will
+ // start using switchpoints some time in the future we'll have to revisit this.
+ if (isScope && context.getEnv()._es6 && (invocation.getSwitchPoints() == null || !hasOwnProperty(name))) {
+ return invocation.addSwitchPoint(getLexicalScopeSwitchPoint());
+ }
+
+ return invocation;
+ }
+
+ @Override
+ public GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
+ final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
+
+ if (lexicalScope != null && isScope) {
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ if (lexicalScope.hasOwnProperty(name)) {
+ return lexicalScope.findSetMethod(desc, request);
+ }
+ }
+
+ final GuardedInvocation invocation = super.findSetMethod(desc, request);
+
+ if (isScope && context.getEnv()._es6) {
+ return invocation.addSwitchPoint(getLexicalScopeSwitchPoint());
+ }
+
+ return invocation;
+ }
+
+ private synchronized SwitchPoint getLexicalScopeSwitchPoint() {
+ SwitchPoint switchPoint = lexicalScopeSwitchPoint;
+ if (switchPoint == null || switchPoint.hasBeenInvalidated()) {
+ switchPoint = lexicalScopeSwitchPoint = new SwitchPoint();
+ }
+ return switchPoint;
+ }
+
+ private synchronized void invalidateLexicalSwitchPoint() {
+ if (lexicalScopeSwitchPoint != null) {
+ context.getLogger(GlobalConstants.class).info("Invalidating non-constant globals on lexical scope update");
+ SwitchPoint.invalidateAll(new SwitchPoint[]{ lexicalScopeSwitchPoint });
+ }
+ }
+
+
+ @SuppressWarnings("unused")
+ private static Object lexicalScopeFilter(final Object self) {
+ if (self instanceof Global) {
+ return ((Global) self).getLexicalScope();
+ }
+ return self;
+ }
+
private <T extends ScriptObject> T initConstructorAndSwitchPoint(final String name, final Class<T> clazz) {
final T func = initConstructor(name, clazz);
tagBuiltinProperties(name, func);
return func;
}
@@ -1735,11 +1875,11 @@
this.decodeURIComponent = ScriptFunctionImpl.makeFunction("decodeURIComponent", GlobalFunctions.DECODE_URICOMPONENT);
this.escape = ScriptFunctionImpl.makeFunction("escape", GlobalFunctions.ESCAPE);
this.unescape = ScriptFunctionImpl.makeFunction("unescape", GlobalFunctions.UNESCAPE);
this.print = ScriptFunctionImpl.makeFunction("print", env._print_no_newline ? PRINT : PRINTLN);
this.load = ScriptFunctionImpl.makeFunction("load", LOAD);
- this.loadWithNewGlobal = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOADWITHNEWGLOBAL);
+ this.loadWithNewGlobal = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOAD_WITH_NEW_GLOBAL);
this.exit = ScriptFunctionImpl.makeFunction("exit", EXIT);
this.quit = ScriptFunctionImpl.makeFunction("quit", EXIT);
// built-in constructors
this.builtinArray = initConstructorAndSwitchPoint("Array", ScriptFunction.class);
@@ -2201,6 +2341,38 @@
@Override
protected boolean isGlobal() {
return true;
}
+
+ /**
+ * A class representing the ES6 global lexical scope.
+ */
+ private static class LexicalScope extends ScriptObject {
+
+ LexicalScope(final ScriptObject proto) {
+ super(proto, PropertyMap.newMap());
+ }
+
+ @Override
+ protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
+ return filterInvocation(super.findGetMethod(desc, request, operator));
+ }
+
+ @Override
+ protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
+ return filterInvocation(super.findSetMethod(desc, request));
+ }
+
+ @Override
+ protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final jdk.nashorn.internal.runtime.Property property) {
+ // We override this method just to make it callable by Global
+ return super.addBoundProperty(propMap, source, property);
+ }
+
+ private static GuardedInvocation filterInvocation(final GuardedInvocation invocation) {
+ final MethodType type = invocation.getInvocation().type();
+ return invocation.asType(type.changeParameterType(0, Object.class)).filterArguments(0, LEXICAL_SCOPE_FILTER);
+ }
+ }
+
}