This commit is contained in:
Lana Steuck 2015-03-05 15:22:39 -08:00
commit 5411c3c53b
32 changed files with 870 additions and 1029 deletions

View File

@ -0,0 +1,82 @@
#// Usage: jjs -fx showenv.js
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
if (!$OPTIONS._fx) {
print("Usage: jjs -fx showenv.js");
exit(1);
}
// This script displays environment entries as a HTML table.
// Demonstrates heredoc to generate HTML content and display
// using JavaFX WebView.
// JavaFX classes used
var Scene = Java.type("javafx.scene.Scene");
var WebView = Java.type("javafx.scene.web.WebView");
// JavaFX start method
function start(stage) {
start.title = "Your Environment";
var wv = new WebView();
var envrows = "";
for (var i in $ENV) {
envrows += <<TBL
<tr>
<td>
${i}
</td>
<td>
${$ENV[i]}
</td>
</tr>
TBL
}
wv.engine.loadContent(<<EOF
<html>
<head>
<title>
Your Environment
</title>
</head>
<body>
<h1>Your Environment</h1>
<table border="1">
${envrows}
</table>
</body>
</html>
EOF, "text/html");
stage.scene = new Scene(wv, 750, 500);
stage.show();
}

View File

@ -0,0 +1,84 @@
#// Usage: jjs -fx showsysprops.js
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
if (!$OPTIONS._fx) {
print("Usage: jjs -fx showsysprops.js");
exit(1);
}
// This script displays System properties as a HTML table.
// Demonstrates heredoc to generate HTML content and display
// using JavaFX WebView.
// JavaFX, Java classes used
var Scene = Java.type("javafx.scene.Scene");
var System = Java.type("java.lang.System");
var WebView = Java.type("javafx.scene.web.WebView");
// JavaFX start method
function start(stage) {
start.title = "Your System Properties";
var wv = new WebView();
var sysproprows = "";
var sysprops = System.properties;
for (var i in sysprops) {
sysproprows += <<TBL
<tr>
<td>
${i}
</td>
<td>
${sysprops[i]}
</td>
</tr>
TBL
}
wv.engine.loadContent(<<EOF
<html>
<head>
<title>
Your System Properties
</title>
</head>
<body>
<h1>Your System Properties</h1>
<table border="1">
${sysproprows}
</table>
</body>
</html>
EOF, "text/html");
stage.scene = new Scene(wv, 750, 500);
stage.show();
}

View File

