mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-21 07:45:11 +00:00
Merge
This commit is contained in:
commit
53ae5516ff
@ -56,7 +56,7 @@
|
||||
<target name="init" depends="init-conditions, init-cc">
|
||||
|
||||
<!-- extends jvm args -->
|
||||
<property name="run.test.jvmargs">${run.test.jvmargs.main} ${run.test.cc.jvmargs}</property>
|
||||
<property name="run.test.jvmargs" value="${run.test.jvmargs.main} ${run.test.cc.jvmargs}"/>
|
||||
<property name="run.test.jvmargs.octane" value="${run.test.jvmargs.octane.main} ${run.test.cc.jvmargs}" />
|
||||
|
||||
<echo message="run.test.jvmargs=${run.test.jvmargs}"/>
|
||||
@ -139,6 +139,31 @@
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="build-fxshell" depends="jar">
|
||||
<description>Builds the javafx shell.</description>
|
||||
<mkdir dir="${fxshell.classes.dir}"/>
|
||||
<javac srcdir="${fxshell.dir}"
|
||||
destdir="${fxshell.classes.dir}"
|
||||
classpath="${dist.jar}:${javac.classpath}"
|
||||
debug="${javac.debug}"
|
||||
encoding="${javac.encoding}"
|
||||
includeantruntime="false">
|
||||
</javac>
|
||||
<jar jarfile="${fxshell.jar}" manifest="${meta.inf.dir}/MANIFEST.MF" index="true" filesetmanifest="merge">
|
||||
<fileset dir="${fxshell.classes.dir}"/>
|
||||
<manifest>
|
||||
<attribute name="Archiver-Version" value="n/a"/>
|
||||
<attribute name="Build-Jdk" value="${java.runtime.version}"/>
|
||||
<attribute name="Built-By" value="n/a"/>
|
||||
<attribute name="Created-By" value="Ant jar task"/>
|
||||
<section name="jdk/nashorn/">
|
||||
<attribute name="Implementation-Title" value="Oracle Nashorn FXShell"/>
|
||||
<attribute name="Implementation-Version" value="${nashorn.version}"/>
|
||||
</section>
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="javadoc" depends="prepare">
|
||||
<javadoc destdir="${dist.javadoc.dir}" use="yes" overview="src/overview.html" windowtitle="${nashorn.product.name} ${nashorn.version}" additionalparam="-quiet" failonerror="true">
|
||||
|
||||
@ -65,6 +65,12 @@ dist.dir=dist
|
||||
dist.jar=${dist.dir}/nashorn.jar
|
||||
dist.javadoc.dir=${dist.dir}/javadoc
|
||||
|
||||
# nashorn javafx shell
|
||||
fxshell.tool = jdk.nashorn.tools.FXShell
|
||||
fxshell.classes.dir = ${build.dir}/fxshell/classes
|
||||
fxshell.dir = tools/fxshell
|
||||
fxshell.jar = ${dist.dir}/nashornfx.jar
|
||||
|
||||
# jars refererred
|
||||
file.reference.testng.jar=test/lib/testng.jar
|
||||
|
||||
|
||||
@ -96,6 +96,11 @@ import jdk.internal.dynalink.support.TypeUtilities;
|
||||
* @author Attila Szegedi
|
||||
*/
|
||||
final class ClassString {
|
||||
/**
|
||||
* An anonymous inner class used solely to represent the "type" of null values for method applicability checking.
|
||||
*/
|
||||
static final Class<?> NULL_CLASS = (new Object() { /* Intentionally empty */ }).getClass();
|
||||
|
||||
private final Class<?>[] classes;
|
||||
private int hashCode;
|
||||
|
||||
@ -203,6 +208,9 @@ final class ClassString {
|
||||
}
|
||||
|
||||
private static boolean canConvert(LinkerServices ls, Class<?> from, Class<?> to) {
|
||||
if(from == NULL_CLASS) {
|
||||
return !to.isPrimitive();
|
||||
}
|
||||
return ls == null ? TypeUtilities.isMethodInvocationConvertible(from, to) : ls.canConvert(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ class OverloadedMethod {
|
||||
final Class<?>[] argTypes = new Class[args.length];
|
||||
for(int i = 0; i < argTypes.length; ++i) {
|
||||
final Object arg = args[i];
|
||||
argTypes[i] = arg == null ? callSiteType.parameterType(i) : arg.getClass();
|
||||
argTypes[i] = arg == null ? ClassString.NULL_CLASS : arg.getClass();
|
||||
}
|
||||
final ClassString classString = new ClassString(argTypes);
|
||||
MethodHandle method = argTypesToMethods.get(classString);
|
||||
|
||||
@ -160,7 +160,7 @@ public final class NativeArray extends ScriptObject {
|
||||
if ("length".equals(key)) {
|
||||
// Step 3a
|
||||
if (!desc.has(VALUE)) {
|
||||
return super.defineOwnProperty("length", propertyDesc, reject);
|
||||
return super.defineOwnProperty("length", desc, reject);
|
||||
}
|
||||
|
||||
// Step 3b
|
||||
@ -242,7 +242,7 @@ public final class NativeArray extends ScriptObject {
|
||||
|
||||
// Step 4c
|
||||
// set the new array element
|
||||
final boolean succeeded = super.defineOwnProperty(key, propertyDesc, false);
|
||||
final boolean succeeded = super.defineOwnProperty(key, desc, false);
|
||||
|
||||
// Step 4d
|
||||
if (!succeeded) {
|
||||
@ -263,7 +263,7 @@ public final class NativeArray extends ScriptObject {
|
||||
}
|
||||
|
||||
// not an index property
|
||||
return super.defineOwnProperty(key, propertyDesc, reject);
|
||||
return super.defineOwnProperty(key, desc, reject);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -337,8 +337,9 @@ public final class NativeArray extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object toString(final Object self) {
|
||||
if (self instanceof ScriptObject) {
|
||||
final ScriptObject sobj = (ScriptObject) self;
|
||||
final Object obj = Global.toObject(self);
|
||||
if (obj instanceof ScriptObject) {
|
||||
final ScriptObject sobj = (ScriptObject)obj;
|
||||
try {
|
||||
final Object join = JOIN.getGetter().invokeExact(sobj);
|
||||
if (join instanceof ScriptFunction) {
|
||||
@ -573,9 +574,9 @@ public final class NativeArray extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object join(final Object self, final Object separator) {
|
||||
final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Iterator<Object> iter = arrayLikeIterator(self, true);
|
||||
final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
final Object obj = iter.next();
|
||||
@ -754,8 +755,9 @@ public final class NativeArray extends ScriptObject {
|
||||
final Object obj = Global.toObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)obj;
|
||||
final long len = JSType.toUint32(sobj.getLength());
|
||||
final long relativeStartUint32 = JSType.toUint32(start);
|
||||
final long relativeStart = JSType.toInteger(start);
|
||||
final double startNum = JSType.toNumber(start);
|
||||
final long relativeStartUint32 = JSType.toUint32(startNum);
|
||||
final long relativeStart = JSType.toInteger(startNum);
|
||||
|
||||
long k = relativeStart < 0 ?
|
||||
Math.max(len + relativeStart, 0) :
|
||||
@ -763,8 +765,9 @@ public final class NativeArray extends ScriptObject {
|
||||
Math.max(relativeStartUint32, relativeStart),
|
||||
len);
|
||||
|
||||
final long relativeEndUint32 = end == ScriptRuntime.UNDEFINED ? len : JSType.toUint32(end);
|
||||
final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toInteger(end);
|
||||
final double endNum = (end == ScriptRuntime.UNDEFINED)? Double.NaN : JSType.toNumber(end);
|
||||
final long relativeEndUint32 = (end == ScriptRuntime.UNDEFINED)? len : JSType.toUint32(endNum);
|
||||
final long relativeEnd = (end == ScriptRuntime.UNDEFINED)? len : JSType.toInteger(endNum);
|
||||
|
||||
final long finale = relativeEnd < 0 ?
|
||||
Math.max(len + relativeEnd, 0) :
|
||||
@ -846,17 +849,26 @@ public final class NativeArray extends ScriptObject {
|
||||
final long len = JSType.toUint32(sobj.getLength());
|
||||
|
||||
if (len > 1) {
|
||||
final Object[] src = new Object[(int) len];
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
src[i] = sobj.get(i);
|
||||
// Get only non-missing elements. Missing elements go at the end
|
||||
// of the sorted array. So, just don't copy these to sort input.
|
||||
|
||||
final ArrayList<Object> src = new ArrayList<>();
|
||||
for (int i = 0; i < (int)len; i++) {
|
||||
if (sobj.has(i)) {
|
||||
src.add(sobj.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
final Object[] sorted = sort(src, comparefn);
|
||||
assert sorted.length == src.length;
|
||||
final Object[] sorted = sort(src.toArray(), comparefn);
|
||||
|
||||
for (int i = 0; i < sorted.length; i++) {
|
||||
sobj.set(i, sorted[i], strict);
|
||||
}
|
||||
|
||||
// delete missing elements - which are at the end of sorted array
|
||||
for (int j = sorted.length; j < (int)len; j++) {
|
||||
sobj.delete(j, strict);
|
||||
}
|
||||
}
|
||||
|
||||
return sobj;
|
||||
@ -895,8 +907,9 @@ public final class NativeArray extends ScriptObject {
|
||||
final ScriptObject sobj = (ScriptObject)obj;
|
||||
final boolean strict = Global.isStrict();
|
||||
final long len = JSType.toUint32(sobj.getLength());
|
||||
final long relativeStartUint32 = JSType.toUint32(start);
|
||||
final long relativeStart = JSType.toInteger(start);
|
||||
final double startNum = JSType.toNumber(start);
|
||||
final long relativeStartUint32 = JSType.toUint32(startNum);
|
||||
final long relativeStart = JSType.toInteger(startNum);
|
||||
|
||||
//TODO: workaround overflow of relativeStart for start > Integer.MAX_VALUE
|
||||
final long actualStart = relativeStart < 0 ?
|
||||
|
||||
@ -844,10 +844,6 @@ public final class NativeDate extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object toJSON(final Object self, final Object key) {
|
||||
if (self instanceof NativeDate) {
|
||||
final NativeDate nd = (NativeDate)self;
|
||||
return (isNaN(nd.getTime())) ? null : toISOStringImpl(nd);
|
||||
}
|
||||
// NOTE: Date.prototype.toJSON is generic. Accepts other objects as well.
|
||||
final Object selfObj = Global.toObject(self);
|
||||
if (!(selfObj instanceof ScriptObject)) {
|
||||
@ -1200,13 +1196,18 @@ public final class NativeDate extends ScriptObject {
|
||||
// Convert Date constructor args, checking for NaN, filling in defaults etc.
|
||||
private static double[] convertCtorArgs(final Object[] args) {
|
||||
final double[] d = new double[7];
|
||||
boolean nullReturn = false;
|
||||
|
||||
// should not bailout on first NaN or infinite. Need to convert all
|
||||
// subsequent args for possible side-effects via valueOf/toString overrides
|
||||
// on argument objects.
|
||||
for (int i = 0; i < d.length; i++) {
|
||||
if (i < args.length) {
|
||||
final double darg = JSType.toNumber(args[i]);
|
||||
if (isNaN(darg) || isInfinite(darg)) {
|
||||
return null;
|
||||
nullReturn = true;
|
||||
}
|
||||
|
||||
d[i] = (long)darg;
|
||||
} else {
|
||||
d[i] = i == 2 ? 1 : 0; // day in month defaults to 1
|
||||
@ -1217,31 +1218,39 @@ public final class NativeDate extends ScriptObject {
|
||||
d[0] += 1900;
|
||||
}
|
||||
|
||||
return d;
|
||||
return nullReturn? null : d;
|
||||
}
|
||||
|
||||
// This method does the hard work for all setter methods: If a value is provided
|
||||
// as argument it is used, otherwise the value is calculated from the existing time value.
|
||||
private static double[] convertArgs(final Object[] args, final double time, final int fieldId, final int start, final int length) {
|
||||
final double[] d = new double[length];
|
||||
boolean nullReturn = false;
|
||||
|
||||
// Need to call toNumber on all args for side-effects - even if an argument
|
||||
// fails to convert to number, subsequent toNumber calls needed for possible
|
||||
// side-effects via valueOf/toString overrides.
|
||||
for (int i = start; i < start + length; i++) {
|
||||
if (fieldId <= i && i < fieldId + args.length) {
|
||||
final double darg = JSType.toNumber(args[i - fieldId]);
|
||||
if (isNaN(darg) || isInfinite(darg)) {
|
||||
return null;
|
||||
nullReturn = true;
|
||||
}
|
||||
|
||||
d[i - start] = (long) darg;
|
||||
} else {
|
||||
// Date.prototype.set* methods require first argument to be defined
|
||||
if (i == fieldId) {
|
||||
return null;
|
||||
nullReturn = true;
|
||||
}
|
||||
|
||||
if (! nullReturn) {
|
||||
d[i - start] = valueFromTime(i, time);
|
||||
}
|
||||
d[i - start] = valueFromTime(i, time);
|
||||
}
|
||||
}
|
||||
return d;
|
||||
|
||||
return nullReturn? null : d;
|
||||
}
|
||||
|
||||
// ECMA 15.9.1.14 TimeClip (time)
|
||||
|
||||
@ -394,22 +394,56 @@ public final class NativeJava {
|
||||
* </pre>
|
||||
* We can see several important concepts in the above example:
|
||||
* <ul>
|
||||
* <li>Every Java class will have exactly one extender subclass in Nashorn - repeated invocations of {@code extend}
|
||||
* for the same type will yield the same extender type. It's a generic adapter that delegates to whatever JavaScript
|
||||
* functions its implementation object has on a per-instance basis.</li>
|
||||
* <li>Every specified list of Java types will have exactly one extender subclass in Nashorn - repeated invocations
|
||||
* of {@code extend} for the same list of types will yield the same extender type. It's a generic adapter that
|
||||
* delegates to whatever JavaScript functions its implementation object has on a per-instance basis.</li>
|
||||
* <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter
|
||||
* must be prepared to deal with all overloads.</li>
|
||||
* <li>You can't invoke {@code super.*()} from adapters for now.</li>
|
||||
* <li>It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that
|
||||
* case, it is treated as a class-level override. {@code extend} will return an extender class where all instances
|
||||
* will have the methods implemented by functions on that object, just as if that object were passed as the last
|
||||
* argument to their constructor. Example:
|
||||
* <pre>
|
||||
* var Runnable = Java.type("java.lang.Runnable")
|
||||
* var R1 = Java.extend(Runnable, {
|
||||
* run: function() {
|
||||
* print("R1.run() invoked!")
|
||||
* }
|
||||
* })
|
||||
* var r1 = new R1
|
||||
* var t = new java.lang.Thread(r1)
|
||||
* t.start()
|
||||
* t.join()
|
||||
* </pre>
|
||||
* As you can see, you don't have to pass any object when you create a new instance of {@code R1} as its
|
||||
* {@code run()} function was defined already when extending the class. Of course, you can still provide
|
||||
* instance-level overrides on these objects. The order of precedence is instance-level method, class-level method,
|
||||
* superclass method, or {@code UnsupportedOperationException} if the superclass method is abstract. If we continue
|
||||
* our previous example:
|
||||
* <pre>
|
||||
* var r2 = new R1(function() { print("r2.run() invoked!") })
|
||||
* r2.run()
|
||||
* </pre>
|
||||
* We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior.
|
||||
* </li>
|
||||
* </ul>
|
||||
* @param self not used
|
||||
* @param types the original types. The caller must pass at least one Java type object of class {@link StaticClass}
|
||||
* representing either a public interface or a non-final public class with at least one public or protected
|
||||
* constructor. If more than one type is specified, at most one can be a class and the rest have to be interfaces.
|
||||
* Invoking the method twice with exactly the same types in the same order will return the same adapter
|
||||
* class, any reordering of types or even addition or removal of redundant types (i.e. interfaces that other types
|
||||
* in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of
|
||||
* interfaces) will result in a different adapter class, even though those adapter classes are functionally
|
||||
* identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists.
|
||||
* Invoking the method twice with exactly the same types in the same order - in absence of class-level overrides -
|
||||
* will return the same adapter class, any reordering of types or even addition or removal of redundant types (i.e.
|
||||
* interfaces that other types in the list already implement/extend, or {@code java.lang.Object} in a list of types
|
||||
* consisting purely of interfaces) will result in a different adapter class, even though those adapter classes are
|
||||
* functionally identical; we deliberately don't want to incur the additional processing cost of canonicalizing type
|
||||
* lists. As a special case, the last argument can be a {@code ScriptObject} instead of a type. In this case, a
|
||||
* separate adapter class is generated - new one for each invocation - that will use the passed script object as its
|
||||
* implementation for all instances. Instances of such adapter classes can then be created without passing another
|
||||
* script object in the constructor, as the class has a class-level behavior defined by the script object. However,
|
||||
* you can still pass a script object (or if it's a SAM type, a function) to the constructor to provide further
|
||||
* instance-level overrides.
|
||||
*
|
||||
* @return a new {@link StaticClass} that represents the adapter for the original types.
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
@ -417,14 +451,27 @@ public final class NativeJava {
|
||||
if(types == null || types.length == 0) {
|
||||
throw typeError("extend.expects.at.least.one.argument");
|
||||
}
|
||||
final Class<?>[] stypes = new Class<?>[types.length];
|
||||
final int l = types.length;
|
||||
final int typesLen;
|
||||
final ScriptObject classOverrides;
|
||||
if(types[l - 1] instanceof ScriptObject) {
|
||||
classOverrides = (ScriptObject)types[l - 1];
|
||||
typesLen = l - 1;
|
||||
if(typesLen == 0) {
|
||||
throw typeError("extend.expects.at.least.one.type.argument");
|
||||
}
|
||||
} else {
|
||||
classOverrides = null;
|
||||
typesLen = l;
|
||||
}
|
||||
final Class<?>[] stypes = new Class<?>[typesLen];
|
||||
try {
|
||||
for(int i = 0; i < types.length; ++i) {
|
||||
for(int i = 0; i < typesLen; ++i) {
|
||||
stypes[i] = ((StaticClass)types[i]).getRepresentedClass();
|
||||
}
|
||||
} catch(final ClassCastException e) {
|
||||
throw typeError("extend.expects.java.types");
|
||||
}
|
||||
return JavaAdapterFactory.getAdapterClassFor(stypes);
|
||||
return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides);
|
||||
}
|
||||
}
|
||||
|
||||
@ -523,8 +523,11 @@ public final class NativeRegExp extends ScriptObject {
|
||||
}
|
||||
|
||||
private RegExpResult execInner(final String string) {
|
||||
int start = getLastIndex();
|
||||
if (! regexp.isGlobal()) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
final int start = regexp.isGlobal() ? getLastIndex() : 0;
|
||||
if (start < 0 || start > string.length()) {
|
||||
setLastIndex(0);
|
||||
return null;
|
||||
|
||||
@ -838,15 +838,13 @@ public final class NativeString extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object split(final Object self, final Object separator, final Object limit) {
|
||||
|
||||
final String str = checkObjectToString(self);
|
||||
final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit);
|
||||
|
||||
if (separator == UNDEFINED) {
|
||||
return new NativeArray(new Object[]{str});
|
||||
}
|
||||
|
||||
final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit);
|
||||
|
||||
if (separator instanceof NativeRegExp) {
|
||||
return ((NativeRegExp) separator).split(str, lim);
|
||||
}
|
||||
|
||||
@ -125,11 +125,17 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
// function object representing TypeErrorThrower
|
||||
private static ScriptFunction typeErrorThrower;
|
||||
|
||||
/*
|
||||
* ECMA section 13.2.3 The [[ThrowTypeError]] Function Object
|
||||
*/
|
||||
static synchronized ScriptFunction getTypeErrorThrower() {
|
||||
if (typeErrorThrower == null) {
|
||||
//name handle
|
||||
final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false, false);
|
||||
// use "getter" so that [[ThrowTypeError]] function's arity is 0 - as specified in step 10 of section 13.2.3
|
||||
final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER, null, null, false, false, false);
|
||||
func.setPrototype(UNDEFINED);
|
||||
// Non-constructor built-in functions do not have "prototype" property
|
||||
func.deleteOwnProperty(func.getMap().findProperty("prototype"));
|
||||
func.preventExtensions();
|
||||
typeErrorThrower = func;
|
||||
}
|
||||
|
||||
@ -152,7 +158,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
}
|
||||
|
||||
private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) {
|
||||
// Bond function map is same as strict function map, but additionally lacks the "prototype" property, see
|
||||
// Bound function map is same as strict function map, but additionally lacks the "prototype" property, see
|
||||
// ECMAScript 5.1 section 15.3.4.5
|
||||
return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype"));
|
||||
}
|
||||
@ -182,6 +188,8 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
|
||||
final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, false, true, false);
|
||||
func.setPrototype(UNDEFINED);
|
||||
// Non-constructor built-in functions do not have "prototype" property
|
||||
func.deleteOwnProperty(func.getMap().findProperty("prototype"));
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
@ -104,10 +104,10 @@ import java.util.Set;
|
||||
*/
|
||||
public final class PropertyHashMap implements Map <String, Property> {
|
||||
/** Number of initial bins. Power of 2. */
|
||||
private static final int INITIAL_BINS = 16;
|
||||
private static final int INITIAL_BINS = 32;
|
||||
|
||||
/** Threshold before using bins. */
|
||||
private static final int LIST_THRESHOLD = 4;
|
||||
private static final int LIST_THRESHOLD = 8;
|
||||
|
||||
/** Initial map. */
|
||||
public static final PropertyHashMap EMPTY_MAP = new PropertyHashMap();
|
||||
@ -300,8 +300,8 @@ public final class PropertyHashMap implements Map <String, Property> {
|
||||
* @return Number of bins required.
|
||||
*/
|
||||
private static int binsNeeded(final int n) {
|
||||
// Allow for 25% padding.
|
||||
return 1 << (32 - Integer.numberOfLeadingZeros((n + oneQuarter(n)) | (INITIAL_BINS - 1)));
|
||||
// 50% padding
|
||||
return 1 << (32 - Integer.numberOfLeadingZeros((n + (n >>> 1)) | (INITIAL_BINS - 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,28 +315,16 @@ public final class PropertyHashMap implements Map <String, Property> {
|
||||
return (n >>> 1) + (n >>> 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to calculate the current capacity of the bins.
|
||||
*
|
||||
* @param n Number of bin slots.
|
||||
*
|
||||
* @return 25% of n.
|
||||
*/
|
||||
private static int oneQuarter(final int n) {
|
||||
return n >>> 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the bin table after changing the number of bins.
|
||||
*
|
||||
* @param list // List of all properties.
|
||||
* @param newSize // New size of {@link PropertyHashMap}.
|
||||
* @param binSize // New size of bins.
|
||||
*
|
||||
* @return Populated bins.
|
||||
*/
|
||||
private static Element[] rehash(final Element list, final int newSize) {
|
||||
final int binsNeeded = binsNeeded(newSize);
|
||||
final Element[] newBins = new Element[binsNeeded];
|
||||
private static Element[] rehash(final Element list, final int binSize) {
|
||||
final Element[] newBins = new Element[binSize];
|
||||
for (Element element = list; element != null; element = element.getLink()) {
|
||||
final Property property = element.getProperty();
|
||||
final String key = property.getKey();
|
||||
@ -390,7 +378,7 @@ public final class PropertyHashMap implements Map <String, Property> {
|
||||
if (bins == null && newSize <= LIST_THRESHOLD) {
|
||||
newBins = null;
|
||||
} else if (newSize > threshold) {
|
||||
newBins = rehash(list, newSize);
|
||||
newBins = rehash(list, binsNeeded(newSize));
|
||||
} else {
|
||||
newBins = bins.clone();
|
||||
}
|
||||
|
||||
@ -540,11 +540,13 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
|
||||
* @param newMap Modified {@link PropertyMap}.
|
||||
*/
|
||||
private void addToHistory(final Property property, final PropertyMap newMap) {
|
||||
if (history == null) {
|
||||
history = new LinkedHashMap<>();
|
||||
}
|
||||
if (!properties.isEmpty()) {
|
||||
if (history == null) {
|
||||
history = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
history.put(property, newMap);
|
||||
history.put(property, newMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
@ -42,9 +43,10 @@ import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
|
||||
*/
|
||||
public final class WithObject extends ScriptObject implements Scope {
|
||||
|
||||
private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
|
||||
private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
|
||||
private static final MethodHandle BIND_TO_EXPRESSION = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
|
||||
private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
|
||||
private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
|
||||
private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
|
||||
private static final MethodHandle BIND_TO_EXPRESSION_FN = findOwnMH("bindToExpression", Object.class, ScriptFunction.class, Object.class);
|
||||
|
||||
/** With expression object. */
|
||||
private final Object expression;
|
||||
@ -237,9 +239,14 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
return link.filterArguments(0, WITHEXPRESSIONFILTER);
|
||||
}
|
||||
|
||||
final MethodHandle linkInvocation = link.getInvocation();
|
||||
final MethodType linkType = linkInvocation.type();
|
||||
final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType());
|
||||
return link.replaceMethods(
|
||||
// Make sure getMethod will bind the script functions it receives to WithObject.expression
|
||||
MH.foldArguments(BIND_TO_EXPRESSION, filter(link.getInvocation(), WITHEXPRESSIONFILTER)),
|
||||
MH.foldArguments(linkReturnsFunction ? BIND_TO_EXPRESSION_FN : BIND_TO_EXPRESSION_OBJ,
|
||||
filter(linkInvocation.asType(linkType.changeReturnType(
|
||||
linkReturnsFunction ? ScriptFunction.class : Object.class)), WITHEXPRESSIONFILTER)),
|
||||
// No clever things for the guard -- it is still identically filtered.
|
||||
filterGuard(link, WITHEXPRESSIONFILTER));
|
||||
}
|
||||
@ -269,7 +276,11 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Object bindToExpression(final Object fn, final Object receiver) {
|
||||
return fn instanceof ScriptFunction ? ((ScriptFunction) fn).makeBoundFunction(withFilterExpression(receiver), new Object[0]) : fn;
|
||||
return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn;
|
||||
}
|
||||
|
||||
private static Object bindToExpression(final ScriptFunction fn, final Object receiver) {
|
||||
return fn.makeBoundFunction(withFilterExpression(receiver), new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class AdaptationException extends Exception {
|
||||
private final AdaptationResult adaptationResult;
|
||||
|
||||
AdaptationException(final AdaptationResult.Outcome outcome, final String classList) {
|
||||
this.adaptationResult = new AdaptationResult(outcome, classList);
|
||||
}
|
||||
|
||||
AdaptationResult getAdaptationResult() {
|
||||
return adaptationResult;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 jdk.nashorn.internal.runtime.ECMAErrors;
|
||||
import jdk.nashorn.internal.runtime.ECMAException;
|
||||
|
||||
/**
|
||||
* A result of generating an adapter for a class. A tuple of an outcome and - in case of an error outcome - a list of
|
||||
* classes that caused the error.
|
||||
*/
|
||||
class AdaptationResult {
|
||||
/**
|
||||
* Contains various outcomes for attempting to generate an adapter class. These are stored in AdapterInfo instances.
|
||||
* We have a successful outcome (adapter class was generated) and four possible error outcomes: superclass is final,
|
||||
* superclass is not public, superclass has no public or protected constructor, more than one superclass was
|
||||
* specified. We don't throw exceptions when we try to generate the adapter, but rather just record these error
|
||||
* conditions as they are still useful as partial outcomes, as Nashorn's linker can still successfully check whether
|
||||
* the class can be autoconverted from a script function even when it is not possible to generate an adapter for it.
|
||||
*/
|
||||
enum Outcome {
|
||||
SUCCESS,
|
||||
ERROR_FINAL_CLASS,
|
||||
ERROR_NON_PUBLIC_CLASS,
|
||||
ERROR_NO_ACCESSIBLE_CONSTRUCTOR,
|
||||
ERROR_MULTIPLE_SUPERCLASSES,
|
||||
ERROR_NO_COMMON_LOADER
|
||||
}
|
||||
|
||||
static final AdaptationResult SUCCESSFUL_RESULT = new AdaptationResult(Outcome.SUCCESS, "");
|
||||
|
||||
private final Outcome outcome;
|
||||
private final String classList;
|
||||
|
||||
AdaptationResult(final Outcome outcome, final String classList) {
|
||||
this.outcome = outcome;
|
||||
this.classList = classList;
|
||||
}
|
||||
|
||||
Outcome getOutcome() {
|
||||
return outcome;
|
||||
}
|
||||
|
||||
String getClassList() {
|
||||
return classList;
|
||||
}
|
||||
|
||||
ECMAException typeError() {
|
||||
return ECMAErrors.typeError("extend." + outcome, classList);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.runtime.ECMAErrors.typeError;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its
|
||||
* equals/hashCode is defined in terms of the identity of the class loader. The rationale for this class is that it
|
||||
* couples a class loader with a random representative class coming from that loader - this representative class is then
|
||||
* used to determine if one loader can see the other loader's classes.
|
||||
*/
|
||||
final class ClassAndLoader {
|
||||
private final Class<?> representativeClass;
|
||||
// Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing,
|
||||
// getLoader().
|
||||
private ClassLoader loader;
|
||||
// We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For
|
||||
// the most basic case of looking up an already-generated adapter info for a single type, we avoid it.
|
||||
private boolean loaderRetrieved;
|
||||
|
||||
ClassAndLoader(final Class<?> representativeClass, final boolean retrieveLoader) {
|
||||
this.representativeClass = representativeClass;
|
||||
if(retrieveLoader) {
|
||||
retrieveLoader();
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> getRepresentativeClass() {
|
||||
return representativeClass;
|
||||
}
|
||||
|
||||
boolean canSee(ClassAndLoader other) {
|
||||
try {
|
||||
final Class<?> otherClass = other.getRepresentativeClass();
|
||||
return Class.forName(otherClass.getName(), false, getLoader()) == otherClass;
|
||||
} catch (final ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ClassLoader getLoader() {
|
||||
if(!loaderRetrieved) {
|
||||
retrieveLoader();
|
||||
}
|
||||
return getRetrievedLoader();
|
||||
}
|
||||
|
||||
ClassLoader getRetrievedLoader() {
|
||||
assert loaderRetrieved;
|
||||
return loader;
|
||||
}
|
||||
|
||||
private void retrieveLoader() {
|
||||
loader = representativeClass.getClassLoader();
|
||||
loaderRetrieved = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(getRetrievedLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the
|
||||
* list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a
|
||||
* class loader that can also see all other types is returned. If there is no such loader, an exception is thrown.
|
||||
* @param types the input types
|
||||
* @return the first type from the array that is defined in a class loader that can also see all other types.
|
||||
*/
|
||||
static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) {
|
||||
// Short circuit the cheap case
|
||||
if(types.length == 1) {
|
||||
return new ClassAndLoader(types[0], false);
|
||||
}
|
||||
|
||||
return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() {
|
||||
@Override
|
||||
public ClassAndLoader run() {
|
||||
return getDefiningClassAndLoaderPrivileged(types);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) {
|
||||
final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types);
|
||||
|
||||
final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
|
||||
if(maximumVisibilityLoaders.size() == 1) {
|
||||
// Fortunate case - single maximally specific class loader; return its representative class.
|
||||
return it.next();
|
||||
}
|
||||
|
||||
// Ambiguity; throw an error.
|
||||
assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero
|
||||
final StringBuilder b = new StringBuilder();
|
||||
b.append(it.next().getRepresentativeClass().getCanonicalName());
|
||||
while(it.hasNext()) {
|
||||
b.append(", ").append(it.next().getRepresentativeClass().getCanonicalName());
|
||||
}
|
||||
throw typeError("extend.ambiguous.defining.class", b.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of types, return a subset of their class loaders that are maximal according to the
|
||||
* "can see other loaders' classes" relation, which is presumed to be a partial ordering.
|
||||
* @param types types
|
||||
* @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element.
|
||||
*/
|
||||
private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) {
|
||||
final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>();
|
||||
outer: for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) {
|
||||
final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
|
||||
while(it.hasNext()) {
|
||||
final ClassAndLoader existingMax = it.next();
|
||||
final boolean candidateSeesExisting = maxCandidate.canSee(existingMax);
|
||||
final boolean exitingSeesCandidate = existingMax.canSee(maxCandidate);
|
||||
if(candidateSeesExisting) {
|
||||
if(!exitingSeesCandidate) {
|
||||
// The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal.
|
||||
it.remove();
|
||||
}
|
||||
// NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do
|
||||
// about that one, as two distinct class loaders both seeing each other's classes is weird and
|
||||
// violates the assumption that the relation "sees others' classes" is a partial ordering. We'll
|
||||
// just not do anything, and treat them as incomparable; hopefully some later class loader that
|
||||
// comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and
|
||||
// throw an error at the end.
|
||||
} else if(exitingSeesCandidate) {
|
||||
// Existing sees the candidate, so drop the candidate.
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
// If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new
|
||||
// maximum.
|
||||
maximumVisibilityLoaders.add(maxCandidate);
|
||||
}
|
||||
return maximumVisibilityLoaders;
|
||||
}
|
||||
|
||||
private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) {
|
||||
final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>();
|
||||
for(final Class<?> c: types) {
|
||||
final ClassAndLoader cl = new ClassAndLoader(c, true);
|
||||
if(!classesAndLoaders.containsKey(cl)) {
|
||||
classesAndLoaders.put(cl, cl);
|
||||
}
|
||||
}
|
||||
return classesAndLoaders.keySet();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,884 @@
|
||||
/*
|
||||
* 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.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.POP;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
|
||||
/**
|
||||
* Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}.
|
||||
* </p><p>
|
||||
* For every protected or public constructor in the extended class, the adapter class will have between one to three
|
||||
* public constructors (visibility of protected constructors in the extended class is promoted to public).
|
||||
* <ul>
|
||||
* <li>In every case, a constructor taking a trailing ScriptObject argument preceded by original constructor arguments
|
||||
* is always created on the adapter class. When such a constructor is invoked, the passed ScriptObject's member
|
||||
* functions are used to implement and/or override methods on the original class, dispatched by name. A single
|
||||
* JavaScript function will act as the implementation for all overloaded methods of the same name. When methods on an
|
||||
* adapter instance are invoked, the functions are invoked having the ScriptObject passed in the instance constructor as
|
||||
* their "this". Subsequent changes to the ScriptObject (reassignment or removal of its functions) are not reflected in
|
||||
* the adapter instance; the method implementations are bound to functions at constructor invocation time.
|
||||
* {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The
|
||||
* only restriction is that since every JavaScript object already has a {@code toString} function through the
|
||||
* {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a
|
||||
* {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be
|
||||
* implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too.
|
||||
* </li>
|
||||
* <li>
|
||||
* If the original types collectively have only one abstract method, or have several of them, but all share the
|
||||
* same name, an additional constructor is provided for every original constructor; this one takes a ScriptFunction as
|
||||
* its last argument preceded by original constructor arguments. This constructor will use the passed function as the
|
||||
* implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method
|
||||
* name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is
|
||||
* invoked with {@code null} as its "this".
|
||||
* </li>
|
||||
* <li>
|
||||
* If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass
|
||||
* constructors are also created. These constructors simply delegate to the superclass constructor. They are used to
|
||||
* create instances of the adapter class with no instance-level overrides.
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p><p>
|
||||
* For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect
|
||||
* to coerce the JavaScript function return value to the expected Java return type.
|
||||
* </p><p>
|
||||
* Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be
|
||||
* declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The
|
||||
* reason we are passing the additional argument at the end of the argument list instead at the front is that the
|
||||
* source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses
|
||||
* to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>.
|
||||
* </p><p>
|
||||
* It is possible to create two different classes: those that can have both class-level and instance-level overrides,
|
||||
* and those that can only have instance-level overrides. When
|
||||
* {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked with non-null {@code classOverrides}
|
||||
* parameter, an adapter class is created that can have class-level overrides, and the passed script object will be used
|
||||
* as the implementations for its methods, just as in the above case of the constructor taking a script object. Note
|
||||
* that in the case of class-level overrides, a new adapter class is created on every invocation, and the implementation
|
||||
* object is bound to the class, not to any instance. All created instances will share these functions. Of course, when
|
||||
* instances of such a class are being created, they can still take another object (or possibly a function) in their
|
||||
* constructor's trailing position and thus provide further instance-specific overrides. The order of invocation is
|
||||
* always instance-specified method, then a class-specified method, and finally the superclass method.
|
||||
*/
|
||||
final class JavaAdapterBytecodeGenerator extends JavaAdapterGeneratorBase {
|
||||
private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class);
|
||||
private static final Type STRING_TYPE = Type.getType(String.class);
|
||||
private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
|
||||
private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
|
||||
private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
|
||||
OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
|
||||
private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
|
||||
SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
|
||||
private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
|
||||
private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
|
||||
private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
|
||||
private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class);
|
||||
|
||||
private static final String SERVICES_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterServices.class);
|
||||
private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName();
|
||||
private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class);
|
||||
private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName();
|
||||
private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName();
|
||||
|
||||
private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor();
|
||||
private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
|
||||
private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class));
|
||||
|
||||
// Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because
|
||||
// it's a java.* package.
|
||||
private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/internal/javaadapters/";
|
||||
// Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package.
|
||||
private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter";
|
||||
private static final String JAVA_PACKAGE_PREFIX = "java/";
|
||||
private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 238; //255 - 17; 17 is the maximum possible length for the global setter inner class suffix
|
||||
|
||||
private static final String CLASS_INIT = "<clinit>";
|
||||
private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal";
|
||||
|
||||
/**
|
||||
* Collection of methods we never override: Object.clone(), Object.finalize().
|
||||
*/
|
||||
private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods();
|
||||
|
||||
private static final Random random = new SecureRandom();
|
||||
|
||||
// This is the superclass for our generated adapter.
|
||||
private final Class<?> superClass;
|
||||
// Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class
|
||||
// loader that has the visibility of all original types (class to extend and interfaces to implement) and of the
|
||||
// Nashorn classes.
|
||||
private final ClassLoader commonLoader;
|
||||
// Is this a generator for the version of the class that can have overrides on the class level?
|
||||
private final boolean classOverride;
|
||||
// Binary name of the superClass
|
||||
private final String superClassName;
|
||||
// Binary name of the generated class.
|
||||
private final String generatedClassName;
|
||||
// Binary name of the PrivilegedAction inner class that is used to
|
||||
private final String globalSetterClassName;
|
||||
private final Set<String> usedFieldNames = new HashSet<>();
|
||||
private final Set<String> abstractMethodNames = new HashSet<>();
|
||||
private final String samName;
|
||||
private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
|
||||
private final Set<MethodInfo> methodInfos = new HashSet<>();
|
||||
private boolean autoConvertibleFromFunction = false;
|
||||
|
||||
private final ClassWriter cw;
|
||||
|
||||
/**
|
||||
* Creates a generator for the bytecode for the adapter for the specified superclass and interfaces.
|
||||
* @param superClass the superclass the adapter will extend.
|
||||
* @param interfaces the interfaces the adapter will implement.
|
||||
* @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes.
|
||||
* @param classOverride true to generate the bytecode for the adapter that has both class-level and instance-level
|
||||
* overrides, false to generate the bytecode for the adapter that only has instance-level overrides.
|
||||
* @throws AdaptationException if the adapter can not be generated for some reason.
|
||||
*/
|
||||
JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces,
|
||||
final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException {
|
||||
assert superClass != null && !superClass.isInterface();
|
||||
assert interfaces != null;
|
||||
|
||||
this.superClass = superClass;
|
||||
this.classOverride = classOverride;
|
||||
this.commonLoader = commonLoader;
|
||||
cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
protected String getCommonSuperClass(final String type1, final String type2) {
|
||||
// We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class
|
||||
// loader to find the common superclass of two types when needed.
|
||||
return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2);
|
||||
}
|
||||
};
|
||||
superClassName = Type.getInternalName(superClass);
|
||||
generatedClassName = getGeneratedClassName(superClass, interfaces);
|
||||
|
||||
// Randomize the name of the privileged global setter, to make it non-feasible to find.
|
||||
final long l;
|
||||
synchronized(random) {
|
||||
l = random.nextLong();
|
||||
}
|
||||
|
||||
// NOTE: they way this class name is calculated affects the value of MAX_GENERATED_TYPE_NAME_LENGTH constant. If
|
||||
// you change the calculation of globalSetterClassName, adjust the constant too.
|
||||
globalSetterClassName = generatedClassName.concat("$" + Long.toHexString(l & Long.MAX_VALUE));
|
||||
cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
|
||||
|
||||
generateGlobalFields();
|
||||
|
||||
gatherMethods(superClass);
|
||||
gatherMethods(interfaces);
|
||||
samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null;
|
||||
generateHandleFields();
|
||||
if(classOverride) {
|
||||
generateClassInit();
|
||||
}
|
||||
generateConstructors();
|
||||
generateMethods();
|
||||
// }
|
||||
cw.visitEnd();
|
||||
}
|
||||
|
||||
private void generateGlobalFields() {
|
||||
cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
|
||||
usedFieldNames.add(GLOBAL_FIELD_NAME);
|
||||
if(classOverride) {
|
||||
cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
|
||||
usedFieldNames.add(STATIC_GLOBAL_FIELD_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
JavaAdapterClassLoader createAdapterClassLoader() {
|
||||
return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray(), globalSetterClassName);
|
||||
}
|
||||
|
||||
boolean isAutoConvertibleFromFunction() {
|
||||
return autoConvertibleFromFunction;
|
||||
}
|
||||
|
||||
private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) {
|
||||
// The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're
|
||||
// just implementing interfaces or extending Object), then the first implemented interface or Object.
|
||||
final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType;
|
||||
final Package pkg = namingType.getPackage();
|
||||
final String namingTypeName = Type.getInternalName(namingType);
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) {
|
||||
// Can't define new classes in java.* packages
|
||||
buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName);
|
||||
} else {
|
||||
buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX);
|
||||
}
|
||||
final Iterator<Class<?>> it = interfaces.iterator();
|
||||
if(superType == Object.class && it.hasNext()) {
|
||||
it.next(); // Skip first interface, it was used to primarily name the adapter
|
||||
}
|
||||
// Append interface names to the adapter name
|
||||
while(it.hasNext()) {
|
||||
buf.append("$$").append(it.next().getSimpleName());
|
||||
}
|
||||
return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of class objects, return an array with their binary names. Used to generate the array of interface
|
||||
* names to implement.
|
||||
* @param classes the classes
|
||||
* @return an array of names
|
||||
*/
|
||||
private static String[] getInternalTypeNames(final List<Class<?>> classes) {
|
||||
final int interfaceCount = classes.size();
|
||||
final String[] interfaceNames = new String[interfaceCount];
|
||||
for(int i = 0; i < interfaceCount; ++i) {
|
||||
interfaceNames[i] = Type.getInternalName(classes.get(i));
|
||||
}
|
||||
return interfaceNames;
|
||||
}
|
||||
|
||||
private void generateHandleFields() {
|
||||
for (final MethodInfo mi: methodInfos) {
|
||||
cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
|
||||
if(classOverride) {
|
||||
cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateClassInit() {
|
||||
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT,
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE), null, null));
|
||||
|
||||
mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR);
|
||||
// Assign MethodHandle fields through invoking getHandle()
|
||||
for (final MethodInfo mi : methodInfos) {
|
||||
mv.dup();
|
||||
mv.aconst(mi.getName());
|
||||
mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
|
||||
mv.iconst(mi.method.isVarArgs() ? 1 : 0);
|
||||
mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR);
|
||||
mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
|
||||
}
|
||||
|
||||
// Assign "staticGlobal = Context.getGlobal()"
|
||||
invokeGetGlobalWithNullCheck(mv);
|
||||
mv.putstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
|
||||
|
||||
endInitMethod(mv);
|
||||
}
|
||||
|
||||
private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) {
|
||||
invokeGetGlobal(mv);
|
||||
mv.dup();
|
||||
mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context
|
||||
mv.pop();
|
||||
}
|
||||
|
||||
private void generateConstructors() throws AdaptationException {
|
||||
boolean gotCtor = false;
|
||||
for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) {
|
||||
final int modifier = ctor.getModifiers();
|
||||
if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
|
||||
generateConstructors(ctor);
|
||||
gotCtor = true;
|
||||
}
|
||||
}
|
||||
if(!gotCtor) {
|
||||
throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
private void generateConstructors(final Constructor<?> ctor) {
|
||||
if(classOverride) {
|
||||
// Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want
|
||||
// to create instances without further per-instance overrides.
|
||||
generateDelegatingConstructor(ctor);
|
||||
}
|
||||
|
||||
// Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
|
||||
// beginning of its parameter list.
|
||||
generateOverridingConstructor(ctor, false);
|
||||
|
||||
if (samName != null) {
|
||||
if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
|
||||
// If the original type only has a single abstract method name, as well as a default ctor, then it can
|
||||
// be automatically converted from JS function.
|
||||
autoConvertibleFromFunction = true;
|
||||
}
|
||||
// If all our abstract methods have a single name, generate an additional constructor, one that takes a
|
||||
// ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
|
||||
generateOverridingConstructor(ctor, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDelegatingConstructor(final Constructor<?> ctor) {
|
||||
final Type originalCtorType = Type.getType(ctor);
|
||||
final Type[] argTypes = originalCtorType.getArgumentTypes();
|
||||
|
||||
// All constructors must be public, even if in the superclass they were protected.
|
||||
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
|
||||
Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null));
|
||||
|
||||
mv.visitCode();
|
||||
// Invoke super constructor with the same arguments.
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
int offset = 1; // First arg is at position 1, after this.
|
||||
for (Type argType: argTypes) {
|
||||
mv.load(offset, argType);
|
||||
offset += argType.getSize();
|
||||
}
|
||||
mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
|
||||
|
||||
endInitMethod(mv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a constructor for the adapter class. This constructor will take the same arguments as the supertype
|
||||
* constructor passed as the argument here, and delegate to it. However, it will take an additional argument of
|
||||
* either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize
|
||||
* all the method handle fields of the adapter instance with functions from the script object (or the script
|
||||
* function itself, if that's what's passed). There is one method handle field in the adapter class for every method
|
||||
* that can be implemented or overridden; the name of every field is same as the name of the method, with a number
|
||||
* suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
|
||||
* {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
|
||||
* boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
|
||||
* adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
|
||||
* The constructor that takes a script function will only initialize the methods with the same name as the single
|
||||
* abstract method. The constructor will also store the Nashorn global that was current at the constructor
|
||||
* invocation time in a field named "global". The generated constructor will be public, regardless of whether the
|
||||
* supertype constructor was public or protected. The generated constructor will not be variable arity, even if the
|
||||
* supertype constructor was.
|
||||
* @param ctor the supertype constructor that is serving as the base for the generated constructor.
|
||||
* @param fromFunction true if we're generating a constructor that initializes SAM types from a single
|
||||
* ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a
|
||||
* ScriptObject passed to it.
|
||||
*/
|
||||
private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) {
|
||||
final Type originalCtorType = Type.getType(ctor);
|
||||
final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
|
||||
final int argLen = originalArgTypes.length;
|
||||
final Type[] newArgTypes = new Type[argLen + 1];
|
||||
|
||||
// Insert ScriptFunction|Object as the last argument to the constructor
|
||||
final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE;
|
||||
newArgTypes[argLen] = extraArgumentType;
|
||||
System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
|
||||
|
||||
// All constructors must be public, even if in the superclass they were protected.
|
||||
// Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...).
|
||||
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
|
||||
Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
|
||||
|
||||
mv.visitCode();
|
||||
// First, invoke super constructor with original arguments. If the form of the constructor we're generating is
|
||||
// <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...).
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
final Class<?>[] argTypes = ctor.getParameterTypes();
|
||||
int offset = 1; // First arg is at position 1, after this.
|
||||
for (int i = 0; i < argLen; ++i) {
|
||||
final Type argType = Type.getType(argTypes[i]);
|
||||
mv.load(offset, argType);
|
||||
offset += argType.getSize();
|
||||
}
|
||||
mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
|
||||
|
||||
// Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method.
|
||||
final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR;
|
||||
|
||||
// Assign MethodHandle fields through invoking getHandle()
|
||||
for (final MethodInfo mi : methodInfos) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
if (fromFunction && !mi.getName().equals(samName)) {
|
||||
// Constructors initializing from a ScriptFunction only initialize methods with the SAM name.
|
||||
// NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This
|
||||
// is a deliberate design choice. All other method handles are initialized to null.
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, offset);
|
||||
if(!fromFunction) {
|
||||
mv.aconst(mi.getName());
|
||||
}
|
||||
mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
|
||||
mv.iconst(mi.method.isVarArgs() ? 1 : 0);
|
||||
mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
|
||||
}
|
||||
mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
|
||||
}
|
||||
|
||||
// Assign "this.global = Context.getGlobal()"
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
invokeGetGlobalWithNullCheck(mv);
|
||||
mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
|
||||
|
||||
endInitMethod(mv);
|
||||
}
|
||||
|
||||
private static void endInitMethod(final InstructionAdapter mv) {
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private static void invokeGetGlobal(final InstructionAdapter mv) {
|
||||
mv.invokestatic(CONTEXT_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR);
|
||||
}
|
||||
|
||||
private void invokeSetGlobal(final InstructionAdapter mv) {
|
||||
mv.invokestatic(globalSetterClassName, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the
|
||||
* reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the
|
||||
* method handle serving as the implementation of this method in adapter instances.
|
||||
*
|
||||
*/
|
||||
private static class MethodInfo {
|
||||
private final Method method;
|
||||
private final MethodType type;
|
||||
private String methodHandleInstanceFieldName;
|
||||
private String methodHandleClassFieldName;
|
||||
|
||||
private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException {
|
||||
this(clazz.getDeclaredMethod(name, argTypes));
|
||||
}
|
||||
|
||||
private MethodInfo(final Method method) {
|
||||
this.method = method;
|
||||
this.type = MH.type(method.getReturnType(), method.getParameterTypes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return obj instanceof MethodInfo && equals((MethodInfo)obj);
|
||||
}
|
||||
|
||||
private boolean equals(final MethodInfo other) {
|
||||
// Only method name and type are used for comparison; method handle field name is not.
|
||||
return getName().equals(other.getName()) && type.equals(other.type);
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return method.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getName().hashCode() ^ type.hashCode();
|
||||
}
|
||||
|
||||
void setIsCanonical(final Set<String> usedFieldNames, boolean classOverride) {
|
||||
methodHandleInstanceFieldName = nextName(usedFieldNames);
|
||||
if(classOverride) {
|
||||
methodHandleClassFieldName = nextName(usedFieldNames);
|
||||
}
|
||||
}
|
||||
|
||||
String nextName(final Set<String> usedFieldNames) {
|
||||
int i = 0;
|
||||
final String name = getName();
|
||||
String nextName = name;
|
||||
while (!usedFieldNames.add(nextName)) {
|
||||
final String ordinal = String.valueOf(i++);
|
||||
final int maxNameLen = 255 - ordinal.length();
|
||||
nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal);
|
||||
}
|
||||
return nextName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void generateMethods() {
|
||||
for(final MethodInfo mi: methodInfos) {
|
||||
generateMethod(mi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a method in the adapter class that adapts a method from the original class. The generated methods will
|
||||
* inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation
|
||||
* for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an
|
||||
* {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is
|
||||
* invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the
|
||||
* current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter
|
||||
* instance, the creating global is set to be the current global. In this case, the previously current global is
|
||||
* restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared
|
||||
* exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
|
||||
* exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
|
||||
* the method; this is guaranteed by the way constructors of the adapter class obtain them using
|
||||
* {@link #getHandle(Object, String, MethodType, boolean)}.
|
||||
* @param mi the method info describing the method to be generated.
|
||||
*/
|
||||
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 MethodType type = mi.type;
|
||||
final String methodDesc = type.toMethodDescriptorString();
|
||||
final String name = mi.getName();
|
||||
|
||||
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));
|
||||
mv.visitCode();
|
||||
|
||||
final Label instanceHandleDefined = new Label();
|
||||
final Label classHandleDefined = new Label();
|
||||
|
||||
final Type asmReturnType = Type.getType(type.returnType());
|
||||
|
||||
// See if we have instance handle defined
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.getfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
|
||||
// stack: [instanceHandle]
|
||||
jumpIfNonNullKeepOperand(mv, instanceHandleDefined);
|
||||
|
||||
if(classOverride) {
|
||||
// See if we have the static handle
|
||||
mv.getstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
|
||||
// stack: [classHandle]
|
||||
jumpIfNonNullKeepOperand(mv, classHandleDefined);
|
||||
}
|
||||
|
||||
// No handle is available, fall back to default behavior
|
||||
if(Modifier.isAbstract(mod)) {
|
||||
// If the super method is abstract, throw an exception
|
||||
mv.anew(UNSUPPORTED_OPERATION_TYPE);
|
||||
mv.dup();
|
||||
mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR);
|
||||
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);
|
||||
}
|
||||
|
||||
final Label setupGlobal = new Label();
|
||||
|
||||
if(classOverride) {
|
||||
mv.visitLabel(classHandleDefined);
|
||||
// If class handle is defined, load the static defining global
|
||||
mv.getstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
|
||||
// stack: [creatingGlobal := classGlobal, classHandle]
|
||||
mv.goTo(setupGlobal);
|
||||
}
|
||||
|
||||
mv.visitLabel(instanceHandleDefined);
|
||||
// If instance handle is defined, load the instance defining global
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
|
||||
// stack: [creatingGlobal := instanceGlobal, instanceHandle]
|
||||
|
||||
// fallthrough to setupGlobal
|
||||
|
||||
// stack: [creatingGlobal, someHandle]
|
||||
mv.visitLabel(setupGlobal);
|
||||
|
||||
final int currentGlobalVar = nextLocalVar++;
|
||||
final int globalsDifferVar = nextLocalVar++;
|
||||
|
||||
mv.dup();
|
||||
// stack: [creatingGlobal, creatingGlobal, someHandle]
|
||||
|
||||
// Emit code for switching to the creating global
|
||||
// ScriptObject currentGlobal = Context.getGlobal();
|
||||
invokeGetGlobal(mv);
|
||||
mv.dup();
|
||||
mv.visitVarInsn(ASTORE, currentGlobalVar);
|
||||
// stack: [currentGlobal, creatingGlobal, creatingGlobal, someHandle]
|
||||
// if(definingGlobal == currentGlobal) {
|
||||
final Label globalsDiffer = new Label();
|
||||
mv.ifacmpne(globalsDiffer);
|
||||
// stack: [someGlobal, someHandle]
|
||||
// globalsDiffer = false
|
||||
mv.pop();
|
||||
// stack: [someHandle]
|
||||
mv.iconst(0); // false
|
||||
// stack: [false, someHandle]
|
||||
final Label invokeHandle = new Label();
|
||||
mv.goTo(invokeHandle);
|
||||
mv.visitLabel(globalsDiffer);
|
||||
// } else {
|
||||
// Context.setGlobal(definingGlobal);
|
||||
// stack: [someGlobal, someHandle]
|
||||
invokeSetGlobal(mv);
|
||||
// stack: [someHandle]
|
||||
// globalsDiffer = true
|
||||
mv.iconst(1);
|
||||
// stack: [true, someHandle]
|
||||
|
||||
mv.visitLabel(invokeHandle);
|
||||
mv.visitVarInsn(ISTORE, globalsDifferVar);
|
||||
// stack: [someHandle]
|
||||
|
||||
// Load all parameters back on stack for dynamic invocation.
|
||||
int varOffset = 1;
|
||||
for (final Type t : asmArgTypes) {
|
||||
mv.load(varOffset, t);
|
||||
varOffset += t.getSize();
|
||||
}
|
||||
|
||||
// Invoke the target method handle
|
||||
final Label tryBlockStart = new Label();
|
||||
mv.visitLabel(tryBlockStart);
|
||||
mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString());
|
||||
final Label tryBlockEnd = new Label();
|
||||
mv.visitLabel(tryBlockEnd);
|
||||
emitFinally(mv, currentGlobalVar, globalsDifferVar);
|
||||
mv.areturn(asmReturnType);
|
||||
|
||||
// If Throwable is not declared, we need an adapter from Throwable to RuntimeException
|
||||
final boolean throwableDeclared = isThrowableDeclared(exceptions);
|
||||
final Label throwableHandler;
|
||||
if (!throwableDeclared) {
|
||||
// Add "throw new RuntimeException(Throwable)" handler for Throwable
|
||||
throwableHandler = new Label();
|
||||
mv.visitLabel(throwableHandler);
|
||||
mv.anew(RUNTIME_EXCEPTION_TYPE);
|
||||
mv.dupX1();
|
||||
mv.swap();
|
||||
mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE));
|
||||
// Fall through to rethrow handler
|
||||
} else {
|
||||
throwableHandler = null;
|
||||
}
|
||||
final Label rethrowHandler = new Label();
|
||||
mv.visitLabel(rethrowHandler);
|
||||
// Rethrow handler for RuntimeException, Error, and all declared exception types
|
||||
emitFinally(mv, currentGlobalVar, globalsDifferVar);
|
||||
mv.athrow();
|
||||
final Label methodEnd = new Label();
|
||||
mv.visitLabel(methodEnd);
|
||||
|
||||
mv.visitLocalVariable("currentGlobal", SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd, currentGlobalVar);
|
||||
mv.visitLocalVariable("globalsDiffer", Type.INT_TYPE.getDescriptor(), null, setupGlobal, methodEnd, globalsDifferVar);
|
||||
|
||||
if(throwableDeclared) {
|
||||
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
|
||||
assert throwableHandler == null;
|
||||
} else {
|
||||
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
|
||||
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
|
||||
for(final String excName: exceptionNames) {
|
||||
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
|
||||
}
|
||||
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
|
||||
}
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits code for jumping to a label if the top stack operand is not null. The operand is kept on the stack if it
|
||||
* is not null (so is available to code at the jump address) and is popped if it is null.
|
||||
* @param mv the instruction adapter being used to emit code
|
||||
* @param label the label to jump to
|
||||
*/
|
||||
private static void jumpIfNonNullKeepOperand(final InstructionAdapter mv, final Label label) {
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitJumpInsn(IFNONNULL, label);
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit code to restore the previous Nashorn Context when needed.
|
||||
* @param mv the instruction adapter
|
||||
* @param currentGlobalVar index of the local variable holding the reference to the current global at method
|
||||
* entry.
|
||||
* @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored.
|
||||
*/
|
||||
private void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) {
|
||||
// Emit code to restore the previous Nashorn global if needed
|
||||
mv.visitVarInsn(ILOAD, globalsDifferVar);
|
||||
final Label skip = new Label();
|
||||
mv.ifeq(skip);
|
||||
mv.visitVarInsn(ALOAD, currentGlobalVar);
|
||||
invokeSetGlobal(mv);
|
||||
mv.visitLabel(skip);
|
||||
}
|
||||
|
||||
private static boolean isThrowableDeclared(final Class<?>[] exceptions) {
|
||||
for (final Class<?> exception : exceptions) {
|
||||
if (exception == Throwable.class) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its
|
||||
* superclass and the interfaces it implements, and add further methods that were not directly declared on the
|
||||
* class.
|
||||
* @param type the type defining the methods.
|
||||
*/
|
||||
private void gatherMethods(final Class<?> type) {
|
||||
if (Modifier.isPublic(type.getModifiers())) {
|
||||
final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
|
||||
|
||||
for (final Method typeMethod: typeMethods) {
|
||||
final int m = typeMethod.getModifiers();
|
||||
if (Modifier.isStatic(m)) {
|
||||
continue;
|
||||
}
|
||||
if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
|
||||
final MethodInfo mi = new MethodInfo(typeMethod);
|
||||
if (Modifier.isFinal(m)) {
|
||||
finalMethods.add(mi);
|
||||
} else if (!finalMethods.contains(mi) && methodInfos.add(mi)) {
|
||||
if (Modifier.isAbstract(m)) {
|
||||
abstractMethodNames.add(mi.getName());
|
||||
}
|
||||
mi.setIsCanonical(usedFieldNames, classOverride);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done.
|
||||
// Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to
|
||||
// see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a
|
||||
// superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and
|
||||
// getMethods() does provide those declared in a superinterface.
|
||||
if (!type.isInterface()) {
|
||||
final Class<?> superType = type.getSuperclass();
|
||||
if (superType != null) {
|
||||
gatherMethods(superType);
|
||||
}
|
||||
for (final Class<?> itf: type.getInterfaces()) {
|
||||
gatherMethods(itf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void gatherMethods(final List<Class<?>> classes) {
|
||||
for(final Class<?> c: classes) {
|
||||
gatherMethods(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters,
|
||||
* as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and
|
||||
* {@code Object.clone()}.
|
||||
* @return a collection of method infos representing those methods that we never override in adapter classes.
|
||||
*/
|
||||
private static Collection<MethodInfo> getExcludedMethods() {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() {
|
||||
@Override
|
||||
public Collection<MethodInfo> run() {
|
||||
try {
|
||||
return Arrays.asList(
|
||||
new MethodInfo(Object.class, "finalize"),
|
||||
new MethodInfo(Object.class, "clone"));
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getCommonSuperClass(final String type1, final String type2) {
|
||||
try {
|
||||
final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
|
||||
final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
|
||||
if (c1.isAssignableFrom(c2)) {
|
||||
return type1;
|
||||
}
|
||||
if (c2.isAssignableFrom(c1)) {
|
||||
return type2;
|
||||
}
|
||||
if (c1.isInterface() || c2.isInterface()) {
|
||||
return OBJECT_TYPE_NAME;
|
||||
}
|
||||
return assignableSuperClass(c1, c2).getName().replace('.', '/');
|
||||
} catch(final ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) {
|
||||
final Class<?> superClass = c1.getSuperclass();
|
||||
return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.AllPermission;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.CodeSource;
|
||||
import java.security.Permissions;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.security.SecureClassLoader;
|
||||
import jdk.internal.dynalink.beans.StaticClass;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
|
||||
/**
|
||||
* This class encapsulates the bytecode of the adapter class and can be used to load it into the JVM as an actual Class.
|
||||
* It can be invoked repeatedly to create multiple adapter classes from the same bytecode; adapter classes that have
|
||||
* class-level overrides must be re-created for every set of such overrides. Note that while this class is named
|
||||
* "class loader", it does not, in fact, extend {@code ClassLoader}, but rather uses them internally. Instances of this
|
||||
* class are normally created by {@link JavaAdapterBytecodeGenerator}.
|
||||
*/
|
||||
class JavaAdapterClassLoader extends JavaAdapterGeneratorBase {
|
||||
private static final Type PRIVILEGED_ACTION_TYPE = Type.getType(PrivilegedAction.class);
|
||||
|
||||
private static final String PRIVILEGED_ACTION_TYPE_NAME = PRIVILEGED_ACTION_TYPE.getInternalName();
|
||||
private static final String PRIVILEGED_RUN_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
|
||||
|
||||
private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain();
|
||||
|
||||
private final String className;
|
||||
private final byte[] classBytes;
|
||||
private final String globalSetterClassName;
|
||||
|
||||
JavaAdapterClassLoader(String className, byte[] classBytes, String globalSetterClassName) {
|
||||
this.className = className.replace('/', '.');
|
||||
this.classBytes = classBytes;
|
||||
this.globalSetterClassName = globalSetterClassName.replace('/', '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the generated adapter class into the JVM.
|
||||
* @param parentLoader the parent class loader for the generated class loader
|
||||
* @return the generated adapter class
|
||||
*/
|
||||
StaticClass generateClass(final ClassLoader parentLoader) {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<StaticClass>() {
|
||||
@Override
|
||||
public StaticClass run() {
|
||||
try {
|
||||
return StaticClass.forClass(Class.forName(className, true, createClassLoader(parentLoader)));
|
||||
} catch (final ClassNotFoundException e) {
|
||||
throw new AssertionError(e); // cannot happen
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class AdapterLoader extends SecureClassLoader {
|
||||
AdapterLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isAdapterClass(Class<?> clazz) {
|
||||
return clazz.getClassLoader() instanceof AdapterLoader;
|
||||
}
|
||||
|
||||
// Note that the adapter class is created in the protection domain of the class/interface being
|
||||
// extended/implemented, and only the privileged global setter action class is generated in the protection domain
|
||||
// of Nashorn itself. Also note that the creation and loading of the global setter is deferred until it is
|
||||
// required by JVM linker, which will only happen on first invocation of any of the adapted method. We could defer
|
||||
// it even more by separating its invocation into a separate static method on the adapter class, but then someone
|
||||
// with ability to introspect on the class and use setAccessible(true) on it could invoke the method. It's a
|
||||
// security tradeoff...
|
||||
private ClassLoader createClassLoader(final ClassLoader parentLoader) {
|
||||
return new AdapterLoader(parentLoader) {
|
||||
private final ClassLoader myLoader = getClass().getClassLoader();
|
||||
private final ProtectionDomain myProtectionDomain = getClass().getProtectionDomain();
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
|
||||
try {
|
||||
return super.loadClass(name, resolve);
|
||||
} catch (final SecurityException se) {
|
||||
// we may be implementing an interface or extending a class that was
|
||||
// loaded by a loader that prevents package.access. If so, it'd throw
|
||||
// SecurityException for nashorn's classes!. For adapter's to work, we
|
||||
// should be able to refer to nashorn classes.
|
||||
if (name.startsWith("jdk.nashorn.internal.")) {
|
||||
return myLoader.loadClass(name);
|
||||
}
|
||||
throw se;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(final String name) throws ClassNotFoundException {
|
||||
if(name.equals(className)) {
|
||||
return defineClass(name, classBytes, 0, classBytes.length, GENERATED_PROTECTION_DOMAIN);
|
||||
} else if(name.equals(globalSetterClassName)) {
|
||||
final byte[] bytes = generatePrivilegedActionClassBytes(globalSetterClassName.replace('.', '/'));
|
||||
return defineClass(name, bytes, 0, bytes.length, myProtectionDomain);
|
||||
} else {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ProtectionDomain createGeneratedProtectionDomain() {
|
||||
// Generated classes need to have AllPermission. Since we require the "createClassLoader" RuntimePermission, we
|
||||
// can create a class loader that'll load new classes with any permissions. Our generated classes are just
|
||||
// delegating adapters, so having AllPermission can't cause anything wrong; the effective set of permissions for
|
||||
// the executing script functions will still be limited by the permissions of the caller and the permissions of
|
||||
// the script.
|
||||
final Permissions permissions = new Permissions();
|
||||
permissions.add(new AllPermission());
|
||||
return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a PrivilegedAction implementation class for invoking {@link Context#setGlobal(ScriptObject)} from the
|
||||
* adapter class.
|
||||
*/
|
||||
private static byte[] generatePrivilegedActionClassBytes(final String className) {
|
||||
final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
// class GlobalSetter implements PrivilegedAction {
|
||||
w.visit(Opcodes.V1_7, ACC_SUPER | ACC_FINAL, className, null, OBJECT_TYPE_NAME, new String[] {
|
||||
PRIVILEGED_ACTION_TYPE_NAME
|
||||
});
|
||||
|
||||
// private final ScriptObject global;
|
||||
w.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
|
||||
|
||||
// private GlobalSetter(ScriptObject global) {
|
||||
InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PRIVATE, INIT,
|
||||
SET_GLOBAL_METHOD_DESCRIPTOR, null, new String[0]));
|
||||
mv.visitCode();
|
||||
// super();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.invokespecial(OBJECT_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR);
|
||||
// this.global = global;
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.putfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
|
||||
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitEnd();
|
||||
mv.visitMaxs(0, 0);
|
||||
|
||||
// public Object run() {
|
||||
mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC, "run", PRIVILEGED_RUN_METHOD_DESCRIPTOR, null,
|
||||
new String[0]));
|
||||
mv.visitCode();
|
||||
// Context.setGlobal(this.global);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.getfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
|
||||
mv.invokestatic(CONTEXT_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
|
||||
// return null;
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
mv.visitInsn(ARETURN);
|
||||
|
||||
mv.visitEnd();
|
||||
mv.visitMaxs(0, 0);
|
||||
|
||||
// static void setGlobal(ScriptObject global) {
|
||||
mv = new InstructionAdapter(w.visitMethod(ACC_STATIC, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, null,
|
||||
new String[0]));
|
||||
mv.visitCode();
|
||||
// new GlobalSetter(ScriptObject global)
|
||||
mv.anew(Type.getType("L" + className + ";"));
|
||||
mv.dup();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.invokespecial(className, INIT, SET_GLOBAL_METHOD_DESCRIPTOR);
|
||||
// AccessController.doPrivileged(...)
|
||||
mv.invokestatic(Type.getInternalName(AccessController.class), "doPrivileged", Type.getMethodDescriptor(
|
||||
OBJECT_TYPE, PRIVILEGED_ACTION_TYPE));
|
||||
mv.pop();
|
||||
mv.visitInsn(RETURN);
|
||||
|
||||
mv.visitEnd();
|
||||
mv.visitMaxs(0, 0);
|
||||
|
||||
return w.toByteArray();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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. 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 jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
|
||||
/**
|
||||
* Base class for both {@link JavaAdapterBytecodeGenerator} and {@link JavaAdapterClassLoader}, containing those
|
||||
* bytecode types, type names and method descriptor that are used by both.
|
||||
*/
|
||||
abstract class JavaAdapterGeneratorBase {
|
||||
static final Type CONTEXT_TYPE = Type.getType(Context.class);
|
||||
static final Type OBJECT_TYPE = Type.getType(Object.class);
|
||||
static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
|
||||
|
||||
static final String CONTEXT_TYPE_NAME = CONTEXT_TYPE.getInternalName();
|
||||
static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName();
|
||||
|
||||
static final String INIT = "<init>";
|
||||
|
||||
static final String GLOBAL_FIELD_NAME = "global";
|
||||
|
||||
static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor();
|
||||
|
||||
static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, SCRIPT_OBJECT_TYPE);
|
||||
static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
|
||||
protected JavaAdapterGeneratorBase() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.runtime.ECMAErrors.typeError;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.Undefined;
|
||||
|
||||
/**
|
||||
* Provides static utility services to generated Java adapter classes.
|
||||
*/
|
||||
public class JavaAdapterServices {
|
||||
private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>();
|
||||
|
||||
private JavaAdapterServices() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity
|
||||
* to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes
|
||||
* can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method
|
||||
* handles for their abstract method implementations.
|
||||
* @param fn the script function
|
||||
* @param type the method type it has to conform to
|
||||
* @param varArg if the Java method for which the function is being adapted is a variable arity method
|
||||
* @return the appropriately adapted method handle for invoking the script function.
|
||||
*/
|
||||
public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type, final boolean varArg) {
|
||||
// JS "this" will be null for SAMs
|
||||
return adaptHandle(fn.getBoundInvokeHandle(null), type, varArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and
|
||||
* adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly
|
||||
* for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object
|
||||
* in its first argument to obtain the method handles for their method implementations.
|
||||
* @param obj the script obj
|
||||
* @param name the name of the property that contains the function
|
||||
* @param type the method type it has to conform to
|
||||
* @param varArg if the Java method for which the function is being adapted is a variable arity method
|
||||
* @return the appropriately adapted method handle for invoking the script function, or null if the value of the
|
||||
* property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
|
||||
* define it but just inherits it through prototype.
|
||||
*/
|
||||
public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) {
|
||||
if (! (obj instanceof ScriptObject)) {
|
||||
throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
|
||||
}
|
||||
|
||||
final ScriptObject sobj = (ScriptObject)obj;
|
||||
// Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified
|
||||
if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Object fnObj = sobj.get(name);
|
||||
if (fnObj instanceof ScriptFunction) {
|
||||
return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg);
|
||||
} else if(fnObj == null || fnObj instanceof Undefined) {
|
||||
return null;
|
||||
} else {
|
||||
throw typeError("not.a.function", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a thread-local JS object used to define methods for the adapter class being initialized on the current
|
||||
* thread. This method is public solely for implementation reasons, so the adapter classes can invoke it from their
|
||||
* static initializers.
|
||||
* @return the thread-local JS object used to define methods for the class being initialized.
|
||||
*/
|
||||
public static ScriptObject getClassOverrides() {
|
||||
final ScriptObject overrides = classOverrides.get();
|
||||
assert overrides != null;
|
||||
return overrides;
|
||||
}
|
||||
|
||||
static void setClassOverrides(ScriptObject overrides) {
|
||||
classOverrides.set(overrides);
|
||||
}
|
||||
|
||||
private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type, final boolean varArg) {
|
||||
return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, varArg), type);
|
||||
}
|
||||
}
|
||||
@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Modifier;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.ConversionComparator;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
@ -131,10 +132,22 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
|
||||
}
|
||||
|
||||
private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
|
||||
return JavaAdapterFactory.isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
|
||||
return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
|
||||
JavaAdapterFactory.isAutoConvertibleFromFunction(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method used by few other places in the code. Tests if the class has the abstract modifier and is not an
|
||||
* array class. For some reason, array classes have the abstract modifier set in HotSpot JVM, and we don't want to
|
||||
* treat array classes as abstract.
|
||||
* @param clazz the inspected class
|
||||
* @return true if the class is abstract and is not an array type.
|
||||
*/
|
||||
static boolean isAbstractClass(final Class<?> clazz) {
|
||||
return Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
|
||||
if(ScriptObject.class.isAssignableFrom(sourceType)) {
|
||||
|
||||
@ -68,10 +68,10 @@ final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
|
||||
final Class<?> receiverClass = ((StaticClass) self).getRepresentedClass();
|
||||
// Is the class abstract? (This includes interfaces.)
|
||||
if (JavaAdapterFactory.isAbstractClass(receiverClass)) {
|
||||
if (NashornLinker.isAbstractClass(receiverClass)) {
|
||||
// Change this link request into a link request on the adapter class.
|
||||
final Object[] args = request.getArguments();
|
||||
args[0] = JavaAdapterFactory.getAdapterClassFor(new Class<?>[] { receiverClass });
|
||||
args[0] = JavaAdapterFactory.getAdapterClassFor(new Class<?>[] { receiverClass }, null);
|
||||
final LinkRequest adapterRequest = request.replaceArguments(request.getCallSiteDescriptor(), args);
|
||||
final GuardedInvocation gi = checkNullConstructor(delegate(linkerServices, adapterRequest), receiverClass);
|
||||
// Finally, modify the guard to test for the original abstract class.
|
||||
|
||||
@ -26,11 +26,10 @@
|
||||
package jdk.nashorn.internal.runtime.regexp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import jdk.nashorn.internal.parser.Lexer;
|
||||
@ -58,7 +57,7 @@ final class RegExpScanner extends Scanner {
|
||||
private final List<Capture> caps = new LinkedList<>();
|
||||
|
||||
/** Forward references to capturing parenthesis to be resolved later.*/
|
||||
private final Set<Integer> forwardReferences = new LinkedHashSet<>();
|
||||
private final LinkedList<Integer> forwardReferences = new LinkedList<>();
|
||||
|
||||
/** Current level of zero-width negative lookahead assertions. */
|
||||
private int negativeLookaheadLevel;
|
||||
@ -104,10 +103,20 @@ final class RegExpScanner extends Scanner {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Integer ref : forwardReferences) {
|
||||
if (ref.intValue() > caps.size()) {
|
||||
neverMatches = true;
|
||||
break;
|
||||
Iterator<Integer> iterator = forwardReferences.descendingIterator();
|
||||
while (iterator.hasNext()) {
|
||||
final int pos = iterator.next();
|
||||
final int num = iterator.next();
|
||||
if (num > caps.size()) {
|
||||
// Non-existing reference should never match, if smaller than 8 convert to octal escape
|
||||
// to be compatible with other engines.
|
||||
if (num < 8) {
|
||||
String escape = "\\x0" + num;
|
||||
sb.insert(pos, escape);
|
||||
} else {
|
||||
neverMatches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,6 +411,10 @@ final class RegExpScanner extends Scanner {
|
||||
if (ch0 == '}') {
|
||||
pop('}');
|
||||
commit(1);
|
||||
} else {
|
||||
// Bad quantifier should be rejected but is accepted by all major engines
|
||||
restart(startIn, startOut);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -637,7 +650,16 @@ final class RegExpScanner extends Scanner {
|
||||
throw new RuntimeException("\\ at end of pattern"); // will be converted to PatternSyntaxException
|
||||
}
|
||||
// ES 5.1 A.7 requires "not IdentifierPart" here but all major engines accept any character here.
|
||||
if (NON_IDENT_ESCAPES.indexOf(ch0) == -1) {
|
||||
if (ch0 == 'c') {
|
||||
// Ignore invalid control letter escape if within a character class
|
||||
if (inCharClass && ch1 != ']') {
|
||||
sb.setLength(sb.length() - 1);
|
||||
skip(2);
|
||||
return true;
|
||||
} else {
|
||||
sb.append('\\'); // Treat invalid \c control sequence as \\c
|
||||
}
|
||||
} else if (NON_IDENT_ESCAPES.indexOf(ch0) == -1) {
|
||||
sb.setLength(sb.length() - 1);
|
||||
}
|
||||
return commit(1);
|
||||
@ -677,8 +699,9 @@ final class RegExpScanner extends Scanner {
|
||||
// Forward reference to a capture group. Forward references are always undefined so we
|
||||
// can omit it from the output buffer. Additionally, if the capture group does not exist
|
||||
// the whole regexp becomes invalid, so register the reference for later processing.
|
||||
forwardReferences.add(num);
|
||||
sb.setLength(sb.length() - 1);
|
||||
forwardReferences.add(num);
|
||||
forwardReferences.add(sb.length());
|
||||
skip(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -113,6 +113,7 @@ type.error.cant.convert.to.java.string=Cannot convert object of type {0} to a Ja
|
||||
type.error.cant.convert.to.java.number=Cannot convert object of type {0} to a Java argument of number type
|
||||
type.error.cant.convert.to.javascript.array=Can only convert Java arrays and lists to JavaScript arrays. Can't convert object of type {0}.
|
||||
type.error.extend.expects.at.least.one.argument=Java.extend needs at least one argument.
|
||||
type.error.extend.expects.at.least.one.type.argument=Java.extend needs at least one type argument.
|
||||
type.error.extend.expects.java.types=Java.extend needs Java types as its arguments.
|
||||
type.error.extend.ambiguous.defining.class=There is no class loader that can see all of {0} at once.
|
||||
type.error.extend.ERROR_FINAL_CLASS=Can not extend final class {0}.
|
||||
|
||||
@ -89,7 +89,8 @@ nashorn.option.class.cache.size ={ \
|
||||
short_name="--ccs", \
|
||||
desc="Size of the Class cache size per global scope.", \
|
||||
is_undocumented=true, \
|
||||
type=Integer \
|
||||
type=Integer, \
|
||||
default=50 \
|
||||
}
|
||||
|
||||
nashorn.option.classpath ={ \
|
||||
@ -101,7 +102,7 @@ nashorn.option.classpath ={ \
|
||||
}
|
||||
|
||||
nashorn.option.compile.only = { \
|
||||
name="--compile-only", \
|
||||
name="--compile-only", \
|
||||
short_name="-co", \
|
||||
is_undocumented=true, \
|
||||
desc="Compile without running.", \
|
||||
@ -117,10 +118,10 @@ nashorn.option.d = { \
|
||||
type=String \
|
||||
}
|
||||
|
||||
nashorn.option.doe = { \
|
||||
name="-dump-on-error", \
|
||||
short_name="-doe", \
|
||||
desc="Dump a stack trace on errors."\
|
||||
nashorn.option.doe = { \
|
||||
name="-dump-on-error", \
|
||||
short_name="-doe", \
|
||||
desc="Dump a stack trace on errors." \
|
||||
}
|
||||
|
||||
nashorn.option.empty.statements = { \
|
||||
@ -196,7 +197,7 @@ nashorn.option.package = { \
|
||||
}
|
||||
|
||||
nashorn.option.parse.only = { \
|
||||
name="--parse-only", \
|
||||
name="--parse-only", \
|
||||
is_undocumented=true, \
|
||||
desc="Parse without compiling." \
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ Object.defineProperty(this, "importPackage", {
|
||||
var global = this;
|
||||
var oldNoSuchProperty = global.__noSuchProperty__;
|
||||
global.__noSuchProperty__ = function(name) {
|
||||
'use strict';
|
||||
for (var i in _packages) {
|
||||
try {
|
||||
var type = Java.type(_packages[i] + "." + name);
|
||||
@ -57,7 +58,15 @@ Object.defineProperty(this, "importPackage", {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return oldNoSuchProperty? oldNoSuchProperty(name) : undefined;
|
||||
if (oldNoSuchProperty) {
|
||||
return oldNoSuchProperty.call(this, name);
|
||||
} else {
|
||||
if (this === undefined) {
|
||||
throw new ReferenceError(name + " is not defined");
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var prefix = "[JavaPackage ";
|
||||
|
||||
93
nashorn/test/script/basic/JDK-8009230.js
Normal file
93
nashorn/test/script/basic/JDK-8009230.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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-8009230: Nashorn rejects extended RegExp syntax accepted by all major JS engines
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
|
||||
// Invalid ControlEscape/IdentityEscape character treated as literal.
|
||||
print(/\z/.exec("z")); // Invalid escape, same as /z/
|
||||
// Incomplete/Invalid ControlEscape treated as "\\c"
|
||||
print(/\c/.exec("\\c")); // same as /\\c/
|
||||
print(/\c2/.exec("\\c2")); // same as /\\c2/
|
||||
print(/\C/.exec("C")); // same as /C/
|
||||
print(/\C2/.exec("C2")); // same as /C2/
|
||||
// Incomplete HexEscapeSequence escape treated as "x".
|
||||
print(/\x/.exec("x")); // incomplete x-escape
|
||||
print(/\x1/.exec("x1")); // incomplete x-escape
|
||||
print(/\x1z/.exec("x1z")); // incomplete x-escape
|
||||
// Incomplete UnicodeEscapeSequence escape treated as "u".
|
||||
print(/\u/.exec("u")); // incomplete u-escape
|
||||
print(/\uz/.exec("uz")); // incomplete u-escape
|
||||
print(/\u1/.exec("u1")); // incomplete u-escape
|
||||
print(/\u1z/.exec("u1z")); // incomplete u-escape
|
||||
print(/\u12/.exec("u12")); // incomplete u-escape
|
||||
print(/\u12z/.exec("u12z")); // incomplete u-escape
|
||||
print(/\u123/.exec("u123")); // incomplete u-escape
|
||||
print(/\u123z/.exec("u123z")); // incomplete u-escape
|
||||
// Bad quantifier range:
|
||||
print(/x{z/.exec("x{z")); // same as /x\{z/
|
||||
print(/x{1z/.exec("x{1z")); // same as /x\{1z/
|
||||
print(/x{1,z/.exec("x{1,z")); // same as /x\{1,z/
|
||||
print(/x{1,2z/.exec("x{1,2z")); // same as /x\{1,2z/
|
||||
print(/x{10000,20000z/.exec("x{10000,20000z")); // same as /x\{10000,20000z/
|
||||
// Notice: It needs arbitrary lookahead to determine the invalidity,
|
||||
// except Mozilla that limits the numbers.
|
||||
|
||||
// Zero-initialized Octal escapes.
|
||||
/\012/; // same as /\x0a/
|
||||
|
||||
// Nonexisting back-references smaller than 8 treated as octal escapes:
|
||||
print(/\5/.exec("\u0005")); // same as /\x05/
|
||||
print(/\7/.exec("\u0007")); // same as /\x07/
|
||||
print(/\8/.exec("\u0008")); // does not match
|
||||
|
||||
// Invalid PatternCharacter accepted unescaped
|
||||
print(/]/.exec("]"));
|
||||
print(/{/.exec("{"));
|
||||
print(/}/.exec("}"));
|
||||
|
||||
// Bad escapes also inside CharacterClass.
|
||||
print(/[\z]/.exec("z"));
|
||||
print(/[\c]/.exec("c"));
|
||||
print(/[\c2]/.exec("c"));
|
||||
print(/[\x]/.exec("x"));
|
||||
print(/[\x1]/.exec("x1"));
|
||||
print(/[\x1z]/.exec("x1z"));
|
||||
print(/[\u]/.exec("u"));
|
||||
print(/[\uz]/.exec("u"));
|
||||
print(/[\u1]/.exec("u"));
|
||||
print(/[\u1z]/.exec("u"));
|
||||
print(/[\u12]/.exec("u"));
|
||||
print(/[\u12z]/.exec("u"));
|
||||
print(/[\u123]/.exec("u"));
|
||||
print(/[\u123z]/.exec("u"));
|
||||
print(/[\012]/.exec("0"));
|
||||
print(/[\5]/.exec("5"));
|
||||
// And in addition:
|
||||
print(/[\B]/.exec("B"));
|
||||
print(/()()[\2]/.exec("")); // Valid backreference should be invalid.
|
||||
45
nashorn/test/script/basic/JDK-8009230.js.EXPECTED
Normal file
45
nashorn/test/script/basic/JDK-8009230.js.EXPECTED
Normal file
@ -0,0 +1,45 @@
|
||||
z
|
||||
\c
|
||||
\c2
|
||||
C
|
||||
C2
|
||||
x
|
||||
x1
|
||||
x1z
|
||||
u
|
||||
uz
|
||||
u1
|
||||
u1z
|
||||
u12
|
||||
u12z
|
||||
u123
|
||||
u123z
|
||||
x{z
|
||||
x{1z
|
||||
x{1,z
|
||||
x{1,2z
|
||||
x{10000,20000z
|
||||
|
||||
|
||||
null
|
||||
]
|
||||
{
|
||||
}
|
||||
z
|
||||
c
|
||||
null
|
||||
x
|
||||
x
|
||||
x
|
||||
u
|
||||
u
|
||||
u
|
||||
u
|
||||
u
|
||||
u
|
||||
u
|
||||
u
|
||||
null
|
||||
null
|
||||
B
|
||||
null
|
||||
67
nashorn/test/script/basic/JDK-8010924.js
Normal file
67
nashorn/test/script/basic/JDK-8010924.js
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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-8010924: Dealing with undefined property gets you a fatal stack
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @option -scripting
|
||||
*/
|
||||
|
||||
load("nashorn:mozilla_compat.js");
|
||||
|
||||
if (this.non_existent_foo !== undefined) {
|
||||
fail("this.non_existent_foo is defined!");
|
||||
}
|
||||
|
||||
try {
|
||||
non_existent_foo;
|
||||
fail("should have thrown ReferenceError");
|
||||
} catch (e) {
|
||||
if (! (e instanceof ReferenceError)) {
|
||||
fail("ReferenceError expected, got " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// try the same via script engine
|
||||
|
||||
var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager");
|
||||
var engine = new ScriptEngineManager().getEngineByName("nashorn");
|
||||
|
||||
engine.eval("load('nashorn:mozilla_compat.js')");
|
||||
|
||||
if (! engine.eval("this.non_existent_foo === undefined")) {
|
||||
fail("this.non_existent_foo is not undefined");
|
||||
}
|
||||
|
||||
engine.eval(<<EOF
|
||||
try {
|
||||
non_existent_foo;
|
||||
throw new Error("should have thrown ReferenceError");
|
||||
} catch (e) {
|
||||
if (! (e instanceof ReferenceError)) {
|
||||
throw new Error("ReferenceError expected, got " + e);
|
||||
}
|
||||
}
|
||||
EOF);
|
||||
76
nashorn/test/script/basic/JDK-8011209.js
Normal file
76
nashorn/test/script/basic/JDK-8011209.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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-8011209: Object.getOwnPropertyDescriptor(function(){"use strict"},"caller").get.length is not 0
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var callerPropDesc = Object.getOwnPropertyDescriptor(function(){"use strict"},"caller");
|
||||
|
||||
var getterLen = callerPropDesc.get.length;
|
||||
if (getterLen != 0) {
|
||||
fail("caller's get.length != 0");
|
||||
}
|
||||
|
||||
var setterLen = callerPropDesc.set.length;
|
||||
if (setterLen != 0) {
|
||||
fail("caller's set.length != 1");
|
||||
}
|
||||
|
||||
var argumentsPropDesc = Object.getOwnPropertyDescriptor(function(){"use strict"},"arguments");
|
||||
|
||||
getterLen = argumentsPropDesc.get.length;
|
||||
if (getterLen != 0) {
|
||||
fail("arguments's get.length != 0");
|
||||
}
|
||||
|
||||
setterLen = argumentsPropDesc.set.length;
|
||||
if (setterLen != 0) {
|
||||
fail("arguments's set.length != 1");
|
||||
}
|
||||
|
||||
var strictArgs = (function() { 'use strict'; return arguments; })();
|
||||
callerPropDesc = Object.getOwnPropertyDescriptor(strictArgs,"caller");
|
||||
getterLen = callerPropDesc.get.length;
|
||||
if (getterLen != 0) {
|
||||
fail("argument.caller's get.length != 0");
|
||||
}
|
||||
|
||||
setterLen = callerPropDesc.set.length;
|
||||
if (setterLen != 0) {
|
||||
fail("argument.caller's set.length != 1");
|
||||
}
|
||||
|
||||
calleePropDesc = Object.getOwnPropertyDescriptor(strictArgs,"callee");
|
||||
getterLen = calleePropDesc.get.length;
|
||||
if (getterLen != 0) {
|
||||
fail("argument.callee's get.length != 0");
|
||||
}
|
||||
|
||||
setterLen = calleePropDesc.set.length;
|
||||
if (setterLen != 0) {
|
||||
fail("argument.callee's set.length != 1");
|
||||
}
|
||||
39
nashorn/test/script/basic/JDK-8011237.js
Normal file
39
nashorn/test/script/basic/JDK-8011237.js
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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-8011237: Object.isExtensible(Object.getOwnPropertyDescriptor(function(){"use strict"},"caller").get) should be false
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
// ECMA Section 13.2.3 The [[ThrowTypeError]] Function Object
|
||||
// 11. Set the [[Extensible]] internal property of F to false
|
||||
|
||||
var strictFunc = (function() { 'use strict' });
|
||||
var strictFuncCallerDesc = Object.getOwnPropertyDescriptor(strictFunc, "caller")
|
||||
var isExtensible = Object.isExtensible(strictFuncCallerDesc.get);
|
||||
if (isExtensible) {
|
||||
fail("strict function caller's getter is extensible!");
|
||||
}
|
||||
48
nashorn/test/script/basic/JDK-8011274.js
Normal file
48
nashorn/test/script/basic/JDK-8011274.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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-8011324: Object.getOwnPropertyDescriptor(function(){"use strict"},"caller").get.hasOwnProperty("prototype") should be false
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var strictFunc = (function() { 'use strict' });
|
||||
var desc = Object.getOwnPropertyDescriptor(strictFunc, "caller");
|
||||
if (desc.get.hasOwnProperty("prototype")) {
|
||||
fail("strict function's caller getter has 'prototype' property");
|
||||
}
|
||||
|
||||
// try few built-ins
|
||||
if (parseInt.hasOwnProperty("prototype")) {
|
||||
fail("parseInt.hasOwnProperty('prototype') is true");
|
||||
}
|
||||
|
||||
if (parseFloat.hasOwnProperty("prototype")) {
|
||||
fail("parseFloat.hasOwnProperty('prototype') is true");
|
||||
}
|
||||
|
||||
if (isFinite.hasOwnProperty("prototype")) {
|
||||
fail("isFinite.hasOwnProperty('prototype') is true");
|
||||
}
|
||||
68
nashorn/test/script/basic/JDK-8011357.js
Normal file
68
nashorn/test/script/basic/JDK-8011357.js
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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-8011357: Array.prototype.slice and Array.prototype.splice should not call user defined valueOf of start, end arguments more than once
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var startValueOf = 0;
|
||||
var endValueOf = 0;
|
||||
|
||||
[].slice(
|
||||
{
|
||||
valueOf: function() {
|
||||
startValueOf++;
|
||||
}
|
||||
},
|
||||
{
|
||||
valueOf: function() {
|
||||
endValueOf++;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (startValueOf !== 1) {
|
||||
fail("Array.prototype.slice should call valueOf on start arg once");
|
||||
}
|
||||
|
||||
if (endValueOf !== 1) {
|
||||
fail("Array.prototype.slice should call valueOf on end arg once");
|
||||
}
|
||||
|
||||
startValueOf = 0;
|
||||
|
||||
[].splice(
|
||||
{
|
||||
valueOf: function() {
|
||||
startValueOf++;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (startValueOf !== 1) {
|
||||
fail("Array.prototype.splice should call valueOf on start arg once");
|
||||
}
|
||||
|
||||
34
nashorn/test/script/basic/JDK-8011362.js
Normal file
34
nashorn/test/script/basic/JDK-8011362.js
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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-8011362: Overloaded method resolution foiled by nulls
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var subject = new (Java.type("jdk.nashorn.test.models.Jdk8011362TestSubject"))
|
||||
|
||||
print(subject.overloaded("", null))
|
||||
print(subject.overloaded(0, null))
|
||||
2
nashorn/test/script/basic/JDK-8011362.js.EXPECTED
Normal file
2
nashorn/test/script/basic/JDK-8011362.js.EXPECTED
Normal file
@ -0,0 +1,2 @@
|
||||
overloaded(String, String)
|
||||
overloaded(Double, Double)
|
||||
72
nashorn/test/script/basic/JDK-8011365.js
Normal file
72
nashorn/test/script/basic/JDK-8011365.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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-8011365: Array.prototype.join and Array.prototype.toString do not throw TypeError on null, undefined
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
try {
|
||||
Array.prototype.join.call(null, { toString:function() { throw 2 } });
|
||||
fail("should have thrown TypeError");
|
||||
} catch (e) {
|
||||
if (! (e instanceof TypeError)) {
|
||||
fail("TypeError expected, got " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// check all Array.prototype functions to be sure
|
||||
var names = Object.getOwnPropertyNames(Array.prototype);
|
||||
|
||||
for (var n in names) {
|
||||
var funcName = names[n];
|
||||
// ignore constructor
|
||||
if (funcName == "constructor") {
|
||||
continue;
|
||||
}
|
||||
|
||||
var prop = Array.prototype[funcName];
|
||||
if (prop instanceof Function) {
|
||||
// try 'null' this
|
||||
try {
|
||||
prop.call(null);
|
||||
fail(funcName + " does not throw TypeError on 'null' this");
|
||||
} catch (e) {
|
||||
if (! (e instanceof TypeError)) {
|
||||
fail("TypeError expected from " + funcName + ", got " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// try 'undefined' this
|
||||
try {
|
||||
prop.call(undefined);
|
||||
fail(funcName + " does not throw TypeError on 'undefined' this");
|
||||
} catch (e) {
|
||||
if (! (e instanceof TypeError)) {
|
||||
fail("TypeError expected from " + funcName + ", got " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
nashorn/test/script/basic/JDK-8011382.js
Normal file
115
nashorn/test/script/basic/JDK-8011382.js
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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-8011382: Data prototype methods and constructor do not call user defined toISOString, valueOf methods per spec.
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var yearValueOf = 0;
|
||||
var monthValueOf = 0;
|
||||
var dayValueOf = 0;
|
||||
|
||||
var d = new Date(
|
||||
{
|
||||
valueOf: function() { yearValueOf++; return NaN; }
|
||||
},
|
||||
{
|
||||
valueOf: function() { monthValueOf++; return NaN; }
|
||||
},
|
||||
{
|
||||
valueOf: function() { dayValueOf++; return NaN; }
|
||||
}
|
||||
);
|
||||
|
||||
if (yearValueOf !== 1) {
|
||||
fail("Date constructor does not call valueOf on year argument once");
|
||||
}
|
||||
|
||||
if (monthValueOf !== 1) {
|
||||
fail("Date constructor does not call valueOf on month argument once");
|
||||
}
|
||||
|
||||
if (dayValueOf !== 1) {
|
||||
fail("Date constructor does not call valueOf on day argument once");
|
||||
}
|
||||
|
||||
yearValueOf = 0;
|
||||
monthValueOf = 0;
|
||||
dayValueOf = 0;
|
||||
|
||||
d = new Date();
|
||||
|
||||
d.setFullYear(
|
||||
{
|
||||
valueOf: function() { yearValueOf++; return NaN; }
|
||||
},
|
||||
{
|
||||
valueOf: function() { monthValueOf++; return NaN; }
|
||||
},
|
||||
{
|
||||
valueOf: function() { dayValueOf++; return NaN; }
|
||||
}
|
||||
);
|
||||
|
||||
if (yearValueOf !== 1) {
|
||||
fail("Date setFullYear does not call valueOf on year argument once");
|
||||
}
|
||||
|
||||
if (monthValueOf !== 1) {
|
||||
fail("Date setFullYear does not call valueOf on month argument once");
|
||||
}
|
||||
|
||||
if (dayValueOf !== 1) {
|
||||
fail("Date setFullYear does not call valueOf on day argument once");
|
||||
}
|
||||
|
||||
// check toJSON calls toISOString override
|
||||
var toISOStringCalled = 0;
|
||||
d = new Date();
|
||||
d.toISOString = function() {
|
||||
toISOStringCalled++;
|
||||
};
|
||||
|
||||
d.toJSON();
|
||||
if (toISOStringCalled !== 1) {
|
||||
fail("toISOString was not called by Date.prototype.toJSON once");
|
||||
}
|
||||
|
||||
toISOStringCalled = 0;
|
||||
|
||||
// toJSON is generic - try for non-Date object
|
||||
Date.prototype.toJSON.call({
|
||||
toISOString: function() {
|
||||
toISOStringCalled++;
|
||||
},
|
||||
valueOf: function() {
|
||||
return 12;
|
||||
}
|
||||
});
|
||||
|
||||
if (toISOStringCalled !== 1) {
|
||||
fail("toISOString was not called by Date.prototype.toJSON once");
|
||||
}
|
||||
45
nashorn/test/script/basic/JDK-8011394.js
Normal file
45
nashorn/test/script/basic/JDK-8011394.js
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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-8011394: RegExp.prototype.test() does not call valueOf on lastIndex property as per the spec.
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var re = new RegExp();
|
||||
var lastIndexValueOfCalled = false;
|
||||
|
||||
re.lastIndex = {
|
||||
valueOf: function() {
|
||||
lastIndexValueOfCalled = true;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
re.test("");
|
||||
|
||||
if (! lastIndexValueOfCalled) {
|
||||
fail("RegExp.prototype.test() does not call 'valueOf' on 'lastIndex'");
|
||||
}
|
||||
51
nashorn/test/script/basic/JDK-8011421.js
Normal file
51
nashorn/test/script/basic/JDK-8011421.js
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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-8011421: When using Object.defineProperty on arrays, PropertyDescriptor's property accessors are invoked multiple times
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var configurableGetterCalled = 0;
|
||||
|
||||
// create a property descriptor object with "configurable"
|
||||
// property with a user defined getter
|
||||
var propDesc = Object.defineProperty({},
|
||||
"configurable",
|
||||
{
|
||||
get: function() {
|
||||
configurableGetterCalled++;
|
||||
return false
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// make array length non-configurable
|
||||
Object.defineProperty([], "length", propDesc);
|
||||
|
||||
// above should have called "configurable" getter only once
|
||||
if (configurableGetterCalled !== 1) {
|
||||
fail("defineProperty on array should call propDesc getters only once");
|
||||
}
|
||||
42
nashorn/test/script/basic/JDK-8011543.js
Normal file
42
nashorn/test/script/basic/JDK-8011543.js
Normal 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-8011543: "".split(undefined,{valueOf:function(){throw 2}}) does not throw exception
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
try {
|
||||
"".split(undefined,{
|
||||
valueOf: function() {
|
||||
throw 42;
|
||||
}
|
||||
});
|
||||
fail("should have thrown 42");
|
||||
} catch (e) {
|
||||
if (e != 42) {
|
||||
fail("expected 42 to be thrown");
|
||||
}
|
||||
}
|
||||
37
nashorn/test/script/basic/JDK-8011552.js
Normal file
37
nashorn/test/script/basic/JDK-8011552.js
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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-8011552: Arrays with missing elements are not properly sorted
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
if ([,void 0].sort().hasOwnProperty("1")) {
|
||||
fail("missing element found in sorted array");
|
||||
}
|
||||
|
||||
if ([1,,2,,-1].sort().toString() != "-1,1,2,,") {
|
||||
faiil("missing elements are not at the end of sorted array");
|
||||
}
|
||||
42
nashorn/test/script/basic/JDK-8011555.js
Normal file
42
nashorn/test/script/basic/JDK-8011555.js
Normal 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-8011555: Invalid class name in with block with JavaImporter causes MH type mismatch
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
with(new JavaImporter()) {
|
||||
try {
|
||||
new X()
|
||||
print("Expected to fail!")
|
||||
} catch(e) {
|
||||
// We expect to get a TypeError for trying to use __noSuchMethod__ as
|
||||
// a constructor. Before we fixed this bug, we were getting a runtime
|
||||
// exception with MH type mismatch on a MH.foldArguments within the
|
||||
// WithObject code instead.
|
||||
print(e)
|
||||
}
|
||||
}
|
||||
1
nashorn/test/script/basic/JDK-8011555.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8011555.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
TypeError: function __noSuchMethod__() { [native code] } is not a constructor function
|
||||
86
nashorn/test/script/basic/javaclassoverrides.js
Normal file
86
nashorn/test/script/basic/javaclassoverrides.js
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check behavior of class-level overrides.
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
|
||||
// Make two classes with class overrides
|
||||
|
||||
var R1 = Java.extend(java.lang.Runnable, {
|
||||
run: function() {
|
||||
print("R1.run() invoked")
|
||||
}
|
||||
})
|
||||
|
||||
var R2 = Java.extend(java.lang.Runnable, {
|
||||
run: function() {
|
||||
print("R2.run() invoked")
|
||||
}
|
||||
})
|
||||
|
||||
var r1 = new R1
|
||||
var r2 = new R2
|
||||
// Create one with an instance-override too
|
||||
var r3 = new R2(function() { print("r3.run() invoked") })
|
||||
|
||||
// Run 'em - we're passing them through a Thread to make sure they indeed
|
||||
// are full-blown Runnables
|
||||
function runInThread(r) {
|
||||
var t = new java.lang.Thread(r)
|
||||
t.start()
|
||||
t.join()
|
||||
}
|
||||
runInThread(r1)
|
||||
runInThread(r2)
|
||||
runInThread(r3)
|
||||
|
||||
// Two class-override classes differ
|
||||
print("r1.class != r2.class: " + (r1.class != r2.class))
|
||||
// However, adding instance-overrides doesn't change the class
|
||||
print("r2.class == r3.class: " + (r2.class == r3.class))
|
||||
|
||||
function checkAbstract(r) {
|
||||
try {
|
||||
r.run()
|
||||
print("Expected to fail!")
|
||||
} catch(e) {
|
||||
print("Got exception: " + e)
|
||||
}
|
||||
}
|
||||
|
||||
// Check we're hitting UnsupportedOperationException if neither class
|
||||
// overrides nor instance overrides are present
|
||||
var RAbstract = Java.extend(java.lang.Runnable, {})
|
||||
checkAbstract(new RAbstract()) // class override (empty)
|
||||
checkAbstract(new RAbstract() {}) // class+instance override (empty)
|
||||
|
||||
// Check we delegate to superclass if neither class
|
||||
// overrides nor instance overrides are present
|
||||
var ExtendsList = Java.extend(java.util.ArrayList, {})
|
||||
print("(new ExtendsList).size() = " + (new ExtendsList).size())
|
||||
print("(new ExtendsList(){}).size() = " + (new ExtendsList(){}).size())
|
||||
9
nashorn/test/script/basic/javaclassoverrides.js.EXPECTED
Normal file
9
nashorn/test/script/basic/javaclassoverrides.js.EXPECTED
Normal file
@ -0,0 +1,9 @@
|
||||
R1.run() invoked
|
||||
R2.run() invoked
|
||||
r3.run() invoked
|
||||
r1.class != r2.class: true
|
||||
r2.class == r3.class: true
|
||||
Got exception: java.lang.UnsupportedOperationException
|
||||
Got exception: java.lang.UnsupportedOperationException
|
||||
(new ExtendsList).size() = 0
|
||||
(new ExtendsList(){}).size() = 0
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.test.models;
|
||||
|
||||
/**
|
||||
* Test class used by JDK-8011362.js.
|
||||
*/
|
||||
public class Jdk8011362TestSubject {
|
||||
// This is selected for overloaded("", null)
|
||||
public String overloaded(String a, String b) {
|
||||
return "overloaded(String, String)";
|
||||
}
|
||||
|
||||
// This is selected for overloaded(0, null)
|
||||
public String overloaded(Double a, Double b) {
|
||||
return "overloaded(Double, Double)";
|
||||
}
|
||||
|
||||
// This method is added to test that null will not match a primitive type, that is overloaded(0, null) will always
|
||||
// select the (Double, Double) over (Double, double).
|
||||
public String overloaded(Double a, double b) {
|
||||
return "overloaded(Double, double)";
|
||||
}
|
||||
}
|
||||
194
nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java
Normal file
194
nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.tools;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
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 jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
||||
|
||||
/**
|
||||
* This shell is designed to launch a JavaFX application written in Nashorn JavaScript.
|
||||
*/
|
||||
public class FXShell extends Application {
|
||||
/**
|
||||
* Script engine manager to search.
|
||||
*/
|
||||
private ScriptEngineManager manager;
|
||||
/**
|
||||
* Nashorn script engine factory.
|
||||
*/
|
||||
private NashornScriptEngineFactory factory;
|
||||
/**
|
||||
* Main instance of Nashorn script engine.
|
||||
*/
|
||||
private ScriptEngine engine;
|
||||
|
||||
/**
|
||||
* Needed so that the FX launcher can create an instance of this class.
|
||||
*/
|
||||
public FXShell() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point. Never actually used.
|
||||
* @param args Command lien arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
}
|
||||
|
||||
/*
|
||||
* Application overrides.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
// Script engine manager to search.
|
||||
this.manager = new ScriptEngineManager();
|
||||
|
||||
// Locate the Nashorn script engine factory. Needed for passing arguments.
|
||||
for (ScriptEngineFactory engineFactory : this.manager.getEngineFactories()) {
|
||||
if (engineFactory.getEngineName().equals("Oracle Nashorn") && engineFactory instanceof NashornScriptEngineFactory) {
|
||||
this.factory = (NashornScriptEngineFactory)engineFactory;
|
||||
}
|
||||
}
|
||||
|
||||
// If none located.
|
||||
if (this.factory == null) {
|
||||
System.err.println("Nashorn script engine not available");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Get the command line and JNLP parameters.
|
||||
final Parameters parameters = getParameters();
|
||||
|
||||
// To collect the script paths and command line arguments.
|
||||
final List<String> paths = new ArrayList<>();
|
||||
final List<String> args = new ArrayList<>();
|
||||
|
||||
// Pull out relevant JNLP named parameters.
|
||||
final Map<String, String> named = parameters.getNamed();
|
||||
for (Map.Entry<String, String> entry : named.entrySet()) {
|
||||
final String key = entry.getKey();
|
||||
final String value = entry.getValue();
|
||||
|
||||
if ((key.equals("cp") || key.equals("classpath")) && value != null) {
|
||||
args.add("-classpath");
|
||||
args.add(value);
|
||||
} else if (key.equals("source") && value != null && value.toLowerCase().endsWith(".js")) {
|
||||
paths.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Pull out relevant command line arguments.
|
||||
boolean addNextArg = false;
|
||||
boolean addAllArgs = false;
|
||||
for (String parameter : parameters.getUnnamed()) {
|
||||
if (addAllArgs || addNextArg) {
|
||||
args.add(parameter);
|
||||
addNextArg = false;
|
||||
} else if (parameter.equals("--")) {
|
||||
args.add(parameter);
|
||||
addAllArgs = true;
|
||||
} else if (parameter.startsWith("-")) {
|
||||
args.add(parameter);
|
||||
addNextArg = parameter.equals("-cp") || parameter.equals("-classpath");
|
||||
} else if (parameter.toLowerCase().endsWith(".js")) {
|
||||
paths.add(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a Nashorn script engine with specified arguments.
|
||||
engine = factory.getScriptEngine(args.toArray(new String[args.size()]));
|
||||
|
||||
// Load initial scripts.
|
||||
for (String path : paths) {
|
||||
load(path);
|
||||
}
|
||||
|
||||
// Invoke users JavaScript init function if present.
|
||||
try {
|
||||
((Invocable) engine).invokeFunction("init");
|
||||
} catch (NoSuchMethodException ex) {
|
||||
// Presence of init is optional.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
// Invoke users JavaScript start function if present.
|
||||
try {
|
||||
((Invocable) engine).invokeFunction("start", stage);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
// Presence of start is optional.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
// Invoke users JavaScript stop function if present.
|
||||
try {
|
||||
((Invocable) engine).invokeFunction("stop");
|
||||
} catch (NoSuchMethodException ex) {
|
||||
// Presence of stop is optional.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and evaluate the specified JavaScript file.
|
||||
*
|
||||
* @param path Path to UTF-8 encoded JavaScript file.
|
||||
*
|
||||
* @return Last evalulation result (discarded.)
|
||||
*/
|
||||
private Object load(String path) {
|
||||
try {
|
||||
FileInputStream file = new FileInputStream(path);
|
||||
InputStreamReader input = new InputStreamReader(file, "UTF-8");
|
||||
return engine.eval(input);
|
||||
} catch (FileNotFoundException | UnsupportedEncodingException | ScriptException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user