8027043: Turn global accesses into MethodHandle.constant, with one chance of reassignment, e.g. x = value occuring once in the global scope is ok, twice is not

Reviewed-by: attila, sundar, jlaskey
This commit is contained in:
Marcus Lagergren 2014-03-31 14:13:34 +02:00
parent e0f84784f2
commit 379d9f239f
32 changed files with 760 additions and 136 deletions

@ -0,0 +1,32 @@
#!/bin/sh
#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
FILENAME="./optimistic_dual_catch_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
DIR=..
FAST_CATCH_COMBINATOR=
#$DIR/bin/fastCatchCombinator.jar
NASHORN_JAR=$DIR/dist/nashorn.jar
$JAVA_HOME/bin/java \
$FLAGS \
-ea \
-esa \
-Xbootclasspath/p:$FAST_CATCH_COMBINATOR:$NASHORN_JAR \
-Xms2G -Xmx2G \
-XX:+UnlockCommercialFeatures \
-XX:TypeProfileLevel=222 \
-XX:+UnlockExperimentalVMOptions \
-XX:+UnlockDiagnosticVMOptions \
-cp $CLASSPATH:../build/test/classes/ \
jdk.nashorn.tools.Shell ${@}
#-XX:+FlightRecorder \
#-XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=1024 \
#-XX:+UseTypeSpeculation \
#-XX:+UseMathExactIntrinsics \
#-XX:+ShowHiddenFrames \
#-XX:+PrintOptoAssembly \
#-XX:-TieredCompilation \
#-XX:CICompilerCount=1 \

