--- a/nashorn/samples/EvalWithArbitraryThis.java.orig Fri Sep 18 10:46:55 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-import javax.script.*;
-import jdk.nashorn.api.scripting.*;
-
-// Simple nashorn demo that evals a script with arbitrary script
-// object bound as "this" for the evaluated script.
-
-public class EvalWithArbitraryThis {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager m = new ScriptEngineManager();
- ScriptEngine e = m.getEngineByName("nashorn");
- Object sobj = e.eval("( { foo: 343, bar: 'hello' } )");
-
- // "this" bound to sobj in this eval.
- // so it prints sobj.foo and sobj.bar.
- ((ScriptObjectMirror)sobj).eval("print(this.foo); print(this.bar)");
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/exceptionswallow.js Fri Sep 18 14:21:22 2015 -0700
@@ -0,0 +1,136 @@
+#// Usage: jjs exceptionswallow.js -- <directory>
+
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This example demonstrates Java subclassing by Java.extend
+// and javac Compiler and Tree API. This example looks for
+// empty catch blocks ("exception swallow") and reports those.
+
+if (arguments.length == 0) {
+ print("Usage: jjs exceptionswallow.js -- <directory>");
+ exit(1);
+}
+
+// Java types used
+var File = Java.type("java.io.File");
+var Files = Java.type("java.nio.file.Files");
+var StringArray = Java.type("java.lang.String[]");
+var ToolProvider = Java.type("javax.tools.ToolProvider");
+var Tree = Java.type("com.sun.source.tree.Tree");
+var EmptyStatementTree = Java.type("com.sun.source.tree.EmptyStatementTree");
+var Trees = Java.type("com.sun.source.util.Trees");
+var TreeScanner = Java.type("com.sun.source.util.TreeScanner");
+
+// printEmptyCatch
+
+function printEmptyCatch() {
+ // get the system compiler tool
+ var compiler = ToolProvider.systemJavaCompiler;
+ // get standard file manager
+ var fileMgr = compiler.getStandardFileManager(null, null, null);
+ // Using Java.to convert script array (arguments) to a Java String[]
+ var compUnits = fileMgr.getJavaFileObjects(
+ Java.to(arguments, StringArray));
+ // create a new compilation task
+ var task = compiler.getTask(null, fileMgr, null, null, null, compUnits);
+
+ // SourcePositions object to get positions of AST nodes
+ var sourcePositions = Trees.instance(task).sourcePositions;
+
+ // subclass SimpleTreeVisitor - to print empty catch
+ var EmptyCatchFinder = Java.extend(TreeScanner);
+
+ function hasOnlyEmptyStats(stats) {
+ var itr = stats.iterator();
+ while (itr.hasNext()) {
+ if (! (itr.next() instanceof EmptyStatementTree)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ var visitor = new EmptyCatchFinder() {
+ // current CompilationUnitTree
+ compUnit: null,
+ // current LineMap (pos -> line, column)
+ lineMap: null,
+ // current compilation unit's file name
+ fileName: null,
+
+ // overrides of TreeScanner methods
+
+ visitCompilationUnit: function(node, p) {
+ // capture info about current Compilation unit
+ this.compUnit = node;
+ this.lineMap = node.lineMap;
+ this.fileName = node.sourceFile.name;
+
+ // Using Java.super API to call super class method here
+ return Java.super(visitor).visitCompilationUnit(node, p);
+ },
+
+ visitCatch: function (node, p) {
+ var stats = node.block.statements;
+ if (stats.empty || hasOnlyEmptyStats(stats)) {
+ // print information on this empty catch
+ var pos = sourcePositions.getStartPosition(this.compUnit, node);
+ var line = this.lineMap.getLineNumber(pos);
+ var col = this.lineMap.getColumnNumber(pos);
+ print("Exception swallow" + " @ " + this.fileName + ":" + line + ":" + col);
+ // print(node);
+ }
+ }
+ }
+
+ for each (var cu in task.parse()) {
+ cu.accept(visitor, null);
+ }
+}
+
+// for each ".java" file in directory (recursively) and check it!
+function main(dir) {
+ Files.walk(dir.toPath()).
+ forEach(function(p) {
+ var name = p.toFile().absolutePath;
+ if (name.endsWith(".java")) {
+ try {
+ printEmptyCatch(p.toFile().getAbsolutePath());
+ } catch (e) {
+ print(e);
+ }
+ }
+ });
+}
+
+main(new File(arguments[0]));
--- a/nashorn/samples/find_nonfinals2.js Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/samples/find_nonfinals2.js Fri Sep 18 14:21:22 2015 -0700
@@ -43,7 +43,6 @@
// Java types used
var File = Java.type("java.io.File");
var Files = Java.type("java.nio.file.Files");
-var FileVisitOption = Java.type("java.nio.file.FileVisitOption");
var StringArray = Java.type("java.lang.String[]");
var ToolProvider = Java.type("javax.tools.ToolProvider");
var Tree = Java.type("com.sun.source.tree.Tree");
@@ -106,7 +105,7 @@
// for each ".java" file in directory (recursively).
function main(dir) {
var totalCount = 0;
- Files.walk(dir.toPath(), FileVisitOption.FOLLOW_LINKS).
+ Files.walk(dir.toPath()).
forEach(function(p) {
var name = p.toFile().absolutePath;
if (name.endsWith(".java")) {
--- a/nashorn/samples/javafoovars.js Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/samples/javafoovars.js Fri Sep 18 14:21:22 2015 -0700
@@ -42,7 +42,6 @@
// Java types used
var File = Java.type("java.io.File");
var Files = Java.type("java.nio.file.Files");
-var FileVisitOption = Java.type("java.nio.file.FileVisitOption");
var StringArray = Java.type("java.lang.String[]");
var ToolProvider = Java.type("javax.tools.ToolProvider");
var Tree = Java.type("com.sun.source.tree.Tree");
@@ -81,7 +80,7 @@
// for each ".java" file in directory (recursively) count "foo".
function main(dir) {
var totalCount = 0;
- Files.walk(dir.toPath(), FileVisitOption.FOLLOW_LINKS).
+ Files.walk(dir.toPath()).
forEach(function(p) {
var name = p.toFile().absolutePath;
if (name.endsWith(".java")) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/resourcetrysuggester.js Fri Sep 18 14:21:22 2015 -0700
@@ -0,0 +1,156 @@
+#// Usage: jjs resourcetrysuggester.js -- <directory>
+
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This example demonstrates Java subclassing by Java.extend
+// and javac Compiler and Tree API. This example looks for
+// finally clauses with "close" call and suggests "resource try"!
+
+if (arguments.length == 0) {
+ print("Usage: jjs resourcetrysuggester.js -- <directory>");
+ exit(1);
+}
+
+// Java types used
+var ExpressionStatementTree = Java.type("com.sun.source.tree.ExpressionStatementTree");
+var File = Java.type("java.io.File");
+var Files = Java.type("java.nio.file.Files");
+var MemberSelectTree = Java.type("com.sun.source.tree.MemberSelectTree");
+var MethodInvocationTree = Java.type("com.sun.source.tree.MethodInvocationTree");
+var StringArray = Java.type("java.lang.String[]");
+var ToolProvider = Java.type("javax.tools.ToolProvider");
+var Tree = Java.type("com.sun.source.tree.Tree");
+var Trees = Java.type("com.sun.source.util.Trees");
+var TreeScanner = Java.type("com.sun.source.util.TreeScanner");
+
+// resourceTrySuggestions
+
+function resourceTrySuggestions() {
+ // get the system compiler tool
+ var compiler = ToolProvider.systemJavaCompiler;
+ // get standard file manager
+ var fileMgr = compiler.getStandardFileManager(null, null, null);
+ // Using Java.to convert script array (arguments) to a Java String[]
+ var compUnits = fileMgr.getJavaFileObjects(
+ Java.to(arguments, StringArray));
+ // create a new compilation task
+ var task = compiler.getTask(null, fileMgr, null, null, null, compUnits);
+
+ // SourcePositions object to get positions of AST nodes
+ var sourcePositions = Trees.instance(task).sourcePositions;
+
+ // subclass SimpleTreeVisitor - to print resource try suggestions
+ var ResourceTrySuggester = Java.extend(TreeScanner);
+
+ function hasOnlyEmptyStats(stats) {
+ var itr = stats.iterator();
+ while (itr.hasNext()) {
+ if (! (itr.next() instanceof EmptyStatementTree)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // does the given statement list has an expression statement which
+ // calls "close" method (don't worry about types - just crude one will do)
+ function hasCloseCall(stats) {
+ var itr = stats.iterator();
+ while (itr.hasNext()) {
+ var stat = itr.next();
+ if (stat instanceof ExpressionStatementTree) {
+ var expr = stat.expression;
+ if (expr instanceof MethodInvocationTree) {
+ var method = expr.methodSelect;
+ if (method instanceof MemberSelectTree) {
+ return method.identifier.toString().equals("close");
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ var visitor = new ResourceTrySuggester() {
+ // current CompilationUnitTree
+ compUnit: null,
+ // current LineMap (pos -> line, column)
+ lineMap: null,
+ // current compilation unit's file name
+ fileName: null,
+
+ // overrides of TreeScanner methods
+
+ visitCompilationUnit: function(node, p) {
+ // capture info about current Compilation unit
+ this.compUnit = node;
+ this.lineMap = node.lineMap;
+ this.fileName = node.sourceFile.name;
+
+ // Using Java.super API to call super class method here
+ return Java.super(visitor).visitCompilationUnit(node, p);
+ },
+
+ visitTry: function (node, p) {
+ var finallyBlk = node.finallyBlock;
+ if (finallyBlk != null && hasCloseCall(finallyBlk.statements)) {
+ var pos = sourcePositions.getStartPosition(this.compUnit, node);
+ var line = this.lineMap.getLineNumber(pos);
+ var col = this.lineMap.getColumnNumber(pos);
+ print("Consider resource try statement " + " @ " + this.fileName + ":" + line + ":" + col);
+ // print(node);
+ }
+ }
+ }
+
+ for each (var cu in task.parse()) {
+ cu.accept(visitor, null);
+ }
+}
+
+// for each ".java" file in directory (recursively) and check it!
+function main(dir) {
+ Files.walk(dir.toPath()).
+ forEach(function(p) {
+ var name = p.toFile().absolutePath;
+ if (name.endsWith(".java")) {
+ try {
+ resourceTrySuggestions(p.toFile().getAbsolutePath());
+ } catch (e) {
+ print(e);
+ }
+ }
+ });
+}
+
+main(new File(arguments[0]));
--- a/nashorn/samples/zipfs.js Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/samples/zipfs.js Fri Sep 18 14:21:22 2015 -0700
@@ -36,13 +36,12 @@
var Files = Java.type("java.nio.file.Files")
var FileSystems = Java.type("java.nio.file.FileSystems")
-var FileVisitOption = Java.type("java.nio.file.FileVisitOption")
var Paths = Java.type("java.nio.file.Paths")
var zipfile = Paths.get(arguments[0])
var fs = FileSystems.newFileSystem(zipfile, null)
var root = fs.rootDirectories[0]
-Files.walk(root, FileVisitOption.FOLLOW_LINKS).forEach(
+Files.walk(root).forEach(
function(p) (print(p), print(Files.readAttributes(p, "zip:*")))
)
fs.close()
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/EditPad.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/EditPad.java Fri Sep 18 14:21:22 2015 -0700
@@ -49,8 +49,8 @@
private final boolean[] closeLock;
private final Consumer<String> saveHandler;
- EditPad(Consumer<String> errorHandler, String initialText,
- boolean[] closeLock, Consumer<String> saveHandler) {
+ EditPad(final Consumer<String> errorHandler, final String initialText,
+ final boolean[] closeLock, final Consumer<String> saveHandler) {
super("Edit Pad (Experimental)");
this.errorHandler = errorHandler;
this.initialText = initialText;
@@ -62,7 +62,7 @@
public void run() {
addWindowListener(new WindowAdapter() {
@Override
- public void windowClosing(WindowEvent e) {
+ public void windowClosing(final WindowEvent e) {
EditPad.this.dispose();
notifyClose();
}
@@ -77,7 +77,7 @@
setVisible(true);
}
- private JPanel buttons(JTextArea textArea) {
+ private JPanel buttons(final JTextArea textArea) {
FlowLayout flow = new FlowLayout();
flow.setHgap(35);
JPanel buttons = new JPanel(flow);
@@ -118,8 +118,8 @@
}
}
- static void edit(Consumer<String> errorHandler, String initialText,
- Consumer<String> saveHandler) {
+ static void edit(final Consumer<String> errorHandler, final String initialText,
+ final Consumer<String> saveHandler) {
boolean[] closeLock = new boolean[1];
SwingUtilities.invokeLater(
new EditPad(errorHandler, initialText, closeLock, saveHandler));
@@ -127,7 +127,7 @@
while (!closeLock[0]) {
try {
closeLock.wait();
- } catch (InterruptedException ex) {
+ } catch (final InterruptedException ex) {
// ignore and loop
}
}
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/ExternalEditor.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/ExternalEditor.java Fri Sep 18 14:21:22 2015 -0700
@@ -49,13 +49,13 @@
private Path dir;
private Path tmpfile;
- ExternalEditor(Consumer<String> errorHandler, Consumer<String> saveHandler, Console input) {
+ ExternalEditor(final Consumer<String> errorHandler, final Consumer<String> saveHandler, final Console input) {
this.errorHandler = errorHandler;
this.saveHandler = saveHandler;
this.input = input;
}
- private void edit(String cmd, String initialText) {
+ private void edit(final String cmd, final String initialText) {
try {
setupWatch(initialText);
launch(cmd);
@@ -67,7 +67,7 @@
/**
* Creates a WatchService and registers the given directory
*/
- private void setupWatch(String initialText) throws IOException {
+ private void setupWatch(final String initialText) throws IOException {
this.watcher = FileSystems.getDefault().newWatchService();
this.dir = Files.createTempDirectory("REPL");
this.tmpfile = Files.createTempFile(dir, null, ".js");
@@ -81,9 +81,9 @@
WatchKey key;
try {
key = watcher.take();
- } catch (ClosedWatchServiceException ex) {
+ } catch (final ClosedWatchServiceException ex) {
break;
- } catch (InterruptedException ex) {
+ } catch (final InterruptedException ex) {
continue; // tolerate an intrupt
}
@@ -103,7 +103,7 @@
watchedThread.start();
}
- private void launch(String cmd) throws IOException {
+ private void launch(final String cmd) throws IOException {
ProcessBuilder pb = new ProcessBuilder(cmd, tmpfile.toString());
pb = pb.inheritIO();
@@ -111,9 +111,9 @@
input.suspend();
Process process = pb.start();
process.waitFor();
- } catch (IOException ex) {
+ } catch (final IOException ex) {
errorHandler.accept("process IO failure: " + ex.getMessage());
- } catch (InterruptedException ex) {
+ } catch (final InterruptedException ex) {
errorHandler.accept("process interrupt: " + ex.getMessage());
} finally {
try {
@@ -132,7 +132,7 @@
List<String> lines;
try {
lines = Files.readAllLines(tmpfile);
- } catch (IOException ex) {
+ } catch (final IOException ex) {
errorHandler.accept("Failure read edit file: " + ex.getMessage());
return ;
}
@@ -144,8 +144,8 @@
saveHandler.accept(sb.toString());
}
- static void edit(String cmd, Consumer<String> errorHandler, String initialText,
- Consumer<String> saveHandler, Console input) {
+ static void edit(final String cmd, final Consumer<String> errorHandler, final String initialText,
+ final Consumer<String> saveHandler, final Console input) {
ExternalEditor ed = new ExternalEditor(errorHandler, saveHandler, input);
ed.edit(cmd, initialText);
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ApplySpecialization.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ApplySpecialization.java Fri Sep 18 14:21:22 2015 -0700
@@ -40,7 +40,6 @@
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
@@ -384,7 +383,7 @@
callSiteTypes.pop();
explodedArguments.pop();
- return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED);
+ return newFunctionNode;
}
private static boolean isApply(final CallNode callNode) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Fri Sep 18 14:21:22 2015 -0700
@@ -65,7 +65,6 @@
import jdk.nashorn.internal.ir.Expression;
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.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
@@ -828,7 +827,7 @@
lc.applyTopFlags(functionNode))))
.setThisProperties(lc, thisProperties.pop().size()));
}
- return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED);
+ return finalizedFunction;
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Sep 18 14:21:22 2015 -0700
@@ -93,7 +93,6 @@
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.GetSplitState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
@@ -2142,7 +2141,7 @@
markOptimistic = false;
}
- FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.BYTECODE_GENERATED);
+ FunctionNode newFunctionNode = functionNode;
if (markOptimistic) {
newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_DEOPTIMIZABLE);
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Sep 18 14:21:22 2015 -0700
@@ -25,32 +25,17 @@
package jdk.nashorn.internal.codegen;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.OPTIMISTIC_TYPES_ASSIGNED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
import java.io.PrintWriter;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
@@ -67,15 +52,9 @@
* A compilation phase is a step in the processes of turning a JavaScript
* FunctionNode into bytecode. It has an optional return value.
*/
-enum CompilationPhase {
- /**
- * Constant folding pass Simple constant folding that will make elementary
- * constructs go away
- */
- CONSTANT_FOLDING_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED)) {
+abstract class CompilationPhase {
+
+ private static final class ConstantFoldingPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return transformFunction(fn, new FoldConstants(compiler));
@@ -85,20 +64,15 @@
public String toString() {
return "'Constant Folding'";
}
- },
+ }
/**
- * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
- * finally constructs and similar things. Establishes termination criteria
- * for nodes Guarantee return instructions to method making sure control
- * flow cannot fall off the end. Replacing high level nodes with lower such
- * as runtime nodes where applicable.
+ * Constant folding pass Simple constant folding that will make elementary
+ * constructs go away
*/
- LOWERING_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED)) {
+ static final CompilationPhase CONSTANT_FOLDING_PHASE = new ConstantFoldingPhase();
+
+ private static final class LoweringPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return transformFunction(fn, new Lower(compiler));
@@ -108,42 +82,35 @@
public String toString() {
return "'Control Flow Lowering'";
}
- },
+ }
/**
- * Phase used only when doing optimistic code generation. It assigns all potentially
- * optimistic ops a program point so that an UnwarrantedException knows from where
- * a guess went wrong when creating the continuation to roll back this execution
+ * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
+ * finally constructs and similar things. Establishes termination criteria
+ * for nodes Guarantee return instructions to method making sure control
+ * flow cannot fall off the end. Replacing high level nodes with lower such
+ * as runtime nodes where applicable.
*/
- TRANSFORM_BUILTINS_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED)) {
- //we only do this if we have a param type map, otherwise this is not a specialized recompile
+ static final CompilationPhase LOWERING_PHASE = new LoweringPhase();
+
+ private static final class ApplySpecializationPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- return setStates(transformFunction(fn, new ApplySpecialization(compiler)), BUILTINS_TRANSFORMED);
+ return transformFunction(fn, new ApplySpecialization(compiler));
}
@Override
public String toString() {
return "'Builtin Replacement'";
}
- },
+ };
/**
- * Splitter Split the AST into several compile units based on a heuristic size calculation.
- * Split IR can lead to scope information being changed.
+ * Phase used to transform Function.prototype.apply.
*/
- SPLITTING_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED)) {
+ static final CompilationPhase APPLY_SPECIALIZATION_PHASE = new ApplySpecializationPhase();
+
+ private static final class SplittingPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L);
@@ -170,16 +137,15 @@
public String toString() {
return "'Code Splitting'";
}
- },
+ };
- PROGRAM_POINT_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT)) {
+ /**
+ * Splitter Split the AST into several compile units based on a heuristic size calculation.
+ * Split IR can lead to scope information being changed.
+ */
+ static final CompilationPhase SPLITTING_PHASE = new SplittingPhase();
+
+ private static final class ProgramPointPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return transformFunction(fn, new ProgramPoints());
@@ -189,16 +155,16 @@
public String toString() {
return "'Program Point Calculation'";
}
- },
+ };
- CACHE_AST(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT)) {
+ /**
+ * Phase used only when doing optimistic code generation. It assigns all potentially
+ * optimistic ops a program point so that an UnwarrantedException knows from where
+ * a guess went wrong when creating the continuation to roll back this execution
+ */
+ static final CompilationPhase PROGRAM_POINT_PHASE = new ProgramPointPhase();
+
+ private static final class CacheAstPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
if (!compiler.isOnDemandCompilation()) {
@@ -217,16 +183,11 @@
public String toString() {
return "'Cache ASTs'";
}
- },
+ };
- SYMBOL_ASSIGNMENT_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT)) {
+ static final CompilationPhase CACHE_AST_PHASE = new CacheAstPhase();
+
+ private static final class SymbolAssignmentPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return transformFunction(fn, new AssignSymbols(compiler));
@@ -236,17 +197,11 @@
public String toString() {
return "'Symbol Assignment'";
}
- },
+ };
- SCOPE_DEPTH_COMPUTATION_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT,
- SYMBOLS_ASSIGNED)) {
+ static final CompilationPhase SYMBOL_ASSIGNMENT_PHASE = new SymbolAssignmentPhase();
+
+ private static final class ScopeDepthComputationPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
return transformFunction(fn, new FindScopeDepths(compiler));
@@ -256,18 +211,11 @@
public String toString() {
return "'Scope Depth Computation'";
}
- },
+ }
- DECLARE_LOCAL_SYMBOLS_TO_COMPILER(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT,
- SYMBOLS_ASSIGNED,
- SCOPE_DEPTHS_COMPUTED)) {
+ static final CompilationPhase SCOPE_DEPTH_COMPUTATION_PHASE = new ScopeDepthComputationPhase();
+
+ private static final class DeclareLocalSymbolsPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
// It's not necessary to guard the marking of symbols as locals with this "if" condition for
@@ -301,43 +249,28 @@
public String toString() {
return "'Local Symbols Declaration'";
}
- },
+ };
- OPTIMISTIC_TYPE_ASSIGNMENT_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT,
- SYMBOLS_ASSIGNED,
- SCOPE_DEPTHS_COMPUTED)) {
+ static final CompilationPhase DECLARE_LOCAL_SYMBOLS_PHASE = new DeclareLocalSymbolsPhase();
+
+ private static final class OptimisticTypeAssignmentPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
if (compiler.useOptimisticTypes()) {
return transformFunction(fn, new OptimisticTypesCalculator(compiler));
}
- return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED);
+ return fn;
}
@Override
public String toString() {
return "'Optimistic Type Assignment'";
}
- },
+ }
- LOCAL_VARIABLE_TYPE_CALCULATION_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT,
- SYMBOLS_ASSIGNED,
- SCOPE_DEPTHS_COMPUTED,
- OPTIMISTIC_TYPES_ASSIGNED)) {
+ static final CompilationPhase OPTIMISTIC_TYPE_ASSIGNMENT_PHASE = new OptimisticTypeAssignmentPhase();
+
+ private static final class LocalVariableTypeCalculationPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler));
@@ -362,25 +295,11 @@
public String toString() {
return "'Local Variable Type Calculation'";
}
- },
-
+ };
- /**
- * Reuse compile units, if they are already present. We are using the same compiler
- * to recompile stuff
- */
- REUSE_COMPILE_UNITS_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT,
- SYMBOLS_ASSIGNED,
- SCOPE_DEPTHS_COMPUTED,
- OPTIMISTIC_TYPES_ASSIGNED,
- LOCAL_VARIABLE_TYPES_CALCULATED)) {
+ static final CompilationPhase LOCAL_VARIABLE_TYPE_CALCULATION_PHASE = new LocalVariableTypeCalculationPhase();
+
+ private static final class ReuseCompileUnitsPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods";
@@ -428,16 +347,15 @@
public String toString() {
return "'Reuse Compile Units'";
}
- },
+ }
- REINITIALIZE_CACHED(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT)) {
+ /**
+ * Reuse compile units, if they are already present. We are using the same compiler
+ * to recompile stuff
+ */
+ static final CompilationPhase REUSE_COMPILE_UNITS_PHASE = new ReuseCompileUnitsPhase();
+
+ private static final class ReinitializeCachedPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet();
@@ -480,26 +398,11 @@
public String toString() {
return "'Reinitialize cached'";
}
- },
+ }
- /**
- * Bytecode generation:
- *
- * Generate the byte code class(es) resulting from the compiled FunctionNode
- */
- BYTECODE_GENERATION_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT,
- SYMBOLS_ASSIGNED,
- SCOPE_DEPTHS_COMPUTED,
- OPTIMISTIC_TYPES_ASSIGNED,
- LOCAL_VARIABLE_TYPES_CALCULATED)) {
+ static final CompilationPhase REINITIALIZE_CACHED = new ReinitializeCachedPhase();
+ private static final class BytecodeGenerationPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
final ScriptEnvironment senv = compiler.getScriptEnvironment();
@@ -517,7 +420,7 @@
try {
// Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
// in the lazy + optimistic world. See CodeGenerator.skipFunction().
- newFunctionNode = transformFunction(newFunctionNode, codegen).setState(null, BYTECODE_GENERATED);
+ newFunctionNode = transformFunction(newFunctionNode, codegen);
codegen.generateScopeCalls();
} catch (final VerifyError e) {
if (senv._verify_code || senv._print_code) {
@@ -565,22 +468,16 @@
public String toString() {
return "'Bytecode Generation'";
}
- },
+ }
- INSTALL_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED,
- BUILTINS_TRANSFORMED,
- SPLIT,
- SYMBOLS_ASSIGNED,
- SCOPE_DEPTHS_COMPUTED,
- OPTIMISTIC_TYPES_ASSIGNED,
- LOCAL_VARIABLE_TYPES_CALCULATED,
- BYTECODE_GENERATED)) {
+ /**
+ * Bytecode generation:
+ *
+ * Generate the byte code class(es) resulting from the compiled FunctionNode
+ */
+ static final CompilationPhase BYTECODE_GENERATION_PHASE = new BytecodeGenerationPhase();
+ private static final class InstallPhase extends CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
final DebugLogger log = compiler.getLogger();
@@ -591,8 +488,9 @@
Class<?> rootClass = null;
long length = 0L;
- final CodeInstaller codeInstaller = compiler.getCodeInstaller();
- final Map<String, byte[]> bytecode = compiler.getBytecode();
+ final CodeInstaller origCodeInstaller = compiler.getCodeInstaller();
+ final Map<String, byte[]> bytecode = compiler.getBytecode();
+ final CodeInstaller codeInstaller = bytecode.size() > 1 ? origCodeInstaller.getMultiClassCodeInstaller() : origCodeInstaller;
for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
final String className = entry.getKey();
@@ -648,18 +546,16 @@
log.fine(sb.toString());
}
- return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED);
+ return fn.setRootClass(null, rootClass);
}
@Override
public String toString() {
return "'Class Installation'";
}
-
- };
+ }
- /** pre conditions required for function node to which this transform is to be applied */
- private final EnumSet<CompilationState> pre;
+ static final CompilationPhase INSTALL_PHASE = new InstallPhase();
/** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */
private long startTime;
@@ -670,21 +566,7 @@
/** boolean that is true upon transform completion */
private boolean isFinished;
- private CompilationPhase(final EnumSet<CompilationState> pre) {
- this.pre = pre;
- }
-
- private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) {
- if (!AssertsEnabled.assertsEnabled()) {
- return functionNode;
- }
- return transformFunction(functionNode, new NodeVisitor<LexicalContext>(new LexicalContext()) {
- @Override
- public Node leaveFunctionNode(final FunctionNode fn) {
- return fn.setState(lc, state);
- }
- });
- }
+ private CompilationPhase() {}
/**
* Start a compilation phase
@@ -694,23 +576,7 @@
*/
protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) {
compiler.getLogger().indent();
-
- assert pre != null;
-
- if (!functionNode.hasState(pre)) {
- final StringBuilder sb = new StringBuilder("Compilation phase ");
- sb.append(this).
- append(" is not applicable to ").
- append(quote(functionNode.getName())).
- append("\n\tFunctionNode state = ").
- append(functionNode.getState()).
- append("\n\tRequired state = ").
- append(this.pre);
-
- throw new CompilationException(sb.toString());
- }
-
- startTime = System.nanoTime();
+ startTime = System.nanoTime();
return functionNode;
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Fri Sep 18 14:21:22 2015 -0700
@@ -172,17 +172,17 @@
"Common initial phases",
CompilationPhase.CONSTANT_FOLDING_PHASE,
CompilationPhase.LOWERING_PHASE,
- CompilationPhase.TRANSFORM_BUILTINS_PHASE,
+ CompilationPhase.APPLY_SPECIALIZATION_PHASE,
CompilationPhase.SPLITTING_PHASE,
CompilationPhase.PROGRAM_POINT_PHASE,
CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
- CompilationPhase.CACHE_AST
+ CompilationPhase.CACHE_AST_PHASE
);
private final static CompilationPhases COMPILE_CACHED_UPTO_BYTECODE = new CompilationPhases(
"After common phases, before bytecode generator",
- CompilationPhase.DECLARE_LOCAL_SYMBOLS_TO_COMPILER,
+ CompilationPhase.DECLARE_LOCAL_SYMBOLS_PHASE,
CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE
);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java Fri Sep 18 14:21:22 2015 -0700
@@ -34,7 +34,6 @@
import java.util.Set;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
@@ -180,8 +179,7 @@
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
final String name = functionNode.getName();
- FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.SCOPE_DEPTHS_COMPUTED);
-
+ FunctionNode newFunctionNode = functionNode;
if (compiler.isOnDemandCompilation()) {
final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId());
if (data.inDynamicContext()) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FoldConstants.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FoldConstants.java Fri Sep 18 14:21:22 2015 -0700
@@ -37,7 +37,6 @@
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;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
@@ -101,7 +100,7 @@
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
- return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED);
+ return functionNode;
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Fri Sep 18 14:21:22 2015 -0700
@@ -54,7 +54,6 @@
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.GetSplitState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
@@ -1478,7 +1477,6 @@
newFunction = newFunction.setReturnType(lc, returnType);
- newFunction = newFunction.setState(lc, CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED);
newFunction = newFunction.setParameters(lc, newFunction.visitParameters(applyChangesVisitor));
return newFunction;
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Fri Sep 18 14:21:22 2015 -0700
@@ -52,7 +52,6 @@
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;
@@ -276,7 +275,7 @@
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
log.info("END FunctionNode: ", functionNode.getName());
- return functionNode.setState(lc, CompilationState.LOWERED);
+ return functionNode;
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Fri Sep 18 14:21:22 2015 -0700
@@ -38,7 +38,6 @@
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;
@@ -208,7 +207,7 @@
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
neverOptimistic.pop();
- return functionNode.setState(lc, CompilationState.OPTIMISTIC_TYPES_ASSIGNED);
+ return functionNode;
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Fri Sep 18 14:21:22 2015 -0700
@@ -29,7 +29,6 @@
import java.util.List;
import jdk.nashorn.internal.ir.CompileUnitHolder;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
@@ -64,7 +63,7 @@
@Override
public Node leaveFunctionNode(final FunctionNode node) {
- return node.setCompileUnit(lc, getExistingReplacement(node)).setState(lc, CompilationState.COMPILE_UNITS_REUSED);
+ return node.setCompileUnit(lc, getExistingReplacement(node));
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Fri Sep 18 14:21:22 2015 -0700
@@ -47,7 +47,6 @@
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.GetSplitState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
@@ -176,11 +175,9 @@
// we still use IS_SPLIT as the criteria in CompilationPhase.SERIALIZE_SPLIT_PHASE.
FunctionNode.IS_ANONYMOUS | FunctionNode.USES_ANCESTOR_SCOPE | FunctionNode.IS_SPLIT,
body,
- CompilationState.INITIALIZED,
null
)
- .setCompileUnit(lc, splitNode.getCompileUnit())
- .copyCompilationState(lc, originalFn);
+ .setCompileUnit(lc, splitNode.getCompileUnit());
// Call the function:
// either "(function () { ... }).call(this)"
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Splitter.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Splitter.java Fri Sep 18 14:21:22 2015 -0700
@@ -33,7 +33,6 @@
import java.util.Map;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
@@ -158,7 +157,7 @@
assert functionNode.getCompileUnit() != null;
- return functionNode.setState(null, CompilationState.SPLIT);
+ return functionNode;
}
private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Fri Sep 18 14:21:22 2015 -0700
@@ -33,10 +33,8 @@
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES;
import java.util.Collections;
-import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
-import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -74,40 +72,6 @@
SETTER
}
- /** Compilation states available */
- public enum CompilationState {
- /** compiler is ready */
- INITIALIZED,
- /** method has been parsed */
- PARSED,
- /** method has been parsed */
- PARSE_ERROR,
- /** constant folding pass */
- CONSTANT_FOLDED,
- /** method has been lowered */
- LOWERED,
- /** program points have been assigned to unique locations */
- PROGRAM_POINTS_ASSIGNED,
- /** any transformations of builtins have taken place, e.g. apply=>call */
- BUILTINS_TRANSFORMED,
- /** method has been split */
- SPLIT,
- /** method has had symbols assigned */
- SYMBOLS_ASSIGNED,
- /** computed scope depths for symbols */
- SCOPE_DEPTHS_COMPUTED,
- /** method has had types calculated*/
- OPTIMISTIC_TYPES_ASSIGNED,
- /** method has had types calculated */
- LOCAL_VARIABLE_TYPES_CALCULATED,
- /** compile units reused (optional) */
- COMPILE_UNITS_REUSED,
- /** method has been emitted to bytecode */
- BYTECODE_GENERATED,
- /** method has been installed */
- BYTECODE_INSTALLED
- }
-
/** Source of entity. */
private transient final Source source;
@@ -145,10 +109,6 @@
/** Method's namespace. */
private transient final Namespace namespace;
- /** Current compilation state */
- @Ignore
- private final EnumSet<CompilationState> compilationState;
-
/** Number of properties of "this" object assigned in this function */
@Ignore
private final int thisProperties;
@@ -306,7 +266,6 @@
* @param kind kind of function as in {@link FunctionNode.Kind}
* @param flags initial flags
* @param body body of the function
- * @param state The initial state from the parser. Must be one of {@link CompilationState#PARSED} and {@link CompilationState#PARSE_ERROR}
* @param endParserState The parser state at the end of the parsing.
*/
public FunctionNode(
@@ -323,7 +282,6 @@
final FunctionNode.Kind kind,
final int flags,
final Block body,
- final CompilationState state,
final Object endParserState) {
super(token, finish);
@@ -336,7 +294,6 @@
this.firstToken = firstToken;
this.lastToken = lastToken;
this.namespace = namespace;
- this.compilationState = EnumSet.of(CompilationState.INITIALIZED, state);
this.flags = flags;
this.compileUnit = null;
this.body = body;
@@ -353,7 +310,6 @@
final String name,
final Type returnType,
final CompileUnit compileUnit,
- final EnumSet<CompilationState> compilationState,
final Block body,
final List<IdentNode> parameters,
final int thisProperties,
@@ -368,7 +324,6 @@
this.returnType = returnType;
this.compileUnit = compileUnit;
this.lastToken = lastToken;
- this.compilationState = compilationState;
this.body = body;
this.parameters = parameters;
this.thisProperties = thisProperties;
@@ -468,7 +423,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -544,80 +498,6 @@
}
/**
- * Get the compilation state of this function
- * @return the compilation state
- */
- public EnumSet<CompilationState> getState() {
- return compilationState;
- }
-
- /**
- * Check whether this FunctionNode has reached a give CompilationState.
- *
- * @param state the state to check for
- * @return true of the node is in the given state
- */
- public boolean hasState(final EnumSet<CompilationState> state) {
- return !AssertsEnabled.assertsEnabled() || compilationState.containsAll(state);
- }
-
- /**
- * Add a state to the total CompilationState of this node, e.g. if
- * FunctionNode has been lowered, the compiler will add
- * {@code CompilationState#LOWERED} to the state vector
- *
- * @param lc lexical context
- * @param state {@link CompilationState} to add
- * @return function node or a new one if state was changed
- */
- public FunctionNode setState(final LexicalContext lc, final CompilationState state) {
- if (!AssertsEnabled.assertsEnabled() || this.compilationState.contains(state)) {
- return this;
- }
- final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
- newState.add(state);
- return setCompilationState(lc, newState);
- }
-
- /**
- * Copy a compilation state from an original function to this function. Used when creating synthetic
- * function nodes by the splitter.
- *
- * @param lc lexical context
- * @param original the original function node to copy compilation state from
- * @return function node or a new one if state was changed
- */
- public FunctionNode copyCompilationState(final LexicalContext lc, final FunctionNode original) {
- final EnumSet<CompilationState> origState = original.compilationState;
- if (!AssertsEnabled.assertsEnabled() || this.compilationState.containsAll(origState)) {
- return this;
- }
- final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
- newState.addAll(origState);
- return setCompilationState(lc, newState);
- }
-
- private FunctionNode setCompilationState(final LexicalContext lc, final EnumSet<CompilationState> compilationState) {
- return Node.replaceInLexicalContext(
- lc,
- this,
- new FunctionNode(
- this,
- lastToken,
- endParserState,
- flags,
- name,
- returnType,
- compileUnit,
- compilationState,
- body,
- parameters,
- thisProperties,
- rootClass, source, namespace));
- }
-
-
- /**
* Create a unique name in the namespace of this FunctionNode
* @param base prefix for name
* @return base if no collision exists, otherwise a name prefix with base
@@ -682,7 +562,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -823,7 +702,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -919,7 +797,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -996,7 +873,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -1070,7 +946,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -1158,7 +1033,6 @@
name,
type,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -1224,7 +1098,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
@@ -1280,7 +1153,6 @@
name,
returnType,
compileUnit,
- compilationState,
body,
parameters,
thisProperties,
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java Fri Sep 18 14:21:22 2015 -0700
@@ -627,7 +627,7 @@
return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
func.createBound(this, new Object[] { name })), 0, Object.class),
testJSAdaptor(adaptee, null, null, null),
- adaptee.getProtoSwitchPoint(__call__, find.getOwner()));
+ adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null);
}
}
throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
@@ -698,7 +698,7 @@
return new GuardedInvocation(
methodHandle,
testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func),
- adaptee.getProtoSwitchPoint(hook, findData.getOwner()));
+ adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null);
}
}
}
@@ -710,7 +710,7 @@
final MethodHandle methodHandle = hook.equals(__put__) ?
MH.asType(Lookup.EMPTY_SETTER, type) :
Lookup.emptyGetter(type.returnType());
- return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoint(hook, null));
+ return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null);
}
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java Fri Sep 18 14:21:22 2015 -0700
@@ -345,7 +345,8 @@
/**
* Given a script object and a Java type, converts the script object into the desired Java type. Currently it
* performs shallow creation of Java arrays, as well as wrapping of objects in Lists, Dequeues, Queues,
- * and Collections. Example:
+ * and Collections. If conversion is not possible or fails for some reason, TypeError is thrown.
+ * Example:
* <pre>
* var anArray = [1, "13", false]
* var javaIntArray = Java.to(anArray, "int[]")
@@ -389,7 +390,11 @@
}
if(targetClass.isArray()) {
- return JSType.toJavaArray(obj, targetClass.getComponentType());
+ try {
+ return JSType.toJavaArray(obj, targetClass.getComponentType());
+ } catch (final Exception exp) {
+ throw typeError(exp, "java.array.conversion.failed", targetClass.getName());
+ }
}
if (targetClass == List.class || targetClass == Deque.class || targetClass == Queue.class || targetClass == Collection.class) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java Fri Sep 18 14:21:22 2015 -0700
@@ -134,7 +134,7 @@
}
@Override
- protected Object invokeNoSuchProperty(final String name, final int programPoint) {
+ protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
final Object retval = createProperty(name);
if (isValid(programPoint)) {
throw new UnwarrantedOptimismException(retval, programPoint);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Fri Sep 18 14:21:22 2015 -0700
@@ -84,7 +84,6 @@
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;
@@ -487,7 +486,6 @@
}
private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int functionLine, final Block body){
- final CompilationState state = errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED;
// Start new block.
final FunctionNode functionNode =
new FunctionNode(
@@ -504,7 +502,6 @@
kind,
function.getFlags(),
body,
- state,
function.getEndParserState());
printAST(functionNode);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AllocationStrategy.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AllocationStrategy.java Fri Sep 18 14:21:22 2015 -0700
@@ -29,6 +29,7 @@
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.ref.WeakReference;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
@@ -53,6 +54,9 @@
/** lazily generated allocator */
private transient MethodHandle allocator;
+ /** Last used allocator map */
+ private transient AllocatorMap lastMap;
+
/**
* Construct an allocation strategy with the given map and class name.
* @param fieldCount number of fields in the allocated object
@@ -71,11 +75,49 @@
return allocatorClassName;
}
- PropertyMap getAllocatorMap() {
- // Create a new map for each function instance
- return PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
+ /**
+ * Get the property map for the allocated object.
+ * @param prototype the prototype object
+ * @return the property map
+ */
+ synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
+ assert prototype != null;
+ final PropertyMap protoMap = prototype.getMap();
+
+ if (lastMap != null) {
+ if (!lastMap.hasSharedProtoMap()) {
+ if (lastMap.hasSamePrototype(prototype)) {
+ return lastMap.allocatorMap;
+ }
+ if (lastMap.hasSameProtoMap(protoMap) && lastMap.hasUnchangedProtoMap()) {
+ // Convert to shared prototype map. Allocated objects will use the same property map
+ // that can be used as long as none of the prototypes modify the shared proto map.
+ final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
+ final SharedPropertyMap sharedProtoMap = new SharedPropertyMap(protoMap);
+ allocatorMap.setSharedProtoMap(sharedProtoMap);
+ prototype.setMap(sharedProtoMap);
+ lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
+ return allocatorMap;
+ }
+ }
+
+ if (lastMap.hasValidSharedProtoMap() && lastMap.hasSameProtoMap(protoMap)) {
+ prototype.setMap(lastMap.getSharedProtoMap());
+ return lastMap.allocatorMap;
+ }
+ }
+
+ final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
+ lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
+
+ return allocatorMap;
}
+ /**
+ * Allocate an object with the given property map
+ * @param map the property map
+ * @return the allocated object
+ */
ScriptObject allocate(final PropertyMap map) {
try {
if (allocator == null) {
@@ -94,4 +136,43 @@
public String toString() {
return "AllocationStrategy[fieldCount=" + fieldCount + "]";
}
+
+ static class AllocatorMap {
+ final private WeakReference<ScriptObject> prototype;
+ final private WeakReference<PropertyMap> prototypeMap;
+
+ private PropertyMap allocatorMap;
+
+ AllocatorMap(final ScriptObject prototype, final PropertyMap protoMap, final PropertyMap allocMap) {
+ this.prototype = new WeakReference<>(prototype);
+ this.prototypeMap = new WeakReference<>(protoMap);
+ this.allocatorMap = allocMap;
+ }
+
+ boolean hasSamePrototype(final ScriptObject proto) {
+ return prototype.get() == proto;
+ }
+
+ boolean hasSameProtoMap(final PropertyMap protoMap) {
+ return prototypeMap.get() == protoMap || allocatorMap.getSharedProtoMap() == protoMap;
+ }
+
+ boolean hasUnchangedProtoMap() {
+ final ScriptObject proto = prototype.get();
+ return proto != null && proto.getMap() == prototypeMap.get();
+ }
+
+ boolean hasSharedProtoMap() {
+ return getSharedProtoMap() != null;
+ }
+
+ boolean hasValidSharedProtoMap() {
+ return hasSharedProtoMap() && getSharedProtoMap().isValidSharedProtoMap();
+ }
+
+ PropertyMap getSharedProtoMap() {
+ return allocatorMap.getSharedProtoMap();
+ }
+
+ }
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java Fri Sep 18 14:21:22 2015 -0700
@@ -101,16 +101,24 @@
public StoredScript loadScript(Source source, String functionKey);
/**
- * Returns a new code installer that shares most of the functionality of this code installer, but uses a
- * new, independent class loader.
- * @return a new code installer with a new independent class loader.
+ * Returns a code installer {@code #isCompatibleWith(CodeInstaller) compatible with} this installer, but
+ * is suitable for on-demand compilations. Can return itself if it is itself suitable.
+ * @return a compatible code installer suitable for on-demand compilations.
*/
- public CodeInstaller withNewLoader();
+ public CodeInstaller getOnDemandCompilationInstaller();
+
+ /**
+ * Returns a code installer {@code #isCompatibleWith(CodeInstaller) compatible with} this installer, but
+ * is suitable for installation of multiple classes that reference each other by name. Should be used when
+ * a compilation job produces multiple compilation units. Can return itself if it is itself suitable.
+ * @return a compatible code installer suitable for installation of multiple classes.
+ */
+ public CodeInstaller getMultiClassCodeInstaller();
/**
* Returns true if this code installer is compatible with the other code installer. Compatibility is expected to be
* an equivalence relation, and installers are supposed to be compatible with those they create using
- * {@link #withNewLoader()}.
+ * {@link #getOnDemandCompilationInstaller()}.
* @param other the other code installer tested for compatibility with this code installer.
* @return true if this code installer is compatible with the other code installer.
*/
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Fri Sep 18 14:21:22 2015 -0700
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.runtime;
+import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
@@ -41,8 +42,10 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
+import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
@@ -61,14 +64,18 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
@@ -87,6 +94,7 @@
import jdk.nashorn.internal.runtime.logging.Logger;
import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
import jdk.nashorn.internal.runtime.options.Options;
+import sun.misc.Unsafe;
/**
* This class manages the global state of execution. Context is immutable.
@@ -128,9 +136,12 @@
private static final String LOAD_FX = "fx:";
private static final String LOAD_NASHORN = "nashorn:";
- private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
- private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
+ private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+ private static final MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
+ private static final LongAdder NAMED_INSTALLED_SCRIPT_COUNT = new LongAdder();
+ private static final LongAdder ANONYMOUS_INSTALLED_SCRIPT_COUNT = new LongAdder();
+ private static final boolean DISABLE_VM_ANONYMOUS_CLASSES = Options.getBooleanProperty("nashorn.disableVmAnonymousClasses");
/**
* Should scripts use only object slots for fields, or dual long/object slots? The default
* behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled
@@ -163,25 +174,24 @@
DebuggerSupport.FORCELOAD = true;
}
+ static long getNamedInstalledScriptCount() {
+ return NAMED_INSTALLED_SCRIPT_COUNT.sum();
+ }
+
+ static long getAnonymousInstalledScriptCount() {
+ return ANONYMOUS_INSTALLED_SCRIPT_COUNT.sum();
+ }
+
/**
* ContextCodeInstaller that has the privilege of installing classes in the Context.
* Can only be instantiated from inside the context and is opaque to other classes
*/
- public static class ContextCodeInstaller implements CodeInstaller {
- private final Context context;
- private final ScriptLoader loader;
- private final CodeSource codeSource;
- private int usageCount = 0;
- private int bytesDefined = 0;
+ private abstract static class ContextCodeInstaller implements CodeInstaller {
+ final Context context;
+ final CodeSource codeSource;
- // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition
- // will occur much earlier, the second is a safety measure for very large scripts/functions.
- private final static int MAX_USAGES = 10;
- private final static int MAX_BYTES_DEFINED = 200_000;
-
- private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
- this.context = context;
- this.loader = loader;
+ ContextCodeInstaller(final Context context, final CodeSource codeSource) {
+ this.context = context;
this.codeSource = codeSource;
}
@@ -191,14 +201,6 @@
}
@Override
- public Class<?> install(final String className, final byte[] bytecode) {
- usageCount++;
- bytesDefined += bytecode.length;
- final String binaryName = Compiler.binaryName(className);
- return loader.installClass(binaryName, bytecode, codeSource);
- }
-
- @Override
public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@@ -250,15 +252,6 @@
}
@Override
- public CodeInstaller withNewLoader() {
- // Reuse this installer if we're within our limits.
- if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) {
- return this;
- }
- return new ContextCodeInstaller(context, context.createNewLoader(), codeSource);
- }
-
- @Override
public boolean isCompatibleWith(final CodeInstaller other) {
if (other instanceof ContextCodeInstaller) {
final ContextCodeInstaller cci = (ContextCodeInstaller)other;
@@ -268,6 +261,116 @@
}
}
+ private static class NamedContextCodeInstaller extends ContextCodeInstaller {
+ private final ScriptLoader loader;
+ private int usageCount = 0;
+ private int bytesDefined = 0;
+
+ // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition
+ // will occur much earlier, the second is a safety measure for very large scripts/functions.
+ private final static int MAX_USAGES = 10;
+ private final static int MAX_BYTES_DEFINED = 200_000;
+
+ private NamedContextCodeInstaller(final Context context, final CodeSource codeSource, final ScriptLoader loader) {
+ super(context, codeSource);
+ this.loader = loader;
+ }
+
+ @Override
+ public Class<?> install(final String className, final byte[] bytecode) {
+ usageCount++;
+ bytesDefined += bytecode.length;
+ NAMED_INSTALLED_SCRIPT_COUNT.increment();
+ return loader.installClass(Compiler.binaryName(className), bytecode, codeSource);
+ }
+
+ @Override
+ public CodeInstaller getOnDemandCompilationInstaller() {
+ // Reuse this installer if we're within our limits.
+ if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) {
+ return this;
+ }
+ return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
+ }
+
+ @Override
+ public CodeInstaller getMultiClassCodeInstaller() {
+ // This installer is perfectly suitable for installing multiple classes that reference each other
+ // as it produces classes with resolvable names, all defined in a single class loader.
+ return this;
+ }
+ }
+
+ private final Map<CodeSource, Reference<Class<?>>> anonymousHostClasses = new ConcurrentHashMap<>();
+
+ private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller {
+ private static final Unsafe UNSAFE = getUnsafe();
+ private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost";
+ private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes();
+
+ private final Class<?> hostClass;
+
+ private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource) {
+ super(context, codeSource);
+ hostClass = getAnonymousHostClass();
+ }
+
+ @Override
+ public Class<?> install(final String className, final byte[] bytecode) {
+ ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment();
+ return UNSAFE.defineAnonymousClass(hostClass, bytecode, null);
+ }
+
+ @Override
+ public CodeInstaller getOnDemandCompilationInstaller() {
+ // This code loader can be indefinitely reused for on-demand recompilations for the same code source.
+ return this;
+ }
+
+ @Override
+ public CodeInstaller getMultiClassCodeInstaller() {
+ // This code loader can not be used to install multiple classes that reference each other, as they
+ // would have no resolvable names. Therefore, in such situation we must revert to an installer that
+ // produces named classes.
+ return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
+ }
+
+ private Class<?> getAnonymousHostClass() {
+ final Reference<Class<?>> ref = context.anonymousHostClasses.get(codeSource);
+ if (ref != null) {
+ final Class<?> existingHostClass = ref.get();
+ if (existingHostClass != null) {
+ return existingHostClass;
+ }
+ }
+ final Class<?> newHostClass = context.createNewLoader().installClass(ANONYMOUS_HOST_CLASS_NAME, ANONYMOUS_HOST_CLASS_BYTES, codeSource);
+ context.anonymousHostClasses.put(codeSource, new WeakReference<>(newHostClass));
+ return newHostClass;
+ }
+
+ private static final byte[] getAnonymousHostClassBytes() {
+ final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null);
+ cw.visitEnd();
+ return cw.toByteArray();
+ }
+
+ private static Unsafe getUnsafe() {
+ return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() {
+ @Override
+ public Unsafe run() {
+ try {
+ final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafeField.setAccessible(true);
+ return (Unsafe)theUnsafeField.get(null);
+ } catch (final ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+ }
+
/** Is Context global debug mode enabled ? */
public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
@@ -1294,9 +1397,15 @@
}
final URL url = source.getURL();
- final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
- final CodeInstaller installer = new ContextCodeInstaller(this, loader, cs);
+ final CodeInstaller installer;
+ if (DISABLE_VM_ANONYMOUS_CLASSES || env._persistent_cache || !env._lazy_compilation) {
+ // Persistent code cache and eager compilation preclude use of VM anonymous classes
+ final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
+ installer = new NamedContextCodeInstaller(this, cs, loader);
+ } else {
+ installer = new AnonymousContextCodeInstaller(this, cs);
+ }
if (storedScript == null) {
final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Debug.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Debug.java Fri Sep 18 14:21:22 2015 -0700
@@ -26,6 +26,8 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.parser.TokenType.EOF;
+
+import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenStream;
@@ -63,6 +65,15 @@
}
/**
+ * Return a formatted script stack trace string with frames information separated by '\n'.
+ * This is a shortcut for {@code NashornException.getScriptStackString(new Throwable())}.
+ * @return formatted stack trace string
+ */
+ public static String scriptStack() {
+ return NashornException.getScriptStackString(new Throwable());
+ }
+
+ /**
* Return the system identity hashcode for an object as a human readable
* string
*
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NativeJavaPackage.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NativeJavaPackage.java Fri Sep 18 14:21:22 2015 -0700
@@ -206,7 +206,7 @@
}
@Override
- protected Object invokeNoSuchProperty(final String key, final int programPoint) {
+ protected Object invokeNoSuchProperty(final String key, final boolean isScope, final int programPoint) {
final Object retval = createProperty(key);
if (isValid(programPoint)) {
throw new UnwarrantedOptimismException(retval, programPoint);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java Fri Sep 18 14:21:22 2015 -0700
@@ -75,16 +75,20 @@
}
/**
- * Return listeners added to this ScriptObject.
+ * Return number of listeners added to a ScriptObject.
* @param obj the object
* @return the listener count
*/
public static int getListenerCount(final ScriptObject obj) {
- final PropertyListeners propertyListeners = obj.getMap().getListeners();
- if (propertyListeners != null) {
- return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size();
- }
- return 0;
+ return obj.getMap().getListenerCount();
+ }
+
+ /**
+ * Return the number of listeners added to this PropertyListeners instance.
+ * @return the listener count;
+ */
+ public int getListenerCount() {
+ return listeners == null ? 0 : listeners.size();
}
// Property listener management methods
@@ -156,7 +160,7 @@
final WeakPropertyMapSet set = listeners.get(prop.getKey());
if (set != null) {
for (final PropertyMap propertyMap : set.elements()) {
- propertyMap.propertyAdded(prop);
+ propertyMap.propertyAdded(prop, false);
}
listeners.remove(prop.getKey());
if (Context.DEBUG) {
@@ -176,7 +180,7 @@
final WeakPropertyMapSet set = listeners.get(prop.getKey());
if (set != null) {
for (final PropertyMap propertyMap : set.elements()) {
- propertyMap.propertyDeleted(prop);
+ propertyMap.propertyDeleted(prop, false);
}
listeners.remove(prop.getKey());
if (Context.DEBUG) {
@@ -198,7 +202,7 @@
final WeakPropertyMapSet set = listeners.get(oldProp.getKey());
if (set != null) {
for (final PropertyMap propertyMap : set.elements()) {
- propertyMap.propertyModified(oldProp, newProp);
+ propertyMap.propertyModified(oldProp, newProp, false);
}
listeners.remove(oldProp.getKey());
if (Context.DEBUG) {
@@ -215,7 +219,7 @@
if (listeners != null) {
for (final WeakPropertyMapSet set : listeners.values()) {
for (final PropertyMap propertyMap : set.elements()) {
- propertyMap.protoChanged();
+ propertyMap.protoChanged(false);
}
}
listeners.clear();
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java Fri Sep 18 14:21:22 2015 -0700
@@ -54,32 +54,36 @@
* All property maps are immutable. If a property is added, modified or removed, the mutator
* will return a new map.
*/
-public final class PropertyMap implements Iterable<Object>, Serializable {
+public class PropertyMap implements Iterable<Object>, Serializable {
/** Used for non extensible PropertyMaps, negative logic as the normal case is extensible. See {@link ScriptObject#preventExtensions()} */
- public static final int NOT_EXTENSIBLE = 0b0000_0001;
+ private static final int NOT_EXTENSIBLE = 0b0000_0001;
/** Does this map contain valid array keys? */
- public static final int CONTAINS_ARRAY_KEYS = 0b0000_0010;
+ private static final int CONTAINS_ARRAY_KEYS = 0b0000_0010;
/** Map status flags. */
- private int flags;
+ private final int flags;
/** Map of properties. */
private transient PropertyHashMap properties;
/** Number of fields in use. */
- private int fieldCount;
+ private final int fieldCount;
/** Number of fields available. */
private final int fieldMaximum;
/** Length of spill in use. */
- private int spillLength;
+ private final int spillLength;
/** Structure class name */
- private String className;
+ private final String className;
+
+ /** A reference to the expected shared prototype property map. If this is set this
+ * property map should only be used if it the same as the actual prototype map. */
+ private transient SharedPropertyMap sharedProtoMap;
/** {@link SwitchPoint}s for gets on inherited properties. */
- private transient HashMap<String, SwitchPoint> protoGetSwitches;
+ private transient HashMap<String, SwitchPoint> protoSwitches;
/** History of maps, used to limit map duplication. */
private transient WeakHashMap<Property, SoftReference<PropertyMap>> history;
@@ -95,24 +99,21 @@
private static final long serialVersionUID = -7041836752008732533L;
/**
- * Constructor.
+ * Constructs a new property map.
*
* @param properties A {@link PropertyHashMap} with initial contents.
* @param fieldCount Number of fields in use.
* @param fieldMaximum Number of fields available.
* @param spillLength Number of spill slots used.
- * @param containsArrayKeys True if properties contain numeric keys
*/
- private PropertyMap(final PropertyHashMap properties, final String className, final int fieldCount,
- final int fieldMaximum, final int spillLength, final boolean containsArrayKeys) {
+ private PropertyMap(final PropertyHashMap properties, final int flags, final String className,
+ final int fieldCount, final int fieldMaximum, final int spillLength) {
this.properties = properties;
this.className = className;
this.fieldCount = fieldCount;
this.fieldMaximum = fieldMaximum;
this.spillLength = spillLength;
- if (containsArrayKeys) {
- setContainsArrayKeys();
- }
+ this.flags = flags;
if (Context.DEBUG) {
count.increment();
@@ -120,20 +121,22 @@
}
/**
- * Cloning constructor.
+ * Constructs a clone of {@code propertyMap} with changed properties, flags, or boundaries.
*
* @param propertyMap Existing property map.
* @param properties A {@link PropertyHashMap} with a new set of properties.
*/
- private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties) {
+ private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties, final int flags, final int fieldCount, final int spillLength) {
this.properties = properties;
- this.flags = propertyMap.flags;
- this.spillLength = propertyMap.spillLength;
- this.fieldCount = propertyMap.fieldCount;
+ this.flags = flags;
+ this.spillLength = spillLength;
+ this.fieldCount = fieldCount;
this.fieldMaximum = propertyMap.fieldMaximum;
+ this.className = propertyMap.className;
// We inherit the parent property listeners instance. It will be cloned when a new listener is added.
this.listeners = propertyMap.listeners;
this.freeSlots = propertyMap.freeSlots;
+ this.sharedProtoMap = propertyMap.sharedProtoMap;
if (Context.DEBUG) {
count.increment();
@@ -142,12 +145,12 @@
}
/**
- * Cloning constructor.
+ * Constructs an exact clone of {@code propertyMap}.
*
* @param propertyMap Existing property map.
*/
- private PropertyMap(final PropertyMap propertyMap) {
- this(propertyMap, propertyMap.properties);
+ protected PropertyMap(final PropertyMap propertyMap) {
+ this(propertyMap, propertyMap.properties, propertyMap.flags, propertyMap.fieldCount, propertyMap.spillLength);
}
private void writeObject(final ObjectOutputStream out) throws IOException {
@@ -183,7 +186,7 @@
*/
public static PropertyMap newMap(final Collection<Property> properties, final String className, final int fieldCount, final int fieldMaximum, final int spillLength) {
final PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties);
- return new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength, false);
+ return new PropertyMap(newProperties, 0, className, fieldCount, fieldMaximum, spillLength);
}
/**
@@ -205,7 +208,7 @@
* @return New empty {@link PropertyMap}.
*/
public static PropertyMap newMap(final Class<? extends ScriptObject> clazz) {
- return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false);
+ return new PropertyMap(EMPTY_HASHMAP, 0, clazz.getName(), 0, 0, 0);
}
/**
@@ -227,12 +230,12 @@
}
/**
- * Get the listeners of this map, or null if none exists
+ * Get the number of listeners of this map
*
- * @return the listeners
+ * @return the number of listeners
*/
- public PropertyListeners getListeners() {
- return listeners;
+ public int getListenerCount() {
+ return listeners == null ? 0 : listeners.getListenerCount();
}
/**
@@ -253,9 +256,12 @@
* A new property is being added.
*
* @param property The new Property added.
+ * @param isSelf was the property added to this map?
*/
- public void propertyAdded(final Property property) {
- invalidateProtoGetSwitchPoint(property);
+ public void propertyAdded(final Property property, final boolean isSelf) {
+ if (!isSelf) {
+ invalidateProtoSwitchPoint(property.getKey());
+ }
if (listeners != null) {
listeners.propertyAdded(property);
}
@@ -265,9 +271,12 @@
* An existing property is being deleted.
*
* @param property The property being deleted.
+ * @param isSelf was the property deleted from this map?
*/
- public void propertyDeleted(final Property property) {
- invalidateProtoGetSwitchPoint(property);
+ public void propertyDeleted(final Property property, final boolean isSelf) {
+ if (!isSelf) {
+ invalidateProtoSwitchPoint(property.getKey());
+ }
if (listeners != null) {
listeners.propertyDeleted(property);
}
@@ -278,9 +287,12 @@
*
* @param oldProperty The old property
* @param newProperty The new property
+ * @param isSelf was the property modified on this map?
*/
- public void propertyModified(final Property oldProperty, final Property newProperty) {
- invalidateProtoGetSwitchPoint(oldProperty);
+ public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) {
+ if (!isSelf) {
+ invalidateProtoSwitchPoint(oldProperty.getKey());
+ }
if (listeners != null) {
listeners.propertyModified(oldProperty, newProperty);
}
@@ -288,9 +300,15 @@
/**
* The prototype of an object associated with this {@link PropertyMap} is changed.
+ *
+ * @param isSelf was the prototype changed on the object using this map?
*/
- public void protoChanged() {
- invalidateAllProtoGetSwitchPoints();
+ public void protoChanged(final boolean isSelf) {
+ if (!isSelf) {
+ invalidateAllProtoSwitchPoints();
+ } else if (sharedProtoMap != null) {
+ sharedProtoMap.invalidateSwitchPoint();
+ }
if (listeners != null) {
listeners.protoChanged();
}
@@ -303,14 +321,14 @@
* @return A shared {@link SwitchPoint} for the property.
*/
public synchronized SwitchPoint getSwitchPoint(final String key) {
- if (protoGetSwitches == null) {
- protoGetSwitches = new HashMap<>();
+ if (protoSwitches == null) {
+ protoSwitches = new HashMap<>();
}
- SwitchPoint switchPoint = protoGetSwitches.get(key);
+ SwitchPoint switchPoint = protoSwitches.get(key);
if (switchPoint == null) {
switchPoint = new SwitchPoint();
- protoGetSwitches.put(key, switchPoint);
+ protoSwitches.put(key, switchPoint);
}
return switchPoint;
@@ -319,19 +337,17 @@
/**
* Indicate that a prototype property has changed.
*
- * @param property {@link Property} to invalidate.
+ * @param key {@link Property} key to invalidate.
*/
- synchronized void invalidateProtoGetSwitchPoint(final Property property) {
- if (protoGetSwitches != null) {
-
- final String key = property.getKey();
- final SwitchPoint sp = protoGetSwitches.get(key);
+ synchronized void invalidateProtoSwitchPoint(final String key) {
+ if (protoSwitches != null) {
+ final SwitchPoint sp = protoSwitches.get(key);
if (sp != null) {
- protoGetSwitches.remove(key);
+ protoSwitches.remove(key);
if (Context.DEBUG) {
protoInvalidations.increment();
}
- SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ SwitchPoint.invalidateAll(new SwitchPoint[]{sp});
}
}
}
@@ -339,15 +355,15 @@
/**
* Indicate that proto itself has changed in hierarchy somewhere.
*/
- synchronized void invalidateAllProtoGetSwitchPoints() {
- if (protoGetSwitches != null) {
- final int size = protoGetSwitches.size();
+ synchronized void invalidateAllProtoSwitchPoints() {
+ if (protoSwitches != null) {
+ final int size = protoSwitches.size();
if (size > 0) {
if (Context.DEBUG) {
protoInvalidations.add(size);
}
- SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[size]));
- protoGetSwitches.clear();
+ SwitchPoint.invalidateAll(protoSwitches.values().toArray(new SwitchPoint[size]));
+ protoSwitches.clear();
}
}
}
@@ -363,7 +379,7 @@
* @return New {@link PropertyMap} with {@link Property} added.
*/
PropertyMap addPropertyBind(final AccessorProperty property, final Object bindTo) {
- // No need to store bound property in the history as bound properties can't be reused.
+ // We must not store bound property in the history as bound properties can't be reused.
return addPropertyNoHistory(new AccessorProperty(property, bindTo));
}
@@ -376,16 +392,16 @@
return property.isSpill() ? slot + fieldMaximum : slot;
}
- // Update boundaries and flags after a property has been added
- private void updateFlagsAndBoundaries(final Property newProperty) {
- if(newProperty.isSpill()) {
- spillLength = Math.max(spillLength, newProperty.getSlot() + 1);
- } else {
- fieldCount = Math.max(fieldCount, newProperty.getSlot() + 1);
- }
- if (isValidArrayIndex(getArrayIndex(newProperty.getKey()))) {
- setContainsArrayKeys();
- }
+ private int newSpillLength(final Property newProperty) {
+ return newProperty.isSpill() ? Math.max(spillLength, newProperty.getSlot() + 1) : spillLength;
+ }
+
+ private int newFieldCount(final Property newProperty) {
+ return !newProperty.isSpill() ? Math.max(fieldCount, newProperty.getSlot() + 1) : fieldCount;
+ }
+
+ private int newFlags(final Property newProperty) {
+ return isValidArrayIndex(getArrayIndex(newProperty.getKey())) ? flags | CONTAINS_ARRAY_KEYS : flags;
}
// Update the free slots bitmap for a property that has been deleted and/or added. This method is not synchronized
@@ -420,13 +436,10 @@
* @param property {@link Property} being added.
* @return New {@link PropertyMap} with {@link Property} added.
*/
- public PropertyMap addPropertyNoHistory(final Property property) {
- if (listeners != null) {
- listeners.propertyAdded(property);
- }
+ public final PropertyMap addPropertyNoHistory(final Property property) {
+ propertyAdded(property, true);
final PropertyHashMap newProperties = properties.immutableAdd(property);
- final PropertyMap newMap = new PropertyMap(this, newProperties);
- newMap.updateFlagsAndBoundaries(property);
+ final PropertyMap newMap = new PropertyMap(this, newProperties, newFlags(property), newFieldCount(property), newSpillLength(property));
newMap.updateFreeSlots(null, property);
return newMap;
@@ -439,16 +452,13 @@
*
* @return New {@link PropertyMap} with {@link Property} added.
*/
- public synchronized PropertyMap addProperty(final Property property) {
- if (listeners != null) {
- listeners.propertyAdded(property);
- }
+ public final synchronized PropertyMap addProperty(final Property property) {
+ propertyAdded(property, true);
PropertyMap newMap = checkHistory(property);
if (newMap == null) {
final PropertyHashMap newProperties = properties.immutableAdd(property);
- newMap = new PropertyMap(this, newProperties);
- newMap.updateFlagsAndBoundaries(property);
+ newMap = new PropertyMap(this, newProperties, newFlags(property), newFieldCount(property), newSpillLength(property));
newMap.updateFreeSlots(null, property);
addToHistory(property, newMap);
}
@@ -463,10 +473,8 @@
*
* @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found.
*/
- public synchronized PropertyMap deleteProperty(final Property property) {
- if (listeners != null) {
- listeners.propertyDeleted(property);
- }
+ public final synchronized PropertyMap deleteProperty(final Property property) {
+ propertyDeleted(property, true);
PropertyMap newMap = checkHistory(property);
final String key = property.getKey();
@@ -477,13 +485,13 @@
// If deleted property was last field or spill slot we can make it reusable by reducing field/slot count.
// Otherwise mark it as free in free slots bitset.
if (isSpill && slot >= 0 && slot == spillLength - 1) {
- newMap = new PropertyMap(newProperties, className, fieldCount, fieldMaximum, spillLength - 1, containsArrayKeys());
+ newMap = new PropertyMap(this, newProperties, flags, fieldCount, spillLength - 1);
newMap.freeSlots = freeSlots;
} else if (!isSpill && slot >= 0 && slot == fieldCount - 1) {
- newMap = new PropertyMap(newProperties, className, fieldCount - 1, fieldMaximum, spillLength, containsArrayKeys());
+ newMap = new PropertyMap(this, newProperties, flags, fieldCount - 1, spillLength);
newMap.freeSlots = freeSlots;
} else {
- newMap = new PropertyMap(this, newProperties);
+ newMap = new PropertyMap(this, newProperties, flags, fieldCount, spillLength);
newMap.updateFreeSlots(property, null);
}
addToHistory(property, newMap);
@@ -500,13 +508,8 @@
*
* @return New {@link PropertyMap} with {@link Property} replaced.
*/
- public PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
- if (listeners != null) {
- listeners.propertyModified(oldProperty, newProperty);
- }
- // Add replaces existing property.
- final PropertyHashMap newProperties = properties.immutableReplace(oldProperty, newProperty);
- final PropertyMap newMap = new PropertyMap(this, newProperties);
+ public final PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
+ propertyModified(oldProperty, newProperty, true);
/*
* See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods.
*
@@ -528,14 +531,17 @@
newProperty instanceof UserAccessorProperty :
"arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getLocalType() + " => " + newProperty.getLocalType() + "]";
- newMap.flags = flags;
-
/*
* spillLength remains same in case (1) and (2) because of slot reuse. Only for case (3), we need
* to add spill count of the newly added UserAccessorProperty property.
*/
+ final int newSpillLength = sameType ? spillLength : Math.max(spillLength, newProperty.getSlot() + 1);
+
+ // Add replaces existing property.
+ final PropertyHashMap newProperties = properties.immutableReplace(oldProperty, newProperty);
+ final PropertyMap newMap = new PropertyMap(this, newProperties, flags, fieldCount, newSpillLength);
+
if (!sameType) {
- newMap.spillLength = Math.max(spillLength, newProperty.getSlot() + 1);
newMap.updateFreeSlots(oldProperty, newProperty);
}
return newMap;
@@ -551,7 +557,7 @@
* @param propertyFlags attribute flags of the property
* @return the newly created UserAccessorProperty
*/
- public UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) {
+ public final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) {
return new UserAccessorProperty(key, propertyFlags, getFreeSpillSlot());
}
@@ -562,7 +568,7 @@
*
* @return {@link Property} matching key.
*/
- public Property findProperty(final String key) {
+ public final Property findProperty(final String key) {
return properties.find(key);
}
@@ -573,12 +579,12 @@
*
* @return New {@link PropertyMap} with added properties.
*/
- public PropertyMap addAll(final PropertyMap other) {
+ public final PropertyMap addAll(final PropertyMap other) {
assert this != other : "adding property map to itself";
final Property[] otherProperties = other.properties.getProperties();
final PropertyHashMap newProperties = properties.immutableAdd(otherProperties);
- final PropertyMap newMap = new PropertyMap(this, newProperties);
+ final PropertyMap newMap = new PropertyMap(this, newProperties, flags, fieldCount, spillLength);
for (final Property property : otherProperties) {
// This method is only safe to use with non-slotted, native getter/setter properties
assert property.getSlot() == -1;
@@ -593,7 +599,7 @@
*
* @return Properties as an array.
*/
- public Property[] getProperties() {
+ public final Property[] getProperties() {
return properties.getProperties();
}
@@ -602,7 +608,7 @@
*
* @return class name of owner objects.
*/
- public String getClassName() {
+ public final String getClassName() {
return className;
}
@@ -612,9 +618,7 @@
* @return New map with {@link #NOT_EXTENSIBLE} flag set.
*/
PropertyMap preventExtensions() {
- final PropertyMap newMap = new PropertyMap(this);
- newMap.flags |= NOT_EXTENSIBLE;
- return newMap;
+ return new PropertyMap(this, properties, flags | NOT_EXTENSIBLE, fieldCount, spillLength);
}
/**
@@ -630,10 +634,7 @@
newProperties = newProperties.immutableAdd(oldProperty.addFlags(Property.NOT_CONFIGURABLE));
}
- final PropertyMap newMap = new PropertyMap(this, newProperties);
- newMap.flags |= NOT_EXTENSIBLE;
-
- return newMap;
+ return new PropertyMap(this, newProperties, flags | NOT_EXTENSIBLE, fieldCount, spillLength);
}
/**
@@ -655,10 +656,7 @@
newProperties = newProperties.immutableAdd(oldProperty.addFlags(propertyFlags));
}
- final PropertyMap newMap = new PropertyMap(this, newProperties);
- newMap.flags |= NOT_EXTENSIBLE;
-
- return newMap;
+ return new PropertyMap(this, newProperties, flags | NOT_EXTENSIBLE, fieldCount, spillLength);
}
/**
@@ -830,13 +828,6 @@
}
/**
- * Flag this object as having array keys in defined properties
- */
- private void setContainsArrayKeys() {
- flags |= CONTAINS_ARRAY_KEYS;
- }
-
- /**
* Test to see if {@link PropertyMap} is extensible.
*
* @return {@code true} if {@link PropertyMap} can be added to.
@@ -914,12 +905,72 @@
setProtoNewMapCount.increment();
}
- final PropertyMap newMap = new PropertyMap(this);
+ final PropertyMap newMap = makeUnsharedCopy();
addToProtoHistory(newProto, newMap);
return newMap;
}
+ /**
+ * Make a copy of this property map with the shared prototype field set to null. Note that this is
+ * only necessary for shared maps of top-level objects. Shared prototype maps represented by
+ * {@link SharedPropertyMap} are automatically converted to plain property maps when they evolve.
+ *
+ * @return a copy with the shared proto map unset
+ */
+ PropertyMap makeUnsharedCopy() {
+ final PropertyMap newMap = new PropertyMap(this);
+ newMap.sharedProtoMap = null;
+ return newMap;
+ }
+
+ /**
+ * Set a reference to the expected parent prototype map. This is used for class-like
+ * structures where we only want to use a top-level property map if all of the
+ * prototype property maps have not been modified.
+ *
+ * @param protoMap weak reference to the prototype property map
+ */
+ void setSharedProtoMap(final SharedPropertyMap protoMap) {
+ sharedProtoMap = protoMap;
+ }
+
+ /**
+ * Get the expected prototype property map if it is known, or null.
+ *
+ * @return parent map or null
+ */
+ public PropertyMap getSharedProtoMap() {
+ return sharedProtoMap;
+ }
+
+ /**
+ * Returns {@code true} if this map has been used as a shared prototype map (i.e. as a prototype
+ * for a JavaScript constructor function) and has not had properties added, deleted or replaced since then.
+ * @return true if this is a valid shared prototype map
+ */
+ boolean isValidSharedProtoMap() {
+ return false;
+ }
+
+ /**
+ * Returns the shared prototype switch point, or null if this is not a shared prototype map.
+ * @return the shared prototype switch point, or null
+ */
+ SwitchPoint getSharedProtoSwitchPoint() {
+ return null;
+ }
+
+ /**
+ * Return true if this map has a shared prototype map which has either been invalidated or does
+ * not match the map of {@code proto}.
+ * @param prototype the prototype object
+ * @return true if this is an invalid shared map for {@code prototype}
+ */
+ boolean isInvalidSharedMapFor(final ScriptObject prototype) {
+ return sharedProtoMap != null
+ && (!sharedProtoMap.isValidSharedProtoMap() || prototype == null || sharedProtoMap != prototype.getMap());
+ }
/**
* {@link PropertyMap} iterator.
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Fri Sep 18 14:21:22 2015 -0700
@@ -368,8 +368,8 @@
}
@Override
- PropertyMap getAllocatorMap() {
- return allocationStrategy.getAllocatorMap();
+ PropertyMap getAllocatorMap(final ScriptObject prototype) {
+ return allocationStrategy.getAllocatorMap(prototype);
}
@Override
@@ -649,7 +649,7 @@
*/
private CodeInstaller getInstallerForNewCode() {
final ScriptEnvironment env = installer.getContext().getEnv();
- return env._optimistic_types || env._loader_per_compile ? installer.withNewLoader() : installer;
+ return env._optimistic_types || env._loader_per_compile ? installer.getOnDemandCompilationInstaller() : installer;
}
Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunction.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunction.java Fri Sep 18 14:21:22 2015 -0700
@@ -521,35 +521,39 @@
assert !isBoundFunction(); // allocate never invoked on bound functions
- final ScriptObject object = data.allocate(getAllocatorMap());
+ final ScriptObject prototype = getAllocatorPrototype();
+ final ScriptObject object = data.allocate(getAllocatorMap(prototype));
if (object != null) {
- final Object prototype = getPrototype();
- if (prototype instanceof ScriptObject) {
- object.setInitialProto((ScriptObject) prototype);
- }
-
- if (object.getProto() == null) {
- object.setInitialProto(getObjectPrototype());
- }
+ object.setInitialProto(prototype);
}
return object;
}
- private PropertyMap getAllocatorMap() {
- if (allocatorMap == null) {
- allocatorMap = data.getAllocatorMap();
+ /**
+ * Get the property map used by "allocate"
+ * @param prototype actual prototype object
+ * @return property map
+ */
+ private synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
+ if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) {
+ // The prototype map has changed since this function was last used as constructor.
+ // Get a new allocator map.
+ allocatorMap = data.getAllocatorMap(prototype);
}
return allocatorMap;
}
/**
- * Return Object.prototype - used by "allocate"
- *
- * @return Object.prototype
+ * Return the actual prototype used by "allocate"
+ * @return allocator prototype
*/
- protected final ScriptObject getObjectPrototype() {
+ private ScriptObject getAllocatorPrototype() {
+ final Object prototype = getPrototype();
+ if (prototype instanceof ScriptObject) {
+ return (ScriptObject) prototype;
+ }
return Global.objectPrototype();
}
@@ -591,10 +595,10 @@
*
* @param newPrototype new prototype object
*/
- public final void setPrototype(Object newPrototype) {
+ public synchronized final void setPrototype(final Object newPrototype) {
if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) {
- // Replace our current allocator map with one that is associated with the new prototype.
- allocatorMap = allocatorMap.changeProto((ScriptObject) newPrototype);
+ // Unset allocator map to be replaced with one matching the new prototype.
+ allocatorMap = null;
}
this.prototype = newPrototype;
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunctionData.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunctionData.java Fri Sep 18 14:21:22 2015 -0700
@@ -389,9 +389,10 @@
/**
* Get the property map to use for objects allocated by this function.
*
+ * @param prototype the prototype of the allocated object
* @return the property map for allocated objects.
*/
- PropertyMap getAllocatorMap() {
+ PropertyMap getAllocatorMap(final ScriptObject prototype) {
return null;
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Fri Sep 18 14:21:22 2015 -0700
@@ -149,7 +149,7 @@
/** Method handle to retrieve prototype of this object */
public static final MethodHandle GETPROTO = findOwnMH_V("getProto", ScriptObject.class);
- static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class);
+ static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class, boolean.class);
static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class);
static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class);
@@ -809,9 +809,11 @@
if (deep) {
final ScriptObject myProto = getProto();
- if (myProto != null) {
- return myProto.findProperty(key, deep, start);
- }
+ final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, start);
+ // checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate
+ // shared proto invalidation up the prototype chain. It also must be invoked when prototype is null.
+ checkSharedProtoMap();
+ return find;
}
return null;
@@ -832,7 +834,7 @@
if (deep) {
final ScriptObject myProto = getProto();
if (myProto != null) {
- return myProto.hasProperty(key, deep);
+ return myProto.hasProperty(key, true);
}
}
@@ -1258,11 +1260,8 @@
if (oldProto != newProto) {
proto = newProto;
- // Let current listeners know that the prototype has changed and set our map
- final PropertyListeners listeners = getMap().getListeners();
- if (listeners != null) {
- listeners.protoChanged();
- }
+ // Let current listeners know that the prototype has changed
+ getMap().protoChanged(true);
// Replace our current allocator map with one that is associated with the new prototype.
setMap(getMap().changeProto(newProto));
}
@@ -1314,7 +1313,7 @@
}
p = p.getProto();
}
- setProto((ScriptObject)newProto);
+ setProto((ScriptObject) newProto);
} else {
throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
}
@@ -2012,11 +2011,11 @@
final ScriptObject owner = find.getOwner();
final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class;
- final SwitchPoint protoSwitchPoint;
+ final SwitchPoint[] protoSwitchPoints;
if (mh == null) {
mh = Lookup.emptyGetter(returnType);
- protoSwitchPoint = getProtoSwitchPoint(name, owner);
+ protoSwitchPoints = getProtoSwitchPoints(name, owner);
} else if (!find.isSelf()) {
assert mh.type().returnType().equals(returnType) :
"return type mismatch for getter " + mh.type().returnType() + " != " + returnType;
@@ -2024,30 +2023,30 @@
// Add a filter that replaces the self object with the prototype owning the property.
mh = addProtoFilter(mh, find.getProtoChainLength());
}
- protoSwitchPoint = getProtoSwitchPoint(name, owner);
+ protoSwitchPoints = getProtoSwitchPoints(name, owner);
} else {
- protoSwitchPoint = null;
+ protoSwitchPoints = null;
}
- final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
+ final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception);
return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: " + desc + " " + name + " " +isMethod);
- final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
+ final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, NashornCallSiteDescriptor.isScope(desc));
final MethodHandle guard = getScriptObjectGuard(desc.getMethodType(), true);
return new GuardedInvocation(invoker, guard);
}
@SuppressWarnings("unused")
- private Object megamorphicGet(final String key, final boolean isMethod) {
+ private Object megamorphicGet(final String key, final boolean isMethod, final boolean isScope) {
final FindProperty find = findProperty(key, true);
if (find != null) {
return find.getObjectValue();
}
- return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
+ return isMethod ? getNoSuchMethod(key, isScope, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, isScope, INVALID_PROGRAM_POINT);
}
// Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST
@@ -2128,17 +2127,32 @@
* @param owner the property owner, null if property is not defined
* @return a SwitchPoint or null
*/
- public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) {
+ public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) {
if (owner == this || getProto() == null) {
return null;
}
+ final List<SwitchPoint> switchPoints = new ArrayList<>();
for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
final ScriptObject parent = obj.getProto();
parent.getMap().addListener(name, obj.getMap());
+ final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint();
+ if (sp != null && !sp.hasBeenInvalidated()) {
+ switchPoints.add(sp);
+ }
}
- return getMap().getSwitchPoint(name);
+ switchPoints.add(getMap().getSwitchPoint(name));
+ return switchPoints.toArray(new SwitchPoint[switchPoints.size()]);
+ }
+
+ private void checkSharedProtoMap() {
+ // Check if our map has an expected shared prototype property map. If it has, make sure that
+ // the prototype map has not been invalidated, and that it does match the actual map of the prototype.
+ if (getMap().isInvalidSharedMapFor(getProto())) {
+ // Change our own map to one that does not assume a shared prototype map.
+ setMap(getMap().makeUnsharedCopy());
+ }
}
/**
@@ -2220,7 +2234,7 @@
return new GuardedInvocation(
Lookup.EMPTY_SETTER,
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
- getProtoSwitchPoint(name, null),
+ getProtoSwitchPoints(name, null),
explicitInstanceOfCheck ? null : ClassCastException.class);
}
@@ -2366,7 +2380,7 @@
find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
find.getProtoChainLength(),
func),
- getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
+ getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()),
//TODO this doesn't need a ClassCastException as guard always checks script object
null);
}
@@ -2382,20 +2396,21 @@
/**
* Invoke fall back if a property is not found.
* @param name Name of property.
+ * @param isScope is this a scope access?
* @param programPoint program point
* @return Result from call.
*/
- protected Object invokeNoSuchProperty(final String name, final int programPoint) {
+ protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
+ final Object func = (find != null)? find.getObjectValue() : null;
Object ret = UNDEFINED;
-
- if (find != null) {
- final Object func = find.getObjectValue();
-
- if (func instanceof ScriptFunction) {
- ret = ScriptRuntime.apply((ScriptFunction)func, this, name);
- }
+ if (func instanceof ScriptFunction) {
+ final ScriptFunction sfunc = (ScriptFunction)func;
+ final Object self = isScope && sfunc.isStrict()? UNDEFINED : this;
+ ret = ScriptRuntime.apply(sfunc, self, name);
+ } else if (isScope) {
+ throw referenceError("not.defined", name);
}
if (isValid(programPoint)) {
@@ -2409,21 +2424,27 @@
/**
* Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
* @param name the method name
+ * @param isScope is this a scope access?
* @return the bound function, or undefined
*/
- private Object getNoSuchMethod(final String name, final int programPoint) {
+ private Object getNoSuchMethod(final String name, final boolean isScope, final int programPoint) {
final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
if (find == null) {
- return invokeNoSuchProperty(name, programPoint);
+ return invokeNoSuchProperty(name, isScope, programPoint);
}
final Object value = find.getObjectValue();
if (!(value instanceof ScriptFunction)) {
+ if (isScope) {
+ throw referenceError("not.defined", name);
+ }
return UNDEFINED;
}
- return ((ScriptFunction)value).createBound(this, new Object[] {name});
+ final ScriptFunction func = (ScriptFunction)value;
+ final Object self = isScope && func.isStrict()? UNDEFINED : this;
+ return func.createBound(self, new Object[] {name});
}
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) {
@@ -2432,7 +2453,7 @@
}
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
- NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null),
+ NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null),
explicitInstanceOfCheck ? null : ClassCastException.class);
}
@@ -2738,7 +2759,7 @@
}
}
- return JSType.toInt32(invokeNoSuchProperty(key, programPoint));
+ return JSType.toInt32(invokeNoSuchProperty(key, false, programPoint));
}
@Override
@@ -2820,7 +2841,7 @@
}
}
- return JSType.toLong(invokeNoSuchProperty(key, programPoint));
+ return JSType.toLong(invokeNoSuchProperty(key, false, programPoint));
}
@Override
@@ -2902,7 +2923,7 @@
}
}
- return JSType.toNumber(invokeNoSuchProperty(key, INVALID_PROGRAM_POINT));
+ return JSType.toNumber(invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT));
}
@Override
@@ -2983,7 +3004,7 @@
}
}
- return invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
+ return invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT);
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java Fri Sep 18 14:21:22 2015 -0700
@@ -186,10 +186,7 @@
private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) {
final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint);
- final PropertyListeners listeners = map.getListeners();
- if (listeners != null) {
- listeners.propertyAdded(sm.property);
- }
+ map.propertyAdded(sm.property, true);
return sm;
}
@@ -204,7 +201,7 @@
//fast type specific setter
final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already
- //slow setter, that calls ScriptObject.set with appropraite type and key name
+ //slow setter, that calls ScriptObject.set with appropriate type and key name
MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)];
slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.getFlags(desc));
slowSetter = MH.insertArguments(slowSetter, 1, name);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SharedPropertyMap.java Fri Sep 18 14:21:22 2015 -0700
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.runtime;
+
+import java.lang.invoke.SwitchPoint;
+
+/**
+ * This class represents a property map that can be shared among multiple prototype objects, allowing all inheriting
+ * top-level objects to also share one property map. This is class is only used for prototype objects, the
+ * top-level objects use ordinary {@link PropertyMap}s with the {@link PropertyMap#sharedProtoMap} field
+ * set to the expected shared prototype map.
+ *
+ * <p>When an instance of this class is evolved because a property is added, removed, or modified in an object
+ * using it, the {@link #invalidateSwitchPoint()} method is invoked to signal to all callsites and inheriting
+ * objects that the assumption of a single shared prototype map is no longer valid. The property map resulting
+ * from the modification will no longer be an instance of this class.</p>
+ */
+public final class SharedPropertyMap extends PropertyMap {
+
+ private SwitchPoint switchPoint;
+
+ private static final long serialVersionUID = 2166297719721778876L;
+
+ /**
+ * Create a new shared property map from the given {@code map}.
+ * @param map property map to copy
+ */
+ public SharedPropertyMap(final PropertyMap map) {
+ super(map);
+ this.switchPoint = new SwitchPoint();
+ }
+
+ @Override
+ public void propertyAdded(final Property property, final boolean isSelf) {
+ if (isSelf) {
+ invalidateSwitchPoint();
+ }
+ super.propertyAdded(property, isSelf);
+ }
+
+ @Override
+ public void propertyDeleted(final Property property, final boolean isSelf) {
+ if (isSelf) {
+ invalidateSwitchPoint();
+ }
+ super.propertyDeleted(property, isSelf);
+ }
+
+ @Override
+ public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) {
+ if (isSelf) {
+ invalidateSwitchPoint();
+ }
+ super.propertyModified(oldProperty, newProperty, isSelf);
+ }
+
+ @Override
+ synchronized boolean isValidSharedProtoMap() {
+ return switchPoint != null;
+ }
+
+ @Override
+ synchronized SwitchPoint getSharedProtoSwitchPoint() {
+ return switchPoint;
+ }
+
+ /**
+ * Invalidate the shared prototype switch point if this is a shared prototype map.
+ */
+ synchronized void invalidateSwitchPoint() {
+ if (switchPoint != null) {
+ assert !switchPoint.hasBeenInvalidated();
+ SwitchPoint.invalidateAll(new SwitchPoint[]{ switchPoint });
+ switchPoint = null;
+ }
+ }
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java Fri Sep 18 14:21:22 2015 -0700
@@ -220,19 +220,20 @@
}
final long total = t - startTime;
- sb.append('\n');
- sb.append("Total runtime: ").
+ return sb.append("\nTotal runtime: ").
append(toMillisPrint(total)).
append(" ms (Non-runtime: ").
append(toMillisPrint(knownTime)).
append(" ms [").
append((int)(knownTime * 100.0 / total)).
- append("%])");
-
- sb.append("\n\nEmitted compile units: ").
- append(CompileUnit.getEmittedUnitCount());
-
- return sb.toString();
+ append("%])").
+ append("\n\nEmitted compile units: ").
+ append(CompileUnit.getEmittedUnitCount()).
+ append("\nCompile units installed as named classes: ").
+ append(Context.getNamedInstalledScriptCount()).
+ append("\nCompile units installed as anonymous classes: ").
+ append(Context.getAnonymousInstalledScriptCount()).
+ toString();
}
private void accumulateTime(final String module, final long duration) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java Fri Sep 18 14:21:22 2015 -0700
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -45,7 +46,7 @@
*
*/
public final class WithObject extends Scope {
- private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint.class);
+ private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint[].class);
private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
@@ -209,16 +210,18 @@
}
@Override
- protected Object invokeNoSuchProperty(final String name, final int programPoint) {
+ protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true);
if (find != null) {
final Object func = find.getObjectValue();
if (func instanceof ScriptFunction) {
- return ScriptRuntime.apply((ScriptFunction)func, expression, name);
+ final ScriptFunction sfunc = (ScriptFunction)func;
+ final Object self = isScope && sfunc.isStrict()? UNDEFINED : expression;
+ return ScriptRuntime.apply(sfunc, self, name);
}
}
- return getProto().invokeNoSuchProperty(name, programPoint);
+ return getProto().invokeNoSuchProperty(name, isScope, programPoint);
}
@Override
@@ -357,13 +360,24 @@
private MethodHandle expressionGuard(final String name, final ScriptObject owner) {
final PropertyMap map = expression.getMap();
- final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner);
+ final SwitchPoint[] sp = expression.getProtoSwitchPoints(name, owner);
return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp);
}
@SuppressWarnings("unused")
- private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint sp) {
- return ((WithObject)receiver).expression.getMap() == map && (sp == null || !sp.hasBeenInvalidated());
+ private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint[] sp) {
+ return ((WithObject)receiver).expression.getMap() == map && !hasBeenInvalidated(sp);
+ }
+
+ private static boolean hasBeenInvalidated(final SwitchPoint[] switchPoints) {
+ if (switchPoints != null) {
+ for (final SwitchPoint switchPoint : switchPoints) {
+ if (switchPoint.hasBeenInvalidated()) {
+ return true;
+ }
+ }
+ }
+ return false;
}
/**
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Fri Sep 18 14:21:22 2015 -0700
@@ -150,6 +150,7 @@
type.error.method.not.constructor=Java method {0} cannot be used as a constructor.
type.error.env.not.object=$ENV must be an Object.
type.error.unsupported.java.to.type=Unsupported Java.to target type {0}.
+type.error.java.array.conversion.failed=Java.to conversion to array type {0} failed
type.error.constructor.requires.new=Constructor {0} requires "new".
type.error.new.on.nonpublic.javatype=new cannot be used with non-public java type {0}.
--- a/nashorn/test/script/basic/JDK-8044750.js Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/test/script/basic/JDK-8044750.js Fri Sep 18 14:21:22 2015 -0700
@@ -25,6 +25,8 @@
* JDK-8044750: megamorphic getter for scope objects does not call __noSuchProperty__ hook
*
* @test
+ * @fork
+ * @option -Dnashorn.unstable.relink.threshold=16
* @run
*/
@@ -40,7 +42,9 @@
}
}
-for (var i = 0; i < 20; i++) {
+var LIMIT = 20; // should be more than megamorphic threshold set via @option
+
+for (var i = 0; i < LIMIT; i++) {
var obj = {};
obj.foo = i;
obj[i] = i;
@@ -51,3 +55,30 @@
// callsite inside func should see __noSuchProperty__
// hook on global scope object.
func({});
+
+function checkFoo() {
+ with({}) {
+ try {
+ foo;
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+}
+
+var oldNoSuchProperty = this.__noSuchProperty__;
+delete this.__noSuchProperty__;
+
+// keep deleting/restorting __noSuchProperty__ alternatively
+// to make "foo" access in checkFoo function megamorphic!
+
+for (var i = 0; i < LIMIT; i++) {
+ // no __noSuchProperty__ and 'with' scope object has no 'foo'
+ delete __noSuchProperty__;
+ Assert.assertFalse(checkFoo(), "Expected false in iteration " + i);
+
+ // __noSuchProperty__ is exists but 'with' scope object has no 'foo'
+ this.__noSuchProperty__ = oldNoSuchProperty;
+ Assert.assertTrue(checkFoo(), "Expected true in iteration " + i);
+}
--- a/nashorn/test/script/basic/JDK-8134569.js Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/test/script/basic/JDK-8134569.js Fri Sep 18 14:21:22 2015 -0700
@@ -62,67 +62,146 @@
return new C();
}
+function createDeeper() {
+ function C() {
+ this.i1 = 1;
+ this.i2 = 2;
+ this.i3 = 3;
+ return this;
+ }
+ function D() {
+ this.p1 = 1;
+ this.p2 = 2;
+ this.p3 = 3;
+ return this;
+ }
+ function E() {
+ this.e1 = 1;
+ this.e2 = 2;
+ this.e3 = 3;
+ return this;
+ }
+ D.prototype = new E();
+ C.prototype = new D();
+ return new C();
+}
+
function createEval() {
return eval("Object.create({})");
}
function p(o) { print(o.x) }
-var a, b;
+function e(o) { print(o.e1) }
+
+var a, b, c;
create();
a = create();
b = create();
+c = create();
a.__proto__.x = 123;
p(a);
p(b);
+p(c);
a = create();
b = create();
+c = create();
b.__proto__.x = 123;
p(a);
p(b);
+p(c);
a = createEmpty();
b = createEmpty();
+c = createEmpty();
a.__proto__.x = 123;
p(a);
p(b);
+p(c);
a = createEmpty();
b = createEmpty();
+c = createEmpty();
b.__proto__.x = 123;
p(a);
p(b);
+p(c);
a = createDeep();
b = createDeep();
+c = createDeep();
a.__proto__.__proto__.x = 123;
p(a);
p(b);
+p(c);
a = createDeep();
b = createDeep();
+c = createDeep();
b.__proto__.__proto__.x = 123;
p(a);
p(b);
+p(c);
+
+a = createDeeper();
+b = createDeeper();
+c = createDeeper();
+a.__proto__.__proto__.__proto__.x = 123;
+
+p(a);
+p(b);
+p(c);
+
+a = createDeeper();
+b = createDeeper();
+c = createDeeper();
+b.__proto__.__proto__.__proto__.x = 123;
+
+p(a);
+p(b);
+p(c);
+
+a = createDeeper();
+b = createDeeper();
+c = createDeeper();
+a.__proto__.__proto__ = null;
+
+e(a);
+e(b);
+e(c);
+
+a = createDeeper();
+b = createDeeper();
+c = createDeeper();
+b.__proto__.__proto__ = null;
+
+e(a);
+e(b);
+e(c);
+
a = createEval();
b = createEval();
+c = createEval();
a.__proto__.x = 123;
p(a);
p(b);
+p(c);
a = createEval();
b = createEval();
+c = createEval();
b.__proto__.x = 123;
p(a);
p(b);
+p(c);
--- a/nashorn/test/script/basic/JDK-8134569.js.EXPECTED Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/test/script/basic/JDK-8134569.js.EXPECTED Fri Sep 18 14:21:22 2015 -0700
@@ -1,16 +1,36 @@
123
undefined
undefined
+undefined
123
+undefined
123
undefined
undefined
+undefined
+123
+undefined
+123
+undefined
+undefined
+undefined
123
+undefined
123
undefined
undefined
+undefined
123
+undefined
+undefined
+1
+1
+1
+undefined
+1
123
undefined
undefined
+undefined
123
+undefined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8134609.js Fri Sep 18 14:21:22 2015 -0700
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8134609: Allow constructors with same prototoype map to share the allocator map
+ *
+ * @test
+ * @run
+ * @fork
+ * @option -Dnashorn.debug
+ */
+
+function createProto(members) {
+ function P() {
+ for (var id in members) {
+ if (members.hasOwnProperty(id)) {
+ this[id] = members[id];
+ }
+ }
+ return this;
+ }
+ return new P();
+}
+
+function createSubclass(prototype, members) {
+ function C() {
+ for (var id in members) {
+ if (members.hasOwnProperty(id)) {
+ this[id] = members[id];
+ }
+ }
+ return this;
+ }
+
+ C.prototype = prototype;
+
+ return new C();
+}
+
+function assertP1(object, value) {
+ Assert.assertTrue(object.p1 === value);
+}
+
+// First prototype will have non-shared proto-map. Second and third will be shared.
+var proto0 = createProto({p1: 0, p2: 1});
+var proto1 = createProto({p1: 1, p2: 2});
+var proto2 = createProto({p1: 2, p2: 3});
+
+Assert.assertTrue(Debug.map(proto1) === Debug.map(proto2));
+
+assertP1(proto1, 1);
+assertP1(proto2, 2);
+
+// First instantiation will have a non-shared prototype map, from the second one
+// maps will be shared until a different proto map comes along.
+var child0 = createSubclass(proto1, {c1: 1, c2: 2});
+var child1 = createSubclass(proto2, {c1: 2, c2: 3});
+var child2 = createSubclass(proto1, {c1: 3, c2: 4});
+var child3 = createSubclass(proto2, {c1: 1, c2: 2});
+var child4 = createSubclass(proto0, {c1: 3, c2: 2});
+
+Assert.assertTrue(Debug.map(child1) === Debug.map(child2));
+Assert.assertTrue(Debug.map(child1) === Debug.map(child3));
+Assert.assertTrue(Debug.map(child3) !== Debug.map(child4));
+
+assertP1(child1, 2);
+assertP1(child2, 1);
+assertP1(child3, 2);
+assertP1(child4, 0);
+
+Assert.assertTrue(delete proto2.p1);
+
+assertP1(child3, undefined);
+assertP1(child2, 1);
+Assert.assertTrue(Debug.map(child1) !== Debug.map(child3));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8136544.js Fri Sep 18 14:21:22 2015 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8136544: Call site switching to megamorphic causes incorrect property read
+ *
+ * @test
+ * @fork
+ * @option -Dnashorn.unstable.relink.threshold=8
+ * @run
+ */
+
+var ScriptContext = Java.type("javax.script.ScriptContext");
+var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager");
+var m = new ScriptEngineManager();
+var e = m.getEngineByName("nashorn");
+
+var scope = e.getBindings(ScriptContext.ENGINE_SCOPE);
+var MYVAR = "myvar";
+
+function loopupVar() {
+ try {
+ e.eval(MYVAR);
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+
+// make sure we exercise callsite beyond megamorphic threshold we set
+// in this test via nashorn.unstable.relink.threshold property
+// In each iteration, callsite is exercised twice (two evals)
+// So, LIMIT should be more than 4 to exercise megamorphic callsites.
+
+var LIMIT = 5; // This LIMIT should be more than 4
+
+for (var i = 0; i < LIMIT; i++) {
+ // remove the variable and lookup
+ delete scope[MYVAR];
+ Assert.assertFalse(loopupVar(), "Expected true in iteration " + i);
+
+ // set that variable and check again
+ scope[MYVAR] = "foo";
+ Assert.assertTrue(loopupVar(), "Expected false in iteration " + i);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8136694.js Fri Sep 18 14:21:22 2015 -0700
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8136694: Megemorphic scope access does not throw ReferenceError when property is missing
+ *
+ * @test
+ * @fork
+ * @option -Dnashorn.unstable.relink.threshold=16
+ * @run
+ */
+
+function checkFoo() {
+ try {
+ // The 'foo' access becomes megamorphic
+ foo;
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+
+
+// Similar check for 'with' blocks as well.
+function checkFooInWith() {
+ with({}) {
+ try {
+ // The 'foo' access becomes megamorphic
+ foo;
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+}
+
+function loop(checker) {
+ // LIMIT has to be more than the megamorphic threashold
+ // set via @option in this test header!
+ var LIMIT = 20;
+ for (var i = 0; i < LIMIT; i++) {
+ // make sure global has no "foo"
+ delete foo;
+ Assert.assertFalse(checker(), "Expected false in interation " + i);
+
+ // now add 'foo' in global
+ foo = 44;
+ Assert.assertTrue(checker(), "Expected true in interation " + i);
+ }
+}
+
+
+loop(checkFoo);
+loop(checkFooInWith);
--- a/nashorn/test/script/basic/javaarrayconversion.js Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/test/script/basic/javaarrayconversion.js Fri Sep 18 14:21:22 2015 -0700
@@ -128,24 +128,32 @@
// Converting to string, toString takes precedence over valueOf
test({ valueOf: function() { return "42"; }, toString: function() { return "43"; } }, "java.lang.String", "43")
+function assertCanConvert(sourceType, targetType) {
+ Java.to([new (Java.type(sourceType))()], targetType + "[]")
+ ++testCount;
+}
+
function assertCantConvert(sourceType, targetType) {
try {
- Java.to([new Java.type(sourceType)()], targetType + "[]")
+ Java.to([new (Java.type(sourceType))()], targetType + "[]")
throw "no TypeError encountered"
} catch(e) {
- if(!(e instanceof TypeError)) {
+ if(!(e instanceof TypeError) ||
+ !e.message.startsWith("Java.to conversion to array type")) {
throw e;
}
++testCount;
}
}
+// Arbitrary POJOs to JS Primitive type should work
+assertCanConvert("java.util.BitSet", "int")
+assertCanConvert("java.util.BitSet", "double")
+assertCanConvert("java.util.BitSet", "long")
+assertCanConvert("java.util.BitSet", "boolean")
+assertCanConvert("java.util.BitSet", "java.lang.String")
+
// Arbitrary POJOs can't be converted to Java values
-assertCantConvert("java.util.BitSet", "int")
-assertCantConvert("java.util.BitSet", "double")
-assertCantConvert("java.util.BitSet", "long")
-assertCantConvert("java.util.BitSet", "boolean")
-assertCantConvert("java.util.BitSet", "java.lang.String")
assertCantConvert("java.util.BitSet", "java.lang.Double")
assertCantConvert("java.util.BitSet", "java.lang.Long")
--- a/nashorn/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Fri Sep 18 10:46:55 2015 -0700
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/test/ScopeTest.java Fri Sep 18 14:21:22 2015 -0700
@@ -26,6 +26,7 @@
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import javax.script.Bindings;
@@ -820,4 +821,38 @@
public void recursiveEvalCallScriptContextTest() throws ScriptException {
new RecursiveEval().program();
}
+
+ private static final String VAR_NAME = "myvar";
+
+ private static boolean lookupVar(final ScriptEngine engine, final String varName) {
+ try {
+ engine.eval(varName);
+ return true;
+ } catch (final ScriptException se) {
+ return false;
+ }
+ }
+
+ // @bug 8136544: Call site switching to megamorphic causes incorrect property read
+ @Test
+ public void megamorphicPropertyReadTest() throws ScriptException {
+ final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+ final ScriptEngine engine = factory.getScriptEngine();
+ final Bindings scope = engine.getBindings(ScriptContext.ENGINE_SCOPE);
+ boolean ret;
+
+ // Why 16 is the upper limit of this loop? The default nashorn dynalink megamorphic threshold is 16.
+ // See jdk.nashorn.internal.runtime.linker.Bootstrap.NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD
+ // We do, 'eval' of the same in this loop twice. So, 16*2 = 32 times that callsite in the script
+ // is exercised - much beyond the default megamorphic threshold.
+
+ for (int i = 0; i < 16; i++) {
+ scope.remove(VAR_NAME);
+ ret = lookupVar(engine, VAR_NAME);
+ assertFalse(ret, "Expected false in iteration " + i);
+ scope.put(VAR_NAME, "foo");
+ ret = lookupVar(engine, VAR_NAME);
+ assertTrue(ret, "Expected true in iteration " + i);
+ }
+ }
}