1 /*
   2  * Copyright (c) 2015, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package compiler.compilercontrol.share.scenario;
  25 
  26 import compiler.compilercontrol.share.JSONFile;
  27 import compiler.compilercontrol.share.method.MethodDescriptor;
  28 import compiler.compilercontrol.share.method.MethodGenerator;
  29 import jdk.test.lib.Pair;
  30 import pool.PoolHelper;
  31 
  32 import java.lang.reflect.Executable;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.ArrayList;
  36 import java.util.HashMap;
  37 import java.util.LinkedHashMap;
  38 import java.util.concurrent.Callable;
  39 
  40 /**
  41  * Directive file and state builder class
  42  */
  43 public class DirectiveBuilder implements StateBuilder<CompileCommand> {
  44     private static final List<Pair<Executable, Callable<?>>> METHODS
  45             = new PoolHelper().getAllMethods();
  46     private final Map<Executable, State> stateMap = new HashMap<>();
  47     private final String fileName;
  48     private Map<MethodDescriptor, List<CompileCommand>> matchBlocks
  49             = new LinkedHashMap<>();
  50     private List<String> inlineMatch = new ArrayList<>();
  51     private boolean isFileValid = true;
  52 
  53     public DirectiveBuilder(String fileName) {
  54         this.fileName = fileName;
  55     }
  56 
  57     @Override
  58     public List<String> getOptions() {
  59         List<String> options = new ArrayList<>();
  60         if (!matchBlocks.isEmpty()) {
  61             // add option only if there are any directive available
  62             options.add("-XX:CompilerDirectivesFile=" + fileName);
  63         }
  64         return options;
  65     }
  66 
  67     @Override
  68     public List<CompileCommand> getCompileCommands() {
  69         throw new Error("TESTBUG: isn't applicable for directives");
  70     }
  71 
  72     @Override
  73     public boolean isValid() {
  74         // Invalid directives file makes VM exit with error code
  75         return isFileValid;
  76     }
  77 
  78     @Override
  79     public Map<Executable, State> getStates() {
  80         try (DirectiveWriter dirFile = new DirectiveWriter(fileName)) {
  81             for (MethodDescriptor matchDescriptor : matchBlocks.keySet()) {
  82                 // Write match block with all options converted from commands
  83                 dirFile.match(matchDescriptor);
  84                 for (CompileCommand compileCommand :
  85                         matchBlocks.get(matchDescriptor)) {
  86                     isFileValid &= compileCommand.isValid();
  87                     handleCommand(dirFile, compileCommand);
  88                 }
  89                 if ("Inlinee.caller".matches((matchDescriptor.getRegexp()))) {
  90                     // Got a *.* match block, where inline would be written
  91                     dirFile.inline(inlineMatch.toArray(
  92                             new String[inlineMatch.size()]));
  93                     inlineMatch.clear();
  94                 }
  95                 dirFile.end(); // ends match block
  96             }
  97 
  98             /*
  99              * Write inline directive in the end to the latest match block
 100              * if we didn't do this before
 101              * Inlinee caller methods should match this block only
 102              */
 103             if (!inlineMatch.isEmpty()) {
 104                 Pair<Executable, Callable<?>> pair = METHODS.get(0);
 105                 MethodDescriptor md = MethodGenerator.anyMatchDescriptor(
 106                         pair.first);
 107                 CompileCommand cc = new CompileCommand(Command.QUIET, md, null,
 108                         Scenario.Type.DIRECTIVE);
 109                 List<CompileCommand> commands = new ArrayList<>();
 110 
 111                 // Add appropriate "*.*" match block
 112                 commands.add(cc);
 113                 matchBlocks.put(md, commands);
 114                 // Add match block for this descriptor with inlines
 115                 dirFile.match(md);
 116                 dirFile.inline(inlineMatch.toArray(
 117                         new String[inlineMatch.size()]));
 118                 dirFile.end();
 119             }
 120             if (!matchBlocks.isEmpty()) {
 121                 // terminates file
 122                 dirFile.end();
 123             }
 124 
 125             // Build states for each method according to match blocks
 126             for (Pair<Executable, Callable<?>> pair : METHODS) {
 127                 State state = getState(pair);
 128                 if (state != null) {
 129                     stateMap.put(pair.first, state);
 130                 }
 131             }
 132         }
 133         if (isFileValid) {
 134             return stateMap;
 135         } else {
 136             // return empty map because invalid file doesn't change states
 137             return new HashMap<>();
 138         }
 139     }
 140 
 141     private State getState(Pair<Executable, Callable<?>> pair) {
 142         State state = null;
 143         MethodDescriptor execDesc = MethodGenerator.commandDescriptor(
 144                 pair.first);
 145         boolean isMatchFound = false;
 146 
 147         if (stateMap.containsKey(pair.first)) {
 148             state = stateMap.get(pair.first);
 149         }
 150         for (MethodDescriptor matchDesc : matchBlocks.keySet()) {
 151             if (execDesc.getCanonicalString().matches(matchDesc.getRegexp())) {
 152                 /*
 153                  * if executable matches regex
 154                  * then apply commands from this match to the state
 155                  */
 156                 for (CompileCommand cc : matchBlocks.get(matchDesc)) {
 157                     state = new State();
 158                     if (!isMatchFound) {
 159                         // this is a first found match, apply all commands
 160                         state.apply(cc);
 161                     } else {
 162                         // apply only inline directives
 163                         switch (cc.command) {
 164                             case INLINE:
 165                             case DONTINLINE:
 166                                 state.apply(cc);
 167                                 break;
 168                         }
 169                     }
 170                 }
 171                 isMatchFound = true;
 172             }
 173         }
 174         return state;
 175     }
 176 
 177     private void handleCommand(DirectiveWriter dirFile, CompileCommand cmd) {
 178         Command command = cmd.command;
 179 
 180         switch (command) {
 181             case COMPILEONLY:
 182                 dirFile.excludeCompile(cmd.compiler, false);
 183                 break;
 184             case EXCLUDE:
 185                 dirFile.excludeCompile(cmd.compiler, true);
 186                 break;
 187             case INLINE:
 188             case DONTINLINE:
 189                 // Inline commands will be written later
 190                 break;
 191             case LOG:
 192                 dirFile.option(DirectiveWriter.Option.LOG, true);
 193                 break;
 194             case QUIET:
 195                 // there are no appropriate directive for this
 196                 break;
 197             case PRINT:
 198                 dirFile.option(DirectiveWriter.Option.PRINT_ASSEMBLY, true);
 199                 break;
 200             case NONEXISTENT:
 201                 dirFile.write(JSONFile.Element.PAIR, command.name);
 202                 dirFile.write(JSONFile.Element.OBJECT);
 203                 dirFile.write(JSONFile.Element.PAIR, command.name);
 204                 dirFile.write(JSONFile.Element.VALUE,
 205                         cmd.methodDescriptor.getString());
 206                 dirFile.end(); // ends object
 207                 break;
 208             default:
 209                 throw new Error("TESTBUG: wrong command: " + command);
 210         }
 211     }
 212 
 213     @Override
 214     public void add(CompileCommand compileCommand) {
 215         MethodDescriptor methodDescriptor = compileCommand.methodDescriptor;
 216 
 217         switch (compileCommand.command) {
 218             case INLINE:
 219                 inlineMatch.add("+" + methodDescriptor.getString());
 220                 break;
 221             case DONTINLINE:
 222                 inlineMatch.add("-" + methodDescriptor.getString());
 223                 break;
 224         }
 225         for (MethodDescriptor md: matchBlocks.keySet()) {
 226             if (methodDescriptor.getCanonicalString().matches(md.getRegexp())) {
 227                 matchBlocks.get(md).add(compileCommand);
 228             }
 229         }
 230         if (!matchBlocks.containsKey(compileCommand.methodDescriptor)) {
 231             List<CompileCommand> commands = new ArrayList<>();
 232             commands.add(compileCommand);
 233             matchBlocks.put(compileCommand.methodDescriptor, commands);
 234         }
 235     }
 236 }