This commit is contained in:
Athijegannathan Sundararajan 2013-08-23 16:44:02 +05:30
commit 1e1c0d3b80
51 changed files with 2813 additions and 987 deletions

View file

@ -55,6 +55,7 @@ import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.GlobalObject;
@ -74,6 +75,12 @@ import jdk.nashorn.internal.runtime.options.Options;
*/
public final class NashornScriptEngine extends AbstractScriptEngine implements Compilable, Invocable {
/**
* Key used to associate Nashorn global object mirror with arbitrary Bindings instance.
*/
public static final String NASHORN_GLOBAL = "nashorn.global";
// commonly used access control context objects
private static AccessControlContext createPermAccCtxt(final String permName) {
final Permissions perms = new Permissions();
perms.add(new RuntimePermission(permName));
@ -83,16 +90,23 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
private static final AccessControlContext CREATE_CONTEXT_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_CONTEXT);
private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_GLOBAL);
// the factory that created this engine
private final ScriptEngineFactory factory;
// underlying nashorn Context - 1:1 with engine instance
private final Context nashornContext;
// do we want to share single Nashorn global instance across ENGINE_SCOPEs?
private final boolean _global_per_engine;
// This is the initial default Nashorn global object.
// This is used as "shared" global if above option is true.
private final ScriptObject global;
// initialized bit late to be made 'final'. Property object for "context"
// property of global object
private Property contextProperty;
// initialized bit late to be made 'final'.
// Property object for "context" property of global object.
private volatile Property contextProperty;
// default options passed to Nashorn Options object
private static final String[] DEFAULT_OPTIONS = new String[] { "-scripting", "-doe" };
// Nashorn script engine error message management
private static final String MESSAGES_RESOURCE = "jdk.nashorn.api.scripting.resources.Messages";
private static final ResourceBundle MESSAGES_BUNDLE;
@ -100,6 +114,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
MESSAGES_BUNDLE = ResourceBundle.getBundle(MESSAGES_RESOURCE, Locale.getDefault());
}
// helper to get Nashorn script engine error message
private static String getMessage(final String msgId, final String... args) {
try {
return new MessageFormat(MESSAGES_BUNDLE.getString(msgId)).format(args);
@ -108,6 +123,30 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
}
// load engine.js and return content as a char[]
private static char[] loadEngineJSSource() {
final String script = "resources/engine.js";
try {
final InputStream is = AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
@Override
public InputStream run() throws Exception {
final URL url = NashornScriptEngine.class.getResource(script);
return url.openStream();
}
});
return Source.readFully(new InputStreamReader(is));
} catch (final PrivilegedActionException | IOException e) {
if (Context.DEBUG) {
e.printStackTrace();
}
throw new RuntimeException(e);
}
}
// Source object for engine.js
private static final Source ENGINE_SCRIPT_SRC = new Source(NashornException.ENGINE_SCRIPT_SOURCE_NAME, loadEngineJSSource());
NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) {
this(factory, DEFAULT_OPTIONS, appLoader);
}
@ -134,20 +173,13 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
}, CREATE_CONTEXT_ACC_CTXT);
// create new global object
this.global = createNashornGlobal();
// set the default engine scope for the default context
context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE);
// cache this option that is used often
this._global_per_engine = nashornContext.getEnv()._global_per_engine;
// evaluate engine initial script
try {
evalEngineScript();
} catch (final ScriptException e) {
if (Context.DEBUG) {
e.printStackTrace();
}
throw new RuntimeException(e);
}
// create new global object
this.global = createNashornGlobal(context);
// set the default ENGINE_SCOPE object for the default context
context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE);
}
@Override
@ -176,8 +208,13 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
@Override
public Bindings createBindings() {
final ScriptObject newGlobal = createNashornGlobal();
return new ScriptObjectMirror(newGlobal, newGlobal);
if (_global_per_engine) {
// just create normal SimpleBindings.
// We use same 'global' for all Bindings.
return new SimpleBindings();
} else {
return createGlobalMirror(null);
}
}
// Compilable methods
@ -213,6 +250,48 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
return invokeImpl(thiz, name, args);
}
@Override
public <T> T getInterface(final Class<T> clazz) {
return getInterfaceInner(null, clazz);
}
@Override
public <T> T getInterface(final Object thiz, final Class<T> clazz) {
if (thiz == null) {
throw new IllegalArgumentException(getMessage("thiz.cannot.be.null"));
}
return getInterfaceInner(thiz, clazz);
}
// These are called from the "engine.js" script
/**
* This hook is used to search js global variables exposed from Java code.
*
* @param self 'this' passed from the script
* @param ctxt current ScriptContext in which name is searched
* @param name name of the variable searched
* @return the value of the named variable
*/
public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) {
if (ctxt != null) {
final int scope = ctxt.getAttributesScope(name);
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
if (scope != -1) {
return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal);
}
if (self == UNDEFINED) {
// scope access and so throw ReferenceError
throw referenceError(ctxtGlobal, "not.defined", name);
}
}
return UNDEFINED;
}
// Implementation only below this point
private <T> T getInterfaceInner(final Object thiz, final Class<T> clazz) {
if (clazz == null || !clazz.isInterface()) {
throw new IllegalArgumentException(getMessage("interface.class.expected"));
@ -280,58 +359,56 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
}
@Override
public <T> T getInterface(final Class<T> clazz) {
return getInterfaceInner(null, clazz);
}
@Override
public <T> T getInterface(final Object thiz, final Class<T> clazz) {
if (thiz == null) {
throw new IllegalArgumentException(getMessage("thiz.cannot.be.null"));
}
return getInterfaceInner(thiz, clazz);
}
// These are called from the "engine.js" script
/**
* This hook is used to search js global variables exposed from Java code.
*
* @param self 'this' passed from the script
* @param ctxt current ScriptContext in which name is searched
* @param name name of the variable searched
* @return the value of the named variable
*/
public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) {
final int scope = ctxt.getAttributesScope(name);
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
if (scope != -1) {
return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal);
}
if (self == UNDEFINED) {
// scope access and so throw ReferenceError
throw referenceError(ctxtGlobal, "not.defined", name);
}
return UNDEFINED;
}
// Retrieve nashorn Global object for a given ScriptContext object
private ScriptObject getNashornGlobalFrom(final ScriptContext ctxt) {
final Bindings bindings = ctxt.getBindings(ScriptContext.ENGINE_SCOPE);
if (bindings instanceof ScriptObjectMirror) {
ScriptObject sobj = ((ScriptObjectMirror)bindings).getScriptObject();
if (sobj instanceof GlobalObject) {
return sobj;
}
if (_global_per_engine) {
// shared single global object for all ENGINE_SCOPE Bindings
return global;
}
// didn't find global object from context given - return the engine-wide global
return global;
final Bindings bindings = ctxt.getBindings(ScriptContext.ENGINE_SCOPE);
// is this Nashorn's own Bindings implementation?
if (bindings instanceof ScriptObjectMirror) {
final ScriptObject sobj = globalFromMirror((ScriptObjectMirror)bindings);
if (sobj != null) {
return sobj;
}
}
// Arbitrary user Bindings implementation. Look for NASHORN_GLOBAL in it!
Object scope = bindings.get(NASHORN_GLOBAL);
if (scope instanceof ScriptObjectMirror) {
final ScriptObject sobj = globalFromMirror((ScriptObjectMirror)scope);
if (sobj != null) {
return sobj;
}
}
// We didn't find associated nashorn global mirror in the Bindings given!
// Create new global instance mirror and associate with the Bindings.
final ScriptObjectMirror mirror = createGlobalMirror(ctxt);
bindings.put(NASHORN_GLOBAL, mirror);
return mirror.getScriptObject();
}
private ScriptObject createNashornGlobal() {
// Retrieve nashorn Global object from a given ScriptObjectMirror
private ScriptObject globalFromMirror(final ScriptObjectMirror mirror) {
ScriptObject sobj = mirror.getScriptObject();
if (sobj instanceof GlobalObject && sobj.isOfContext(nashornContext)) {
return sobj;
}
return null;
}
// Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object
private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) {
final ScriptObject newGlobal = createNashornGlobal(ctxt);
return new ScriptObjectMirror(newGlobal, newGlobal);
}
// Create a new Nashorn Global object
private ScriptObject createNashornGlobal(final ScriptContext ctxt) {
final ScriptObject newGlobal = AccessController.doPrivileged(new PrivilegedAction<ScriptObject>() {
@Override
public ScriptObject run() {
@ -352,7 +429,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
// current ScriptContext exposed as "context"
// "context" is non-writable from script - but script engine still
// needs to set it and so save the context Property object
contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, UNDEFINED);
contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, null);
// current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as
// NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property
// in the Global of a Context we just created - both the Context and the Global were just created and can not be
@ -362,38 +439,17 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
newGlobal.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED);
// file name default is null
newGlobal.addOwnProperty(ScriptEngine.FILENAME, Property.NOT_ENUMERABLE, null);
// evaluate engine.js initialization script this new global object
try {
evalImpl(compileImpl(ENGINE_SCRIPT_SRC, newGlobal), ctxt, newGlobal);
} catch (final ScriptException exp) {
throw new RuntimeException(exp);
}
return newGlobal;
}
private void evalEngineScript() throws ScriptException {
final String script = "resources/engine.js";
final String name = NashornException.ENGINE_SCRIPT_SOURCE_NAME;
try {
final InputStream is = AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
@Override
public InputStream run() throws Exception {
final URL url = NashornScriptEngine.class.getResource(script);
return url.openStream();
}
});
put(ScriptEngine.FILENAME, name);
try (final InputStreamReader isr = new InputStreamReader(is)) {
eval(isr);
}
} catch (final PrivilegedActionException | IOException e) {
if (Context.DEBUG) {
e.printStackTrace();
}
throw new ScriptException(e);
} finally {
put(ScriptEngine.FILENAME, null);
}
}
// scripts should see "context" and "engine" as variables
private void setContextVariables(final ScriptContext ctxt) {
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
// scripts should see "context" and "engine" as variables in the given global object
private void setContextVariables(final ScriptObject ctxtGlobal, final ScriptContext ctxt) {
// set "context" global variable via contextProperty - because this
// property is non-writable
contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
@ -402,8 +458,10 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
args = ScriptRuntime.EMPTY_ARRAY;
}
// if no arguments passed, expose it
args = ((GlobalObject)ctxtGlobal).wrapAsObject(args);
ctxtGlobal.set("arguments", args, false);
if (! (args instanceof ScriptObject)) {
args = ((GlobalObject)ctxtGlobal).wrapAsObject(args);
ctxtGlobal.set("arguments", args, false);
}
}
private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
@ -456,18 +514,24 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt) throws ScriptException {
return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt));
}
private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final ScriptObject ctxtGlobal) throws ScriptException {
if (script == null) {
return null;
}
final ScriptObject oldGlobal = Context.getGlobal();
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
final boolean globalChanged = (oldGlobal != ctxtGlobal);
try {
if (globalChanged) {
Context.setGlobal(ctxtGlobal);
}
setContextVariables(ctxt);
// set ScriptContext variables if ctxt is non-null
if (ctxt != null) {
setContextVariables(ctxtGlobal, ctxt);
}
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
} catch (final Exception e) {
throwAsScriptException(e);
@ -517,15 +581,18 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException {
return compileImpl(source, getNashornGlobalFrom(ctxt));
}
private ScriptFunction compileImpl(final Source source, final ScriptObject newGlobal) throws ScriptException {
final ScriptObject oldGlobal = Context.getGlobal();
final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
final boolean globalChanged = (oldGlobal != ctxtGlobal);
final boolean globalChanged = (oldGlobal != newGlobal);
try {
if (globalChanged) {
Context.setGlobal(ctxtGlobal);
Context.setGlobal(newGlobal);
}
return nashornContext.compileScript(source, ctxtGlobal);
return nashornContext.compileScript(source, newGlobal);
} catch (final Exception e) {
throwAsScriptException(e);
throw new AssertionError("should not reach here");

View file

@ -99,12 +99,14 @@ public final class ScriptObjectMirror extends JSObject implements Bindings {
}
final Object val = functionName == null? sobj : sobj.get(functionName);
if (! (val instanceof ScriptFunction)) {
throw new NoSuchMethodException("No such function " + ((functionName != null)? functionName : ""));
if (val instanceof ScriptFunction) {
final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
return wrap(ScriptRuntime.checkAndApply((ScriptFunction)val, sobj, unwrapArray(modArgs, global)), global);
} else if (val instanceof ScriptObjectMirror && ((ScriptObjectMirror)val).isFunction()) {
return ((ScriptObjectMirror)val).call(null, args);
}
final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
return wrap(ScriptRuntime.checkAndApply((ScriptFunction)val, sobj, unwrapArray(modArgs, global)), global);
throw new NoSuchMethodException("No such function " + ((functionName != null)? functionName : ""));
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
@ -127,12 +129,14 @@ public final class ScriptObjectMirror extends JSObject implements Bindings {
}
final Object val = functionName == null? sobj : sobj.get(functionName);
if (! (val instanceof ScriptFunction)) {
throw new RuntimeException("not a constructor " + ((functionName != null)? functionName : ""));
if (val instanceof ScriptFunction) {
final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
return wrap(ScriptRuntime.checkAndConstruct((ScriptFunction)val, unwrapArray(modArgs, global)), global);
} else if (val instanceof ScriptObjectMirror && ((ScriptObjectMirror)val).isFunction()) {
return ((ScriptObjectMirror)val).newObject(null, args);
}
final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
return wrap(ScriptRuntime.checkAndConstruct((ScriptFunction)val, unwrapArray(modArgs, global)), global);
throw new RuntimeException("not a constructor " + ((functionName != null)? functionName : ""));
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
@ -373,6 +377,19 @@ public final class ScriptObjectMirror extends JSObject implements Bindings {
});
}
/**
* Set the __proto__ of this object.
* @param proto new proto for this object
*/
public void setProto(final Object proto) {
inGlobal(new Callable<Void>() {
@Override public Void call() {
sobj.setProtoCheck(unwrap(proto, global));
return null;
}
});
}
/**
* ECMA 8.12.1 [[GetOwnProperty]] (P)
*

View file

@ -23,10 +23,9 @@
/**
* This script file is executed by script engine at the construction
* of the engine. The functions here assume global variables "context"
* of type javax.script.ScriptContext and "engine" of the type
* of the every new Global object. The functions here assume global variables
* "context" of type javax.script.ScriptContext and "engine" of the type
* jdk.nashorn.api.scripting.NashornScriptEngine.
*
**/
Object.defineProperty(this, "__noSuchProperty__", {
@ -40,7 +39,7 @@ Object.defineProperty(this, "__noSuchProperty__", {
});
function print() {
var writer = context.getWriter();
var writer = context != null? context.writer : engine.context.writer;
if (! (writer instanceof java.io.PrintWriter)) {
writer = new java.io.PrintWriter(writer);
}

View file

@ -543,8 +543,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
public Node leaveIdentNode(final IdentNode identNode) {
final String name = identNode.getName();
start(identNode);
if (identNode.isPropertyName()) {
// assign a pseudo symbol to property name
final Symbol pseudoSymbol = pseudoSymbol(name);
@ -1850,9 +1848,10 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
append(lc.getCurrentFunction().getName());
append(lc.getCurrentFunction().getName()).
append('\'');
if(node instanceof Expression) {
if (node instanceof Expression) {
final Symbol symbol = ((Expression)node).getSymbol();
if (symbol == null) {
sb.append(" <NO SYMBOL>");

View file

@ -32,7 +32,6 @@ import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.internal.objects.annotations.Attribute;
@ -44,6 +43,7 @@ import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ListAdapter;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
/**
@ -539,4 +539,25 @@ public final class NativeJava {
}
return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides);
}
/**
* When given an object created using {@code Java.extend()} or equivalent mechanism (that is, any JavaScript-to-Java
* adapter), returns an object that can be used to invoke superclass methods on that object. E.g.:
* <pre>
* var cw = new FilterWriterAdapter(sw) {
* write: function(s, off, len) {
* s = capitalize(s, off, len)
* cw_super.write(s, 0, s.length())
* }
* }
* var cw_super = Java.super(cw)
* </pre>
* @param self the {@code Java} object itself - not used.
* @param adapter the original Java adapter instance for which the super adapter is created.
* @return a super adapter for the original adapter
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR, name="super")
public static Object _super(final Object self, final Object adapter) {
return Bootstrap.createSuperAdapter(adapter);
}
}

View file

@ -124,6 +124,28 @@ public final class NativeObject {
}
}
/**
* Nashorn extension: Object.setPrototypeOf ( O, proto )
* Also found in ES6 draft specification.
*
* @param self self reference
* @param obj object to set prototype for
* @param proto prototype object to be used
* @return object whose prototype is set
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object setPrototypeOf(final Object self, final Object obj, final Object proto) {
if (obj instanceof ScriptObject) {
((ScriptObject)obj).setProtoCheck(proto);
return obj;
} else if (obj instanceof ScriptObjectMirror) {
((ScriptObjectMirror)obj).setProto(proto);
return obj;
}
throw notAnObject(obj);
}
/**
* ECMA 15.2.3.3 Object.getOwnPropertyDescriptor ( O, P )
*
@ -184,7 +206,7 @@ public final class NativeObject {
// FIXME: should we create a proper object with correct number of
// properties?
final ScriptObject newObj = Global.newEmptyInstance();
newObj.setProtoCheck(proto);
newObj.setProto((ScriptObject)proto);
if (props != UNDEFINED) {
NativeObject.defineProperties(self, newObj, props);
}

View file

@ -91,6 +91,11 @@ public final class Context {
*/
public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
/* Force DebuggerSupport to be loaded. */
static {
DebuggerSupport.FORCELOAD = true;
}
/**
* ContextCodeInstaller that has the privilege of installing classes in the Context.
* Can only be instantiated from inside the context and is opaque to other classes

View file

@ -0,0 +1,310 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime;
import java.util.HashSet;
import java.util.Set;
/**
* This class provides support for external debuggers. Its primary purpose is
* is to simplify the debugger tasks and provide better performance.
*/
final class DebuggerSupport {
/**
* Hook to force the loading of the DebuggerSupport class so that it is
* available to external debuggers.
*/
static boolean FORCELOAD = true;
static {
/**
* Hook to force the loading of the DebuggerValueDesc class so that it is
* available to external debuggers.
*/
DebuggerValueDesc forceLoad = new DebuggerValueDesc(null, false, null, null);
}
/** This class is used to send a bulk description of a value. */
static class DebuggerValueDesc {
/** Property key (or index) or field name. */
final String key;
/** If the value is expandable. */
final boolean expandable;
/** Property or field value as object. */
final Object valueAsObject;
/** Property or field value as string. */
final String valueAsString;
DebuggerValueDesc(final String key, final boolean expandable, final Object valueAsObject, final String valueAsString) {
this.key = key;
this.expandable = expandable;
this.valueAsObject = valueAsObject;
this.valueAsString = valueAsString;
}
}
/**
* Return the current context global.
* @return context global.
*/
static Object getGlobal() {
return Context.getGlobalTrusted();
}
/**
* This method returns a bulk description of an object's properties.
* @param object Script object to be displayed by the debugger.
* @param all true if to include non-enumerable values.
* @return An array of DebuggerValueDesc.
*/
static DebuggerValueDesc[] valueInfos(final Object object, final boolean all) {
assert object instanceof ScriptObject;
return getDebuggerValueDescs((ScriptObject)object, all, new HashSet<>());
}
/**
* This method returns a debugger description of the value.
* @param name Name of value (property name).
* @param value Data value.
* @param all true if to include non-enumerable values.
* @return A DebuggerValueDesc.
*/
static DebuggerValueDesc valueInfo(final String name, final Object value, final boolean all) {
return valueInfo(name, value, all, new HashSet<>());
}
/**
* This method returns a debugger description of the value.
* @param name Name of value (property name).
* @param value Data value.
* @param all true if to include non-enumerable values.
* @param duplicates Duplication set to avoid cycles.
* @return A DebuggerValueDesc.
*/
private static DebuggerValueDesc valueInfo(final String name, final Object value, final boolean all, final Set<Object> duplicates) {
if (value instanceof ScriptObject && !(value instanceof ScriptFunction)) {
final ScriptObject object = (ScriptObject)value;
return new DebuggerValueDesc(name, !object.isEmpty(), value, objectAsString(object, all, duplicates));
} else {
return new DebuggerValueDesc(name, false, value, valueAsString(value));
}
}
/**
* Generate the descriptions for an object's properties.
* @param object Object to introspect.
* @param all true if to include non-enumerable values.
* @param duplicates Duplication set to avoid cycles.
* @return An array of DebuggerValueDesc.
*/
private static DebuggerValueDesc[] getDebuggerValueDescs(final ScriptObject object, final boolean all, final Set<Object> duplicates) {
if (duplicates.contains(object)) {
return null;
}
duplicates.add(object);
final String[] keys = object.getOwnKeys(all);
final DebuggerValueDesc[] descs = new DebuggerValueDesc[keys.length];
for (int i = 0; i < keys.length; i++) {
final String key = keys[i];
descs[i] = valueInfo(key, object.get(key), true, duplicates);
}
duplicates.remove(object);
return descs;
}
/**
* Generate a string representation of a Script object.
* @param object Script object to represent.
* @param all true if to include non-enumerable values.
* @param duplicates Duplication set to avoid cycles.
* @return String representation.
*/
private static String objectAsString(final ScriptObject object, final boolean all, final Set<Object> duplicates) {
final StringBuilder sb = new StringBuilder();
if (ScriptObject.isArray(object)) {
sb.append('[');
final long length = object.getLong("length");
for (long i = 0; i < length; i++) {
if (object.has(i)) {
final Object valueAsObject = object.get(i);
final boolean isUndefined = JSType.of(valueAsObject) == JSType.UNDEFINED;
if (isUndefined) {
if (i != 0) {
sb.append(",");
}
} else {
if (i != 0) {
sb.append(", ");
}
if (valueAsObject instanceof ScriptObject && !(valueAsObject instanceof ScriptFunction)) {
final String objectString = objectAsString((ScriptObject)valueAsObject, true, duplicates);
sb.append(objectString != null ? objectString : "{...}");
} else {
sb.append(valueAsString(valueAsObject));
}
}
} else {
if (i != 0) {
sb.append(',');
}
}
}
sb.append(']');
} else {
sb.append('{');
final DebuggerValueDesc[] descs = getDebuggerValueDescs(object, all, duplicates);
if (descs != null) {
for (int i = 0; i < descs.length; i++) {
if (i != 0) {
sb.append(", ");
}
final String valueAsString = descs[i].valueAsString;
sb.append(descs[i].key);
sb.append(": ");
sb.append(descs[i].valueAsString);
}
}
sb.append('}');
}
return sb.toString();
}
/**
* This method returns a string representation of a value.
* @param value Arbitrary value to be displayed by the debugger.
* @return A string representation of the value or an array of DebuggerValueDesc.
*/
private static String valueAsString(final Object value) {
final JSType type = JSType.of(value);
switch (type) {
case BOOLEAN:
return value.toString();
case STRING:
return escape((String)value);
case NUMBER:
return JSType.toString(((Number)value).doubleValue());
case NULL:
return "null";
case UNDEFINED:
return "undefined";
case OBJECT:
return ScriptRuntime.safeToString(value);
case FUNCTION:
if (value instanceof ScriptFunction) {
return ((ScriptFunction)value).toSource();
} else {
return value.toString();
}
default:
return value.toString();
}
}
/**
* Escape a string into a form that can be parsed by JavaScript.
* @param value String to be escaped.
* @return Escaped string.
*/
private static String escape(final String value) {
final StringBuilder sb = new StringBuilder();
sb.append("\"");
for (final char ch : value.toCharArray()) {
switch (ch) {
case '\\':
sb.append("\\\\");
break;
case '"':
sb.append("\\\"");
break;
case '\'':
sb.append("\\\'");
break;
case '\b':
sb.append("\\b");
break;
case '\f':
sb.append("\\f");
break;
case '\n':
sb.append("\\n");
break;
case '\r':
sb.append("\\r");
break;
case '\t':
sb.append("\\t");
break;
default:
if (ch < ' ' || ch >= 0xFF) {
sb.append("\\u");
final String hex = Integer.toHexString(ch);
for (int i = hex.length(); i < 4; i++) {
sb.append('0');
}
sb.append(hex);
} else {
sb.append(ch);
}
break;
}
}
sb.append("\"");
return sb.toString();
}
}

View file

@ -30,11 +30,10 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.lang.invoke.MethodHandles;
import java.util.Locale;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
* Representation for ECMAScript types - this maps directly to the ECMA script standard
@ -148,22 +147,10 @@ public enum JSType {
return JSType.STRING;
}
if (obj instanceof ScriptObject) {
return (obj instanceof ScriptFunction) ? JSType.FUNCTION : JSType.OBJECT;
}
if (obj instanceof StaticClass) {
if (Bootstrap.isCallable(obj)) {
return JSType.FUNCTION;
}
if (BeansLinker.isDynamicMethod(obj)) {
return JSType.FUNCTION;
}
if (obj instanceof ScriptObjectMirror) {
return ((ScriptObjectMirror)obj).isFunction()? JSType.FUNCTION : JSType.OBJECT;
}
return JSType.OBJECT;
}

View file

@ -54,4 +54,13 @@ public interface PropertyListener {
*
*/
public void propertyModified(ScriptObject object, Property oldProp, Property newProp);
/**
* Given object's __proto__ has changed.
*
* @param object object whose __proto__ has changed.
* @param oldProto old __proto__
* @param newProto new __proto__
*/
public void protoChanged(ScriptObject object, ScriptObject oldProto, ScriptObject newProto);
}

View file

@ -140,6 +140,21 @@ public class PropertyListenerManager implements PropertyListener {
}
}
/**
* This method can be called to notify __proto__ modification to this object's listeners.
*
* @param object The ScriptObject whose __proto__ was changed.
* @param oldProto old __proto__
* @param newProto new __proto__
*/
protected synchronized final void notifyProtoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
if (listeners != null) {
for (PropertyListener listener : listeners.keySet()) {
listener.protoChanged(object, oldProto, newProto);
}
}
}
// PropertyListener methods
@Override
@ -156,4 +171,9 @@ public class PropertyListenerManager implements PropertyListener {
public final void propertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
notifyPropertyModified(object, oldProp, newProp);
}
@Override
public final void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
notifyProtoChanged(object, oldProto, newProto);
}
}

View file

@ -230,7 +230,7 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
}
/**
* Indicate that a prototype property hash changed.
* Indicate that a prototype property has changed.
*
* @param property {@link Property} to invalidate.
*/
@ -250,6 +250,18 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
}
}
/**
* Indicate that proto itself has changed in hierachy somewhere.
*/
private void invalidateAllProtoGetSwitchPoints() {
assert !isShared() : "proto invalidation on a shared PropertyMap";
if (protoGetSwitches != null) {
final Collection<SwitchPoint> sws = protoGetSwitches.values();
SwitchPoint.invalidateAll(sws.toArray(new SwitchPoint[sws.size()]));
}
}
/**
* Add a property to the map, re-binding its getters and setters,
* if available, to a given receiver. This is typically the global scope. See
@ -878,6 +890,15 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
invalidateProtoGetSwitchPoint(oldProp);
}
@Override
public void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
// We may walk and invalidate SwitchPoints for properties inherited
// from 'object' or it's old proto chain. But, it may not be worth it.
// For example, a new proto may have a user defined getter/setter for
// a data property down the chain. So, invalidating all is better.
invalidateAllProtoGetSwitchPoints();
}
/*
* Debugging and statistics.
*/

View file

@ -86,6 +86,9 @@ public final class ScriptEnvironment {
/** Launch using as fx application */
public final boolean _fx;
/** Use single Global instance per jsr223 engine instance. */
public final boolean _global_per_engine;
/**
* Behavior when encountering a function declaration in a lexical context where only statements are acceptable
* (function declarations are source elements, but not statements).
@ -128,9 +131,6 @@ public final class ScriptEnvironment {
/** Do not support typed arrays. */
public final boolean _no_typed_arrays;
/** Package to which generated class files are added */
public final String _package;
/** Only parse the source code, do not compile */
public final boolean _parse_only;
@ -211,12 +211,12 @@ public final class ScriptEnvironment {
_function_statement = FunctionStatementBehavior.ACCEPT;
}
_fx = options.getBoolean("fx");
_global_per_engine = options.getBoolean("global.per.engine");
_lazy_compilation = options.getBoolean("lazy.compilation");
_loader_per_compile = options.getBoolean("loader.per.compile");
_no_java = options.getBoolean("no.java");
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
_no_typed_arrays = options.getBoolean("no.typed.arrays");
_package = options.getString("package");
_parse_only = options.getBoolean("parse.only");
_print_ast = options.getBoolean("print.ast");
_print_lower_ast = options.getBoolean("print.lower.ast");

View file

@ -1008,10 +1008,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return getMap().findProperty(key);
}
static String convertKey(final Object key) {
return (key instanceof String) ? (String)key : JSType.toString(key);
}
/**
* Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
* Used for argument access in a vararg function using parameter name.
@ -1129,6 +1125,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
proto = newProto;
if (isPrototype()) {
// tell listeners that my __proto__ has been changed
notifyProtoChanged(this, oldProto, newProto);
if (oldProto != null) {
oldProto.removePropertyListener(this);
}
@ -1144,7 +1143,19 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
* @param newProto Prototype to set.
*/
public final void setProtoCheck(final Object newProto) {
if (!isExtensible()) {
throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
}
if (newProto == null || newProto instanceof ScriptObject) {
// check for circularity
ScriptObject proto = (ScriptObject)newProto;
while (proto != null) {
if (proto == this) {
throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
}
proto = proto.getProto();
}
setProto((ScriptObject)newProto);
} else {
final ScriptObject global = Context.getGlobalTrusted();
@ -2359,7 +2370,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getInt(index);
}
return getInt(index, convertKey(key));
return getInt(index, JSType.toString(key));
}
@Override
@ -2371,7 +2382,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getInt(index);
}
return getInt(index, convertKey(key));
return getInt(index, JSType.toString(key));
}
@Override
@ -2383,7 +2394,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getInt(index);
}
return getInt(index, convertKey(key));
return getInt(index, JSType.toString(key));
}
@Override
@ -2394,7 +2405,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getInt(key);
}
return getInt(key, convertKey(key));
return getInt(key, JSType.toString(key));
}
private long getLong(final int index, final String key) {
@ -2436,7 +2447,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getLong(index);
}
return getLong(index, convertKey(key));
return getLong(index, JSType.toString(key));
}
@Override
@ -2448,7 +2459,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getLong(index);
}
return getLong(index, convertKey(key));
return getLong(index, JSType.toString(key));
}
@Override
@ -2460,7 +2471,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getLong(index);
}
return getLong(index, convertKey(key));
return getLong(index, JSType.toString(key));
}
@Override
@ -2471,7 +2482,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getLong(key);
}
return getLong(key, convertKey(key));
return getLong(key, JSType.toString(key));
}
private double getDouble(final int index, final String key) {
@ -2513,7 +2524,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getDouble(index);
}
return getDouble(index, convertKey(key));
return getDouble(index, JSType.toString(key));
}
@Override
@ -2525,7 +2536,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getDouble(index);
}
return getDouble(index, convertKey(key));
return getDouble(index, JSType.toString(key));
}
@Override
@ -2537,7 +2548,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getDouble(index);
}
return getDouble(index, convertKey(key));
return getDouble(index, JSType.toString(key));
}
@Override
@ -2548,7 +2559,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getDouble(key);
}
return getDouble(key, convertKey(key));
return getDouble(key, JSType.toString(key));
}
private Object get(final int index, final String key) {
@ -2590,7 +2601,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getObject(index);
}
return get(index, convertKey(key));
return get(index, JSType.toString(key));
}
@Override
@ -2602,7 +2613,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getObject(index);
}
return get(index, convertKey(key));
return get(index, JSType.toString(key));
}
@Override
@ -2614,7 +2625,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getObject(index);
}
return get(index, convertKey(key));
return get(index, JSType.toString(key));
}
@Override
@ -2625,7 +2636,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return array.getObject(key);
}
return get(key, convertKey(key));
return get(key, JSType.toString(key));
}
/**
@ -2640,7 +2651,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final long longIndex = index & JSType.MAX_UINT;
if (!getArray().has(index)) {
final String key = convertKey(longIndex);
final String key = JSType.toString(longIndex);
final FindProperty find = findProperty(key, true);
if (find != null) {
@ -2786,7 +2797,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
final String propName = convertKey(key);
final String propName = JSType.toString(key);
final FindProperty find = findProperty(propName, true);
setObject(find, strict, propName, value);
@ -3008,7 +3019,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(convertKey(key), true);
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
}
@ -3025,7 +3036,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(convertKey(key), true);
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
}
@ -3042,7 +3053,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(convertKey(key), true);
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
}
@ -3059,7 +3070,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(convertKey(key), true);
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
}
@ -3072,7 +3083,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return true;
}
final FindProperty find = findProperty(convertKey(key), false);
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
}
@ -3085,7 +3096,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return true;
}
final FindProperty find = findProperty(convertKey(key), false);
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
}
@ -3098,7 +3109,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return true;
}
final FindProperty find = findProperty(convertKey(key), false);
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
}
@ -3111,7 +3122,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return true;
}
final FindProperty find = findProperty(convertKey(key), false);
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
}
@ -3181,7 +3192,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private boolean deleteObject(final Object key, final boolean strict) {
final String propName = convertKey(key);
final String propName = JSType.toString(key);
final FindProperty find = findProperty(propName, false);
if (find == null) {

View file

@ -37,7 +37,9 @@ import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import jdk.internal.dynalink.beans.StaticClass;
@ -221,49 +223,71 @@ public final class ScriptRuntime {
}
/**
* Used to determine property iterator used in for in.
* @param obj Object to iterate on.
* @return Iterator.
* Returns an iterator over property identifiers used in the {@code for...in} statement. Note that the ECMAScript
* 5.1 specification, chapter 12.6.4. uses the terminology "property names", which seems to imply that the property
* identifiers are expected to be strings, but this is not actually spelled out anywhere, and Nashorn will in some
* cases deviate from this. Namely, we guarantee to always return an iterator over {@link String} values for any
* built-in JavaScript object. We will however return an iterator over {@link Integer} objects for native Java
* arrays and {@link List} objects, as well as arbitrary objects representing keys of a {@link Map}. Therefore, the
* expression {@code typeof i} within a {@code for(i in obj)} statement can return something other than
* {@code string} when iterating over native Java arrays, {@code List}, and {@code Map} objects.
* @param obj object to iterate on.
* @return iterator over the object's property names.
*/
public static Iterator<String> toPropertyIterator(final Object obj) {
public static Iterator<?> toPropertyIterator(final Object obj) {
if (obj instanceof ScriptObject) {
return ((ScriptObject)obj).propertyIterator();
}
if (obj != null && obj.getClass().isArray()) {
final int length = Array.getLength(obj);
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < length;
}
@Override
public String next() {
return "" + index++; //TODO numeric property iterator?
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
return new RangeIterator(Array.getLength(obj));
}
if (obj instanceof ScriptObjectMirror) {
return ((ScriptObjectMirror)obj).keySet().iterator();
}
if (obj instanceof List) {
return new RangeIterator(((List<?>)obj).size());
}
if (obj instanceof Map) {
return ((Map<?,?>)obj).keySet().iterator();
}
return Collections.emptyIterator();
}
private static final class RangeIterator implements Iterator<Integer> {
private final int length;
private int index;
RangeIterator(int length) {
this.length = length;
}
@Override
public boolean hasNext() {
return index < length;
}
@Override
public Integer next() {
return index++;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* Used to determine property value iterator used in for each in.
* @param obj Object to iterate on.
* @return Iterator.
* Returns an iterator over property values used in the {@code for each...in} statement. Aside from built-in JS
* objects, it also operates on Java arrays, any {@link Iterable}, as well as on {@link Map} objects, iterating over
* map values.
* @param obj object to iterate on.
* @return iterator over the object's property values.
*/
public static Iterator<?> toValueIterator(final Object obj) {
if (obj instanceof ScriptObject) {
@ -301,6 +325,10 @@ public final class ScriptRuntime {
return ((ScriptObjectMirror)obj).values().iterator();
}
if (obj instanceof Map) {
return ((Map<?,?>)obj).values().iterator();
}
if (obj instanceof Iterable) {
return ((Iterable<?>)obj).iterator();
}

View file

@ -73,7 +73,7 @@ public final class WithObject extends ScriptObject implements Scope {
public boolean delete(final Object key, final boolean strict) {
if (expression instanceof ScriptObject) {
final ScriptObject self = (ScriptObject)expression;
final String propName = ScriptObject.convertKey(key);
final String propName = JSType.toString(key);
final FindProperty find = self.findProperty(propName, true);

View file

@ -36,6 +36,7 @@ import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.DynamicLinker;
import jdk.internal.dynalink.DynamicLinkerFactory;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
@ -61,7 +62,7 @@ public final class Bootstrap {
static {
final DynamicLinkerFactory factory = new DynamicLinkerFactory();
factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(),
new BoundDynamicMethodLinker(), new JSObjectLinker(), new ReflectionCheckLinker());
new BoundDynamicMethodLinker(), new JavaSuperAdapterLinker(), new JSObjectLinker(), new ReflectionCheckLinker());
factory.setFallbackLinkers(new BeansLinker(), new NashornBottomLinker());
factory.setSyncOnRelink(true);
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1);
@ -88,7 +89,8 @@ public final class Bootstrap {
return obj instanceof ScriptFunction ||
((obj instanceof ScriptObjectMirror) && ((ScriptObjectMirror)obj).isFunction()) ||
isDynamicMethod(obj) ||
isFunctionalInterfaceObject(obj);
isFunctionalInterfaceObject(obj) ||
obj instanceof StaticClass;
}
/**
@ -261,6 +263,16 @@ public final class Bootstrap {
return new BoundDynamicMethod(dynamicMethod, boundThis);
}
/**
* Creates a super-adapter for an adapter, that is, an adapter to the adapter that allows invocation of superclass
* methods on it.
* @param adapter the original adapter
* @return a new adapter that can be used to invoke super methods on the original adapter.
*/
public static Object createSuperAdapter(final Object adapter) {
return new JavaSuperAdapter(adapter);
}
/**
* If the given class is a reflection-specific class (anything in {@code java.lang.reflect} and
* {@code java.lang.invoke} package, as well a {@link Class} and any subclass of {@link ClassLoader}) and there is

View file

@ -67,11 +67,12 @@ final class BoundDynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
// BeansLinker.
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
final MethodType type = descriptor.getMethodType();
final Class<?> dynamicMethodClass = dynamicMethod.getClass();
final CallSiteDescriptor newDescriptor = descriptor.changeMethodType(
type.changeParameterType(0, dynamicMethod.getClass()).changeParameterType(1, boundThis.getClass()));
type.changeParameterType(0, dynamicMethodClass).changeParameterType(1, boundThis.getClass()));
// Delegate to BeansLinker
final GuardedInvocation inv = BeansLinker.getLinkerForClass(dynamicMethod.getClass()).getGuardedInvocation(
final GuardedInvocation inv = BeansLinker.getLinkerForClass(dynamicMethodClass).getGuardedInvocation(
linkRequest.replaceArguments(newDescriptor, args), linkerServices);
if(inv == null) {
return null;

View file

@ -173,6 +173,9 @@ final class JavaAdapterBytecodeGenerator {
private static final String CLASS_INIT = "<clinit>";
private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal";
// Method name prefix for invoking super-methods
static final String SUPER_PREFIX = "super$";
/**
* Collection of methods we never override: Object.clone(), Object.finalize().
*/
@ -240,6 +243,7 @@ final class JavaAdapterBytecodeGenerator {
}
generateConstructors();
generateMethods();
generateSuperMethods();
// }
cw.visitEnd();
}
@ -507,6 +511,10 @@ final class JavaAdapterBytecodeGenerator {
private static void endInitMethod(final InstructionAdapter mv) {
mv.visitInsn(RETURN);
endMethod(mv);
}
private static void endMethod(final InstructionAdapter mv) {
mv.visitMaxs(0, 0);
mv.visitEnd();
}
@ -603,13 +611,8 @@ final class JavaAdapterBytecodeGenerator {
*/
private void generateMethod(final MethodInfo mi) {
final Method method = mi.method;
final int mod = method.getModifiers();
final int access = ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
final Class<?>[] exceptions = method.getExceptionTypes();
final String[] exceptionNames = new String[exceptions.length];
for (int i = 0; i < exceptions.length; ++i) {
exceptionNames[i] = Type.getInternalName(exceptions[i]);
}
final String[] exceptionNames = getExceptionNames(exceptions);
final MethodType type = mi.type;
final String methodDesc = type.toMethodDescriptorString();
final String name = mi.getName();
@ -617,14 +620,8 @@ final class JavaAdapterBytecodeGenerator {
final Type asmType = Type.getMethodType(methodDesc);
final Type[] asmArgTypes = asmType.getArgumentTypes();
// Determine the first index for a local variable
int nextLocalVar = 1; // this
for(final Type t: asmArgTypes) {
nextLocalVar += t.getSize();
}
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(access, name, methodDesc, null,
exceptionNames));
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name,
methodDesc, null, exceptionNames));
mv.visitCode();
final Label instanceHandleDefined = new Label();
@ -646,7 +643,7 @@ final class JavaAdapterBytecodeGenerator {
}
// No handle is available, fall back to default behavior
if(Modifier.isAbstract(mod)) {
if(Modifier.isAbstract(method.getModifiers())) {
// If the super method is abstract, throw an exception
mv.anew(UNSUPPORTED_OPERATION_TYPE);
mv.dup();
@ -654,14 +651,7 @@ final class JavaAdapterBytecodeGenerator {
mv.athrow();
} else {
// If the super method is not abstract, delegate to it.
mv.visitVarInsn(ALOAD, 0);
int nextParam = 1;
for(final Type t: asmArgTypes) {
mv.load(nextParam, t);
nextParam += t.getSize();
}
mv.invokespecial(superClassName, name, methodDesc);
mv.areturn(asmReturnType);
emitSuperCall(mv, name, methodDesc);
}
final Label setupGlobal = new Label();
@ -685,6 +675,12 @@ final class JavaAdapterBytecodeGenerator {
// stack: [creatingGlobal, someHandle]
mv.visitLabel(setupGlobal);
// Determine the first index for a local variable
int nextLocalVar = 1; // "this" is at 0
for(final Type t: asmArgTypes) {
nextLocalVar += t.getSize();
}
// Set our local variable indices
final int currentGlobalVar = nextLocalVar++;
final int globalsDifferVar = nextLocalVar++;
@ -775,8 +771,7 @@ final class JavaAdapterBytecodeGenerator {
}
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
}
mv.visitMaxs(0, 0);
mv.visitEnd();
endMethod(mv);
}
/**
@ -817,6 +812,53 @@ final class JavaAdapterBytecodeGenerator {
return false;
}
private void generateSuperMethods() {
for(final MethodInfo mi: methodInfos) {
if(!Modifier.isAbstract(mi.method.getModifiers())) {
generateSuperMethod(mi);
}
}
}
private void generateSuperMethod(MethodInfo mi) {
final Method method = mi.method;
final String methodDesc = mi.type.toMethodDescriptorString();
final String name = mi.getName();
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method),
SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes())));
mv.visitCode();
emitSuperCall(mv, name, methodDesc);
endMethod(mv);
}
private void emitSuperCall(final InstructionAdapter mv, final String name, final String methodDesc) {
mv.visitVarInsn(ALOAD, 0);
int nextParam = 1;
final Type methodType = Type.getMethodType(methodDesc);
for(final Type t: methodType.getArgumentTypes()) {
mv.load(nextParam, t);
nextParam += t.getSize();
}
mv.invokespecial(superClassName, name, methodDesc);
mv.areturn(methodType.getReturnType());
}
private static String[] getExceptionNames(final Class<?>[] exceptions) {
final String[] exceptionNames = new String[exceptions.length];
for (int i = 0; i < exceptions.length; ++i) {
exceptionNames[i] = Type.getInternalName(exceptions[i]);
}
return exceptionNames;
}
private static int getAccessModifiers(final Method method) {
return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
}
/**
* Gathers methods that can be implemented or overridden from the specified type into this factory's
* {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from

View file

@ -34,7 +34,6 @@ import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.SecureClassLoader;
import jdk.internal.dynalink.beans.StaticClass;
/**

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime.linker;
/**
* Represents a an adapter for invoking superclass methods on an adapter instance generated by
* {@link JavaAdapterBytecodeGenerator}. Note that objects of this class are just wrappers around the adapter instances,
* without any behavior. All the behavior is defined in the {@code JavaSuperAdapterLinker}.
*/
class JavaSuperAdapter {
private final Object adapter;
JavaSuperAdapter(final Object adapter) {
adapter.getClass(); // NPE check
this.adapter = adapter;
}
public Object getAdapter() {
return adapter;
}
}

View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.lookup.Lookup.EMPTY_GETTER;
import static jdk.nashorn.internal.runtime.linker.JavaAdapterBytecodeGenerator.SUPER_PREFIX;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.internal.dynalink.support.Lookup;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* A linker for instances of {@link JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the
* bean linker for the adapter class and prepending {@code super$} to method names.
*
*/
final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
private static final String GET_METHOD = "getMethod";
private static final String DYN_GET_METHOD = "dyn:" + GET_METHOD;
private static final String DYN_GET_METHOD_FIXED = DYN_GET_METHOD + ":" + SUPER_PREFIX;
private static final MethodHandle ADD_PREFIX_TO_METHOD_NAME;
private static final MethodHandle BIND_DYNAMIC_METHOD;
private static final MethodHandle GET_ADAPTER;
private static final MethodHandle IS_ADAPTER_OF_CLASS;
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
ADD_PREFIX_TO_METHOD_NAME = lookup.findOwnStatic("addPrefixToMethodName", Object.class, Object.class);
BIND_DYNAMIC_METHOD = lookup.findOwnStatic("bindDynamicMethod", Object.class, Object.class, Object.class);
GET_ADAPTER = lookup.findVirtual(JavaSuperAdapter.class, "getAdapter", MethodType.methodType(Object.class));
IS_ADAPTER_OF_CLASS = lookup.findOwnStatic("isAdapterOfClass", boolean.class, Class.class, Object.class);
}
@Override
public boolean canLinkType(final Class<?> type) {
return type == JavaSuperAdapter.class;
}
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
throws Exception {
final Object objSuperAdapter = linkRequest.getReceiver();
if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
return null;
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if(!CallSiteDescriptorFactory.tokenizeOperators(descriptor).contains(GET_METHOD)) {
// We only handle getMethod
return null;
}
final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
// Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
final Object[] args = linkRequest.getArguments();
args[0] = adapter;
// Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
final MethodType type = descriptor.getMethodType();
final Class<?> adapterClass = adapter.getClass();
final boolean hasFixedName = descriptor.getNameTokenCount() > 2;
final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD;
final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(descriptor.getLookup(), opName,
type.changeParameterType(0, adapterClass), 0);
// Delegate to BeansLinker
final GuardedInvocation guardedInv = BeansLinker.getLinkerForClass(adapterClass).getGuardedInvocation(
linkRequest.replaceArguments(newDescriptor, args), linkerServices);
final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
if(guardedInv == null) {
// Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned
// null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and
// wait().
return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1,
type.parameterCount())), guard).asType(descriptor);
}
final MethodHandle invocation = guardedInv.getInvocation();
final MethodType invType = invocation.type();
// For invocation typed R(T0, ...) create a dynamic method binder of type R(R, T0)
final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(invType.returnType(),
invType.returnType(), invType.parameterType(0)));
// For invocation typed R(T0, T1, ...) create a dynamic method binder of type R(R, T0, T1, ...)
final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
invType.parameterList().subList(1, invType.parameterCount()));
// Finally, fold the invocation into the binder to produce a method handle that will bind every returned
// DynamicMethod object from dyn:getMethod calls to the actual receiver
// R(R(T0, T1, ...), T0, T1, ...)
final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
final MethodHandle adaptedInvocation;
if(hasFixedName) {
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
} else {
// Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod".
final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
}
return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor);
}
/**
* Adapts the type of a method handle used as a filter in a position from a source method type to a target method type.
* @param filter the filter method handle
* @param pos the position in the argument list that it's filtering
* @param targetType the target method type for filtering
* @param sourceType the source method type for filtering
* @return a type adapted filter
*/
private static MethodHandle asFilterType(final MethodHandle filter, int pos, MethodType targetType, MethodType sourceType) {
return filter.asType(MethodType.methodType(targetType.parameterType(pos), sourceType.parameterType(pos)));
}
@SuppressWarnings("unused")
private static Object addPrefixToMethodName(final Object name) {
return SUPER_PREFIX.concat(String.valueOf(name));
}
/**
* Used to transform the return value of getMethod; transform a {@code DynamicMethod} into a
* {@code BoundDynamicMethod} while also accounting for the possibility of a non-existent method.
* @param dynamicMethod the dynamic method to bind
* @param boundThis the adapter underlying a super adapter, to which the dynamic method is bound.
* @return a dynamic method bound to the adapter instance.
*/
@SuppressWarnings("unused")
private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindDynamicMethod(dynamicMethod, boundThis);
}
/**
* Used as the guard of linkages, as the receiver is not guaranteed to be a JavaSuperAdapter.
* @param clazz the class the receiver's adapter is tested against.
* @param obj receiver
* @return true if the receiver is a super adapter, and its underlying adapter is of the specified class
*/
@SuppressWarnings("unused")
private static boolean isAdapterOfClass(Class<?> clazz, Object obj) {
return obj instanceof JavaSuperAdapter && clazz == (((JavaSuperAdapter)obj).getAdapter()).getClass();
}
}

