# HG changeset patch # User lagergren # Date 1381334002 -7200 # Node ID 03a68e7ca1d5ef261c0c3055733e80fc75f9b44c # Parent ec3094d9d5d59723998a50b1fc3241d89f578196 8026137: Fix Issues with Binary Evaluation Order Reviewed-by: hannesw, jlaskey Contributed-by: marcus.lagergren@oracle.com, attila.szegedi@oracle.com diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/Attr.java --- a/src/jdk/nashorn/internal/codegen/Attr.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/Attr.java Wed Oct 09 17:53:22 2013 +0200 @@ -480,6 +480,10 @@ } //unknown parameters are promoted to object type. + if (newFunctionNode.hasLazyChildren()) { + //the final body has already been assigned as we have left the function node block body by now + objectifySymbols(body); + } newFunctionNode = finalizeParameters(newFunctionNode); newFunctionNode = finalizeTypes(newFunctionNode); for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) { @@ -489,11 +493,6 @@ } } - if (newFunctionNode.hasLazyChildren()) { - //the final body has already been assigned as we have left the function node block body by now - objectifySymbols(body); - } - List syntheticInitializers = null; if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) { @@ -503,8 +502,8 @@ syntheticInitializers.add(createSyntheticInitializer(newFunctionNode.getIdent(), CALLEE, newFunctionNode)); } - if(newFunctionNode.needsArguments()) { - if(syntheticInitializers == null) { + if (newFunctionNode.needsArguments()) { + if (syntheticInitializers == null) { syntheticInitializers = new ArrayList<>(1); } // "var arguments = :arguments" @@ -512,12 +511,12 @@ ARGUMENTS, newFunctionNode)); } - if(syntheticInitializers != null) { - final List stmts = body.getStatements(); + if (syntheticInitializers != null) { + final List stmts = newFunctionNode.getBody().getStatements(); final List newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size()); newStatements.addAll(syntheticInitializers); newStatements.addAll(stmts); - newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements)); + newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements)); } if (returnTypes.peek().isUnknown()) { @@ -558,12 +557,6 @@ } @Override - public Node leaveCONVERT(final UnaryNode unaryNode) { - assert false : "There should be no convert operators in IR during Attribution"; - return end(unaryNode); - } - - @Override public Node leaveIdentNode(final IdentNode identNode) { final String name = identNode.getName(); @@ -991,7 +984,7 @@ @Override public Node leaveNEW(final UnaryNode unaryNode) { - return end(ensureSymbol(Type.OBJECT, unaryNode)); + return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew()))); } @Override @@ -1287,7 +1280,9 @@ private Node leaveCmp(final BinaryNode binaryNode) { ensureTypeNotUnknown(binaryNode.lhs()); ensureTypeNotUnknown(binaryNode.rhs()); - + Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); + ensureSymbol(widest, binaryNode.lhs()); + ensureSymbol(widest, binaryNode.rhs()); return end(ensureSymbol(Type.BOOLEAN, binaryNode)); } @@ -1630,7 +1625,7 @@ if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) { LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to); Symbol symbol = node.getSymbol(); - if(symbol.isShared() && symbol.wouldChangeType(to)) { + if (symbol.isShared() && symbol.wouldChangeType(to)) { symbol = temporarySymbols.getTypedTemporarySymbol(to); } newType(symbol, to); @@ -1646,40 +1641,105 @@ return !node.isLazy(); } - /** - * Eg. - * - * var d = 17; - * var e; - * e = d; //initially typed as int for node type, should retype as double - * e = object; - * - * var d = 17; - * var e; - * e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric - * e = object; - * - */ + // + // Eg. + // + // var d = 17; + // var e; + // e = d; //initially typed as int for node type, should retype as double + // e = object; + // + // var d = 17; + // var e; + // e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric + // e = object; + // @SuppressWarnings("fallthrough") @Override public Node leaveBinaryNode(final BinaryNode binaryNode) { final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); BinaryNode newBinaryNode = binaryNode; - switch (binaryNode.tokenType()) { - default: - if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) { + + if (isAdd(binaryNode)) { + newBinaryNode = (BinaryNode)widen(newBinaryNode, widest); + if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) { + return new RuntimeNode(newBinaryNode, Request.ADD); + } + } else if (binaryNode.isComparison()) { + final Expression lhs = newBinaryNode.lhs(); + final Expression rhs = newBinaryNode.rhs(); + + Type cmpWidest = Type.widest(lhs.getType(), rhs.getType()); + + boolean newRuntimeNode = false, finalized = false; + switch (newBinaryNode.tokenType()) { + case EQ_STRICT: + case NE_STRICT: + if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { + newRuntimeNode = true; + cmpWidest = Type.OBJECT; + finalized = true; + } + //fallthru + default: + if (newRuntimeNode || cmpWidest.isObject()) { + return new RuntimeNode(newBinaryNode, Request.requestFor(binaryNode)).setIsFinal(finalized); + } break; } + + return newBinaryNode; + } else { + if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) { + return newBinaryNode; + } + checkThisAssignment(binaryNode); newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest)); - case ADD: newBinaryNode = (BinaryNode)widen(newBinaryNode, widest); } + return newBinaryNode; + + } + + private boolean isAdd(final Node node) { + return node.isTokenType(TokenType.ADD); + } + + /** + * Determine if the outcome of + operator is a string. + * + * @param node Node to test. + * @return true if a string result. + */ + private boolean isAddString(final Node node) { + if (node instanceof BinaryNode && isAdd(node)) { + final BinaryNode binaryNode = (BinaryNode)node; + final Node lhs = binaryNode.lhs(); + final Node rhs = binaryNode.rhs(); + + return isAddString(lhs) || isAddString(rhs); + } + + return node instanceof LiteralNode && ((LiteralNode)node).isString(); + } + + private void checkThisAssignment(final BinaryNode binaryNode) { + if (binaryNode.isAssignment()) { + if (binaryNode.lhs() instanceof AccessNode) { + final AccessNode accessNode = (AccessNode) binaryNode.lhs(); + + if (accessNode.getBase().getSymbol().isThis()) { + lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName()); + } + } + } } }); lc.replace(currentFunctionNode, newFunctionNode); currentFunctionNode = newFunctionNode; } while (!changed.isEmpty()); + return currentFunctionNode; } @@ -1692,7 +1752,6 @@ final Expression lhs = binaryNode.lhs(); newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType -// ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine return end(ensureSymbol(destType, binaryNode)); } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/BranchOptimizer.java --- a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java Wed Oct 09 17:53:22 2013 +0200 @@ -56,10 +56,6 @@ branchOptimizer(node, label, state); } - private void load(final Expression node) { - codegen.load(node); - } - private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) { final Expression rhs = unaryNode.rhs(); @@ -67,18 +63,16 @@ case NOT: branchOptimizer(rhs, label, !state); return; - case CONVERT: + default: if (unaryNode.getType().isBoolean()) { branchOptimizer(rhs, label, state); return; } break; - default: - break; } // convert to boolean - load(unaryNode); + codegen.load(unaryNode); method.convert(Type.BOOLEAN); if (state) { method.ifne(label); @@ -90,6 +84,7 @@ private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) { final Expression lhs = binaryNode.lhs(); final Expression rhs = binaryNode.rhs(); + Type widest = Type.widest(lhs.getType(), rhs.getType()); switch (binaryNode.tokenType()) { case AND: @@ -118,45 +113,33 @@ case EQ: case EQ_STRICT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, widest); method.conditionalJump(state ? EQ : NE, true, label); return; case NE: case NE_STRICT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, widest); method.conditionalJump(state ? NE : EQ, true, label); return; case GE: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, widest); method.conditionalJump(state ? GE : LT, !state, label); return; case GT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, widest); method.conditionalJump(state ? GT : LE, !state, label); return; case LE: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol(); - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, widest); method.conditionalJump(state ? LE : GT, state, label); return; case LT: - assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol() + " in " + binaryNode; - load(lhs); - load(rhs); + codegen.loadBinaryOperands(lhs, rhs, widest); method.conditionalJump(state ? LT : GE, state, label); return; @@ -164,7 +147,7 @@ break; } - load(binaryNode); + codegen.load(binaryNode); method.convert(Type.BOOLEAN); if (state) { method.ifne(label); @@ -187,7 +170,7 @@ } } - load(node); + codegen.load(node); method.convert(Type.BOOLEAN); if (state) { method.ifne(label); diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Oct 09 17:53:22 2013 +0200 @@ -43,7 +43,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; -import static jdk.nashorn.internal.codegen.CompilerConstants.staticField; import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; @@ -60,7 +59,6 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Locale; import java.util.Set; import java.util.TreeMap; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; @@ -111,7 +109,6 @@ import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.objects.Global; @@ -217,12 +214,12 @@ * @param identNode an identity node to load * @return the method generator used */ - private MethodEmitter loadIdent(final IdentNode identNode) { + private MethodEmitter loadIdent(final IdentNode identNode, final Type type) { final Symbol symbol = identNode.getSymbol(); if (!symbol.isScope()) { assert symbol.hasSlot() || symbol.isParam(); - return method.load(symbol); + return method.load(symbol).convert(type); } final String name = symbol.getName(); @@ -243,11 +240,11 @@ if (isFastScope(symbol)) { // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope. if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) { - return loadSharedScopeVar(identNode.getType(), symbol, flags); + return loadSharedScopeVar(type, symbol, flags); } - return loadFastScopeVar(identNode.getType(), symbol, flags, identNode.isFunction()); + return loadFastScopeVar(type, symbol, flags, identNode.isFunction()); } - return method.dynamicGet(identNode.getType(), identNode.getName(), flags, identNode.isFunction()); + return method.dynamicGet(type, identNode.getName(), flags, identNode.isFunction()); } } @@ -313,9 +310,9 @@ return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod); } - private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) { + private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) { loadFastScopeProto(symbol, true); - method.dynamicSet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE); + method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE); return method; } @@ -359,14 +356,61 @@ * @return the method emitter used */ MethodEmitter load(final Expression node) { - return load(node, false); + return load(node, node.hasType() ? node.getType() : null, false); + } + + private static boolean safeLiteral(final Expression rhs) { + return rhs instanceof LiteralNode && !(rhs instanceof ArrayLiteralNode); + } + + MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type) { + return loadBinaryOperands(lhs, rhs, type, false); } - private MethodEmitter load(final Expression node, final boolean baseAlreadyOnStack) { + private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type, final boolean baseAlreadyOnStack) { + // ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary + // expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT + // RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we + // can combine a LOAD with a CONVERT operation (e.g. use a dynamic getter with the conversion target type as its + // return value). What we do here is reorder LOAD RIGHT and CONVERT LEFT when possible; it is possible only when + // we can prove that executing CONVERT LEFT can't have a side effect that changes the value of LOAD RIGHT. + // Basically, if we know that either LEFT is not an object, or RIGHT is a constant literal, then we can do the + // reordering and collapse LOAD/CONVERT into a single operation; otherwise we need to do the more costly + // separate operations to preserve specification semantics. + final Type lhsType = lhs.getType(); + if (lhsType.isObject() && !safeLiteral(rhs)) { + // Can't reorder. Load and convert separately. + load(lhs, lhsType, baseAlreadyOnStack); + load(rhs, rhs.getType(), false); + // Avoid empty SWAP, SWAP bytecode sequence if CONVERT LEFT is a no-op + if (!lhsType.isEquivalentTo(type)) { + method.swap(); + method.convert(type); + method.swap(); + } + method.convert(type); + } else { + // Can reorder. Combine load and convert into single operations. + load(lhs, type, baseAlreadyOnStack); + load(rhs, type, false); + } + + return method; + } + + MethodEmitter loadBinaryOperands(final BinaryNode node) { + return loadBinaryOperands(node.lhs(), node.rhs(), node.getType(), false); + } + + private MethodEmitter load(final Expression node, final Type type) { + return load(node, type, false); + } + + private MethodEmitter load(final Expression node, final Type type, final boolean baseAlreadyOnStack) { final Symbol symbol = node.getSymbol(); // If we lack symbols, we just generate what we see. - if (symbol == null) { + if (symbol == null || type == null) { node.accept(this); return method; } @@ -378,10 +422,10 @@ */ final CodeGenerator codegen = this; - node.accept(new NodeVisitor(new LexicalContext()) { + node.accept(new NodeVisitor(lc) { @Override public boolean enterIdentNode(final IdentNode identNode) { - loadIdent(identNode); + loadIdent(identNode, type); return false; } @@ -391,7 +435,7 @@ load(accessNode.getBase()).convert(Type.OBJECT); } assert method.peekType().isObject(); - method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction()); + method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction()); return false; } @@ -401,7 +445,7 @@ load(indexNode.getBase()).convert(Type.OBJECT); load(indexNode.getIndex()); } - method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction()); + method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction()); return false; } @@ -410,13 +454,29 @@ // function nodes will always leave a constructed function object on stack, no need to load the symbol // separately as in enterDefault() functionNode.accept(codegen); + method.convert(type); return false; } @Override + public boolean enterCallNode(CallNode callNode) { + return codegen.enterCallNode(callNode, type); + } + + @Override + public boolean enterLiteralNode(LiteralNode literalNode) { + return codegen.enterLiteralNode(literalNode, type); + } + + @Override public boolean enterDefault(final Node otherNode) { + final Node currentDiscard = codegen.lc.getCurrentDiscard(); otherNode.accept(codegen); // generate code for whatever we are looking at. - method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there) + if(currentDiscard != otherNode) { + method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there) + assert method.peekType() != null; + method.convert(type); + } return false; } }); @@ -583,15 +643,19 @@ return argCount; } + @Override public boolean enterCallNode(final CallNode callNode) { + return enterCallNode(callNode, callNode.getType()); + } + + private boolean enterCallNode(final CallNode callNode, final Type callNodeType) { lineNumber(callNode.getLineNumber()); final List args = callNode.getArgs(); final Expression function = callNode.getFunction(); final Block currentBlock = lc.getCurrentBlock(); final CodeGeneratorLexicalContext codegenLexicalContext = lc; - final Type callNodeType = callNode.getType(); function.accept(new NodeVisitor(new LexicalContext()) { @@ -612,16 +676,14 @@ } private void scopeCall(final IdentNode node, final int flags) { - load(node); - method.convert(Type.OBJECT); // foo() makes no sense if foo == 3 + load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. method.loadNull(); //the 'this' method.dynamicCall(callNodeType, 2 + loadArgs(args), flags); } private void evalCall(final IdentNode node, final int flags) { - load(node); - method.convert(Type.OBJECT); // foo() makes no sense if foo == 3 + load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3 final Label not_eval = new Label("not_eval"); final Label eval_done = new Label("eval_done"); @@ -638,8 +700,7 @@ final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); // load evaluated code - load(evalArgs.getCode()); - method.convert(Type.OBJECT); + load(evalArgs.getCode(), Type.OBJECT); // special/extra 'eval' arguments load(evalArgs.getThis()); method.load(evalArgs.getLocation()); @@ -690,13 +751,11 @@ @Override public boolean enterAccessNode(final AccessNode node) { - load(node.getBase()); - method.convert(Type.OBJECT); + load(node.getBase(), Type.OBJECT); method.dup(); method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true); method.swap(); method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags()); - assert method.peekType().equals(callNodeType); return false; } @@ -727,18 +786,17 @@ @Override public boolean enterIndexNode(final IndexNode node) { - load(node.getBase()); - method.convert(Type.OBJECT); + load(node.getBase(), Type.OBJECT); method.dup(); - load(node.getIndex()); final Type indexType = node.getIndex().getType(); if (indexType.isObject() || indexType.isBoolean()) { - method.convert(Type.OBJECT); //TODO + load(node.getIndex(), Type.OBJECT); //TODO + } else { + load(node.getIndex()); } method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true); method.swap(); method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags()); - assert method.peekType().equals(callNode.getType()); return false; } @@ -746,11 +804,9 @@ @Override protected boolean enterDefault(final Node node) { // Load up function. - load(function); - method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions + load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE); - assert method.peekType().equals(callNode.getType()); return false; } @@ -853,8 +909,7 @@ final Expression init = forNode.getInit(); - load(modify); - assert modify.getType().isObject(); + load(modify, Type.OBJECT); method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR); method.store(iter); method._goto(forNode.getContinueLabel()); @@ -1203,8 +1258,7 @@ if (element == null) { method.loadEmpty(elementType); } else { - assert elementType.isEquivalentTo(element.getType()) : "array element type doesn't match array type"; - load(element); + load(element, elementType); } method.arraystore(); @@ -1274,7 +1328,7 @@ } // literal values - private MethodEmitter load(final LiteralNode node) { + private MethodEmitter loadLiteral(final LiteralNode node, final Type type) { final Object value = node.getValue(); if (value == null) { @@ -1294,15 +1348,26 @@ } else if (value instanceof Boolean) { method.load((Boolean)value); } else if (value instanceof Integer) { - method.load((Integer)value); + if(type.isEquivalentTo(Type.NUMBER)) { + method.load(((Integer)value).doubleValue()); + } else if(type.isEquivalentTo(Type.LONG)) { + method.load(((Integer)value).longValue()); + } else { + method.load((Integer)value); + } } else if (value instanceof Long) { - method.load((Long)value); + if(type.isEquivalentTo(Type.NUMBER)) { + method.load(((Long)value).doubleValue()); + } else { + method.load((Long)value); + } } else if (value instanceof Double) { method.load((Double)value); } else if (node instanceof ArrayLiteralNode) { - final ArrayType type = (ArrayType)node.getType(); - loadArray((ArrayLiteralNode)node, type); - globalAllocateArray(type); + final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node; + final ArrayType atype = arrayLiteral.getArrayType(); + loadArray(arrayLiteral, atype); + globalAllocateArray(atype); } else { assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value; } @@ -1346,8 +1411,12 @@ @Override public boolean enterLiteralNode(final LiteralNode literalNode) { + return enterLiteralNode(literalNode, literalNode.getType()); + } + + private boolean enterLiteralNode(final LiteralNode literalNode, final Type type) { assert literalNode.getSymbol() != null : literalNode + " has no symbol"; - load(literalNode).store(literalNode.getSymbol()); + loadLiteral(literalNode, type).convert(type).store(literalNode.getSymbol()); return false; } @@ -1622,10 +1691,8 @@ return enterCmp(lhs, rhs, Condition.GT, type, symbol); case ADD: Type widest = Type.widest(lhs.getType(), rhs.getType()); - load(lhs); - method.convert(widest); - load(rhs); - method.convert(widest); + load(lhs, widest); + load(rhs, widest); method.add(); method.convert(type); method.store(symbol); @@ -1638,15 +1705,15 @@ } if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) { - return false; + return false; } if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) { - return false; + return false; } for (final Expression arg : args) { - load(arg).convert(Type.OBJECT); //TODO this should not be necessary below Lower + load(arg).convert(Type.OBJECT); } method.invokestatic( @@ -1903,24 +1970,15 @@ method.lookupswitch(defaultLabel, ints, labels); } } else { - load(expression); - - if (expression.getType().isInteger()) { - method.convert(Type.NUMBER).dup(); - method.store(tag); - method.conditionalJump(Condition.NE, true, defaultLabel); - } else { - assert tag.getSymbolType().isObject(); - method.convert(Type.OBJECT); //e.g. 1 literal pushed and tag is object - method.store(tag); - } + load(expression, Type.OBJECT); + method.store(tag); for (final CaseNode caseNode : cases) { final Expression test = caseNode.getTest(); if (test != null) { method.load(tag); - load(test); + load(test, Type.OBJECT); method.invoke(ScriptRuntime.EQ_STRICT); method.ifne(caseNode.getEntry()); } @@ -1961,8 +2019,7 @@ final int line = throwNode.getLineNumber(); final int column = source.getColumn(position); - load(expression); - assert expression.getType().isObject(); + load(expression, Type.OBJECT); method.load(source.getName()); method.load(line); @@ -2087,29 +2144,28 @@ lineNumber(varNode); - final Symbol varSymbol = varNode.getName().getSymbol(); - assert varSymbol != null : "variable node " + varNode + " requires a name with a symbol"; + final IdentNode identNode = varNode.getName(); + final Symbol identSymbol = identNode.getSymbol(); + assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol"; assert method != null; - final boolean needsScope = varSymbol.isScope(); + final boolean needsScope = identSymbol.isScope(); if (needsScope) { method.loadCompilerConstant(SCOPE); } - load(init); if (needsScope) { + load(init); int flags = CALLSITE_SCOPE | getCallSiteFlags(); - final IdentNode identNode = varNode.getName(); - final Type type = identNode.getType(); - if (isFastScope(varSymbol)) { - storeFastScopeVar(type, varSymbol, flags); + if (isFastScope(identSymbol)) { + storeFastScopeVar(identSymbol, flags); } else { - method.dynamicSet(type, identNode.getName(), flags); + method.dynamicSet(identNode.getName(), flags); } } else { - method.convert(varNode.getName().getType()); // aw: convert moved here - method.store(varSymbol); + load(init, identNode.getType()); + method.store(identSymbol); } return false; @@ -2168,8 +2224,7 @@ tryLabel = null; } - load(expression); - assert expression.getType().isObject() : "with expression needs to be object: " + expression; + load(expression, Type.OBJECT); if (hasScope) { // Construct a WithObject if we have a scope @@ -2211,54 +2266,15 @@ @Override public boolean enterADD(final UnaryNode unaryNode) { - load(unaryNode.rhs()); - assert unaryNode.rhs().getType().isNumber() : unaryNode.rhs().getType() + " "+ unaryNode.getSymbol(); + load(unaryNode.rhs(), unaryNode.getType()); + assert unaryNode.getType().isNumeric(); method.store(unaryNode.getSymbol()); - return false; } @Override public boolean enterBIT_NOT(final UnaryNode unaryNode) { - load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol()); - return false; - } - - // do this better with convert calls to method. TODO - @Override - public boolean enterCONVERT(final UnaryNode unaryNode) { - final Expression rhs = unaryNode.rhs(); - final Type to = unaryNode.getType(); - - if (to.isObject() && rhs instanceof LiteralNode) { - final LiteralNode literalNode = (LiteralNode)rhs; - final Object value = literalNode.getValue(); - - if (value instanceof Number) { - assert !to.isArray() : "type hygiene - cannot convert number to array: (" + to.getTypeClass().getSimpleName() + ')' + value; - if (value instanceof Integer) { - method.load((Integer)value); - } else if (value instanceof Long) { - method.load((Long)value); - } else if (value instanceof Double) { - method.load((Double)value); - } else { - assert false; - } - method.convert(Type.OBJECT); - } else if (value instanceof Boolean) { - method.getField(staticField(Boolean.class, value.toString().toUpperCase(Locale.ENGLISH), Boolean.class)); - } else { - load(rhs); - method.convert(unaryNode.getType()); - } - } else { - load(rhs); - method.convert(unaryNode.getType()); - } - - method.store(unaryNode.getSymbol()); - + load(unaryNode.rhs(), Type.INT).load(-1).xor().store(unaryNode.getSymbol()); return false; } @@ -2276,9 +2292,7 @@ @Override protected void evaluate() { - load(rhs, true); - - method.convert(type); + load(rhs, type, true); if (!isPostfix) { if (type.isInteger()) { method.load(isIncrement ? 1 : -1); @@ -2344,12 +2358,11 @@ public boolean enterNOT(final UnaryNode unaryNode) { final Expression rhs = unaryNode.rhs(); - load(rhs); + load(rhs, Type.BOOLEAN); final Label trueLabel = new Label("true"); final Label afterLabel = new Label("after"); - method.convert(Type.BOOLEAN); method.ifne(trueLabel); method.load(true); method._goto(afterLabel); @@ -2363,8 +2376,8 @@ @Override public boolean enterSUB(final UnaryNode unaryNode) { - load(unaryNode.rhs()).neg().store(unaryNode.getSymbol()); - + assert unaryNode.getType().isNumeric(); + load(unaryNode.rhs()).convert(unaryNode.getType()).neg().store(unaryNode.getSymbol()); return false; } @@ -2377,9 +2390,7 @@ } private void enterNumericAdd(final Expression lhs, final Expression rhs, final Type type, final Symbol symbol) { - assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs); - load(lhs); - load(rhs); + loadBinaryOperands(lhs, rhs, type); method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack? method.store(symbol); } @@ -2393,8 +2404,7 @@ if (type.isNumeric()) { enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol()); } else { - load(lhs).convert(Type.OBJECT); - load(rhs).convert(Type.OBJECT); + loadBinaryOperands(binaryNode); method.add(); method.store(binaryNode.getSymbol()); } @@ -2439,13 +2449,16 @@ if (!lhsType.isEquivalentTo(rhsType)) { //this is OK if scoped, only locals are wrong - assert !(lhs instanceof IdentNode) || lhs.getSymbol().isScope() : new ASTWriter(binaryNode); } new Store(binaryNode, lhs) { @Override protected void evaluate() { - load(rhs); + if ((lhs instanceof IdentNode) && !lhs.getSymbol().isScope()) { + load(rhs, lhsType); + } else { + load(rhs); + } } }.store(); @@ -2484,8 +2497,7 @@ @Override protected void evaluate() { - load(assignNode.lhs(), true).convert(opType); - load(assignNode.rhs()).convert(opType); + loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), opType, true); op(); method.convert(assignNode.getType()); } @@ -2656,8 +2668,7 @@ protected abstract void op(); protected void evaluate(final BinaryNode node) { - load(node.lhs()); - load(node.rhs()); + loadBinaryOperands(node); op(); method.store(node.getSymbol()); } @@ -2739,11 +2750,7 @@ final Type widest = Type.widest(lhsType, rhsType); assert widest.isNumeric() || widest.isBoolean() : widest; - load(lhs); - method.convert(widest); - load(rhs); - method.convert(widest); - + loadBinaryOperands(lhs, rhs, widest); final Label trueLabel = new Label("trueLabel"); final Label afterLabel = new Label("skip"); @@ -2862,6 +2869,12 @@ public boolean enterSHR(final BinaryNode binaryNode) { new BinaryArith() { @Override + protected void evaluate(final BinaryNode node) { + loadBinaryOperands(node.lhs(), node.rhs(), Type.INT); + op(); + method.store(node.getSymbol()); + } + @Override protected void op() { method.shr(); method.convert(Type.LONG).load(JSType.MAX_UINT).and(); @@ -2898,21 +2911,17 @@ widest = Type.OBJECT; } - load(test); - assert test.getType().isBoolean() : "lhs in ternary must be boolean"; - + load(test, Type.BOOLEAN); // we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17 // will left as (Object)x=55 : (Object)17 by Lower. Then the first term can be {I}x=55 of type int, which breaks the // symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions // too early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to // do this property. Then we never need any conversions in CodeGenerator method.ifeq(falseLabel); - load(trueExpr); - method.convert(widest); + load(trueExpr, widest); method._goto(exitLabel); method.label(falseLabel); - load(falseExpr); - method.convert(widest); + load(falseExpr, widest); method.label(exitLabel); method.store(symbol); @@ -3044,8 +3053,7 @@ final BaseNode baseNode = (BaseNode)target; final Expression base = baseNode.getBase(); - load(base); - method.convert(Type.OBJECT); + load(base, Type.OBJECT); depth += Type.OBJECT.getSlots(); if (isSelfModifying()) { @@ -3064,10 +3072,11 @@ enterBaseNode(); final Expression index = node.getIndex(); - // could be boolean here as well - load(index); if (!index.getType().isNumeric()) { - method.convert(Type.OBJECT); + // could be boolean here as well + load(index, Type.OBJECT); + } else { + load(index); } depth += index.getType().getSlots(); @@ -3136,8 +3145,6 @@ * need to do a conversion on non-equivalent types exists, but is * very rare. See for example test/script/basic/access-specializer.js */ - method.convert(target.getType()); - target.accept(new NodeVisitor(new LexicalContext()) { @Override protected boolean enterDefault(Node node) { @@ -3145,24 +3152,17 @@ } @Override - public boolean enterUnaryNode(final UnaryNode node) { - if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) { - method.convert(node.rhs().getType()); - } - return true; - } - - @Override public boolean enterIdentNode(final IdentNode node) { final Symbol symbol = node.getSymbol(); assert symbol != null; if (symbol.isScope()) { if (isFastScope(symbol)) { - storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags()); + storeFastScopeVar(symbol, CALLSITE_SCOPE | getCallSiteFlags()); } else { - method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags()); + method.dynamicSet(node.getName(), CALLSITE_SCOPE | getCallSiteFlags()); } } else { + method.convert(node.getType()); method.store(symbol); } return false; @@ -3171,7 +3171,7 @@ @Override public boolean enterAccessNode(final AccessNode node) { - method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags()); + method.dynamicSet(node.getProperty().getName(), getCallSiteFlags()); return false; } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/CompileUnit.java --- a/src/jdk/nashorn/internal/codegen/CompileUnit.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java Wed Oct 09 17:53:22 2013 +0200 @@ -28,7 +28,7 @@ /** * Used to track split class compilation. */ -public class CompileUnit { +public class CompileUnit implements Comparable { /** Current class name */ private final String className; @@ -116,4 +116,9 @@ public String toString() { return "[classname=" + className + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']'; } + + @Override + public int compareTo(CompileUnit o) { + return className.compareTo(o.className); + } } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/Compiler.java --- a/src/jdk/nashorn/internal/codegen/Compiler.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Wed Oct 09 17:53:22 2013 +0200 @@ -36,8 +36,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; -import jdk.nashorn.internal.ir.TemporarySymbols; - import java.io.File; import java.lang.reflect.Field; import java.security.AccessController; @@ -48,18 +46,20 @@ import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.TreeSet; import java.util.logging.Level; import jdk.internal.dynalink.support.NameCodec; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.TemporarySymbols; import jdk.nashorn.internal.ir.debug.ClassHistogramElement; import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator; import jdk.nashorn.internal.runtime.CodeInstaller; @@ -256,8 +256,8 @@ this.sequence = sequence; this.installer = installer; this.constantData = new ConstantData(); - this.compileUnits = new HashSet<>(); - this.bytecode = new HashMap<>(); + this.compileUnits = new TreeSet<>(); + this.bytecode = new LinkedHashMap<>(); } private void initCompiler(final FunctionNode functionNode) { diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/FinalizeTypes.java --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Wed Oct 09 17:53:22 2013 +0200 @@ -28,49 +28,22 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.ir.AccessNode; -import jdk.nashorn.internal.ir.Assignment; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; -import jdk.nashorn.internal.ir.CallNode; -import jdk.nashorn.internal.ir.CaseNode; -import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; 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.LexicalContext; -import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.ReturnNode; -import jdk.nashorn.internal.ir.RuntimeNode; -import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.TemporarySymbols; -import jdk.nashorn.internal.ir.TernaryNode; -import jdk.nashorn.internal.ir.ThrowNode; -import jdk.nashorn.internal.ir.TypeOverride; import jdk.nashorn.internal.ir.UnaryNode; -import jdk.nashorn.internal.ir.VarNode; -import jdk.nashorn.internal.ir.WhileNode; -import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; -import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; -import jdk.nashorn.internal.runtime.Debug; import jdk.nashorn.internal.runtime.DebugLogger; -import jdk.nashorn.internal.runtime.JSType; /** * Lower to more primitive operations. After lowering, an AST has symbols and @@ -97,272 +70,32 @@ } @Override - public Node leaveCallNode(final CallNode callNode) { - // AccessSpecializer - call return type may change the access for this location - final Node function = callNode.getFunction(); - if (function instanceof FunctionNode) { - return setTypeOverride(callNode, ((FunctionNode)function).getReturnType()); - } - return callNode; - } - - private Node leaveUnary(final UnaryNode unaryNode) { - return unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType())); - } - - @Override - public Node leaveADD(final UnaryNode unaryNode) { - return leaveUnary(unaryNode); - } - - @Override - public Node leaveBIT_NOT(final UnaryNode unaryNode) { - return leaveUnary(unaryNode); - } - - @Override - public Node leaveCONVERT(final UnaryNode unaryNode) { - assert unaryNode.rhs().tokenType() != TokenType.CONVERT : "convert(convert encountered. check its origin and remove it"; - return unaryNode; - } - - @Override - public Node leaveDECINC(final UnaryNode unaryNode) { - return specialize(unaryNode).node; - } - - @Override - public Node leaveNEW(final UnaryNode unaryNode) { - assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject(); - return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew()); - } - - @Override - public Node leaveSUB(final UnaryNode unaryNode) { - return leaveUnary(unaryNode); - } - - /** - * Add is a special binary, as it works not only on arithmetic, but for - * strings etc as well. - */ - @Override - public Expression leaveADD(final BinaryNode binaryNode) { - final Expression lhs = binaryNode.lhs(); - final Expression rhs = binaryNode.rhs(); - - final Type type = binaryNode.getType(); - - if (type.isObject()) { - if (!isAddString(binaryNode)) { - return new RuntimeNode(binaryNode, Request.ADD); - } + public Node leaveForNode(final ForNode forNode) { + if (forNode.isForIn()) { + return forNode; } - return binaryNode.setLHS(convert(lhs, type)).setRHS(convert(rhs, type)); - } - - @Override - public Node leaveAND(final BinaryNode binaryNode) { - return binaryNode; - } - - @Override - public Node leaveASSIGN(final BinaryNode binaryNode) { - final SpecializedNode specialized = specialize(binaryNode); - final BinaryNode specBinaryNode = (BinaryNode)specialized.node; - Type destType = specialized.type; - if (destType == null) { - destType = specBinaryNode.getType(); - } - // Register assignments to this object in case this is used as constructor - if (binaryNode.lhs() instanceof AccessNode) { - AccessNode accessNode = (AccessNode) binaryNode.lhs(); - - if (accessNode.getBase().getSymbol().isThis()) { - lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName()); - } - } - return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType)); - } - - @Override - public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } + final Expression init = forNode.getInit(); + final Expression test = forNode.getTest(); + final Expression modify = forNode.getModify(); - @Override - public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - @Override - public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } + assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction(); - @Override - public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { - return leaveASSIGN(binaryNode); - } - - private boolean symbolIsInteger(final Expression node) { - final Symbol symbol = node.getSymbol(); - assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + lc.getCurrentFunction().getSource(); - return true; - } - - @Override - public Node leaveBIT_AND(final BinaryNode binaryNode) { - assert symbolIsInteger(binaryNode); - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveBIT_OR(final BinaryNode binaryNode) { - assert symbolIsInteger(binaryNode); - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveBIT_XOR(final BinaryNode binaryNode) { - assert symbolIsInteger(binaryNode); - return leaveBinary(binaryNode, Type.INT, Type.INT); + return forNode. + setInit(lc, init == null ? null : discard(init)). + setModify(lc, modify == null ? null : discard(modify)); } @Override public Node leaveCOMMALEFT(final BinaryNode binaryNode) { assert binaryNode.getSymbol() != null; - final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs())); - // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed - // in that case, update the node type as well - return propagateType(newBinaryNode, newBinaryNode.lhs().getType()); + return binaryNode.setRHS(discard(binaryNode.rhs())); } @Override public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { assert binaryNode.getSymbol() != null; - final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs())); - // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed - // in that case, update the node type as well - return propagateType(newBinaryNode, newBinaryNode.rhs().getType()); - } - - @Override - public Node leaveDIV(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); - } - - - @Override - public Node leaveEQ(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ); - } - - @Override - public Node leaveEQ_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.EQ_STRICT); - } - - @Override - public Node leaveGE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GE); - } - - @Override - public Node leaveGT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.GT); - } - - @Override - public Node leaveLE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LE); - } - - @Override - public Node leaveLT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.LT); - } - - @Override - public Node leaveMOD(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); - } - - @Override - public Node leaveMUL(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); - } - - @Override - public Node leaveNE(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE); - } - - @Override - public Node leaveNE_STRICT(final BinaryNode binaryNode) { - return leaveCmp(binaryNode, Request.NE_STRICT); - } - - @Override - public Node leaveOR(final BinaryNode binaryNode) { - return binaryNode; - } - - @Override - public Node leaveSAR(final BinaryNode binaryNode) { - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveSHL(final BinaryNode binaryNode) { - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveSHR(final BinaryNode binaryNode) { - assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong() : "long coercion expected: " + binaryNode.getSymbol(); - return leaveBinary(binaryNode, Type.INT, Type.INT); - } - - @Override - public Node leaveSUB(final BinaryNode binaryNode) { - return leaveBinaryArith(binaryNode); + return binaryNode.setLHS(discard(binaryNode.lhs())); } @Override @@ -372,38 +105,12 @@ } @Override - public Node leaveCatchNode(final CatchNode catchNode) { - final Expression exceptionCondition = catchNode.getExceptionCondition(); - if (exceptionCondition != null) { - return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); - } - return catchNode; - } - - @Override public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { temporarySymbols.reuse(); return expressionStatement.setExpression(discard(expressionStatement.getExpression())); } @Override - public Node leaveForNode(final ForNode forNode) { - final Expression init = forNode.getInit(); - final Expression test = forNode.getTest(); - final Expression modify = forNode.getModify(); - - if (forNode.isForIn()) { - return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 - } - assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction(); - - return forNode. - setInit(lc, init == null ? null : discard(init)). - setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)). - setModify(lc, modify == null ? null : discard(modify)); - } - - @Override public boolean enterFunctionNode(final FunctionNode functionNode) { if (functionNode.isLazy()) { return false; @@ -430,113 +137,6 @@ return functionNode.setState(lc, CompilationState.FINALIZED); } - @Override - public Node leaveIfNode(final IfNode ifNode) { - return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN)); - } - - @SuppressWarnings("rawtypes") - @Override - public boolean enterLiteralNode(final LiteralNode literalNode) { - if (literalNode instanceof ArrayLiteralNode) { - final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; - final Expression[] array = arrayLiteralNode.getValue(); - final Type elementType = arrayLiteralNode.getElementType(); - - for (int i = 0; i < array.length; i++) { - final Node element = array[i]; - if (element != null) { - array[i] = convert((Expression)element.accept(this), elementType); - } - } - } - - return false; - } - - @Override - public Node leaveReturnNode(final ReturnNode returnNode) { - final Expression expr = returnNode.getExpression(); - if (expr != null) { - return returnNode.setExpression(convert(expr, lc.getCurrentFunction().getReturnType())); - } - return returnNode; - } - - @Override - public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { - final List args = runtimeNode.getArgs(); - for (final Expression arg : args) { - assert !arg.getType().isUnknown(); - } - return runtimeNode; - } - - @Override - public Node leaveSwitchNode(final SwitchNode switchNode) { - final boolean allInteger = switchNode.getTag().getSymbolType().isInteger(); - - if (allInteger) { - return switchNode; - } - - final Expression expression = switchNode.getExpression(); - final List cases = switchNode.getCases(); - final List newCases = new ArrayList<>(); - - for (final CaseNode caseNode : cases) { - final Expression test = caseNode.getTest(); - newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode); - } - - return switchNode. - setExpression(lc, convert(expression, Type.OBJECT)). - setCases(lc, newCases); - } - - @Override - public Node leaveTernaryNode(final TernaryNode ternaryNode) { - return ternaryNode.setTest(convert(ternaryNode.getTest(), Type.BOOLEAN)); - } - - @Override - public Node leaveThrowNode(final ThrowNode throwNode) { - return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT)); - } - - @Override - public Node leaveVarNode(final VarNode varNode) { - final Expression init = varNode.getInit(); - if (init != null) { - final SpecializedNode specialized = specialize(varNode); - final VarNode specVarNode = (VarNode)specialized.node; - Type destType = specialized.type; - if (destType == null) { - destType = specVarNode.getName().getType(); - } - assert specVarNode.getName().hasType() : specVarNode + " doesn't have a type"; - final Expression convertedInit = convert(init, destType); - temporarySymbols.reuse(); - return specVarNode.setInit(convertedInit); - } - temporarySymbols.reuse(); - return varNode; - } - - @Override - public Node leaveWhileNode(final WhileNode whileNode) { - final Expression test = whileNode.getTest(); - if (test != null) { - return whileNode.setTest(lc, convert(test, Type.BOOLEAN)); - } - return whileNode; - } - - @Override - public Node leaveWithNode(final WithNode withNode) { - return withNode.setExpression(lc, convert(withNode.getExpression(), Type.OBJECT)); - } - private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { if (LOG.isEnabled()) { if (!symbol.isScope()) { @@ -583,260 +183,6 @@ } } - /** - * Exit a comparison node and do the appropriate replacements. We need to introduce runtime - * nodes late for comparisons as types aren't known until the last minute - * - * Both compares and adds may turn into runtimes node at this level as when we first bump - * into the op in Attr, we may type it according to what we know there, which may be wrong later - * - * e.g. i (int) < 5 -> normal compare - * i = object - * then the post pass that would add the conversion to the 5 needs to - * - * @param binaryNode binary node to leave - * @param request runtime request - * @return lowered cmp node - */ - @SuppressWarnings("fallthrough") - private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { - final Expression lhs = binaryNode.lhs(); - final Expression rhs = binaryNode.rhs(); - - Type widest = Type.widest(lhs.getType(), rhs.getType()); - - boolean newRuntimeNode = false, finalized = false; - switch (request) { - case EQ_STRICT: - case NE_STRICT: - if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { - newRuntimeNode = true; - widest = Type.OBJECT; - finalized = true; - } - //fallthru - default: - if (newRuntimeNode || widest.isObject()) { - return new RuntimeNode(binaryNode, request).setIsFinal(finalized); - } - break; - } - - return binaryNode.setLHS(convert(lhs, widest)).setRHS(convert(rhs, widest)); - } - - /** - * Compute the binary arithmetic type given the lhs and an rhs of a binary expression - * @param lhsType the lhs type - * @param rhsType the rhs type - * @return the correct binary type - */ - private static Type binaryArithType(final Type lhsType, final Type rhsType) { - if (!Compiler.shouldUseIntegerArithmetic()) { - return Type.NUMBER; - } - return Type.widest(lhsType, rhsType, Type.NUMBER); - } - - private Node leaveBinaryArith(final BinaryNode binaryNode) { - final Type type = binaryArithType(binaryNode.lhs().getType(), binaryNode.rhs().getType()); - return leaveBinary(binaryNode, type, type); - } - - private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) { - Node b = binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType)); - return b; - } - - /** - * A symbol (and {@link jdk.nashorn.internal.runtime.Property}) can be tagged as "may be primitive". - * This is used a hint for dual fields that it is even worth it to try representing this - * field as something other than java.lang.Object. - * - * @param node node in which to tag symbols as primitive - * @param to which primitive type to use for tagging - */ - private static void setCanBePrimitive(final Node node, final Type to) { - final HashSet exclude = new HashSet<>(); - - node.accept(new NodeVisitor(new LexicalContext()) { - private void setCanBePrimitive(final Symbol symbol) { - LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol)); - symbol.setCanBePrimitive(to); - } - - @Override - public boolean enterIdentNode(final IdentNode identNode) { - if (!exclude.contains(identNode)) { - setCanBePrimitive(identNode.getSymbol()); - } - return false; - } - - @Override - public boolean enterAccessNode(final AccessNode accessNode) { - setCanBePrimitive(accessNode.getProperty().getSymbol()); - return false; - } - - @Override - public boolean enterIndexNode(final IndexNode indexNode) { - exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine - return true; - } - }); - } - - private static class SpecializedNode { - final Node node; - final Type type; - - SpecializedNode(Node node, Type type) { - this.node = node; - this.type = type; - } - } - - SpecializedNode specialize(final Assignment assignment) { - final Node node = ((Node)assignment); - final T lhs = assignment.getAssignmentDest(); - final Expression rhs = assignment.getAssignmentSource(); - - if (!canHaveCallSiteType(lhs)) { - return new SpecializedNode(node, null); - } - - final Type to; - if (node.isSelfModifying()) { - to = node.getWidestOperationType(); - } else { - to = rhs.getType(); - } - - if (!isSupportedCallSiteType(to)) { - //meaningless to specialize to boolean or object - return new SpecializedNode(node, null); - } - - final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to)); - final Node typePropagatedNode; - if(newNode instanceof Expression) { - typePropagatedNode = propagateType((Expression)newNode, to); - } else if(newNode instanceof VarNode) { - // VarNode, being a statement, doesn't have its own symbol; it uses the symbol of its name instead. - final VarNode varNode = (VarNode)newNode; - typePropagatedNode = varNode.setName((IdentNode)propagateType(varNode.getName(), to)); - } else { - throw new AssertionError(); - } - return new SpecializedNode(typePropagatedNode, to); - } - - - /** - * Is this a node that can have its type overridden. This is true for - * AccessNodes, IndexNodes and IdentNodes - * - * @param node the node to check - * @return true if node can have a callsite type - */ - private static boolean canHaveCallSiteType(final Node node) { - return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType(); - } - - /** - * Is the specialization type supported. Currently we treat booleans as objects - * and have no special boolean type accessor, thus booleans are ignored. - * TODO - support booleans? NASHORN-590 - * - * @param castTo the type to check - * @return true if call site type is supported - */ - private static boolean isSupportedCallSiteType(final Type castTo) { - return castTo.isNumeric(); // don't specializable for boolean - } - - /** - * Override the type of a node for e.g. access specialization of scope - * objects. Normally a variable can only get a wider type and narrower type - * sets are ignored. Not that a variable can still be on object type as - * per the type analysis, but a specific access may be narrower, e.g. if it - * is used in an arithmetic op. This overrides a type, regardless of - * type environment and is used primarily by the access specializer - * - * @param node node for which to change type - * @param to new type - */ - @SuppressWarnings("unchecked") - T setTypeOverride(final T node, final Type to) { - final Type from = node.getType(); - if (!node.getType().equals(to)) { - LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to); - if (!to.isObject() && from.isObject()) { - setCanBePrimitive(node, to); - } - } - LOG.info("Type override for lhs in '", node, "' => ", to); - return ((TypeOverride)node).setType(temporarySymbols, lc, to); - } - - /** - * Add an explicit conversion. This is needed when attribution has created types - * that do not mesh into an op type, e.g. a = b, where b is object and a is double - * at the end of Attr, needs explicit conversion logic. - * - * An explicit conversion can be one of the following: - * + Convert a literal - just replace it with another literal - * + Convert a scope object - just replace the type of the access, e.g. get()D->get()I - * + Explicit convert placement, e.g. a = (double)b - all other cases - * - * No other part of the world after {@link Attr} may introduce new symbols. This - * is the only place. - * - * @param node node to convert - * @param to destination type - * @return conversion node - */ - private Expression convert(final Expression node, final Type to) { - assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass(); - assert node != null : "node is null"; - assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + lc.getCurrentFunction(); - assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + lc.getCurrentFunction(); - - final Type from = node.getType(); - - if (Type.areEquivalent(from, to)) { - return node; - } - - if (from.isObject() && to.isObject()) { - return node; - } - - Expression resultNode = node; - - if (node instanceof LiteralNode && !(node instanceof ArrayLiteralNode) && !to.isObject()) { - final LiteralNode newNode = new LiteralNodeConstantEvaluator((LiteralNode)node, to).eval(); - if (newNode != null) { - resultNode = newNode; - } - } else { - if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) { - assert node instanceof TypeOverride; - return setTypeOverride(node, to); - } - resultNode = new UnaryNode(Token.recast(node.getToken(), TokenType.CONVERT), node); - } - - LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'"); - - assert !node.isTerminal(); - - //This is the only place in this file that can create new temporaries - //FinalizeTypes may not introduce ANY node that is not a conversion. - return temporarySymbols.ensureSymbol(lc, to, resultNode); - } - private static Expression discard(final Expression node) { if (node.getSymbol() != null) { final UnaryNode discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node); @@ -849,82 +195,5 @@ return node; } - /** - * Whenever an expression like an addition or an assignment changes type, it - * may be that case that {@link Attr} created a symbol for an intermediate - * result of the expression, say for an addition. This also has to be updated - * if the expression type changes. - * - * Assignments use their lhs as node symbol, and in this case we can't modify - * it. Then {@link CodeGenerator.Store} needs to do an explicit conversion. - * This is happens very rarely. - * - * @param node - * @param to - */ - private Expression propagateType(final Expression node, final Type to) { - Symbol symbol = node.getSymbol(); - if (symbol.isTemp() && symbol.getSymbolType() != to) { - symbol = symbol.setTypeOverrideShared(to, temporarySymbols); - LOG.info("Type override for temporary in '", node, "' => ", to); - } - return node.setSymbol(lc, symbol); - } - /** - * Determine if the outcome of + operator is a string. - * - * @param node Node to test. - * @return true if a string result. - */ - private boolean isAddString(final Node node) { - if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) { - final BinaryNode binaryNode = (BinaryNode)node; - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); - - return isAddString(lhs) || isAddString(rhs); - } - - return node instanceof LiteralNode && ((LiteralNode)node).isString(); - } - - /** - * Whenever an explicit conversion is needed and the convertee is a literal, we can - * just change the literal - */ - class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator> { - private final Type type; - - LiteralNodeConstantEvaluator(final LiteralNode parent, final Type type) { - super(parent); - this.type = type; - } - - @Override - protected LiteralNode eval() { - final Object value = ((LiteralNode)parent).getValue(); - - LiteralNode literalNode = null; - - if (type.isString()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toString(value)); - } else if (type.isBoolean()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toBoolean(value)); - } else if (type.isInteger()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toInt32(value)); - } else if (type.isLong()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toLong(value)); - } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) { - literalNode = LiteralNode.newInstance(token, finish, JSType.toNumber(value)); - } - - if (literalNode != null) { - //inherit literal symbol for attr. - literalNode = (LiteralNode)literalNode.setSymbol(lc, parent.getSymbol()); - } - - return literalNode; - } - } } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/MethodEmitter.java --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Oct 09 17:53:22 2013 +0200 @@ -69,7 +69,6 @@ import java.lang.reflect.Array; import java.util.EnumSet; import java.util.List; - import jdk.internal.dynalink.support.NameCodec; import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.MethodVisitor; @@ -1560,7 +1559,7 @@ MethodEmitter convert(final Type to) { final Type type = peekType().convert(method, to); if (type != null) { - if (peekType() != to) { + if (!peekType().isEquivalentTo(to)) { debug("convert", peekType(), "->", to); } popType(); @@ -1790,15 +1789,14 @@ * @param name name of property * @param flags call site flags */ - void dynamicSet(final Type valueType, final String name, final int flags) { + void dynamicSet(final String name, final int flags) { debug("dynamic_set", name, peekType()); - Type type = valueType; - if (type.isObject() || type.isBoolean()) { //promote strings to objects etc + Type type = peekType(); + if (type.isObject()) { //promote strings to objects etc type = Type.OBJECT; convert(Type.OBJECT); //TODO bad- until we specialize boolean setters, } - popType(type); popType(Type.SCOPE); diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/WeighNodes.java --- a/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed Oct 09 17:53:22 2013 +0200 @@ -297,11 +297,6 @@ } @Override - public Node leaveCONVERT(final UnaryNode unaryNode) { - return unaryNodeWeight(unaryNode); - } - - @Override public Node leaveDECINC(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/types/BooleanType.java --- a/src/jdk/nashorn/internal/codegen/types/BooleanType.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/types/BooleanType.java Wed Oct 09 17:53:22 2013 +0200 @@ -136,8 +136,7 @@ invokeStatic(method, JSType.TO_LONG); } else if (to.isString()) { invokeStatic(method, VALUE_OF); - invokeStatic(method, JSType.TO_PRIMITIVE); - invokeStatic(method, JSType.TO_STRING); + invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING); } else if (to.isObject()) { invokeStatic(method, VALUE_OF); } else { diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/types/ObjectType.java --- a/src/jdk/nashorn/internal/codegen/types/ObjectType.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/types/ObjectType.java Wed Oct 09 17:53:22 2013 +0200 @@ -153,8 +153,7 @@ } else if (to.isBoolean()) { invokeStatic(method, JSType.TO_BOOLEAN); } else if (to.isString()) { - invokeStatic(method, JSType.TO_PRIMITIVE); - invokeStatic(method, JSType.TO_STRING); + invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING); } else { assert false : "Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString; } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/codegen/types/Type.java --- a/src/jdk/nashorn/internal/codegen/types/Type.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/codegen/types/Type.java Wed Oct 09 17:53:22 2013 +0200 @@ -441,7 +441,12 @@ if (type0.isArray() && type1.isArray()) { return ((ArrayType)type0).getElementType() == ((ArrayType)type1).getElementType() ? type0 : Type.OBJECT; } else if (type0.isArray() != type1.isArray()) { - return Type.OBJECT; //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense + //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense + return Type.OBJECT; + } else if (type0.isObject() && type1.isObject() && ((ObjectType)type0).getTypeClass() != ((ObjectType)type1).getTypeClass()) { + // Object and Object will produce Object + // TODO: maybe find most specific common superclass? + return Type.OBJECT; } return type0.weight() > type1.weight() ? type0 : type1; } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/AccessNode.java --- a/src/jdk/nashorn/internal/ir/AccessNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/AccessNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -25,7 +25,6 @@ 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; @@ -46,12 +45,12 @@ * @param property property */ public AccessNode(final long token, final int finish, final Expression base, final IdentNode property) { - super(token, finish, base, false, false); + super(token, finish, base, false); this.property = property.setIsPropertyName(); } - private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) { - super(accessNode, base, isFunction, hasCallSiteType); + private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction) { + super(accessNode, base, isFunction); this.property = property; } @@ -73,13 +72,6 @@ public void toString(final StringBuilder sb) { final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true); - if (hasCallSiteType()) { - sb.append('{'); - final String desc = getType().getDescriptor(); - sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor()); - sb.append('}'); - } - if (needsParen) { sb.append('('); } @@ -107,21 +99,14 @@ if (this.base == base) { return this; } - return new AccessNode(this, base, property, isFunction(), hasCallSiteType()); + return new AccessNode(this, base, property, isFunction()); } private AccessNode setProperty(final IdentNode property) { if (this.property == property) { return this; } - return new AccessNode(this, base, property, isFunction(), hasCallSiteType()); - } - - @Override - public AccessNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - logTypeChange(type); - final AccessNode newAccessNode = (AccessNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts)); - return new AccessNode(newAccessNode, base, property.setType(ts, lc, type), isFunction(), hasCallSiteType()); + return new AccessNode(this, base, property, isFunction()); } @Override @@ -129,7 +114,7 @@ if (isFunction()) { return this; } - return new AccessNode(this, base, property, true, hasCallSiteType()); + return new AccessNode(this, base, property, true); } } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/BaseNode.java --- a/src/jdk/nashorn/internal/ir/BaseNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/BaseNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -25,10 +25,6 @@ package jdk.nashorn.internal.ir; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; - -import jdk.nashorn.internal.codegen.ObjectClassGenerator; -import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; /** @@ -38,15 +34,13 @@ * @see IndexNode */ @Immutable -public abstract class BaseNode extends Expression implements FunctionCall, TypeOverride { +public abstract class BaseNode extends Expression implements FunctionCall { /** Base Node. */ protected final Expression base; private final boolean isFunction; - private final boolean hasCallSiteType; - /** * Constructor * @@ -54,13 +48,11 @@ * @param finish finish * @param base base node * @param isFunction is this a function - * @param hasCallSiteType does this access have a callsite type */ - public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction, final boolean hasCallSiteType) { + public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction) { super(token, base.getStart(), finish); this.base = base; this.isFunction = isFunction; - this.hasCallSiteType = hasCallSiteType; } /** @@ -68,13 +60,11 @@ * @param baseNode node to inherit from * @param base base * @param isFunction is this a function - * @param hasCallSiteType does this access have a callsite type */ - protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final boolean hasCallSiteType) { + protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction) { super(baseNode); this.base = base; this.isFunction = isFunction; - this.hasCallSiteType = hasCallSiteType; } /** @@ -96,26 +86,4 @@ */ public abstract BaseNode setIsFunction(); - @Override - public boolean canHaveCallSiteType() { - return true; //carried by the symbol and always the same nodetype==symboltype - } - - /** - * Does the access have a call site type override? - * @return true if overridden - */ - protected boolean hasCallSiteType() { - return hasCallSiteType; - } - - /** - * Debug type change - * @param type new type - */ - protected final void logTypeChange(final Type type) { - if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { - ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType()); - } - } } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/CallNode.java --- a/src/jdk/nashorn/internal/ir/CallNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/CallNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -36,9 +36,7 @@ * IR representation for a function call. */ @Immutable -public final class CallNode extends LexicalContextExpression implements TypeOverride { - - private final Type type; +public final class CallNode extends LexicalContextExpression { /** Function identifier or function body. */ private final Expression function; @@ -150,18 +148,16 @@ this.function = function; this.args = args; this.flags = 0; - this.type = null; this.evalArgs = null; this.lineNumber = lineNumber; } - private CallNode(final CallNode callNode, final Expression function, final List args, final int flags, final Type type, final EvalArgs evalArgs) { + private CallNode(final CallNode callNode, final Expression function, final List args, final int flags, final EvalArgs evalArgs) { super(callNode); this.lineNumber = callNode.lineNumber; this.function = function; this.args = args; this.flags = flags; - this.type = type; this.evalArgs = evalArgs; } @@ -175,29 +171,9 @@ @Override public Type getType() { - if (hasCallSiteType()) { - return type; - } return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT; } - @Override - public CallNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - if (this.type == type) { - return this; - } - return new CallNode(this, function, args, flags, type, evalArgs); - } - - private boolean hasCallSiteType() { - return this.type != null; - } - - @Override - public boolean canHaveCallSiteType() { - return true; - } - /** * Assist in IR navigation. * @@ -212,7 +188,6 @@ setFunction((Expression)function.accept(visitor)). setArgs(Node.accept(visitor, Expression.class, args)). setFlags(flags). - setType(null, lc, type). setEvalArgs(evalArgs == null ? null : evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)). @@ -229,13 +204,6 @@ @Override public void toString(final StringBuilder sb) { - if (hasCallSiteType()) { - sb.append('{'); - final String desc = getType().getDescriptor(); - sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor()); - sb.append('}'); - } - function.toString(sb); sb.append('('); @@ -271,7 +239,7 @@ if (this.args == args) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } /** @@ -293,7 +261,7 @@ if (this.evalArgs == evalArgs) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } /** @@ -321,7 +289,7 @@ if (this.function == function) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } /** @@ -344,6 +312,6 @@ if (this.flags == flags) { return this; } - return new CallNode(this, function, args, flags, type, evalArgs); + return new CallNode(this, function, args, flags, evalArgs); } } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/IdentNode.java --- a/src/jdk/nashorn/internal/ir/IdentNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/IdentNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -28,9 +28,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__; import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__; import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; -import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -39,7 +37,7 @@ * IR representation for an identifier. */ @Immutable -public final class IdentNode extends Expression implements PropertyKey, TypeOverride, FunctionCall { +public final class IdentNode extends Expression implements PropertyKey, FunctionCall { private static final int PROPERTY_NAME = 1 << 0; private static final int INITIALIZED_HERE = 1 << 1; private static final int FUNCTION = 1 << 2; @@ -101,19 +99,6 @@ return callSiteType != null; } - @Override - public IdentNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't - if (this.callSiteType == type) { - return this; - } - if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { - ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType()); - } - - return new IdentNode(this, name, type, flags); - } - /** * Assist in IR navigation. * @@ -154,31 +139,6 @@ } /** - * We can only override type if the symbol lives in the scope, as otherwise - * it is strongly determined by the local variable already allocated. - * - *

