Wed, 10 Sep 2014 12:37:44 +0200
8057703: More empty classes generated by Nashorn
Reviewed-by: attila, sundar
1.1 --- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java Fri Sep 05 16:28:17 2014 +0200 1.2 +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java Wed Sep 10 12:37:44 2014 +0200 1.3 @@ -59,6 +59,7 @@ 1.4 import java.util.EnumSet; 1.5 import java.util.HashSet; 1.6 import java.util.Set; 1.7 + 1.8 import jdk.internal.org.objectweb.asm.ClassWriter; 1.9 import jdk.internal.org.objectweb.asm.MethodVisitor; 1.10 import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; 1.11 @@ -135,6 +136,16 @@ 1.12 /** Set of constants access methods required. */ 1.13 private Set<Class<?>> constantMethodNeeded; 1.14 1.15 + private int methodCount; 1.16 + 1.17 + private int initCount; 1.18 + 1.19 + private int clinitCount; 1.20 + 1.21 + private int fieldCount; 1.22 + 1.23 + private final Set<String> methodNames; 1.24 + 1.25 /** 1.26 * Constructor - only used internally in this class as it breaks 1.27 * abstraction towards ASM or other code generator below 1.28 @@ -146,6 +157,11 @@ 1.29 this.context = context; 1.30 this.cw = cw; 1.31 this.methodsStarted = new HashSet<>(); 1.32 + this.methodNames = new HashSet<>(); 1.33 + } 1.34 + 1.35 + public Set<String> getMethodNames() { 1.36 + return methodNames; 1.37 } 1.38 1.39 /** 1.40 @@ -209,6 +225,38 @@ 1.41 } 1.42 1.43 /** 1.44 + * Get the method count, including init and clinit methods 1.45 + * @return method count 1.46 + */ 1.47 + public int getMethodCount() { 1.48 + return methodCount; 1.49 + } 1.50 + 1.51 + /** 1.52 + * Get the clinit count 1.53 + * @return clinit count 1.54 + */ 1.55 + public int getClinitCount() { 1.56 + return clinitCount; 1.57 + } 1.58 + 1.59 + /** 1.60 + * Get the init count 1.61 + * @return init count 1.62 + */ 1.63 + public int getInitCount() { 1.64 + return initCount; 1.65 + } 1.66 + 1.67 + /** 1.68 + * Get the field count 1.69 + * @return field count 1.70 + */ 1.71 + public int getFieldCount() { 1.72 + return fieldCount; 1.73 + } 1.74 + 1.75 + /** 1.76 * Convert a binary name to a package/class name. 1.77 * 1.78 * @param name Binary name. 1.79 @@ -359,9 +407,16 @@ 1.80 */ 1.81 @Override 1.82 public void end() { 1.83 - assert classStarted; 1.84 + assert classStarted : "class not started for " + unitClassName; 1.85 1.86 if (unitClassName != null) { 1.87 + final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE)); 1.88 + initMethod.begin(); 1.89 + initMethod.load(Type.OBJECT, 0); 1.90 + initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class); 1.91 + initMethod.returnVoid(); 1.92 + initMethod.end(); 1.93 + 1.94 defineCommonUtilities(); 1.95 } 1.96 1.97 @@ -419,6 +474,8 @@ 1.98 } 1.99 1.100 SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 1.101 + methodCount++; 1.102 + methodNames.add(methodName); 1.103 return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); 1.104 } 1.105 1.106 @@ -446,6 +503,8 @@ 1.107 * @return method emitter to use for weaving this method 1.108 */ 1.109 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 1.110 + methodCount++; 1.111 + methodNames.add(methodName); 1.112 return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes)); 1.113 } 1.114 1.115 @@ -471,6 +530,8 @@ 1.116 * @return method emitter to use for weaving this method 1.117 */ 1.118 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) { 1.119 + methodCount++; 1.120 + methodNames.add(methodName); 1.121 return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null)); 1.122 } 1.123 1.124 @@ -481,6 +542,8 @@ 1.125 * @return method emitter to use for weaving this method 1.126 */ 1.127 MethodEmitter method(final FunctionNode functionNode) { 1.128 + methodCount++; 1.129 + methodNames.add(functionNode.getName()); 1.130 final FunctionSignature signature = new FunctionSignature(functionNode); 1.131 final MethodVisitor mv = cw.visitMethod( 1.132 ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0), 1.133 @@ -499,6 +562,8 @@ 1.134 * @return method emitter to use for weaving this method 1.135 */ 1.136 MethodEmitter restOfMethod(final FunctionNode functionNode) { 1.137 + methodCount++; 1.138 + methodNames.add(functionNode.getName()); 1.139 final MethodVisitor mv = cw.visitMethod( 1.140 ACC_PUBLIC | ACC_STATIC, 1.141 functionNode.getName(), 1.142 @@ -516,6 +581,7 @@ 1.143 * @return method emitter to use for weaving <clinit> 1.144 */ 1.145 MethodEmitter clinit() { 1.146 + clinitCount++; 1.147 return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class); 1.148 } 1.149 1.150 @@ -525,6 +591,7 @@ 1.151 * @return method emitter to use for weaving <init>()V 1.152 */ 1.153 MethodEmitter init() { 1.154 + initCount++; 1.155 return method(INIT.symbolName(), void.class); 1.156 } 1.157 1.158 @@ -535,6 +602,7 @@ 1.159 * @return method emitter to use for weaving <init>()V 1.160 */ 1.161 MethodEmitter init(final Class<?>... ptypes) { 1.162 + initCount++; 1.163 return method(INIT.symbolName(), void.class, ptypes); 1.164 } 1.165 1.166 @@ -547,6 +615,7 @@ 1.167 * @return method emitter to use for weaving <init>(...)V 1.168 */ 1.169 MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) { 1.170 + initCount++; 1.171 return method(flags, INIT.symbolName(), void.class, ptypes); 1.172 } 1.173 1.174 @@ -561,6 +630,7 @@ 1.175 * @see ClassEmitter.Flag 1.176 */ 1.177 final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) { 1.178 + fieldCount++; 1.179 cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd(); 1.180 } 1.181
2.1 --- a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Fri Sep 05 16:28:17 2014 +0200 2.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Wed Sep 10 12:37:44 2014 +0200 2.3 @@ -31,6 +31,7 @@ 2.4 import java.util.Deque; 2.5 import java.util.HashMap; 2.6 import java.util.Map; 2.7 + 2.8 import jdk.nashorn.internal.IntDeque; 2.9 import jdk.nashorn.internal.codegen.types.Type; 2.10 import jdk.nashorn.internal.ir.Block; 2.11 @@ -158,7 +159,9 @@ 2.12 2.13 CompileUnit popCompileUnit(final CompileUnit oldUnit) { 2.14 assert compileUnits.peek() == oldUnit; 2.15 - compileUnits.pop(); 2.16 + final CompileUnit unit = compileUnits.pop(); 2.17 + assert unit.hasCode() : "compile unit popped without code"; 2.18 + unit.setUsed(); 2.19 return compileUnits.isEmpty() ? null : compileUnits.peek(); 2.20 } 2.21
3.1 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Sep 05 16:28:17 2014 +0200 3.2 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Sep 10 12:37:44 2014 +0200 3.3 @@ -53,7 +53,6 @@ 3.4 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 3.5 import jdk.nashorn.internal.ir.FunctionNode; 3.6 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 3.7 -import jdk.nashorn.internal.ir.CompileUnitHolder; 3.8 import jdk.nashorn.internal.ir.LexicalContext; 3.9 import jdk.nashorn.internal.ir.LiteralNode; 3.10 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 3.11 @@ -302,70 +301,6 @@ 3.12 } 3.13 }, 3.14 3.15 - /** 3.16 - * Mark compile units used in the method tree. Used for non-rest compilation only 3.17 - */ 3.18 - MARK_USED_COMPILE_UNITS( 3.19 - EnumSet.of( 3.20 - INITIALIZED, 3.21 - PARSED, 3.22 - CONSTANT_FOLDED, 3.23 - LOWERED, 3.24 - BUILTINS_TRANSFORMED, 3.25 - SPLIT, 3.26 - SYMBOLS_ASSIGNED, 3.27 - SCOPE_DEPTHS_COMPUTED, 3.28 - OPTIMISTIC_TYPES_ASSIGNED, 3.29 - LOCAL_VARIABLE_TYPES_CALCULATED)) { 3.30 - 3.31 - @Override 3.32 - FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 3.33 - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 3.34 - private void tagUsed(final CompileUnitHolder node) { 3.35 - assert node.getCompileUnit() != null : "no compile unit in " + node; 3.36 - node.getCompileUnit().setUsed(); 3.37 - } 3.38 - 3.39 - @Override 3.40 - public Node leaveFunctionNode(final FunctionNode node) { 3.41 - tagUsed(node); 3.42 - return node; 3.43 - } 3.44 - 3.45 - @Override 3.46 - public Node leaveSplitNode(final SplitNode node) { 3.47 - tagUsed(node); 3.48 - return node; 3.49 - } 3.50 - 3.51 - @Override 3.52 - public Node leaveLiteralNode(final LiteralNode<?> node) { 3.53 - if (node instanceof ArrayLiteralNode) { 3.54 - final ArrayLiteralNode aln = (ArrayLiteralNode)node; 3.55 - if (aln.getUnits() != null) { 3.56 - for (final ArrayUnit au : aln.getUnits()) { 3.57 - tagUsed(au); 3.58 - } 3.59 - } 3.60 - return aln; 3.61 - } 3.62 - return node; 3.63 - } 3.64 - 3.65 - @Override 3.66 - public Node leaveDefault(final Node node) { 3.67 - return node.ensureUniqueLabels(lc); 3.68 - } 3.69 - }); 3.70 - 3.71 - return newFunctionNode; 3.72 - } 3.73 - 3.74 - @Override 3.75 - public String toString() { 3.76 - return "'Tag Compile Units Used'"; 3.77 - } 3.78 - }, 3.79 3.80 /** 3.81 * Reuse compile units, if they are already present. We are using the same compiler 3.82 @@ -401,6 +336,8 @@ 3.83 if (phases.isRestOfCompilation()) { 3.84 sb.append("$restOf"); 3.85 } 3.86 + //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what 3.87 + //fills those out anyway. Thus no need for a copy constructor 3.88 final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); 3.89 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); 3.90 map.put(oldUnit, newUnit); 3.91 @@ -425,7 +362,6 @@ 3.92 assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; 3.93 3.94 log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); 3.95 - newUnit.setUsed(); 3.96 return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED); 3.97 } 3.98 3.99 @@ -438,7 +374,6 @@ 3.100 assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; 3.101 3.102 log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); 3.103 - newUnit.setUsed(); 3.104 return node.setCompileUnit(lc, newUnit); 3.105 } 3.106 3.107 @@ -453,7 +388,6 @@ 3.108 for (final ArrayUnit au : aln.getUnits()) { 3.109 final CompileUnit newUnit = map.get(au.getCompileUnit()); 3.110 assert newUnit != null; 3.111 - newUnit.setUsed(); 3.112 newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi())); 3.113 } 3.114 return aln.setUnits(lc, newArrayUnits); 3.115 @@ -500,8 +434,14 @@ 3.116 3.117 FunctionNode newFunctionNode = fn; 3.118 3.119 + //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped 3.120 + //in CodeGeneration - the rest can be used as a working "is compile unit used" metric 3.121 + fn.getCompileUnit().setUsed(); 3.122 + 3.123 compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); 3.124 + 3.125 final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); 3.126 + 3.127 try { 3.128 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program 3.129 // in the lazy + optimistic world. See CodeGenerator.skipFunction(). 3.130 @@ -522,19 +462,21 @@ 3.131 } 3.132 3.133 for (final CompileUnit compileUnit : compiler.getCompileUnits()) { 3.134 + final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 3.135 + classEmitter.end(); 3.136 + 3.137 if (!compileUnit.isUsed()) { 3.138 compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); 3.139 continue; 3.140 } 3.141 3.142 - final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 3.143 - classEmitter.end(); 3.144 - 3.145 final byte[] bytecode = classEmitter.toByteArray(); 3.146 assert bytecode != null; 3.147 3.148 final String className = compileUnit.getUnitClassName(); 3.149 - compiler.addClass(className, bytecode); 3.150 + compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used 3.151 + 3.152 + CompileUnit.increaseEmitCount(); 3.153 3.154 // should we verify the generated code? 3.155 if (senv._verify_code) {
4.1 --- a/src/jdk/nashorn/internal/codegen/CompileUnit.java Fri Sep 05 16:28:17 2014 +0200 4.2 +++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java Wed Sep 10 12:37:44 2014 +0200 4.3 @@ -44,6 +44,8 @@ 4.4 4.5 private boolean isUsed; 4.6 4.7 + private static int emittedUnitCount; 4.8 + 4.9 CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) { 4.10 this.className = className; 4.11 this.weight = initialWeight; 4.12 @@ -54,6 +56,14 @@ 4.13 return new TreeSet<>(); 4.14 } 4.15 4.16 + static void increaseEmitCount() { 4.17 + emittedUnitCount++; 4.18 + } 4.19 + 4.20 + public static int getEmittedUnitCount() { 4.21 + return emittedUnitCount; 4.22 + } 4.23 + 4.24 /** 4.25 * Check if this compile unit is used 4.26 * @return true if tagged as in use - i.e active code that needs to be generated 4.27 @@ -62,6 +72,10 @@ 4.28 return isUsed; 4.29 } 4.30 4.31 + public boolean hasCode() { 4.32 + return (classEmitter.getMethodCount() - classEmitter.getInitCount() - classEmitter.getClinitCount()) > 0; 4.33 + } 4.34 + 4.35 /** 4.36 * Tag this compile unit as used 4.37 */ 4.38 @@ -138,7 +152,8 @@ 4.39 4.40 @Override 4.41 public String toString() { 4.42 - return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']'; 4.43 + final String methods = classEmitter != null ? classEmitter.getMethodNames().toString() : "<anon>"; 4.44 + return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + " hasCode=" + methods + ']'; 4.45 } 4.46 4.47 @Override
5.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java Fri Sep 05 16:28:17 2014 +0200 5.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Wed Sep 10 12:37:44 2014 +0200 5.3 @@ -38,7 +38,6 @@ 5.4 import java.util.Arrays; 5.5 import java.util.Collections; 5.6 import java.util.Comparator; 5.7 -import java.util.EnumSet; 5.8 import java.util.HashMap; 5.9 import java.util.Iterator; 5.10 import java.util.LinkedHashMap; 5.11 @@ -51,7 +50,6 @@ 5.12 import java.util.function.Consumer; 5.13 import java.util.logging.Level; 5.14 import jdk.internal.dynalink.support.NameCodec; 5.15 -import jdk.nashorn.internal.codegen.ClassEmitter.Flag; 5.16 import jdk.nashorn.internal.codegen.types.Type; 5.17 import jdk.nashorn.internal.ir.Expression; 5.18 import jdk.nashorn.internal.ir.FunctionNode; 5.19 @@ -173,14 +171,13 @@ 5.20 CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, 5.21 CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, 5.22 CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, 5.23 - CompilationPhase.MARK_USED_COMPILE_UNITS, 5.24 CompilationPhase.BYTECODE_GENERATION_PHASE, 5.25 CompilationPhase.INSTALL_PHASE 5.26 }); 5.27 5.28 /** Compile all for a rest of method */ 5.29 public final static CompilationPhases COMPILE_ALL_RESTOF = 5.30 - COMPILE_ALL.setDescription("Compile all, rest of").replace(CompilationPhase.MARK_USED_COMPILE_UNITS, CompilationPhase.REUSE_COMPILE_UNITS_PHASE); 5.31 + COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE); 5.32 5.33 /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ 5.34 public final static CompilationPhases COMPILE_ALL_NO_INSTALL = 5.35 @@ -251,6 +248,7 @@ 5.36 return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); 5.37 } 5.38 5.39 + @SuppressWarnings("unused") //TODO I'll use this soon 5.40 private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) { 5.41 final LinkedList<CompilationPhase> list = new LinkedList<>(); 5.42 for (final CompilationPhase p : phases) { 5.43 @@ -259,7 +257,6 @@ 5.44 return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); 5.45 } 5.46 5.47 - @SuppressWarnings("unused") //TODO I'll use this soon 5.48 private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) { 5.49 final LinkedList<CompilationPhase> list = new LinkedList<>(); 5.50 for (final CompilationPhase p : phases) { 5.51 @@ -700,16 +697,8 @@ 5.52 CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) { 5.53 final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict()); 5.54 final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight); 5.55 - 5.56 classEmitter.begin(); 5.57 5.58 - final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE)); 5.59 - initMethod.begin(); 5.60 - initMethod.load(Type.OBJECT, 0); 5.61 - initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class); 5.62 - initMethod.returnVoid(); 5.63 - initMethod.end(); 5.64 - 5.65 return compileUnit; 5.66 } 5.67
6.1 --- a/src/jdk/nashorn/internal/runtime/Timing.java Fri Sep 05 16:28:17 2014 +0200 6.2 +++ b/src/jdk/nashorn/internal/runtime/Timing.java Wed Sep 10 12:37:44 2014 +0200 6.3 @@ -33,6 +33,8 @@ 6.4 import java.util.Map; 6.5 import java.util.concurrent.TimeUnit; 6.6 import java.util.function.Supplier; 6.7 + 6.8 +import jdk.nashorn.internal.codegen.CompileUnit; 6.9 import jdk.nashorn.internal.runtime.logging.DebugLogger; 6.10 import jdk.nashorn.internal.runtime.logging.Loggable; 6.11 import jdk.nashorn.internal.runtime.logging.Logger; 6.12 @@ -224,6 +226,9 @@ 6.13 append((int)(knownTime * 100.0 / total)). 6.14 append("%])"); 6.15 6.16 + sb.append("\n\nEmitted compile units: "). 6.17 + append(CompileUnit.getEmittedUnitCount()); 6.18 + 6.19 return sb.toString(); 6.20 } 6.21