View file

@ -60,7 +60,7 @@ public abstract class RegExp {
* @param flags the flags string
*/
protected RegExp(final String source, final String flags) {
this.source = source;
this.source = source.length() == 0 ? "(?:)" : source;
for (int i = 0; i < flags.length(); i++) {
final char ch = flags.charAt(i);
switch (ch) {

View file

@ -94,6 +94,8 @@ type.error.cant.delete.property=Cannot delete property "{0}" of {1}
type.error.cant.redefine.property=Cannot redefine property "{0}" of {1}
type.error.property.not.writable="{0}" is not a writable property of {1}
type.error.object.non.extensible=Cannot add new property "{0}" to non-extensible {1}
type.error.__proto__.set.non.extensible=Cannot set __proto__ of non-extensible {0}
type.error.circular.__proto__.set=Cannot create__proto__ cycle for {0}
# miscellaneous
type.error.regex.cant.supply.flags=Cannot supply flags when constructing one RegExp from another

View file

@ -157,6 +157,14 @@ nashorn.option.fx = { \
default=false \
}
nashorn.option.global.per.engine = { \
name="--global-per-engine", \
desc="Use single Global instance per script engine instance.", \
is_undocumented=true, \
type=Boolean, \
default=false \
}
nashorn.option.log = { \
name="--log", \
is_undocumented=true, \
@ -216,15 +224,6 @@ nashorn.option.no.typed.arrays = { \
default=false \
}
nashorn.option.package = { \
name="--package", \
is_undocumented=true, \
desc="Package to which generated .class files are added.", \
params="<package>", \
type=String, \
default="" \
}
nashorn.option.parse.only = { \
name="--parse-only", \
is_undocumented=true, \

View file

@ -144,7 +144,7 @@ Object.defineProperty(Object.prototype, "__proto__", {
return Object.getPrototypeOf(this);
},
set: function(x) {
throw new TypeError("__proto__ set not supported");
Object.setPrototypeOf(this, x);
}
});

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8022903: Enhance for-in and for-each for Lists and Maps
*
* @test
* @run
*/
var colors = new java.util.ArrayList()
colors.add("red")
colors.add("purple")
colors.add("pink")
for(var index in colors) {
print("colors[" + index + "]=" + colors[index])
}
for each(var color in colors) {
print(color)
}
var capitals = new java.util.LinkedHashMap()
capitals.Sweden = "Stockholm"
capitals.Hungary = "Budapet"
capitals.Croatia = "Zagreb"
for(var key in capitals) {
print("capital of " + key + " is " + capitals[key])
}
for each(var capital in capitals) {
print(capital)
}

View file

@ -0,0 +1,12 @@
colors[0]=red
colors[1]=purple
colors[2]=pink
red
purple
pink
capital of Sweden is Stockholm
capital of Hungary is Budapet
capital of Croatia is Zagreb
Stockholm
Budapet
Zagreb

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023368: Instance __proto__ property should exist and be writable.
*
* @test
* @run
*/
load("nashorn:mozilla_compat.js");
// function to force same callsites
function check(obj) {
print(obj.func());
print(obj.x);
print(obj.toString());
}
function Func() {
}
Func.prototype.func = function() {
return "Func.prototype.func";
}
Func.prototype.x = "hello";
var obj = new Func();
var obj2 = Object.create(obj);
// check direct and indirect __proto__ change
check(obj);
check(obj2);
obj.__proto__ = {
func: function() {
return "obj.__proto__.func @ " + __LINE__;
},
x: 344
};
check(obj);
check(obj2);
// check indirect (1 and 2 levels) __proto__ function change
obj.__proto__.__proto__ = {
toString: function() {
return "new object.toString";
}
};
check(obj);
check(obj2);

View file

@ -0,0 +1,18 @@
Func.prototype.func
hello
[object Object]
Func.prototype.func
hello
[object Object]
obj.__proto__.func @ 57
344
[object Object]
obj.__proto__.func @ 57
344
[object Object]
obj.__proto__.func @ 57
344
new object.toString
obj.__proto__.func @ 57
344
new object.toString

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023368: Instance __proto__ property should exist and be writable.
*
* @test
* @run
*/
// check Object.setPrototypeOf extension rather than using __proto__
// function to force same callsites
function check(obj) {
print(obj.func());
print(obj.x);
print(obj.toString());
}
function Func() {
}
Func.prototype.func = function() {
return "Func.prototype.func";
}
Func.prototype.x = "hello";
var obj = new Func();
var obj2 = Object.create(obj);
// check direct and indirect __proto__ change
check(obj);
check(obj2);
Object.setPrototypeOf(obj, {
func: function() {
return "obj.__proto__.func @ " + __LINE__;
},
x: 344
});
check(obj);
check(obj2);
// check indirect (1 and 2 levels) __proto__ function change
Object.setPrototypeOf(Object.getPrototypeOf(obj), {
toString: function() {
return "new object.toString";
}
});
check(obj);
check(obj2);

View file

@ -0,0 +1,18 @@
Func.prototype.func
hello
[object Object]
Func.prototype.func
hello
[object Object]
obj.__proto__.func @ 57
344
[object Object]
obj.__proto__.func @ 57
344
[object Object]
obj.__proto__.func @ 57
344
new object.toString
obj.__proto__.func @ 57
344
new object.toString

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023373: allow super invocation for adapters
*
* @test
* @run
*/
var CharArray = Java.type("char[]")
var jString = Java.type("java.lang.String")
var Character = Java.type("java.lang.Character")
function capitalize(s) {
if(s instanceof CharArray) {
return new jString(s).toUpperCase()
}
if(s instanceof jString) {
return s.toUpperCase()
}
return Character.toUpperCase(s) // must be int
}
var sw = new (Java.type("java.io.StringWriter"))
var FilterWriterAdapter = Java.extend(Java.type("java.io.FilterWriter"))
var cw = new FilterWriterAdapter(sw) {
write: function(s, off, len) {
s = capitalize(s)
// Must handle overloads by arity
if(off === undefined) {
cw.super$write(s, 0, s.length())
} else if (typeof s === "string") {
cw.super$write(s, off, len)
}
}
}
cw.write("abcd")
cw.write("e".charAt(0))
cw.write("fgh".toCharArray())
cw.write("**ijk**", 2, 3)
cw.write("***lmno**".toCharArray(), 3, 4)
cw.flush()
print(sw)
// Can invoke super for Object methods
print("cw has super hashCode(): " + (typeof cw.super$hashCode === "function"))
print("cw has super equals(): " + (typeof cw.super$equals === "function"))
// Can't invoke super for final methods
print("cw has no super getClass(): " + (typeof cw.super$getClass === "undefined"))
print("cw has no super wait(): " + (typeof cw.super$wait === "undefined"))
var r = new (Java.type("java.lang.Runnable"))(function() {})
// Can't invoke super for abstract methods
print("r has no super run(): " + (typeof r.super$run === "undefined"))
// Interfaces can also invoke super Object methods
print("r has super hashCode(): " + (typeof r.super$hashCode === "function"))
print("r has super equals(): " + (typeof r.super$equals === "function"))
// But still can't invoke final methods
print("r has no super getClass(): " + (typeof r.super$getClass === "undefined"))
print("r has no super wait(): " + (typeof r.super$wait === "undefined"))

View file

@ -0,0 +1,10 @@
ABCDEFGHIJKLMNO
cw has super hashCode(): true
cw has super equals(): true
cw has no super getClass(): true
cw has no super wait(): true
r has no super run(): true
r has super hashCode(): true
r has super equals(): true
r has no super getClass(): true
r has no super wait(): true

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023531: new RegExp('').toString() should return '/(?:)/'
*
* @test
* @run
*/
if (new RegExp("").toString() !== "/(?:)/") {
throw new Error();
} else if (!(new RegExp("").test(""))) {
throw new Error();
}
if (new RegExp("", "g").toString() !== "/(?:)/g") {
throw new Error();
} else if (!(new RegExp("", "g").test(""))) {
throw new Error();
}
if (new RegExp("", "i").toString() !== "/(?:)/i") {
throw new Error();
} else if (!(new RegExp("", "i").test(""))) {
throw new Error();
}
if (new RegExp("", "m").toString() !== "/(?:)/m") {
throw new Error();
} else if (!(new RegExp("", "m").test(""))) {
throw new Error();
}
if (RegExp("").toString() !== "/(?:)/") {
throw new Error();
} else if (!RegExp("").test("")) {
throw new Error();
}
if (RegExp("", "g").toString() !== "/(?:)/g") {
throw new Error();
} else if (!RegExp("", "g").test("")) {
throw new Error();
}
if (RegExp("", "i").toString() !== "/(?:)/i") {
throw new Error();
} else if (!RegExp("", "i").test("")) {
throw new Error();
}
if (RegExp("", "m").toString() !== "/(?:)/m") {
throw new Error();
} else if (!RegExp("", "m").test("")) {
throw new Error();
}
var re = /abc/;
re.compile("");
if (re.toString() !== "/(?:)/") {
throw new Error();
} else if (!re.test("")) {
throw new Error();
}
re.compile("", "g");
if (re.toString() !== "/(?:)/g") {
throw new Error();
} else if (!re.test("")) {
throw new Error();
}
re.compile("", "i");
if (re.toString() !== "/(?:)/i") {
throw new Error();
} else if (!re.test("")) {
throw new Error();
}
re.compile("", "m");
if (re.toString() !== "/(?:)/m") {
throw new Error();
} else if (!re.test("")) {
throw new Error();
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023551: Mirror functions can not be invoked using invokeMethod, invokeFunction
*
* @test
* @run
*/
var m = new javax.script.ScriptEngineManager();
var e = m.getEngineByName("nashorn");
function func(x) {
print("func: " + x);
}
e.put("func", func);
e.invokeFunction("func", "hello");
var obj = e.eval("({ foo: func })");
e.invokeMethod(obj, "foo", "world");

View file

@ -0,0 +1,2 @@
func: hello
func: world

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023630: Implement Java.super() as the preferred way to call super methods
*
* @test
* @run
*/
var CharArray = Java.type("char[]")
var jString = Java.type("java.lang.String")
var Character = Java.type("java.lang.Character")
function capitalize(s) {
if(s instanceof CharArray) {
return new jString(s).toUpperCase()
}
if(s instanceof jString) {
return s.toUpperCase()
}
return Character.toUpperCase(s) // must be int
}
var sw = new (Java.type("java.io.StringWriter"))
var FilterWriterAdapter = Java.extend(Java.type("java.io.FilterWriter"))
var cw = new FilterWriterAdapter(sw) {
write: function(s, off, len) {
s = capitalize(s)
// Must handle overloads by arity
if(off === undefined) {
cw_super.write(s, 0, s.length())
} else if (typeof s === "string") {
cw_super.write(s, off, len)
}
}
}
var cw_super = Java.super(cw)
cw.write("abcd")
cw.write("e".charAt(0))
cw.write("fgh".toCharArray())
cw.write("**ijk**", 2, 3)
cw.write("***lmno**".toCharArray(), 3, 4)
cw.flush()
print(sw)
// Can invoke super for Object methods
print("cw_super has hashCode(): " + (typeof cw_super.hashCode === "function"))
print("cw_super has super equals(): " + (typeof cw_super.equals === "function"))
// Can't invoke super for final methods
print("cw_super has no getClass(): " + (typeof cw_super.getClass === "undefined"))
print("cw_super has no wait(): " + (typeof cw_super.wait === "undefined"))
var r = new (Java.type("java.lang.Runnable"))(function() {})
var r_super = Java.super(r)
// Can't invoke super for abstract methods
print("r_super has no run(): " + (typeof r_super.run === "undefined"))
// Interfaces can also invoke super Object methods
print("r_super has hashCode(): " + (typeof r_super.hashCode === "function"))
print("r_super has equals(): " + (typeof r_super.equals === "function"))
// But still can't invoke final methods
print("r_super has no getClass(): " + (typeof r_super.getClass === "undefined"))
print("r_super has no wait(): " + (typeof r_super.wait === "undefined"))
var name = "write"
print("cw_super can access write through [] getter: " + (typeof cw_super[name] === "function"))
var name = "hashCode"
print("cw_super can access hashCode through [] getter: " + (typeof cw_super[name] === "function"))
var name = "getClass"
print("cw_super can not access getClass through [] getter: " + (typeof cw_super[name] === "undefined"))

View file

@ -0,0 +1,13 @@
ABCDEFGHIJKLMNO
cw_super has hashCode(): true
cw_super has super equals(): true
cw_super has no getClass(): true
cw_super has no wait(): true
r_super has no run(): true
r_super has hashCode(): true
r_super has equals(): true
r_super has no getClass(): true
r_super has no wait(): true
cw_super can access write through [] getter: true
cw_super can access hashCode through [] getter: true
cw_super can not access getClass through [] getter: true

View file

@ -35,7 +35,10 @@ if (typeof (5).x !== 'number') {
fail("typeof(5).x is not 'number'");
}
if (typeof (java.lang.System.out) != 'object') {
// It is function because PrintStream implements Closeable, which is
// marked with @FunctionalInterface. Yes, this means calling a stream
// like "stream()" closes it.
if (typeof (java.lang.System.out) != 'function') {
fail("typeof java.lang.System.out is not 'object'");
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023368: Instance __proto__ property should exist and be writable.
*
* @test
* @run
*/
// check that we cannot create __proto__ cycle
load("nashorn:mozilla_compat.js");
var obj = {};
var obj2 = Object.create(obj);
// attempt to create __proto__ cycle
try {
obj.__proto__ = obj2;
fail("Should have thrown TypeError");
} catch (e) {
if (! (e instanceof TypeError)) {
fail("Expected TypeError, got " + e);
}
print(e);
}

View file

@ -0,0 +1 @@
TypeError: Cannot create__proto__ cycle for [object Object]

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023368: Instance __proto__ property should exist and be writable.
*
* @test
* @run
*/
// check that Object.setPrototypeOf works for mirror objects as well.
var global = loadWithNewGlobal({
name: "test",
script: "var obj = {}; this"
});
var proto = global.eval("({ foo: 323 })");
Object.setPrototypeOf(global.obj, proto);
function func(obj) {
// check proto inherited value
print("obj.foo = " + obj.foo);
}
func(global.obj);
var newProto = global.eval("({ foo: 'hello' })");
Object.setPrototypeOf(global.obj, newProto);
func(global.obj);

View file

@ -0,0 +1,2 @@
obj.foo = 323
obj.foo = hello

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023368: Instance __proto__ property should exist and be writable.
*
* @test
* @run
*/
load("nashorn:mozilla_compat.js")
// check that we cannot assign to __proto__ of a non-extensible object
try {
var obj = {}
Object.preventExtensions(obj);
obj.__proto__ = { };
fail("Should have thrown TypeError");
} catch (e) {
if (! (e instanceof TypeError)) {
fail("Expected TypeError, got " + e);
}
print(e);
}

View file

@ -0,0 +1 @@
TypeError: Cannot set __proto__ of non-extensible [object Object]

View file

@ -0,0 +1,525 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting;
import java.util.Objects;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import org.testng.Assert;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import org.testng.annotations.Test;
/**
* Tests for javax.script.Invocable implementation of nashorn.
*/
public class InvocableTest {
private void log(String msg) {
org.testng.Reporter.log(msg, true);
}
@Test
public void invokeMethodTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var Example = function() { this.hello = function() { return 'Hello World!'; };}; myExample = new Example();");
final Object obj = e.get("myExample");
final Object res = ((Invocable) e).invokeMethod(obj, "hello");
assertEquals(res, "Hello World!");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that we can call invokeMethod on an object that we got by
* evaluating script with different Context set.
*/
public void invokeMethodDifferentContextTest() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
Object obj = e.eval("({ hello: function() { return 'Hello World!'; } })");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
e.setContext(ctxt);
// invoke 'func' on obj - but with current script context changed
final Object res = ((Invocable) e).invokeMethod(obj, "hello");
assertEquals(res, "Hello World!");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that invokeMethod throws NPE on null method name.
*/
public void invokeMethodNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
final Object res = ((Invocable) e).invokeMethod(obj, null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (!(exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeMethod throws NoSuchMethodException on missing method.
*/
public void invokeMethodMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
final Object res = ((Invocable) e).invokeMethod(obj, "nonExistentMethod");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (!(exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on non-script object 'thiz' results in
* IllegalArgumentException.
*/
public void invokeMethodNonScriptObjectThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).invokeMethod(new Object(), "toString");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on null 'thiz' results in
* IllegalArgumentException.
*/
public void invokeMethodNullThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).invokeMethod(null, "toString");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on mirror created by another engine results in
* IllegalArgumentException.
*/
public void invokeMethodMixEnginesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine engine1 = m.getEngineByName("nashorn");
final ScriptEngine engine2 = m.getEngineByName("nashorn");
try {
Object obj = engine1.eval("({ run: function() {} })");
// pass object from engine1 to engine2 as 'thiz' for invokeMethod
((Invocable) engine2).invokeMethod(obj, "run");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void getInterfaceTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Invocable inv = (Invocable) e;
// try to get interface from global functions
try {
e.eval("function run() { print('run'); };");
final Runnable runnable = inv.getInterface(Runnable.class);
runnable.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
// try interface on specific script object
try {
e.eval("var obj = { run: function() { print('run from obj'); } };");
Object obj = e.get("obj");
final Runnable runnable = inv.getInterface(obj, Runnable.class);
runnable.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
public interface Foo {
public void bar();
}
public interface Foo2 extends Foo {
public void bar2();
}
@Test
public void getInterfaceMissingTest() {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
// don't define any function.
try {
engine.eval("");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
Runnable runnable = ((Invocable) engine).getInterface(Runnable.class);
if (runnable != null) {
fail("runnable is not null!");
}
// now define "run"
try {
engine.eval("function run() { print('this is run function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
runnable = ((Invocable) engine).getInterface(Runnable.class);
// should not return null now!
runnable.run();
// define only one method of "Foo2"
try {
engine.eval("function bar() { print('bar function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
Foo2 foo2 = ((Invocable) engine).getInterface(Foo2.class);
if (foo2 != null) {
throw new RuntimeException("foo2 is not null!");
}
// now define other method of "Foo2"
try {
engine.eval("function bar2() { print('bar2 function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
foo2 = ((Invocable) engine).getInterface(Foo2.class);
foo2.bar();
foo2.bar2();
}
@Test
/**
* Try passing non-interface Class object for interface implementation.
*/
public void getNonInterfaceGetInterfaceTest() {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
try {
log(Objects.toString(((Invocable) engine).getInterface(Object.class)));
fail("Should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
fail("IllegalArgumentException expected, got " + exp);
}
}
}
@Test
/**
* Check that we can get interface out of a script object even after
* switching to use different ScriptContext.
*/
public void getInterfaceDifferentContext() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
Object obj = e.eval("({ run: function() { } })");
// change script context
ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
e.setContext(ctxt);
Runnable r = ((Invocable) e).getInterface(obj, Runnable.class);
r.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that getInterface on non-script object 'thiz' results in
* IllegalArgumentException.
*/
public void getInterfaceNonScriptObjectThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).getInterface(new Object(), Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that getInterface on null 'thiz' results in
* IllegalArgumentException.
*/
public void getInterfaceNullThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).getInterface(null, Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling getInterface on mirror created by another engine
* results in IllegalArgumentException.
*/
public void getInterfaceMixEnginesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine engine1 = m.getEngineByName("nashorn");
final ScriptEngine engine2 = m.getEngineByName("nashorn");
try {
Object obj = engine1.eval("({ run: function() {} })");
// pass object from engine1 to engine2 as 'thiz' for getInterface
((Invocable) engine2).getInterface(obj, Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* check that null function name results in NPE.
*/
public void invokeFunctionNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object res = ((Invocable) e).invokeFunction(null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (!(exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that attempt to call missing function results in
* NoSuchMethodException.
*/
public void invokeFunctionMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object res = ((Invocable) e).invokeFunction("NonExistentFunc");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (!(exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeFunction calls functions only from current context's
* Bindings.
*/
public void invokeFunctionDifferentContextTest() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
Object obj = e.eval("function hello() { return 'Hello World!'; }");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
// change engine's current context
e.setContext(ctxt);
((Invocable) e).invokeFunction("hello"); // no 'hello' in new context!
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (!(exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void invokeFunctionExceptionTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("function func() { throw new TypeError(); }");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
((Invocable) e).invokeFunction("func");
fail("should have thrown exception");
} catch (final ScriptException se) {
// ECMA TypeError property wrapped as a ScriptException
log("got " + se + " as expected");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
public void invokeMethodExceptionTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var sobj = {}; sobj.foo = function func() { throw new TypeError(); }");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
final Object sobj = e.get("sobj");
((Invocable) e).invokeMethod(sobj, "foo");
fail("should have thrown exception");
} catch (final ScriptException se) {
// ECMA TypeError property wrapped as a ScriptException
log("got " + se + " as expected");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
/**
* Tests whether invocation of a JavaScript method through a variable arity
* Java method will pass the vararg array. Both non-vararg and vararg
* JavaScript methods are tested.
*
* @throws ScriptException
*/
public void variableArityInterfaceTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
e.eval(
"function test1(i, strings) {"
+ " return 'i == ' + i + ', strings instanceof java.lang.String[] == ' + (strings instanceof Java.type('java.lang.String[]')) + ', strings == ' + java.util.Arrays.toString(strings)"
+ "}"
+ "function test2() {"
+ " return 'arguments[0] == ' + arguments[0] + ', arguments[1] instanceof java.lang.String[] == ' + (arguments[1] instanceof Java.type('java.lang.String[]')) + ', arguments[1] == ' + java.util.Arrays.toString(arguments[1])"
+ "}");
final VariableArityTestInterface itf = ((Invocable) e).getInterface(VariableArityTestInterface.class);
Assert.assertEquals(itf.test1(42, "a", "b"), "i == 42, strings instanceof java.lang.String[] == true, strings == [a, b]");
Assert.assertEquals(itf.test2(44, "c", "d", "e"), "arguments[0] == 44, arguments[1] instanceof java.lang.String[] == true, arguments[1] == [c, d, e]");
}
}

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;
import org.testng.Assert;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import org.testng.annotations.Test;
/**
* Tests for jsr223 Bindings "scope" (engine, global scopes)
*/
public class ScopeTest {
@Test
public void createBindingsTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
Bindings b = e.createBindings();
b.put("foo", 42.0);
Object res = null;
try {
res = e.eval("foo == 42.0", b);
} catch (final ScriptException | NullPointerException se) {
se.printStackTrace();
fail(se.getMessage());
}
assertEquals(res, Boolean.TRUE);
}
@Test
public void engineScopeTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE);
// check few ECMA standard built-in global properties
assertNotNull(engineScope.get("Object"));
assertNotNull(engineScope.get("TypeError"));
assertNotNull(engineScope.get("eval"));
// can access via ScriptEngine.get as well
assertNotNull(e.get("Object"));
assertNotNull(e.get("TypeError"));
assertNotNull(e.get("eval"));
// Access by either way should return same object
assertEquals(engineScope.get("Array"), e.get("Array"));
assertEquals(engineScope.get("EvalError"), e.get("EvalError"));
assertEquals(engineScope.get("undefined"), e.get("undefined"));
// try exposing a new variable from scope
engineScope.put("myVar", "foo");
try {
assertEquals(e.eval("myVar"), "foo");
} catch (final ScriptException se) {
se.printStackTrace();
fail(se.getMessage());
}
// update "myVar" in script an check the value from scope
try {
e.eval("myVar = 'nashorn';");
} catch (final ScriptException se) {
se.printStackTrace();
fail(se.getMessage());
}
// now check modified value from scope and engine
assertEquals(engineScope.get("myVar"), "nashorn");
assertEquals(e.get("myVar"), "nashorn");
}
@Test
public void multiGlobalTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Bindings b = e.createBindings();
final ScriptContext newCtxt = new SimpleScriptContext();
newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
try {
Object obj1 = e.eval("Object");
Object obj2 = e.eval("Object", newCtxt);
Assert.assertNotEquals(obj1, obj2);
Assert.assertNotNull(obj1);
Assert.assertNotNull(obj2);
Assert.assertEquals(obj1.toString(), obj2.toString());
e.eval("x = 'hello'");
e.eval("x = 'world'", newCtxt);
Object x1 = e.getContext().getAttribute("x");
Object x2 = newCtxt.getAttribute("x");
Assert.assertNotEquals(x1, x2);
Assert.assertEquals(x1, "hello");
Assert.assertEquals(x2, "world");
x1 = e.eval("x");
x2 = e.eval("x", newCtxt);
Assert.assertNotEquals(x1, x2);
Assert.assertEquals(x1, "hello");
Assert.assertEquals(x2, "world");
final ScriptContext origCtxt = e.getContext();
e.setContext(newCtxt);
e.eval("y = new Object()");
e.eval("y = new Object()", origCtxt);
Object y1 = origCtxt.getAttribute("y");
Object y2 = newCtxt.getAttribute("y");
Assert.assertNotEquals(y1, y2);
Assert.assertNotEquals(e.eval("y"), e.eval("y", origCtxt));
Assert.assertEquals("[object Object]", y1.toString());
Assert.assertEquals("[object Object]", y2.toString());
} catch (final ScriptException se) {
se.printStackTrace();
fail(se.getMessage());
}
}
@Test
public void userEngineScopeBindingsTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
e.eval("function func() {}");
final ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE);
// we are using a new bindings - so it should have 'func' defined
Object value = e.eval("typeof func", newContext);
assertTrue(value.equals("undefined"));
}
@Test
public void userEngineScopeBindingsNoLeakTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE);
e.eval("function foo() {}", newContext);
// in the default context's ENGINE_SCOPE, 'foo' shouldn't exist
assertTrue(e.eval("typeof foo").equals("undefined"));
}
@Test
public void userEngineScopeBindingsRetentionTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE);
e.eval("function foo() {}", newContext);
// definition retained with user's ENGINE_SCOPE Binding
assertTrue(e.eval("typeof foo", newContext).equals("function"));
final Bindings oldBindings = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// but not in another ENGINE_SCOPE binding
newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE);
assertTrue(e.eval("typeof foo", newContext).equals("undefined"));
// restore ENGINE_SCOPE and check again
newContext.setBindings(oldBindings, ScriptContext.ENGINE_SCOPE);
assertTrue(e.eval("typeof foo", newContext).equals("function"));
}
@Test
// check that engine.js definitions are visible in all new global instances
public void checkBuiltinsInNewBindingsTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
// check default global instance has engine.js definitions
final Bindings g = (Bindings) e.eval("this");
Object value = g.get("__noSuchProperty__");
assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction());
value = g.get("print");
assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction());
// check new global instance created has engine.js definitions
Bindings b = e.createBindings();
value = b.get("__noSuchProperty__");
assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction());
value = b.get("print");
assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction());
// put a mapping into GLOBAL_SCOPE
final Bindings globalScope = e.getContext().getBindings(ScriptContext.GLOBAL_SCOPE);
globalScope.put("x", "hello");
// GLOBAL_SCOPE mapping should be visible from default ScriptContext eval
assertTrue(e.eval("x").equals("hello"));
final ScriptContext ctx = new SimpleScriptContext();
ctx.setBindings(globalScope, ScriptContext.GLOBAL_SCOPE);
ctx.setBindings(b, ScriptContext.ENGINE_SCOPE);
// GLOBAL_SCOPE mapping should be visible from non-default ScriptContext eval
assertTrue(e.eval("x", ctx).equals("hello"));
// try some arbitray Bindings for ENGINE_SCOPE
Bindings sb = new SimpleBindings();
ctx.setBindings(sb, ScriptContext.ENGINE_SCOPE);
// GLOBAL_SCOPE mapping should be visible from non-default ScriptContext eval
assertTrue(e.eval("x", ctx).equals("hello"));
// engine.js builtins are still defined even with arbitrary Bindings
assertTrue(e.eval("typeof print", ctx).equals("function"));
assertTrue(e.eval("typeof __noSuchProperty__", ctx).equals("function"));
// ENGINE_SCOPE definition should 'hide' GLOBAL_SCOPE definition
sb.put("x", "newX");
assertTrue(e.eval("x", ctx).equals("newX"));
}
}

View file

@ -26,7 +26,6 @@
package jdk.nashorn.api.scripting;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@ -34,21 +33,13 @@ import static org.testng.Assert.fail;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
@ -238,214 +229,6 @@ public class ScriptEngineTest {
}
}
@Test
public void createBindingsTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
Bindings b = e.createBindings();
b.put("foo", 42.0);
Object res = null;
try {
res = e.eval("foo == 42.0", b);
} catch (final ScriptException | NullPointerException se) {
se.printStackTrace();
fail(se.getMessage());
}
assertEquals(res, Boolean.TRUE);
}
@Test
public void getInterfaceTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Invocable inv = (Invocable)e;
// try to get interface from global functions
try {
e.eval("function run() { print('run'); };");
final Runnable runnable = inv.getInterface(Runnable.class);
runnable.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
// try interface on specific script object
try {
e.eval("var obj = { run: function() { print('run from obj'); } };");
Object obj = e.get("obj");
final Runnable runnable = inv.getInterface(obj, Runnable.class);
runnable.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
public interface Foo {
public void bar();
}
public interface Foo2 extends Foo {
public void bar2();
}
@Test
public void getInterfaceMissingTest() {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
// don't define any function.
try {
engine.eval("");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
Runnable runnable = ((Invocable)engine).getInterface(Runnable.class);
if (runnable != null) {
fail("runnable is not null!");
}
// now define "run"
try {
engine.eval("function run() { print('this is run function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
runnable = ((Invocable)engine).getInterface(Runnable.class);
// should not return null now!
runnable.run();
// define only one method of "Foo2"
try {
engine.eval("function bar() { print('bar function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
Foo2 foo2 = ((Invocable)engine).getInterface(Foo2.class);
if (foo2 != null) {
throw new RuntimeException("foo2 is not null!");
}
// now define other method of "Foo2"
try {
engine.eval("function bar2() { print('bar2 function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
foo2 = ((Invocable)engine).getInterface(Foo2.class);
foo2.bar();
foo2.bar2();
}
@Test
/**
* Try passing non-interface Class object for interface implementation.
*/
public void getNonInterfaceGetInterfaceTest() {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
try {
log(Objects.toString(((Invocable)engine).getInterface(Object.class)));
fail("Should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
fail("IllegalArgumentException expected, got " + exp);
}
}
}
@Test
/**
* Check that we can get interface out of a script object even after
* switching to use different ScriptContext.
*/
public void getInterfaceDifferentContext() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
Object obj = e.eval("({ run: function() { } })");
// change script context
ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
e.setContext(ctxt);
Runnable r = ((Invocable)e).getInterface(obj, Runnable.class);
r.run();
}catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that getInterface on non-script object 'thiz' results in IllegalArgumentException.
*/
public void getInterfaceNonScriptObjectThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable)e).getInterface(new Object(), Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that getInterface on null 'thiz' results in IllegalArgumentException.
*/
public void getInterfaceNullThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable)e).getInterface(null, Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling getInterface on mirror created by another engine results in IllegalArgumentException.
*/
public void getInterfaceMixEnginesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine engine1 = m.getEngineByName("nashorn");
final ScriptEngine engine2 = m.getEngineByName("nashorn");
try {
Object obj = engine1.eval("({ run: function() {} })");
// pass object from engine1 to engine2 as 'thiz' for getInterface
((Invocable)engine2).getInterface(obj, Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void accessGlobalTest() {
final ScriptEngineManager m = new ScriptEngineManager();
@ -621,88 +404,6 @@ public class ScriptEngineTest {
assertEquals(sw.toString().replaceAll("\r", ""), "hello world\n");
}
@SuppressWarnings("unchecked")
@Test
public void reflectionTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
e.eval("var obj = { x: 344, y: 'nashorn' }");
int count = 0;
Map<Object, Object> map = (Map<Object, Object>)e.get("obj");
assertFalse(map.isEmpty());
assertTrue(map.keySet().contains("x"));
assertTrue(map.containsKey("x"));
assertTrue(map.values().contains("nashorn"));
assertTrue(map.containsValue("nashorn"));
for (final Map.Entry<?, ?> ex : map.entrySet()) {
final Object key = ex.getKey();
if (key.equals("x")) {
assertTrue(344 == ((Number)ex.getValue()).doubleValue());
count++;
} else if (key.equals("y")) {
assertEquals(ex.getValue(), "nashorn");
count++;
}
}
assertEquals(2, count);
assertEquals(2, map.size());
// add property
map.put("z", "hello");
assertEquals(e.eval("obj.z"), "hello");
assertEquals(map.get("z"), "hello");
assertTrue(map.keySet().contains("z"));
assertTrue(map.containsKey("z"));
assertTrue(map.values().contains("hello"));
assertTrue(map.containsValue("hello"));
assertEquals(map.size(), 3);
final Map<Object, Object> newMap = new HashMap<>();
newMap.put("foo", 23.0);
newMap.put("bar", true);
map.putAll(newMap);
assertEquals(e.eval("obj.foo"), 23.0);
assertEquals(e.eval("obj.bar"), true);
// remove using map method
map.remove("foo");
assertEquals(e.eval("typeof obj.foo"), "undefined");
count = 0;
e.eval("var arr = [ true, 'hello' ]");
map = (Map<Object, Object>)e.get("arr");
assertFalse(map.isEmpty());
assertTrue(map.containsKey("length"));
assertTrue(map.containsValue("hello"));
for (final Map.Entry<?, ?> ex : map.entrySet()) {
final Object key = ex.getKey();
if (key.equals("0")) {
assertEquals(ex.getValue(), Boolean.TRUE);
count++;
} else if (key.equals("1")) {
assertEquals(ex.getValue(), "hello");
count++;
}
}
assertEquals(count, 2);
assertEquals(map.size(), 2);
// add element
map.put("2", "world");
assertEquals(map.get("2"), "world");
assertEquals(map.size(), 3);
// remove all
map.clear();
assertTrue(map.isEmpty());
assertEquals(e.eval("typeof arr[0]"), "undefined");
assertEquals(e.eval("typeof arr[1]"), "undefined");
assertEquals(e.eval("typeof arr[2]"), "undefined");
}
@Test
public void redefineEchoTest() {
final ScriptEngineManager m = new ScriptEngineManager();
@ -715,150 +416,6 @@ public class ScriptEngineTest {
fail(exp.getMessage());
}
}
@Test
public void invokeMethodTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var Example = function() { this.hello = function() { return 'Hello World!'; };}; myExample = new Example();");
final Object obj = e.get("myExample");
final Object res = ((Invocable)e).invokeMethod(obj, "hello");
assertEquals(res, "Hello World!");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that we can call invokeMethod on an object that we got by evaluating
* script with different Context set.
*/
public void invokeMethodDifferentContextTest() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
Object obj = e.eval("({ hello: function() { return 'Hello World!'; } })");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
e.setContext(ctxt);
// invoke 'func' on obj - but with current script context changed
final Object res = ((Invocable)e).invokeMethod(obj, "hello");
assertEquals(res, "Hello World!");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that invokeMethod throws NPE on null method name.
*/
public void invokeMethodNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
final Object res = ((Invocable)e).invokeMethod(obj, null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (! (exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeMethod throws NoSuchMethodException on missing method.
*/
public void invokeMethodMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
final Object res = ((Invocable)e).invokeMethod(obj, "nonExistentMethod");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (! (exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on non-script object 'thiz' results in IllegalArgumentException.
*/
public void invokeMethodNonScriptObjectThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable)e).invokeMethod(new Object(), "toString");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on null 'thiz' results in IllegalArgumentException.
*/
public void invokeMethodNullThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable)e).invokeMethod(null, "toString");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on mirror created by another engine results in IllegalArgumentException.
*/
public void invokeMethodMixEnginesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine engine1 = m.getEngineByName("nashorn");
final ScriptEngine engine2 = m.getEngineByName("nashorn");
try {
Object obj = engine1.eval("({ run: function() {} })");
// pass object from engine1 to engine2 as 'thiz' for invokeMethod
((Invocable)engine2).invokeMethod(obj, "run");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (! (exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void noEnumerablePropertiesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
@ -919,308 +476,6 @@ public class ScriptEngineTest {
}
}
@Test
public void jsobjectTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }");
JSObject obj = (JSObject) e.get("obj");
// try basic get on existing properties
if (! obj.getMember("bar").equals("hello")) {
fail("obj.bar != 'hello'");
}
if (! obj.getSlot(1).equals("world")) {
fail("obj[1] != 'world'");
}
if (! obj.call("func", new Object[0]).equals("hello")) {
fail("obj.call('func') != 'hello'");
}
// try setting properties
obj.setMember("bar", "new-bar");
obj.setSlot(1, "new-element-1");
if (! obj.getMember("bar").equals("new-bar")) {
fail("obj.bar != 'new-bar'");
}
if (! obj.getSlot(1).equals("new-element-1")) {
fail("obj[1] != 'new-element-1'");
}
// try adding properties
obj.setMember("prop", "prop-value");
obj.setSlot(12, "element-12");
if (! obj.getMember("prop").equals("prop-value")) {
fail("obj.prop != 'prop-value'");
}
if (! obj.getSlot(12).equals("element-12")) {
fail("obj[12] != 'element-12'");
}
// delete properties
obj.removeMember("prop");
if ("prop-value".equals(obj.getMember("prop"))) {
fail("obj.prop is not deleted!");
}
// Simple eval tests
assertEquals(obj.eval("typeof Object"), "function");
assertEquals(obj.eval("'nashorn'.substring(3)"), "horn");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* check that null function name results in NPE.
*/
public void invokeFunctionNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object res = ((Invocable)e).invokeFunction(null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (! (exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that attempt to call missing function results in NoSuchMethodException.
*/
public void invokeFunctionMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object res = ((Invocable)e).invokeFunction("NonExistentFunc");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (! (exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeFunction calls functions only from current context's Bindings.
*/
public void invokeFunctionDifferentContextTest() {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
Object obj = e.eval("function hello() { return 'Hello World!'; }");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
// change engine's current context
e.setContext(ctxt);
((Invocable)e).invokeFunction("hello"); // no 'hello' in new context!
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (! (exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void invokeFunctionExceptionTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("function func() { throw new TypeError(); }");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
((Invocable)e).invokeFunction("func");
fail("should have thrown exception");
} catch (final ScriptException se) {
// ECMA TypeError property wrapped as a ScriptException
log("got " + se + " as expected");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
public void invokeMethodExceptionTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var sobj = {}; sobj.foo = function func() { throw new TypeError(); }");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
final Object sobj = e.get("sobj");
((Invocable)e).invokeMethod(sobj, "foo");
fail("should have thrown exception");
} catch (final ScriptException se) {
// ECMA TypeError property wrapped as a ScriptException
log("got " + se + " as expected");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
public void scriptObjectMirrorToStringTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
Object obj = e.eval("new TypeError('wrong type')");
assertEquals(obj.toString(), "TypeError: wrong type", "toString returns wrong value");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
Object obj = e.eval("function func() { print('hello'); }");
assertEquals(obj.toString(), "function func() { print('hello'); }", "toString returns wrong value");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
public void engineScopeTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE);
// check few ECMA standard built-in global properties
assertNotNull(engineScope.get("Object"));
assertNotNull(engineScope.get("TypeError"));
assertNotNull(engineScope.get("eval"));
// can access via ScriptEngine.get as well
assertNotNull(e.get("Object"));
assertNotNull(e.get("TypeError"));
assertNotNull(e.get("eval"));
// Access by either way should return same object
assertEquals(engineScope.get("Array"), e.get("Array"));
assertEquals(engineScope.get("EvalError"), e.get("EvalError"));
assertEquals(engineScope.get("undefined"), e.get("undefined"));
// try exposing a new variable from scope
engineScope.put("myVar", "foo");
try {
assertEquals(e.eval("myVar"), "foo");
} catch (final ScriptException se) {
se.printStackTrace();
fail(se.getMessage());
}
// update "myVar" in script an check the value from scope
try {
e.eval("myVar = 'nashorn';");
} catch (final ScriptException se) {
se.printStackTrace();
fail(se.getMessage());
}
// now check modified value from scope and engine
assertEquals(engineScope.get("myVar"), "nashorn");
assertEquals(e.get("myVar"), "nashorn");
}
@Test
public void multiGlobalTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Bindings b = e.createBindings();
final ScriptContext newCtxt = new SimpleScriptContext();
newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
try {
Object obj1 = e.eval("Object");
Object obj2 = e.eval("Object", newCtxt);
Assert.assertNotEquals(obj1, obj2);
Assert.assertNotNull(obj1);
Assert.assertNotNull(obj2);
Assert.assertEquals(obj1.toString(), obj2.toString());
e.eval("x = 'hello'");
e.eval("x = 'world'", newCtxt);
Object x1 = e.getContext().getAttribute("x");
Object x2 = newCtxt.getAttribute("x");
Assert.assertNotEquals(x1, x2);
Assert.assertEquals(x1, "hello");
Assert.assertEquals(x2, "world");
x1 = e.eval("x");
x2 = e.eval("x", newCtxt);
Assert.assertNotEquals(x1, x2);
Assert.assertEquals(x1, "hello");
Assert.assertEquals(x2, "world");
final ScriptContext origCtxt = e.getContext();
e.setContext(newCtxt);
e.eval("y = new Object()");
e.eval("y = new Object()", origCtxt);
Object y1 = origCtxt.getAttribute("y");
Object y2 = newCtxt.getAttribute("y");
Assert.assertNotEquals(y1, y2);
Assert.assertNotEquals(e.eval("y"), e.eval("y", origCtxt));
Assert.assertEquals("[object Object]", y1.toString());
Assert.assertEquals("[object Object]", y2.toString());
} catch (final ScriptException se) {
se.printStackTrace();
fail(se.getMessage());
}
}
@Test
/**
* Tests whether invocation of a JavaScript method through a variable arity Java method will pass the vararg array.
* Both non-vararg and vararg JavaScript methods are tested.
* @throws ScriptException
*/
public void variableArityInterfaceTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
e.eval(
"function test1(i, strings) {" +
" return 'i == ' + i + ', strings instanceof java.lang.String[] == ' + (strings instanceof Java.type('java.lang.String[]')) + ', strings == ' + java.util.Arrays.toString(strings)" +
"}" +
"function test2() {" +
" return 'arguments[0] == ' + arguments[0] + ', arguments[1] instanceof java.lang.String[] == ' + (arguments[1] instanceof Java.type('java.lang.String[]')) + ', arguments[1] == ' + java.util.Arrays.toString(arguments[1])" +
"}"
);
final VariableArityTestInterface itf = ((Invocable)e).getInterface(VariableArityTestInterface.class);
Assert.assertEquals(itf.test1(42, "a", "b"), "i == 42, strings instanceof java.lang.String[] == true, strings == [a, b]");
Assert.assertEquals(itf.test2(44, "c", "d", "e"), "arguments[0] == 44, arguments[1] instanceof java.lang.String[] == true, arguments[1] == [c, d, e]");
}
@Test
// check that print function prints arg followed by newline char
public void printTest() {

View file

@ -0,0 +1,230 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting;
import java.util.HashMap;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import org.testng.annotations.Test;
/**
* Tests to check jdk.nashorn.api.scripting.ScriptObjectMirror API.
*/
public class ScriptObjectMirrorTest {
@SuppressWarnings("unchecked")
@Test
public void reflectionTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
e.eval("var obj = { x: 344, y: 'nashorn' }");
int count = 0;
Map<Object, Object> map = (Map<Object, Object>) e.get("obj");
assertFalse(map.isEmpty());
assertTrue(map.keySet().contains("x"));
assertTrue(map.containsKey("x"));
assertTrue(map.values().contains("nashorn"));
assertTrue(map.containsValue("nashorn"));
for (final Map.Entry<?, ?> ex : map.entrySet()) {
final Object key = ex.getKey();
if (key.equals("x")) {
assertTrue(344 == ((Number) ex.getValue()).doubleValue());
count++;
} else if (key.equals("y")) {
assertEquals(ex.getValue(), "nashorn");
count++;
}
}
assertEquals(2, count);
assertEquals(2, map.size());
// add property
map.put("z", "hello");
assertEquals(e.eval("obj.z"), "hello");
assertEquals(map.get("z"), "hello");
assertTrue(map.keySet().contains("z"));
assertTrue(map.containsKey("z"));
assertTrue(map.values().contains("hello"));
assertTrue(map.containsValue("hello"));
assertEquals(map.size(), 3);
final Map<Object, Object> newMap = new HashMap<>();
newMap.put("foo", 23.0);
newMap.put("bar", true);
map.putAll(newMap);
assertEquals(e.eval("obj.foo"), 23.0);
assertEquals(e.eval("obj.bar"), true);
// remove using map method
map.remove("foo");
assertEquals(e.eval("typeof obj.foo"), "undefined");
count = 0;
e.eval("var arr = [ true, 'hello' ]");
map = (Map<Object, Object>) e.get("arr");
assertFalse(map.isEmpty());
assertTrue(map.containsKey("length"));
assertTrue(map.containsValue("hello"));
for (final Map.Entry<?, ?> ex : map.entrySet()) {
final Object key = ex.getKey();
if (key.equals("0")) {
assertEquals(ex.getValue(), Boolean.TRUE);
count++;
} else if (key.equals("1")) {
assertEquals(ex.getValue(), "hello");
count++;
}
}
assertEquals(count, 2);
assertEquals(map.size(), 2);
// add element
map.put("2", "world");
assertEquals(map.get("2"), "world");
assertEquals(map.size(), 3);
// remove all
map.clear();
assertTrue(map.isEmpty());
assertEquals(e.eval("typeof arr[0]"), "undefined");
assertEquals(e.eval("typeof arr[1]"), "undefined");
assertEquals(e.eval("typeof arr[2]"), "undefined");
}
@Test
public void jsobjectTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }");
JSObject obj = (JSObject) e.get("obj");
// try basic get on existing properties
if (!obj.getMember("bar").equals("hello")) {
fail("obj.bar != 'hello'");
}
if (!obj.getSlot(1).equals("world")) {
fail("obj[1] != 'world'");
}
if (!obj.call("func", new Object[0]).equals("hello")) {
fail("obj.call('func') != 'hello'");
}
// try setting properties
obj.setMember("bar", "new-bar");
obj.setSlot(1, "new-element-1");
if (!obj.getMember("bar").equals("new-bar")) {
fail("obj.bar != 'new-bar'");
}
if (!obj.getSlot(1).equals("new-element-1")) {
fail("obj[1] != 'new-element-1'");
}
// try adding properties
obj.setMember("prop", "prop-value");
obj.setSlot(12, "element-12");
if (!obj.getMember("prop").equals("prop-value")) {
fail("obj.prop != 'prop-value'");
}
if (!obj.getSlot(12).equals("element-12")) {
fail("obj[12] != 'element-12'");
}
// delete properties
obj.removeMember("prop");
if ("prop-value".equals(obj.getMember("prop"))) {
fail("obj.prop is not deleted!");
}
// Simple eval tests
assertEquals(obj.eval("typeof Object"), "function");
assertEquals(obj.eval("'nashorn'.substring(3)"), "horn");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
public void scriptObjectMirrorToStringTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
Object obj = e.eval("new TypeError('wrong type')");
assertEquals(obj.toString(), "TypeError: wrong type", "toString returns wrong value");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
Object obj = e.eval("function func() { print('hello'); }");
assertEquals(obj.toString(), "function func() { print('hello'); }", "toString returns wrong value");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
public void mirrorNewObjectGlobalFunctionTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final ScriptEngine e2 = m.getEngineByName("nashorn");
e.eval("function func() {}");
e2.put("foo", e.get("func"));
final Object e2global = e2.eval("this");
final Object newObj = ((ScriptObjectMirror) e2global).newObject("foo");
assertTrue(newObj instanceof ScriptObjectMirror);
}
@Test
public void mirrorNewObjectInstanceFunctionTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final ScriptEngine e2 = m.getEngineByName("nashorn");
e.eval("function func() {}");
e2.put("func", e.get("func"));
final Object e2obj = e2.eval("({ foo: func })");
final Object newObj = ((ScriptObjectMirror) e2obj).newObject("foo");
assertTrue(newObj instanceof ScriptObjectMirror);
}
}

View file

@ -32,7 +32,10 @@ import static org.testng.Assert.fail;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptContext;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.testng.annotations.Test;
@ -196,4 +199,25 @@ public class TrustedScriptEngineTest {
}
fail("Cannot find nashorn factory!");
}
@Test
public void globalPerEngineTest() throws ScriptException {
final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
final String[] options = new String[] { "--global-per-engine" };
final ScriptEngine e = fac.getScriptEngine(options);
e.eval("function foo() {}");
final ScriptContext newCtx = new SimpleScriptContext();
newCtx.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
// all global definitions shared and so 'foo' should be
// visible in new Bindings as well.
assertTrue(e.eval("typeof foo", newCtx).equals("function"));
e.eval("function bar() {}", newCtx);
// bar should be visible in default context
assertTrue(e.eval("typeof bar").equals("function"));
}
}