From 9ad712ca825640ee64a8f1f9bee1d367dc1b8f26 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 1 Dec 2014 18:08:40 +0000 Subject: 8057629: Third Party License Readme update for 8u40 Reviewed-by: tbell --- THIRD_PARTY_README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/THIRD_PARTY_README b/THIRD_PARTY_README index 6d1c60f2..c34ce6b3 100644 --- a/THIRD_PARTY_README +++ b/THIRD_PARTY_README @@ -3385,7 +3385,7 @@ with JRE 8, JDK 8, and OpenJDK 8. included with JRE 8, JDK 8, and OpenJDK 8. Apache Commons Math 3.2 - Apache Derby 10.10.1.3 + Apache Derby 10.11.1.2 Apache Jakarta BCEL 5.1 Apache Jakarta Regexp 1.4 Apache Santuario XML Security for Java 1.5.4 -- cgit v1.2.3 From 43eada765f54df8725d79f2ccde71d3c4cd7cdf3 Mon Sep 17 00:00:00 2001 From: hannesw Date: Wed, 3 Dec 2014 14:49:36 +0100 Subject: 8065769: OOM on Window/Solaris in test compile-octane-splitter.js Reviewed-by: sundar, jlaskey --- src/jdk/nashorn/internal/codegen/AstSerializer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jdk/nashorn/internal/codegen/AstSerializer.java b/src/jdk/nashorn/internal/codegen/AstSerializer.java index 19197a26..dc35f964 100644 --- a/src/jdk/nashorn/internal/codegen/AstSerializer.java +++ b/src/jdk/nashorn/internal/codegen/AstSerializer.java @@ -48,11 +48,13 @@ final class AstSerializer { private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); static byte[] serialize(final FunctionNode fn) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, - new Deflater(COMPRESSION_LEVEL)))) { + final Deflater deflater = new Deflater(COMPRESSION_LEVEL); + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) { oout.writeObject(removeInnerFunctionBodies(fn)); } catch (final IOException e) { throw new AssertionError("Unexpected exception serializing function", e); + } finally { + deflater.end(); } return out.toByteArray(); } -- cgit v1.2.3 From c953a34fa1cc15c71119a4b1268ccaf994abee86 Mon Sep 17 00:00:00 2001 From: attila Date: Mon, 8 Dec 2014 15:13:16 +0100 Subject: 8066230: Undefined object type assertion when computing TypeBounds Reviewed-by: hannesw, lagergren --- .../nashorn/internal/codegen/CodeGenerator.java | 25 +++++++++------- test/script/basic/JDK-8066230.js | 34 ++++++++++++++++++++++ test/script/basic/JDK-8066230.js.EXPECTED | 1 + 3 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 test/script/basic/JDK-8066230.js create mode 100644 test/script/basic/JDK-8066230.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 1bf3b382..360410ae 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -571,9 +571,11 @@ final class CodeGenerator extends NodeOperatorVisitor Date: Mon, 8 Dec 2014 15:14:11 +0100 Subject: 8066227: CodeGenerator load unitialized slot Reviewed-by: hannesw, lagergren --- .../nashorn/internal/codegen/CodeGenerator.java | 3 +- .../codegen/LocalVariableTypesCalculator.java | 159 ++++++++++++++++----- src/jdk/nashorn/internal/ir/BinaryNode.java | 5 +- test/script/basic/JDK-8066227.js | 40 ++++++ test/script/basic/JDK-8066227.js.EXPECTED | 10 ++ 5 files changed, 173 insertions(+), 44 deletions(-) create mode 100644 test/script/basic/JDK-8066227.js create mode 100644 test/script/basic/JDK-8066227.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 360410ae..7bb0da3f 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -3697,8 +3697,7 @@ final class CodeGenerator extends NodeOperatorVisitor{ @Override public boolean enterBinaryNode(final BinaryNode binaryNode) { + // NOTE: regardless of operator's lexical associativity, lhs is always evaluated first. final Expression lhs = binaryNode.lhs(); - final Expression rhs = binaryNode.rhs(); final boolean isAssignment = binaryNode.isAssignment(); - - final TokenType tokenType = Token.descType(binaryNode.getToken()); - if(tokenType.isLeftAssociative()) { - assert !isAssignment; - final boolean isLogical = binaryNode.isLogical(); - final Label joinLabel = isLogical ? new Label("") : null; - lhs.accept(this); - if(isLogical) { - jumpToLabel((JoinPredecessor)lhs, joinLabel); - } - rhs.accept(this); - if(isLogical) { - jumpToLabel((JoinPredecessor)rhs, joinLabel); - } - joinOnLabel(joinLabel); - } else { - rhs.accept(this); - if(isAssignment) { - if(lhs instanceof BaseNode) { - ((BaseNode)lhs).getBase().accept(this); - if(lhs instanceof IndexNode) { - ((IndexNode)lhs).getIndex().accept(this); - } else { - assert lhs instanceof AccessNode; - } + LvarType lhsTypeOnLoad = null; + if(isAssignment) { + if(lhs instanceof BaseNode) { + ((BaseNode)lhs).getBase().accept(this); + if(lhs instanceof IndexNode) { + ((IndexNode)lhs).getIndex().accept(this); } else { - assert lhs instanceof IdentNode; - if(binaryNode.isSelfModifying()) { - ((IdentNode)lhs).accept(this); - } + assert lhs instanceof AccessNode; } } else { - lhs.accept(this); + assert lhs instanceof IdentNode; + if(binaryNode.isSelfModifying()) { + final IdentNode ident = ((IdentNode)lhs); + ident.accept(this); + // Self-assignment can cause a change in the type of the variable. For purposes of evaluating + // the type of the operation, we must use its type as it was when it was loaded. If we didn't + // do this, some awkward expressions would end up being calculated incorrectly, e.g. + // "var x; x += x = 0;". In this case we have undefined+int so the result type is double (NaN). + // However, if we used the type of "x" on LHS after we evaluated RHS, we'd see int+int, so the + // result type would be either optimistic int or pessimistic long, which would be wrong. + lhsTypeOnLoad = getLocalVariableTypeIfBytecode(ident.getSymbol()); + } } + } else { + lhs.accept(this); + } + + final boolean isLogical = binaryNode.isLogical(); + assert !(isAssignment && isLogical); // there are no logical assignment operators in JS + final Label joinLabel = isLogical ? new Label("") : null; + if(isLogical) { + jumpToLabel((JoinPredecessor)lhs, joinLabel); + } + + final Expression rhs = binaryNode.rhs(); + rhs.accept(this); + if(isLogical) { + jumpToLabel((JoinPredecessor)rhs, joinLabel); } + joinOnLabel(joinLabel); if(isAssignment && lhs instanceof IdentNode) { if(binaryNode.isSelfModifying()) { - onSelfAssignment((IdentNode)lhs, binaryNode); + onSelfAssignment((IdentNode)lhs, binaryNode, lhsTypeOnLoad); } else { onAssignment((IdentNode)lhs, rhs); } @@ -919,7 +924,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ if(unaryNode.isSelfModifying()) { if(expr instanceof IdentNode) { - onSelfAssignment((IdentNode)expr, unaryNode); + final IdentNode ident = (IdentNode)expr; + onSelfAssignment(ident, unaryNode, getLocalVariableTypeIfBytecode(ident.getSymbol())); } } return false; @@ -973,12 +979,41 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return types; } + /** + * Returns the current type of the local variable represented by the symbol. This is the most strict of all + * {@code getLocalVariableType*} methods, as it will throw an assertion if the type is null. Therefore, it is only + * safe to be invoked on symbols known to be bytecode locals, and only after they have been initialized. + * Regardless, it is recommended to use this method in majority of cases, as because of its strictness it is the + * best suited for catching missing type calculation bugs early. + * @param symbol a symbol representing a bytecode local variable. + * @return the current type of the local variable represented by the symbol + */ private LvarType getLocalVariableType(final Symbol symbol) { final LvarType type = getLocalVariableTypeOrNull(symbol); assert type != null; return type; } + /** + * Gets the type for a local variable if it is a bytecode local, otherwise null. Can be used in circumstances where + * the type is irrelevant if the symbol is not a bytecode local. Note that for bytecode locals, it delegates to + * {@link #getLocalVariableType(Symbol)}, so it will still assert that the type for such variable is already + * defined (that is, not null). + * @param symbol the symbol representing the variable. + * @return the current variable type, if it is a bytecode local, otherwise null. + */ + private LvarType getLocalVariableTypeIfBytecode(final Symbol symbol) { + return symbol.isBytecodeLocal() ? getLocalVariableType(symbol) : null; + } + + /** + * Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict + * of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where + * a just-defined symbol might still be null). + * @param symbol the symbol + * @return the current type for the symbol, or null if the type is not known either because the symbol has not been + * initialized, or because the symbol does not represent a bytecode local variable. + */ private LvarType getLocalVariableTypeOrNull(final Symbol symbol) { return localVariableTypes.get(symbol); } @@ -1358,13 +1393,13 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ jumpToCatchBlock(identNode); } - private void onSelfAssignment(final IdentNode identNode, final Expression assignment) { + private void onSelfAssignment(final IdentNode identNode, final Expression assignment, final LvarType typeOnLoad) { final Symbol symbol = identNode.getSymbol(); assert symbol != null : identNode.getName(); if(!symbol.isBytecodeLocal()) { return; } - final LvarType type = toLvarType(getType(assignment)); + final LvarType type = toLvarType(getType(assignment, symbol, typeOnLoad.type)); // Self-assignment never produce either a boolean or undefined assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN; setType(symbol, type); @@ -1445,13 +1480,24 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ symbolIsUsed(symbol, getLocalVariableType(symbol)); } + /** + * Gets the type of the expression, dependent on the current types of the local variables. + * + * @param expr the expression + * @return the current type of the expression dependent on the current types of the local variables. + */ private Type getType(final Expression expr) { return expr.getType(getSymbolToType()); } + /** + * Returns a function object from symbols to their types, used by the expressions to evaluate their type. + * {@link BinaryNode} specifically uses identity of the function to cache type calculations. This method makes + * sure to return the same function object while the local variable types don't change, and create a new function + * object if the local variable types have been changed. + * @return a function object representing a mapping from symbols to their types. + */ private Function getSymbolToType() { - // BinaryNode uses identity of the function to cache type calculations. Therefore, we must use different - // function instances for different localVariableTypes instances. if(symbolToType.isStale()) { symbolToType = new SymbolToType(); } @@ -1469,4 +1515,41 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return boundTypes != localVariableTypes; } } + + /** + * Gets the type of the expression, dependent on the current types of the local variables and a single overridden + * symbol type. Used by type calculation on compound operators to ensure the type of the LHS at the time it was + * loaded (which can potentially be different after RHS evaluation, e.g. "var x; x += x = 0;") is preserved for + * the calculation. + * + * @param expr the expression + * @param overriddenSymbol the overridden symbol + * @param overriddenType the overridden type + * @return the current type of the expression dependent on the current types of the local variables and the single + * potentially overridden type. + */ + private Type getType(final Expression expr, final Symbol overriddenSymbol, final Type overriddenType) { + return expr.getType(getSymbolToType(overriddenSymbol, overriddenType)); + } + + private Function getSymbolToType(final Symbol overriddenSymbol, final Type overriddenType) { + return getLocalVariableType(overriddenSymbol).type == overriddenType ? getSymbolToType() : + new SymbolToTypeOverride(overriddenSymbol, overriddenType); + } + + private class SymbolToTypeOverride implements Function { + private final Function originalSymbolToType = getSymbolToType(); + private final Symbol overriddenSymbol; + private final Type overriddenType; + + SymbolToTypeOverride(final Symbol overriddenSymbol, final Type overriddenType) { + this.overriddenSymbol = overriddenSymbol; + this.overriddenType = overriddenType; + } + + @Override + public Type apply(final Symbol symbol) { + return symbol == overriddenSymbol ? overriddenType : originalSymbolToType.apply(symbol); + } + } } diff --git a/src/jdk/nashorn/internal/ir/BinaryNode.java b/src/jdk/nashorn/internal/ir/BinaryNode.java index cb8cc918..f625fe1a 100644 --- a/src/jdk/nashorn/internal/ir/BinaryNode.java +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java @@ -341,10 +341,7 @@ public final class BinaryNode extends Expression implements Assignment visitor) { if (visitor.enterBinaryNode(this)) { - if(tokenType().isLeftAssociative()) { - return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor))); - } - return visitor.leaveBinaryNode(setRHS((Expression)rhs.accept(visitor)).setLHS((Expression)lhs.accept(visitor))); + return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor))); } return this; diff --git a/test/script/basic/JDK-8066227.js b/test/script/basic/JDK-8066227.js new file mode 100644 index 00000000..aded343f --- /dev/null +++ b/test/script/basic/JDK-8066227.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8066227: CodeGenerator load unitialized slot + * + * @test + * @run + */ + +print((function () { var x; (x += x = 0); return x; })()); +print((function () { var x; (x -= x = 0); return x; })()); +print((function () { var x; (x *= x = 0); return x; })()); +print((function () { var x; (x /= x = 0); return x; })()); +print((function () { var x; (x %= x = 0); return x; })()); +print((function () { var x; (x <<= x = 0); return x; })()); +print((function () { var x; (x >>= x = 0); return x; })()); +print((function () { var x; (x >>>= x = 0); return x; })()); +print((function () { var x; (x |= x = 0); return x; })()); +print((function () { var x; (x &= x = 0); return x; })()); diff --git a/test/script/basic/JDK-8066227.js.EXPECTED b/test/script/basic/JDK-8066227.js.EXPECTED new file mode 100644 index 00000000..18418221 --- /dev/null +++ b/test/script/basic/JDK-8066227.js.EXPECTED @@ -0,0 +1,10 @@ +NaN +NaN +NaN +NaN +NaN +0 +0 +0 +0 +0 -- cgit v1.2.3 From e9ee5116da0c0f5ee9b76a3a7344c8683994091f Mon Sep 17 00:00:00 2001 From: sundar Date: Wed, 10 Dec 2014 19:42:01 +0530 Subject: 8067136: BrowserJSObjectLinker does not handle call on JSObjects Reviewed-by: attila, hannesw, lagergren --- samples/browser_dom.js | 91 ++++++++++++++++++++++ .../runtime/linker/BrowserJSObjectLinker.java | 9 +++ test/script/basic/JDK-8067136.js | 69 ++++++++++++++++ test/script/basic/JDK-8067136.js.EXPECTED | 1 + 4 files changed, 170 insertions(+) create mode 100644 samples/browser_dom.js create mode 100644 test/script/basic/JDK-8067136.js create mode 100644 test/script/basic/JDK-8067136.js.EXPECTED diff --git a/samples/browser_dom.js b/samples/browser_dom.js new file mode 100644 index 00000000..94324c7a --- /dev/null +++ b/samples/browser_dom.js @@ -0,0 +1,91 @@ +#// Usage: jjs -fx browser.js + +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +if (!$OPTIONS._fx) { + print("Usage: jjs -fx browser.js"); + exit(1); +} + +// JavaFX classes used +var ChangeListener = Java.type("javafx.beans.value.ChangeListener"); +var Scene = Java.type("javafx.scene.Scene"); +var WebView = Java.type("javafx.scene.web.WebView"); +var EventListener = Java.type("org.w3c.dom.events.EventListener"); + +// JavaFX start method +function start(stage) { + start.title = "Web View"; + var wv = new WebView(); + wv.engine.loadContent(< + + +This is the title + + + + +Button from the input html
+
+ + +EOF, "text/html"); + + // attach onload handler + wv.engine.loadWorker.stateProperty().addListener( + new ChangeListener() { + changed: function() { + // DOM document element + var document = wv.engine.document; + // DOM manipulation + var btn = document.createElement("button"); + var n = 0; + // attach a button handler - nashorn function! + btn.onclick = new EventListener(function() { + n++; print("You clicked " + n + " time(s)"); + print("you clicked OK " + wv.engine.executeScript("okCount")); + }); + // attach text to button + var t = document.createTextNode("Click Me!"); + btn.appendChild(t); + // attach button to the document + document.body.appendChild(btn); + } + } + ); + stage.scene = new Scene(wv, 750, 500); + stage.show(); +} diff --git a/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java b/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java index fa43e4c8..d2f23e82 100644 --- a/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java @@ -29,6 +29,7 @@ import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObject import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETSLOT; import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETMEMBER; import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETSLOT; +import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_CALL; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import jdk.internal.dynalink.CallSiteDescriptor; @@ -123,6 +124,8 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { case "setProp": case "setElem": return c > 2 ? findSetMethod(desc) : findSetIndexMethod(); + case "call": + return findCallMethod(desc); default: return null; } @@ -148,6 +151,11 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { return new GuardedInvocation(JSOBJECTLINKER_PUT, IS_JSOBJECT_GUARD); } + private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) { + final MethodHandle call = MH.insertArguments(JSOBJECT_CALL, 1, "call"); + return new GuardedInvocation(MH.asCollector(call, Object[].class, desc.getMethodType().parameterCount() - 1), IS_JSOBJECT_GUARD); + } + @SuppressWarnings("unused") private static boolean isJSObject(final Object self) { return jsObjectClass.isInstance(self); @@ -207,6 +215,7 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { static final MethodHandle JSOBJECT_GETSLOT = findJSObjectMH_V("getSlot", Object.class, int.class).asType(MH.type(Object.class, Object.class, int.class)); static final MethodHandle JSOBJECT_SETMEMBER = findJSObjectMH_V("setMember", Void.TYPE, String.class, Object.class).asType(MH.type(Void.TYPE, Object.class, String.class, Object.class)); static final MethodHandle JSOBJECT_SETSLOT = findJSObjectMH_V("setSlot", Void.TYPE, int.class, Object.class).asType(MH.type(Void.TYPE, Object.class, int.class, Object.class)); + static final MethodHandle JSOBJECT_CALL = findJSObjectMH_V("call", Object.class, String.class, Object[].class).asType(MH.type(Object.class, Object.class, String.class, Object[].class)); private static MethodHandle findJSObjectMH_V(final String name, final Class rtype, final Class... types) { checkJSObjectClass(); diff --git a/test/script/basic/JDK-8067136.js b/test/script/basic/JDK-8067136.js new file mode 100644 index 00000000..1c7c13a2 --- /dev/null +++ b/test/script/basic/JDK-8067136.js @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8067136: BrowserJSObjectLinker does not handle call on JSObjects + * + * @test + * @option -scripting + * @run + */ + +// call on netscape.javascript.JSObject + +function main() { + var JSObject; + try { + JSObject = Java.type("netscape.javascript.JSObject"); + } catch (e) { + if (e instanceof java.lang.ClassNotFoundException) { + // pass vacuously by emitting the .EXPECTED file content + var str = readFully(__DIR__ + "JDK-8067136.js.EXPECTED"); + print(str.substring(0, str.length - 1)); + return; + } else{ + fail("unexpected exception for JSObject", e); + } + } + test(JSObject); +} + +function test(JSObject) { + var obj = new (Java.extend(JSObject))() { + getMember: function(name) { + if (name == "func") { + return new (Java.extend(JSObject)) { + call: function(n) { + print("func called"); + } + } + } + return name.toUpperCase(); + }, + + }; + + obj.func(); +} + +main(); diff --git a/test/script/basic/JDK-8067136.js.EXPECTED b/test/script/basic/JDK-8067136.js.EXPECTED new file mode 100644 index 00000000..5897ee65 --- /dev/null +++ b/test/script/basic/JDK-8067136.js.EXPECTED @@ -0,0 +1 @@ +func called -- cgit v1.2.3 From 118da86f6ed4d0bc0ac63ebc157ddfb63d41198f Mon Sep 17 00:00:00 2001 From: attila Date: Wed, 10 Dec 2014 11:55:04 +0100 Subject: 8066225: NPE in MethodEmitter with duplicate integer switch cases Reviewed-by: hannesw, lagergren --- .../nashorn/internal/codegen/AssignSymbols.java | 2 +- .../nashorn/internal/codegen/CodeGenerator.java | 2 +- .../nashorn/internal/codegen/FoldConstants.java | 31 ++++++++++++++ .../codegen/LocalVariableTypesCalculator.java | 2 +- src/jdk/nashorn/internal/codegen/Lower.java | 2 +- src/jdk/nashorn/internal/ir/SwitchNode.java | 47 +++++++++++++--------- test/script/basic/JDK-8066225.js | 36 +++++++++++++++++ test/script/basic/JDK-8066225.js.EXPECTED | 2 + 8 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 test/script/basic/JDK-8066225.js create mode 100644 test/script/basic/JDK-8066225.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/AssignSymbols.java b/src/jdk/nashorn/internal/codegen/AssignSymbols.java index 6eb89a16..a4fb7bd1 100644 --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java @@ -910,7 +910,7 @@ final class AssignSymbols extends NodeVisitor implements Loggabl @Override public Node leaveSwitchNode(final SwitchNode switchNode) { // We only need a symbol for the tag if it's not an integer switch node - if(!switchNode.isInteger()) { + if(!switchNode.isUniqueInteger()) { switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); } return switchNode; diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 7bb0da3f..3ef1b08a 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -2817,7 +2817,7 @@ final class CodeGenerator extends NodeOperatorVisitor tree = new TreeMap<>(); diff --git a/src/jdk/nashorn/internal/codegen/FoldConstants.java b/src/jdk/nashorn/internal/codegen/FoldConstants.java index 79d20ea1..72f9fd12 100644 --- a/src/jdk/nashorn/internal/codegen/FoldConstants.java +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java @@ -26,12 +26,16 @@ package jdk.nashorn.internal.codegen; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.BlockStatement; +import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.EmptyNode; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IfNode; @@ -40,6 +44,7 @@ import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; @@ -131,6 +136,32 @@ final class FoldConstants extends NodeVisitor implements Loggabl return ternaryNode; } + @Override + public Node leaveSwitchNode(final SwitchNode switchNode) { + return switchNode.setUniqueInteger(lc, isUniqueIntegerSwitchNode(switchNode)); + } + + private static boolean isUniqueIntegerSwitchNode(final SwitchNode switchNode) { + final Set alreadySeen = new HashSet<>(); + for (final CaseNode caseNode : switchNode.getCases()) { + final Expression test = caseNode.getTest(); + if (test != null && !isUniqueIntegerLiteral(test, alreadySeen)) { + return false; + } + } + return true; + } + + private static boolean isUniqueIntegerLiteral(final Expression expr, final Set alreadySeen) { + if (expr instanceof LiteralNode) { + final Object value = ((LiteralNode)expr).getValue(); + if (value instanceof Integer) { + return alreadySeen.add((Integer)value); + } + } + return false; + } + /** * Helper class to evaluate constant expressions at compile time This is * also a simplifier used by BinaryNode visits, UnaryNode visits and diff --git a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java index 91e0c254..af1a8786 100644 --- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java @@ -709,7 +709,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ // Control flow is different for all-integer cases where we dispatch by switch table, and for all other cases // where we do sequential comparison. Note that CaseNode objects act as join points. - final boolean isInteger = switchNode.isInteger(); + final boolean isInteger = switchNode.isUniqueInteger(); final Label breakLabel = switchNode.getBreakLabel(); final boolean hasDefault = switchNode.getDefaultCase() != null; diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java index b52d3984..6f8f3e98 100644 --- a/src/jdk/nashorn/internal/codegen/Lower.java +++ b/src/jdk/nashorn/internal/codegen/Lower.java @@ -275,7 +275,7 @@ final class Lower extends NodeOperatorVisitor implements Lo @Override public Node leaveSwitchNode(final SwitchNode switchNode) { - if(!switchNode.isInteger()) { + if(!switchNode.isUniqueInteger()) { // Wrap it in a block so its internally created tag is restricted in scope addStatementEnclosedInBlock(switchNode); } else { diff --git a/src/jdk/nashorn/internal/ir/SwitchNode.java b/src/jdk/nashorn/internal/ir/SwitchNode.java index 8936764c..ca447587 100644 --- a/src/jdk/nashorn/internal/ir/SwitchNode.java +++ b/src/jdk/nashorn/internal/ir/SwitchNode.java @@ -48,6 +48,10 @@ public final class SwitchNode extends BreakableStatement { /** Switch default index. */ private final int defaultCaseIndex; + /** True if all cases are 32-bit signed integer constants, without repetitions. It's a prerequisite for + * using a tableswitch/lookupswitch when generating code. */ + private final boolean uniqueInteger; + /** Tag symbol. */ private Symbol tag; @@ -66,15 +70,17 @@ public final class SwitchNode extends BreakableStatement { this.expression = expression; this.cases = cases; this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase); + this.uniqueInteger = false; } private SwitchNode(final SwitchNode switchNode, final Expression expression, final List cases, - final int defaultCaseIndex, final LocalVariableConversion conversion) { + final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger) { super(switchNode, conversion); this.expression = expression; this.cases = cases; this.defaultCaseIndex = defaultCaseIndex; - this.tag = switchNode.getTag(); //TODO are symbols inhereted as references? + this.tag = switchNode.getTag(); //TODO are symbols inherited as references? + this.uniqueInteger = uniqueInteger; } @Override @@ -83,7 +89,7 @@ public final class SwitchNode extends BreakableStatement { for (final CaseNode caseNode : cases) { newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion())); } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger)); } @Override @@ -151,7 +157,7 @@ public final class SwitchNode extends BreakableStatement { if (this.cases == cases) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); } /** @@ -183,7 +189,7 @@ public final class SwitchNode extends BreakableStatement { if (this.expression == expression) { return this; } - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); } /** @@ -205,25 +211,30 @@ public final class SwitchNode extends BreakableStatement { } /** - * Returns true if all cases of this switch statement are 32-bit signed integer constants. - * @return true if all cases of this switch statement are 32-bit signed integer constants. + * Returns true if all cases of this switch statement are 32-bit signed integer constants, without repetitions. + * @return true if all cases of this switch statement are 32-bit signed integer constants, without repetitions. */ - public boolean isInteger() { - for (final CaseNode caseNode : cases) { - final Expression test = caseNode.getTest(); - if (test != null && !isIntegerLiteral(test)) { - return false; - } + public boolean isUniqueInteger() { + return uniqueInteger; + } + + /** + * Sets whether all cases of this switch statement are 32-bit signed integer constants, without repetitions. + * @param lc lexical context + * @param uniqueInteger if true, all cases of this switch statement have been determined to be 32-bit signed + * integer constants, without repetitions. + * @return this switch node, if the value didn't change, or a new switch node with the changed value + */ + public SwitchNode setUniqueInteger(final LexicalContext lc, final boolean uniqueInteger) { + if(this.uniqueInteger == uniqueInteger) { + return this; } - return true; + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); } @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { - return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion)); + return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger)); } - private static boolean isIntegerLiteral(final Expression expr) { - return expr instanceof LiteralNode && ((LiteralNode)expr).getValue() instanceof Integer; - } } diff --git a/test/script/basic/JDK-8066225.js b/test/script/basic/JDK-8066225.js new file mode 100644 index 00000000..84d56b81 --- /dev/null +++ b/test/script/basic/JDK-8066225.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8066225: NPE in MethodEmitter with duplicate integer switch cases + * + * @test + * @run + */ + +(function (x){ + switch(x) { + case 44: for (var x in {}) {x}; print("1"); + case 44: print("2"); + } +})(44); diff --git a/test/script/basic/JDK-8066225.js.EXPECTED b/test/script/basic/JDK-8066225.js.EXPECTED new file mode 100644 index 00000000..1191247b --- /dev/null +++ b/test/script/basic/JDK-8066225.js.EXPECTED @@ -0,0 +1,2 @@ +1 +2 -- cgit v1.2.3 From 9771c5c5826ed3c888ff61e5c0a6c44c8de6cc35 Mon Sep 17 00:00:00 2001 From: attila Date: Wed, 10 Dec 2014 11:55:25 +0100 Subject: 8066224: fixes for folding a constant-test ternary operator Reviewed-by: hannesw, lagergren --- .../nashorn/internal/codegen/CodeGenerator.java | 15 ++++++++- .../nashorn/internal/codegen/FoldConstants.java | 2 +- test/script/basic/JDK-8066224.js | 38 ++++++++++++++++++++++ test/script/basic/JDK-8066224.js.EXPECTED | 1 + 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 test/script/basic/JDK-8066224.js create mode 100644 test/script/basic/JDK-8066224.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 3ef1b08a..8d6f980b 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -2021,6 +2021,19 @@ final class CodeGenerator extends NodeOperatorVisitor implements Loggabl public Node leaveTernaryNode(final TernaryNode ternaryNode) { final Node test = ternaryNode.getTest(); if (test instanceof LiteralNode.PrimitiveLiteralNode) { - return ((LiteralNode.PrimitiveLiteralNode)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression(); + return (((LiteralNode.PrimitiveLiteralNode)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression()).getExpression(); } return ternaryNode; } diff --git a/test/script/basic/JDK-8066224.js b/test/script/basic/JDK-8066224.js new file mode 100644 index 00000000..b29ba353 --- /dev/null +++ b/test/script/basic/JDK-8066224.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8066224: fixes for folding a constant-test ternary operator + * + * @test + * @run + */ + +print((function(){ + if(false ? 0 : '') { + throw false; + } else if (x = this) { + var x = x; + } + return x === this; +})()) diff --git a/test/script/basic/JDK-8066224.js.EXPECTED b/test/script/basic/JDK-8066224.js.EXPECTED new file mode 100644 index 00000000..27ba77dd --- /dev/null +++ b/test/script/basic/JDK-8066224.js.EXPECTED @@ -0,0 +1 @@ +true -- cgit v1.2.3 From 4dbb99b9367c0faeab8e13923060c14dd58e39b1 Mon Sep 17 00:00:00 2001 From: attila Date: Wed, 10 Dec 2014 12:30:48 +0100 Subject: 8066236: RuntimeNode forces copy creation on visitation Reviewed-by: hannesw, lagergren --- .../codegen/LocalVariableTypesCalculator.java | 7 ++++ src/jdk/nashorn/internal/ir/RuntimeNode.java | 7 +--- test/script/basic/JDK-8066236.js | 46 ++++++++++++++++++++++ test/script/basic/JDK-8066236.js.EXPECTED | 3 ++ 4 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 test/script/basic/JDK-8066236.js create mode 100644 test/script/basic/JDK-8066236.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java index af1a8786..ac3c2934 100644 --- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java @@ -93,6 +93,13 @@ import jdk.nashorn.internal.parser.TokenType; * variable to its widest used type after the join point. That would eliminate some widenings of undefined variables to * object, most notably those used only in loops. We need a full liveness analysis for that. Currently, we can establish * per-type liveness, which eliminates most of unwanted dead widenings. + * NOTE: the way this class is implemented, it actually processes the AST in two passes. The first pass is top-down and + * implemented in {@code enterXxx} methods. This pass does not mutate the AST (except for one occurrence, noted below), + * as being able to find relevant labels for control flow joins is sensitive to their reference identity, and mutated + * label-carrying nodes will create copies of their labels. A second bottom-up pass applying the changes is implemented + * in the separate visitor sitting in {@link #leaveFunctionNode(FunctionNode)}. This visitor will also instantiate new + * instances of the calculator to be run on nested functions (when not lazy compiling). + * */ final class LocalVariableTypesCalculator extends NodeVisitor{ diff --git a/src/jdk/nashorn/internal/ir/RuntimeNode.java b/src/jdk/nashorn/internal/ir/RuntimeNode.java index fd34c3ec..4eca8ff0 100644 --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -468,11 +467,7 @@ public class RuntimeNode extends Expression implements Optimistic { @Override public Node accept(final NodeVisitor visitor) { if (visitor.enterRuntimeNode(this)) { - final List newArgs = new ArrayList<>(); - for (final Node arg : args) { - newArgs.add((Expression)arg.accept(visitor)); - } - return visitor.leaveRuntimeNode(setArgs(newArgs)); + return visitor.leaveRuntimeNode(setArgs(Node.accept(visitor, args))); } return this; diff --git a/test/script/basic/JDK-8066236.js b/test/script/basic/JDK-8066236.js new file mode 100644 index 00000000..760e0f46 --- /dev/null +++ b/test/script/basic/JDK-8066236.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8066236: RuntimeNode forces copy creation on visitation + * + * @test + * @run + */ + +// Note: we're using Function("code") instead of (function(){ code }) so that +// we don't trigger parser API validation in JDK-8008448 tests. The test code +// encapsulated in functions below can't be correctly handled by the parser API +// currently, as it contains parser-generated REFERENCE_ERROR runtime nodes. +try { + Function("L: {this = x;break L}")(); +} catch (e) { + print("threw ReferenceError: " + (e instanceof ReferenceError)); +} +try { + Function("L:with(this--)break L;")(); +} catch (e) { + print("threw ReferenceError: " + (e instanceof ReferenceError)); +} +Function("L:with(Object in Object)break L;")(); +print("SUCCESS"); diff --git a/test/script/basic/JDK-8066236.js.EXPECTED b/test/script/basic/JDK-8066236.js.EXPECTED new file mode 100644 index 00000000..b64a3056 --- /dev/null +++ b/test/script/basic/JDK-8066236.js.EXPECTED @@ -0,0 +1,3 @@ +threw ReferenceError: true +threw ReferenceError: true +SUCCESS -- cgit v1.2.3 From 84a429270270aa1a98e6d5de2a7fab4aeca882d2 Mon Sep 17 00:00:00 2001 From: attila Date: Wed, 10 Dec 2014 18:28:41 +0100 Subject: 8066221: anonymous function statement name clashes with another symbol Reviewed-by: lagergren, sundar --- src/jdk/nashorn/internal/parser/Parser.java | 28 ++++++++++++++++++++++---- test/script/basic/JDK-8066221.js | 31 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 test/script/basic/JDK-8066221.js diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java index 9c6f974d..12ad59e3 100644 --- a/src/jdk/nashorn/internal/parser/Parser.java +++ b/src/jdk/nashorn/internal/parser/Parser.java @@ -2644,7 +2644,7 @@ loop: // name is null, generate anonymous name boolean isAnonymous = false; if (name == null) { - final String tmpName = getDefaultValidFunctionName(functionLine); + final String tmpName = getDefaultValidFunctionName(functionLine, isStatement); name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName); isAnonymous = true; } @@ -2653,7 +2653,15 @@ loop: final List parameters = formalParameterList(); expect(RPAREN); - FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine); + FunctionNode functionNode; + // Hide the current default name across function boundaries. E.g. "x3 = function x1() { function() {}}" + // If we didn't hide the current default name, then the innermost anonymous function would receive "x3". + hideDefaultName(); + try { + functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine); + } finally { + defaultNames.pop(); + } if (isStatement) { if (topLevel || useBlockScope()) { @@ -2722,9 +2730,17 @@ loop: return functionNode; } - private String getDefaultValidFunctionName(final int functionLine) { + private String getDefaultValidFunctionName(final int functionLine, final boolean isStatement) { final String defaultFunctionName = getDefaultFunctionName(); - return isValidIdentifier(defaultFunctionName) ? defaultFunctionName : ANON_FUNCTION_PREFIX.symbolName() + functionLine; + if (isValidIdentifier(defaultFunctionName)) { + if (isStatement) { + // The name will be used as the LHS of a symbol assignment. We add the anonymous function + // prefix to ensure that it can't clash with another variable. + return ANON_FUNCTION_PREFIX.symbolName() + defaultFunctionName; + } + return defaultFunctionName; + } + return ANON_FUNCTION_PREFIX.symbolName() + functionLine; } private static boolean isValidIdentifier(final String name) { @@ -2758,6 +2774,10 @@ loop: private void markDefaultNameUsed() { defaultNames.pop(); + hideDefaultName(); + } + + private void hideDefaultName() { // Can be any value as long as getDefaultFunctionName doesn't recognize it as something it can extract a value // from. Can't be null defaultNames.push(""); diff --git a/test/script/basic/JDK-8066221.js b/test/script/basic/JDK-8066221.js new file mode 100644 index 00000000..7af4dfa1 --- /dev/null +++ b/test/script/basic/JDK-8066221.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8066221: anonymous function statement name clashes with another symbol + * (compile-only test) + * + * @test + */ + +x3 = function x1(x3) { function (){} }; -- cgit v1.2.3 From eef5dee65c1939b8f2a8f54835500776a9649d78 Mon Sep 17 00:00:00 2001 From: hannesw Date: Thu, 11 Dec 2014 14:32:26 +0100 Subject: 8066932: __noSuchMethod__ binds to this-object without proper guard Reviewed-by: attila, lagergren --- src/jdk/nashorn/internal/runtime/ScriptObject.java | 9 ++-- test/script/basic/JDK-8066932.js | 48 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 test/script/basic/JDK-8066932.js diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java index 3f605910..c47497de 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java @@ -2333,8 +2333,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { } final ScriptFunction func = (ScriptFunction)value; - final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this; + final Object thiz = scopeCall && func.isStrict() ? UNDEFINED : this; // TODO: It'd be awesome if we could bind "name" without binding "this". + // Since we're binding this we must use an identity guard here. return new GuardedInvocation( MH.dropArguments( MH.constant( @@ -2342,9 +2343,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class), - NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), - (SwitchPoint)null, - explicitInstanceOfCheck ? null : ClassCastException.class); + NashornGuards.combineGuards( + NashornGuards.getIdentityGuard(this), + NashornGuards.getMapGuard(getMap(), true))); } /** diff --git a/test/script/basic/JDK-8066932.js b/test/script/basic/JDK-8066932.js new file mode 100644 index 00000000..6dda1e09 --- /dev/null +++ b/test/script/basic/JDK-8066932.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8066932: __noSuchMethod__ binds to this-object without proper guard + * + * @test + * @run + */ + +function C(id) { + this.id = id; +} + +C.prototype.__noSuchMethod__ = function(name, args) { + return this.id; +}; + +function test(id) { + var c = new C(id); + return c.nonExistingMethod(); +} + +for (var i = 0; i < 30; i++) { + if (test(i) !== i) { + throw new Error("Wrong result from noSuchMethod in iteration " + i); + } +} -- cgit v1.2.3 From dfc130c9207f0b7617691b75c359c2722d6c56df Mon Sep 17 00:00:00 2001 From: hannesw Date: Thu, 11 Dec 2014 17:46:50 +0100 Subject: 8066669: dust.js performance regression caused by primitive field conversion Reviewed-by: attila, sundar --- .../nashorn/internal/codegen/CodeGenerator.java | 22 ++++---- src/jdk/nashorn/internal/codegen/Lower.java | 31 +++++++++++- .../nashorn/internal/codegen/MethodEmitter.java | 28 ++++++++--- .../nashorn/internal/codegen/SharedScopeCall.java | 2 +- src/jdk/nashorn/internal/ir/AccessNode.java | 10 ++++ src/jdk/nashorn/internal/runtime/ScriptObject.java | 3 +- test/script/basic/JDK-8066669.js | 58 ++++++++++++++++++++++ test/script/basic/JDK-8066669.js.EXPECTED | 13 +++++ test/script/basic/list.js.EXPECTED | 2 +- 9 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 test/script/basic/JDK-8066669.js create mode 100644 test/script/basic/JDK-8066669.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 8d6f980b..4c132454 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -465,10 +465,10 @@ final class CodeGenerator extends NodeOperatorVisitor implements Lo private final DebugLogger log; + // Conservative pattern to test if element names consist of characters valid for identifiers. + // This matches any non-zero length alphanumeric string including _ and $ and not starting with a digit. + private static Pattern SAFE_PROPERTY_NAME = Pattern.compile("[a-zA-Z_$][\\w$]*"); + /** * Constructor. */ @@ -140,7 +147,7 @@ final class Lower extends NodeOperatorVisitor implements Lo } }); - this.log = initLogger(compiler.getContext()); + this.log = initLogger(compiler.getContext()); } @Override @@ -180,6 +187,28 @@ final class Lower extends NodeOperatorVisitor implements Lo return false; } + @Override + public Node leaveIndexNode(final IndexNode indexNode) { + final String name = getConstantPropertyName(indexNode.getIndex()); + if (name != null) { + // If index node is a constant property name convert index node to access node. + assert Token.descType(indexNode.getToken()) == TokenType.LBRACKET; + return new AccessNode(indexNode.getToken(), indexNode.getFinish(), indexNode.getBase(), name); + } + return super.leaveIndexNode(indexNode); + } + + // If expression is a primitive literal that is not an array index and does return its string value. Else return null. + private static String getConstantPropertyName(final Expression expression) { + if (expression instanceof LiteralNode.PrimitiveLiteralNode) { + final Object value = ((LiteralNode) expression).getValue(); + if (value instanceof String && SAFE_PROPERTY_NAME.matcher((String) value).matches()) { + return (String) value; + } + } + return null; + } + @Override public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { final Expression expr = expressionStatement.getExpression(); diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java index 87bb297f..362a7363 100644 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java @@ -2214,10 +2214,10 @@ public class MethodEmitter implements Emitter { * @param name name of property * @param flags call site flags * @param isMethod should it prefer retrieving methods - * + * @param isIndex is this an index operation? * @return the method emitter */ - MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) { + MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod, final boolean isIndex) { debug("dynamic_get", name, valueType, getProgramPoint(flags)); Type type = valueType; @@ -2226,8 +2226,8 @@ public class MethodEmitter implements Emitter { } popType(Type.SCOPE); - method.visitInvokeDynamicInsn((isMethod ? "dyn:getMethod|getProp|getElem:" : "dyn:getProp|getElem|getMethod:") + - NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn(dynGetOperation(isMethod, isIndex) + ':' + NameCodec.encode(name), + Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags); pushType(type); convert(valueType); //most probably a nop @@ -2240,8 +2240,9 @@ public class MethodEmitter implements Emitter { * * @param name name of property * @param flags call site flags + * @param isIndex is this an index operation? */ - void dynamicSet(final String name, final int flags) { + void dynamicSet(final String name, final int flags, final boolean isIndex) { assert !isOptimistic(flags); debug("dynamic_set", name, peekType()); @@ -2253,7 +2254,8 @@ public class MethodEmitter implements Emitter { popType(type); popType(Type.SCOPE); - method.visitInvokeDynamicInsn("dyn:setProp|setElem:" + NameCodec.encode(name), methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn(dynSetOperation(isIndex) + ':' + NameCodec.encode(name), + methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags); } /** @@ -2286,7 +2288,7 @@ public class MethodEmitter implements Emitter { final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index); - method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod", signature, LINKERBOOTSTRAP, flags); + method.visitInvokeDynamicInsn(dynGetOperation(isMethod, true), signature, LINKERBOOTSTRAP, flags); pushType(resultType); if (result.isBoolean()) { @@ -2500,6 +2502,18 @@ public class MethodEmitter implements Emitter { } } + private static String dynGetOperation(final boolean isMethod, final boolean isIndex) { + if (isMethod) { + return isIndex ? "dyn:getMethod|getElem|getProp" : "dyn:getMethod|getProp|getElem"; + } else { + return isIndex ? "dyn:getElem|getProp|getMethod" : "dyn:getProp|getElem|getMethod"; + } + } + + private static String dynSetOperation(final boolean isIndex) { + return isIndex ? "dyn:setElem|setProp" : "dyn:setProp|setElem"; + } + private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) { final Type from = conversion.getFrom(); final Type to = conversion.getTo(); diff --git a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java index 56da4e00..078324cc 100644 --- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java +++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java @@ -156,7 +156,7 @@ class SharedScopeCall { assert !isCall || valueType.isObject(); // Callables are always objects // If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously // only apply to the call. - method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall); + method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall, false); // If this is a get we're done, otherwise call the value as function. if (isCall) { diff --git a/src/jdk/nashorn/internal/ir/AccessNode.java b/src/jdk/nashorn/internal/ir/AccessNode.java index 315ee395..b8b64820 100644 --- a/src/jdk/nashorn/internal/ir/AccessNode.java +++ b/src/jdk/nashorn/internal/ir/AccessNode.java @@ -28,6 +28,8 @@ package jdk.nashorn.internal.ir; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.parser.Token; +import jdk.nashorn.internal.parser.TokenType; /** * IR representation of a property access (period operator.) @@ -101,6 +103,14 @@ public final class AccessNode extends BaseNode { return property; } + /** + * Return true if this node represents an index operation normally represented as {@link IndexNode}. + * @return true if an index access. + */ + public boolean isIndex() { + return Token.descType(getToken()) == TokenType.LBRACKET; + } + private AccessNode setBase(final Expression base) { if (this.base == base) { return this; diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java index c47497de..90244bc0 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java @@ -2001,12 +2001,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { if (find == null) { switch (operator) { + case "getElem": // getElem only gets here if element name is constant, so treat it like a property access case "getProp": return noSuchProperty(desc, request); case "getMethod": return noSuchMethod(desc, request); - case "getElem": - return createEmptyGetter(desc, explicitInstanceOfCheck, name); default: throw new AssertionError(operator); // never invoked with any other operation } diff --git a/test/script/basic/JDK-8066669.js b/test/script/basic/JDK-8066669.js new file mode 100644 index 00000000..ea5a2860 --- /dev/null +++ b/test/script/basic/JDK-8066669.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8066669: dust.js performance regression caused by primitive field conversion + * + * @test + * @run + */ + +// Make sure index access on Java objects is working as expected. +var map = new java.util.HashMap(); + +map["foo"] = "bar"; +map[1] = 2; +map[false] = true; +map[null] = 0; + +print(map); + +var keys = map.keySet().iterator(); + +while(keys.hasNext()) { + var key = keys.next(); + print(typeof key, key); +} + +print(typeof map["foo"], map["foo"]); +print(typeof map[1], map[1]); +print(typeof map[false], map[false]); +print(typeof map[null], map[null]); + +print(map.foo); +print(map.false); +print(map.null); + +map.foo = "baz"; +print(map); diff --git a/test/script/basic/JDK-8066669.js.EXPECTED b/test/script/basic/JDK-8066669.js.EXPECTED new file mode 100644 index 00000000..4af53817 --- /dev/null +++ b/test/script/basic/JDK-8066669.js.EXPECTED @@ -0,0 +1,13 @@ +{null=0, 1=2, false=true, foo=bar} +object null +number 1 +boolean false +string foo +string bar +number 2 +boolean true +number 0 +bar +null +null +{null=0, 1=2, false=true, foo=baz} diff --git a/test/script/basic/list.js.EXPECTED b/test/script/basic/list.js.EXPECTED index 47f3bd4f..3e410982 100644 --- a/test/script/basic/list.js.EXPECTED +++ b/test/script/basic/list.js.EXPECTED @@ -10,7 +10,7 @@ bar l[0]=foo l[1]=a l[0.9]=null -l['blah']=null +l['blah']=undefined l[size_name]()=2 java.lang.IndexOutOfBoundsException: Index: 2, Size: 2 java.lang.IndexOutOfBoundsException: Index: Infinity, Size: 2 -- cgit v1.2.3 From d69658d60f8a899fe388fa30aac93ed8f30e9d12 Mon Sep 17 00:00:00 2001 From: hannesw Date: Thu, 11 Dec 2014 19:32:45 +0100 Subject: 8067219: NPE in ScriptObject.clone() when running with object fields Reviewed-by: attila, lagergren --- .../nashorn/internal/codegen/CodeGenerator.java | 2 +- src/jdk/nashorn/internal/runtime/ScriptObject.java | 4 +- test/script/basic/es6/for-let-object-fields.js | 81 ++++++++++++++++++++++ .../basic/es6/for-let-object-fields.js.EXPECTED | 33 +++++++++ test/script/basic/es6/for-let.js | 3 +- 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 test/script/basic/es6/for-let-object-fields.js create mode 100644 test/script/basic/es6/for-let-object-fields.js.EXPECTED diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 4c132454..59aa258e 100644 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -3282,7 +3282,7 @@ final class CodeGenerator extends NodeOperatorVisitor