Fri, 05 Jun 2015 14:46:52 +0530
8081809: Missing final modifier in method parameters (nashorn code convention)
Reviewed-by: attila, lagergren
attila@963 | 1 | /* |
attila@963 | 2 | * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. |
attila@963 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
attila@963 | 4 | * |
attila@963 | 5 | * This code is free software; you can redistribute it and/or modify it |
attila@963 | 6 | * under the terms of the GNU General Public License version 2 only, as |
attila@963 | 7 | * published by the Free Software Foundation. Oracle designates this |
attila@963 | 8 | * particular file as subject to the "Classpath" exception as provided |
attila@963 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
attila@963 | 10 | * |
attila@963 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
attila@963 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
attila@963 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
attila@963 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
attila@963 | 15 | * accompanied this code). |
attila@963 | 16 | * |
attila@963 | 17 | * You should have received a copy of the GNU General Public License version |
attila@963 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
attila@963 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
attila@963 | 20 | * |
attila@963 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
attila@963 | 22 | * or visit www.oracle.com if you need additional information or have any |
attila@963 | 23 | * questions. |
attila@963 | 24 | */ |
attila@963 | 25 | |
lagergren@89 | 26 | package jdk.nashorn.internal.codegen; |
lagergren@89 | 27 | |
attila@963 | 28 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED; |
attila@963 | 29 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED; |
attila@963 | 30 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED; |
lagergren@89 | 31 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED; |
lagergren@89 | 32 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED; |
attila@963 | 33 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED; |
lagergren@89 | 34 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED; |
attila@963 | 35 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.OPTIMISTIC_TYPES_ASSIGNED; |
lagergren@137 | 36 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED; |
attila@963 | 37 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED; |
lagergren@89 | 38 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; |
attila@963 | 39 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED; |
attila@963 | 40 | import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; |
attila@1064 | 41 | |
attila@963 | 42 | import java.io.PrintWriter; |
lagergren@89 | 43 | import java.util.EnumSet; |
attila@963 | 44 | import java.util.HashMap; |
attila@963 | 45 | import java.util.LinkedHashMap; |
attila@963 | 46 | import java.util.Map; |
attila@963 | 47 | import java.util.Map.Entry; |
lagergren@108 | 48 | import java.util.Set; |
attila@980 | 49 | import jdk.nashorn.internal.AssertsEnabled; |
attila@963 | 50 | import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; |
lagergren@89 | 51 | import jdk.nashorn.internal.ir.FunctionNode; |
attila@416 | 52 | import jdk.nashorn.internal.ir.FunctionNode.CompilationState; |
lagergren@290 | 53 | import jdk.nashorn.internal.ir.LexicalContext; |
attila@963 | 54 | import jdk.nashorn.internal.ir.LiteralNode; |
attila@416 | 55 | import jdk.nashorn.internal.ir.Node; |
lagergren@89 | 56 | import jdk.nashorn.internal.ir.debug.ASTWriter; |
lagergren@89 | 57 | import jdk.nashorn.internal.ir.debug.PrintVisitor; |
attila@144 | 58 | import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
attila@963 | 59 | import jdk.nashorn.internal.runtime.CodeInstaller; |
attila@963 | 60 | import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; |
sundar@118 | 61 | import jdk.nashorn.internal.runtime.ScriptEnvironment; |
attila@963 | 62 | import jdk.nashorn.internal.runtime.logging.DebugLogger; |
lagergren@89 | 63 | |
lagergren@89 | 64 | /** |
lagergren@211 | 65 | * A compilation phase is a step in the processes of turning a JavaScript |
lagergren@211 | 66 | * FunctionNode into bytecode. It has an optional return value. |
lagergren@89 | 67 | */ |
lagergren@89 | 68 | enum CompilationPhase { |
attila@963 | 69 | /** |
attila@963 | 70 | * Constant folding pass Simple constant folding that will make elementary |
attila@963 | 71 | * constructs go away |
attila@963 | 72 | */ |
attila@963 | 73 | CONSTANT_FOLDING_PHASE( |
attila@963 | 74 | EnumSet.of( |
attila@963 | 75 | INITIALIZED, |
attila@963 | 76 | PARSED)) { |
attila@963 | 77 | @Override |
attila@963 | 78 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 79 | return transformFunction(fn, new FoldConstants(compiler)); |
attila@963 | 80 | } |
lagergren@89 | 81 | |
attila@963 | 82 | @Override |
attila@963 | 83 | public String toString() { |
attila@963 | 84 | return "'Constant Folding'"; |
attila@963 | 85 | } |
attila@963 | 86 | }, |
attila@963 | 87 | |
attila@963 | 88 | /** |
attila@963 | 89 | * Lower (Control flow pass) Finalizes the control flow. Clones blocks for |
attila@963 | 90 | * finally constructs and similar things. Establishes termination criteria |
attila@963 | 91 | * for nodes Guarantee return instructions to method making sure control |
attila@963 | 92 | * flow cannot fall off the end. Replacing high level nodes with lower such |
attila@963 | 93 | * as runtime nodes where applicable. |
lagergren@89 | 94 | */ |
attila@963 | 95 | LOWERING_PHASE( |
attila@963 | 96 | EnumSet.of( |
attila@963 | 97 | INITIALIZED, |
attila@963 | 98 | PARSED, |
attila@963 | 99 | CONSTANT_FOLDED)) { |
lagergren@89 | 100 | @Override |
attila@963 | 101 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 102 | return transformFunction(fn, new Lower(compiler)); |
attila@963 | 103 | } |
lagergren@89 | 104 | |
attila@963 | 105 | @Override |
attila@963 | 106 | public String toString() { |
attila@963 | 107 | return "'Control Flow Lowering'"; |
attila@963 | 108 | } |
attila@963 | 109 | }, |
lagergren@211 | 110 | |
attila@963 | 111 | /** |
attila@963 | 112 | * Phase used only when doing optimistic code generation. It assigns all potentially |
attila@963 | 113 | * optimistic ops a program point so that an UnwarrantedException knows from where |
attila@963 | 114 | * a guess went wrong when creating the continuation to roll back this execution |
attila@963 | 115 | */ |
attila@963 | 116 | TRANSFORM_BUILTINS_PHASE( |
attila@963 | 117 | EnumSet.of( |
attila@963 | 118 | INITIALIZED, |
attila@963 | 119 | PARSED, |
attila@963 | 120 | CONSTANT_FOLDED, |
attila@963 | 121 | LOWERED)) { |
attila@963 | 122 | //we only do this if we have a param type map, otherwise this is not a specialized recompile |
attila@963 | 123 | @Override |
attila@963 | 124 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 125 | return setStates(transformFunction(fn, new ApplySpecialization(compiler)), BUILTINS_TRANSFORMED); |
attila@963 | 126 | } |
lagergren@211 | 127 | |
attila@963 | 128 | @Override |
attila@963 | 129 | public String toString() { |
attila@963 | 130 | return "'Builtin Replacement'"; |
attila@963 | 131 | } |
attila@963 | 132 | }, |
attila@963 | 133 | |
attila@963 | 134 | /** |
attila@963 | 135 | * Splitter Split the AST into several compile units based on a heuristic size calculation. |
attila@963 | 136 | * Split IR can lead to scope information being changed. |
attila@963 | 137 | */ |
attila@963 | 138 | SPLITTING_PHASE( |
attila@963 | 139 | EnumSet.of( |
attila@963 | 140 | INITIALIZED, |
attila@963 | 141 | PARSED, |
attila@963 | 142 | CONSTANT_FOLDED, |
attila@963 | 143 | LOWERED, |
attila@963 | 144 | BUILTINS_TRANSFORMED)) { |
attila@963 | 145 | @Override |
attila@963 | 146 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 147 | final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L); |
attila@963 | 148 | |
attila@963 | 149 | FunctionNode newFunctionNode; |
attila@963 | 150 | |
attila@963 | 151 | //ensure elementTypes, postsets and presets exist for splitter and arraynodes |
attila@1064 | 152 | newFunctionNode = transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { |
lagergren@108 | 153 | @Override |
attila@963 | 154 | public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) { |
attila@963 | 155 | return literalNode.initialize(lc); |
lagergren@89 | 156 | } |
lagergren@89 | 157 | }); |
lagergren@108 | 158 | |
attila@963 | 159 | newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true); |
attila@1064 | 160 | newFunctionNode = transformFunction(newFunctionNode, new SplitIntoFunctions(compiler)); |
attila@963 | 161 | assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit; |
attila@963 | 162 | assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName()); |
attila@963 | 163 | |
attila@963 | 164 | return newFunctionNode; |
attila@963 | 165 | } |
attila@963 | 166 | |
attila@963 | 167 | @Override |
attila@963 | 168 | public String toString() { |
attila@963 | 169 | return "'Code Splitting'"; |
attila@963 | 170 | } |
attila@963 | 171 | }, |
attila@963 | 172 | |
attila@1064 | 173 | PROGRAM_POINT_PHASE( |
attila@1064 | 174 | EnumSet.of( |
attila@1064 | 175 | INITIALIZED, |
attila@1064 | 176 | PARSED, |
attila@1064 | 177 | CONSTANT_FOLDED, |
attila@1064 | 178 | LOWERED, |
attila@1064 | 179 | BUILTINS_TRANSFORMED, |
attila@1064 | 180 | SPLIT)) { |
attila@1064 | 181 | @Override |
attila@1064 | 182 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 183 | return transformFunction(fn, new ProgramPoints()); |
attila@1064 | 184 | } |
attila@1064 | 185 | |
attila@1064 | 186 | @Override |
attila@1064 | 187 | public String toString() { |
attila@1064 | 188 | return "'Program Point Calculation'"; |
attila@1064 | 189 | } |
attila@1064 | 190 | }, |
attila@1064 | 191 | |
attila@1064 | 192 | SERIALIZE_SPLIT_PHASE( |
attila@1064 | 193 | EnumSet.of( |
attila@1064 | 194 | INITIALIZED, |
attila@1064 | 195 | PARSED, |
attila@1064 | 196 | CONSTANT_FOLDED, |
attila@1064 | 197 | LOWERED, |
attila@1064 | 198 | BUILTINS_TRANSFORMED, |
attila@1064 | 199 | SPLIT)) { |
attila@1064 | 200 | @Override |
attila@1064 | 201 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 202 | return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { |
attila@1064 | 203 | @Override |
attila@1064 | 204 | public boolean enterFunctionNode(final FunctionNode functionNode) { |
attila@1064 | 205 | if (functionNode.isSplit()) { |
attila@1064 | 206 | compiler.serializeAst(functionNode); |
attila@1064 | 207 | } |
attila@1064 | 208 | return true; |
attila@1064 | 209 | } |
attila@1064 | 210 | }); |
attila@1064 | 211 | } |
attila@1064 | 212 | |
attila@1064 | 213 | @Override |
attila@1064 | 214 | public String toString() { |
attila@1064 | 215 | return "'Serialize Split Functions'"; |
attila@1064 | 216 | } |
attila@1064 | 217 | }, |
attila@1064 | 218 | |
attila@963 | 219 | SYMBOL_ASSIGNMENT_PHASE( |
attila@963 | 220 | EnumSet.of( |
attila@963 | 221 | INITIALIZED, |
attila@963 | 222 | PARSED, |
attila@963 | 223 | CONSTANT_FOLDED, |
attila@963 | 224 | LOWERED, |
attila@963 | 225 | BUILTINS_TRANSFORMED, |
attila@963 | 226 | SPLIT)) { |
attila@963 | 227 | @Override |
attila@963 | 228 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 229 | return transformFunction(fn, new AssignSymbols(compiler)); |
attila@963 | 230 | } |
attila@963 | 231 | |
attila@963 | 232 | @Override |
attila@963 | 233 | public String toString() { |
attila@963 | 234 | return "'Symbol Assignment'"; |
attila@963 | 235 | } |
attila@963 | 236 | }, |
attila@963 | 237 | |
attila@963 | 238 | SCOPE_DEPTH_COMPUTATION_PHASE( |
attila@963 | 239 | EnumSet.of( |
attila@963 | 240 | INITIALIZED, |
attila@963 | 241 | PARSED, |
attila@963 | 242 | CONSTANT_FOLDED, |
attila@963 | 243 | LOWERED, |
attila@963 | 244 | BUILTINS_TRANSFORMED, |
attila@963 | 245 | SPLIT, |
attila@963 | 246 | SYMBOLS_ASSIGNED)) { |
attila@963 | 247 | @Override |
attila@963 | 248 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 249 | return transformFunction(fn, new FindScopeDepths(compiler)); |
attila@963 | 250 | } |
attila@963 | 251 | |
attila@963 | 252 | @Override |
attila@963 | 253 | public String toString() { |
attila@963 | 254 | return "'Scope Depth Computation'"; |
attila@963 | 255 | } |
attila@963 | 256 | }, |
attila@963 | 257 | |
attila@963 | 258 | OPTIMISTIC_TYPE_ASSIGNMENT_PHASE( |
attila@963 | 259 | EnumSet.of( |
attila@963 | 260 | INITIALIZED, |
attila@963 | 261 | PARSED, |
attila@963 | 262 | CONSTANT_FOLDED, |
attila@963 | 263 | LOWERED, |
attila@963 | 264 | BUILTINS_TRANSFORMED, |
attila@963 | 265 | SPLIT, |
attila@963 | 266 | SYMBOLS_ASSIGNED, |
attila@963 | 267 | SCOPE_DEPTHS_COMPUTED)) { |
attila@963 | 268 | @Override |
attila@963 | 269 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 270 | if (compiler.useOptimisticTypes()) { |
attila@1064 | 271 | return transformFunction(fn, new OptimisticTypesCalculator(compiler)); |
attila@963 | 272 | } |
attila@963 | 273 | return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED); |
attila@963 | 274 | } |
attila@963 | 275 | |
attila@963 | 276 | @Override |
attila@963 | 277 | public String toString() { |
attila@963 | 278 | return "'Optimistic Type Assignment'"; |
attila@963 | 279 | } |
attila@963 | 280 | }, |
attila@963 | 281 | |
attila@963 | 282 | LOCAL_VARIABLE_TYPE_CALCULATION_PHASE( |
attila@963 | 283 | EnumSet.of( |
attila@963 | 284 | INITIALIZED, |
attila@963 | 285 | PARSED, |
attila@963 | 286 | CONSTANT_FOLDED, |
attila@963 | 287 | LOWERED, |
attila@963 | 288 | BUILTINS_TRANSFORMED, |
attila@963 | 289 | SPLIT, |
attila@963 | 290 | SYMBOLS_ASSIGNED, |
attila@963 | 291 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 292 | OPTIMISTIC_TYPES_ASSIGNED)) { |
attila@963 | 293 | @Override |
attila@963 | 294 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 295 | final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler)); |
attila@963 | 296 | final ScriptEnvironment senv = compiler.getScriptEnvironment(); |
attila@963 | 297 | final PrintWriter err = senv.getErr(); |
attila@963 | 298 | |
attila@963 | 299 | //TODO separate phase for the debug printouts for abstraction and clarity |
attila@963 | 300 | if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) { |
attila@963 | 301 | err.println("Lower AST for: " + quote(newFunctionNode.getName())); |
attila@963 | 302 | err.println(new ASTWriter(newFunctionNode)); |
lagergren@108 | 303 | } |
lagergren@108 | 304 | |
attila@963 | 305 | if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) { |
attila@963 | 306 | err.println("Lower AST for: " + quote(newFunctionNode.getName())); |
attila@963 | 307 | err.println(new PrintVisitor(newFunctionNode)); |
attila@963 | 308 | } |
attila@963 | 309 | |
attila@963 | 310 | return newFunctionNode; |
attila@963 | 311 | } |
attila@963 | 312 | |
attila@963 | 313 | @Override |
attila@963 | 314 | public String toString() { |
attila@963 | 315 | return "'Local Variable Type Calculation'"; |
attila@963 | 316 | } |
attila@963 | 317 | }, |
attila@963 | 318 | |
lagergren@1003 | 319 | |
lagergren@1003 | 320 | /** |
attila@963 | 321 | * Reuse compile units, if they are already present. We are using the same compiler |
attila@963 | 322 | * to recompile stuff |
attila@963 | 323 | */ |
attila@963 | 324 | REUSE_COMPILE_UNITS_PHASE( |
attila@963 | 325 | EnumSet.of( |
attila@963 | 326 | INITIALIZED, |
attila@963 | 327 | PARSED, |
attila@963 | 328 | CONSTANT_FOLDED, |
attila@963 | 329 | LOWERED, |
attila@963 | 330 | BUILTINS_TRANSFORMED, |
attila@963 | 331 | SPLIT, |
attila@963 | 332 | SYMBOLS_ASSIGNED, |
attila@963 | 333 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 334 | OPTIMISTIC_TYPES_ASSIGNED, |
attila@963 | 335 | LOCAL_VARIABLE_TYPES_CALCULATED)) { |
attila@963 | 336 | @Override |
attila@963 | 337 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 338 | assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods"; |
attila@963 | 339 | |
attila@963 | 340 | final Map<CompileUnit, CompileUnit> map = new HashMap<>(); |
attila@963 | 341 | final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet(); |
attila@963 | 342 | |
attila@963 | 343 | final DebugLogger log = compiler.getLogger(); |
attila@963 | 344 | |
attila@963 | 345 | log.fine("Clearing bytecode cache"); |
attila@963 | 346 | compiler.clearBytecode(); |
attila@963 | 347 | |
attila@963 | 348 | for (final CompileUnit oldUnit : compiler.getCompileUnits()) { |
attila@963 | 349 | assert map.get(oldUnit) == null; |
attila@1064 | 350 | final CompileUnit newUnit = createNewCompileUnit(compiler, phases); |
attila@963 | 351 | log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); |
attila@963 | 352 | map.put(oldUnit, newUnit); |
attila@963 | 353 | assert newUnit != null; |
attila@963 | 354 | newUnits.add(newUnit); |
attila@963 | 355 | } |
attila@963 | 356 | |
attila@963 | 357 | log.fine("Replacing compile units in Compiler..."); |
attila@963 | 358 | compiler.replaceCompileUnits(newUnits); |
attila@963 | 359 | log.fine("Done"); |
attila@963 | 360 | |
attila@963 | 361 | //replace old compile units in function nodes, if any are assigned, |
attila@963 | 362 | //for example by running the splitter on this function node in a previous |
attila@963 | 363 | //partial code generation |
attila@1064 | 364 | final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() { |
attila@144 | 365 | @Override |
sundar@1397 | 366 | CompileUnit getReplacement(final CompileUnit original) { |
attila@1064 | 367 | return map.get(original); |
attila@963 | 368 | } |
lagergren@211 | 369 | |
attila@963 | 370 | @Override |
attila@963 | 371 | public Node leaveDefault(final Node node) { |
attila@963 | 372 | return node.ensureUniqueLabels(lc); |
attila@144 | 373 | } |
attila@144 | 374 | }); |
lagergren@211 | 375 | |
lagergren@211 | 376 | return newFunctionNode; |
lagergren@89 | 377 | } |
lagergren@89 | 378 | |
lagergren@89 | 379 | @Override |
lagergren@89 | 380 | public String toString() { |
attila@963 | 381 | return "'Reuse Compile Units'"; |
lagergren@89 | 382 | } |
lagergren@89 | 383 | }, |
lagergren@89 | 384 | |
attila@1064 | 385 | REINITIALIZE_SERIALIZED( |
attila@1064 | 386 | EnumSet.of( |
attila@1064 | 387 | INITIALIZED, |
attila@1064 | 388 | PARSED, |
attila@1064 | 389 | CONSTANT_FOLDED, |
attila@1064 | 390 | LOWERED, |
attila@1064 | 391 | BUILTINS_TRANSFORMED, |
attila@1064 | 392 | SPLIT)) { |
attila@1064 | 393 | @Override |
attila@1064 | 394 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@1064 | 395 | final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet(); |
attila@1064 | 396 | final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>(); |
attila@1064 | 397 | |
attila@1064 | 398 | // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase |
attila@1064 | 399 | // will use that as the root class. |
attila@1064 | 400 | createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases); |
attila@1064 | 401 | |
attila@1064 | 402 | final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() { |
attila@1064 | 403 | @Override |
attila@1064 | 404 | CompileUnit getReplacement(final CompileUnit oldUnit) { |
attila@1064 | 405 | final CompileUnit existing = unitMap.get(oldUnit); |
attila@1064 | 406 | if (existing != null) { |
attila@1064 | 407 | return existing; |
attila@1064 | 408 | } |
attila@1064 | 409 | return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases); |
attila@1064 | 410 | } |
attila@1064 | 411 | |
attila@1064 | 412 | @Override |
attila@1064 | 413 | public Node leaveFunctionNode(final FunctionNode fn2) { |
attila@1064 | 414 | return super.leaveFunctionNode( |
attila@1064 | 415 | // restore flags for deserialized nested function nodes |
attila@1064 | 416 | compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2)); |
attila@1064 | 417 | }; |
attila@1064 | 418 | }); |
attila@1064 | 419 | compiler.replaceCompileUnits(unitSet); |
attila@1064 | 420 | return newFn; |
attila@1064 | 421 | } |
attila@1064 | 422 | |
attila@1064 | 423 | private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet, |
attila@1064 | 424 | final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) { |
attila@1064 | 425 | final CompileUnit newUnit = createNewCompileUnit(compiler, phases); |
attila@1064 | 426 | unitMap.put(oldUnit, newUnit); |
attila@1064 | 427 | unitSet.add(newUnit); |
attila@1064 | 428 | return newUnit; |
attila@1064 | 429 | } |
attila@1064 | 430 | |
attila@1064 | 431 | @Override |
attila@1064 | 432 | public String toString() { |
attila@1064 | 433 | return "'Deserialize'"; |
attila@1064 | 434 | } |
attila@1064 | 435 | }, |
attila@1064 | 436 | |
attila@1064 | 437 | /** |
lagergren@89 | 438 | * Bytecode generation: |
lagergren@89 | 439 | * |
lagergren@211 | 440 | * Generate the byte code class(es) resulting from the compiled FunctionNode |
lagergren@89 | 441 | */ |
attila@963 | 442 | BYTECODE_GENERATION_PHASE( |
attila@963 | 443 | EnumSet.of( |
attila@963 | 444 | INITIALIZED, |
attila@963 | 445 | PARSED, |
attila@963 | 446 | CONSTANT_FOLDED, |
attila@963 | 447 | LOWERED, |
attila@963 | 448 | BUILTINS_TRANSFORMED, |
attila@963 | 449 | SPLIT, |
attila@963 | 450 | SYMBOLS_ASSIGNED, |
attila@963 | 451 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 452 | OPTIMISTIC_TYPES_ASSIGNED, |
attila@963 | 453 | LOCAL_VARIABLE_TYPES_CALCULATED)) { |
attila@963 | 454 | |
lagergren@89 | 455 | @Override |
attila@963 | 456 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 457 | final ScriptEnvironment senv = compiler.getScriptEnvironment(); |
attila@963 | 458 | |
lagergren@211 | 459 | FunctionNode newFunctionNode = fn; |
lagergren@89 | 460 | |
lagergren@1004 | 461 | //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped |
lagergren@1004 | 462 | //in CodeGeneration - the rest can be used as a working "is compile unit used" metric |
lagergren@1004 | 463 | fn.getCompileUnit().setUsed(); |
lagergren@1004 | 464 | |
attila@963 | 465 | compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); |
lagergren@1004 | 466 | |
attila@963 | 467 | final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); |
lagergren@1004 | 468 | |
lagergren@89 | 469 | try { |
attila@963 | 470 | // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program |
attila@963 | 471 | // in the lazy + optimistic world. See CodeGenerator.skipFunction(). |
attila@1064 | 472 | newFunctionNode = transformFunction(newFunctionNode, codegen).setState(null, BYTECODE_GENERATED); |
lagergren@89 | 473 | codegen.generateScopeCalls(); |
lagergren@89 | 474 | } catch (final VerifyError e) { |
attila@963 | 475 | if (senv._verify_code || senv._print_code) { |
attila@963 | 476 | senv.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage()); |
attila@963 | 477 | if (senv._dump_on_error) { |
attila@963 | 478 | e.printStackTrace(senv.getErr()); |
lagergren@89 | 479 | } |
lagergren@89 | 480 | } else { |
lagergren@89 | 481 | throw e; |
lagergren@89 | 482 | } |
attila@963 | 483 | } catch (final Throwable e) { |
attila@963 | 484 | // Provide source file and line number being compiled when the assertion occurred |
attila@963 | 485 | throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e); |
lagergren@89 | 486 | } |
lagergren@89 | 487 | |
lagergren@89 | 488 | for (final CompileUnit compileUnit : compiler.getCompileUnits()) { |
lagergren@1004 | 489 | final ClassEmitter classEmitter = compileUnit.getClassEmitter(); |
lagergren@1004 | 490 | classEmitter.end(); |
lagergren@1004 | 491 | |
lagergren@1003 | 492 | if (!compileUnit.isUsed()) { |
lagergren@1003 | 493 | compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); |
lagergren@1003 | 494 | continue; |
lagergren@1003 | 495 | } |
lagergren@1003 | 496 | |
lagergren@89 | 497 | final byte[] bytecode = classEmitter.toByteArray(); |
lagergren@89 | 498 | assert bytecode != null; |
lagergren@89 | 499 | |
lagergren@89 | 500 | final String className = compileUnit.getUnitClassName(); |
lagergren@1004 | 501 | compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used |
lagergren@1004 | 502 | |
lagergren@1004 | 503 | CompileUnit.increaseEmitCount(); |
lagergren@89 | 504 | |
lagergren@211 | 505 | // should we verify the generated code? |
attila@963 | 506 | if (senv._verify_code) { |
sundar@118 | 507 | compiler.getCodeInstaller().verify(bytecode); |
lagergren@89 | 508 | } |
lagergren@89 | 509 | |
attila@963 | 510 | DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className); |
lagergren@89 | 511 | } |
lagergren@211 | 512 | |
lagergren@211 | 513 | return newFunctionNode; |
lagergren@89 | 514 | } |
lagergren@89 | 515 | |
lagergren@89 | 516 | @Override |
lagergren@89 | 517 | public String toString() { |
attila@963 | 518 | return "'Bytecode Generation'"; |
lagergren@89 | 519 | } |
attila@963 | 520 | }, |
lagergren@89 | 521 | |
attila@963 | 522 | INSTALL_PHASE( |
attila@963 | 523 | EnumSet.of( |
attila@963 | 524 | INITIALIZED, |
attila@963 | 525 | PARSED, |
attila@963 | 526 | CONSTANT_FOLDED, |
attila@963 | 527 | LOWERED, |
attila@963 | 528 | BUILTINS_TRANSFORMED, |
attila@963 | 529 | SPLIT, |
attila@963 | 530 | SYMBOLS_ASSIGNED, |
attila@963 | 531 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 532 | OPTIMISTIC_TYPES_ASSIGNED, |
attila@963 | 533 | LOCAL_VARIABLE_TYPES_CALCULATED, |
attila@963 | 534 | BYTECODE_GENERATED)) { |
attila@963 | 535 | |
attila@963 | 536 | @Override |
attila@963 | 537 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 538 | final DebugLogger log = compiler.getLogger(); |
attila@963 | 539 | |
attila@963 | 540 | final Map<String, Class<?>> installedClasses = new LinkedHashMap<>(); |
attila@963 | 541 | |
attila@963 | 542 | boolean first = true; |
attila@963 | 543 | Class<?> rootClass = null; |
attila@963 | 544 | long length = 0L; |
attila@963 | 545 | |
attila@963 | 546 | final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller(); |
attila@963 | 547 | final Map<String, byte[]> bytecode = compiler.getBytecode(); |
attila@963 | 548 | |
attila@963 | 549 | for (final Entry<String, byte[]> entry : bytecode.entrySet()) { |
attila@963 | 550 | final String className = entry.getKey(); |
attila@963 | 551 | //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName(); |
attila@963 | 552 | final byte[] code = entry.getValue(); |
attila@963 | 553 | length += code.length; |
attila@963 | 554 | |
attila@963 | 555 | final Class<?> clazz = codeInstaller.install(className, code); |
attila@963 | 556 | if (first) { |
attila@963 | 557 | rootClass = clazz; |
attila@963 | 558 | first = false; |
attila@963 | 559 | } |
attila@963 | 560 | installedClasses.put(className, clazz); |
attila@963 | 561 | } |
attila@963 | 562 | |
attila@963 | 563 | if (rootClass == null) { |
attila@963 | 564 | throw new CompilationException("Internal compiler error: root class not found!"); |
attila@963 | 565 | } |
attila@963 | 566 | |
attila@963 | 567 | final Object[] constants = compiler.getConstantData().toArray(); |
attila@963 | 568 | codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants); |
attila@963 | 569 | |
attila@963 | 570 | // initialize transient fields on recompilable script function data |
attila@963 | 571 | for (final Object constant: constants) { |
attila@963 | 572 | if (constant instanceof RecompilableScriptFunctionData) { |
attila@963 | 573 | ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller); |
attila@963 | 574 | } |
attila@963 | 575 | } |
attila@963 | 576 | |
attila@963 | 577 | // initialize function in the compile units |
attila@963 | 578 | for (final CompileUnit unit : compiler.getCompileUnits()) { |
lagergren@1003 | 579 | if (!unit.isUsed()) { |
lagergren@1003 | 580 | continue; |
lagergren@1003 | 581 | } |
attila@963 | 582 | unit.setCode(installedClasses.get(unit.getUnitClassName())); |
hannesw@1337 | 583 | unit.initializeFunctionsCode(); |
attila@963 | 584 | } |
attila@963 | 585 | |
attila@963 | 586 | if (log.isEnabled()) { |
attila@963 | 587 | final StringBuilder sb = new StringBuilder(); |
attila@963 | 588 | |
attila@963 | 589 | sb.append("Installed class '"). |
attila@963 | 590 | append(rootClass.getSimpleName()). |
attila@963 | 591 | append('\''). |
attila@963 | 592 | append(" ["). |
attila@963 | 593 | append(rootClass.getName()). |
attila@963 | 594 | append(", size="). |
attila@963 | 595 | append(length). |
attila@963 | 596 | append(" bytes, "). |
attila@963 | 597 | append(compiler.getCompileUnits().size()). |
attila@963 | 598 | append(" compile unit(s)]"); |
attila@963 | 599 | |
attila@963 | 600 | log.fine(sb.toString()); |
attila@963 | 601 | } |
attila@963 | 602 | |
attila@963 | 603 | return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED); |
attila@963 | 604 | } |
attila@963 | 605 | |
attila@963 | 606 | @Override |
attila@963 | 607 | public String toString() { |
attila@963 | 608 | return "'Class Installation'"; |
attila@963 | 609 | } |
attila@963 | 610 | |
attila@963 | 611 | }; |
attila@963 | 612 | |
attila@963 | 613 | /** pre conditions required for function node to which this transform is to be applied */ |
lagergren@89 | 614 | private final EnumSet<CompilationState> pre; |
attila@963 | 615 | |
attila@963 | 616 | /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ |
lagergren@89 | 617 | private long startTime; |
attila@963 | 618 | |
attila@963 | 619 | /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ |
lagergren@89 | 620 | private long endTime; |
attila@963 | 621 | |
attila@963 | 622 | /** boolean that is true upon transform completion */ |
lagergren@89 | 623 | private boolean isFinished; |
lagergren@89 | 624 | |
lagergren@89 | 625 | private CompilationPhase(final EnumSet<CompilationState> pre) { |
lagergren@137 | 626 | this.pre = pre; |
lagergren@89 | 627 | } |
lagergren@89 | 628 | |
attila@963 | 629 | private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) { |
attila@980 | 630 | if (!AssertsEnabled.assertsEnabled()) { |
attila@980 | 631 | return functionNode; |
attila@980 | 632 | } |
attila@1064 | 633 | return transformFunction(functionNode, new NodeVisitor<LexicalContext>(new LexicalContext()) { |
attila@963 | 634 | @Override |
attila@963 | 635 | public Node leaveFunctionNode(final FunctionNode fn) { |
attila@963 | 636 | return fn.setState(lc, state); |
attila@963 | 637 | } |
attila@963 | 638 | }); |
lagergren@89 | 639 | } |
lagergren@89 | 640 | |
attila@963 | 641 | /** |
attila@963 | 642 | * Start a compilation phase |
lagergren@1028 | 643 | * @param compiler the compiler to use |
attila@963 | 644 | * @param functionNode function to compile |
attila@963 | 645 | * @return function node |
attila@963 | 646 | */ |
attila@963 | 647 | protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) { |
attila@963 | 648 | compiler.getLogger().indent(); |
attila@963 | 649 | |
attila@963 | 650 | assert pre != null; |
attila@963 | 651 | |
attila@980 | 652 | if (!functionNode.hasState(pre)) { |
attila@963 | 653 | final StringBuilder sb = new StringBuilder("Compilation phase "); |
attila@963 | 654 | sb.append(this). |
attila@963 | 655 | append(" is not applicable to "). |
attila@963 | 656 | append(quote(functionNode.getName())). |
attila@963 | 657 | append("\n\tFunctionNode state = "). |
attila@963 | 658 | append(functionNode.getState()). |
attila@963 | 659 | append("\n\tRequired state = "). |
attila@963 | 660 | append(this.pre); |
attila@963 | 661 | |
attila@963 | 662 | throw new CompilationException(sb.toString()); |
attila@963 | 663 | } |
attila@963 | 664 | |
attila@977 | 665 | startTime = System.nanoTime(); |
attila@963 | 666 | |
attila@963 | 667 | return functionNode; |
attila@963 | 668 | } |
attila@963 | 669 | |
attila@963 | 670 | /** |
attila@963 | 671 | * End a compilation phase |
attila@963 | 672 | * @param compiler the compiler |
attila@963 | 673 | * @param functionNode function node to compile |
attila@963 | 674 | * @return function node |
attila@963 | 675 | */ |
attila@963 | 676 | protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) { |
attila@963 | 677 | compiler.getLogger().unindent(); |
attila@977 | 678 | endTime = System.nanoTime(); |
attila@963 | 679 | compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime); |
lagergren@89 | 680 | |
lagergren@89 | 681 | isFinished = true; |
lagergren@211 | 682 | return functionNode; |
lagergren@89 | 683 | } |
lagergren@89 | 684 | |
lagergren@89 | 685 | boolean isFinished() { |
lagergren@89 | 686 | return isFinished; |
lagergren@89 | 687 | } |
lagergren@89 | 688 | |
lagergren@89 | 689 | long getStartTime() { |
lagergren@89 | 690 | return startTime; |
lagergren@89 | 691 | } |
lagergren@89 | 692 | |
lagergren@89 | 693 | long getEndTime() { |
lagergren@89 | 694 | return endTime; |
lagergren@89 | 695 | } |
lagergren@89 | 696 | |
attila@963 | 697 | abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException; |
lagergren@89 | 698 | |
attila@963 | 699 | /** |
attila@963 | 700 | * Apply a transform to a function node, returning the transfored function node. If the transform is not |
attila@963 | 701 | * applicable, an exception is thrown. Every transform requires the function to have a certain number of |
attila@963 | 702 | * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor |
attila@963 | 703 | * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states. |
attila@963 | 704 | * |
attila@963 | 705 | * @param compiler compiler |
attila@963 | 706 | * @param phases current complete pipeline of which this phase is one |
attila@963 | 707 | * @param functionNode function node to transform |
attila@963 | 708 | * |
attila@963 | 709 | * @return transformed function node |
attila@963 | 710 | * |
attila@963 | 711 | * @throws CompilationException if function node lacks the state required to run the transform on it |
attila@963 | 712 | */ |
attila@963 | 713 | final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException { |
attila@963 | 714 | assert phases.contains(this); |
attila@963 | 715 | |
attila@963 | 716 | return end(compiler, transform(compiler, phases, begin(compiler, functionNode))); |
lagergren@89 | 717 | } |
lagergren@89 | 718 | |
attila@1064 | 719 | private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) { |
attila@1064 | 720 | return (FunctionNode) fn.accept(visitor); |
attila@1064 | 721 | } |
attila@1064 | 722 | |
attila@1064 | 723 | private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) { |
attila@1064 | 724 | final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); |
attila@1064 | 725 | if (phases.isRestOfCompilation()) { |
attila@1064 | 726 | sb.append("$restOf"); |
attila@1064 | 727 | } |
attila@1064 | 728 | //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what |
attila@1064 | 729 | //fills those out anyway. Thus no need for a copy constructor |
attila@1064 | 730 | return compiler.createCompileUnit(sb.toString(), 0); |
attila@1064 | 731 | } |
lagergren@89 | 732 | } |