We also return true if the symbol represents the return value of a function with a - * non-generic return type as in this case we need to propagate the type instead of - * converting to object, for example if the symbol is used as the left hand side of an - * assignment such as in the code below.

- * - *
-     *   try {
-     *     return 2;
-     *   } finally {
-     *     return 3;
-     *   }
-     * }
-     * 
- * - * @return true if can have callsite type - */ - @Override - public boolean canHaveCallSiteType() { - return getSymbol() != null && (getSymbol().isScope() || getSymbol().isNonGenericReturn()); - } - - /** * Check if this IdentNode is a property name * @return true if this is a property name */ diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/IndexNode.java --- a/src/jdk/nashorn/internal/ir/IndexNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/IndexNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -25,7 +25,6 @@ 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; @@ -46,12 +45,12 @@ * @param index index for access */ public IndexNode(final long token, final int finish, final Expression base, final Expression index) { - super(token, finish, base, false, false); + super(token, finish, base, false); this.index = index; } - private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final boolean hasCallSiteType) { - super(indexNode, base, isFunction, hasCallSiteType); + private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction) { + super(indexNode, base, isFunction); this.index = index; } @@ -69,13 +68,6 @@ public void toString(final StringBuilder sb) { final boolean needsParen = tokenType().needsParens(base.tokenType(), true); - if (hasCallSiteType()) { - sb.append('{'); - final String desc = getType().getDescriptor(); - sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor()); - sb.append('}'); - } - if (needsParen) { sb.append('('); } @@ -103,7 +95,7 @@ if (this.base == base) { return this; } - return new IndexNode(this, base, index, isFunction(), hasCallSiteType()); + return new IndexNode(this, base, index, isFunction()); } /** @@ -115,7 +107,7 @@ if(this.index == index) { return this; } - return new IndexNode(this, base, index, isFunction(), hasCallSiteType()); + return new IndexNode(this, base, index, isFunction()); } @Override @@ -123,14 +115,7 @@ if (isFunction()) { return this; } - return new IndexNode(this, base, index, true, hasCallSiteType()); - } - - @Override - public IndexNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - logTypeChange(type); - final IndexNode newIndexNode = (IndexNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts)); - return new IndexNode(newIndexNode, base, index, isFunction(), true); + return new IndexNode(this, base, index, true); } } diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/LiteralNode.java --- a/src/jdk/nashorn/internal/ir/LiteralNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -28,10 +28,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; + import jdk.nashorn.internal.codegen.CompileUnit; +import jdk.nashorn.internal.codegen.types.ArrayType; 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.objects.NativeArray; import jdk.nashorn.internal.parser.Lexer.LexerToken; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.TokenType; @@ -526,12 +529,6 @@ return object; } else if (object instanceof LiteralNode) { return objectAsConstant(((LiteralNode)object).getValue()); - } else if (object instanceof UnaryNode) { - final UnaryNode unaryNode = (UnaryNode)object; - - if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) { - return objectAsConstant(unaryNode.rhs()); - } } return POSTSET_MARKER; @@ -782,8 +779,7 @@ return value; } - @Override - public Type getType() { + public ArrayType getArrayType() { if (elementType.isInteger()) { return Type.INT_ARRAY; } else if (elementType.isLong()) { @@ -795,6 +791,11 @@ } } + @Override + public Type getType() { + return Type.typeFor(NativeArray.class); + } + /** * Get the element type of this array literal * @return element type diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/RuntimeNode.java --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -38,7 +38,7 @@ * IR representation for a runtime call. */ @Immutable -public class RuntimeNode extends Expression implements TypeOverride { +public class RuntimeNode extends Expression { /** * Request enum used for meta-information about the runtime request @@ -159,6 +159,36 @@ } /** + * Derive a runtime node request type for a node + * @param node the node + * @return request type + */ + public static Request requestFor(final Node node) { + assert node.isComparison(); + switch (node.tokenType()) { + case EQ_STRICT: + return Request.EQ_STRICT; + case NE_STRICT: + return Request.NE_STRICT; + case EQ: + return Request.EQ; + case NE: + return Request.NE; + case LT: + return Request.LT; + case LE: + return Request.LE; + case GT: + return Request.GT; + case GE: + return Request.GE; + default: + assert false; + return null; + } + } + + /** * Is this an EQ or EQ_STRICT? * * @param request a request @@ -268,9 +298,6 @@ /** Call arguments. */ private final List args; - /** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */ - private final Type callSiteType; - /** is final - i.e. may not be removed again, lower in the code pipeline */ private final boolean isFinal; @@ -287,16 +314,14 @@ this.request = request; this.args = args; - this.callSiteType = null; this.isFinal = false; } - private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final Type callSiteType, final boolean isFinal, final List args) { + private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List args) { super(runtimeNode); this.request = request; this.args = args; - this.callSiteType = callSiteType; this.isFinal = isFinal; } @@ -335,7 +360,6 @@ this.request = request; this.args = args; - this.callSiteType = null; this.isFinal = false; } @@ -376,7 +400,7 @@ if (this.isFinal == isFinal) { return this; } - return new RuntimeNode(this, request, callSiteType, isFinal, args); + return new RuntimeNode(this, request, isFinal, args); } /** @@ -384,24 +408,7 @@ */ @Override public Type getType() { - return hasCallSiteType() ? callSiteType : request.getReturnType(); - } - - @Override - public RuntimeNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) { - if (this.callSiteType == type) { - return this; - } - return new RuntimeNode(this, request, type, isFinal, args); - } - - @Override - public boolean canHaveCallSiteType() { - return request == Request.ADD; - } - - private boolean hasCallSiteType() { - return callSiteType != null; + return request.getReturnType(); } @Override @@ -450,7 +457,7 @@ if (this.args == args) { return this; } - return new RuntimeNode(this, request, callSiteType, isFinal, args); + return new RuntimeNode(this, request, isFinal, args); } /** diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/TypeOverride.java --- a/src/jdk/nashorn/internal/ir/TypeOverride.java Wed Oct 09 14:50:39 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.ir; - -import jdk.nashorn.internal.codegen.types.Type; - -/** - * A type override makes it possible to change the return type of a node, if we know - * that the linker can provide it directly. For example, an identity node that is - * in the scope, can very well look like an object to the compiler of the method it - * is in, but if someone does (int)x, it make senses to ask for it directly - * with an int getter instead of loading it as an object and explicitly converting it - * by using JSType.toInt32. Especially in scenarios where the field is already stored - * as a primitive, this will be much faster than the "object is all I see" scope - * available in the method - * @param the type of the node implementing the interface - */ - -public interface TypeOverride { - /** - * Set the override type - * - * @param ts temporary symbols - * @param lc the current lexical context - * @param type the type - * @return a node equivalent to this one except for the requested change. - */ - public T setType(final TemporarySymbols ts, final LexicalContext lc, final Type type); - - /** - * Returns true if this node can have a callsite override, e.g. all scope ident nodes - * which lead to dynamic getters can have it, local variable nodes (slots) can't. - * Call nodes can have it unconditionally and so on - * - * @return true if it is possible to assign a type override to this node - */ - public boolean canHaveCallSiteType(); - -} diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/UnaryNode.java --- a/src/jdk/nashorn/internal/ir/UnaryNode.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/UnaryNode.java Wed Oct 09 17:53:22 2013 +0200 @@ -26,7 +26,6 @@ package jdk.nashorn.internal.ir; import static jdk.nashorn.internal.parser.TokenType.BIT_NOT; -import static jdk.nashorn.internal.parser.TokenType.CONVERT; import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; @@ -150,19 +149,10 @@ final TokenType type = tokenType(); final String name = type.getName(); final boolean isPostfix = type == DECPOSTFIX || type == INCPOSTFIX; - final boolean isConvert = type == CONVERT && getSymbol() != null; boolean rhsParen = type.needsParens(rhs().tokenType(), false); - int convertPos = 0; - if (isConvert) { - convertPos = sb.length(); - sb.append("("); - sb.append(getType()); - sb.append(")("); - } - - if (!isPostfix && !isConvert) { + if (!isPostfix) { if (name == null) { sb.append(type.name()); rhsParen = true; @@ -186,16 +176,6 @@ if (isPostfix) { sb.append(type == DECPOSTFIX ? "--" : "++"); } - - if (isConvert) { - // strip extra cast parenthesis which makes the printout harder to read - final boolean endsWithParenthesis = sb.charAt(sb.length() - 1) == ')'; - if (!endsWithParenthesis) { - sb.append(')'); - } else { - sb.setCharAt(convertPos, ' '); - } - } } /** diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java --- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Wed Oct 09 17:53:22 2013 +0200 @@ -51,8 +51,6 @@ return enterADD(unaryNode); case BIT_NOT: return enterBIT_NOT(unaryNode); - case CONVERT: - return enterCONVERT(unaryNode); case DELETE: return enterDELETE(unaryNode); case DISCARD: @@ -84,8 +82,6 @@ return leaveADD(unaryNode); case BIT_NOT: return leaveBIT_NOT(unaryNode); - case CONVERT: - return leaveCONVERT(unaryNode); case DELETE: return leaveDELETE(unaryNode); case DISCARD: @@ -323,26 +319,6 @@ } /** - * Unary enter - callback for entering a conversion - * - * @param unaryNode the node - * @return true if traversal should continue and node children be traversed, false otherwise - */ - public boolean enterCONVERT(final UnaryNode unaryNode) { - return enterDefault(unaryNode); - } - - /** - * Unary leave - callback for leaving a conversion - * - * @param unaryNode the node - * @return processed node, which will replace the original one, or the original node - */ - public Node leaveCONVERT(final UnaryNode unaryNode) { - return leaveDefault(unaryNode); - } - - /** * Unary enter - callback for entering a ++ or -- operator * * @param unaryNode the node diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/parser/TokenType.java --- a/src/jdk/nashorn/internal/parser/TokenType.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/parser/TokenType.java Wed Oct 09 17:53:22 2013 +0200 @@ -178,7 +178,6 @@ ARRAY (LITERAL, null), COMMALEFT (IR, null), - CONVERT (IR, null), DISCARD (IR, null), DECPOSTFIX (IR, null), INCPOSTFIX (IR, null); diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/runtime/Context.java --- a/src/jdk/nashorn/internal/runtime/Context.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/Context.java Wed Oct 09 17:53:22 2013 +0200 @@ -625,11 +625,11 @@ * @param clazz Class object * @throw SecurityException if not accessible */ - public static void checkPackageAccess(final Class clazz) { + public static void checkPackageAccess(final Class clazz) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { - Class bottomClazz = clazz; - while(bottomClazz.isArray()) { + Class bottomClazz = clazz; + while (bottomClazz.isArray()) { bottomClazz = bottomClazz.getComponentType(); } checkPackageAccess(sm, bottomClazz.getName()); @@ -664,7 +664,7 @@ * @param clazz Class object * @return true if package is accessible, false otherwise */ - private static boolean isAccessiblePackage(final Class clazz) { + private static boolean isAccessiblePackage(final Class clazz) { try { checkPackageAccess(clazz); return true; @@ -838,7 +838,7 @@ return Context.getContextTrusted(); } - private URL getResourceURL(final String resName) throws IOException { + private URL getResourceURL(final String resName) { // try the classPathLoader if we have and then // try the appLoader if non-null. if (classPathLoader != null) { diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/runtime/JSType.java --- a/src/jdk/nashorn/internal/runtime/JSType.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Wed Oct 09 17:53:22 2013 +0200 @@ -104,14 +104,11 @@ /** JavaScript compliant conversion function from number to int64 */ public static final Call TO_INT64_D = staticCall(myLookup, JSType.class, "toInt64", long.class, double.class); - /** JavaScript compliant conversion function from Object to String */ - public static final Call TO_STRING = staticCall(myLookup, JSType.class, "toString", String.class, Object.class); - /** JavaScript compliant conversion function from number to String */ public static final Call TO_STRING_D = staticCall(myLookup, JSType.class, "toString", String.class, double.class); - /** JavaScript compliant conversion function from Object to primitive */ - public static final Call TO_PRIMITIVE = staticCall(myLookup, JSType.class, "toPrimitive", Object.class, Object.class); + /** Combined call to toPrimitive followed by toString. */ + public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class, Object.class); private static final double INT32_LIMIT = 4294967296.0; @@ -273,6 +270,17 @@ } /** + * Combines a hintless toPrimitive and a toString call. + * + * @param obj an object + * + * @return the string form of the primitive form of the object + */ + public static String toPrimitiveToString(Object obj) { + return toString(toPrimitive(obj)); + } + + /** * JavaScript compliant conversion of number to boolean * * @param num a number @@ -874,7 +882,7 @@ if (obj instanceof ScriptObject) { return convertArray(((ScriptObject)obj).getArray().asObjectArray(), componentType); } else if (obj instanceof JSObject) { - final ArrayLikeIterator itr = ArrayLikeIterator.arrayLikeIterator(obj); + final ArrayLikeIterator itr = ArrayLikeIterator.arrayLikeIterator(obj); final int len = (int) itr.getLength(); final Object[] res = new Object[len]; int idx = 0; diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/runtime/arrays/JavaArrayIterator.java --- a/src/jdk/nashorn/internal/runtime/arrays/JavaArrayIterator.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/arrays/JavaArrayIterator.java Wed Oct 09 17:53:22 2013 +0200 @@ -77,4 +77,4 @@ public void remove() { throw new UnsupportedOperationException("remove"); } -} \ No newline at end of file +} diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/runtime/arrays/ReverseJavaArrayIterator.java --- a/src/jdk/nashorn/internal/runtime/arrays/ReverseJavaArrayIterator.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/arrays/ReverseJavaArrayIterator.java Wed Oct 09 17:53:22 2013 +0200 @@ -55,4 +55,4 @@ protected long bumpIndex() { return index--; } -} \ No newline at end of file +} diff -r ec3094d9d5d5 -r 03a68e7ca1d5 src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Wed Oct 09 14:50:39 2013 +0200 +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Wed Oct 09 17:53:22 2013 +0200 @@ -140,7 +140,7 @@ @SuppressWarnings("unused") private static Object get(final Object jsobj, final Object key) { if (key instanceof Integer) { - return ((JSObject)jsobj).getSlot((int)(Integer)key); + return ((JSObject)jsobj).getSlot((Integer)key); } else if (key instanceof Number) { final int index = getIndex((Number)key); if (index > -1) { @@ -155,7 +155,7 @@ @SuppressWarnings("unused") private static void put(final Object jsobj, final Object key, final Object value) { if (key instanceof Integer) { - ((JSObject)jsobj).setSlot((int)(Integer)key, value); + ((JSObject)jsobj).setSlot((Integer)key, value); } else if (key instanceof Number) { ((JSObject)jsobj).setSlot(getIndex((Number)key), value); } else if (key instanceof String) { diff -r ec3094d9d5d5 -r 03a68e7ca1d5 test/script/basic/JDK-8026137.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8026137.js Wed Oct 09 17:53:22 2013 +0200 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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-8026137: Binary evaluation order in JavaScript is load load + * convert convert, not load convert load convert. + * + * @test + * @run + */ + +try { + (function f() { Object.defineProperty({},"x",{get: function(){return {valueOf:function(){throw 0}}}}).x - Object.defineProperty({},"x",{get: function(){throw 1}}).x })() +} +catch (e) { + print(e); +} + +try { + ({valueOf: function(){throw 0}}) - ({valueOf: function(){throw 1}} - 1) +} catch (e) { + print(e); +} + diff -r ec3094d9d5d5 -r 03a68e7ca1d5 test/script/basic/JDK-8026137.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8026137.js.EXPECTED Wed Oct 09 17:53:22 2013 +0200 @@ -0,0 +1,2 @@ +1 +1