8057588: Lots of trivial (empty) classes were generated by the Nashorn compiler as part of restOf-method generation
Reviewed-by: attila, sundar, hannesw
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Sep 05 16:28:02 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Sep 05 16:28:17 2014 +0200
@@ -48,10 +48,12 @@
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.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.CompileUnitHolder;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
@@ -301,6 +303,71 @@
},
/**
+ * Mark compile units used in the method tree. Used for non-rest compilation only
+ */
+ MARK_USED_COMPILE_UNITS(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT,
+ SYMBOLS_ASSIGNED,
+ SCOPE_DEPTHS_COMPUTED,
+ OPTIMISTIC_TYPES_ASSIGNED,
+ LOCAL_VARIABLE_TYPES_CALCULATED)) {
+
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ private void tagUsed(final CompileUnitHolder node) {
+ assert node.getCompileUnit() != null : "no compile unit in " + node;
+ node.getCompileUnit().setUsed();
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ tagUsed(node);
+ return node;
+ }
+
+ @Override
+ public Node leaveSplitNode(final SplitNode node) {
+ tagUsed(node);
+ return node;
+ }
+
+ @Override
+ public Node leaveLiteralNode(final LiteralNode<?> node) {
+ if (node instanceof ArrayLiteralNode) {
+ final ArrayLiteralNode aln = (ArrayLiteralNode)node;
+ if (aln.getUnits() != null) {
+ for (final ArrayUnit au : aln.getUnits()) {
+ tagUsed(au);
+ }
+ }
+ return aln;
+ }
+ return node;
+ }
+
+ @Override
+ public Node leaveDefault(final Node node) {
+ return node.ensureUniqueLabels(lc);
+ }
+ });
+
+ return newFunctionNode;
+ }
+
+ @Override
+ public String toString() {
+ return "'Tag Compile Units Used'";
+ }
+ },
+
+ /**
* Reuse compile units, if they are already present. We are using the same compiler
* to recompile stuff
*/
@@ -358,6 +425,7 @@
assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
+ newUnit.setUsed();
return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED);
}
@@ -370,6 +438,7 @@
assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
+ newUnit.setUsed();
return node.setCompileUnit(lc, newUnit);
}
@@ -384,6 +453,7 @@
for (final ArrayUnit au : aln.getUnits()) {
final CompileUnit newUnit = map.get(au.getCompileUnit());
assert newUnit != null;
+ newUnit.setUsed();
newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi()));
}
return aln.setUnits(lc, newArrayUnits);
@@ -452,6 +522,11 @@
}
for (final CompileUnit compileUnit : compiler.getCompileUnits()) {
+ if (!compileUnit.isUsed()) {
+ compiler.getLogger().fine("Skipping unused compile unit ", compileUnit);
+ continue;
+ }
+
final ClassEmitter classEmitter = compileUnit.getClassEmitter();
classEmitter.end();
@@ -459,7 +534,6 @@
assert bytecode != null;
final String className = compileUnit.getUnitClassName();
-
compiler.addClass(className, bytecode);
// should we verify the generated code?
@@ -536,6 +610,9 @@
// initialize function in the compile units
for (final CompileUnit unit : compiler.getCompileUnits()) {
+ if (!unit.isUsed()) {
+ continue;
+ }
unit.setCode(installedClasses.get(unit.getUnitClassName()));
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompileUnit.java Fri Sep 05 16:28:02 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompileUnit.java Fri Sep 05 16:28:17 2014 +0200
@@ -42,6 +42,8 @@
private Class<?> clazz;
+ private boolean isUsed;
+
CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
this.className = className;
this.weight = initialWeight;
@@ -53,6 +55,21 @@
}
/**
+ * Check if this compile unit is used
+ * @return true if tagged as in use - i.e active code that needs to be generated
+ */
+ public boolean isUsed() {
+ return isUsed;
+ }
+
+ /**
+ * Tag this compile unit as used
+ */
+ public void setUsed() {
+ this.isUsed = true;
+ }
+
+ /**
* Return the class that contains the code for this unit, null if not
* generated yet
*
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Fri Sep 05 16:28:02 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Fri Sep 05 16:28:17 2014 +0200
@@ -171,13 +171,14 @@
CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
+ CompilationPhase.MARK_USED_COMPILE_UNITS,
CompilationPhase.BYTECODE_GENERATION_PHASE,
CompilationPhase.INSTALL_PHASE
});
/** Compile all for a rest of method */
public final static CompilationPhases COMPILE_ALL_RESTOF =
- COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
+ COMPILE_ALL.setDescription("Compile all, rest of").replace(CompilationPhase.MARK_USED_COMPILE_UNITS, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
/** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
public final static CompilationPhases COMPILE_ALL_NO_INSTALL =
@@ -248,6 +249,15 @@
return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
}
+ private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) {
+ final LinkedList<CompilationPhase> list = new LinkedList<>();
+ for (final CompilationPhase p : phases) {
+ list.add(p == phase ? newPhase : p);
+ }
+ return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ }
+
+ @SuppressWarnings("unused") //TODO I'll use this soon
private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
final LinkedList<CompilationPhase> list = new LinkedList<>();
for (final CompilationPhase p : phases) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CompileUnitHolder.java Fri Sep 05 16:28:17 2014 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir;
+
+import jdk.nashorn.internal.codegen.CompileUnit;
+
+/**
+ * Marker interface for things in the IR that can hold compile units.
+ * {@link CompileUnit}
+ */
+public interface CompileUnitHolder {
+ /**
+ * Return the compile unit held by this instance
+ * @return compile unit
+ */
+ public CompileUnit getCompileUnit();
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Fri Sep 05 16:28:02 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Fri Sep 05 16:28:17 2014 +0200
@@ -34,10 +34,8 @@
import java.util.Collections;
import java.util.EnumSet;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
import java.util.function.Function;
import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.CompileUnit;
@@ -57,7 +55,7 @@
* IR representation for function (or script.)
*/
@Immutable
-public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode> {
+public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode>, CompileUnitHolder {
/** Type used for all FunctionNodes */
public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
@@ -1102,6 +1100,7 @@
* @see Compiler
* @return the compile unit
*/
+ @Override
public CompileUnit getCompileUnit() {
return compileUnit;
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Fri Sep 05 16:28:02 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Fri Sep 05 16:28:17 2014 +0200
@@ -603,7 +603,7 @@
* An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
* be split if they are too large, for bytecode generation reasons
*/
- public static final class ArrayUnit {
+ public static final class ArrayUnit implements CompileUnitHolder {
/** Compile unit associated with the postsets range. */
private final CompileUnit compileUnit;
@@ -642,6 +642,7 @@
* The array compile unit
* @return array compile unit
*/
+ @Override
public CompileUnit getCompileUnit() {
return compileUnit;
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitNode.java Fri Sep 05 16:28:02 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitNode.java Fri Sep 05 16:28:17 2014 +0200
@@ -39,7 +39,7 @@
* Node indicating code is split across classes.
*/
@Immutable
-public class SplitNode extends LexicalContextStatement implements Labels {
+public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder {
/** Split node method name. */
private final String name;
@@ -116,6 +116,7 @@
* Get the compile unit for this split node
* @return compile unit
*/
+ @Override
public CompileUnit getCompileUnit() {
return compileUnit;
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Fri Sep 05 16:28:02 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Fri Sep 05 16:28:17 2014 +0200
@@ -38,6 +38,7 @@
import java.util.TreeMap;
import java.util.function.Supplier;
import java.util.logging.Level;
+
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;