This commit is contained in:
Lana Steuck 2013-04-16 08:16:17 -07:00
commit 53ae5516ff
50 changed files with 3168 additions and 1295 deletions

View File

@ -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">

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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 ?

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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);
}
}
/**

View File

@ -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]);
}
/**

View 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. 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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. 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() {
}
}

View File

@ -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);
}
}

View File

@ -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)) {

View File

@ -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.

View File

@ -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;
}

View File

@ -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}.

View File

@ -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." \
}

View File

@ -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 ";

View 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.

View 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

View 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);

View 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");
}

View 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!");
}

View 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");
}

View 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");
}

View 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))

View File

@ -0,0 +1,2 @@
overloaded(String, String)
overloaded(Double, Double)

View 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);
}
}
}
}

View 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");
}

View 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'");
}

View 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");
}

View File

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

View 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");
}

View File

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

View File

@ -0,0 +1 @@
TypeError: function __noSuchMethod__() { [native code] } is not a constructor function

View 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())

View 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

View File

@ -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)";
}
}

View 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;
}
}