This commit is contained in:
Lana Steuck 2015-03-19 16:13:54 -07:00
commit 1dae45d60e
20 changed files with 1867 additions and 911 deletions

@ -52,7 +52,6 @@ var Parser = Java.type("jdk.nashorn.api.tree.Parser");
var SimpleTreeVisitor = Java.type("jdk.nashorn.api.tree.SimpleTreeVisitorES5_1");
var parser = Parser.create("-scripting", "--const-as-var");
var protoFound = false;
function checkFile(file) {
// print("checking " + file);
@ -92,7 +91,3 @@ if (file.isDirectory()) {
} else {
checkFile(file);
}
if (protoFound) {
print("__proto__ is non-standard. Use Object.get/setPrototypeOf instead");
}

@ -0,0 +1,642 @@
/*
* 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.
*/
/**
* This script is a AST pretty printer for ECMAScript. It uses
* Nashorn parser API to parser given script and uses tree visitor
* to pretty print the AST to stdout as a script string.
*/
var File = Java.type("java.io.File");
var file = arguments.length == 0? new File(__FILE__) : new File(arguments[0]);
if (! file.isFile()) {
print(arguments[0] + " is not a file");
exit(1);
}
// Java classes used
var ArrayAccess = Java.type("jdk.nashorn.api.tree.ArrayAccessTree");
var Block = Java.type("jdk.nashorn.api.tree.BlockTree");
var FunctionDeclaration = Java.type("jdk.nashorn.api.tree.FunctionDeclarationTree");
var FunctionExpression = Java.type("jdk.nashorn.api.tree.FunctionExpressionTree");
var Identifier = Java.type("jdk.nashorn.api.tree.IdentifierTree");
var Kind = Java.type("jdk.nashorn.api.tree.Tree.Kind");
var MemberSelect = Java.type("jdk.nashorn.api.tree.MemberSelectTree");
var ObjectLiteral = Java.type("jdk.nashorn.api.tree.ObjectLiteralTree");
var Parser = Java.type("jdk.nashorn.api.tree.Parser");
var SimpleTreeVisitor = Java.type("jdk.nashorn.api.tree.SimpleTreeVisitorES5_1");
var System = Java.type("java.lang.System");
// make a nashorn parser
var parser = Parser.create("-scripting", "--const-as-var");
// symbols for nashorn operators
var operatorSymbols = {
POSTFIX_INCREMENT: "++",
POSTFIX_DECREMENT: "--",
PREFIX_INCREMENT: "++",
PREFIX_DECREMENT: "--",
UNARY_PLUS: "+",
UNARY_MINUS: "-",
BITWISE_COMPLEMENT: "~",
LOGICAL_COMPLEMENT: "!",
DELETE: "delete ",
TYPEOF: "typeof ",
VOID: "void ",
COMMA: ",",
MULTIPLY: "*",
DIVIDE: "/",
REMINDER: "%",
PLUS: "+",
MINUS: "-",
LEFT_SHIFT: "<<",
RIGHT_SHIFT: ">>",
UNSIGNED_RIGHT_SHIFT: ">>>",
LESS_THAN: "<",
GREATER_THAN: ">",
LESS_THAN_EQUAL: "<=",
GREATER_THAN_EQUAL: ">=",
IN: "in",
EQUAL_TO: "==",
NOT_EQUAL_TO: "!=",
STRICT_EQUAL_TO: "===",
STRICT_NOT_EQUAL_TO: "!==",
AND: "&",
XOR: "^",
OR: "|",
CONDITIONAL_AND: "&&",
CONDITIONAL_OR: "||",
MULTIPLY_ASSIGNMENT: "*=",
DIVIDE_ASSIGNMENT: "/=",
REMINDER_ASSIGNMENT: "%=",
PLUS_ASSIGNMENT: "+=",
MINUS_ASSIGNMENT: "-=",
LEFT_SHIFT_ASSIGNMENT: "<<=",
RIGHT_SHIFT_ASSIGNMENT: ">>=",
UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: ">>>=",
AND_ASSIGNMENT: "&=",
XOR_ASSIGNMENT: "^=",
OR_ASSIGNMENT: "|="
};
function operatorOf(kind) {
var name = kind.name();
if (name in operatorSymbols) {
return operatorSymbols[name];
}
throw "invalid operator: " + name;
}
var gprint = print;
function prettyPrint(file) {
var ast = parser.parse(file, gprint);
if (!ast) {
// failed to parse. don't print anything!
return;
}
// AST visitor
var visitor;
// current indent level
var indentLevel = 0;
var out = System.out;
function print(obj) {
out.print(String(obj));
}
function println(obj) {
obj? out.println(String(obj)) : out.println();
}
// semicolon and end-of-line
function eol() {
println(";");
}
// print indentation - 4 spaces per level
function indent() {
for (var i = 0; i < indentLevel; i++) {
// 4 spaces per indent level
print(" ");
}
}
// escape string literals
function escapeString(str) {
// FIXME: incomplete, revisit again!
return str.replace(/[\\"']/g, '\\$&')
}
// print a single statement (could be a block too)
function printStatement(stat, extra, end) {
if (stat instanceof Block) {
println(" {");
printStatements(stat.statements, extra);
indent();
print('}');
typeof end != "undefined"? print(end) : println();
} else {
println();
indentLevel++;
try {
stat.accept(visitor, extra);
} finally {
indentLevel--;
}
}
}
// print a statement list
function printStatements(stats, extra) {
indentLevel++;
try {
for each (var stat in stats) {
stat.accept(visitor, extra);
}
} finally {
indentLevel--;
}
}
// function arguments, array literal elements.
function printCommaList(args, extra) {
var len = args.length;
for (var i = 0; i < len; i++) {
args[i].accept(visitor, extra);
if (i != len - 1) {
print(", ");
}
}
}
// print function declarations and expressions
function printFunction(func, extra, end) {
// extra lines around function declarations for clarity
var funcDecl = (func instanceof FunctionDeclaration);
if (funcDecl) {
println();
indent();
}
print("function ");
if (func.name) {
print(func.name);
}
printFunctionBody(func, extra, end);
if (funcDecl) {
println();
}
}
// print function declaration/expression body
function printFunctionBody(func, extra, end) {
print('(');
var params = func.parameters;
if (params) {
printCommaList(params);
}
print(')');
printStatement(func.body, extra, end);
}
// print object literal property
function printProperty(node, extra, comma) {
var key = node.key;
var val = node.value;
var getter = node.getter;
var setter = node.setter;
if (getter) {
print("get ");
} else if (setter) {
print("set ");
}
if (typeof key == "string") {
print(key);
} else {
key.accept(visitor, extra);
}
if (val) {
print(": ");
if (val instanceof FunctionExpression) {
printFunction(val, extra, comma? ',' : undefined);
} else {
val.accept(visitor, extra);
if (comma) print(',');
}
} else if (getter) {
printFunctionBody(getter, extra, comma? ',' : undefined);
} else if (setter) {
printFunctionBody(setter, extra, comma? ',' : undefined);
}
}
ast.accept(visitor = new (Java.extend(SimpleTreeVisitor)) {
visitAssignment: function(node, extra) {
node.variable.accept(visitor, extra);
print(" = ");
node.expression.accept(visitor, extra);
},
visitCompoundAssignment: function(node, extra) {
node.variable.accept(visitor, extra);
print(' ' + operatorOf(node.kind) + ' ');
node.expression.accept(visitor, extra);
},
visitBinary: function(node, extra) {
node.leftOperand.accept(visitor, extra);
print(' ' + operatorOf(node.kind) + ' ');
node.rightOperand.accept(visitor, extra);
},
visitBlock: function(node, extra) {
indent();
println('{');
printStatements(node.statements, extra);
indent();
println('}');
},
visitBreak: function(node, extra) {
indent();
print("break");
if (node.label) {
print(' ' + node.label);
}
eol();
},
visitCase: function(node, extra) {
var expr = node.expression;
indent();
if (expr) {
print("case ");
expr.accept(visitor, extra);
println(':');
} else {
println("default:");
}
printStatements(node.statements, extra);
},
visitCatch: function(node, extra) {
indent();
print("catch (" + node.parameter.name);
var cond = node.condition;
if (cond) {
print(" if ");
cond.accept(visitor, extra);
}
print(')');
printStatement(node.block);
},
visitConditionalExpression: function(node, extra) {
print('(');
node.condition.accept(visitor, extra);
print(" ? ");
node.trueExpression.accept(visitor, extra);
print(" : ");
node.falseExpression.accept(visitor, extra);
print(')');
},
visitContinue: function(node, extra) {
indent();
print("continue");
if (node.label) {
print(' ' + node.label);
}
eol();
},
visitDebugger: function(node, extra) {
indent();
print("debugger");
eol();
},
visitDoWhileLoop: function(node, extra) {
indent();
print("do");
printStatement(node.statement, extra);
indent();
print("while (");
node.condition.accept(visitor, extra);
print(')');
eol();
},
visitExpressionStatement: function(node, extra) {
indent();
var expr = node.expression;
var objLiteral = expr instanceof ObjectLiteral;
if (objLiteral) {
print('(');
}
expr.accept(visitor, extra);
if (objLiteral) {
print(')');
}
eol();
},
visitForLoop: function(node, extra) {
indent();
print("for (");
if (node.initializer) {
node.initializer.accept(visitor, extra);
}
print(';');
if (node.condition) {
node.condition.accept(visitor, extra);
}
print(';');
if (node.update) {
node.update.accept(visitor, extra);
}
print(')');
printStatement(node.statement);
},
visitForInLoop: function(node, extra) {
indent();
print("for ");
if (node.forEach) {
print("each ");
}
print('(');
node.variable.accept(visitor, extra);
print(" in ");
node.expression.accept(visitor, extra);
print(')');
printStatement(node.statement);
},
visitFunctionCall: function(node, extra) {
var func = node.functionSelect;
// We need parens around function selected
// in many non-simple cases. Eg. function
// expression created and called immediately.
// Such parens are not preserved in AST and so
// introduce here.
var simpleFunc =
(func instanceof ArrayAccess) ||
(func instanceof Identifier) ||
(func instanceof MemberSelect);
if (! simpleFunc) {
print('(');
}
func.accept(visitor, extra);
if (! simpleFunc) {
print(')');
}
print('(');
printCommaList(node.arguments, extra);
print(')');
},
visitFunctionDeclaration: function(node, extra) {
printFunction(node, extra);
},
visitFunctionExpression: function(node, extra) {
printFunction(node, extra);
},
visitIdentifier: function(node, extra) {
print(node.name);
},
visitIf: function(node, extra) {
indent();
print("if (");
node.condition.accept(visitor, extra);
print(')');
printStatement(node.thenStatement);
var el = node.elseStatement;
if (el) {
indent();
print("else");
printStatement(el);
}
},
visitArrayAccess: function(node, extra) {
node.expression.accept(visitor, extra);
print('[');
node.index.accept(visitor, extra);
print(']');
},
visitArrayLiteral: function(node, extra) {
print('[');
printCommaList(node.elements);
print(']');
},
visitLabeledStatement: function(node, extra) {
indent();
print(node.label);
print(':');
printStatement(node.statement);
},
visitLiteral: function(node, extra) {
var val = node.value;
if (typeof val == "string") {
print("'" + escapeString(val) + "'");
} else {
print(val);
}
},
visitParenthesized: function(node, extra) {
print('(');
node.expression.accept(visitor, extra);
print(')');
},
visitReturn: function(node, extra) {
indent();
print("return");
if (node.expression) {
print(' ');
node.expression.accept(visitor, extra);
}
eol();
},
visitMemberSelect: function(node, extra) {
node.expression.accept(visitor, extra);
print('.' + node.identifier);
},
visitNew: function(node, extra) {
print("new ");
node.constructorExpression.accept(visitor, extra);
},
visitObjectLiteral: function(node, extra) {
println('{');
indentLevel++;
try {
var props = node.properties;
var len = props.length;
for (var p = 0; p < len; p++) {
var last = (p == len - 1);
indent();
printProperty(props[p], extra, !last);
println();
}
} finally {
indentLevel--;
}
indent();
print('}');
},
visitRegExpLiteral: function(node, extra) {
print('/' + node.pattern + '/');
print(node.options);
},
visitEmptyStatement: function(node, extra) {
indent();
eol();
},
visitSwitch: function(node, extra) {
indent();
print("switch (");
node.expression.accept(visitor, extra);
println(") {");
indentLevel++;
try {
for each (var c in node.cases) {
c.accept(visitor, extra);
}
} finally {
indentLevel--;
}
indent();
println('}');
},
visitThrow: function(node, extra) {
indent();
print("throw ");
node.expression.accept(visitor, extra);
eol();
},
visitCompilationUnit: function(node, extra) {
for each (var stat in node.sourceElements) {
stat.accept(visitor, extra);
}
},
visitTry: function(node, extra) {
indent();
print("try");
printStatement(node.block);
var catches = node.catches;
for each (var c in catches) {
c.accept(visitor, extra);
}
var finallyBlock = node.finallyBlock;
if (finallyBlock) {
indent();
print("finally");
printStatement(finallyBlock);
}
},
visitInstanceOf: function(node, extra) {
node.expression.accept(visitor, extra);
print(" instanceof ");
node.type.accept(visitor, extra);
},
visitUnary: function(node, extra) {
var kind = node.kind;
var prefix = kind != Kind.POSTFIX_INCREMENT && kind != Kind.POSTFIX_DECREMENT;
if (prefix) {
print(operatorOf(kind));
}
node.expression.accept(visitor, extra);
if (!prefix) {
print(operatorOf(kind));
}
},
visitVariable: function(node, extra) {
indent();
print("var " + node.name);
var init = node.initializer;
if (init) {
print(" = ");
if (init instanceof FunctionExpression) {
printFunction(init, extra, "");
} else {
init.accept(visitor, extra);
}
}
eol();
},
visitWhileLoop: function(node, extra) {
indent();
print("while (");
node.condition.accept(visitor, extra);
print(')');
printStatement(node.statement);
},
visitWith: function(node, extra) {
indent();
print("with (");
node.scope.accept(visitor, extra);
print(')');
printStatement(node.statement);
}
}, null);
}
prettyPrint(file);

@ -44,6 +44,7 @@ public interface DoWhileLoopTree extends ConditionalLoopTree {
*
* @return the condition expression
*/
@Override
ExpressionTree getCondition();
/**
@ -51,5 +52,6 @@ public interface DoWhileLoopTree extends ConditionalLoopTree {
*
* @return the statement
*/
@Override
StatementTree getStatement();
}

@ -57,6 +57,7 @@ public interface ForInLoopTree extends LoopTree {
*
* @return the statement
*/
@Override
StatementTree getStatement();
/**

@ -43,7 +43,7 @@ final class FunctionDeclarationTreeImpl extends StatementTreeImpl
assert node.getInit() instanceof FunctionNode : "function expected";
funcNode = (FunctionNode)node.getInit();
assert funcNode.isDeclared() : "function declaration expected";
funcName = node.getName().getName();
funcName = funcNode.isAnonymous()? null : node.getName().getName();
this.params = params;
this.body = body;
}

@ -25,6 +25,7 @@
package jdk.nashorn.api.tree;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
@ -92,7 +93,7 @@ final class IRTranslator extends NodeVisitor<LexicalContext> {
final Block body = node.getBody();
return new CompilationUnitTreeImpl(node,
translateStats(body != null? body.getStatements() : null));
translateStats(body != null? getOrderedStatements(body.getStatements()) : null));
}
@Override
@ -103,25 +104,7 @@ final class IRTranslator extends NodeVisitor<LexicalContext> {
@Override
public boolean enterBlock(final Block block) {
// FIXME: revisit this!
if (block.isSynthetic()) {
final int statCount = block.getStatementCount();
switch (statCount) {
case 0: {
final EmptyNode emptyNode = new EmptyNode(-1, block.getToken(), block.getFinish());
curStat = new EmptyStatementTreeImpl(emptyNode);
return false;
}
case 1: {
curStat = translateStat(block.getStatements().get(0));
return false;
}
}
}
curStat = new BlockTreeImpl(block,
translateStats(block.getStatements()));
return false;
return handleBlock(block, false);
}
@Override
@ -245,7 +228,7 @@ final class IRTranslator extends NodeVisitor<LexicalContext> {
final List<? extends ExpressionTree> paramTrees
= translateExprs(functionNode.getParameters());
final BlockTree blockTree = (BlockTree) translateBlock(functionNode.getBody());
final BlockTree blockTree = (BlockTree) translateBlock(functionNode.getBody(), true);
curExpr = new FunctionExpressionTreeImpl(functionNode, paramTrees, blockTree);
return false;
@ -420,7 +403,7 @@ final class IRTranslator extends NodeVisitor<LexicalContext> {
final List<? extends ExpressionTree> paramTrees
= translateExprs(funcNode.getParameters());
final BlockTree blockTree = (BlockTree) translateBlock(funcNode.getBody());
final BlockTree blockTree = (BlockTree) translateBlock(funcNode.getBody(), true);
curStat = new FunctionDeclarationTreeImpl(varNode, paramTrees, blockTree);
} else {
curStat = new VariableTreeImpl(varNode, translateExpr(initNode));
@ -453,14 +436,51 @@ final class IRTranslator extends NodeVisitor<LexicalContext> {
}
private StatementTree translateBlock(final Block blockNode) {
return translateBlock(blockNode, false);
}
private StatementTree translateBlock(final Block blockNode, final boolean sortStats) {
if (blockNode == null) {
return null;
}
curStat = null;
blockNode.accept(this);
handleBlock(blockNode, sortStats);
return curStat;
}
private boolean handleBlock(final Block block, final boolean sortStats) {
// FIXME: revisit this!
if (block.isSynthetic()) {
final int statCount = block.getStatementCount();
switch (statCount) {
case 0: {
final EmptyNode emptyNode = new EmptyNode(-1, block.getToken(), block.getFinish());
curStat = new EmptyStatementTreeImpl(emptyNode);
return false;
}
case 1: {
curStat = translateStat(block.getStatements().get(0));
return false;
}
default: {
// fall through
break;
}
}
}
final List<? extends Statement> stats = block.getStatements();
curStat = new BlockTreeImpl(block,
translateStats(sortStats? getOrderedStatements(stats) : stats));
return false;
}
private List<? extends Statement> getOrderedStatements(final List<? extends Statement> stats) {
final List<? extends Statement> statList = new ArrayList<>(stats);
statList.sort(Comparator.comparingInt(Node::getSourceOrder));
return statList;
}
private List<? extends StatementTree> translateStats(final List<? extends Statement> stats) {
if (stats == null) {
return null;
@ -511,7 +531,7 @@ final class IRTranslator extends NodeVisitor<LexicalContext> {
return curStat;
}
private IdentifierTree translateIdent(final IdentNode ident) {
private static IdentifierTree translateIdent(final IdentNode ident) {
return new IdentifierTreeImpl(ident);
}
}

@ -139,7 +139,7 @@ final class ParserImpl implements Parser {
}
}
private CompilationUnitTree translate(final FunctionNode node) {
private static CompilationUnitTree translate(final FunctionNode node) {
return new IRTranslator().translate(node);
}
}

@ -43,6 +43,7 @@ public interface WhileLoopTree extends ConditionalLoopTree {
*
* @return the condition expression
*/
@Override
ExpressionTree getCondition();
/**
@ -50,5 +51,6 @@ public interface WhileLoopTree extends ConditionalLoopTree {
*
* @return the statement contained
*/
@Override
StatementTree getStatement();
}

@ -183,6 +183,17 @@ public abstract class Node implements Cloneable, Serializable {
return start;
}
/**
* Integer to sort nodes in source order. This order is
* used by parser API to sort statements in correct order.
* By default, this is the start position of this node.
*
* @return int code to sort this node.
*/
public int getSourceOrder() {
return getStart();
}
@Override
protected Object clone() {
try {

@ -45,6 +45,13 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
/** Is this a var statement (as opposed to a "var" in a for loop statement) */
private final int flags;
/**
* source order id to be used for this node. If this is -1, then we
* the default which is start position of this node. See also the
* method Node::getSourceOrder.
*/
private final int sourceOrder;
/** Flag for ES6 LET declaration */
public static final int IS_LET = 1 << 0;
@ -71,6 +78,7 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
private VarNode(final VarNode varNode, final IdentNode name, final Expression init, final int flags) {
super(varNode);
this.sourceOrder = -1;
this.name = init == null ? name : name.setIsInitializedHere();
this.init = init;
this.flags = flags;
@ -79,21 +87,41 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
/**
* Constructor
*
* @param lineNumber line number
* @param token token
* @param finish finish
* @param name name of variable
* @param init init node or null if just a declaration
* @param flags flags
* @param lineNumber line number
* @param token token
* @param finish finish
* @param name name of variable
* @param init init node or null if just a declaration
* @param flags flags
*/
public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init, final int flags) {
super(lineNumber, token, finish);
this(lineNumber, token, -1, finish, name, init, flags);
}
/**
* Constructor
*
* @param lineNumber line number
* @param token token
* @param sourceOrder source order
* @param finish finish
* @param name name of variable
* @param init init node or null if just a declaration
* @param flags flags
*/
public VarNode(final int lineNumber, final long token, final int sourceOrder, final int finish, final IdentNode name, final Expression init, final int flags) {
super(lineNumber, token, finish);
this.sourceOrder = sourceOrder;
this.name = init == null ? name : name.setIsInitializedHere();
this.init = init;
this.flags = flags;
}
@Override
public int getSourceOrder() {
return sourceOrder == -1? super.getSourceOrder() : sourceOrder;
}
@Override
public boolean isAssignment() {
return hasInit();

@ -26,7 +26,9 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.nio.ByteBuffer;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
@ -101,7 +103,7 @@ public final class NativeArrayBuffer extends ScriptObject {
}
if (args.length == 0) {
throw new RuntimeException("missing length argument");
return new NativeArrayBuffer(0);
}
return new NativeArrayBuffer(JSType.toInt32(args[0]));

@ -1066,6 +1066,10 @@ loop:
* @param isStatement True if a statement (not used in a FOR.)
*/
private List<VarNode> variableStatement(final TokenType varType, final boolean isStatement) {
return variableStatement(varType, isStatement, -1);
}
private List<VarNode> variableStatement(final TokenType varType, final boolean isStatement, final int sourceOrder) {
// VAR tested in caller.
next();
@ -1104,7 +1108,7 @@ loop:
}
// Allocate var node.
final VarNode var = new VarNode(varLine, varToken, finish, name.setIsDeclaredHere(), init, varFlags);
final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, name.setIsDeclaredHere(), init, varFlags);
vars.add(var);
appendStatement(var);
@ -1211,6 +1215,10 @@ loop:
private void forStatement() {
final long forToken = token;
final int forLine = line;
// start position of this for statement. This is used
// for sort order for variables declared in the initialzer
// part of this 'for' statement (if any).
final int forStart = Token.descPosition(forToken);
// When ES6 for-let is enabled we create a container block to capture the LET.
final int startLine = start;
final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null;
@ -1243,7 +1251,7 @@ loop:
switch (type) {
case VAR:
// Var declaration captured in for outer block.
vars = variableStatement(type, false);
vars = variableStatement(type, false, forStart);
break;
case SEMICOLON:
break;
@ -1253,12 +1261,12 @@ loop:
flags |= ForNode.PER_ITERATION_SCOPE;
}
// LET/CONST declaration captured in container block created above.
vars = variableStatement(type, false);
vars = variableStatement(type, false, forStart);
break;
}
if (env._const_as_var && type == CONST) {
// Var declaration captured in for outer block.
vars = variableStatement(TokenType.VAR, false);
vars = variableStatement(TokenType.VAR, false, forStart);
break;
}

@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -595,7 +594,7 @@ final class CompiledFunction {
* switchpoint has been invalidated by a {@code RewriteException} triggered on another thread for this function.
* This is not a problem, though, as these switch points are always used to produce call sites that fall back to
* relinking when they are invalidated, and in this case the execution will end up here again. What this method
* basically does is reduce such busy-loop relinking while the function is being recompiled on a different thread.
* basically does is minimize such busy-loop relinking while the function is being recompiled on a different thread.
* @param invocationSupplier the supplier that constructs the actual invocation method handle; should use the
* {@code CompiledFunction} method itself in some capacity.
* @return a tuple object containing the method handle as created by the supplier and an optimistic assumptions
@ -603,27 +602,20 @@ final class CompiledFunction {
* function can't be further deoptimized).
*/
private synchronized HandleAndAssumptions getValidOptimisticInvocation(final Supplier<MethodHandle> invocationSupplier) {
for(int i = 0; i < 2; ++i) {
for(;;) {
final MethodHandle handle = invocationSupplier.get();
final SwitchPoint assumptions = canBeDeoptimized() ? optimismInfo.optimisticAssumptions : null;
if(i == 0 && assumptions != null && assumptions.hasBeenInvalidated()) {
if(assumptions != null && assumptions.hasBeenInvalidated()) {
// We can be in a situation where one thread is in the middle of a deoptimizing compilation when we hit
// this and thus, it has invalidated the old switch point, but hasn't created the new one yet. Note that
// the behavior of invalidating the old switch point before recompilation, and only creating the new one
// after recompilation is by design. If we didn't wait here, we would be busy looping through the
// fallback path of the invalidated switch point, relinking the call site again with the same
// invalidated switch point, invoking the fallback, etc. stealing CPU cycles from the recompilation
// task we're dependent on. This can still happen if the switch point gets invalidated after we grabbed
// it here, in which case we'll indeed do one busy relink immediately.
// On the other hand, in order to avoid a rare livelock, we aren't doing an infinite loop, and we
// aren't wait()-ing indefinitely. We'll do at most one, at most 1000ms long wait after which we'll
// return the current handle even if it's invalidated (and which'll then trigger one loop through the
// relink mechanism). We therefore strike a balance between busy looping and a livelock risk by making
// sure that there's at most one iteration of busy loop per second. It is theoretically possible to
// correctly implement this without ever risking a livelock, but using this heuristic we eliminate the
// chance of the livelock, while still maintaining a good enough busy-looping prevention.
// after recompilation is by design. If we didn't wait here for the recompilation to complete, we would
// be busy looping through the fallback path of the invalidated switch point, relinking the call site
// again with the same invalidated switch point, invoking the fallback, etc. stealing CPU cycles from
// the recompilation task we're dependent on. This can still happen if the switch point gets invalidated
// after we grabbed it here, in which case we'll indeed do one busy relink immediately.
try {
wait(1000L);
wait();
} catch (final InterruptedException e) {
// Intentionally ignored. There's nothing meaningful we can do if we're interrupted
}
@ -631,7 +623,6 @@ final class CompiledFunction {
return new HandleAndAssumptions(handle, assumptions);
}
}
throw new AssertionError(); // never reached
}
private static void relinkComposableInvoker(final CallSite cs, final CompiledFunction inv, final boolean constructor) {

@ -330,12 +330,15 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
* Indicate that proto itself has changed in hierarchy somewhere.
*/
synchronized void invalidateAllProtoGetSwitchPoints() {
if (protoGetSwitches != null && !protoGetSwitches.isEmpty()) {
if (Context.DEBUG) {
protoInvalidations += protoGetSwitches.size();
if (protoGetSwitches != null) {
final int size = protoGetSwitches.size();
if (size > 0) {
if (Context.DEBUG) {
protoInvalidations += size;
}
SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[size]));
protoGetSwitches.clear();
}
SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[protoGetSwitches.values().size()]));
protoGetSwitches.clear();
}
}
@ -375,7 +378,8 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
}
}
// Update the free slots bitmap for a property that has been deleted and/or added.
// Update the free slots bitmap for a property that has been deleted and/or added. This method is not synchronized
// as it is always invoked on a newly created instance.
private void updateFreeSlots(final Property oldProperty, final Property newProperty) {
// Free slots bitset is possibly shared with parent map, so we must clone it before making modifications.
boolean freeSlotsCloned = false;
@ -425,7 +429,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
*
* @return New {@link PropertyMap} with {@link Property} added.
*/
public PropertyMap addProperty(final Property property) {
public synchronized PropertyMap addProperty(final Property property) {
if (listeners != null) {
listeners.propertyAdded(property);
}
@ -434,9 +438,9 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
if (newMap == null) {
final PropertyHashMap newProperties = properties.immutableAdd(property);
newMap = new PropertyMap(this, newProperties);
addToHistory(property, newMap);
newMap.updateFlagsAndBoundaries(property);
newMap.updateFreeSlots(null, property);
addToHistory(property, newMap);
}
return newMap;
@ -449,7 +453,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
*
* @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found.
*/
public PropertyMap deleteProperty(final Property property) {
public synchronized PropertyMap deleteProperty(final Property property) {
if (listeners != null) {
listeners.propertyDeleted(property);
}
@ -881,8 +885,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
* @param newProto New prototype object to replace oldProto.
* @return New {@link PropertyMap} with prototype changed.
*/
public PropertyMap changeProto(final ScriptObject newProto) {
public synchronized PropertyMap changeProto(final ScriptObject newProto) {
final PropertyMap nextMap = checkProtoHistory(newProto);
if (nextMap != null) {
return nextMap;

@ -0,0 +1,72 @@
/*
* 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-8075090: Add tests for the basic failure of try/finally compilation
*
* @test
* @run
*/
(function() {
var finallyExpected = false;
try {
for(var i = 0; i < 2; ++i) {
if(i == 1) {
continue;
}
}
finallyExpected = true;
} finally {
Assert.assertTrue(finallyExpected);
}
})();
(function() {
var finallyExpected = false;
try {
for(var i = 0; i < 2; ++i) {
if(i == 1) {
break;
}
}
finallyExpected = true;
} finally {
Assert.assertTrue(finallyExpected);
}
})();
(function() {
var finallyExpected = false;
try {
L1: {
if ((function(){return true})()) {
break L1;
}
Assert.fail(); // unreachable
}
finallyExpected = true;
} finally {
Assert.assertTrue(finallyExpected);
}
})();

@ -0,0 +1,76 @@
/*
* 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-8075207: Nashorn parser API returns StatementTree objects in out of order
*
* @test
* @option -scripting
* @run
*/
var Parser = Java.type("jdk.nashorn.api.tree.Parser");
var ExpressionStatementTree = Java.type("jdk.nashorn.api.tree.ExpressionStatementTree");
var FunctionDeclarationTree = Java.type("jdk.nashorn.api.tree.FunctionDeclarationTree");
var VariableTree = Java.type("jdk.nashorn.api.tree.VariableTree");
var parser = Parser.create();
var ast = parser.parse("hello.js", <<CODE
var hello = 'hello';
function print_hello() {
var x = 2;
print(hello);
function inner_func() {}
var y = function() {
var PI = Math.PI;
function inner2() {}
var E = Math.E;
}
}
var hello = "hello 2";
CODE, print);
var stats = ast.sourceElements;
Assert.assertTrue(stats.get(0) instanceof VariableTree);
Assert.assertTrue(stats.get(1) instanceof FunctionDeclarationTree);
Assert.assertTrue(stats.get(2) instanceof VariableTree);
var print_hello = stats.get(1);
Assert.assertEquals(print_hello.name, "print_hello");
var print_hello_stats = print_hello.body.statements;
Assert.assertTrue(print_hello_stats.get(0) instanceof VariableTree);
Assert.assertTrue(print_hello_stats.get(1) instanceof ExpressionStatementTree);
Assert.assertTrue(print_hello_stats.get(2) instanceof FunctionDeclarationTree);
Assert.assertTrue(print_hello_stats.get(3) instanceof VariableTree);
var anonFunc = print_hello_stats.get(3).initializer;
var anonFunc_stats = anonFunc.body.statements;
Assert.assertTrue(anonFunc_stats.get(0) instanceof VariableTree);
Assert.assertTrue(anonFunc_stats.get(1) instanceof FunctionDeclarationTree);
Assert.assertTrue(anonFunc_stats.get(2) instanceof VariableTree);

@ -0,0 +1,49 @@
/*
* 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-8075448: nashorn parser API returns init variable tree object of a for
* loop after for loop statement tree object
*
* @test
* @option -scripting
* @run
*/
var Parser = Java.type("jdk.nashorn.api.tree.Parser");
var ForLoopTree = Java.type("jdk.nashorn.api.tree.ForLoopTree");
var VariableTree = Java.type("jdk.nashorn.api.tree.VariableTree");
var parser = Parser.create();
var code = <<EOF
for (var i = 0; i < 10; i++)
print("hello");
EOF;
var ast = parser.parse("test.js", code, print);
var stats = ast.sourceElements;
Assert.assertTrue(stats[0] instanceof VariableTree);
Assert.assertEquals(stats[0].name, "i");
Assert.assertTrue(stats[1] instanceof ForLoopTree);

@ -0,0 +1,43 @@
/*
* 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-8075454: Anonymous functions have internal names exposed via parser API
*
* @test
* @option -scripting
* @run
*/
var Parser = Java.type("jdk.nashorn.api.tree.Parser");
var parser = Parser.create();
var ast = parser.parse("test.js", <<EOF
function(x) {
return x*x
}
EOF, print);
Assert.assertNull(ast.sourceElements[0].name);

@ -28,6 +28,17 @@
* @run
*/
//JDK-8066217, constructor for arraybuffer not behaving as per spec
function checkLength(ab, l) {
if (ab.byteLength != l) {
throw "length error: " + ab.byteLength + " != " + l;
}
}
checkLength(new ArrayBuffer(), 0);
checkLength(new ArrayBuffer(0), 0);
checkLength(new ArrayBuffer(1024), 1024);
checkLength(new ArrayBuffer(1,2,3), 1);
checkLength(new ArrayBuffer([17]), 17);
var typeDefinitions = [
Int8Array,

File diff suppressed because it is too large Load Diff