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:
parent
e0f84784f2
commit
379d9f239f
nashorn
bin
src/jdk/nashorn
api/scripting
internal
codegen
lookup
objects
runtime
test
script
basic
currently-failing
src/jdk/nashorn/api/scripting
32
nashorn/bin/runoptdualcatch9.sh
Normal file
32
nashorn/bin/runoptdualcatch9.sh
Normal file
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
418
nashorn/src/jdk/nashorn/internal/runtime/GlobalConstants.java
Normal file
418
nashorn/src/jdk/nashorn/internal/runtime/GlobalConstants.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user