--- a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AbstractJavaLinker.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AbstractJavaLinker.java Wed Dec 20 17:36:50 2017 +0100
@@ -414,20 +414,21 @@
protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req)
throws Exception {
- if (!req.namespaces.isEmpty()) {
- final Namespace ns = req.namespaces.get(0);
- final Operation op = req.baseOperation;
- if (op == StandardOperation.GET) {
- if (ns == StandardNamespace.PROPERTY) {
- return getPropertyGetter(req.popNamespace());
- } else if (ns == StandardNamespace.METHOD) {
- return getMethodGetter(req.popNamespace());
- }
- } else if (op == StandardOperation.SET && ns == StandardNamespace.PROPERTY) {
- return getPropertySetter(req.popNamespace());
+ if (req.namespaces.isEmpty()) {
+ return null;
+ }
+ final Namespace ns = req.namespaces.get(0);
+ final Operation op = req.baseOperation;
+ if (op == StandardOperation.GET) {
+ if (ns == StandardNamespace.PROPERTY) {
+ return getPropertyGetter(req.popNamespace());
+ } else if (ns == StandardNamespace.METHOD) {
+ return getMethodGetter(req.popNamespace());
}
+ } else if (op == StandardOperation.SET && ns == StandardNamespace.PROPERTY) {
+ return getPropertySetter(req.popNamespace());
}
- return null;
+ return getNextComponent(req.popNamespace());
}
GuardedInvocationComponent getNextComponent(final ComponentLinkRequest req) throws Exception {
--- a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java Wed Dec 20 17:36:50 2017 +0100
@@ -136,24 +136,21 @@
@Override
protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req) throws Exception {
- final GuardedInvocationComponent superGic = super.getGuardedInvocationComponent(req);
- if(superGic != null) {
- return superGic;
+ if (req.namespaces.isEmpty()) {
+ return null;
}
- if (!req.namespaces.isEmpty()) {
+ final Namespace ns = req.namespaces.get(0);
+ if (ns == StandardNamespace.ELEMENT) {
final Operation op = req.baseOperation;
- final Namespace ns = req.namespaces.get(0);
- if (ns == StandardNamespace.ELEMENT) {
- if (op == StandardOperation.GET) {
- return getElementGetter(req.popNamespace());
- } else if (op == StandardOperation.SET) {
- return getElementSetter(req.popNamespace());
- } else if (op == StandardOperation.REMOVE) {
- return getElementRemover(req.popNamespace());
- }
+ if (op == StandardOperation.GET) {
+ return getElementGetter(req.popNamespace());
+ } else if (op == StandardOperation.SET) {
+ return getElementSetter(req.popNamespace());
+ } else if (op == StandardOperation.REMOVE) {
+ return getElementRemover(req.popNamespace());
}
}
- return null;
+ return super.getGuardedInvocationComponent(req);
}
@Override
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Dec 20 17:36:50 2017 +0100
@@ -59,6 +59,7 @@
import java.util.Map;
import java.util.Set;
import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CatchNode;
@@ -735,72 +736,13 @@
@Override
public Node leaveUnaryNode(final UnaryNode unaryNode) {
- switch (unaryNode.tokenType()) {
- case DELETE:
- return leaveDELETE(unaryNode);
- case TYPEOF:
+ if (unaryNode.tokenType() == TokenType.TYPEOF) {
return leaveTYPEOF(unaryNode);
- default:
+ } else {
return super.leaveUnaryNode(unaryNode);
}
}
- private Node leaveDELETE(final UnaryNode unaryNode) {
- final FunctionNode currentFunctionNode = lc.getCurrentFunction();
- final boolean strictMode = currentFunctionNode.isStrict();
- final Expression rhs = unaryNode.getExpression();
- final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
-
- Request request = Request.DELETE;
- final List<Expression> args = new ArrayList<>();
-
- if (rhs instanceof IdentNode) {
- final IdentNode ident = (IdentNode)rhs;
- // If this is a declared variable or a function parameter, delete always fails (except for globals).
- final String name = ident.getName();
- final Symbol symbol = ident.getSymbol();
-
- if (symbol.isThis()) {
- // Can't delete "this", ignore and return true
- return LiteralNode.newInstance(unaryNode, true);
- }
- final Expression literalNode = LiteralNode.newInstance(unaryNode, name);
- final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
-
- if (!failDelete) {
- args.add(compilerConstantIdentifier(SCOPE));
- }
- args.add(literalNode);
- args.add(strictFlagNode);
-
- if (failDelete) {
- request = Request.FAIL_DELETE;
- } else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) {
- request = Request.SLOW_DELETE;
- }
- } else if (rhs instanceof AccessNode) {
- final Expression base = ((AccessNode)rhs).getBase();
- final String property = ((AccessNode)rhs).getProperty();
-
- args.add(base);
- args.add(LiteralNode.newInstance(unaryNode, property));
- args.add(strictFlagNode);
-
- } else if (rhs instanceof IndexNode) {
- final IndexNode indexNode = (IndexNode)rhs;
- final Expression base = indexNode.getBase();
- final Expression index = indexNode.getIndex();
-
- args.add(base);
- args.add(index);
- args.add(strictFlagNode);
-
- } else {
- throw new AssertionError("Unexpected delete with " + rhs.getClass().getName() + " expression");
- }
- return new RuntimeNode(unaryNode, request, args);
- }
-
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForInOrOf()) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Dec 20 17:36:50 2017 +0100
@@ -151,6 +151,7 @@
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
@@ -1142,6 +1143,12 @@
}
@Override
+ public boolean enterDELETE(final UnaryNode unaryNode) {
+ loadDELETE(unaryNode);
+ return false;
+ }
+
+ @Override
public boolean enterEQ(final BinaryNode binaryNode) {
loadCmp(binaryNode, Condition.EQ);
return false;
@@ -3791,6 +3798,53 @@
}
}
+ public void loadDELETE(final UnaryNode unaryNode) {
+ final Expression expression = unaryNode.getExpression();
+ if (expression instanceof IdentNode) {
+ final IdentNode ident = (IdentNode)expression;
+ final Symbol symbol = ident.getSymbol();
+ final String name = ident.getName();
+
+ if (symbol.isThis()) {
+ // Can't delete "this", ignore and return true
+ if (!lc.popDiscardIfCurrent(unaryNode)) {
+ method.load(true);
+ }
+ } else if (lc.getCurrentFunction().isStrict()) {
+ // All other scope identifier delete attempts fail for strict mode
+ method.load(name);
+ method.invoke(ScriptRuntime.STRICT_FAIL_DELETE);
+ } else if (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))) {
+ // If symbol is a function parameter, or a declared non-global variable, delete is a no-op and returns false.
+ if (!lc.popDiscardIfCurrent(unaryNode)) {
+ method.load(false);
+ }
+ } else {
+ method.loadCompilerConstant(SCOPE);
+ method.load(name);
+ if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) {
+ method.invoke(ScriptRuntime.SLOW_DELETE);
+ } else {
+ method.load(false); // never strict here; that was handled with STRICT_FAIL_DELETE above.
+ method.invoke(ScriptObject.DELETE);
+ }
+ }
+ } else if (expression instanceof BaseNode) {
+ loadExpressionAsObject(((BaseNode)expression).getBase());
+ if (expression instanceof AccessNode) {
+ final AccessNode accessNode = (AccessNode) expression;
+ method.dynamicRemove(accessNode.getProperty(), getCallSiteFlags(), accessNode.isIndex());
+ } else if (expression instanceof IndexNode) {
+ loadExpressionAsObject(((IndexNode) expression).getIndex());
+ method.dynamicRemoveIndex(getCallSiteFlags());
+ } else {
+ throw new AssertionError(expression.getClass().getName());
+ }
+ } else {
+ throw new AssertionError(expression.getClass().getName());
+ }
+ }
+
public void loadADD(final BinaryNode binaryNode, final TypeBounds resultBounds) {
new OptimisticOperation(binaryNode, resultBounds) {
@Override
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Dec 20 17:36:50 2017 +0100
@@ -43,6 +43,7 @@
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BreakNode;
@@ -1093,9 +1094,15 @@
@Override
public boolean enterUnaryNode(final UnaryNode unaryNode) {
final Expression expr = unaryNode.getExpression();
- final LvarType unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType());
- if(unaryNode.isSelfModifying() && expr instanceof IdentNode) {
- onSelfAssignment((IdentNode)expr, unaryType);
+ final LvarType unaryType;
+ if (unaryNode.tokenType() == TokenType.DELETE && expr instanceof IdentNode) {
+ // not visiting deleted identifiers; they don't count as use
+ unaryType = toLvarType(unaryNode.getType());
+ } else {
+ unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType());
+ if (unaryNode.isSelfModifying() && expr instanceof IdentNode) {
+ onSelfAssignment((IdentNode) expr, unaryType);
+ }
}
typeStack.push(unaryType);
return false;
@@ -1348,6 +1355,12 @@
return true;
}
+ @Override
+ public boolean enterUnaryNode(final UnaryNode unaryNode) {
+ // not visiting deleted identifiers; they don't count as use
+ return !(unaryNode.tokenType() == TokenType.DELETE && unaryNode.getExpression() instanceof IdentNode);
+ }
+
@SuppressWarnings("fallthrough")
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Dec 20 17:36:50 2017 +0100
@@ -2203,7 +2203,7 @@
}
/**
- * Generate dynamic getter. Pop scope from stack. Push result
+ * Generate dynamic getter. Pop object from stack. Push result.
*
* @param valueType type of the value to set
* @param name name of property
@@ -2224,7 +2224,7 @@
type = Type.OBJECT; //promote e.g strings to object generic setter
}
- popType(Type.SCOPE);
+ popType(Type.OBJECT);
method.visitInvokeDynamicInsn(NameCodec.encode(name),
Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags | dynGetOperation(isMethod, isIndex));
@@ -2256,13 +2256,38 @@
convert(Type.OBJECT); //TODO bad- until we specialize boolean setters,
}
popType(type);
- popType(Type.SCOPE);
+ popType(Type.OBJECT);
method.visitInvokeDynamicInsn(NameCodec.encode(name),
methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags | dynSetOperation(isIndex));
}
- /**
+ /**
+ * Generate dynamic remover. Pop object from stack. Push result.
+ *
+ * @param name name of property
+ * @param flags call site flags
+ * @return the method emitter
+ */
+ MethodEmitter dynamicRemove(final String name, final int flags, final boolean isIndex) {
+ if (name.length() > LARGE_STRING_THRESHOLD) { // use removeIndex for extremely long names
+ return load(name).dynamicRemoveIndex(flags);
+ }
+
+ debug("dynamic_remove", name, Type.BOOLEAN, getProgramPoint(flags));
+
+ popType(Type.OBJECT);
+ // Type is widened to OBJECT then coerced back to BOOLEAN
+ method.visitInvokeDynamicInsn(NameCodec.encode(name),
+ Type.getMethodDescriptor(Type.OBJECT, Type.OBJECT), LINKERBOOTSTRAP, flags | dynRemoveOperation(isIndex));
+
+ pushType(Type.OBJECT);
+ convert(Type.BOOLEAN); //most probably a nop
+
+ return this;
+ }
+
+ /**
* Dynamic getter for indexed structures. Pop index and receiver from stack,
* generate appropriate signatures based on types
*
@@ -2342,6 +2367,35 @@
}
/**
+ * Dynamic remover for indexed structures. Pop index and receiver from stack,
+ * generate appropriate signatures based on types
+ *
+ * @param flags call site flags for getter
+ *
+ * @return the method emitter
+ */
+ MethodEmitter dynamicRemoveIndex(final int flags) {
+ debug("dynamic_remove_index", peekType(1), "[", peekType(), "]", getProgramPoint(flags));
+
+ Type index = peekType();
+ if (index.isObject() || index.isBoolean()) {
+ index = Type.OBJECT; //e.g. string->object
+ convert(Type.OBJECT);
+ }
+ popType();
+
+ popType(Type.OBJECT);
+
+ final String signature = Type.getMethodDescriptor(Type.OBJECT, Type.OBJECT /*e.g STRING->OBJECT*/, index);
+
+ method.visitInvokeDynamicInsn(EMPTY_NAME, signature, LINKERBOOTSTRAP, flags | dynRemoveOperation(true));
+ pushType(Type.OBJECT);
+ convert(Type.BOOLEAN);
+
+ return this;
+ }
+
+ /**
* Load a key value in the proper form.
*
* @param key
@@ -2520,6 +2574,10 @@
return isIndex ? NashornCallSiteDescriptor.SET_ELEMENT : NashornCallSiteDescriptor.SET_PROPERTY;
}
+ private static int dynRemoveOperation(final boolean isIndex) {
+ return isIndex ? NashornCallSiteDescriptor.REMOVE_ELEMENT : NashornCallSiteDescriptor.REMOVE_PROPERTY;
+ }
+
private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) {
final Type from = conversion.getFrom();
final Type to = conversion.getTo();
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java Wed Dec 20 17:36:50 2017 +0100
@@ -54,12 +54,6 @@
TYPEOF,
/** Reference error type */
REFERENCE_ERROR,
- /** Delete operator */
- DELETE(TokenType.DELETE, Type.BOOLEAN, 1),
- /** Delete operator for slow scopes */
- SLOW_DELETE(TokenType.DELETE, Type.BOOLEAN, 1, false),
- /** Delete operator that always fails -- see Lower */
- FAIL_DELETE(TokenType.DELETE, Type.BOOLEAN, 1, false),
/** === operator with at least one object */
EQ_STRICT(TokenType.EQ_STRICT, Type.BOOLEAN, 2, true),
/** == operator with at least one object */
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Wed Dec 20 17:36:50 2017 +0100
@@ -189,6 +189,8 @@
/** Method handle for generic property setter */
public static final Call GENERIC_SET = virtualCallNoLookup(ScriptObject.class, "set", void.class, Object.class, Object.class, int.class);
+ public static final Call DELETE = virtualCall(MethodHandles.lookup(), ScriptObject.class, "delete", boolean.class, Object.class, boolean.class);
+
static final MethodHandle[] SET_SLOW = new MethodHandle[] {
findOwnMH_V("set", void.class, Object.class, int.class, int.class),
findOwnMH_V("set", void.class, Object.class, double.class, int.class),
@@ -202,6 +204,9 @@
static final MethodHandle EXTENSION_CHECK = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class);
static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class);
+ private static final GuardedInvocation DELETE_GUARDED = new GuardedInvocation(MH.insertArguments(DELETE.methodHandle(), 2, false), NashornGuards.getScriptObjectGuard());
+ private static final GuardedInvocation DELETE_GUARDED_STRICT = new GuardedInvocation(MH.insertArguments(DELETE.methodHandle(), 2, true), NashornGuards.getScriptObjectGuard());
+
/**
* Constructor
*/
@@ -1869,6 +1874,13 @@
return desc.getOperation() instanceof NamedOperation
? findSetMethod(desc, request)
: findSetIndexMethod(desc, request);
+ case REMOVE:
+ final GuardedInvocation inv = NashornCallSiteDescriptor.isStrict(desc) ? DELETE_GUARDED_STRICT : DELETE_GUARDED;
+ final Object name = NamedOperation.getName(desc.getOperation());
+ if (name != null) {
+ return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
+ }
+ return inv;
case CALL:
return findCallMethod(desc, request);
case NEW:
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java Wed Dec 20 17:36:50 2017 +0100
@@ -136,6 +136,16 @@
public static final Call INVALIDATE_RESERVED_BUILTIN_NAME = staticCallNoLookup(ScriptRuntime.class, "invalidateReservedBuiltinName", void.class, String.class);
/**
+ * Used to perform failed delete under strict mode
+ */
+ public static final Call STRICT_FAIL_DELETE = staticCallNoLookup(ScriptRuntime.class, "strictFailDelete", boolean.class, String.class);
+
+ /**
+ * Used to find the scope for slow delete
+ */
+ public static final Call SLOW_DELETE = staticCallNoLookup(ScriptRuntime.class, "slowDelete", boolean.class, ScriptObject.class, String.class);
+
+ /**
* Converts a switch tag value to a simple integer. deflt value if it can't.
*
* @param tag Switch statement tag value.
@@ -780,87 +790,40 @@
}
/**
- * ECMA 11.4.1 - delete operation, generic implementation
+ * ECMA 11.4.1 - delete operator, implementation for slow scopes
*
- * @param obj object with property to delete
+ * This implementation of 'delete' walks the scope chain to find the scope that contains the
+ * property to be deleted, then invokes delete on it. Always used on scopes, never strict.
+ *
+ * @param obj top scope object
* @param property property to delete
- * @param strict are we in strict mode
*
* @return true if property was successfully found and deleted
*/
- public static boolean DELETE(final Object obj, final Object property, final Object strict) {
- if (obj instanceof ScriptObject) {
- return ((ScriptObject)obj).delete(property, Boolean.TRUE.equals(strict));
- }
-
- if (obj instanceof Undefined) {
- return ((Undefined)obj).delete(property, false);
- }
-
- if (obj == null) {
- throw typeError("cant.delete.property", safeToString(property), "null");
- }
-
- if (obj instanceof ScriptObjectMirror) {
- return ((ScriptObjectMirror)obj).delete(property);
- }
-
- if (JSType.isPrimitive(obj)) {
- return ((ScriptObject) JSType.toScriptObject(obj)).delete(property, Boolean.TRUE.equals(strict));
- }
-
- if (obj instanceof JSObject) {
- ((JSObject)obj).removeMember(Objects.toString(property));
- return true;
+ public static boolean slowDelete(final ScriptObject obj, final String property) {
+ ScriptObject sobj = obj;
+ while (sobj != null && sobj.isScope()) {
+ final FindProperty find = sobj.findProperty(property, false);
+ if (find != null) {
+ return sobj.delete(property, false);
+ }
+ sobj = sobj.getProto();
}
-
- // if object is not reference type, vacuously delete is successful.
- return true;
- }
-
- /**
- * ECMA 11.4.1 - delete operator, implementation for slow scopes
- *
- * This implementation of 'delete' walks the scope chain to find the scope that contains the
- * property to be deleted, then invokes delete on it.
- *
- * @param obj top scope object
- * @param property property to delete
- * @param strict are we in strict mode
- *
- * @return true if property was successfully found and deleted
- */
- public static boolean SLOW_DELETE(final Object obj, final Object property, final Object strict) {
- if (obj instanceof ScriptObject) {
- ScriptObject sobj = (ScriptObject) obj;
- final String key = property.toString();
- while (sobj != null && sobj.isScope()) {
- final FindProperty find = sobj.findProperty(key, false);
- if (find != null) {
- return sobj.delete(key, Boolean.TRUE.equals(strict));
- }
- sobj = sobj.getProto();
- }
- }
- return DELETE(obj, property, strict);
+ return obj.delete(property, false);
}
/**
* ECMA 11.4.1 - delete operator, special case
*
- * This is 'delete' that always fails. We have to check strict mode and throw error.
- * That is why this is a runtime function. Or else we could have inlined 'false'.
+ * This is 'delete' on a scope; it always fails under strict mode.
+ * It always throws an exception, but is declared to return a boolean
+ * to be compatible with the delete operator type.
*
* @param property property to delete
- * @param strict are we in strict mode
- *
- * @return false always
+ * @return nothing, always throws an exception.
*/
- public static boolean FAIL_DELETE(final Object property, final Object strict) {
- if (Boolean.TRUE.equals(strict)) {
- throw syntaxError("strict.cant.delete", safeToString(property));
- }
- return false;
+ public static boolean strictFailDelete(final String property) {
+ throw syntaxError("strict.cant.delete", property);
}
/**
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Wed Dec 20 17:36:50 2017 +0100
@@ -112,6 +112,11 @@
return findSetIndexMethod(desc);
}
return findSetMethod(desc);
+ case REMOVE:
+ if (!(desc.getOperation() instanceof NamedOperation)) {
+ return findDeleteIndexMethod(desc);
+ }
+ return findDeleteMethod(desc);
default:
}
return null;
@@ -124,6 +129,7 @@
private static final MethodHandle GET_METHOD = findOwnMH("get", Object.class, Object.class);
private static final MethodHandle SET_METHOD = MH.insertArguments(findOwnMH("set", void.class, Object.class, Object.class, int.class), 3, NashornCallSiteDescriptor.CALLSITE_STRICT);
+ private static final MethodHandle DELETE_METHOD = MH.insertArguments(findOwnMH("delete", boolean.class, Object.class, boolean.class), 2, false);
private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
return new GuardedInvocation(MH.insertArguments(GET_METHOD, 1, NashornCallSiteDescriptor.getOperand(desc)), UNDEFINED_GUARD).asType(desc);
@@ -141,6 +147,15 @@
return new GuardedInvocation(SET_METHOD, UNDEFINED_GUARD).asType(desc);
}
+ private static GuardedInvocation findDeleteMethod(final CallSiteDescriptor desc) {
+ return new GuardedInvocation(MH.insertArguments(DELETE_METHOD, 1, NashornCallSiteDescriptor.getOperand(desc)), UNDEFINED_GUARD).asType(desc);
+ }
+
+ private static GuardedInvocation findDeleteIndexMethod(final CallSiteDescriptor desc) {
+ return new GuardedInvocation(DELETE_METHOD, UNDEFINED_GUARD).asType(desc);
+ }
+
+
@Override
public Object get(final Object key) {
throw typeError("cant.read.property.of.undefined", ScriptRuntime.safeToString(key));
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Wed Dec 20 17:36:50 2017 +0100
@@ -31,7 +31,7 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;
-import javax.script.Bindings;
+import java.util.Objects;
import jdk.dynalink.CallSiteDescriptor;
import jdk.dynalink.Operation;
import jdk.dynalink.StandardOperation;
@@ -64,11 +64,10 @@
return canLinkTypeStatic(type);
}
- static boolean canLinkTypeStatic(final Class<?> type) {
- // can link JSObject also handles Map, Bindings to make
+ private static boolean canLinkTypeStatic(final Class<?> type) {
+ // can link JSObject also handles Map (this includes Bindings) to make
// sure those are not JSObjects.
return Map.class.isAssignableFrom(type) ||
- Bindings.class.isAssignableFrom(type) ||
JSObject.class.isAssignableFrom(type);
}
@@ -84,7 +83,7 @@
if (self instanceof JSObject) {
inv = lookup(desc, request, linkerServices);
inv = inv.replaceMethods(linkerServices.filterInternalObjects(inv.getInvocation()), inv.getGuard());
- } else if (self instanceof Map || self instanceof Bindings) {
+ } else if (self instanceof Map) {
// guard to make sure the Map or Bindings does not turn into JSObject later!
final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
inv = new GuardedInvocation(beanInv.getInvocation(),
@@ -116,6 +115,13 @@
return name != null ? findSetMethod(name) : findSetIndexMethod();
}
break;
+ case REMOVE:
+ if (NashornCallSiteDescriptor.hasStandardNamespace(desc)) {
+ return new GuardedInvocation(
+ name == null ? JSOBJECTLINKER_DEL : MH.insertArguments(JSOBJECTLINKER_DEL, 1, name),
+ IS_JSOBJECT_GUARD);
+ }
+ break;
case CALL:
return findCallMethod(desc);
case NEW:
@@ -206,6 +212,15 @@
}
}
+ @SuppressWarnings("unused")
+ private static boolean del(final Object jsobj, final Object key) {
+ if (jsobj instanceof ScriptObjectMirror) {
+ return ((ScriptObjectMirror)jsobj).delete(key);
+ }
+ ((JSObject) jsobj).removeMember(Objects.toString(key));
+ return true;
+ }
+
private static int getIndex(final Number n) {
final double value = n.doubleValue();
return JSType.isRepresentableAsInt(value) ? (int)value : -1;
@@ -245,6 +260,7 @@
private static final MethodHandle IS_JSOBJECT_GUARD = findOwnMH_S("isJSObject", boolean.class, Object.class);
private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH_S("get", Object.class, MethodHandle.class, Object.class, Object.class);
private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH_S("put", Void.TYPE, Object.class, Object.class, Object.class);
+ private static final MethodHandle JSOBJECTLINKER_DEL = findOwnMH_S("del", boolean.class, Object.class, Object.class);
// method handles of JSObject class
private static final MethodHandle JSOBJECT_GETMEMBER = findJSObjectMH_V("getMember", Object.class, String.class);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Wed Dec 20 17:36:50 2017 +0100
@@ -39,11 +39,13 @@
import jdk.dynalink.NamedOperation;
import jdk.dynalink.Operation;
import jdk.dynalink.beans.BeansLinker;
+import jdk.dynalink.beans.StaticClass;
import jdk.dynalink.linker.GuardedInvocation;
import jdk.dynalink.linker.GuardingDynamicLinker;
import jdk.dynalink.linker.GuardingTypeConverterFactory;
import jdk.dynalink.linker.LinkRequest;
import jdk.dynalink.linker.LinkerServices;
+import jdk.dynalink.linker.support.Guards;
import jdk.dynalink.linker.support.Lookup;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.ECMAException;
@@ -86,12 +88,16 @@
MH.dropArguments(EMPTY_PROP_SETTER, 0, Object.class);
private static final MethodHandle THROW_STRICT_PROPERTY_SETTER;
+ private static final MethodHandle THROW_STRICT_PROPERTY_REMOVER;
private static final MethodHandle THROW_OPTIMISTIC_UNDEFINED;
+ private static final MethodHandle MISSING_PROPERTY_REMOVER;
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
THROW_STRICT_PROPERTY_SETTER = lookup.findOwnStatic("throwStrictPropertySetter", void.class, Object.class, Object.class);
+ THROW_STRICT_PROPERTY_REMOVER = lookup.findOwnStatic("throwStrictPropertyRemover", boolean.class, Object.class, Object.class);
THROW_OPTIMISTIC_UNDEFINED = lookup.findOwnStatic("throwOptimisticUndefined", Object.class, int.class);
+ MISSING_PROPERTY_REMOVER = lookup.findOwnStatic("missingPropertyRemover", boolean.class, Object.class, Object.class);
}
private static GuardedInvocation linkBean(final LinkRequest linkRequest) throws Exception {
@@ -124,6 +130,7 @@
static MethodHandle linkMissingBeanMember(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
final String operand = NashornCallSiteDescriptor.getOperand(desc);
+ final boolean strict = NashornCallSiteDescriptor.isStrict(desc);
switch (NashornCallSiteDescriptor.getStandardOperation(desc)) {
case GET:
if (NashornCallSiteDescriptor.isOptimistic(desc)) {
@@ -133,13 +140,17 @@
}
return getInvocation(EMPTY_ELEM_GETTER, linkerServices, desc);
case SET:
- final boolean strict = NashornCallSiteDescriptor.isStrict(desc);
if (strict) {
return adaptThrower(bindOperand(THROW_STRICT_PROPERTY_SETTER, operand), desc);
} else if (operand != null) {
return getInvocation(EMPTY_PROP_SETTER, linkerServices, desc);
}
return getInvocation(EMPTY_ELEM_SETTER, linkerServices, desc);
+ case REMOVE:
+ if (strict) {
+ return adaptThrower(bindOperand(THROW_STRICT_PROPERTY_REMOVER, operand), desc);
+ }
+ return getInvocation(bindOperand(MISSING_PROPERTY_REMOVER, operand), linkerServices, desc);
default:
throw new AssertionError("unknown call type " + desc);
}
@@ -162,6 +173,33 @@
throw createTypeError(self, name, "cant.set.property");
}
+ @SuppressWarnings("unused")
+ private static boolean throwStrictPropertyRemover(final Object self, final Object name) {
+ if (isNonConfigurableProperty(self, name)) {
+ throw createTypeError(self, name, "cant.delete.property");
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean missingPropertyRemover(final Object self, final Object name) {
+ return !isNonConfigurableProperty(self, name);
+ }
+
+ // Corresponds to ECMAScript 5.1 8.12.7 [[Delete]] point 3 check for "isConfigurable" (but negated)
+ private static boolean isNonConfigurableProperty(final Object self, final Object name) {
+ if (self instanceof StaticClass) {
+ final Class<?> clazz = ((StaticClass)self).getRepresentedClass();
+ return BeansLinker.getReadableStaticPropertyNames(clazz).contains(name) ||
+ BeansLinker.getWritableStaticPropertyNames(clazz).contains(name) ||
+ BeansLinker.getStaticMethodNames(clazz).contains(name);
+ }
+ final Class<?> clazz = self.getClass();
+ return BeansLinker.getReadableInstancePropertyNames(clazz).contains(name) ||
+ BeansLinker.getWritableInstancePropertyNames(clazz).contains(name) ||
+ BeansLinker.getInstanceMethodNames(clazz).contains(name);
+ }
+
private static ECMAException createTypeError(final Object self, final Object name, final String msg) {
return typeError(msg, String.valueOf(name), ScriptRuntime.safeToString(self));
}
@@ -215,6 +253,8 @@
throw typeError(NashornCallSiteDescriptor.isMethodFirstOperation(desc) ? "no.such.function" : "cant.get.property", getArgument(linkRequest), "null");
case SET:
throw typeError("cant.set.property", getArgument(linkRequest), "null");
+ case REMOVE:
+ throw typeError("cant.delete.property", getArgument(linkRequest), "null");
default:
throw new AssertionError("unknown call type " + desc);
}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed Dec 20 08:05:04 2017 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed Dec 20 17:36:50 2017 +0100
@@ -29,6 +29,7 @@
import static jdk.dynalink.StandardNamespace.METHOD;
import static jdk.dynalink.StandardNamespace.PROPERTY;
import static jdk.dynalink.StandardOperation.GET;
+import static jdk.dynalink.StandardOperation.REMOVE;
import static jdk.dynalink.StandardOperation.SET;
import java.lang.invoke.MethodHandles;
@@ -63,7 +64,7 @@
* form of static methods.
*/
public final class NashornCallSiteDescriptor extends CallSiteDescriptor {
- // Lowest three bits describe the operation
+ // Lowest four bits describe the operation
/** Property getter operation {@code obj.prop} */
public static final int GET_PROPERTY = 0;
/** Element getter operation {@code obj[index]} */
@@ -76,12 +77,16 @@
public static final int SET_PROPERTY = 4;
/** Element setter operation {@code obj[index] = value} */
public static final int SET_ELEMENT = 5;
+ /** Property remove operation {@code delete obj.prop} */
+ public static final int REMOVE_PROPERTY = 6;
+ /** Element remove operation {@code delete obj[index]} */
+ public static final int REMOVE_ELEMENT = 7;
/** Call operation {@code fn(args...)} */
- public static final int CALL = 6;
+ public static final int CALL = 8;
/** New operation {@code new Constructor(args...)} */
- public static final int NEW = 7;
+ public static final int NEW = 9;
- private static final int OPERATION_MASK = 7;
+ private static final int OPERATION_MASK = 15;
// Correspond to the operation indices above.
private static final Operation[] OPERATIONS = new Operation[] {
@@ -91,42 +96,44 @@
GET.withNamespaces(METHOD, ELEMENT, PROPERTY),
SET.withNamespaces(PROPERTY, ELEMENT),
SET.withNamespaces(ELEMENT, PROPERTY),
+ REMOVE.withNamespaces(PROPERTY, ELEMENT),
+ REMOVE.withNamespaces(ELEMENT, PROPERTY),
StandardOperation.CALL,
StandardOperation.NEW
};
/** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
* property access expression. */
- public static final int CALLSITE_SCOPE = 1 << 3;
+ public static final int CALLSITE_SCOPE = 1 << 4;
/** Flags that the call site is in code that uses ECMAScript strict mode. */
- public static final int CALLSITE_STRICT = 1 << 4;
+ public static final int CALLSITE_STRICT = 1 << 5;
/** Flags that a property getter or setter call site references a scope variable that is located at a known distance
* in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
- public static final int CALLSITE_FAST_SCOPE = 1 << 5;
+ public static final int CALLSITE_FAST_SCOPE = 1 << 6;
/** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
* descriptor, and in that case we have to throw an UnwarrantedOptimismException */
- public static final int CALLSITE_OPTIMISTIC = 1 << 6;
+ public static final int CALLSITE_OPTIMISTIC = 1 << 7;
/** Is this really an apply that we try to call as a call? */
- public static final int CALLSITE_APPLY_TO_CALL = 1 << 7;
+ public static final int CALLSITE_APPLY_TO_CALL = 1 << 8;
/** Does this a callsite for a variable declaration? */
- public static final int CALLSITE_DECLARE = 1 << 8;
+ public static final int CALLSITE_DECLARE = 1 << 9;
/** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
* code where call sites have this flag set. */
- public static final int CALLSITE_PROFILE = 1 << 9;
+ public static final int CALLSITE_PROFILE = 1 << 10;
/** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
* call sites have this flag set. */
- public static final int CALLSITE_TRACE = 1 << 10;
+ public static final int CALLSITE_TRACE = 1 << 11;
/** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
* {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
- public static final int CALLSITE_TRACE_MISSES = 1 << 11;
+ public static final int CALLSITE_TRACE_MISSES = 1 << 12;
/** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
* {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
- public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 12;
+ public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 13;
/** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
* that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
* have this flag set. */
- public static final int CALLSITE_TRACE_VALUES = 1 << 13;
+ public static final int CALLSITE_TRACE_VALUES = 1 << 14;
//we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
//right now given the program points
@@ -138,10 +145,10 @@
* TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
* trace/profile settings.
*/
- public static final int CALLSITE_PROGRAM_POINT_SHIFT = 14;
+ public static final int CALLSITE_PROGRAM_POINT_SHIFT = 15;
/**
- * Maximum program point value. We have 18 bits left over after flags, and
+ * Maximum program point value. We have 17 bits left over after flags, and
* it should be plenty. Program points are local to a single function. Every
* function maps to a single JVM bytecode method that can have at most 65535
* bytes. (Large functions are synthetically split into smaller functions.)
@@ -222,8 +229,10 @@
case 3: return "GET_METHOD_ELEMENT";
case 4: return "SET_PROPERTY";
case 5: return "SET_ELEMENT";
- case 6: return "CALL";
- case 7: return "NEW";
+ case 6: return "REMOVE_PROPERTY";
+ case 7: return "REMOVE_ELEMENT";
+ case 8: return "CALL";
+ case 9: return "NEW";
default: throw new AssertionError();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nashorn/script/basic/JDK-8193371.js Wed Dec 20 17:36:50 2017 +0100
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8193371: Use Dynalink REMOVE operation in Nashorn
+ *
+ * @test
+ * @run
+ */
+
+// This test exercises new functionality enabled by the issue, namely removal of elements from Java lists and maps.
+
+var ArrayList = java.util.ArrayList;
+var HashMap = java.util.HashMap;
+var listOf = java.util.List.of;
+var mapOf = java.util.Map.of;
+
+// Remove from a list
+(function() {
+ var a = new ArrayList(listOf("foo", "bar", "baz"));
+ Assert.assertFalse(delete a.add);
+
+ // Delete actual element
+ Assert.assertTrue(delete a[1]);
+ Assert.assertEquals(a, listOf("foo", "baz"));
+
+ // Gracefully ignore silly indices
+ Assert.assertTrue(delete a[5]);
+ Assert.assertTrue(delete a[-1]);
+ Assert.assertTrue(delete a["whatever"]);
+ Assert.assertTrue(delete a.whatever);
+
+ // Gracefully ignore attempts at deleting methods and properties
+ Assert.assertFalse(delete a.add);
+ Assert.assertFalse(delete a.class);
+
+ Assert.assertEquals(a, listOf("foo", "baz"));
+
+ print("List passed.")
+})();
+
+// Remove from a list, strict
+(function() {
+ "use strict";
+
+ var a = new ArrayList(listOf("foo", "bar", "baz"));
+
+ // Delete actual element
+ Assert.assertTrue(delete a[1]);
+ Assert.assertEquals(a, listOf("foo", "baz"));
+
+ // Gracefully ignore silly indices
+ Assert.assertTrue(delete a[5]);
+ Assert.assertTrue(delete a[-1]);
+ Assert.assertTrue(delete a["whatever"]);
+ Assert.assertTrue(delete a.whatever);
+
+ // Fail deleting methods and properties
+ try { delete a.add; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+ try { delete a.class; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+
+ Assert.assertEquals(a, listOf("foo", "baz"));
+
+ print("Strict list passed.")
+})();
+
+// Remove from a map
+(function() {
+ var m = new HashMap(mapOf("a", 1, "b", 2, "c", 3));
+
+ // Delete actual elements
+ Assert.assertTrue(delete m.a);
+ Assert.assertEquals(m, mapOf("b", 2, "c", 3));
+ var key = "b"
+ Assert.assertTrue(delete m[key]);
+ Assert.assertEquals(m, mapOf("c", 3));
+
+ // Gracefully ignore silly indices
+ Assert.assertTrue(delete m.x);
+ Assert.assertTrue(delete m[5]);
+ Assert.assertTrue(delete m[-1]);
+ Assert.assertTrue(delete m["whatever"]);
+
+ // Gracefully ignore attempts at deleting methods and properties
+ Assert.assertFalse(delete m.put);
+ Assert.assertFalse(delete m.class);
+
+ Assert.assertEquals(m, mapOf("c", 3));
+ print("Map passed.")
+})();
+
+// Remove from a map, strict
+(function() {
+ "use strict";
+
+ var m = new HashMap(mapOf("a", 1, "b", 2, "c", 3));
+
+ // Delete actual elements
+ Assert.assertTrue(delete m.a);
+ Assert.assertEquals(m, mapOf("b", 2, "c", 3));
+ var key = "b"
+ Assert.assertTrue(delete m[key]);
+ Assert.assertEquals(m, mapOf("c", 3));
+
+ // Gracefully ignore silly indices
+ Assert.assertTrue(delete m.x);
+ Assert.assertTrue(delete m[5]);
+ Assert.assertTrue(delete m[-1]);
+ Assert.assertTrue(delete m["whatever"]);
+
+ // Fail deleting methods and properties
+ try { delete m.size; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+ try { delete m.class; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+
+ // Somewhat counterintuitive, but if we define an element of a map, we can
+ // delete it, however then the method surfaces, and we can't delete that.
+ m.size = 4
+ Assert.assertTrue(delete m.size)
+ try { delete m.size; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+
+ Assert.assertEquals(m, mapOf("c", 3));
+
+ print("Strict map passed.")
+})();
+
+// Remove from arrays and beans
+(function() {
+ var a = new (Java.type("int[]"))(2)
+ a[0] = 42
+ a[1] = 13
+
+ // Huh, Dynalink doesn't expose .clone() on Java arrays?
+ var c = new (Java.type("int[]"))(2)
+ c[0] = 42
+ c[1] = 13
+
+ // passes vacuously, but does nothing
+ Assert.assertTrue(delete a[0])
+ Assert.assertEquals(a, c);
+
+ var b = new java.util.BitSet()
+ b.set(2)
+ // does nothing
+ Assert.assertFalse(delete b.get)
+ // Method is still there and operational
+ Assert.assertTrue(b.get(2))
+
+ // passes vacuously for non-existant property
+ Assert.assertTrue(delete b.foo)
+
+ // statics
+ var Calendar = java.util.Calendar
+ Assert.assertFalse(delete Calendar.UNDECIMBER) // field
+ Assert.assertFalse(delete Calendar.availableLocales) // property
+ Assert.assertFalse(delete Calendar.getInstance) // method
+ Assert.assertTrue(delete Calendar.BLAH) // no such thing
+
+ print("Beans passed.")
+})();
+
+// Remove from arrays and beans, strict
+(function() {
+ "use strict";
+
+ var a = new (Java.type("int[]"))(2)
+ a[0] = 42
+ a[1] = 13
+
+ var c = new (Java.type("int[]"))(2)
+ c[0] = 42
+ c[1] = 13
+
+ // passes vacuously, but does nothing
+ Assert.assertTrue(delete a[0])
+ Assert.assertEquals(a, c);
+
+ var b = new java.util.BitSet()
+ b.set(2)
+ // fails to delete a method
+ try { delete b.get; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+ // Method is still there and operational
+ Assert.assertTrue(b.get(2))
+
+ // passes vacuously for non-existant property
+ Assert.assertTrue(delete b.foo)
+
+ // statics
+ var Calendar = java.util.Calendar
+ try { delete Calendar.UNDECIMBER; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+ try { delete Calendar.availableLocales; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+ try { delete Calendar.getInstance; Assert.fail(); } catch (e) { Assert.assertTrue(e instanceof TypeError) }
+ Assert.assertTrue(delete Calendar.BLAH) // no such thing
+
+ print("Strict beans passed.")
+})();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nashorn/script/basic/JDK-8193371.js.EXPECTED Wed Dec 20 17:36:50 2017 +0100
@@ -0,0 +1,6 @@
+List passed.
+Strict list passed.
+Map passed.
+Strict map passed.
+Beans passed.
+Strict beans passed.