@ -126,7 +126,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
// load engine.js and return content as a char[]
@SuppressWarnings("resource")
private static char[] loadEngineJSSource() {
final String script = "resources/engine.js";
try {

@ -42,6 +42,7 @@ import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.FindProperty;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
@ -435,10 +436,10 @@ public final class CompilationEnvironment {
// Safely evaluate the property, and return the narrowest type for the actual value (e.g. Type.INT for a boxed
// integer).
return Type.typeFor(ObjectClassGenerator.unboxedFieldType(property.getObjectValue(owner, owner)));
return Type.typeFor(JSType.unboxedFieldType(property.getObjectValue(owner, owner)));
}
private Object evaluateSafely(Expression expr) {
private Object evaluateSafely(final Expression expr) {
if(expr instanceof IdentNode) {
return runtimeScope == null ? null : evaluatePropertySafely(runtimeScope, ((IdentNode)expr).getName());
} else if(expr instanceof AccessNode) {

@ -778,29 +778,6 @@ public final class ObjectClassGenerator {
}
}
/**
* Get the unboxed (primitive) type for an object
* @param o object
* @return primive type or Object.class if not primitive
*/
public static Class<?> unboxedFieldType(final Object o) {
if (OBJECT_FIELDS_ONLY) {
return Object.class;
}
if (o == null) {
return Object.class;
} else if (o.getClass() == Integer.class) {
return int.class;
} else if (o.getClass() == Long.class) {
return long.class;
} else if (o.getClass() == Double.class) {
return double.class;
} else {
return Object.class;
}
}
/**
* Add padding to field count to avoid creating too many classes and have some spare fields
* @param count the field count

@ -28,13 +28,12 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.unboxedFieldType;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
@ -84,7 +83,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
final Property property = propertyMap.findProperty(key);
if (property != null) {
// normal property key
property.setCurrentType(unboxedFieldType(constantValue));
property.setCurrentType(JSType.unboxedFieldType(constantValue));
final int slot = property.getSlot();
if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) {
jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);

@ -139,12 +139,11 @@ public final class MethodHandleFactory {
* @return return value unmodified
*/
static Object traceReturn(final DebugLogger logger, final Object value) {
final String str = "\treturn" +
final String str = " return" +
(VOID_TAG.equals(value) ?
";" :
" " + stripName(value) + "; // [type=" + (value == null ? "null" : stripName(value.getClass()) + ']'));
logger.log(TRACE_LEVEL, str);
logger.log(TRACE_LEVEL, Debug.firstJSFrame());
return value;
}
@ -225,12 +224,13 @@ public final class MethodHandleFactory {
* Add a debug printout to a method handle, tracing parameters and return values
*
* @param logger a specific logger to which to write the output
* @param level level over which to print
* @param mh method handle to trace
* @param tag start of trace message
* @return traced method handle
*/
public static MethodHandle addDebugPrintout(final DebugLogger logger, final MethodHandle mh, final Object tag) {
return addDebugPrintout(logger, mh, 0, true, tag);
public static MethodHandle addDebugPrintout(final DebugLogger logger, final Level level, final MethodHandle mh, final Object tag) {
return addDebugPrintout(logger, level, mh, 0, true, tag);
}
@ -238,18 +238,19 @@ public final class MethodHandleFactory {
* Add a debug printout to a method handle, tracing parameters and return values
*
* @param logger a specific logger to which to write the output
* @param level level over which to print
* @param mh method handle to trace
* @param paramStart first param to print/trace
* @param printReturnValue should we print/trace return value if available?
* @param tag start of trace message
* @return traced method handle
*/
public static MethodHandle addDebugPrintout(final DebugLogger logger, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
public static MethodHandle addDebugPrintout(final DebugLogger logger, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
final MethodType type = mh.type();
//if there is no logger, or if it's set to log only coarser events
//than the trace level, skip and return
if (logger != null && logger.levelCoarserThan(TRACE_LEVEL)) {
if (logger != null && logger.levelCoarserThan(level)) {
return mh;
}
@ -501,7 +502,7 @@ public final class MethodHandleFactory {
}
public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
return addDebugPrintout(LOG, master, Integer.MAX_VALUE, false, str + ' ' + describe(args));
return addDebugPrintout(LOG, Level.INFO, master, Integer.MAX_VALUE, false, str + ' ' + describe(args));
}
@Override

@ -201,7 +201,7 @@ abstract class ArrayBufferView extends ScriptObject {
}
protected static Object setImpl(final Object self, final Object array, final Object offset0) {
final ArrayBufferView dest = ((ArrayBufferView)self);
final ArrayBufferView dest = (ArrayBufferView)self;
final int length;
if (array instanceof ArrayBufferView) {
// void set(TypedArray array, optional unsigned long offset)
@ -245,7 +245,7 @@ abstract class ArrayBufferView extends ScriptObject {
}
protected static Object subarrayImpl(final Object self, final Object begin0, final Object end0) {
final ArrayBufferView arrayView = ((ArrayBufferView)self);
final ArrayBufferView arrayView = (ArrayBufferView)self;
final int byteOffset = arrayView.byteOffset;
final int bytesPerElement = arrayView.bytesPerElement();
final int elementLength = arrayView.elementLength();

@ -47,6 +47,7 @@ import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.GlobalConstants;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
@ -425,6 +426,7 @@ public final class Global extends ScriptObject implements Scope {
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
GlobalConstants.instance().invalidateAll();
}
/**
@ -890,7 +892,7 @@ public final class Global extends ScriptObject implements Scope {
* @param self self reference
* @param code exit code
*
* @return undefined (will never be reacheD)
* @return undefined (will never be reached)
*/
public static Object exit(final Object self, final Object code) {
System.exit(JSType.toInt32(code));
@ -1867,6 +1869,8 @@ public final class Global extends ScriptObject implements Scope {
res.setInitialProto(getObjectPrototype());
}
res.setIsBuiltin();
return res;
} catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {
@ -1973,4 +1977,9 @@ public final class Global extends ScriptObject implements Scope {
this.lastRegExpResult = regExpResult;
}
@Override
protected boolean isGlobal() {
return true;
}
}

@ -134,8 +134,8 @@ public final class NativeArray extends ScriptObject {
NativeArray(final ArrayData arrayData, final Global global) {
super(global.getArrayPrototype(), $nasgenmap$);
this.setArray(arrayData);
this.setIsArray();
setArray(arrayData);
setIsArray();
}
@Override
@ -307,7 +307,7 @@ public final class NativeArray extends ScriptObject {
}
// Step 3h and 3i
final boolean newWritable = (!newLenDesc.has(WRITABLE) || newLenDesc.isWritable());
final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable();
if (!newWritable) {
newLenDesc.setWritable(true);
}
@ -405,7 +405,7 @@ public final class NativeArray extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object isArray(final Object self, final Object arg) {
return isArray(arg) || (arg instanceof JSObject && ((JSObject)arg).isArray());
return isArray(arg) || arg instanceof JSObject && ((JSObject)arg).isArray();
}
/**
@ -716,7 +716,7 @@ public final class NativeArray extends ScriptObject {
private static void concatToList(final ArrayList<Object> list, final Object obj) {
final boolean isScriptArray = isArray(obj);
final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject;
if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) {
if (isScriptArray || obj instanceof Iterable || obj != null && obj.getClass().isArray()) {
final Iterator<Object> iter = arrayLikeIterator(obj, true);
if (iter.hasNext()) {
for (int i = 0; iter.hasNext(); ++i) {
@ -979,7 +979,7 @@ public final class NativeArray extends ScriptObject {
} else {
boolean hasPrevious = true;
for (long k = 1; k < len; k++) {
boolean hasCurrent = sobj.has(k);
final boolean hasCurrent = sobj.has(k);
if (hasCurrent) {
sobj.set(k - 1, sobj.get(k), true);
} else if (hasPrevious) {
@ -1016,7 +1016,7 @@ public final class NativeArray extends ScriptObject {
final ScriptObject sobj = (ScriptObject)obj;
final long len = JSType.toUint32(sobj.getLength());
final long relativeStart = JSType.toLong(start);
final long relativeEnd = (end == ScriptRuntime.UNDEFINED) ? len : JSType.toLong(end);
final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end);
long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
@ -1146,8 +1146,8 @@ public final class NativeArray extends ScriptObject {
return ScriptRuntime.UNDEFINED;
}
final Object start = (args.length > 0) ? args[0] : ScriptRuntime.UNDEFINED;
final Object deleteCount = (args.length > 1) ? args[1] : ScriptRuntime.UNDEFINED;
final Object start = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
final Object deleteCount = args.length > 1 ? args[1] : ScriptRuntime.UNDEFINED;
Object[] items;
@ -1176,7 +1176,7 @@ public final class NativeArray extends ScriptObject {
for (int i = 0; i < items.length; i++, k++) {
sobj.defineOwnProperty(k, items[i]);
}
} catch (UnsupportedOperationException uoe) {
} catch (final UnsupportedOperationException uoe) {
returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
}
} else {
@ -1199,7 +1199,7 @@ public final class NativeArray extends ScriptObject {
}
if (items.length < deleteCount) {
for (long k = start; k < (len - deleteCount); k++) {
for (long k = start; k < len - deleteCount; k++) {
final long from = k + deleteCount;
final long to = k + items.length;
@ -1210,7 +1210,7 @@ public final class NativeArray extends ScriptObject {
}
}
for (long k = len; k > (len - deleteCount + items.length); k--) {
for (long k = len; k > len - deleteCount + items.length; k--) {
sobj.delete(k - 1, true);
}
} else if (items.length > deleteCount) {
@ -1313,7 +1313,7 @@ public final class NativeArray extends ScriptObject {
}
for (long k = Math.max(0, (n < 0) ? (len - Math.abs(n)) : n); k < len; k++) {
for (long k = Math.max(0, n < 0 ? len - Math.abs(n) : n); k < len; k++) {
if (sobj.has(k)) {
if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
return k;
@ -1344,10 +1344,10 @@ public final class NativeArray extends ScriptObject {
return -1;
}
final Object searchElement = (args.length > 0) ? args[0] : ScriptRuntime.UNDEFINED;
final long n = (args.length > 1) ? JSType.toLong(args[1]) : (len - 1);
final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
final long n = args.length > 1 ? JSType.toLong(args[1]) : len - 1;
for (long k = (n < 0) ? (len - Math.abs(n)) : Math.min(n, len - 1); k >= 0; k--) {
for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) {
if (sobj.has(k)) {
if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
return k;
@ -1380,7 +1380,7 @@ public final class NativeArray extends ScriptObject {
@Override
protected boolean forEach(final Object val, final long i) throws Throwable {
return (result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self));
return result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self);
}
}.apply();
}

@ -88,15 +88,15 @@ public class NativeDataView extends ScriptObject {
// underlying ByteBuffer
private final ByteBuffer buf;
private NativeDataView(NativeArrayBuffer arrBuf) {
private NativeDataView(final NativeArrayBuffer arrBuf) {
this(arrBuf, arrBuf.getBuffer(), 0);
}
private NativeDataView(NativeArrayBuffer arrBuf, int offset) {
private NativeDataView(final NativeArrayBuffer arrBuf, final int offset) {
this(arrBuf, bufferFrom(arrBuf, offset), offset);
}
private NativeDataView(NativeArrayBuffer arrBuf, int offset, int length) {
private NativeDataView(final NativeArrayBuffer arrBuf, final int offset, final int length) {
this(arrBuf, bufferFrom(arrBuf, offset, length), offset, length);
}
@ -235,7 +235,7 @@ public class NativeDataView extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static int getUint8(final Object self, final Object byteOffset) {
try {
return (0xFF & getBuffer(self).get(JSType.toInt32(byteOffset)));
return 0xFF & getBuffer(self).get(JSType.toInt32(byteOffset));
} catch (final IllegalArgumentException iae) {
throw rangeError(iae, "dataview.offset");
}
@ -251,7 +251,7 @@ public class NativeDataView extends ScriptObject {
@SpecializedFunction
public static int getUint8(final Object self, final int byteOffset) {
try {
return (0xFF & getBuffer(self).get(byteOffset));
return 0xFF & getBuffer(self).get(byteOffset);
} catch (final IllegalArgumentException iae) {
throw rangeError(iae, "dataview.offset");
}

@ -316,7 +316,7 @@ public final class NativeError extends ScriptObject {
final Object exception = ECMAException.getException(sobj);
if (exception instanceof Throwable) {
Object value = getScriptStackString(sobj, (Throwable)exception);
final Object value = getScriptStackString(sobj, (Throwable)exception);
sobj.put(STACK, value, false);
return value;
}

@ -349,7 +349,7 @@ public class AccessorProperty extends Property {
* @param initialValue initial value
*/
protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
setCurrentType(ObjectClassGenerator.unboxedFieldType(initialValue));
setCurrentType(JSType.unboxedFieldType(initialValue));
if (initialValue instanceof Integer) {
invokeSetter(owner, ((Integer)initialValue).intValue());
} else if (initialValue instanceof Long) {
@ -669,6 +669,7 @@ public class AccessorProperty extends Property {
if (DEBUG_FIELDS && LOG.levelFinerThanOrEqual(Level.INFO) && shouldInstrument(getKey())) {
return MethodHandleFactory.addDebugPrintout(
LOG,
Level.INFO,
mh,
tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", slot=" + getSlot() + " " + getClass().getSimpleName() + " forType=" + stripName(forType) + ", type=" + stripName(type) + ')');
}

@ -192,6 +192,9 @@ public final class Context {
public static void setGlobal(final Global global) {
// This class in a package.access protected package.
// Trusted code only can call this method.
assert getGlobal() != global;
//same code can be cached between globals, then we need to invalidate method handle constants
GlobalConstants.instance().invalidateAll();
currentGlobal.set(global);
}
@ -232,7 +235,6 @@ public final class Context {
* @param str text to write
* @param crlf write a carriage return/new line after text
*/
@SuppressWarnings("resource")
public static void err(final String str, final boolean crlf) {
final PrintWriter err = Context.getCurrentErr();
if (err != null) {
@ -1028,6 +1030,4 @@ public final class Context {
classCache.cache(source, clazz);
}
}
}

@ -126,7 +126,7 @@ public final class DebugLogger {
quote = '\'';
}
return (startQuote == '\0' ? "" : startQuote) + str + (endQuote == '\0' ? "" : endQuote);
return (startQuote == '\0' ? quote : startQuote) + str + (endQuote == '\0' ? quote : endQuote);
}
/**

@ -0,0 +1,418 @@
/*
* Copyright (c) 2010, 2014, 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;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.runtime.DebugLogger.quote;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.SwitchPoint;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.DynamicLinker;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
/**
* Each global owns one of these. This is basically table of accessors
* for global properties. A global constant is evaluated to a MethodHandle.constant
* for faster access and to avoid walking to proto chain looking for it.
*
* We put a switchpoint on the global setter, which invalidates the
* method handle constant getters, and reverts to the standard access strategy
*
* However, there is a twist - while certain globals like "undefined" and "Math"
* are usually never reassigned, a global value can be reset once, and never again.
* This is a rather common pattern, like:
*
* x = function(something) { ...
*
* Thus everything registered as a global constant gets an extra chance. Set once,
* reregister the switchpoint. Set twice or more - don't try again forever, or we'd
* just end up relinking our way into megamorphisism.
*
* We can extend this to ScriptObjects in general (GLOBAL_ONLY=false), which requires
* a receiver guard on the constant getter, but it currently leaks memory and its benefits
* have not yet been investigated property.
*/
public final class GlobalConstants {
private GlobalConstants() {
//singleton
}
/**
* Return the singleton global constant pool
* @return singleton global constant pool
*/
public static GlobalConstants instance() {
return instance;
}
private static final GlobalConstants instance = new GlobalConstants();
/**
* Should we only try to link globals as constants, and not generic script objects.
* Script objects require a receiver guard, which is memory intensive, so this is currently
* disabled. We might implement a weak reference based approach to this later.
*/
private static final boolean GLOBAL_ONLY = true;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final MethodHandle INVALIDATE_SP = staticCall(LOOKUP, GlobalConstants.class, "invalidateSwitchPoint", Object.class, Object.class, Access.class).methodHandle();
private static final MethodHandle RECEIVER_GUARD = staticCall(LOOKUP, GlobalConstants.class, "receiverGuard", boolean.class, Access.class, Object.class, Object.class).methodHandle();
/** Logger for constant getters */
private static final DebugLogger LOG = new DebugLogger("const");
/**
* Access map for this global - associates a symbol name with an Access object, with getter
* and invalidation information
*/
private final Map<String, Access> map = new HashMap<>();
/**
* Information about a constant access and its potential invalidations
*/
private static class Access {
/** name of symbol */
private final String name;
/** switchpoint that invalidates the getters and setters for this access */
private SwitchPoint sp;
/** invalidation count for this access, i.e. how many times has this property been reset */
private int invalidations;
/** has a guard guarding this property getter failed? */
private boolean guardFailed;
private static final int MAX_RETRIES = 2;
private Access(final String name, final SwitchPoint sp) {
this.name = name;
this.sp = sp;
}
private boolean hasBeenInvalidated() {
return sp.hasBeenInvalidated();
}
private boolean guardFailed() {
return guardFailed;
}
private void failGuard() {
invalidateOnce();
guardFailed = true;
}
private void newSwitchPoint() {
assert hasBeenInvalidated();
sp = new SwitchPoint();
}
private void invalidate(final int count) {
if (!sp.hasBeenInvalidated()) {
SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
invalidations += count;
}
}
/**
* Invalidate the access, but do not contribute to the invalidation count
*/
private void invalidateUncounted() {
invalidate(0);
}
/**
* Invalidate the access, and contribute 1 to the invalidation count
*/
private void invalidateOnce() {
invalidate(1);
}
/**
* Invalidate the access and make sure that we never try to turn this into
* a MethodHandle.constant getter again
*/
private void invalidateForever() {
invalidate(MAX_RETRIES);
}
/**
* Are we allowed to relink this as constant getter, even though it
* it has been reset
* @return true if we can relink as constant, one retry is allowed
*/
private boolean mayRetry() {
return invalidations < MAX_RETRIES;
}
@Override
public String toString() {
return "[" + quote(name) + " <id=" + Debug.id(this) + "> inv#=" + invalidations + '/' + MAX_RETRIES + " sp_inv=" + sp.hasBeenInvalidated() + ']';
}
String getName() {
return name;
}
SwitchPoint getSwitchPoint() {
return sp;
}
}
/**
* To avoid an expensive global guard "is this the same global", similar to the
* receiver guard on the ScriptObject level, we invalidate all getters once
* when we switch globals. This is used from the class cache. We _can_ reuse
* the same class for a new global, but the builtins and global scoped variables
* will have changed.
*/
public void invalidateAll() {
LOG.info("New global created - invalidating all constant callsites without increasing invocation count.");
for (final Access acc : map.values()) {
acc.invalidateUncounted();
}
}
/**
* Invalidate the switchpoint of an access - we have written to
* the property
*
* @param obj receiver
* @param acc access
*
* @return receiver, so this can be used as param filter
*/
@SuppressWarnings("unused")
private static Object invalidateSwitchPoint(final Object obj, final Access acc) {
if (LOG.isEnabled()) {
LOG.info("*** Invalidating switchpoint " + acc.getSwitchPoint() + " for receiver=" + obj + " access=" + acc);
}
acc.invalidateOnce();
if (acc.mayRetry()) {
if (LOG.isEnabled()) {
LOG.info("Retry is allowed for " + acc + "... Creating a new switchpoint.");
}
acc.newSwitchPoint();
} else {
if (LOG.isEnabled()) {
LOG.info("This was the last time I allowed " + quote(acc.getName()) + " to relink as constant.");
}
}
return obj;
}
private Access getOrCreateSwitchPoint(final String name) {
Access acc = map.get(name);
if (acc != null) {
return acc;
}
final SwitchPoint sp = new SwitchPoint();
map.put(name, acc = new Access(name, sp));
return acc;
}
/**
* Called from script object on property deletion to erase a property
* that might be linked as MethodHandle.constant and force relink
* @param name name of property
*/
void delete(final String name) {
final Access acc = map.get(name);
if (acc != null) {
acc.invalidateForever();
}
}
/**
* Receiver guard is used if we extend the global constants to script objects in general.
* As the property can have different values in different script objects, while Global is
* by definition a singleton, we need this for ScriptObject constants (currently disabled)
*
* TODO: Note - this seems to cause memory leaks. Use weak references? But what is leaking seems
* to be the Access objects, which isn't the case for Globals. Weird.
*
* @param acc access
* @param boundReceiver the receiver bound to the callsite
* @param receiver the receiver to check against
*
* @return true if this receiver is still the one we bound to the callsite
*/
@SuppressWarnings("unused")
private static boolean receiverGuard(final Access acc, final Object boundReceiver, final Object receiver) {
final boolean id = receiver == boundReceiver;
if (!id) {
acc.failGuard();
}
return id;
}
private static boolean isGlobalSetter(final ScriptObject receiver, final FindProperty find) {
if (find == null) {
return receiver.isScope();
}
return find.getOwner().isGlobal();
}
/**
* Augment a setter with switchpoint for invalidating its getters, should the setter be called
*
* @param find property lookup
* @param inv normal guarded invocation for this setter, as computed by the ScriptObject linker
* @param desc callsite descriptr
* @param request link request
*
* @return null if failed to set up constant linkage
*/
GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
if (GLOBAL_ONLY && !isGlobalSetter(receiver, find)) {
return null;
}
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final Access acc = getOrCreateSwitchPoint(name);
if (LOG.isEnabled()) {
LOG.fine("Trying to link constant SETTER ", acc);
}
if (!acc.mayRetry()) {
LOG.info("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
return null;
}
assert acc.mayRetry();
if (acc.hasBeenInvalidated()) {
LOG.info("New chance for " + acc);
acc.newSwitchPoint();
}
assert !acc.hasBeenInvalidated();
// if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
final MethodHandle target = inv.getInvocation();
final Class<?> receiverType = target.type().parameterType(0);
final MethodHandle invalidator = MH.asType(INVALIDATE_SP, INVALIDATE_SP.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
assert inv.getSwitchPoint() == null : inv.getSwitchPoint();
LOG.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
}
/**
* Try to reuse constant method handles for getters
* @param c constant value
* @return method handle (with dummy receiver) that returns this constant
*/
private static MethodHandle constantGetter(final Object c) {
return MH.dropArguments(JSType.unboxConstant(c), 0, Object.class);
}
/**
* Try to turn a getter into a MethodHandle.constant, if possible
*
* @param find property lookup
* @param receiver receiver
* @param desc callsite descriptor
* @param request link request
* @param operator operator
*
* @return resulting getter, or null if failed to create constant
*/
GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
if (GLOBAL_ONLY && !find.getOwner().isGlobal()) {
return null;
}
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ?
NashornCallSiteDescriptor.getProgramPoint(desc) :
UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
final boolean isOptimistic = programPoint != UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
final Class<?> retType = desc.getMethodType().returnType();
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final Access acc = getOrCreateSwitchPoint(name);
LOG.fine("Starting to look up object value " + name);
final Object c = find.getObjectValue();
if (LOG.isEnabled()) {
LOG.fine("Trying to link constant GETTER " + acc + " value = " + c);
}
if (acc.hasBeenInvalidated() || acc.guardFailed()) {
LOG.fine("*** GET: Giving up on " + quote(name) + " - retry count has exceeded");
return null;
}
final MethodHandle cmh = constantGetter(c);
MethodHandle mh;
MethodHandle guard;
if (isOptimistic) {
if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
//widen return type - this is pessimistic, so it will always work
mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
} else {
//immediately invalidate - we asked for a too wide constant as a narrower one
mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
}
} else {
//pessimistic return type filter
mh = Lookup.filterReturnType(cmh, retType);
}
if (find.getOwner().isGlobal()) {
guard = null;
} else {
guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
}
if (LOG.isEnabled()) {
LOG.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
mh = MethodHandleFactory.addDebugPrintout(LOG, Level.FINE, mh, "get const " + acc);
}
return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
}
}

@ -26,6 +26,7 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
@ -280,7 +281,7 @@ public enum JSType {
}
if (obj instanceof ScriptObject) {
return (obj instanceof ScriptFunction) ? JSType.FUNCTION : JSType.OBJECT;
return obj instanceof ScriptFunction ? JSType.FUNCTION : JSType.OBJECT;
}
if (obj instanceof Boolean) {
@ -438,7 +439,7 @@ public enum JSType {
*
* @return the string form of the primitive form of the object
*/
public static String toPrimitiveToString(Object obj) {
public static String toPrimitiveToString(final Object obj) {
return toString(toPrimitive(obj));
}
@ -900,7 +901,7 @@ public enum JSType {
}
// Minimum and maximum range between which every long value can be precisely represented as a double.
private static final long MAX_PRECISE_DOUBLE = (1L << 53);
private static final long MAX_PRECISE_DOUBLE = 1L << 53;
private static final long MIN_PRECISE_DOUBLE = -MAX_PRECISE_DOUBLE;
/**
@ -972,7 +973,7 @@ public enum JSType {
* @return a uint16
*/
public static int toUint16(final long num) {
return ((int)num) & 0xffff;
return (int)num & 0xffff;
}
/**
@ -982,7 +983,7 @@ public enum JSType {
* @return a uint16
*/
public static int toUint16(final double num) {
return ((int)doubleToInt32(num)) & 0xffff;
return (int)doubleToInt32(num) & 0xffff;
}
private static long doubleToInt32(final double num) {
@ -996,7 +997,7 @@ public enum JSType {
return 0;
}
// This is rather slow and could probably be sped up using bit-fiddling.
final double d = (num >= 0) ? Math.floor(num) : Math.ceil(num);
final double d = num >= 0 ? Math.floor(num) : Math.ceil(num);
return (long)(d % INT32_LIMIT);
}
@ -1285,7 +1286,7 @@ public enum JSType {
public static int addExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
try {
return Math.addExact(x, y);
} catch (ArithmeticException e) {
} catch (final ArithmeticException e) {
throw new UnwarrantedOptimismException((long)x + (long)y, programPoint);
}
}
@ -1305,7 +1306,7 @@ public enum JSType {
public static long addExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
try {
return Math.addExact(x, y);
} catch (ArithmeticException e) {
} catch (final ArithmeticException e) {
throw new UnwarrantedOptimismException((double)x + (double)y, programPoint);
}
}
@ -1325,7 +1326,7 @@ public enum JSType {
public static int subExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
try {
return Math.subtractExact(x, y);
} catch (ArithmeticException e) {
} catch (final ArithmeticException e) {
throw new UnwarrantedOptimismException((long)x - (long)y, programPoint);
}
}
@ -1345,7 +1346,7 @@ public enum JSType {
public static long subExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
try {
return Math.subtractExact(x, y);
} catch (ArithmeticException e) {
} catch (final ArithmeticException e) {
throw new UnwarrantedOptimismException((double)x - (double)y, programPoint);
}
}
@ -1365,7 +1366,7 @@ public enum JSType {
public static int mulExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
try {
return Math.multiplyExact(x, y);
} catch (ArithmeticException e) {
} catch (final ArithmeticException e) {
throw new UnwarrantedOptimismException((long)x * (long)y, programPoint);
}
}
@ -1682,4 +1683,47 @@ public enum JSType {
}
}
/**
* Create a method handle constant of the correct primitive type
* for a constant object
* @param o object
* @return constant function that returns object
*/
public static MethodHandle unboxConstant(final Object o) {
if (o != null) {
if (o.getClass() == Integer.class) {
return MH.constant(int.class, ((Integer)o).intValue());
} else if (o.getClass() == Long.class) {
return MH.constant(long.class, ((Long)o).longValue());
} else if (o.getClass() == Double.class) {
return MH.constant(double.class, ((Double)o).doubleValue());
}
}
return MH.constant(Object.class, o);
}
/**
* Get the unboxed (primitive) type for an object
* @param o object
* @return primive type or Object.class if not primitive
*/
public static Class<?> unboxedFieldType(final Object o) {
if (OBJECT_FIELDS_ONLY) {
return Object.class;
}
if (o == null) {
return Object.class;
} else if (o.getClass() == Integer.class) {
return int.class;
} else if (o.getClass() == Long.class) {
return long.class;
} else if (o.getClass() == Double.class) {
return double.class;
} else {
return Object.class;
}
}
}

@ -213,6 +213,11 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
@Override
public String toString() {
return super.toString() + '@' + functionNodeId;
}
@Override
public String toStringVerbose() {
final StringBuilder sb = new StringBuilder();
sb.append("fid=").append(functionNodeId).append(' ');
@ -309,6 +314,13 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null, sourceURL);
}
private static String getShortDescriptor(final Object value) {
if (value.getClass() == Object.class) {
return "O";
}
return value.getClass().getSimpleName();
}
private static String stringifyInvalidations(final Map<Integer, Type> ipp) {
if (ipp == null) {
return "";
@ -320,7 +332,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
sb.append('[').
append(entry.getKey()).
append("->").
append(entry.getValue().getShortDescriptor()).
append(getShortDescriptor(entry.getValue())).
append(']');
if (iter.hasNext()) {
sb.append(' ');

@ -35,6 +35,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
@ -169,6 +170,7 @@ public class RewriteException extends Exception {
private Object getReturnValueNonDestructive() {
return getUOE().getReturnValueNonDestructive();
}
/**
* Get return type
* @return return type
@ -213,13 +215,18 @@ public class RewriteException extends Exception {
if (returnValue == null) {
return "null";
}
final String str = returnValue.toString();
return returnValue instanceof Long ? (str + 'L') : str;
String str = returnValue.toString();
if (returnValue instanceof String) {
str = '\'' + str + '\'';
} else if (returnValue instanceof Long) {
str = str + 'l';
}
return str;
}
@Override
public String getMessage() {
return "programPoint=" + getProgramPoint() + " slots=" + (byteCodeSlots == null ? "null" : Arrays.asList(byteCodeSlots)) + ", returnValue=" + stringify(getReturnValueNonDestructive()) + ", returnType=" + getReturnType();
return getMessage(false);
}
/**
@ -227,7 +234,38 @@ public class RewriteException extends Exception {
* @return short message
*/
public String getMessageShort() {
return "[programPoint=" + getProgramPoint() + " returnType=" + getReturnType() + " (" + stringify(getReturnValueNonDestructive()) + ")]";
return getMessage(true);
}
private String getMessage(final boolean isShort) {
final StringBuilder sb = new StringBuilder();
//program point
sb.append("[pp=").
append(getProgramPoint()).
append(", ");
//slot contents
if (!isShort) {
final Object[] slots = getByteCodeSlots();
if (slots != null) {
sb.append("slots=").
append(Arrays.asList(slots)).
append(", ");
}
}
//return type
sb.append("type=").
append(getReturnType()).
append(", ");
//return value
sb.append("value=").
append(stringify(getReturnValueNonDestructive())).
append(")]");
return sb.toString();
}
}

@ -1,3 +1,28 @@
/*
* Copyright (c) 2010, 2014, 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;
import java.util.logging.Level;

@ -179,6 +179,14 @@ public abstract class ScriptFunctionData {
*/
@Override
public String toString() {
return name.isEmpty() ? "<anonymous>" : name;
}
/**
* Verbose description of data
* @return verbose description
*/
public String toStringVerbose() {
final StringBuilder sb = new StringBuilder();
sb.append("name='").

@ -62,6 +62,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@ -119,6 +120,9 @@ public abstract class ScriptObject implements PropertyAccess {
/** Is length property not-writable? */
public static final int IS_LENGTH_NOT_WRITABLE = 1 << 3;
/** Is this a builtin object? */
public static final int IS_BUILTIN = 1 << 4;
/**
* Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and
* {@link ScriptObject#objectSpill} when full
@ -254,6 +258,14 @@ public abstract class ScriptObject implements PropertyAccess {
this.spillLength = spillAllocationLength(primitiveSpill.length);
}
/**
* Check whether this is a global object
* @return true if global
*/
protected boolean isGlobal() {
return false;
}
private static int alignUp(final int size, final int alignment) {
return size + alignment - 1 & ~(alignment - 1);
}
@ -906,9 +918,11 @@ public abstract class ScriptObject implements PropertyAccess {
if (property instanceof UserAccessorProperty) {
((UserAccessorProperty)property).setAccessors(this, getMap(), null);
}
GlobalConstants.instance().delete(property.getKey());
return true;
}
}
}
/**
@ -1549,6 +1563,21 @@ public abstract class ScriptObject implements PropertyAccess {
return (flags & IS_SCOPE) != 0;
}
/**
* Tag this script object as built in
*/
public final void setIsBuiltin() {
flags |= IS_BUILTIN;
}
/**
* Check if this script object is built in
* @return true if build in
*/
public final boolean isBuiltin() {
return (flags & IS_BUILTIN) != 0;
}
/**
* Clears the properties from a ScriptObject
* (java.util.Map-like method to help ScriptObjectMirror implementation)
@ -1879,7 +1908,12 @@ public abstract class ScriptObject implements PropertyAccess {
default:
throw new AssertionError(operator); // never invoked with any other operation
}
}
}
final GuardedInvocation inv = GlobalConstants.instance().findGetMethod(find, this, desc, request, operator);
if (inv != null) {
return inv;
}
final Class<?> returnType = desc.getMethodType().returnType();
final Property property = find.getProperty();
@ -2064,7 +2098,14 @@ public abstract class ScriptObject implements PropertyAccess {
}
}
return new SetMethodCreator(this, find, desc, explicitInstanceOfCheck).createGuardedInvocation();
final GuardedInvocation inv = new SetMethodCreator(this, find, desc, explicitInstanceOfCheck).createGuardedInvocation();
final GuardedInvocation cinv = GlobalConstants.instance().findSetMethod(find, this, inv, desc, request);
if (cinv != null) {
return cinv;
}
return inv;
}
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {

@ -42,6 +42,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
@ -119,7 +120,6 @@ public final class ScriptRuntime {
return (int)d;
}
}
return deflt;
}

@ -174,7 +174,7 @@ final class SetMethodCreator {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
//fast type specific setter
MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already
final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already
//slow setter, that calls ScriptObject.set with appropraite type and key name
MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)];
@ -188,7 +188,7 @@ final class SetMethodCreator {
MethodHandle casMap = MH.insertArguments(ScriptObject.CAS_MAP, 1, oldMap, newMap);
casMap = MH.dropArguments(casMap, 1, type);
casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class));
MethodHandle casGuard = MH.guardWithTest(casMap, fastSetter, slowSetter);
final MethodHandle casGuard = MH.guardWithTest(casMap, fastSetter, slowSetter);
//outermost level needs an extendable check. if object can be extended, guard is true and
//we can run the cas setter. The setter goes to "nop" VOID_RETURN if false or throws an

@ -270,9 +270,16 @@ public final class WithObject extends ScriptObject implements Scope {
}
private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name, final ScriptObject owner) {
final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
return link.replaceMethods(filterReceiver(newLink.getInvocation(), WITHSCOPEFILTER),
NashornGuards.combineGuards(expressionGuard(name, owner), filterGuardReceiver(newLink, WITHSCOPEFILTER)));
final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
final MethodHandle expressionGuard = expressionGuard(name, owner);
final MethodHandle filterGuardReceiver = filterGuardReceiver(newLink, WITHSCOPEFILTER);
return link.replaceMethods(
filterReceiver(
newLink.getInvocation(),
WITHSCOPEFILTER),
NashornGuards.combineGuards(
expressionGuard,
filterGuardReceiver));
}
private static MethodHandle filterGuardReceiver(final GuardedInvocation link, final MethodHandle receiverFilter) {

@ -174,7 +174,13 @@ public final class NashornGuards {
* @return true if both guard1 and guard2 returned true
*/
public static MethodHandle combineGuards(final MethodHandle guard1, final MethodHandle guard2) {
return MH.guardWithTest(guard1, guard2, MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class));
if (guard1 == null) {
return guard2;
} else if (guard2 == null) {
return guard1;
} else {
return MH.guardWithTest(guard1, guard2, MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class));
}
}
@SuppressWarnings("unused")

@ -43,7 +43,7 @@ for each(var color in colors) {
var capitals = new java.util.LinkedHashMap()
capitals.Sweden = "Stockholm"
capitals.Hungary = "Budapet"
capitals.Hungary = "Budapest"
capitals.Croatia = "Zagreb"
for(var key in capitals) {

@ -5,8 +5,8 @@ red
purple
pink
capital of Sweden is Stockholm
capital of Hungary is Budapet
capital of Hungary is Budapest
capital of Croatia is Zagreb
Stockholm
Budapet
Budapest
Zagreb

@ -26,33 +26,35 @@
*
* @test
* @run
* @fork
*/
// var with getter side effect
Object.defineProperty(this, "a", { get: function() {print("get a"); return 1; }});
Object.defineProperty(this, "a1", { get: function() {print("get a"); return 1; }});
// var with both getter and conversion side effect
Object.defineProperty(this, "b", { get: function() {print("get b"); return {valueOf: function() { print("conv b"); return 10; }}; }});
Object.defineProperty(this, "b1", { get: function() {print("get b"); return {valueOf: function() { print("conv b"); return 10; }}; }});
(function() {
// var with toPrimitive conversion side effect
var c = {valueOf: function() { print("conv c"); return 100; }};
print(b + (c + a));
print(b + (c + b));
print(b + (a + b));
print(b + (b + c));
print(b + (b + c));
print(b + (c + (a - b)));
print(b + (c + (c - b)));
print(b + (c + (b - c)));
print(b + (b + (a ? 2 : 3)));
print(b + (b + (b ? 2 : 3)));
print(b + (b + (c ? 2 : 3)));
print(b + ((-c) + (-a)));
print(b + ((-c) + (-b)));
print(b + ((-c) + (-c)));
try { print(b + new a); } catch (e) {}
try { print(b + new b); } catch (e) {}
try { print(b + new c); } catch (e) {}
var c1 = {valueOf: function() { print("conv c"); return 100; }};
print("start");
print(b1 + (c1 + a1));
print("done with first");
print(b1 + (c1 + b1));
print(b1 + (a1 + b1));
print(b1 + (b1 + c1));
print(b1 + (b1 + c1));
print(b1 + (c1 + (a1 - b1)));
print(b1 + (c1 + (c1 - b1)));
print(b1 + (c1 + (b1 - c1)));
print(b1 + (b1 + (a1 ? 2 : 3)));
print(b1 + (b1 + (b1 ? 2 : 3)));
print(b1 + (b1 + (c1 ? 2 : 3)));
print(b1 + ((-c1) + (-a1)));
print(b1 + ((-c1) + (-b1)));
print(b1 + ((-c1) + (-c1)));
try { print(b1 + new a1); } catch (e) {}
try { print(b1 + new b1); } catch (e) {}
try { print(b1 + new c1); } catch (e) {}
})();

@ -1,8 +1,10 @@
start
get b
get a
conv c
conv b
111
done with first
get b
get b
conv c

@ -35,7 +35,7 @@ var WeakReferenceArray = Java.type("java.lang.ref.WeakReference[]");
var refArray = new WeakReferenceArray(N);
for (var i = 0; i < N; i ++) {
var object = new java.lang.Object();
var object = new java.awt.Color(0,0,0);//lang.Object();
array[i] = object;
refArray[i] = new java.lang.ref.WeakReference(object);
}
@ -46,13 +46,13 @@ for (var i = 0; i < N; i ++) {
delete array[i];
}
java.lang.System.gc();
java.lang.System.gc();
java.lang.System.gc();
for (var i = 0; i < N; i ++) {
if (refArray[i].get() != null) {
print("Reference found at " + i);
exit(0);
print("Reference found at " + i + " " + refArray + " " + refArray[i].get());
}
}

@ -47,7 +47,7 @@ public class ScopeTest {
public void createBindingsTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
Bindings b = e.createBindings();
final Bindings b = e.createBindings();
b.put("foo", 42.0);
Object res = null;
try {
@ -64,7 +64,7 @@ public class ScopeTest {
public void engineScopeTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE);
final Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE);
// check few ECMA standard built-in global properties
assertNotNull(engineScope.get("Object"));
@ -112,8 +112,8 @@ public class ScopeTest {
newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
try {
Object obj1 = e.eval("Object");
Object obj2 = e.eval("Object", newCtxt);
final Object obj1 = e.eval("Object");
final Object obj2 = e.eval("Object", newCtxt);
Assert.assertNotEquals(obj1, obj2);
Assert.assertNotNull(obj1);
Assert.assertNotNull(obj2);
@ -138,10 +138,12 @@ public class ScopeTest {
e.eval("y = new Object()");
e.eval("y = new Object()", origCtxt);
Object y1 = origCtxt.getAttribute("y");
Object y2 = newCtxt.getAttribute("y");
final Object y1 = origCtxt.getAttribute("y");
final Object y2 = newCtxt.getAttribute("y");
Assert.assertNotEquals(y1, y2);
Assert.assertNotEquals(e.eval("y"), e.eval("y", origCtxt));
final Object yeval1 = e.eval("y");
final Object yeval2 = e.eval("y", origCtxt);
Assert.assertNotEquals(yeval1, yeval2);
Assert.assertEquals("[object Object]", y1.toString());
Assert.assertEquals("[object Object]", y2.toString());
} catch (final ScriptException se) {
@ -159,7 +161,7 @@ public class ScopeTest {
final ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE);
// we are using a new bindings - so it should have 'func' defined
Object value = e.eval("typeof func", newContext);
final Object value = e.eval("typeof func", newContext);
assertTrue(value.equals("undefined"));
}
@ -210,7 +212,7 @@ public class ScopeTest {
assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction());
// check new global instance created has engine.js definitions
Bindings b = e.createBindings();
final Bindings b = e.createBindings();
value = b.get("__noSuchProperty__");
assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction());
value = b.get("print");
@ -231,7 +233,7 @@ public class ScopeTest {
assertTrue(e.eval("x", ctx).equals("hello"));
// try some arbitray Bindings for ENGINE_SCOPE
Bindings sb = new SimpleBindings();
final Bindings sb = new SimpleBindings();
ctx.setBindings(sb, ScriptContext.ENGINE_SCOPE);
// GLOBAL_SCOPE mapping should be visible from non-default ScriptContext eval
@ -305,7 +307,7 @@ public class ScopeTest {
t1.join();
t2.join();
Object obj3 = e.eval("delete foo; foo = 'newer context';", newCtxt);
final Object obj3 = e.eval("delete foo; foo = 'newer context';", newCtxt);
assertEquals(obj3, "newer context");
final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
@ -342,7 +344,7 @@ public class ScopeTest {
for (int i = 0; i < 1000; i++) {
assertEquals(e.eval(sharedScript, origContext), (double)i);
}
} catch (ScriptException se) {
} catch (final ScriptException se) {
fail(se.toString());
}
}
@ -354,7 +356,7 @@ public class ScopeTest {
for (int i = 2; i < 1000; i++) {
assertEquals(e.eval(sharedScript, newCtxt), (double)i);
}
} catch (ScriptException se) {
} catch (final ScriptException se) {
fail(se.toString());
}
}
@ -377,8 +379,8 @@ public class ScopeTest {
final ScriptContext newCtxt = new SimpleScriptContext();
newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
Object obj1 = e.eval("String.prototype.foo = 'original context';", origContext);
Object obj2 = e.eval("String.prototype.foo = 'new context';", newCtxt);
final Object obj1 = e.eval("String.prototype.foo = 'original context';", origContext);
final Object obj2 = e.eval("String.prototype.foo = 'new context';", newCtxt);
assertEquals(obj1, "original context");
assertEquals(obj2, "new context");
final String sharedScript = "''.foo";
@ -390,7 +392,7 @@ public class ScopeTest {
t1.join();
t2.join();
Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt);
final Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt);
assertEquals(obj3, "newer context");
final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
@ -555,7 +557,7 @@ public class ScopeTest {
for (int i = 0; i < iterations; i++) {
assertEquals(engine.eval(source, context), expected);
}
} catch (ScriptException se) {
} catch (final ScriptException se) {
throw new RuntimeException(se);
}
}