diff options
Diffstat (limited to 'src/jdk/nashorn/internal/objects/Global.java')
-rw-r--r-- | src/jdk/nashorn/internal/objects/Global.java | 654 |
1 files changed, 527 insertions, 127 deletions
diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java index 0614c4b4..bb3e13d2 100644 --- a/src/jdk/nashorn/internal/objects/Global.java +++ b/src/jdk/nashorn/internal/objects/Global.java @@ -26,6 +26,7 @@ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; @@ -33,20 +34,30 @@ 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; @@ -55,13 +66,14 @@ import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.Scope; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptFunction; -import jdk.nashorn.internal.runtime.ScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; 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; @@ -70,9 +82,35 @@ import jdk.nashorn.internal.scripts.JO; */ @ScriptClass("Global") public final class Global extends ScriptObject implements Scope { + // Placeholder value used in place of a location property (__FILE__, __DIR__, __LINE__) + private static final Object LOCATION_PROPERTY_PLACEHOLDER = new Object(); private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class); private final InvokeByName VALUE_OF = new InvokeByName("valueOf", ScriptObject.class); + /** + * Optimistic builtin names that require switchpoint invalidation + * upon assignment. Overly conservative, but works for now, to avoid + * any complicated scope checks and especially heavy weight guards + * like + * + * <pre> + * public boolean setterGuard(final Object receiver) { + * final Global global = Global.instance(); + * final ScriptObject sobj = global.getFunctionPrototype(); + * final Object apply = sobj.get("apply"); + * return apply == receiver; + * } + * </pre> + * + * Naturally, checking for builtin classes like NativeFunction is cheaper, + * it's when you start adding property checks for said builtins you have + * problems with guard speed. + */ + + /** Nashorn extension: arguments array */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) + public Object arguments; + /** ECMA 15.1.2.2 parseInt (string , radix) */ @Property(attributes = Attribute.NOT_ENUMERABLE) public Object parseInt; @@ -135,11 +173,11 @@ public final class Global extends ScriptObject implements Scope { /** Value property NaN of the Global Object - ECMA 15.1.1.1 NaN */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final Object NaN = Double.NaN; + public final double NaN = Double.NaN; /** Value property Infinity of the Global Object - ECMA 15.1.1.2 Infinity */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public final Object Infinity = Double.POSITIVE_INFINITY; + public final double Infinity = Double.POSITIVE_INFINITY; /** Value property Undefined of the Global Object - ECMA 15.1.1.3 Undefined */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT) @@ -303,15 +341,15 @@ public final class Global extends ScriptObject implements Scope { /** Nashorn extension: current script's file name */ @Property(name = "__FILE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public Object __FILE__; + public final Object __FILE__ = LOCATION_PROPERTY_PLACEHOLDER; /** Nashorn extension: current script's directory */ @Property(name = "__DIR__", attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public Object __DIR__; + public final Object __DIR__ = LOCATION_PROPERTY_PLACEHOLDER; /** Nashorn extension: current source line number being executed */ @Property(name = "__LINE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT) - public Object __LINE__; + public final Object __LINE__ = LOCATION_PROPERTY_PLACEHOLDER; /** Used as Date.prototype's default value */ public NativeDate DEFAULT_DATE; @@ -352,19 +390,19 @@ public final class Global extends ScriptObject implements Scope { private ScriptObject builtinJavafx; private ScriptObject builtinJavax; private ScriptObject builtinOrg; - private ScriptObject builtinJavaImporter; + private ScriptFunction builtinJavaImporter; private ScriptObject builtinJavaApi; - private ScriptObject builtinArrayBuffer; - private ScriptObject builtinDataView; - private ScriptObject builtinInt8Array; - private ScriptObject builtinUint8Array; - private ScriptObject builtinUint8ClampedArray; - private ScriptObject builtinInt16Array; - private ScriptObject builtinUint16Array; - private ScriptObject builtinInt32Array; - private ScriptObject builtinUint32Array; - private ScriptObject builtinFloat32Array; - private ScriptObject builtinFloat64Array; + private ScriptFunction builtinArrayBuffer; + private ScriptFunction builtinDataView; + private ScriptFunction builtinInt8Array; + private ScriptFunction builtinUint8Array; + private ScriptFunction builtinUint8ClampedArray; + private ScriptFunction builtinInt16Array; + private ScriptFunction builtinUint16Array; + private ScriptFunction builtinInt32Array; + private ScriptFunction builtinUint32Array; + private ScriptFunction builtinFloat32Array; + private ScriptFunction builtinFloat64Array; /* * ECMA section 13.2.3 The [[ThrowTypeError]] Function Object @@ -377,12 +415,14 @@ public final class Global extends ScriptObject implements Scope { // Used to store the last RegExp result to support deprecated RegExp constructor properties private RegExpResult lastRegExpResult; - private static final MethodHandle EVAL = findOwnMH("eval", Object.class, Object.class, Object.class); - private static final MethodHandle PRINT = findOwnMH("print", Object.class, Object.class, Object[].class); - private static final MethodHandle PRINTLN = findOwnMH("println", Object.class, Object.class, Object[].class); - private static final MethodHandle LOAD = findOwnMH("load", Object.class, Object.class, Object.class); - private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH("loadWithNewGlobal", Object.class, Object.class, Object[].class); - private static final MethodHandle EXIT = findOwnMH("exit", Object.class, Object.class, Object.class); + 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 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$; @@ -390,6 +430,25 @@ public final class Global extends ScriptObject implements Scope { // context to which this global belongs to private final Context context; + // 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) { + this.scontext = scontext; + } + @Override protected Context getContext() { return context; @@ -407,12 +466,7 @@ public final class Global extends ScriptObject implements Scope { // null check on context context.getClass(); - /* - * Duplicate global's map and use it. This way the initial Map filled - * by nasgen (referenced from static field in this class) is retained - * 'as is' (as that one is process wide singleton. - */ - return $nasgenmap$.duplicate(); + return $nasgenmap$; } /** @@ -424,6 +478,7 @@ public final class Global extends ScriptObject implements Scope { super(checkAndGetMap(context)); this.context = context; this.setIsScope(); + this.lexicalScope = context.getEnv()._es6 ? new LexicalScope(this) : null; } /** @@ -432,11 +487,23 @@ public final class Global extends ScriptObject implements Scope { * @return the global singleton */ public static Global instance() { - Global global = Context.getGlobal(); + final Global global = Context.getGlobal(); global.getClass(); // null check return global; } + private static Global instanceFrom(final Object self) { + return self instanceof Global? (Global)self : instance(); + } + + /** + * Check if we have a Global instance + * @return true if one exists + */ + public static boolean hasInstance() { + return Context.getGlobal() != null; + } + /** * Script access to {@link ScriptEnvironment} * @@ -458,6 +525,14 @@ public final class Global extends ScriptObject implements Scope { // Runtime interface to Global /** + * Is there a class filter in the current Context? + * @return class filter + */ + public ClassFilter getClassFilter() { + return context.getClassFilter(); + } + + /** * Is this global of the given Context? * @param ctxt the context * @return true if this global belongs to the given Context @@ -478,28 +553,18 @@ public final class Global extends ScriptObject implements Scope { * Initialize standard builtin objects like "Object", "Array", "Function" etc. * as well as our extension builtin objects like "Java", "JSAdapter" as properties * of the global scope object. + * + * @param engine ScriptEngine to initialize */ - public void initBuiltinObjects() { + @SuppressWarnings("hiding") + public void initBuiltinObjects(final ScriptEngine engine) { if (this.builtinObject != null) { // already initialized, just return return; } - init(); - } - - /** - * Create a new ScriptFunction object - * - * @param name function name - * @param handle invocation handle for function - * @param scope the scope - * @param strict are we in strict mode - * - * @return new script function - */ - public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) { - return new ScriptFunctionImpl(name, handle, scope, null, strict ? ScriptFunctionData.IS_STRICT_CONSTRUCTOR : ScriptFunctionData.IS_CONSTRUCTOR); + this.engine = engine; + init(engine); } /** @@ -537,7 +602,7 @@ public final class Global extends ScriptObject implements Scope { * * @return guarded invocation */ - public GuardedInvocation primitiveLookup(final LinkRequest request, final Object self) { + public static GuardedInvocation primitiveLookup(final LinkRequest request, final Object self) { if (self instanceof String || self instanceof ConsString) { return NativeString.lookupPrimitive(request, self); } else if (self instanceof Number) { @@ -549,6 +614,24 @@ public final class Global extends ScriptObject implements Scope { } /** + * Returns a method handle that creates a wrapper object for a JS primitive value. + * + * @param self receiver object + * @return method handle to create wrapper objects for primitive receiver + */ + public static MethodHandle getPrimitiveWrapFilter(final Object self) { + if (self instanceof String || self instanceof ConsString) { + return NativeString.WRAPFILTER; + } else if (self instanceof Number) { + return NativeNumber.WRAPFILTER; + } else if (self instanceof Boolean) { + return NativeBoolean.WRAPFILTER; + } + throw new IllegalArgumentException("Unsupported primitive: " + self); + } + + + /** * Create a new empty script object * * @return the new ScriptObject @@ -729,6 +812,7 @@ public final class Global extends ScriptObject implements Scope { * @param value of the data property * @param configurable is the property configurable? * @param enumerable is the property enumerable? + * @param writable is the property writable? * @return newly created DataPropertyDescriptor object */ public PropertyDescriptor newDataDescriptor(final Object value, final boolean configurable, final boolean enumerable, final boolean writable) { @@ -758,7 +842,6 @@ public final class Global extends ScriptObject implements Scope { return desc; } - private static <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) { final T obj = map.get(key); if (obj != null) { @@ -800,6 +883,41 @@ public final class Global extends ScriptObject implements Scope { } /** + * Hook to search missing variables in ScriptContext if available + * @param self used to detect if scope call or not (this function is 'strict') + * @param name name of the variable missing + * @return value of the missing variable or undefined (or TypeError for scope search) + */ + public static Object __noSuchProperty__(final Object self, final Object name) { + final Global global = Global.instance(); + final ScriptContext sctxt = global.scontext; + final String nameStr = name.toString(); + + if (sctxt != null) { + final int scope = sctxt.getAttributesScope(nameStr); + if (scope != -1) { + return ScriptObjectMirror.unwrap(sctxt.getAttribute(nameStr, scope), global); + } + } + + switch (nameStr) { + case "context": + return sctxt; + case "engine": + return global.engine; + default: + break; + } + + if (self == UNDEFINED) { + // scope access and so throw ReferenceError + throw referenceError(global, "not.defined", nameStr); + } + + return UNDEFINED; + } + + /** * This is the eval used when 'indirect' eval call is made. * * var global = this; @@ -811,7 +929,7 @@ public final class Global extends ScriptObject implements Scope { * @return the result of eval */ public static Object eval(final Object self, final Object str) { - return directEval(self, str, UNDEFINED, UNDEFINED, UNDEFINED); + return directEval(self, str, UNDEFINED, UNDEFINED, false); } /** @@ -827,14 +945,14 @@ public final class Global extends ScriptObject implements Scope { * * This is directly invoked from generated when eval(code) is called in user code */ - public static Object directEval(final Object self, final Object str, final Object callThis, final Object location, final Object strict) { + public static Object directEval(final Object self, final Object str, final Object callThis, final Object location, final boolean strict) { if (!(str instanceof String || str instanceof ConsString)) { return str; } - final Global global = Global.instance(); - final ScriptObject scope = (self instanceof ScriptObject) ? (ScriptObject)self : global; + final Global global = Global.instanceFrom(self); + final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global; - return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict)); + return global.getContext().eval(scope, str.toString(), callThis, location, strict, true); } /** @@ -846,7 +964,7 @@ public final class Global extends ScriptObject implements Scope { * @return result of print (undefined) */ public static Object print(final Object self, final Object... objects) { - return printImpl(false, objects); + return Global.instanceFrom(self).printImpl(false, objects); } /** @@ -858,7 +976,7 @@ public final class Global extends ScriptObject implements Scope { * @return result of println (undefined) */ public static Object println(final Object self, final Object... objects) { - return printImpl(true, objects); + return Global.instanceFrom(self).printImpl(true, objects); } /** @@ -872,8 +990,8 @@ public final class Global extends ScriptObject implements Scope { * @throws IOException if source could not be read */ public static Object load(final Object self, final Object source) throws IOException { - final Global global = Global.instance(); - final ScriptObject scope = (self instanceof ScriptObject) ? (ScriptObject)self : global; + final Global global = Global.instanceFrom(self); + final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global; return global.getContext().load(scope, source); } @@ -888,7 +1006,7 @@ public final class Global extends ScriptObject implements Scope { * @throws IOException if source could not be read */ public static Object loadWithNewGlobal(final Object self, final Object...args) throws IOException { - final Global global = Global.instance(); + final Global global = Global.instanceFrom(self); final int length = args.length; final boolean hasArgs = 0 < length; final Object from = hasArgs ? args[0] : UNDEFINED; @@ -903,7 +1021,7 @@ public final class Global extends ScriptObject implements Scope { * @param self self reference * @param code exit code * - * @return undefined (will never be reacheD) + * @return undefined (will never be reached) */ public static Object exit(final Object self, final Object code) { System.exit(JSType.toInt32(code)); @@ -1111,6 +1229,40 @@ public final class Global extends ScriptObject implements Scope { return instance.function == instance.getBuiltinFunction(); } + /** + * Get the switchpoint used to check property changes for Function.prototype.apply + * @return the switchpoint guarding apply (same as guarding call, and everything else in function) + */ + public static SwitchPoint getBuiltinFunctionApplySwitchPoint() { + return ScriptFunction.getPrototype(Global.instance().getBuiltinFunction()).getProperty("apply").getBuiltinSwitchPoint(); + } + + private static boolean isBuiltinFunctionProperty(final String name) { + final Global instance = Global.instance(); + final ScriptFunction builtinFunction = instance.getBuiltinFunction(); + if (builtinFunction == null) { + return false; //conservative for compile-only mode + } + final boolean isBuiltinFunction = instance.function == builtinFunction; + return isBuiltinFunction && ScriptFunction.getPrototype(builtinFunction).getProperty(name).isBuiltin(); + } + + /** + * Check if the Function.prototype.apply has not been replaced + * @return true if Function.prototype.apply has been replaced + */ + public static boolean isBuiltinFunctionPrototypeApply() { + return isBuiltinFunctionProperty("apply"); + } + + /** + * Check if the Function.prototype.apply has not been replaced + * @return true if Function.prototype.call has been replaced + */ + public static boolean isBuiltinFunctionPrototypeCall() { + return isBuiltinFunctionProperty("call"); + } + private ScriptFunction getBuiltinJSAdapter() { return builtinJSAdapter; } @@ -1457,6 +1609,26 @@ public final class Global extends ScriptObject implements Scope { } /** + * Called from generated to replace a location property placeholder with the actual location property value. + * + * @param placeholder the value tested for being a placeholder for a location property + * @param locationProperty the actual value for the location property + * @return locationProperty if placeholder is indeed a placeholder for a location property, the placeholder otherwise + */ + public static Object replaceLocationPropertyPlaceholder(final Object placeholder, final Object locationProperty) { + return isLocationPropertyPlaceholder(placeholder) ? locationProperty : placeholder; + } + + /** + * Called from runtime internals to check if the passed value is a location property placeholder. + * @param placeholder the value tested for being a placeholder for a location property + * @return true if the value is a placeholder, false otherwise. + */ + public static boolean isLocationPropertyPlaceholder(final Object placeholder) { + return placeholder == LOCATION_PROPERTY_PLACEHOLDER; + } + + /** * Create a new RegExp object. * * @param expression Regular expression. @@ -1494,11 +1666,13 @@ public final class Global extends ScriptObject implements Scope { * not the case * * @param obj and object to check + * @return the script object */ - public static void checkObject(final Object obj) { + public static ScriptObject checkObject(final Object obj) { if (!(obj instanceof ScriptObject)) { throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); } + return (ScriptObject)obj; } /** @@ -1533,7 +1707,141 @@ public final class Global extends ScriptObject implements Scope { splitState = state; } - private void init() { + /** + * 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; + } + + @SuppressWarnings("hiding") + private void init(final ScriptEngine engine) { assert Context.getGlobal() == this : "this global is not set as current"; final ScriptEnvironment env = getContext().getEnv(); @@ -1547,7 +1855,19 @@ public final class Global extends ScriptObject implements Scope { // initialize global function properties this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL); - this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT); + this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT, + new Specialization[] { + new Specialization(GlobalFunctions.PARSEINT_Z), + new Specialization(GlobalFunctions.PARSEINT_I), + new Specialization(GlobalFunctions.PARSEINT_J), + new Specialization(GlobalFunctions.PARSEINT_OI), + new Specialization(GlobalFunctions.PARSEINT_O) }); + this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT); + this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN, + new Specialization[] { + new Specialization(GlobalFunctions.IS_NAN_I), + new Specialization(GlobalFunctions.IS_NAN_J), + new Specialization(GlobalFunctions.IS_NAN_D) }); this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT); this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN); this.isFinite = ScriptFunctionImpl.makeFunction("isFinite", GlobalFunctions.IS_FINITE); @@ -1559,20 +1879,20 @@ public final class Global extends ScriptObject implements Scope { 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 = (ScriptFunction)initConstructor("Array"); - this.builtinBoolean = (ScriptFunction)initConstructor("Boolean"); - this.builtinDate = (ScriptFunction)initConstructor("Date"); - this.builtinJSON = initConstructor("JSON"); - this.builtinJSAdapter = (ScriptFunction)initConstructor("JSAdapter"); - this.builtinMath = initConstructor("Math"); - this.builtinNumber = (ScriptFunction)initConstructor("Number"); - this.builtinRegExp = (ScriptFunction)initConstructor("RegExp"); - this.builtinString = (ScriptFunction)initConstructor("String"); + this.builtinArray = initConstructorAndSwitchPoint("Array", ScriptFunction.class); + this.builtinBoolean = initConstructorAndSwitchPoint("Boolean", ScriptFunction.class); + this.builtinDate = initConstructorAndSwitchPoint("Date", ScriptFunction.class); + this.builtinJSON = initConstructorAndSwitchPoint("JSON", ScriptObject.class); + this.builtinJSAdapter = initConstructorAndSwitchPoint("JSAdapter", ScriptFunction.class); + this.builtinMath = initConstructorAndSwitchPoint("Math", ScriptObject.class); + this.builtinNumber = initConstructorAndSwitchPoint("Number", ScriptFunction.class); + this.builtinRegExp = initConstructorAndSwitchPoint("RegExp", ScriptFunction.class); + this.builtinString = initConstructorAndSwitchPoint("String", ScriptFunction.class); // initialize String.prototype.length to 0 // add String.prototype.length @@ -1632,25 +1952,25 @@ public final class Global extends ScriptObject implements Scope { copyBuiltins(); - // initialized with strings so that typeof will work as expected. - this.__FILE__ = ""; - this.__DIR__ = ""; - this.__LINE__ = 0.0; - // expose script (command line) arguments as "arguments" property of global - final List<String> arguments = env.getArguments(); - final Object argsObj = wrapAsObject(arguments.toArray()); - - addOwnProperty("arguments", Attribute.NOT_ENUMERABLE, argsObj); + arguments = wrapAsObject(env.getArguments().toArray()); if (env._scripting) { // synonym for "arguments" in scripting mode - addOwnProperty("$ARG", Attribute.NOT_ENUMERABLE, argsObj); + addOwnProperty("$ARG", Attribute.NOT_ENUMERABLE, arguments); + } + + if (engine != null) { + // default file name + addOwnProperty(ScriptEngine.FILENAME, Attribute.NOT_ENUMERABLE, null); + // __noSuchProperty__ hook for ScriptContext search of missing variables + final ScriptFunction noSuchProp = ScriptFunctionImpl.makeStrictFunction(NO_SUCH_PROPERTY_NAME, NO_SUCH_PROPERTY); + addOwnProperty(NO_SUCH_PROPERTY_NAME, Attribute.NOT_ENUMERABLE, noSuchProp); } } private void initErrorObjects() { // Error objects - this.builtinError = (ScriptFunction)initConstructor("Error"); + this.builtinError = initConstructor("Error", ScriptFunction.class); final ScriptObject errorProto = getErrorPrototype(); // Nashorn specific accessors on Error.prototype - stack, lineNumber, columnNumber and fileName @@ -1669,10 +1989,12 @@ public final class Global extends ScriptObject implements Scope { // ECMA 15.11.4.2 Error.prototype.name // Error.prototype.name = "Error"; - errorProto.set(NativeError.NAME, "Error", false); + errorProto.set(NativeError.NAME, "Error", 0); // ECMA 15.11.4.3 Error.prototype.message // Error.prototype.message = ""; - errorProto.set(NativeError.MESSAGE, "", false); + errorProto.set(NativeError.MESSAGE, "", 0); + + tagBuiltinProperties("Error", builtinError); this.builtinEvalError = initErrorSubtype("EvalError", errorProto); this.builtinRangeError = initErrorSubtype("RangeError", errorProto); @@ -1683,12 +2005,13 @@ public final class Global extends ScriptObject implements Scope { } private ScriptFunction initErrorSubtype(final String name, final ScriptObject errorProto) { - final ScriptObject cons = initConstructor(name); + final ScriptFunction cons = initConstructor(name, ScriptFunction.class); final ScriptObject prototype = ScriptFunction.getPrototype(cons); - prototype.set(NativeError.NAME, name, false); - prototype.set(NativeError.MESSAGE, "", false); + prototype.set(NativeError.NAME, name, 0); + prototype.set(NativeError.MESSAGE, "", 0); prototype.setInitialProto(errorProto); - return (ScriptFunction)cons; + tagBuiltinProperties(name, cons); + return cons; } private void initJavaAccess() { @@ -1700,8 +2023,8 @@ public final class Global extends ScriptObject implements Scope { this.builtinJavafx = new NativeJavaPackage("javafx", objectProto); this.builtinJavax = new NativeJavaPackage("javax", objectProto); this.builtinOrg = new NativeJavaPackage("org", objectProto); - this.builtinJavaImporter = initConstructor("JavaImporter"); - this.builtinJavaApi = initConstructor("Java"); + this.builtinJavaImporter = initConstructor("JavaImporter", ScriptFunction.class); + this.builtinJavaApi = initConstructor("Java", ScriptObject.class); } private void initScripting(final ScriptEnvironment scriptEnv) { @@ -1744,9 +2067,9 @@ public final class Global extends ScriptObject implements Scope { } private static void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) { - for (Field f : scriptEnv.getClass().getFields()) { + for (final Field f : scriptEnv.getClass().getFields()) { try { - options.set(f.getName(), f.get(scriptEnv), false); + options.set(f.getName(), f.get(scriptEnv), 0); } catch (final IllegalArgumentException | IllegalAccessException exp) { throw new RuntimeException(exp); } @@ -1754,17 +2077,18 @@ public final class Global extends ScriptObject implements Scope { } private void initTypedArray() { - this.builtinArrayBuffer = initConstructor("ArrayBuffer"); - this.builtinDataView = initConstructor("DataView"); - this.builtinInt8Array = initConstructor("Int8Array"); - this.builtinUint8Array = initConstructor("Uint8Array"); - this.builtinUint8ClampedArray = initConstructor("Uint8ClampedArray"); - this.builtinInt16Array = initConstructor("Int16Array"); - this.builtinUint16Array = initConstructor("Uint16Array"); - this.builtinInt32Array = initConstructor("Int32Array"); - this.builtinUint32Array = initConstructor("Uint32Array"); - this.builtinFloat32Array = initConstructor("Float32Array"); - this.builtinFloat64Array = initConstructor("Float64Array"); + this.builtinArrayBuffer = initConstructorAndSwitchPoint("ArrayBuffer", ScriptFunction.class); + this.builtinDataView = initConstructorAndSwitchPoint("DataView", ScriptFunction.class); + this.builtinInt8Array = initConstructorAndSwitchPoint("Int8Array", ScriptFunction.class); + this.builtinUint8Array = initConstructorAndSwitchPoint("Uint8Array", ScriptFunction.class); + this.builtinUint8ClampedArray = initConstructorAndSwitchPoint("Uint8ClampedArray", ScriptFunction.class); + this.builtinInt16Array = initConstructorAndSwitchPoint("Int16Array", ScriptFunction.class); + this.builtinUint16Array = initConstructorAndSwitchPoint("Uint16Array", ScriptFunction.class); + this.builtinInt32Array = initConstructorAndSwitchPoint("Int32Array", ScriptFunction.class); + this.builtinUint32Array = initConstructorAndSwitchPoint("Uint32Array", ScriptFunction.class); + this.builtinFloat32Array = initConstructorAndSwitchPoint("Float32Array", ScriptFunction.class); + this.builtinFloat64Array = initConstructorAndSwitchPoint("Float64Array", ScriptFunction.class); + } private void copyBuiltins() { @@ -1809,20 +2133,20 @@ public final class Global extends ScriptObject implements Scope { } private void initDebug() { - this.addOwnProperty("Debug", Attribute.NOT_ENUMERABLE, initConstructor("Debug")); + this.addOwnProperty("Debug", Attribute.NOT_ENUMERABLE, initConstructor("Debug", ScriptObject.class)); } - @SuppressWarnings("resource") - private static Object printImpl(final boolean newLine, final Object... objects) { - final PrintWriter out = Global.getEnv().getOut(); + private Object printImpl(final boolean newLine, final Object... objects) { + @SuppressWarnings("resource") + final PrintWriter out = scontext != null? new PrintWriter(scontext.getWriter()) : getContext().getEnv().getOut(); final StringBuilder sb = new StringBuilder(); - for (final Object object : objects) { + for (final Object obj : objects) { if (sb.length() != 0) { sb.append(' '); } - sb.append(JSType.toString(object)); + sb.append(JSType.toString(obj)); } // Print all at once to ensure thread friendly result. @@ -1837,11 +2161,7 @@ public final class Global extends ScriptObject implements Scope { return UNDEFINED; } - /** - * These classes are generated by nasgen tool and so we have to use - * reflection to load and create new instance of these classes. - */ - private ScriptObject initConstructor(final String name) { + private <T extends ScriptObject> T initConstructor(final String name, final Class<T> clazz) { try { // Assuming class name pattern for built-in JS constructors. final StringBuilder sb = new StringBuilder("jdk.nashorn.internal.objects."); @@ -1850,8 +2170,8 @@ public final class Global extends ScriptObject implements Scope { sb.append(name); sb.append("$Constructor"); - final Class<?> funcClass = Class.forName(sb.toString()); - final ScriptObject res = (ScriptObject)funcClass.newInstance(); + final Class<?> funcClass = Class.forName(sb.toString()); + final T res = clazz.cast(funcClass.newInstance()); if (res instanceof ScriptFunction) { // All global constructor prototypes are not-writable, @@ -1864,13 +2184,53 @@ public final class Global extends ScriptObject implements Scope { res.setInitialProto(getObjectPrototype()); } - return res; + res.setIsBuiltin(); + return res; } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } + private List<jdk.nashorn.internal.runtime.Property> extractBuiltinProperties(final String name, final ScriptObject func) { + final List<jdk.nashorn.internal.runtime.Property> list = new ArrayList<>(); + + list.addAll(Arrays.asList(func.getMap().getProperties())); + + if (func instanceof ScriptFunction) { + final ScriptObject proto = ScriptFunction.getPrototype((ScriptFunction)func); + if (proto != null) { + list.addAll(Arrays.asList(proto.getMap().getProperties())); + } + } + + final jdk.nashorn.internal.runtime.Property prop = getProperty(name); + if (prop != null) { + list.add(prop); + } + + return list; + } + + /** + * Given a builtin object, traverse its properties recursively and associate them with a name that + * will be a key to their invalidation switchpoint. + * @param name name for key + * @param func builtin script object + */ + private void tagBuiltinProperties(final String name, final ScriptObject func) { + SwitchPoint sp = context.getBuiltinSwitchPoint(name); + if (sp == null) { + sp = context.newBuiltinSwitchPoint(name); + } + + //get all builtin properties in this builtin object and register switchpoints keyed on the propery name, + //one overwrite destroys all for now, e.g. Function.prototype.apply = 17; also destroys Function.prototype.call etc + for (final jdk.nashorn.internal.runtime.Property prop : extractBuiltinProperties(name, func)) { + prop.setBuiltinSwitchPoint(sp); + } + } + // Function and Object constructors are inter-dependent. Also, // Function.prototype // functions are not properly initialized. We fix the references here. @@ -1879,10 +2239,11 @@ public final class Global extends ScriptObject implements Scope { // to play with object references carefully!! private void initFunctionAndObject() { // First-n-foremost is Function - this.builtinFunction = (ScriptFunction)initConstructor("Function"); + + this.builtinFunction = initConstructor("Function", ScriptFunction.class); // create global anonymous function - final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction(this); + final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction(); // need to copy over members of Function.prototype to anon function anon.addBoundProperties(getFunctionPrototype()); @@ -1890,7 +2251,7 @@ public final class Global extends ScriptObject implements Scope { // <anon-function> builtinFunction.setInitialProto(anon); builtinFunction.setPrototype(anon); - anon.set("constructor", builtinFunction, false); + anon.set("constructor", builtinFunction, 0); anon.deleteOwnProperty(anon.getMap().findProperty("prototype")); // use "getter" so that [[ThrowTypeError]] function's arity is 0 - as specified in step 10 of section 13.2.3 @@ -1901,7 +2262,7 @@ public final class Global extends ScriptObject implements Scope { typeErrorThrower.preventExtensions(); // now initialize Object - this.builtinObject = (ScriptFunction)initConstructor("Object"); + this.builtinObject = initConstructor("Object", ScriptFunction.class); final ScriptObject ObjectPrototype = getObjectPrototype(); // Object.getPrototypeOf(Function.prototype) === Object.prototype anon.setInitialProto(ObjectPrototype); @@ -1912,7 +2273,6 @@ public final class Global extends ScriptObject implements Scope { final ScriptFunction setProto = ScriptFunctionImpl.makeFunction("setProto", NativeObject.SET__PROTO__); ObjectPrototype.addOwnProperty("__proto__", Attribute.NOT_ENUMERABLE, getProto, setProto); - // Function valued properties of Function.prototype were not properly // initialized. Because, these were created before global.function and // global.object were not initialized. @@ -1947,14 +2307,14 @@ public final class Global extends ScriptObject implements Scope { } properties = getObjectPrototype().getMap().getProperties(); + for (final jdk.nashorn.internal.runtime.Property property : properties) { final Object key = property.getKey(); - final Object value = ObjectPrototype.get(key); - if (key.equals("constructor")) { continue; } + final Object value = ObjectPrototype.get(key); if (value instanceof ScriptFunction) { final ScriptFunction func = (ScriptFunction)value; final ScriptObject prototype = ScriptFunction.getPrototype(func); @@ -1963,9 +2323,13 @@ public final class Global extends ScriptObject implements Scope { } } } + + tagBuiltinProperties("Object", builtinObject); + tagBuiltinProperties("Function", builtinFunction); + tagBuiltinProperties("Function", anon); } - private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { + private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { return MH.findStatic(MethodHandles.lookup(), Global.class, name, MH.type(rtype, types)); } @@ -1977,4 +2341,40 @@ public final class Global extends ScriptObject implements Scope { this.lastRegExpResult = regExpResult; } + @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); + } + } + } |