@ -712,19 +712,8 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
return definingFn == function;
}
private void checkConstAssignment(final IdentNode ident) {
// Check for reassignment of constant
final Symbol symbol = ident.getSymbol();
if (symbol.isConst()) {
throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident);
}
}
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
if (binaryNode.isAssignment() && binaryNode.lhs() instanceof IdentNode) {
checkConstAssignment((IdentNode) binaryNode.lhs());
}
switch (binaryNode.tokenType()) {
case ASSIGN:
return leaveASSIGN(binaryNode);
@ -751,9 +740,6 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override
public Node leaveUnaryNode(final UnaryNode unaryNode) {
if (unaryNode.isAssignment() && unaryNode.getExpression() instanceof IdentNode) {
checkConstAssignment((IdentNode) unaryNode.getExpression());
}
switch (unaryNode.tokenType()) {
case DELETE:
return leaveDELETE(unaryNode);

View File

@ -105,33 +105,33 @@ final class BranchOptimizer {
case EQ:
case EQ_STRICT:
codegen.loadBinaryOperands(binaryNode);
codegen.loadComparisonOperands(binaryNode);
method.conditionalJump(state ? EQ : NE, true, label);
return;
case NE:
case NE_STRICT:
codegen.loadBinaryOperands(binaryNode);
codegen.loadComparisonOperands(binaryNode);
method.conditionalJump(state ? NE : EQ, true, label);
return;
case GE:
codegen.loadBinaryOperands(binaryNode);
codegen.loadComparisonOperands(binaryNode);
method.conditionalJump(state ? GE : LT, false, label);
return;
case GT:
codegen.loadBinaryOperands(binaryNode);
codegen.loadComparisonOperands(binaryNode);
method.conditionalJump(state ? GT : LE, false, label);
return;
case LE:
codegen.loadBinaryOperands(binaryNode);
codegen.loadComparisonOperands(binaryNode);
method.conditionalJump(state ? LE : GT, true, label);
return;
case LT:
codegen.loadBinaryOperands(binaryNode);
codegen.loadComparisonOperands(binaryNode);
method.conditionalJump(state ? LT : GE, true, label);
return;

View File

@ -202,6 +202,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class,
"create", ScriptFunction.class, Object[].class, int.class);
private static final Call TO_NUMBER_FOR_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
"toNumberForEq", double.class, Object.class);
private static final Call TO_NUMBER_FOR_STRICT_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
"toNumberForStrictEq", double.class, Object.class);
private static final Class<?> ITERATOR_CLASS = Iterator.class;
static {
assert ITERATOR_CLASS == CompilerConstants.ITERATOR_PREFIX.type();
@ -343,8 +349,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// This is called the temporal dead zone (TDZ). See https://gist.github.com/rwaldron/f0807a758aa03bcdd58a
private void checkTemporalDeadZone(final IdentNode identNode) {
if (identNode.isDead()) {
method.load(identNode.getSymbol().getName());
method.invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
method.load(identNode.getSymbol().getName()).invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
}
}
// Runtime check for assignment to ES6 const
private void checkAssignTarget(final Expression expression) {
if (expression instanceof IdentNode && ((IdentNode)expression).getSymbol().isConst()) {
method.load(((IdentNode)expression).getSymbol().getName()).invoke(ScriptRuntime.THROW_CONST_TYPE_ERROR);
}
}
@ -612,6 +624,104 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return method;
}
/**
* Similar to {@link #loadBinaryOperands(BinaryNode)} but used specifically for loading operands of
* relational and equality comparison operators where at least one argument is non-object. (When both
* arguments are objects, we use {@link ScriptRuntime#EQ(Object, Object)}, {@link ScriptRuntime#LT(Object, Object)}
* etc. methods instead. Additionally, {@code ScriptRuntime} methods are used for strict (in)equality comparison
* of a boolean to anything that isn't a boolean.) This method handles the special case where one argument
* is an object and another is a primitive. Naively, these could also be delegated to {@code ScriptRuntime} methods
* by boxing the primitive. However, in all such cases the comparison is performed on numeric values, so it is
* possible to strength-reduce the operation by taking the number value of the object argument instead and
* comparing that to the primitive value ("primitive" will always be int, long, double, or boolean, and booleans
* compare as ints in these cases, so they're essentially numbers too). This method will emit code for loading
* arguments for such strength-reduced comparison. When both arguments are primitives, it just delegates to
* {@link #loadBinaryOperands(BinaryNode)}.
*
* @param cmp the comparison operation for which the operands need to be loaded on stack.
* @return the current method emitter.
*/
MethodEmitter loadComparisonOperands(final BinaryNode cmp) {
final Expression lhs = cmp.lhs();
final Expression rhs = cmp.rhs();
final Type lhsType = lhs.getType();
final Type rhsType = rhs.getType();
// Only used when not both are object, for that we have ScriptRuntime.LT etc.
assert !(lhsType.isObject() && rhsType.isObject());
if (lhsType.isObject() || rhsType.isObject()) {
// We can reorder CONVERT LEFT and LOAD RIGHT only if either the left is a primitive, or the right
// is a local. This is more strict than loadBinaryNode reorder criteria, as it can allow JS primitive
// types too (notably: String is a JS primitive, but not a JVM primitive). We disallow String otherwise
// we would prematurely convert it to number when comparing to an optimistic expression, e.g. in
// "Hello" === String("Hello") the RHS starts out as an optimistic-int function call. If we allowed
// reordering, we'd end up with ToNumber("Hello") === {I%}String("Hello") that is obviously incorrect.
final boolean canReorder = lhsType.isPrimitive() || rhs.isLocal();
// If reordering is allowed, and we're using a relational operator (that is, <, <=, >, >=) and not an
// (in)equality operator, then we encourage combining of LOAD and CONVERT into a single operation.
// This is because relational operators' semantics prescribes vanilla ToNumber() conversion, while
// (in)equality operators need the specialized JSType.toNumberFor[Strict]Equals. E.g. in the code snippet
// "i < obj.size" (where i is primitive and obj.size is statically an object), ".size" will thus be allowed
// to compile as:
// invokedynamic dyn:getProp|getElem|getMethod:size(Object;)D
// instead of the more costly:
// invokedynamic dyn:getProp|getElem|getMethod:size(Object;)Object
// invokestatic JSType.toNumber(Object)D
// Note also that even if this is allowed, we're only using it on operands that are non-optimistic, as
// otherwise the logic for determining effective optimistic-ness would turn an optimistic double return
// into a freely coercible one, which would be wrong.
final boolean canCombineLoadAndConvert = canReorder && cmp.isRelational();
// LOAD LEFT
loadExpression(lhs, canCombineLoadAndConvert && !lhs.isOptimistic() ? TypeBounds.NUMBER : TypeBounds.UNBOUNDED);
final Type lhsLoadedType = method.peekType();
final TokenType tt = cmp.tokenType();
if (canReorder) {
// Can reorder CONVERT LEFT and LOAD RIGHT
emitObjectToNumberComparisonConversion(method, tt);
loadExpression(rhs, canCombineLoadAndConvert && !rhs.isOptimistic() ? TypeBounds.NUMBER : TypeBounds.UNBOUNDED);
} else {
// Can't reorder CONVERT LEFT and LOAD RIGHT
loadExpression(rhs, TypeBounds.UNBOUNDED);
if (lhsLoadedType != Type.NUMBER) {
method.swap();
emitObjectToNumberComparisonConversion(method, tt);
method.swap();
}
}
// CONVERT RIGHT
emitObjectToNumberComparisonConversion(method, tt);
return method;
}
// For primitive operands, just don't do anything special.
return loadBinaryOperands(cmp);
}
private static void emitObjectToNumberComparisonConversion(final MethodEmitter method, final TokenType tt) {
switch(tt) {
case EQ:
case NE:
if (method.peekType().isObject()) {
TO_NUMBER_FOR_EQ.invoke(method);
return;
}
break;
case EQ_STRICT:
case NE_STRICT:
if (method.peekType().isObject()) {
TO_NUMBER_FOR_STRICT_EQ.invoke(method);
return;
}
break;
default:
break;
}
method.convert(Type.NUMBER);
}
private static final Type undefinedToNumber(final Type type) {
return type == Type.UNDEFINED ? Type.NUMBER : type;
}
@ -622,6 +732,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
static final TypeBounds INT = exact(Type.INT);
static final TypeBounds NUMBER = exact(Type.NUMBER);
static final TypeBounds OBJECT = exact(Type.OBJECT);
static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
@ -787,72 +898,84 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterASSIGN(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_ADD(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_BIT_AND(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_BIT_OR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_BIT_XOR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_DIV(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_MOD(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_MUL(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SAR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SHL(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SHR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SUB(binaryNode);
return false;
}
@ -1062,6 +1185,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterDECINC(final UnaryNode unaryNode) {
checkAssignTarget(unaryNode.getExpression());
loadDECINC(unaryNode);
return false;
}
@ -2737,25 +2861,18 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
newRuntimeNode = runtimeNode;
}
new OptimisticOperation(newRuntimeNode, TypeBounds.UNBOUNDED) {
@Override
void loadStack() {
for (final Expression arg : args) {
loadExpression(arg, TypeBounds.OBJECT);
}
}
@Override
void consumeStack() {
method.invokestatic(
CompilerConstants.className(ScriptRuntime.class),
newRuntimeNode.getRequest().toString(),
new FunctionSignature(
false,
false,
newRuntimeNode.getType(),
args.size()).toString());
}
}.emit();
for (final Expression arg : args) {
loadExpression(arg, TypeBounds.OBJECT);
}
method.invokestatic(
CompilerConstants.className(ScriptRuntime.class),
newRuntimeNode.getRequest().toString(),
new FunctionSignature(
false,
false,
newRuntimeNode.getType(),
args.size()).toString());
method.convert(newRuntimeNode.getType());
}
@ -3970,8 +4087,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
private void loadCmp(final BinaryNode binaryNode, final Condition cond) {
assert comparisonOperandsArePrimitive(binaryNode) : binaryNode;
loadBinaryOperands(binaryNode);
loadComparisonOperands(binaryNode);
final Label trueLabel = new Label("trueLabel");
final Label afterLabel = new Label("skip");
@ -3985,11 +4101,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
method.label(afterLabel);
}
private static boolean comparisonOperandsArePrimitive(final BinaryNode binaryNode) {
final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
return widest.isNumeric() || widest.isBoolean();
}
private void loadMOD(final BinaryNode binaryNode, final TypeBounds resultBounds) {
new BinaryArith() {
@Override

View File

@ -1359,8 +1359,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
final Expression lhs = binaryNode.lhs();
final Expression rhs = binaryNode.rhs();
Type cmpWidest = Type.widest(lhs.getType(), rhs.getType());
boolean newRuntimeNode = false, finalized = false;
final TokenType tt = binaryNode.tokenType();
switch (tt) {
case EQ_STRICT:
@ -1373,14 +1371,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
// Specialize comparison of boolean with non-boolean
if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
newRuntimeNode = true;
cmpWidest = Type.OBJECT;
finalized = true;
return new RuntimeNode(binaryNode);
}
// fallthrough
default:
if (newRuntimeNode || cmpWidest.isObject()) {
return new RuntimeNode(binaryNode).setIsFinal(finalized);
if (lhs.getType().isObject() && rhs.getType().isObject()) {
return new RuntimeNode(binaryNode);
}
}
} else if(binaryNode.isOptimisticUndecidedType()) {

View File

@ -94,7 +94,6 @@ import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.JoinPredecessor;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.objects.NativeArray;
@ -174,9 +173,6 @@ public class MethodEmitter implements Emitter {
/** Bootstrap for normal indy:s */
private static final Handle LINKERBOOTSTRAP = new Handle(H_INVOKESTATIC, Bootstrap.BOOTSTRAP.className(), Bootstrap.BOOTSTRAP.name(), Bootstrap.BOOTSTRAP.descriptor());
/** Bootstrap for runtime node indy:s */
private static final Handle RUNTIMEBOOTSTRAP = new Handle(H_INVOKESTATIC, RuntimeCallSite.BOOTSTRAP.className(), RuntimeCallSite.BOOTSTRAP.name(), RuntimeCallSite.BOOTSTRAP.descriptor());
/** Bootstrap for array populators */
private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor());
@ -2187,25 +2183,6 @@ public class MethodEmitter implements Emitter {
return this;
}
/**
* Generate a dynamic call for a runtime node
*
* @param name tag for the invoke dynamic for this runtime node
* @param returnType return type
* @param request RuntimeNode request
*
* @return the method emitter
*/
MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
debug("dynamic_runtime_call", name, "args=", request.getArity(), "returnType=", returnType);
final String signature = getDynamicSignature(returnType, request.getArity());
debug(" signature", signature);
method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
pushType(returnType);
return this;
}
/**
* Generate dynamic getter. Pop scope from stack. Push result
*

View File

@ -1,683 +0,0 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.types.Type.BOOLEAN;
import static jdk.nashorn.internal.codegen.types.Type.INT;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
* Optimistic call site that assumes its Object arguments to be of a boxed type.
* Gradually reverts to wider boxed types if the assumption for the RuntimeNode
* is proven wrong. Finally reverts to the generic ScriptRuntime method.
*
* This is used from the CodeGenerator when we have a runtime node, but 1 or more
* primitive arguments. This class generated appropriate specializations, for example
* {@code Object a === int b} is a good idea to specialize to {@code ((Integer)a).intValue() == b}
* surrounded by catch blocks that will try less narrow specializations
*/
public final class RuntimeCallSite extends MutableCallSite {
static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "runtimeBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
private static final MethodHandle NEXT = findOwnMH_V("next", MethodHandle.class, String.class);
private final RuntimeNode.Request request;
/**
* A specialized runtime node, i.e. on where we know at least one more specific type than object
*/
static final class SpecializedRuntimeNode {
private static final char REQUEST_SEPARATOR = ':';
private final RuntimeNode.Request request;
private final Type[] parameterTypes;
private final Type returnType;
/**
* Constructor.
*
* @param request runtime node request to specialize
* @param parameterTypes parameter types of the call site
* @param returnType return type of the call site
*/
SpecializedRuntimeNode(final RuntimeNode.Request request, final Type[] parameterTypes, final Type returnType) {
this.request = request;
this.parameterTypes = parameterTypes;
this.returnType = returnType;
}
/**
* The first type to try to use for this generated runtime node
*
* @return a type
*/
public Type firstTypeGuess() {
Type widest = Type.UNKNOWN;
for (final Type type : parameterTypes) {
if (type.isObject()) {
continue;
}
widest = Type.widest(type, widest);
}
widest = Type.widest(widest, firstTypeGuessForObject(request));
return widest;
}
private static Type firstTypeGuessForObject(final Request request) {
switch (request) {
case ADD:
return INT;
default:
return BOOLEAN;
}
}
Request getRequest() {
return request;
}
Type[] getParameterTypes() {
return parameterTypes;
}
Type getReturnType() {
return returnType;
}
private static char descFor(final Type type) {
if (type.isObject()) {
return 'O';
}
return type.getDescriptor().charAt(0);
}
@Override
public boolean equals(final Object other) {
if (other instanceof SpecializedRuntimeNode) {
final SpecializedRuntimeNode otherNode = (SpecializedRuntimeNode)other;
if (!otherNode.getReturnType().equals(getReturnType())) {
return false;
}
if (getParameterTypes().length != otherNode.getParameterTypes().length) {
return false;
}
for (int i = 0; i < getParameterTypes().length; i++) {
if (!Type.areEquivalent(getParameterTypes()[i], otherNode.getParameterTypes()[i])) {
return false;
}
}
return otherNode.getRequest().equals(getRequest());
}
return false;
}
@Override
public int hashCode() {
int hashCode = getRequest().toString().hashCode();
hashCode ^= getReturnType().hashCode();
for (final Type type : getParameterTypes()) {
hashCode ^= type.hashCode();
}
return hashCode;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(getRequest().toString());
sb.append(REQUEST_SEPARATOR);
sb.append(descFor(getReturnType()));
for (final Type type : getParameterTypes()) {
sb.append(descFor(type));
}
return sb.toString();
}
String getName(final Type extraType) {
return toString() + "_" + descFor(extraType);
}
String getInitialName() {
return getName(firstTypeGuess());
}
}
/**
* Constructor
*
* @param type method type for call site
* @param name name of runtime call
*/
public RuntimeCallSite(final MethodType type, final String name) {
super(type);
this.request = Request.valueOf(name.substring(0, name.indexOf(SpecializedRuntimeNode.REQUEST_SEPARATOR)));
setTarget(makeMethod(name));
}
private String nextName(final String requestName) {
if (requestName.equals(request.toString())) {
return null;
}
final char[] c = requestName.toCharArray();
final int last = c.length - 1;
if (c[last - 1] != '_') {
return null;
}
switch (c[last]) {
case 'Z':
c[last] = 'I';
break;
case 'I':
c[last] = 'J';
break;
case 'J':
c[last] = 'D';
break;
case 'D':
default:
return request.toString();
}
return new String(c);
}
private boolean isSpecialized(final String requestName) {
return nextName(requestName) != null;
}
private MethodHandle makeMethod(final String requestName) {
MethodHandle mh;
if (isSpecialized(requestName)) {
final Class<?> boxedType;
final Class<?> primitiveType;
switch (requestName.charAt(requestName.length() - 1)) {
case 'Z':
boxedType = Boolean.class;
primitiveType = int.class;
break;
case 'I':
boxedType = Integer.class;
primitiveType = int.class;
break;
case 'J':
boxedType = Long.class;
primitiveType = long.class;
break;
case 'D':
boxedType = Number.class;
primitiveType = double.class;
break;
default:
throw new RuntimeException("should not reach here");
}
final boolean isStrictCmp = (request == Request.EQ_STRICT || request == Request.NE_STRICT);
if (isStrictCmp &&
(boxedType != Boolean.class &&
(type().parameterType(0) == boolean.class ||
type().parameterType(1) == boolean.class))) {
// number and boolean are never strictly equal, e.g. 0 !== false
mh = MH.dropArguments(MH.constant(boolean.class, request == Request.NE_STRICT), 0, type().parameterArray());
} else {
mh = METHODS.get(request.nonStrictName() + primitiveType.getSimpleName());
// unbox objects
for (int i = 0; i < type().parameterCount(); i++) {
if (!type().parameterType(i).isPrimitive()) {
mh = MH.filterArguments(mh, i, UNBOX.get(boxedType));
}
}
mh = Lookup.filterReturnType(mh, type().returnType());
mh = MH.explicitCastArguments(mh, type());
}
final MethodHandle fallback = MH.foldArguments(MethodHandles.exactInvoker(type()), MH.insertArguments(NEXT, 0, this, requestName));
MethodHandle guard;
if (type().parameterType(0).isPrimitive()) {
guard = MH.insertArguments(
MH.dropArguments(CHECKCAST, 1, type().parameterType(0)), 0, boxedType);
} else if (type().parameterType(1).isPrimitive()) {
guard = MH.insertArguments(
MH.dropArguments(CHECKCAST, 2, type().parameterType(1)), 0, boxedType);
} else {
assert !type().parameterType(0).isPrimitive() && !type().parameterType(1).isPrimitive();
guard = MH.insertArguments(CHECKCAST2, 0, boxedType);
}
if (request == Request.ADD && boxedType == Integer.class) {
// int add needs additional overflow check
MethodHandle addcheck = ADDCHECK;
for (int i = 0; i < type().parameterCount(); i++) {
if (!type().parameterType(i).isPrimitive()) {
addcheck = MH.filterArguments(addcheck, i, UNBOX.get(boxedType));
}
}
addcheck = MH.explicitCastArguments(addcheck, type().changeReturnType(boolean.class));
guard = MH.guardWithTest(upcastGuard(guard), addcheck,
MH.dropArguments(MH.constant(boolean.class, false), 0, type().parameterArray()));
}
return MH.guardWithTest(upcastGuard(guard), mh, fallback);
}
// generic fallback
return MH.explicitCastArguments(Lookup.filterReturnType(GENERIC_METHODS.get(request.name()), type().returnType()), type());
}
private MethodHandle upcastGuard(final MethodHandle guard) {
return MH.asType(guard, type().changeReturnType(boolean.class));
}
/**
* This is public just so that the generated specialization code can
* use it to get the next wider typed method
*
* Do not call directly
*
* @param name current name (with type) of runtime call at the call site
* @return next wider specialization method for this RuntimeCallSite
*/
public MethodHandle next(final String name) {
final MethodHandle next = makeMethod(nextName(name));
setTarget(next);
return next;
}
/** Method cache */
private static final Map<String, MethodHandle> METHODS;
/** Generic method cache */
private static final Map<String, MethodHandle> GENERIC_METHODS;
/** Unbox cache */
private static final Map<Class<?>, MethodHandle> UNBOX;
private static final MethodHandle CHECKCAST = findOwnMH_S("checkcast", boolean.class, Class.class, Object.class);
private static final MethodHandle CHECKCAST2 = findOwnMH_S("checkcast", boolean.class, Class.class, Object.class, Object.class);
private static final MethodHandle ADDCHECK = findOwnMH_S("ADDcheck", boolean.class, int.class, int.class);
/**
* Build maps of correct boxing operations
*/
static {
UNBOX = new HashMap<>();
UNBOX.put(Boolean.class, findOwnMH_S("unboxZ", int.class, Object.class));
UNBOX.put(Integer.class, findOwnMH_S("unboxI", int.class, Object.class));
UNBOX.put(Long.class, findOwnMH_S("unboxJ", long.class, Object.class));
UNBOX.put(Number.class, findOwnMH_S("unboxD", double.class, Object.class));
METHODS = new HashMap<>();
for (final Request req : Request.values()) {
if (req.canSpecialize()) {
if (req.name().endsWith("_STRICT")) {
continue;
}
final boolean isCmp = Request.isComparison(req);
METHODS.put(req.name() + "int", findOwnMH_S(req.name(), (isCmp ? boolean.class : int.class), int.class, int.class));
METHODS.put(req.name() + "long", findOwnMH_S(req.name(), (isCmp ? boolean.class : long.class), long.class, long.class));
METHODS.put(req.name() + "double", findOwnMH_S(req.name(), (isCmp ? boolean.class : double.class), double.class, double.class));
}
}
GENERIC_METHODS = new HashMap<>();
for (final Request req : Request.values()) {
if (req.canSpecialize()) {
GENERIC_METHODS.put(req.name(), MH.findStatic(MethodHandles.lookup(), ScriptRuntime.class, req.name(),
MH.type(req.getReturnType().getTypeClass(), Object.class, Object.class)));
}
}
}
/**
* Specialized version of != operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a != b
*/
public static boolean NE(final int a, final int b) {
return a != b;
}
/**
* Specialized version of != operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a != b
*/
public static boolean NE(final double a, final double b) {
return a != b;
}
/**
* Specialized version of != operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a != b
*/
public static boolean NE(final long a, final long b) {
return a != b;
}
/**
* Specialized version of == operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a == b
*/
public static boolean EQ(final int a, final int b) {
return a == b;
}
/**
* Specialized version of == operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a == b
*/
public static boolean EQ(final double a, final double b) {
return a == b;
}
/**
* Specialized version of == operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a == b
*/
public static boolean EQ(final long a, final long b) {
return a == b;
}
/**
* Specialized version of {@literal <} operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a {@code <} b
*/
public static boolean LT(final int a, final int b) {
return a < b;
}
/**
* Specialized version of {@literal <} operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a {@literal <} b
*/
public static boolean LT(final double a, final double b) {
return a < b;
}
/**
* Specialized version of {@literal <} operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a {@literal <} b
*/
public static boolean LT(final long a, final long b) {
return a < b;
}
/**
* Specialized version of {@literal <=} operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a {@literal <=} b
*/
public static boolean LE(final int a, final int b) {
return a <= b;
}
/**
* Specialized version of {@literal <=} operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a {@literal <=} b
*/
public static boolean LE(final double a, final double b) {
return a <= b;
}
/**
* Specialized version of {@literal <=} operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a {@literal <=} b
*/
public static boolean LE(final long a, final long b) {
return a <= b;
}
/**
* Specialized version of {@literal >} operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a {@literal >} b
*/
public static boolean GT(final int a, final int b) {
return a > b;
}
/**
* Specialized version of {@literal >} operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a {@literal >} b
*/
public static boolean GT(final double a, final double b) {
return a > b;
}
/**
* Specialized version of {@literal >} operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a {@literal >} b
*/
public static boolean GT(final long a, final long b) {
return a > b;
}
/**
* Specialized version of {@literal >=} operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a {@literal >=} b
*/
public static boolean GE(final int a, final int b) {
return a >= b;
}
/**
* Specialized version of {@literal >=} operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a {@literal >=} b
*/
public static boolean GE(final double a, final double b) {
return a >= b;
}
/**
* Specialized version of {@literal >=} operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a {@code >=} b
*/
public static boolean GE(final long a, final long b) {
return a >= b;
}
/**
* Specialized version of + operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a + b
*/
public static int ADD(final int a, final int b) {
return a + b;
}
/**
* Specialized version of + operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a + b
*/
public static long ADD(final long a, final long b) {
return a + b;
}
/**
* Specialized version of + operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a + b
*/
public static double ADD(final double a, final double b) {
return a + b;
}
/**
* Check that ints are addition compatible, i.e. their sum is equal to the sum
* of them cast to long. Otherwise the addition will overflow. Do not call directly.
*
* @param a int
* @param b int
*
* @return true if addition does not overflow
*/
public static boolean ADDcheck(final int a, final int b) {
return (a + b == (long)a + (long)b);
}
/**
* Checkcast used for specialized ops. Do not call directly
*
* @param type to to check against
* @param obj object to check for type
*
* @return true if type check holds
*/
public static boolean checkcast(final Class<?> type, final Object obj) {
return type.isInstance(obj);
}
/**
* Checkcast used for specialized ops. Do not call directly
*
* @param type type to check against
* @param objA first object to check against type
* @param objB second object to check against type
*
* @return true if type check holds for both objects
*/
public static boolean checkcast(final Class<?> type, final Object objA, final Object objB) {
return type.isInstance(objA) && type.isInstance(objB);
}
/**
* Unbox a java.lang.Boolean. Do not call directly
* @param obj object to cast to int and unbox
* @return an int value for the boolean, 1 is true, 0 is false
*/
public static int unboxZ(final Object obj) {
return (boolean)obj ? 1 : 0;
}
/**
* Unbox a java.lang.Integer. Do not call directly
* @param obj object to cast to int and unbox
* @return an int
*/
public static int unboxI(final Object obj) {
return (int)obj;
}
/**
* Unbox a java.lang.Long. Do not call directly
* @param obj object to cast to long and unbox
* @return a long
*/
public static long unboxJ(final Object obj) {
return (long)obj;
}
/**
* Unbox a java.lang.Number. Do not call directly
* @param obj object to cast to Number and unbox
* @return a double
*/
public static double unboxD(final Object obj) {
return ((Number)obj).doubleValue();
}
private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
}
private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findVirtual(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
}
}

View File

@ -98,7 +98,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
}
/**
* Returns true if the node is a comparison operation.
* Returns true if the node is a comparison operation (either equality, inequality, or relational).
* @return true if the node is a comparison operation.
*/
public boolean isComparison() {
@ -117,6 +117,22 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
}
}
/**
* Returns true if the node is a relational operation (less than (or equals), greater than (or equals)).
* @return true if the node is a relational operation.
*/
public boolean isRelational() {
switch (tokenType()) {
case LT:
case GT:
case LE:
case GE:
return true;
default:
return false;
}
}
/**
* Returns true if the node is a logical operation.
* @return true if the node is a logical operation.

View File

@ -25,8 +25,6 @@
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -39,7 +37,7 @@ import jdk.nashorn.internal.parser.TokenType;
* IR representation for a runtime call.
*/
@Immutable
public class RuntimeNode extends Expression implements Optimistic {
public class RuntimeNode extends Expression {
private static final long serialVersionUID = 1L;
/**
@ -333,11 +331,6 @@ public class RuntimeNode extends Expression implements Optimistic {
/** Call arguments. */
private final List<Expression> args;
/** is final - i.e. may not be removed again, lower in the code pipeline */
private final boolean isFinal;
private final int programPoint;
/**
* Constructor
*
@ -351,17 +344,13 @@ public class RuntimeNode extends Expression implements Optimistic {
this.request = request;
this.args = args;
this.isFinal = false;
this.programPoint = INVALID_PROGRAM_POINT;
}
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List<Expression> args, final int programPoint) {
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final List<Expression> args) {
super(runtimeNode);
this.request = request;
this.args = args;
this.isFinal = isFinal;
this.programPoint = programPoint;
}
/**
@ -399,8 +388,6 @@ public class RuntimeNode extends Expression implements Optimistic {
this.request = request;
this.args = args;
this.isFinal = false;
this.programPoint = parent instanceof Optimistic ? ((Optimistic)parent).getProgramPoint() : INVALID_PROGRAM_POINT;
}
/**
@ -428,32 +415,11 @@ public class RuntimeNode extends Expression implements Optimistic {
* @return new runtime node or same if same request
*/
public RuntimeNode setRequest(final Request request) {
if (this.request == request) {
return this;
}
return new RuntimeNode(this, request, isFinal, args, programPoint);
}
/**
* Is this node final - i.e. it can never be replaced with other nodes again
* @return true if final
*/
public boolean isFinal() {
return isFinal;
}
/**
* Flag this node as final - i.e it may never be replaced with other nodes again
* @param isFinal is the node final, i.e. can not be removed and replaced by a less generic one later in codegen
* @return same runtime node if already final, otherwise a new one
*/
public RuntimeNode setIsFinal(final boolean isFinal) {
if (this.isFinal == isFinal) {
if (this.request == request) {
return this;
}
return new RuntimeNode(this, request, isFinal, args, programPoint);
}
return new RuntimeNode(this, request, args);
}
/**
* Return type for the ReferenceNode
@ -510,7 +476,7 @@ public class RuntimeNode extends Expression implements Optimistic {
if (this.args == args) {
return this;
}
return new RuntimeNode(this, request, isFinal, args, programPoint);
return new RuntimeNode(this, request, args);
}
/**
@ -536,39 +502,4 @@ public class RuntimeNode extends Expression implements Optimistic {
}
return true;
}
//TODO these are blank for now:
@Override
public int getProgramPoint() {
return programPoint;
}
@Override
public RuntimeNode setProgramPoint(final int programPoint) {
if(this.programPoint == programPoint) {
return this;
}
return new RuntimeNode(this, request, isFinal, args, programPoint);
}
@Override
public boolean canBeOptimistic() {
return false;
}
@Override
public Type getMostOptimisticType() {
return getType();
}
@Override
public Type getMostPessimisticType() {
return getType();
}
@Override
public RuntimeNode setType(final Type type) {
return this;
}
}

View File

@ -25,6 +25,8 @@
package jdk.nashorn.internal.lookup;
import static jdk.nashorn.internal.runtime.JSType.isString;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.invoke.MethodHandle;
@ -36,7 +38,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.ScriptObject;
@ -343,7 +344,7 @@ public final class MethodHandleFactory {
final Object d = data[i];
if (d == null) {
sb.append("<null> ");
} else if (d instanceof String || d instanceof ConsString) {
} else if (isString(d)) {
sb.append(d.toString());
sb.append(' ');
} else if (d.getClass().isArray()) {

View File

@ -28,6 +28,7 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isString;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.io.IOException;
@ -55,7 +56,6 @@ import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.objects.annotations.Attribute;
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.ECMAErrors;
import jdk.nashorn.internal.runtime.GlobalConstants;
@ -577,7 +577,7 @@ public final class Global extends ScriptObject implements Scope {
return new NativeBoolean((Boolean)obj, this);
} else if (obj instanceof Number) {
return new NativeNumber(((Number)obj).doubleValue(), this);
} else if (obj instanceof String || obj instanceof ConsString) {
} else if (isString(obj)) {
return new NativeString((CharSequence)obj, this);
} else if (obj instanceof Object[]) { // extension
return new NativeArray(ArrayData.allocate((Object[])obj), this);
@ -604,7 +604,7 @@ public final class Global extends ScriptObject implements Scope {
* @return guarded invocation
*/
public static GuardedInvocation primitiveLookup(final LinkRequest request, final Object self) {
if (self instanceof String || self instanceof ConsString) {
if (isString(self)) {
return NativeString.lookupPrimitive(request, self);
} else if (self instanceof Number) {
return NativeNumber.lookupPrimitive(request, self);
@ -621,7 +621,7 @@ public final class Global extends ScriptObject implements Scope {
* @return method handle to create wrapper objects for primitive receiver
*/
public static MethodHandle getPrimitiveWrapFilter(final Object self) {
if (self instanceof String || self instanceof ConsString) {
if (isString(self)) {
return NativeString.WRAPFILTER;
} else if (self instanceof Number) {
return NativeNumber.WRAPFILTER;
@ -947,11 +947,11 @@ public final class Global extends ScriptObject implements Scope {
* This is directly invoked from generated when eval(code) is called in user code
*/
public static Object directEval(final Object self, final Object str, final Object callThis, final Object location, final boolean strict) {
if (!(str instanceof String || str instanceof ConsString)) {
if (!isString(str)) {
return str;
}
final Global global = Global.instanceFrom(self);
final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
final ScriptObject scope = self instanceof ScriptObject && ((ScriptObject)self).isScope() ? (ScriptObject)self : global;
return global.getContext().eval(scope, str.toString(), callThis, location, strict, true);
}

View File

@ -30,6 +30,7 @@ import static java.lang.Double.isInfinite;
import static java.lang.Double.isNaN;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.Callable;
@ -40,7 +41,6 @@ import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.parser.DateParser;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
@ -183,7 +183,7 @@ public final class NativeDate extends ScriptObject {
case 1:
double num;
final Object arg = JSType.toPrimitive(args[0]);
if (arg instanceof String || arg instanceof ConsString) {
if (JSType.isString(arg)) {
num = parseDateString(arg.toString());
} else {
num = timeClip(JSType.toNumber(args[0]));

View File

@ -181,7 +181,7 @@ public final class NativeJSON extends ScriptObject {
}
gap = sb.toString();
}
} else if (modSpace instanceof String || modSpace instanceof ConsString) {
} else if (JSType.isString(modSpace)) {
final String str = modSpace.toString();
gap = str.substring(0, Math.min(10, str.length()));
} else {

View File

@ -90,7 +90,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
private NativeString(final CharSequence value, final ScriptObject proto, final PropertyMap map) {
super(proto, map);
assert value instanceof String || value instanceof ConsString;
assert JSType.isString(value);
this.value = value;
}
@ -155,7 +155,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
final Object self = request.getReceiver();
final Class<?> returnType = desc.getMethodType().returnType();
if (returnType == Object.class && (self instanceof String || self instanceof ConsString)) {
if (returnType == Object.class && JSType.isString(self)) {
try {
return new GuardedInvocation(MH.findStatic(MethodHandles.lookup(), NativeString.class, "get", desc.getMethodType()), NashornGuards.getInstanceOf2Guard(String.class, ConsString.class));
} catch (final LookupException e) {
@ -1312,7 +1312,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
}
private static CharSequence getCharSequence(final Object self) {
if (self instanceof String || self instanceof ConsString) {
if (JSType.isString(self)) {
return (CharSequence)self;
} else if (self instanceof NativeString) {
return ((NativeString)self).getValue();

View File

@ -25,6 +25,8 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.runtime.JSType.isString;
import java.util.ArrayDeque;
import java.util.Deque;
@ -52,8 +54,8 @@ public final class ConsString implements CharSequence {
* @param right right char sequence
*/
public ConsString(final CharSequence left, final CharSequence right) {
assert left instanceof String || left instanceof ConsString;
assert right instanceof String || right instanceof ConsString;
assert isString(left);
assert isString(right);
this.left = left;
this.right = right;
length = left.length() + right.length();

View File

@ -311,7 +311,7 @@ public enum JSType {
return JSType.BOOLEAN;
}
if (obj instanceof String || obj instanceof ConsString) {
if (isString(obj)) {
return JSType.STRING;
}
@ -349,7 +349,7 @@ public enum JSType {
return JSType.BOOLEAN;
}
if (obj instanceof String || obj instanceof ConsString) {
if (isString(obj)) {
return JSType.STRING;
}
@ -455,8 +455,7 @@ public enum JSType {
obj == ScriptRuntime.UNDEFINED ||
obj instanceof Boolean ||
obj instanceof Number ||
obj instanceof String ||
obj instanceof ConsString;
isString(obj);
}
/**
@ -577,7 +576,7 @@ public enum JSType {
return num != 0 && !Double.isNaN(num);
}
if (obj instanceof String || obj instanceof ConsString) {
if (isString(obj)) {
return ((CharSequence)obj).length() > 0;
}
@ -627,6 +626,15 @@ public enum JSType {
}
}
/**
* Returns true if object represents a primitive JavaScript string value.
* @param obj the object
* @return true if the object represents a primitive JavaScript string value.
*/
public static boolean isString(final Object obj) {
return obj instanceof String || obj instanceof ConsString;
}
/**
* JavaScript compliant conversion of integer to String
*
@ -753,6 +761,36 @@ public enum JSType {
return toNumberGeneric(obj);
}
/**
* Converts an object for a comparison with a number. Almost identical to {@link #toNumber(Object)} but
* converts {@code null} to {@code NaN} instead of zero, so it won't compare equal to zero.
*
* @param obj an object
*
* @return a number
*/
public static double toNumberForEq(final Object obj) {
return obj == null ? Double.NaN : toNumber(obj);
}
/**
* Converts an object for strict comparison with a number. Returns {@code NaN} for any object that is not
* a {@link Number}, so only boxed numerics can compare strictly equal to numbers.
*
* @param obj an object
*
* @return a number
*/
public static double toNumberForStrictEq(final Object obj) {
if (obj instanceof Double) {
return (Double)obj;
}
if (obj instanceof Number) {
return ((Number)obj).doubleValue();
}
return Double.NaN;
}
/**
* JavaScript compliant conversion of Boolean to number

View File

@ -28,6 +28,7 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
@ -456,8 +457,7 @@ public abstract class ScriptFunctionData implements Serializable {
}
static boolean isPrimitiveThis(final Object obj) {
return obj instanceof String || obj instanceof ConsString ||
obj instanceof Number || obj instanceof Boolean;
return JSType.isString(obj) || obj instanceof Number || obj instanceof Boolean;
}
/**

View File

@ -32,6 +32,7 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
import static jdk.nashorn.internal.runtime.JSType.isString;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -56,7 +57,6 @@ import jdk.nashorn.internal.objects.NativeObject;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
* Utilities to be called by JavaScript runtime API and generated classes.
*/
@ -114,6 +114,11 @@ public final class ScriptRuntime {
*/
public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class);
/**
* Throws a reference error for an undefined variable.
*/
public static final Call THROW_CONST_TYPE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwConstTypeError", void.class, String.class);
/**
* Used to invalidate builtin names, e.g "Function" mapping to all properties in Function.prototype and Function.prototype itself.
*/
@ -402,6 +407,15 @@ public final class ScriptRuntime {
throw referenceError("not.defined", name);
}
/**
* Throws a type error for an assignment to a const.
*
* @param name the const name
*/
public static void throwConstTypeError(final String name) {
throw typeError("assign.constant", name);
}
/**
* Call a script function as a constructor with given args.
*
@ -522,8 +536,6 @@ public final class ScriptRuntime {
/**
* ECMA 11.6.1 - The addition operator (+) - generic implementation
* Compiler specializes using {@link jdk.nashorn.internal.codegen.RuntimeCallSite}
* if any type information is available for any of the operands
*
* @param x first term
* @param y second term
@ -550,8 +562,7 @@ public final class ScriptRuntime {
final Object xPrim = JSType.toPrimitive(x);
final Object yPrim = JSType.toPrimitive(y);
if (xPrim instanceof String || yPrim instanceof String
|| xPrim instanceof ConsString || yPrim instanceof ConsString) {
if (isString(xPrim) || isString(yPrim)) {
try {
return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim));
} catch (final IllegalArgumentException iae) {
@ -940,8 +951,15 @@ public final class ScriptRuntime {
* @return true if x is less than y
*/
public static boolean LT(final Object x, final Object y) {
final Object value = lessThan(x, y, true);
return value == UNDEFINED ? false : (Boolean)value;
final Object px = JSType.toPrimitive(x, Number.class);
final Object py = JSType.toPrimitive(y, Number.class);
return areBothString(px, py) ? px.toString().compareTo(py.toString()) < 0 :
JSType.toNumber(px) < JSType.toNumber(py);
}
private static boolean areBothString(final Object x, final Object y) {
return isString(x) && isString(y);
}
/**
@ -953,8 +971,11 @@ public final class ScriptRuntime {
* @return true if x is greater than y
*/
public static boolean GT(final Object x, final Object y) {
final Object value = lessThan(y, x, false);
return value == UNDEFINED ? false : (Boolean)value;
final Object px = JSType.toPrimitive(x, Number.class);
final Object py = JSType.toPrimitive(y, Number.class);
return areBothString(px, py) ? px.toString().compareTo(py.toString()) > 0 :
JSType.toNumber(px) > JSType.toNumber(py);
}
/**
@ -966,8 +987,11 @@ public final class ScriptRuntime {
* @return true if x is less than or equal to y
*/
public static boolean LE(final Object x, final Object y) {
final Object value = lessThan(y, x, false);
return !(Boolean.TRUE.equals(value) || value == UNDEFINED);
final Object px = JSType.toPrimitive(x, Number.class);
final Object py = JSType.toPrimitive(y, Number.class);
return areBothString(px, py) ? px.toString().compareTo(py.toString()) <= 0 :
JSType.toNumber(px) <= JSType.toNumber(py);
}
/**
@ -979,48 +1003,11 @@ public final class ScriptRuntime {
* @return true if x is greater than or equal to y
*/
public static boolean GE(final Object x, final Object y) {
final Object value = lessThan(x, y, true);
return !(Boolean.TRUE.equals(value) || value == UNDEFINED);
}
final Object px = JSType.toPrimitive(x, Number.class);
final Object py = JSType.toPrimitive(y, Number.class);
/** ECMA 11.8.5 The Abstract Relational Comparison Algorithm */
private static Object lessThan(final Object x, final Object y, final boolean leftFirst) {
Object px, py;
//support e.g. x < y should throw exception correctly if x or y are not numeric
if (leftFirst) {
px = JSType.toPrimitive(x, Number.class);
py = JSType.toPrimitive(y, Number.class);
} else {
py = JSType.toPrimitive(y, Number.class);
px = JSType.toPrimitive(x, Number.class);
}
if (JSType.ofNoFunction(px) == JSType.STRING && JSType.ofNoFunction(py) == JSType.STRING) {
// May be String or ConsString
return px.toString().compareTo(py.toString()) < 0;
}
final double nx = JSType.toNumber(px);
final double ny = JSType.toNumber(py);
if (Double.isNaN(nx) || Double.isNaN(ny)) {
return UNDEFINED;
}
if (nx == ny) {
return false;
}
if (nx > 0 && ny > 0 && Double.isInfinite(nx) && Double.isInfinite(ny)) {
return false;
}
if (nx < 0 && ny < 0 && Double.isInfinite(nx) && Double.isInfinite(ny)) {
return false;
}
return nx < ny;
return areBothString(px, py) ? px.toString().compareTo(py.toString()) >= 0 :
JSType.toNumber(px) >= JSType.toNumber(py);
}
/**
@ -1033,9 +1020,7 @@ public final class ScriptRuntime {
final Context context = Context.getContextTrusted();
final SwitchPoint sp = context.getBuiltinSwitchPoint(name);
assert sp != null;
if (sp != null) {
context.getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint");
SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
}
context.getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint");
SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
}
}

View File

@ -107,7 +107,7 @@ public final class ScriptingFunctions {
if (file instanceof File) {
f = (File)file;
} else if (file instanceof String || file instanceof ConsString) {
} else if (JSType.isString(file)) {
f = new java.io.File(((CharSequence)file).toString());
}

View File

@ -48,7 +48,6 @@ import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.RuntimeCallSite;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
import jdk.nashorn.internal.objects.ScriptFunctionImpl;
@ -209,19 +208,6 @@ public final class Bootstrap {
return dynamicLinker.link(LinkerCallSite.newLinkerCallSite(lookup, opDesc, type, flags));
}
/**
* Bootstrapper for a specialized Runtime call
*
* @param lookup lookup
* @param initialName initial name for callsite
* @param type method type for call site
*
* @return callsite for a runtime node
*/
public static CallSite runtimeBootstrap(final MethodHandles.Lookup lookup, final String initialName, final MethodType type) {
return new RuntimeCallSite(type, initialName);
}
/**
* Boostrapper for math calls that may overflow
* @param lookup lookup

View File

@ -25,11 +25,13 @@
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.runtime.JSType.isString;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_CALL;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETMEMBER;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETSLOT;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETMEMBER;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETSLOT;
import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_CALL;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.internal.dynalink.CallSiteDescriptor;
@ -40,7 +42,6 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
/**
@ -186,7 +187,7 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
if (index > -1) {
return JSOBJECT_GETSLOT.invokeExact(jsobj, index);
}
} else if (key instanceof String || key instanceof ConsString) {
} else if (isString(key)) {
final String name = key.toString();
if (name.indexOf('(') != -1) {
return fallback.invokeExact(jsobj, (Object) name);
@ -202,7 +203,7 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
JSOBJECT_SETSLOT.invokeExact(jsobj, (int)key, value);
} else if (key instanceof Number) {
JSOBJECT_SETSLOT.invokeExact(jsobj, getIndex((Number)key), value);
} else if (key instanceof String || key instanceof ConsString) {
} else if (isString(key)) {
JSOBJECT_SETMEMBER.invokeExact(jsobj, key.toString(), value);
}
}

View File

@ -25,6 +25,8 @@
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.runtime.JSType.isString;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Map;
@ -38,7 +40,6 @@ import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
/**
@ -166,7 +167,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
if (index > -1) {
return ((JSObject)jsobj).getSlot(index);
}
} else if (key instanceof String || key instanceof ConsString) {
} else if (isString(key)) {
final String name = key.toString();
// get with method name and signature. delegate it to beans linker!
if (name.indexOf('(') != -1) {
@ -183,7 +184,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
((JSObject)jsobj).setSlot((Integer)key, value);
} else if (key instanceof Number) {
((JSObject)jsobj).setSlot(getIndex((Number)key), value);
} else if (key instanceof String || key instanceof ConsString) {
} else if (isString(key)) {
((JSObject)jsobj).setMember(key.toString(), value);
}
}

View File

@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isString;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
@ -78,7 +79,7 @@ final class JavaArgumentConverters {
}
if (obj == UNDEFINED) {
// NOTE: same reasoning for FindBugs NP_BOOLEAN_RETURN_NUL warning as in the preceding comment.
// NOTE: same reasoning for FindBugs NP_BOOLEAN_RETURN_NULL warning as in the preceding comment.
return null;
}
@ -87,7 +88,7 @@ final class JavaArgumentConverters {
return num != 0 && !Double.isNaN(num);
}
if (obj instanceof String || obj instanceof ConsString) {
if (isString(obj)) {
return ((CharSequence) obj).length() > 0;
}
@ -207,7 +208,7 @@ final class JavaArgumentConverters {
return f.longValue();
} else if (obj instanceof Number) {
return ((Number)obj).longValue();
} else if (obj instanceof String || obj instanceof ConsString) {
} else if (isString(obj)) {
return JSType.toLong(obj);
} else if (obj instanceof Boolean) {
return (Boolean)obj ? 1L : 0L;

View File

@ -39,6 +39,7 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
@ -170,7 +171,7 @@ final class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, Gu
@SuppressWarnings("unused")
private static boolean isJavaScriptPrimitive(final Object o) {
return o instanceof String || o instanceof Boolean || o instanceof Number || o instanceof ConsString || o == null;
return JSType.isString(o) || o instanceof Boolean || o instanceof Number || o == null;
}
private static final MethodHandle GUARD_PRIMITIVE = findOwnMH("isJavaScriptPrimitive", boolean.class, Object.class);

View File

@ -167,7 +167,6 @@ reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side
syntax.error.invalid.json=Invalid JSON: {0}
syntax.error.strict.cant.delete=cannot delete "{0}" in strict mode
syntax.error.redeclare.variable=Variable "{0}" has already been declared
syntax.error.assign.constant=Assignment to constant "{0}"
syntax.error.unprotected.switch.declaration=Unsupported {0} declaration in unprotected switch statement
io.error.cant.write=cannot write "{0}"

View File

@ -0,0 +1,282 @@
/*
* Copyright (c) 2015 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-8035712: Restore some of the RuntimeCallSite specializations
*
* @test
* @run
*/
if ((typeof Assert) == "undefined") {
Assert = {
assertTrue: function(x) { if(!x) { throw "expected true" } },
assertFalse: function(x) { if(x) { throw "expected false" } },
};
}
function nop() {}
function EQ(x, y) {
// Exercise normal evaluation
Assert.assertTrue (x == y);
Assert.assertTrue (y == x);
Assert.assertFalse(x != y);
Assert.assertFalse(y != x);
// Exercise the branch optimizer
if (x == y) { nop(); } else { Assert.fail(); }
if (y == x) { nop(); } else { Assert.fail(); }
if (x != y) { Assert.fail(); } else { nop(); }
if (y != x) { Assert.fail(); } else { nop(); }
}
function NE(x, y) {
// Exercise normal evaluation
Assert.assertTrue (x != y);
Assert.assertTrue (y != x);
Assert.assertFalse(x == y);
Assert.assertFalse(y == x);
// Exercise the branch optimizer
if (x != y) { nop(); } else { Assert.fail(); }
if (y != x) { nop(); } else { Assert.fail(); }
if (x == y) { Assert.fail(); } else { nop(); }
if (y == x) { Assert.fail(); } else { nop(); }
}
function STRICT_EQ(x, y) {
// Exercise normal evaluation
Assert.assertTrue (x === y);
Assert.assertTrue (y === x);
Assert.assertFalse(x !== y);
Assert.assertFalse(y !== x);
// Exercise the branch optimizer
if (x === y) { nop(); } else { Assert.fail(); }
if (y === x) { nop(); } else { Assert.fail(); }
if (x !== y) { Assert.fail(); } else { nop(); }
if (y !== x) { Assert.fail(); } else { nop(); }
}
function STRICT_NE(x, y) {
// Exercise normal evaluation
Assert.assertTrue (x !== y);
Assert.assertTrue (y !== x);
Assert.assertFalse(x === y);
Assert.assertFalse(y === x);
// Exercise the branch optimizer
if (x !== y) { nop(); } else { Assert.fail(); }
if (y !== x) { nop(); } else { Assert.fail(); }
if (x === y) { Assert.fail(); } else { nop(); }
if (y === x) { Assert.fail(); } else { nop(); }
}
function cmpToAnyNumber(cmp, value) {
cmp(1, value);
cmp(4294967296, value);
cmp(1.2, value);
cmp(Infinity, value);
cmp(-Infinity, value);
cmp(1/Infinity, value);
cmp(0, value);
cmp(-0, value);
cmp(true, value);
cmp(false, value);
}
function notEqualToAnyNumber(value) {
cmpToAnyNumber(NE, value);
cmpToAnyNumber(STRICT_NE, value);
}
notEqualToAnyNumber(null);
notEqualToAnyNumber(void 0);
notEqualToAnyNumber("abc");
notEqualToAnyNumber({});
notEqualToAnyNumber(["xyz"]);
function objectWithPrimitiveFunctionNotEqualToAnyNumber(fnName) {
var obj = {
count: 0
};
obj[fnName] = function() { this.count++; return "foo"; };
notEqualToAnyNumber(obj);
// Every NE will invoke it 8 times; cmpToAnyNumber has 10 comparisons
// STRICT_NE doesn't invoke toString.
Assert.assertTrue(80 === obj.count);
}
objectWithPrimitiveFunctionNotEqualToAnyNumber("valueOf");
objectWithPrimitiveFunctionNotEqualToAnyNumber("toString");
function objectEqualButNotStrictlyEqual(val, obj) {
EQ(val, obj);
STRICT_NE(val, obj);
}
function numberEqualButNotStrictlyEqualToObject(num, obj) {
objectEqualButNotStrictlyEqual(num, obj);
objectEqualButNotStrictlyEqual(num, [obj]);
objectEqualButNotStrictlyEqual(num, [[obj]]);
}
function numberEqualButNotStrictlyEqualToZeroObjects(num) {
numberEqualButNotStrictlyEqualToObject(num, [0]);
numberEqualButNotStrictlyEqualToObject(num, "");
numberEqualButNotStrictlyEqualToObject(num, []);
numberEqualButNotStrictlyEqualToObject(num, "0");
}
numberEqualButNotStrictlyEqualToZeroObjects(0);
numberEqualButNotStrictlyEqualToZeroObjects(1/Infinity);
numberEqualButNotStrictlyEqualToZeroObjects(false);
function numberEqualButNotStrictlyEqualToObjectEquivalent(num) {
var str = String(num);
objectEqualButNotStrictlyEqual(num, str);
objectEqualButNotStrictlyEqual(num, { valueOf: function() { return str }});
objectEqualButNotStrictlyEqual(num, { toString: function() { return str }});
objectEqualButNotStrictlyEqual(num, { valueOf: function() { return num }});
objectEqualButNotStrictlyEqual(num, { toString: function() { return num }});
}
numberEqualButNotStrictlyEqualToObjectEquivalent(1);
numberEqualButNotStrictlyEqualToObjectEquivalent(4294967296);
numberEqualButNotStrictlyEqualToObjectEquivalent(1.2);
numberEqualButNotStrictlyEqualToObjectEquivalent(Infinity);
numberEqualButNotStrictlyEqualToObjectEquivalent(-Infinity);
numberEqualButNotStrictlyEqualToObjectEquivalent(1/Infinity);
numberEqualButNotStrictlyEqualToObjectEquivalent(0);
numberEqualButNotStrictlyEqualToObjectEquivalent(-0);
STRICT_EQ(1, new java.lang.Integer(1));
STRICT_EQ(1, new java.lang.Double(1));
STRICT_EQ(1.2, new java.lang.Double(1.2));
function LE(x, y) {
// Exercise normal evaluation
Assert.assertTrue(x <= y);
Assert.assertTrue(y >= x);
Assert.assertFalse(x > y);
Assert.assertFalse(x < y);
// Exercise the branch optimizer
if (x <= y) { nop(); } else { Assert.fail(); }
if (y >= x) { nop(); } else { Assert.fail(); }
if (x > y) { Assert.fail(); } else { nop(); }
if (y < x) { Assert.fail(); } else { nop(); }
}
function mutuallyLessThanOrEqual(x, y) {
LE(x, y);
LE(y, x);
}
mutuallyLessThanOrEqual(0, null);
mutuallyLessThanOrEqual(false, null);
mutuallyLessThanOrEqual(1/Infinity, null);
function mutuallyLessThanEqualToObjectWithValue(num, val) {
mutuallyLessThanOrEqual(num, { valueOf: function() { return val } });
mutuallyLessThanOrEqual(num, { toString: function() { return val } });
}
mutuallyLessThanEqualToObjectWithValue(false, 0);
mutuallyLessThanEqualToObjectWithValue(false, "");
mutuallyLessThanEqualToObjectWithValue(true, 1);
mutuallyLessThanEqualToObjectWithValue(true, "1");
function lessThanEqualToObjectEquivalent(num) {
var str = String(num);
mutuallyLessThanOrEqual(num, str);
mutuallyLessThanEqualToObjectWithValue(num, num);
mutuallyLessThanEqualToObjectWithValue(num, str);
}
lessThanEqualToObjectEquivalent(1);
lessThanEqualToObjectEquivalent(4294967296);
lessThanEqualToObjectEquivalent(1.2);
lessThanEqualToObjectEquivalent(Infinity);
lessThanEqualToObjectEquivalent(-Infinity);
lessThanEqualToObjectEquivalent(1/Infinity);
lessThanEqualToObjectEquivalent(0);
lessThanEqualToObjectEquivalent(-0);
function INCOMPARABLE(x, y) {
// Exercise normal evaluation
Assert.assertFalse(x < y);
Assert.assertFalse(x > y);
Assert.assertFalse(x <= y);
Assert.assertFalse(x >= y);
Assert.assertFalse(y < x);
Assert.assertFalse(y > x);
Assert.assertFalse(y <= x);
Assert.assertFalse(y >= x);
// Exercise the branch optimizer
if (x < y) { Assert.fail(); } else { nop(); }
if (x > y) { Assert.fail(); } else { nop(); }
if (x <= y) { Assert.fail(); } else { nop(); }
if (x >= y) { Assert.fail(); } else { nop(); }
if (y < x) { Assert.fail(); } else { nop(); }
if (y > x) { Assert.fail(); } else { nop(); }
if (y <= x) { Assert.fail(); } else { nop(); }
if (y >= x) { Assert.fail(); } else { nop(); }
}
function isIncomparable(value) {
cmpToAnyNumber(INCOMPARABLE, value);
}
isIncomparable(void 0);
isIncomparable({ valueOf: function() { return NaN }});
isIncomparable({ toString: function() { return NaN }});
// Force ScriptRuntime.LT(Object, Object) etc. comparisons
function cmpObj(fn, x, y) {
fn({valueOf: function() { return x }}, {valueOf: function() { return y }});
}
function LT(x, y) {
Assert.assertTrue(x < y);
Assert.assertTrue(y > x);
Assert.assertFalse(x >= y);
Assert.assertFalse(y <= x);
}
cmpObj(LT, 1, 2);
cmpObj(LT, 1, "2");
cmpObj(LT, "1", 2);
cmpObj(LT, "a", "b");
cmpObj(LT, -Infinity, 0);
cmpObj(LT, 0, Infinity);
cmpObj(LT, -Infinity, Infinity);
cmpObj(INCOMPARABLE, 1, NaN);
cmpObj(INCOMPARABLE, NaN, NaN);
cmpObj(INCOMPARABLE, "boo", NaN);
cmpObj(INCOMPARABLE, 1, "boo"); // boo number value will be NaN
// Test that a comparison call site can deoptimize from (int, int) to (object, object)
(function(){
var x = [1, 2, "a"];
var y = [2, "3", "b"];
for(var i = 0; i < 3; ++i) {
Assert.assertTrue(x[i] < y[i]);
}
})();

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2015, 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-8074021: Indirect eval fails when used as an element of an array or as a property of an object
*
* @test
* @run
*/
var obj = { foo: eval };
Assert.assertTrue(obj.foo("typeof(print) == 'function'"));
Assert.assertTrue(obj.foo("RegExp instanceof Function"));
Assert.assertEquals(obj.foo("String(new Array(2, 4, 3))"), "2,4,3");
obj.foo("print('hello')");
var args = [ eval ];
Assert.assertTrue(args[0]("typeof(print) == 'function'"));
Assert.assertTrue(args[0]("RegExp instanceof Function"));
Assert.assertEquals(args[0]("String(new Array(2, 4, 3))"), "2,4,3");
args[0]("print('hello')");

View File

@ -0,0 +1,2 @@
hello
hello

View File

@ -31,144 +31,147 @@
"use strict";
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x = 1;\n');
const x = 2;
x = 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x++;\n');
const x = 2;
x++;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x--;\n');
const x = 2;
x--;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'++x;\n');
const x = 2;
++x;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'--x;\n');
const x = 2;
--x;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x += 1;\n');
const x = 2;
x += 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x *= 1;\n');
const x = 2;
x *= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x /= 1;\n');
const x = 2;
x /= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x %= 1;\n');
const x = 2;
x %= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x |= 1;\n');
const x = 2;
x |= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x &= 1;\n');
const x = 2;
x &= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x ^= 1;\n');
const x = 2;
x ^= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x <<= 1;\n');
const x = 2;
x <<= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x >>= 1;\n');
const x = 2;
x >>= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x >>>= 1;\n');
const x = 2;
x >>>= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'delete x;\n');
const x = 2;
delete x;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
const c = 1;
try {
c = 2;
fail("const assignment didn't throw");
} catch (e) {
print(e);
}
(function() {
try {
c = 2;
fail("const assignment didn't throw");
} catch (e) {
print(e);
}
})();

View File

@ -1,16 +1,18 @@
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
SyntaxError: cannot delete "x" in strict mode
TypeError: Assignment to constant "c"
TypeError: Assignment to constant "c"

View File

@ -1,8 +1,4 @@
ReferenceError: "a" is not defined
SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
a--
^
SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
a--
^
TypeError: Assignment to constant "a"
TypeError: Assignment to constant "a"
ReferenceError: "a" is not defined