mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
Merge
This commit is contained in:
commit
326c95a872
@ -43,7 +43,7 @@
|
||||
classpath="${javac.classpath}"
|
||||
debug="${javac.debug}"
|
||||
includeantruntime="false" fork="true">
|
||||
<compilerarg value="-J-Djava.ext.dirs="/>
|
||||
<compilerarg value="-J-Xbootclasspath/p:${javac.classpath}"/>
|
||||
<compilerarg value="-Xlint:unchecked"/>
|
||||
<compilerarg value="-Xlint:deprecation"/>
|
||||
<compilerarg value="-XDignore.symbol.file"/>
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
<pathelement location="${dist.dir}/nasgen.jar"/>
|
||||
<pathelement path="${build.dir}/classes"/>
|
||||
</classpath>
|
||||
<jvmarg value="-Djava.ext.dirs="/>
|
||||
<jvmarg value="${boot.class.path}"/>
|
||||
<arg value="${build.dir}/classes"/>
|
||||
<arg value="jdk.nashorn.internal.objects"/>
|
||||
<arg value="${build.dir}/classes"/>
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
<path id="nashorn.boot.prefix.path">
|
||||
<pathelement location="${dist.jar}"/>
|
||||
</path>
|
||||
<property name="boot.class.path" value="-Xbootclasspath/p:"${toString:nashorn.boot.prefix.path}""/>
|
||||
<property name="boot.class.path" value="-Xbootclasspath/p:${toString:nashorn.boot.prefix.path}"/>
|
||||
<condition property="svn.executable" value="/usr/local/bin/svn" else="svn">
|
||||
<available file="/usr/local/bin/svn"/>
|
||||
</condition>
|
||||
@ -271,6 +271,7 @@
|
||||
debug="${javac.debug}"
|
||||
encoding="${javac.encoding}"
|
||||
includeantruntime="false" fork="true">
|
||||
<compilerarg value="${boot.class.path}"/>
|
||||
<compilerarg value="-Xlint:unchecked"/>
|
||||
<compilerarg value="-Xlint:deprecation"/>
|
||||
<compilerarg value="-Xdiags:verbose"/>
|
||||
|
||||
@ -284,7 +284,7 @@ run.test.jvmargs.common=\
|
||||
-XX:+HeapDumpOnOutOfMemoryError
|
||||
|
||||
# turn on assertions for tests
|
||||
run.test.jvmargs.main=${run.test.jvmargs.common} -ea
|
||||
run.test.jvmargs.main=${run.test.jvmargs.common} -esa -ea
|
||||
|
||||
# Extra jvmargs that might be useful for debugging
|
||||
# and performance improvements/monitoring
|
||||
|
||||
91
nashorn/samples/browser_dom.js
Normal file
91
nashorn/samples/browser_dom.js
Normal file
@ -0,0 +1,91 @@
|
||||
#// Usage: jjs -fx browser.js
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 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 browser.js");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// JavaFX classes used
|
||||
var ChangeListener = Java.type("javafx.beans.value.ChangeListener");
|
||||
var Scene = Java.type("javafx.scene.Scene");
|
||||
var WebView = Java.type("javafx.scene.web.WebView");
|
||||
var EventListener = Java.type("org.w3c.dom.events.EventListener");
|
||||
|
||||
// JavaFX start method
|
||||
function start(stage) {
|
||||
start.title = "Web View";
|
||||
var wv = new WebView();
|
||||
wv.engine.loadContent(<<EOF
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
This is the title
|
||||
</title>
|
||||
<script>
|
||||
// click count for OK button
|
||||
var okCount = 0;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Button from the input html<br>
|
||||
<button type="button" onclick="okCount++">OK</button><br>
|
||||
</body>
|
||||
</html>
|
||||
EOF, "text/html");
|
||||
|
||||
// attach onload handler
|
||||
wv.engine.loadWorker.stateProperty().addListener(
|
||||
new ChangeListener() {
|
||||
changed: function() {
|
||||
// DOM document element
|
||||
var document = wv.engine.document;
|
||||
// DOM manipulation
|
||||
var btn = document.createElement("button");
|
||||
var n = 0;
|
||||
// attach a button handler - nashorn function!
|
||||
btn.onclick = new EventListener(function() {
|
||||
n++; print("You clicked " + n + " time(s)");
|
||||
print("you clicked OK " + wv.engine.executeScript("okCount"));
|
||||
});
|
||||
// attach text to button
|
||||
var t = document.createTextNode("Click Me!");
|
||||
btn.appendChild(t);
|
||||
// attach button to the document
|
||||
document.body.appendChild(btn);
|
||||
}
|
||||
}
|
||||
);
|
||||
stage.scene = new Scene(wv, 750, 500);
|
||||
stage.show();
|
||||
}
|
||||
@ -917,7 +917,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
@Override
|
||||
public Node leaveSwitchNode(final SwitchNode switchNode) {
|
||||
// We only need a symbol for the tag if it's not an integer switch node
|
||||
if(!switchNode.isInteger()) {
|
||||
if(!switchNode.isUniqueInteger()) {
|
||||
switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
|
||||
}
|
||||
return switchNode;
|
||||
|
||||
@ -465,10 +465,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
// If this is either __FILE__, __DIR__, or __LINE__ then load the property initially as Object as we'd convert
|
||||
// it anyway for replaceLocationPropertyPlaceholder.
|
||||
if(identNode.isCompileTimePropertyName()) {
|
||||
method.dynamicGet(Type.OBJECT, identNode.getSymbol().getName(), flags, identNode.isFunction());
|
||||
method.dynamicGet(Type.OBJECT, identNode.getSymbol().getName(), flags, identNode.isFunction(), false);
|
||||
replaceCompileTimeProperty();
|
||||
} else {
|
||||
dynamicGet(identNode.getSymbol().getName(), flags, identNode.isFunction());
|
||||
dynamicGet(identNode.getSymbol().getName(), flags, identNode.isFunction(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -486,7 +486,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) {
|
||||
loadFastScopeProto(symbol, true);
|
||||
method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE);
|
||||
method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE, false);
|
||||
return method;
|
||||
}
|
||||
|
||||
@ -571,9 +571,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
// Operands' load type should not be narrower than the narrowest of the individual operand types, nor narrower
|
||||
// than the lower explicit bound, but it should also not be wider than
|
||||
final Type narrowestOperandType = Type.narrowest(Type.widest(lhs.getType(), rhs.getType()), explicitOperandBounds.widest);
|
||||
final Type lhsType = undefinedToNumber(lhs.getType());
|
||||
final Type rhsType = undefinedToNumber(rhs.getType());
|
||||
final Type narrowestOperandType = Type.narrowest(Type.widest(lhsType, rhsType), explicitOperandBounds.widest);
|
||||
final TypeBounds operandBounds = explicitOperandBounds.notNarrowerThan(narrowestOperandType);
|
||||
if (noToPrimitiveConversion(lhs.getType(), explicitOperandBounds.widest) || rhs.isLocal()) {
|
||||
if (noToPrimitiveConversion(lhsType, explicitOperandBounds.widest) || rhs.isLocal()) {
|
||||
// Can reorder. We might still need to separate conversion, but at least we can do it with reordering
|
||||
if (forceConversionSeparation) {
|
||||
// Can reorder, but can't move conversion into the operand as the operation depends on operands
|
||||
@ -594,10 +596,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
// Can't reorder. Load and convert separately.
|
||||
final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType);
|
||||
loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack);
|
||||
final Type lhsType = method.peekType();
|
||||
final Type lhsLoadedType = method.peekType();
|
||||
loadExpression(rhs, safeConvertBounds, false);
|
||||
final Type convertedLhsType = operandBounds.within(method.peekType());
|
||||
if (convertedLhsType != lhsType) {
|
||||
if (convertedLhsType != lhsLoadedType) {
|
||||
// Do it conditionally, so that if conversion is a no-op we don't introduce a SWAP, SWAP.
|
||||
method.swap().convert(convertedLhsType).swap();
|
||||
}
|
||||
@ -609,6 +611,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return method;
|
||||
}
|
||||
|
||||
private static final Type undefinedToNumber(final Type type) {
|
||||
return type == Type.UNDEFINED ? Type.NUMBER : type;
|
||||
}
|
||||
|
||||
private static final class TypeBounds {
|
||||
final Type narrowest;
|
||||
final Type widest;
|
||||
@ -739,7 +745,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
@Override
|
||||
void consumeStack() {
|
||||
final int flags = getCallSiteFlags();
|
||||
dynamicGet(accessNode.getProperty(), flags, accessNode.isFunction());
|
||||
dynamicGet(accessNode.getProperty(), flags, accessNode.isFunction(), accessNode.isIndex());
|
||||
}
|
||||
}.emit(baseAlreadyOnStack ? 1 : 0);
|
||||
return false;
|
||||
@ -1443,7 +1449,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
// NOTE: not using a nested OptimisticOperation on this dynamicGet, as we expect to get back
|
||||
// a callable object. Nobody in their right mind would optimistically type this call site.
|
||||
assert !node.isOptimistic();
|
||||
method.dynamicGet(node.getType(), node.getProperty(), flags, true);
|
||||
method.dynamicGet(node.getType(), node.getProperty(), flags, true, node.isIndex());
|
||||
method.swap();
|
||||
argCount = loadArgs(args);
|
||||
}
|
||||
@ -2015,6 +2021,19 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final Expression test = ifNode.getTest();
|
||||
final Block pass = ifNode.getPass();
|
||||
final Block fail = ifNode.getFail();
|
||||
|
||||
if (Expression.isAlwaysTrue(test)) {
|
||||
loadAndDiscard(test);
|
||||
pass.accept(this);
|
||||
return false;
|
||||
} else if (Expression.isAlwaysFalse(test)) {
|
||||
loadAndDiscard(test);
|
||||
if (fail != null) {
|
||||
fail.accept(this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean hasFailConversion = LocalVariableConversion.hasLiveConversion(ifNode);
|
||||
|
||||
final Label failLabel = new Label("if_fail");
|
||||
@ -2034,7 +2053,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method.beforeJoinPoint(ifNode);
|
||||
}
|
||||
|
||||
if(afterLabel != null) {
|
||||
if(afterLabel != null && afterLabel.isReachable()) {
|
||||
method.label(afterLabel);
|
||||
}
|
||||
|
||||
@ -2811,7 +2830,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
Label defaultLabel = defaultCase != null ? defaultCase.getEntry() : breakLabel;
|
||||
final boolean hasSkipConversion = LocalVariableConversion.hasLiveConversion(switchNode);
|
||||
|
||||
if (switchNode.isInteger()) {
|
||||
if (switchNode.isUniqueInteger()) {
|
||||
// Tree for sorting values.
|
||||
final TreeMap<Integer, Label> tree = new TreeMap<>();
|
||||
|
||||
@ -3146,14 +3165,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
if (isFastScope(identSymbol)) {
|
||||
storeFastScopeVar(identSymbol, flags);
|
||||
} else {
|
||||
method.dynamicSet(identNode.getName(), flags);
|
||||
method.dynamicSet(identNode.getName(), flags, false);
|
||||
}
|
||||
} else {
|
||||
final Type identType = identNode.getType();
|
||||
if(identType == Type.UNDEFINED) {
|
||||
// The symbol must not be slotted; the initializer is either itself undefined (explicit assignment of
|
||||
// undefined to undefined), or the left hand side is a dead variable.
|
||||
assert !identNode.getSymbol().isScope();
|
||||
// The initializer is either itself undefined (explicit assignment of undefined to undefined),
|
||||
// or the left hand side is a dead variable.
|
||||
assert init.getType() == Type.UNDEFINED || identNode.getSymbol().slotCount() == 0;
|
||||
loadAndDiscard(init);
|
||||
return false;
|
||||
@ -3265,7 +3283,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
emitContinueLabel(continueLabel, liveLocalsOnContinue);
|
||||
}
|
||||
|
||||
if (loopNode.hasPerIterationScope() && lc.getParentBlock().needsScope()) {
|
||||
if (loopNode.hasPerIterationScope() && lc.getCurrentBlock().needsScope()) {
|
||||
// ES6 for loops with LET init need a new scope for each iteration. We just create a shallow copy here.
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
method.invoke(virtualCallNoLookup(ScriptObject.class, "copy", ScriptObject.class));
|
||||
@ -3576,9 +3594,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
|
||||
} else {
|
||||
// Non-optimistic, non-FP +. Allow it to overflow.
|
||||
operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest),
|
||||
Type.OBJECT);
|
||||
forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest);
|
||||
final Type widestOperationType = binaryNode.getWidestOperationType();
|
||||
operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest), widestOperationType);
|
||||
forceConversionSeparation = widestOperationType.narrowerThan(resultBounds.widest);
|
||||
}
|
||||
loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
|
||||
}
|
||||
@ -3693,8 +3711,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final Expression lhs = assignNode.lhs();
|
||||
final Expression rhs = assignNode.rhs();
|
||||
final Type widestOperationType = assignNode.getWidestOperationType();
|
||||
final Type widest = assignNode.isTokenType(TokenType.ASSIGN_ADD) ? Type.OBJECT : widestOperationType;
|
||||
final TypeBounds bounds = new TypeBounds(assignNode.getType(), widest);
|
||||
final TypeBounds bounds = new TypeBounds(assignNode.getType(), widestOperationType);
|
||||
new OptimisticOperation(assignNode, bounds) {
|
||||
@Override
|
||||
void loadStack() {
|
||||
@ -4252,7 +4269,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
if (isFastScope(symbol)) {
|
||||
storeFastScopeVar(symbol, flags);
|
||||
} else {
|
||||
method.dynamicSet(node.getName(), flags);
|
||||
method.dynamicSet(node.getName(), flags, false);
|
||||
}
|
||||
} else {
|
||||
final Type storeType = assignNode.getType();
|
||||
@ -4269,7 +4286,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterAccessNode(final AccessNode node) {
|
||||
method.dynamicSet(node.getProperty(), getCallSiteFlags());
|
||||
method.dynamicSet(node.getProperty(), getCallSiteFlags(), node.isIndex());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4607,11 +4624,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* @param isMethod whether we're preferrably retrieving a function
|
||||
* @return the current method emitter
|
||||
*/
|
||||
MethodEmitter dynamicGet(final String name, final int flags, final boolean isMethod) {
|
||||
MethodEmitter dynamicGet(final String name, final int flags, final boolean isMethod, final boolean isIndex) {
|
||||
if(isOptimistic) {
|
||||
return method.dynamicGet(getOptimisticCoercedType(), name, getOptimisticFlags(flags), isMethod);
|
||||
return method.dynamicGet(getOptimisticCoercedType(), name, getOptimisticFlags(flags), isMethod, isIndex);
|
||||
}
|
||||
return method.dynamicGet(resultBounds.within(expression.getType()), name, nonOptimisticFlags(flags), isMethod);
|
||||
return method.dynamicGet(resultBounds.within(expression.getType()), name, nonOptimisticFlags(flags), isMethod, isIndex);
|
||||
}
|
||||
|
||||
MethodEmitter dynamicGetIndex(final int flags, final boolean isMethod) {
|
||||
|
||||
@ -26,12 +26,16 @@
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.BlockStatement;
|
||||
import jdk.nashorn.internal.ir.CaseNode;
|
||||
import jdk.nashorn.internal.ir.EmptyNode;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
@ -40,6 +44,7 @@ import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.Statement;
|
||||
import jdk.nashorn.internal.ir.SwitchNode;
|
||||
import jdk.nashorn.internal.ir.TernaryNode;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
import jdk.nashorn.internal.ir.VarNode;
|
||||
@ -126,11 +131,37 @@ final class FoldConstants extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
public Node leaveTernaryNode(final TernaryNode ternaryNode) {
|
||||
final Node test = ternaryNode.getTest();
|
||||
if (test instanceof LiteralNode.PrimitiveLiteralNode) {
|
||||
return ((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression();
|
||||
return (((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression()).getExpression();
|
||||
}
|
||||
return ternaryNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSwitchNode(final SwitchNode switchNode) {
|
||||
return switchNode.setUniqueInteger(lc, isUniqueIntegerSwitchNode(switchNode));
|
||||
}
|
||||
|
||||
private static boolean isUniqueIntegerSwitchNode(final SwitchNode switchNode) {
|
||||
final Set<Integer> alreadySeen = new HashSet<>();
|
||||
for (final CaseNode caseNode : switchNode.getCases()) {
|
||||
final Expression test = caseNode.getTest();
|
||||
if (test != null && !isUniqueIntegerLiteral(test, alreadySeen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isUniqueIntegerLiteral(final Expression expr, final Set<Integer> alreadySeen) {
|
||||
if (expr instanceof LiteralNode) {
|
||||
final Object value = ((LiteralNode<?>)expr).getValue();
|
||||
if (value instanceof Integer) {
|
||||
return alreadySeen.add((Integer)value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to evaluate constant expressions at compile time This is
|
||||
* also a simplifier used by BinaryNode visits, UnaryNode visits and
|
||||
|
||||
@ -28,6 +28,7 @@ package jdk.nashorn.internal.codegen;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||
import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse;
|
||||
import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -82,7 +83,6 @@ import jdk.nashorn.internal.ir.UnaryNode;
|
||||
import jdk.nashorn.internal.ir.VarNode;
|
||||
import jdk.nashorn.internal.ir.WhileNode;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
|
||||
/**
|
||||
@ -93,6 +93,13 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
* variable to its widest used type after the join point. That would eliminate some widenings of undefined variables to
|
||||
* object, most notably those used only in loops. We need a full liveness analysis for that. Currently, we can establish
|
||||
* per-type liveness, which eliminates most of unwanted dead widenings.
|
||||
* NOTE: the way this class is implemented, it actually processes the AST in two passes. The first pass is top-down and
|
||||
* implemented in {@code enterXxx} methods. This pass does not mutate the AST (except for one occurrence, noted below),
|
||||
* as being able to find relevant labels for control flow joins is sensitive to their reference identity, and mutated
|
||||
* label-carrying nodes will create copies of their labels. A second bottom-up pass applying the changes is implemented
|
||||
* in the separate visitor sitting in {@link #leaveFunctionNode(FunctionNode)}. This visitor will also instantiate new
|
||||
* instances of the calculator to be run on nested functions (when not lazy compiling).
|
||||
*
|
||||
*/
|
||||
final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
@ -398,48 +405,53 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
@Override
|
||||
public boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
// NOTE: regardless of operator's lexical associativity, lhs is always evaluated first.
|
||||
final Expression lhs = binaryNode.lhs();
|
||||
final Expression rhs = binaryNode.rhs();
|
||||
final boolean isAssignment = binaryNode.isAssignment();
|
||||
|
||||
final TokenType tokenType = Token.descType(binaryNode.getToken());
|
||||
if(tokenType.isLeftAssociative()) {
|
||||
assert !isAssignment;
|
||||
final boolean isLogical = binaryNode.isLogical();
|
||||
final Label joinLabel = isLogical ? new Label("") : null;
|
||||
lhs.accept(this);
|
||||
if(isLogical) {
|
||||
jumpToLabel((JoinPredecessor)lhs, joinLabel);
|
||||
}
|
||||
rhs.accept(this);
|
||||
if(isLogical) {
|
||||
jumpToLabel((JoinPredecessor)rhs, joinLabel);
|
||||
}
|
||||
joinOnLabel(joinLabel);
|
||||
} else {
|
||||
rhs.accept(this);
|
||||
if(isAssignment) {
|
||||
if(lhs instanceof BaseNode) {
|
||||
((BaseNode)lhs).getBase().accept(this);
|
||||
if(lhs instanceof IndexNode) {
|
||||
((IndexNode)lhs).getIndex().accept(this);
|
||||
} else {
|
||||
assert lhs instanceof AccessNode;
|
||||
}
|
||||
LvarType lhsTypeOnLoad = null;
|
||||
if(isAssignment) {
|
||||
if(lhs instanceof BaseNode) {
|
||||
((BaseNode)lhs).getBase().accept(this);
|
||||
if(lhs instanceof IndexNode) {
|
||||
((IndexNode)lhs).getIndex().accept(this);
|
||||
} else {
|
||||
assert lhs instanceof IdentNode;
|
||||
if(binaryNode.isSelfModifying()) {
|
||||
((IdentNode)lhs).accept(this);
|
||||
}
|
||||
assert lhs instanceof AccessNode;
|
||||
}
|
||||
} else {
|
||||
lhs.accept(this);
|
||||
assert lhs instanceof IdentNode;
|
||||
if(binaryNode.isSelfModifying()) {
|
||||
final IdentNode ident = ((IdentNode)lhs);
|
||||
ident.accept(this);
|
||||
// Self-assignment can cause a change in the type of the variable. For purposes of evaluating
|
||||
// the type of the operation, we must use its type as it was when it was loaded. If we didn't
|
||||
// do this, some awkward expressions would end up being calculated incorrectly, e.g.
|
||||
// "var x; x += x = 0;". In this case we have undefined+int so the result type is double (NaN).
|
||||
// However, if we used the type of "x" on LHS after we evaluated RHS, we'd see int+int, so the
|
||||
// result type would be either optimistic int or pessimistic long, which would be wrong.
|
||||
lhsTypeOnLoad = getLocalVariableTypeIfBytecode(ident.getSymbol());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lhs.accept(this);
|
||||
}
|
||||
|
||||
final boolean isLogical = binaryNode.isLogical();
|
||||
assert !(isAssignment && isLogical); // there are no logical assignment operators in JS
|
||||
final Label joinLabel = isLogical ? new Label("") : null;
|
||||
if(isLogical) {
|
||||
jumpToLabel((JoinPredecessor)lhs, joinLabel);
|
||||
}
|
||||
|
||||
final Expression rhs = binaryNode.rhs();
|
||||
rhs.accept(this);
|
||||
if(isLogical) {
|
||||
jumpToLabel((JoinPredecessor)rhs, joinLabel);
|
||||
}
|
||||
joinOnLabel(joinLabel);
|
||||
|
||||
if(isAssignment && lhs instanceof IdentNode) {
|
||||
if(binaryNode.isSelfModifying()) {
|
||||
onSelfAssignment((IdentNode)lhs, binaryNode);
|
||||
onSelfAssignment((IdentNode)lhs, binaryNode, lhsTypeOnLoad);
|
||||
} else {
|
||||
onAssignment((IdentNode)lhs, rhs);
|
||||
}
|
||||
@ -704,7 +716,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
// Control flow is different for all-integer cases where we dispatch by switch table, and for all other cases
|
||||
// where we do sequential comparison. Note that CaseNode objects act as join points.
|
||||
final boolean isInteger = switchNode.isInteger();
|
||||
final boolean isInteger = switchNode.isUniqueInteger();
|
||||
final Label breakLabel = switchNode.getBreakLabel();
|
||||
final boolean hasDefault = switchNode.getDefaultCase() != null;
|
||||
|
||||
@ -919,7 +931,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
|
||||
if(unaryNode.isSelfModifying()) {
|
||||
if(expr instanceof IdentNode) {
|
||||
onSelfAssignment((IdentNode)expr, unaryNode);
|
||||
final IdentNode ident = (IdentNode)expr;
|
||||
onSelfAssignment(ident, unaryNode, getLocalVariableTypeIfBytecode(ident.getSymbol()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -973,12 +986,41 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current type of the local variable represented by the symbol. This is the most strict of all
|
||||
* {@code getLocalVariableType*} methods, as it will throw an assertion if the type is null. Therefore, it is only
|
||||
* safe to be invoked on symbols known to be bytecode locals, and only after they have been initialized.
|
||||
* Regardless, it is recommended to use this method in majority of cases, as because of its strictness it is the
|
||||
* best suited for catching missing type calculation bugs early.
|
||||
* @param symbol a symbol representing a bytecode local variable.
|
||||
* @return the current type of the local variable represented by the symbol
|
||||
*/
|
||||
private LvarType getLocalVariableType(final Symbol symbol) {
|
||||
final LvarType type = getLocalVariableTypeOrNull(symbol);
|
||||
assert type != null;
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type for a local variable if it is a bytecode local, otherwise null. Can be used in circumstances where
|
||||
* the type is irrelevant if the symbol is not a bytecode local. Note that for bytecode locals, it delegates to
|
||||
* {@link #getLocalVariableType(Symbol)}, so it will still assert that the type for such variable is already
|
||||
* defined (that is, not null).
|
||||
* @param symbol the symbol representing the variable.
|
||||
* @return the current variable type, if it is a bytecode local, otherwise null.
|
||||
*/
|
||||
private LvarType getLocalVariableTypeIfBytecode(final Symbol symbol) {
|
||||
return symbol.isBytecodeLocal() ? getLocalVariableType(symbol) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict
|
||||
* of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where
|
||||
* a just-defined symbol might still be null).
|
||||
* @param symbol the symbol
|
||||
* @return the current type for the symbol, or null if the type is not known either because the symbol has not been
|
||||
* initialized, or because the symbol does not represent a bytecode local variable.
|
||||
*/
|
||||
private LvarType getLocalVariableTypeOrNull(final Symbol symbol) {
|
||||
return localVariableTypes.get(symbol);
|
||||
}
|
||||
@ -1358,13 +1400,13 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
jumpToCatchBlock(identNode);
|
||||
}
|
||||
|
||||
private void onSelfAssignment(final IdentNode identNode, final Expression assignment) {
|
||||
private void onSelfAssignment(final IdentNode identNode, final Expression assignment, final LvarType typeOnLoad) {
|
||||
final Symbol symbol = identNode.getSymbol();
|
||||
assert symbol != null : identNode.getName();
|
||||
if(!symbol.isBytecodeLocal()) {
|
||||
return;
|
||||
}
|
||||
final LvarType type = toLvarType(getType(assignment));
|
||||
final LvarType type = toLvarType(getType(assignment, symbol, typeOnLoad.type));
|
||||
// Self-assignment never produce either a boolean or undefined
|
||||
assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN;
|
||||
setType(symbol, type);
|
||||
@ -1445,13 +1487,24 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
symbolIsUsed(symbol, getLocalVariableType(symbol));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the expression, dependent on the current types of the local variables.
|
||||
*
|
||||
* @param expr the expression
|
||||
* @return the current type of the expression dependent on the current types of the local variables.
|
||||
*/
|
||||
private Type getType(final Expression expr) {
|
||||
return expr.getType(getSymbolToType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function object from symbols to their types, used by the expressions to evaluate their type.
|
||||
* {@link BinaryNode} specifically uses identity of the function to cache type calculations. This method makes
|
||||
* sure to return the same function object while the local variable types don't change, and create a new function
|
||||
* object if the local variable types have been changed.
|
||||
* @return a function object representing a mapping from symbols to their types.
|
||||
*/
|
||||
private Function<Symbol, Type> getSymbolToType() {
|
||||
// BinaryNode uses identity of the function to cache type calculations. Therefore, we must use different
|
||||
// function instances for different localVariableTypes instances.
|
||||
if(symbolToType.isStale()) {
|
||||
symbolToType = new SymbolToType();
|
||||
}
|
||||
@ -1469,4 +1522,41 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
return boundTypes != localVariableTypes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the expression, dependent on the current types of the local variables and a single overridden
|
||||
* symbol type. Used by type calculation on compound operators to ensure the type of the LHS at the time it was
|
||||
* loaded (which can potentially be different after RHS evaluation, e.g. "var x; x += x = 0;") is preserved for
|
||||
* the calculation.
|
||||
*
|
||||
* @param expr the expression
|
||||
* @param overriddenSymbol the overridden symbol
|
||||
* @param overriddenType the overridden type
|
||||
* @return the current type of the expression dependent on the current types of the local variables and the single
|
||||
* potentially overridden type.
|
||||
*/
|
||||
private Type getType(final Expression expr, final Symbol overriddenSymbol, final Type overriddenType) {
|
||||
return expr.getType(getSymbolToType(overriddenSymbol, overriddenType));
|
||||
}
|
||||
|
||||
private Function<Symbol, Type> getSymbolToType(final Symbol overriddenSymbol, final Type overriddenType) {
|
||||
return getLocalVariableType(overriddenSymbol).type == overriddenType ? getSymbolToType() :
|
||||
new SymbolToTypeOverride(overriddenSymbol, overriddenType);
|
||||
}
|
||||
|
||||
private class SymbolToTypeOverride implements Function<Symbol, Type> {
|
||||
private final Function<Symbol, Type> originalSymbolToType = getSymbolToType();
|
||||
private final Symbol overriddenSymbol;
|
||||
private final Type overriddenType;
|
||||
|
||||
SymbolToTypeOverride(final Symbol overriddenSymbol, final Type overriddenType) {
|
||||
this.overriddenSymbol = overriddenSymbol;
|
||||
this.overriddenType = overriddenType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type apply(final Symbol symbol) {
|
||||
return symbol == overriddenSymbol ? overriddenType : originalSymbolToType.apply(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.regex.Pattern;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
import jdk.nashorn.internal.ir.BaseNode;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
@ -52,6 +54,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
import jdk.nashorn.internal.ir.JumpStatement;
|
||||
import jdk.nashorn.internal.ir.LabelNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
@ -93,6 +96,10 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
|
||||
private final DebugLogger log;
|
||||
|
||||
// Conservative pattern to test if element names consist of characters valid for identifiers.
|
||||
// This matches any non-zero length alphanumeric string including _ and $ and not starting with a digit.
|
||||
private static Pattern SAFE_PROPERTY_NAME = Pattern.compile("[a-zA-Z_$][\\w$]*");
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@ -140,7 +147,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
}
|
||||
});
|
||||
|
||||
this.log = initLogger(compiler.getContext());
|
||||
this.log = initLogger(compiler.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -180,6 +187,28 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIndexNode(final IndexNode indexNode) {
|
||||
final String name = getConstantPropertyName(indexNode.getIndex());
|
||||
if (name != null) {
|
||||
// If index node is a constant property name convert index node to access node.
|
||||
assert Token.descType(indexNode.getToken()) == TokenType.LBRACKET;
|
||||
return new AccessNode(indexNode.getToken(), indexNode.getFinish(), indexNode.getBase(), name);
|
||||
}
|
||||
return super.leaveIndexNode(indexNode);
|
||||
}
|
||||
|
||||
// If expression is a primitive literal that is not an array index and does return its string value. Else return null.
|
||||
private static String getConstantPropertyName(final Expression expression) {
|
||||
if (expression instanceof LiteralNode.PrimitiveLiteralNode) {
|
||||
final Object value = ((LiteralNode) expression).getValue();
|
||||
if (value instanceof String && SAFE_PROPERTY_NAME.matcher((String) value).matches()) {
|
||||
return (String) value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
|
||||
final Expression expr = expressionStatement.getExpression();
|
||||
@ -275,7 +304,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
|
||||
@Override
|
||||
public Node leaveSwitchNode(final SwitchNode switchNode) {
|
||||
if(!switchNode.isInteger()) {
|
||||
if(!switchNode.isUniqueInteger()) {
|
||||
// Wrap it in a block so its internally created tag is restricted in scope
|
||||
addStatementEnclosedInBlock(switchNode);
|
||||
} else {
|
||||
|
||||
@ -2213,10 +2213,10 @@ public class MethodEmitter implements Emitter {
|
||||
* @param name name of property
|
||||
* @param flags call site flags
|
||||
* @param isMethod should it prefer retrieving methods
|
||||
*
|
||||
* @param isIndex is this an index operation?
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
|
||||
MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod, final boolean isIndex) {
|
||||
if (name.length() > LARGE_STRING_THRESHOLD) { // use getIndex for extremely long names
|
||||
return load(name).dynamicGetIndex(valueType, flags, isMethod);
|
||||
}
|
||||
@ -2229,8 +2229,8 @@ public class MethodEmitter implements Emitter {
|
||||
}
|
||||
|
||||
popType(Type.SCOPE);
|
||||
method.visitInvokeDynamicInsn((isMethod ? "dyn:getMethod|getProp|getElem:" : "dyn:getProp|getElem|getMethod:") +
|
||||
NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
|
||||
method.visitInvokeDynamicInsn(dynGetOperation(isMethod, isIndex) + ':' + NameCodec.encode(name),
|
||||
Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
|
||||
|
||||
pushType(type);
|
||||
convert(valueType); //most probably a nop
|
||||
@ -2243,8 +2243,9 @@ public class MethodEmitter implements Emitter {
|
||||
*
|
||||
* @param name name of property
|
||||
* @param flags call site flags
|
||||
* @param isIndex is this an index operation?
|
||||
*/
|
||||
void dynamicSet(final String name, final int flags) {
|
||||
void dynamicSet(final String name, final int flags, final boolean isIndex) {
|
||||
if (name.length() > LARGE_STRING_THRESHOLD) { // use setIndex for extremely long names
|
||||
load(name).swap().dynamicSetIndex(flags);
|
||||
return;
|
||||
@ -2261,7 +2262,8 @@ public class MethodEmitter implements Emitter {
|
||||
popType(type);
|
||||
popType(Type.SCOPE);
|
||||
|
||||
method.visitInvokeDynamicInsn("dyn:setProp|setElem:" + NameCodec.encode(name), methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
|
||||
method.visitInvokeDynamicInsn(dynSetOperation(isIndex) + ':' + NameCodec.encode(name),
|
||||
methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2294,7 +2296,7 @@ public class MethodEmitter implements Emitter {
|
||||
|
||||
final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
|
||||
|
||||
method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod", signature, LINKERBOOTSTRAP, flags);
|
||||
method.visitInvokeDynamicInsn(dynGetOperation(isMethod, true), signature, LINKERBOOTSTRAP, flags);
|
||||
pushType(resultType);
|
||||
|
||||
if (result.isBoolean()) {
|
||||
@ -2508,6 +2510,18 @@ public class MethodEmitter implements Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
private static String dynGetOperation(final boolean isMethod, final boolean isIndex) {
|
||||
if (isMethod) {
|
||||
return isIndex ? "dyn:getMethod|getElem|getProp" : "dyn:getMethod|getProp|getElem";
|
||||
} else {
|
||||
return isIndex ? "dyn:getElem|getProp|getMethod" : "dyn:getProp|getElem|getMethod";
|
||||
}
|
||||
}
|
||||
|
||||
private static String dynSetOperation(final boolean isIndex) {
|
||||
return isIndex ? "dyn:setElem|setProp" : "dyn:setProp|setElem";
|
||||
}
|
||||
|
||||
private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) {
|
||||
final Type from = conversion.getFrom();
|
||||
final Type to = conversion.getTo();
|
||||
|
||||
@ -30,13 +30,18 @@ import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessController;
|
||||
import java.security.MessageDigest;
|
||||
@ -49,6 +54,7 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Predicate;
|
||||
@ -388,6 +394,8 @@ public final class OptimisticTypesPersistence {
|
||||
final File dir = new File(dirStr);
|
||||
return "dev-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(getLastModifiedClassFile(
|
||||
dir, 0L)));
|
||||
} else if(protocol.equals("jrt")) {
|
||||
return getJrtVersionDirName();
|
||||
} else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
@ -546,4 +554,40 @@ public final class OptimisticTypesPersistence {
|
||||
}
|
||||
return Math.max(0, Integer.parseInt(str));
|
||||
}
|
||||
|
||||
// version directory name if nashorn is loaded from jrt:/ URL
|
||||
private static String getJrtVersionDirName() throws Exception {
|
||||
final FileSystem fs = getJrtFileSystem();
|
||||
// consider all .class resources under nashorn module to compute checksum
|
||||
final Path nashorn = fs.getPath("/jdk.scripting.nashorn");
|
||||
if (! Files.isDirectory(nashorn)) {
|
||||
throw new FileNotFoundException("missing /jdk.scripting.nashorn dir in jrt fs");
|
||||
}
|
||||
final MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
Files.walk(nashorn).forEach(new Consumer<Path>() {
|
||||
@Override
|
||||
public void accept(Path p) {
|
||||
// take only the .class resources.
|
||||
if (Files.isRegularFile(p) && p.toString().endsWith(".class")) {
|
||||
try {
|
||||
digest.update(Files.readAllBytes(p));
|
||||
} catch (final IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest.digest());
|
||||
}
|
||||
|
||||
// get the default jrt FileSystem instance
|
||||
private static FileSystem getJrtFileSystem() {
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedAction<FileSystem>() {
|
||||
@Override
|
||||
public FileSystem run() {
|
||||
return FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ class SharedScopeCall {
|
||||
assert !isCall || valueType.isObject(); // Callables are always objects
|
||||
// If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously
|
||||
// only apply to the call.
|
||||
method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall);
|
||||
method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall, false);
|
||||
|
||||
// If this is a get we're done, otherwise call the value as function.
|
||||
if (isCall) {
|
||||
|
||||
@ -28,6 +28,8 @@ package jdk.nashorn.internal.ir;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
|
||||
/**
|
||||
* IR representation of a property access (period operator.)
|
||||
@ -101,6 +103,14 @@ public final class AccessNode extends BaseNode {
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this node represents an index operation normally represented as {@link IndexNode}.
|
||||
* @return true if an index access.
|
||||
*/
|
||||
public boolean isIndex() {
|
||||
return Token.descType(getToken()) == TokenType.LBRACKET;
|
||||
}
|
||||
|
||||
private AccessNode setBase(final Expression base) {
|
||||
if (this.base == base) {
|
||||
return this;
|
||||
|
||||
@ -341,10 +341,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
@Override
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterBinaryNode(this)) {
|
||||
if(tokenType().isLeftAssociative()) {
|
||||
return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
|
||||
}
|
||||
return visitor.leaveBinaryNode(setRHS((Expression)rhs.accept(visitor)).setLHS((Expression)lhs.accept(visitor)));
|
||||
return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -468,11 +467,7 @@ public class RuntimeNode extends Expression implements Optimistic {
|
||||
@Override
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterRuntimeNode(this)) {
|
||||
final List<Expression> newArgs = new ArrayList<>();
|
||||
for (final Node arg : args) {
|
||||
newArgs.add((Expression)arg.accept(visitor));
|
||||
}
|
||||
return visitor.leaveRuntimeNode(setArgs(newArgs));
|
||||
return visitor.leaveRuntimeNode(setArgs(Node.accept(visitor, args)));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
@ -48,6 +48,10 @@ public final class SwitchNode extends BreakableStatement {
|
||||
/** Switch default index. */
|
||||
private final int defaultCaseIndex;
|
||||
|
||||
/** True if all cases are 32-bit signed integer constants, without repetitions. It's a prerequisite for
|
||||
* using a tableswitch/lookupswitch when generating code. */
|
||||
private final boolean uniqueInteger;
|
||||
|
||||
/** Tag symbol. */
|
||||
private Symbol tag;
|
||||
|
||||
@ -66,15 +70,17 @@ public final class SwitchNode extends BreakableStatement {
|
||||
this.expression = expression;
|
||||
this.cases = cases;
|
||||
this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
|
||||
this.uniqueInteger = false;
|
||||
}
|
||||
|
||||
private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases,
|
||||
final int defaultCaseIndex, final LocalVariableConversion conversion) {
|
||||
final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger) {
|
||||
super(switchNode, conversion);
|
||||
this.expression = expression;
|
||||
this.cases = cases;
|
||||
this.defaultCaseIndex = defaultCaseIndex;
|
||||
this.tag = switchNode.getTag(); //TODO are symbols inhereted as references?
|
||||
this.tag = switchNode.getTag(); //TODO are symbols inherited as references?
|
||||
this.uniqueInteger = uniqueInteger;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -83,7 +89,7 @@ public final class SwitchNode extends BreakableStatement {
|
||||
for (final CaseNode caseNode : cases) {
|
||||
newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion()));
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion));
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -151,7 +157,7 @@ public final class SwitchNode extends BreakableStatement {
|
||||
if (this.cases == cases) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,7 +189,7 @@ public final class SwitchNode extends BreakableStatement {
|
||||
if (this.expression == expression) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,25 +211,30 @@ public final class SwitchNode extends BreakableStatement {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all cases of this switch statement are 32-bit signed integer constants.
|
||||
* @return true if all cases of this switch statement are 32-bit signed integer constants.
|
||||
* Returns true if all cases of this switch statement are 32-bit signed integer constants, without repetitions.
|
||||
* @return true if all cases of this switch statement are 32-bit signed integer constants, without repetitions.
|
||||
*/
|
||||
public boolean isInteger() {
|
||||
for (final CaseNode caseNode : cases) {
|
||||
final Expression test = caseNode.getTest();
|
||||
if (test != null && !isIntegerLiteral(test)) {
|
||||
return false;
|
||||
}
|
||||
public boolean isUniqueInteger() {
|
||||
return uniqueInteger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether all cases of this switch statement are 32-bit signed integer constants, without repetitions.
|
||||
* @param lc lexical context
|
||||
* @param uniqueInteger if true, all cases of this switch statement have been determined to be 32-bit signed
|
||||
* integer constants, without repetitions.
|
||||
* @return this switch node, if the value didn't change, or a new switch node with the changed value
|
||||
*/
|
||||
public SwitchNode setUniqueInteger(final LexicalContext lc, final boolean uniqueInteger) {
|
||||
if(this.uniqueInteger == uniqueInteger) {
|
||||
return this;
|
||||
}
|
||||
return true;
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
|
||||
}
|
||||
|
||||
@Override
|
||||
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
|
||||
}
|
||||
|
||||
private static boolean isIntegerLiteral(final Expression expr) {
|
||||
return expr instanceof LiteralNode && ((LiteralNode<?>)expr).getValue() instanceof Integer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ import static jdk.nashorn.internal.parser.TokenType.RPAREN;
|
||||
import static jdk.nashorn.internal.parser.TokenType.SEMICOLON;
|
||||
import static jdk.nashorn.internal.parser.TokenType.TERNARY;
|
||||
import static jdk.nashorn.internal.parser.TokenType.WHILE;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
@ -2659,7 +2660,7 @@ loop:
|
||||
// name is null, generate anonymous name
|
||||
boolean isAnonymous = false;
|
||||
if (name == null) {
|
||||
final String tmpName = getDefaultValidFunctionName(functionLine);
|
||||
final String tmpName = getDefaultValidFunctionName(functionLine, isStatement);
|
||||
name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName);
|
||||
isAnonymous = true;
|
||||
}
|
||||
@ -2671,9 +2672,13 @@ loop:
|
||||
final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.NORMAL, functionLine, parameters);
|
||||
lc.push(functionNode);
|
||||
Block functionBody = null;
|
||||
// Hide the current default name across function boundaries. E.g. "x3 = function x1() { function() {}}"
|
||||
// If we didn't hide the current default name, then the innermost anonymous function would receive "x3".
|
||||
hideDefaultName();
|
||||
try{
|
||||
functionBody = functionBody(functionNode);
|
||||
} finally {
|
||||
defaultNames.pop();
|
||||
lc.pop(functionNode);
|
||||
}
|
||||
|
||||
@ -2754,9 +2759,17 @@ loop:
|
||||
return function;
|
||||
}
|
||||
|
||||
private String getDefaultValidFunctionName(final int functionLine) {
|
||||
private String getDefaultValidFunctionName(final int functionLine, final boolean isStatement) {
|
||||
final String defaultFunctionName = getDefaultFunctionName();
|
||||
return isValidIdentifier(defaultFunctionName) ? defaultFunctionName : ANON_FUNCTION_PREFIX.symbolName() + functionLine;
|
||||
if (isValidIdentifier(defaultFunctionName)) {
|
||||
if (isStatement) {
|
||||
// The name will be used as the LHS of a symbol assignment. We add the anonymous function
|
||||
// prefix to ensure that it can't clash with another variable.
|
||||
return ANON_FUNCTION_PREFIX.symbolName() + defaultFunctionName;
|
||||
}
|
||||
return defaultFunctionName;
|
||||
}
|
||||
return ANON_FUNCTION_PREFIX.symbolName() + functionLine;
|
||||
}
|
||||
|
||||
private static boolean isValidIdentifier(final String name) {
|
||||
@ -2790,6 +2803,10 @@ loop:
|
||||
|
||||
private void markDefaultNameUsed() {
|
||||
defaultNames.pop();
|
||||
hideDefaultName();
|
||||
}
|
||||
|
||||
private void hideDefaultName() {
|
||||
// Can be any value as long as getDefaultFunctionName doesn't recognize it as something it can extract a value
|
||||
// from. Can't be null
|
||||
defaultNames.push("");
|
||||
|
||||
@ -2001,12 +2001,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
|
||||
|
||||
if (find == null) {
|
||||
switch (operator) {
|
||||
case "getElem": // getElem only gets here if element name is constant, so treat it like a property access
|
||||
case "getProp":
|
||||
return noSuchProperty(desc, request);
|
||||
case "getMethod":
|
||||
return noSuchMethod(desc, request);
|
||||
case "getElem":
|
||||
return createEmptyGetter(desc, explicitInstanceOfCheck, name);
|
||||
default:
|
||||
throw new AssertionError(operator); // never invoked with any other operation
|
||||
}
|
||||
@ -2333,8 +2332,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
|
||||
}
|
||||
|
||||
final ScriptFunction func = (ScriptFunction)value;
|
||||
final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
|
||||
final Object thiz = scopeCall && func.isStrict() ? UNDEFINED : this;
|
||||
// TODO: It'd be awesome if we could bind "name" without binding "this".
|
||||
// Since we're binding this we must use an identity guard here.
|
||||
return new GuardedInvocation(
|
||||
MH.dropArguments(
|
||||
MH.constant(
|
||||
@ -2342,9 +2342,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
|
||||
func.makeBoundFunction(thiz, new Object[] { name })),
|
||||
0,
|
||||
Object.class),
|
||||
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
|
||||
(SwitchPoint)null,
|
||||
explicitInstanceOfCheck ? null : ClassCastException.class);
|
||||
NashornGuards.combineGuards(
|
||||
NashornGuards.getIdentityGuard(this),
|
||||
NashornGuards.getMapGuard(getMap(), true)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3710,7 +3710,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
|
||||
final ScriptObject clone = (ScriptObject) super.clone();
|
||||
if (objectSpill != null) {
|
||||
clone.objectSpill = objectSpill.clone();
|
||||
clone.primitiveSpill = primitiveSpill.clone();
|
||||
if (primitiveSpill != null) {
|
||||
clone.primitiveSpill = primitiveSpill.clone();
|
||||
}
|
||||
}
|
||||
clone.arrayData = arrayData.copy();
|
||||
return clone;
|
||||
|
||||
@ -29,6 +29,7 @@ import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObject
|
||||
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;
|
||||
@ -131,6 +132,8 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
|
||||
case "setProp":
|
||||
case "setElem":
|
||||
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
|
||||
case "call":
|
||||
return findCallMethod(desc);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -156,6 +159,11 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
|
||||
return new GuardedInvocation(JSOBJECTLINKER_PUT, IS_JSOBJECT_GUARD);
|
||||
}
|
||||
|
||||
private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) {
|
||||
final MethodHandle call = MH.insertArguments(JSOBJECT_CALL, 1, "call");
|
||||
return new GuardedInvocation(MH.asCollector(call, Object[].class, desc.getMethodType().parameterCount() - 1), IS_JSOBJECT_GUARD);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isJSObject(final Object self) {
|
||||
return jsObjectClass.isInstance(self);
|
||||
@ -215,6 +223,7 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
|
||||
static final MethodHandle JSOBJECT_GETSLOT = findJSObjectMH_V("getSlot", Object.class, int.class).asType(MH.type(Object.class, Object.class, int.class));
|
||||
static final MethodHandle JSOBJECT_SETMEMBER = findJSObjectMH_V("setMember", Void.TYPE, String.class, Object.class).asType(MH.type(Void.TYPE, Object.class, String.class, Object.class));
|
||||
static final MethodHandle JSOBJECT_SETSLOT = findJSObjectMH_V("setSlot", Void.TYPE, int.class, Object.class).asType(MH.type(Void.TYPE, Object.class, int.class, Object.class));
|
||||
static final MethodHandle JSOBJECT_CALL = findJSObjectMH_V("call", Object.class, String.class, Object[].class).asType(MH.type(Object.class, Object.class, String.class, Object[].class));
|
||||
|
||||
private static MethodHandle findJSObjectMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
checkJSObjectClass();
|
||||
|
||||
@ -33,7 +33,22 @@
|
||||
|
||||
var NullProvider = Java.type("jdk.nashorn.test.models.NullProvider");
|
||||
|
||||
if (!NullProvider.getBoolean()) { print("yay"); }
|
||||
print(NullProvider.getLong() * (1 << 33));
|
||||
print(NullProvider.getDouble() / 2.5);
|
||||
print(NullProvider.getInteger() << 1);
|
||||
try {
|
||||
if (!NullProvider.getBoolean()) { print("yay"); }
|
||||
print(NullProvider.getLong() * (1 << 33));
|
||||
print(NullProvider.getDouble() / 2.5);
|
||||
print(NullProvider.getInteger() << 1);
|
||||
} catch (e if e instanceof java.lang.NullPointerException) {
|
||||
var st = e.stackTrace;
|
||||
if (st.length > 0 &&
|
||||
st[0].className.equals("sun.invoke.util.ValueConversions")) {
|
||||
// buggy JVM. ignore NPE and pass vacuously
|
||||
// print to match .EXPECTED output
|
||||
print("yay");
|
||||
print(0);
|
||||
print(0);
|
||||
print(0);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
31
nashorn/test/script/basic/JDK-8066221.js
Normal file
31
nashorn/test/script/basic/JDK-8066221.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066221: anonymous function statement name clashes with another symbol
|
||||
* (compile-only test)
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
|
||||
x3 = function x1(x3) { function (){} };
|
||||
38
nashorn/test/script/basic/JDK-8066224.js
Normal file
38
nashorn/test/script/basic/JDK-8066224.js
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066224: fixes for folding a constant-test ternary operator
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
print((function(){
|
||||
if(false ? 0 : '') {
|
||||
throw false;
|
||||
} else if (x = this) {
|
||||
var x = x;
|
||||
}
|
||||
return x === this;
|
||||
})())
|
||||
1
nashorn/test/script/basic/JDK-8066224.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8066224.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
true
|
||||
36
nashorn/test/script/basic/JDK-8066225.js
Normal file
36
nashorn/test/script/basic/JDK-8066225.js
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066225: NPE in MethodEmitter with duplicate integer switch cases
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
(function (x){
|
||||
switch(x) {
|
||||
case 44: for (var x in {}) {x}; print("1");
|
||||
case 44: print("2");
|
||||
}
|
||||
})(44);
|
||||
2
nashorn/test/script/basic/JDK-8066225.js.EXPECTED
Normal file
2
nashorn/test/script/basic/JDK-8066225.js.EXPECTED
Normal file
@ -0,0 +1,2 @@
|
||||
1
|
||||
2
|
||||
40
nashorn/test/script/basic/JDK-8066227.js
Normal file
40
nashorn/test/script/basic/JDK-8066227.js
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066227: CodeGenerator load unitialized slot
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
print((function () { var x; (x += x = 0); return x; })());
|
||||
print((function () { var x; (x -= x = 0); return x; })());
|
||||
print((function () { var x; (x *= x = 0); return x; })());
|
||||
print((function () { var x; (x /= x = 0); return x; })());
|
||||
print((function () { var x; (x %= x = 0); return x; })());
|
||||
print((function () { var x; (x <<= x = 0); return x; })());
|
||||
print((function () { var x; (x >>= x = 0); return x; })());
|
||||
print((function () { var x; (x >>>= x = 0); return x; })());
|
||||
print((function () { var x; (x |= x = 0); return x; })());
|
||||
print((function () { var x; (x &= x = 0); return x; })());
|
||||
10
nashorn/test/script/basic/JDK-8066227.js.EXPECTED
Normal file
10
nashorn/test/script/basic/JDK-8066227.js.EXPECTED
Normal file
@ -0,0 +1,10 @@
|
||||
NaN
|
||||
NaN
|
||||
NaN
|
||||
NaN
|
||||
NaN
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
34
nashorn/test/script/basic/JDK-8066230.js
Normal file
34
nashorn/test/script/basic/JDK-8066230.js
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066230: Undefined object type assertion when computing TypeBounds
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
(function() { void null + 0; })();
|
||||
(function() { var x; x += void x; })();
|
||||
(function() { var a = true + x, x; })();
|
||||
print("SUCCESS");
|
||||
1
nashorn/test/script/basic/JDK-8066230.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8066230.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
SUCCESS
|
||||
46
nashorn/test/script/basic/JDK-8066236.js
Normal file
46
nashorn/test/script/basic/JDK-8066236.js
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066236: RuntimeNode forces copy creation on visitation
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
// Note: we're using Function("code") instead of (function(){ code }) so that
|
||||
// we don't trigger parser API validation in JDK-8008448 tests. The test code
|
||||
// encapsulated in functions below can't be correctly handled by the parser API
|
||||
// currently, as it contains parser-generated REFERENCE_ERROR runtime nodes.
|
||||
try {
|
||||
Function("L: {this = x;break L}")();
|
||||
} catch (e) {
|
||||
print("threw ReferenceError: " + (e instanceof ReferenceError));
|
||||
}
|
||||
try {
|
||||
Function("L:with(this--)break L;")();
|
||||
} catch (e) {
|
||||
print("threw ReferenceError: " + (e instanceof ReferenceError));
|
||||
}
|
||||
Function("L:with(Object in Object)break L;")();
|
||||
print("SUCCESS");
|
||||
3
nashorn/test/script/basic/JDK-8066236.js.EXPECTED
Normal file
3
nashorn/test/script/basic/JDK-8066236.js.EXPECTED
Normal file
@ -0,0 +1,3 @@
|
||||
threw ReferenceError: true
|
||||
threw ReferenceError: true
|
||||
SUCCESS
|
||||
58
nashorn/test/script/basic/JDK-8066669.js
Normal file
58
nashorn/test/script/basic/JDK-8066669.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066669: dust.js performance regression caused by primitive field conversion
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
// Make sure index access on Java objects is working as expected.
|
||||
var map = new java.util.HashMap();
|
||||
|
||||
map["foo"] = "bar";
|
||||
map[1] = 2;
|
||||
map[false] = true;
|
||||
map[null] = 0;
|
||||
|
||||
print(map);
|
||||
|
||||
var keys = map.keySet().iterator();
|
||||
|
||||
while(keys.hasNext()) {
|
||||
var key = keys.next();
|
||||
print(typeof key, key);
|
||||
}
|
||||
|
||||
print(typeof map["foo"], map["foo"]);
|
||||
print(typeof map[1], map[1]);
|
||||
print(typeof map[false], map[false]);
|
||||
print(typeof map[null], map[null]);
|
||||
|
||||
print(map.foo);
|
||||
print(map.false);
|
||||
print(map.null);
|
||||
|
||||
map.foo = "baz";
|
||||
print(map);
|
||||
13
nashorn/test/script/basic/JDK-8066669.js.EXPECTED
Normal file
13
nashorn/test/script/basic/JDK-8066669.js.EXPECTED
Normal file
@ -0,0 +1,13 @@
|
||||
{null=0, 1=2, false=true, foo=bar}
|
||||
object null
|
||||
number 1
|
||||
boolean false
|
||||
string foo
|
||||
string bar
|
||||
number 2
|
||||
boolean true
|
||||
number 0
|
||||
bar
|
||||
null
|
||||
null
|
||||
{null=0, 1=2, false=true, foo=baz}
|
||||
48
nashorn/test/script/basic/JDK-8066932.js
Normal file
48
nashorn/test/script/basic/JDK-8066932.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8066932: __noSuchMethod__ binds to this-object without proper guard
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
function C(id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
C.prototype.__noSuchMethod__ = function(name, args) {
|
||||
return this.id;
|
||||
};
|
||||
|
||||
function test(id) {
|
||||
var c = new C(id);
|
||||
return c.nonExistingMethod();
|
||||
}
|
||||
|
||||
for (var i = 0; i < 30; i++) {
|
||||
if (test(i) !== i) {
|
||||
throw new Error("Wrong result from noSuchMethod in iteration " + i);
|
||||
}
|
||||
}
|
||||
69
nashorn/test/script/basic/JDK-8067136.js
Normal file
69
nashorn/test/script/basic/JDK-8067136.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8067136: BrowserJSObjectLinker does not handle call on JSObjects
|
||||
*
|
||||
* @test
|
||||
* @option -scripting
|
||||
* @run
|
||||
*/
|
||||
|
||||
// call on netscape.javascript.JSObject
|
||||
|
||||
function main() {
|
||||
var JSObject;
|
||||
try {
|
||||
JSObject = Java.type("netscape.javascript.JSObject");
|
||||
} catch (e) {
|
||||
if (e instanceof java.lang.ClassNotFoundException) {
|
||||
// pass vacuously by emitting the .EXPECTED file content
|
||||
var str = readFully(__DIR__ + "JDK-8067136.js.EXPECTED");
|
||||
print(str.substring(0, str.length - 1));
|
||||
return;
|
||||
} else{
|
||||
fail("unexpected exception for JSObject", e);
|
||||
}
|
||||
}
|
||||
test(JSObject);
|
||||
}
|
||||
|
||||
function test(JSObject) {
|
||||
var obj = new (Java.extend(JSObject))() {
|
||||
getMember: function(name) {
|
||||
if (name == "func") {
|
||||
return new (Java.extend(JSObject)) {
|
||||
call: function(n) {
|
||||
print("func called");
|
||||
}
|
||||
}
|
||||
}
|
||||
return name.toUpperCase();
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
obj.func();
|
||||
}
|
||||
|
||||
main();
|
||||
1
nashorn/test/script/basic/JDK-8067136.js.EXPECTED
Normal file
1
nashorn/test/script/basic/JDK-8067136.js.EXPECTED
Normal file
@ -0,0 +1 @@
|
||||
func called
|
||||
81
nashorn/test/script/basic/es6/for-let-object-fields.js
Normal file
81
nashorn/test/script/basic/es6/for-let-object-fields.js
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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-8067219: NPE in ScriptObject.clone() when running with object fields
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @fork
|
||||
* @option -Dnashorn.fields.objects=true
|
||||
* @option --language=es6
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
print(i);
|
||||
}
|
||||
|
||||
try {
|
||||
print(i);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
let a = [];
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
a.push(function() { print(i); });
|
||||
}
|
||||
|
||||
a.forEach(function(f) { f(); });
|
||||
|
||||
a = [];
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (i == 5) {
|
||||
i = "foo";
|
||||
}
|
||||
a.push(function() { print(i); });
|
||||
}
|
||||
|
||||
a.forEach(function(f) { f(); });
|
||||
|
||||
try {
|
||||
print(i);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
a = [];
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
if (i % 2 == 1) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
a.push(function() { print(i); });
|
||||
}
|
||||
|
||||
a.forEach(function(f) { f(); });
|
||||
@ -0,0 +1,33 @@
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
ReferenceError: "i" is not defined
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
foo
|
||||
ReferenceError: "i" is not defined
|
||||
0
|
||||
4
|
||||
8
|
||||
12
|
||||
16
|
||||
@ -26,7 +26,8 @@
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @option --language=es6 */
|
||||
* @option --language=es6
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ bar
|
||||
l[0]=foo
|
||||
l[1]=a
|
||||
l[0.9]=null
|
||||
l['blah']=null
|
||||
l['blah']=undefined
|
||||
l[size_name]()=2
|
||||
java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
|
||||
java.lang.IndexOutOfBoundsException: Index: Infinity, Size: 2
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user