1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.codegen; 27 28 import java.io.Serializable; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.IdentityHashMap; 32 import java.util.Map; 33 import java.util.Objects; 34 import java.util.Set; 35 import java.util.TreeSet; 36 import jdk.nashorn.internal.ir.CompileUnitHolder; 37 import jdk.nashorn.internal.ir.FunctionNode; 38 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; 39 40 /** 41 * Used to track split class compilation. Note that instances of the class are serializable, but all fields are 42 * transient, making the serialized version of the class only useful for tracking the referential topology of other 43 * AST nodes referencing the same or different compile units. We do want to preserve this topology though as 44 * {@link CompileUnitHolder}s in a deserialized AST will undergo reinitialization. 45 */ 46 public final class CompileUnit implements Comparable<CompileUnit>, Serializable { 47 private static final long serialVersionUID = 1L; 48 49 /** Current class name */ 50 private transient final String className; 51 52 /** Current class generator */ 53 private transient ClassEmitter classEmitter; 54 55 private transient long weight; 56 57 private transient Class<?> clazz; 58 59 private final transient Map<FunctionNode, RecompilableScriptFunctionData> functions = new IdentityHashMap<>(); 60 61 private transient boolean isUsed; 62 63 private static int emittedUnitCount; 64 65 CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) { 66 this.className = className; 67 this.weight = initialWeight; 68 this.classEmitter = classEmitter; 69 } 70 71 static Set<CompileUnit> createCompileUnitSet() { 72 return new TreeSet<>(); 73 } 74 75 static void increaseEmitCount() { 76 emittedUnitCount++; 77 } 78 79 /** 80 * Get the amount of emitted compile units so far in the system 81 * @return emitted compile unit count 82 */ 83 public static int getEmittedUnitCount() { 84 return emittedUnitCount; 85 } 86 87 /** 88 * Check if this compile unit is used 89 * @return true if tagged as in use - i.e active code that needs to be generated 90 */ 91 public boolean isUsed() { 92 return isUsed; 93 } 94 95 /** 96 * Check if a compile unit has code, not counting inits and clinits 97 * @return true of if there is "real code" in the compile unit 98 */ 99 public boolean hasCode() { 100 return (classEmitter.getMethodCount() - classEmitter.getInitCount() - classEmitter.getClinitCount()) > 0; 101 } 102 103 /** 104 * Tag this compile unit as used 105 */ 106 public void setUsed() { 107 this.isUsed = true; 108 } 109 110 /** 111 * Return the class that contains the code for this unit, null if not 112 * generated yet 113 * 114 * @return class with compile unit code 115 */ 116 public Class<?> getCode() { 117 return clazz; 118 } 119 120 /** 121 * Set class when it exists. Only accessible from compiler 122 * @param clazz class with code for this compile unit 123 */ 124 void setCode(final Class<?> clazz) { 125 this.clazz = Objects.requireNonNull(clazz); 126 // Revisit this - refactor to avoid null-ed out non-final fields 127 // null out emitter 128 this.classEmitter = null; 129 } 130 131 void addFunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) { 132 functions.put(functionNode, data); 133 } 134 135 /** 136 * Returns true if this compile unit is responsible for initializing the specified function data with specified 137 * function node. 138 * @param data the function data to check 139 * @param functionNode the function node to check 140 * @return true if this unit is responsible for initializing the function data with the function node, otherwise 141 * false 142 */ 143 public boolean isInitializing(final RecompilableScriptFunctionData data, final FunctionNode functionNode) { 144 return functions.get(functionNode) == data; 145 } 146 147 void initializeFunctionsCode() { 148 for(final Map.Entry<FunctionNode, RecompilableScriptFunctionData> entry : functions.entrySet()) { 149 entry.getValue().initializeCode(entry.getKey()); 150 } 151 } 152 153 Collection<FunctionNode> getFunctionNodes() { 154 return Collections.unmodifiableCollection(functions.keySet()); 155 } 156 157 /** 158 * Add weight to this compile unit 159 * @param w weight to add 160 */ 161 void addWeight(final long w) { 162 this.weight += w; 163 } 164 165 /** 166 * Check if this compile unit can hold {@code weight} more units of weight 167 * @param w weight to check if can be added 168 * @return true if weight fits in this compile unit 169 */ 170 public boolean canHold(final long w) { 171 return (this.weight + w) < Splitter.SPLIT_THRESHOLD; 172 } 173 174 /** 175 * Get the class emitter for this compile unit 176 * @return class emitter 177 */ 178 public ClassEmitter getClassEmitter() { 179 return classEmitter; 180 } 181 182 /** 183 * Get the class name for this compile unit 184 * @return the class name 185 */ 186 public String getUnitClassName() { 187 return className; 188 } 189 190 private static String shortName(final String name) { 191 return name == null ? null : name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1); 192 } 193 194 @Override 195 public String toString() { 196 final String methods = classEmitter != null ? classEmitter.getMethodNames().toString() : "<anon>"; 197 return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + " hasCode=" + methods + ']'; 198 } 199 200 @Override 201 public int compareTo(final CompileUnit o) { 202 return className.compareTo(o.className); 203 } 204 }