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.method.MethodDescriptor;
  27 import compiler.compilercontrol.share.method.MethodGenerator;
  28 import compiler.compilercontrol.share.pool.PoolHelper;
  29 import jdk.test.lib.util.Pair;
  30 
  31 import java.lang.reflect.Executable;
  32 import java.util.ArrayList;
  33 import java.util.HashMap;
  34 import java.util.Iterator;
  35 import java.util.LinkedHashMap;
  36 import java.util.List;
  37 import java.util.Map;
  38 import java.util.concurrent.Callable;
  39 import java.util.stream.Collectors;
  40 
  41 public class JcmdStateBuilder implements StateBuilder<JcmdCommand> {
  42     private static final List<Pair<Executable, Callable<?>>> METHODS
  43             = new PoolHelper().getAllMethods();
  44     private final Map<Executable, State> stateMap = new HashMap<>();
  45     private final DirectiveBuilder directiveBuilder;
  46     private final Map<MethodDescriptor, List<CompileCommand>> matchBlocks
  47             = new LinkedHashMap<>();
  48     private final List<CompileCommand> inlines = new ArrayList<>();
  49     private boolean isFileValid = true;
  50 
  51     public JcmdStateBuilder(String fileName) {
  52         directiveBuilder = new DirectiveBuilder(fileName);
  53     }
  54 
  55     @Override
  56     public void add(JcmdCommand compileCommand) {
  57         switch (compileCommand.jcmdType) {
  58             case ADD:
  59                 directiveBuilder.add(compileCommand);
  60                 addCommand(compileCommand);
  61                 break;
  62             case PRINT:
  63                 // doesn't change the state
  64                 break;
  65             case CLEAR:
  66                 matchBlocks.clear();
  67                 inlines.clear();
  68                 break;
  69             case REMOVE:
  70                 removeDirective();
  71                 break;
  72         }
  73     }
  74 
  75     private void addCommand(JcmdCommand compileCommand) {
  76         isFileValid &= compileCommand.isValid();
  77         MethodDescriptor methodDescriptor = compileCommand.methodDescriptor;
  78 
  79         switch (compileCommand.command) {
  80             case INLINE:
  81             case DONTINLINE:
  82                 inlines.add(compileCommand);
  83                 break;
  84         }
  85         for (MethodDescriptor md: matchBlocks.keySet()) {
  86             if (methodDescriptor.getCanonicalString().matches(md.getRegexp())) {
  87                 matchBlocks.get(md).add(compileCommand);
  88             }
  89         }
  90         if (!matchBlocks.containsKey(compileCommand.methodDescriptor)) {
  91             List<CompileCommand> commands = new ArrayList<>();
  92             commands.add(compileCommand);
  93             matchBlocks.put(compileCommand.methodDescriptor, commands);
  94         }
  95     }
  96 
  97     private void removeDirective() {
  98         Iterator<MethodDescriptor> iterator = matchBlocks.keySet().iterator();
  99         if (iterator.hasNext()) {
 100             MethodDescriptor md = iterator.next();
 101             matchBlocks.remove(md);
 102         }
 103     }
 104 
 105     @Override
 106     public boolean isValid() {
 107         // VM skips invalid directive file added via jcmd command
 108         return true;
 109     }
 110 
 111     @Override
 112     public Map<Executable, State> getStates() {
 113         directiveBuilder.getStates();
 114         for (MethodDescriptor matchDescriptor : matchBlocks.keySet()) {
 115             if ("Inlinee.caller()".matches(matchDescriptor.getRegexp())
 116                     && !inlines.isEmpty()) {
 117                 // Got a *.* match block, where inline would be written
 118                 inlines.clear();
 119             }
 120         }
 121         /*
 122          * Write inline directive in the end to the latest match block
 123          * if we didn't do this before
 124          * Inlinee caller methods should match this block only
 125          */
 126         if (!inlines.isEmpty()) {
 127             Pair<Executable, Callable<?>> pair = METHODS.get(0);
 128             MethodDescriptor md = MethodGenerator.anyMatchDescriptor(
 129                     pair.first);
 130             CompileCommand cc = new CompileCommand(Command.QUIET, md,
 131                     null, Scenario.Type.DIRECTIVE);
 132             List<CompileCommand> commands = new ArrayList<>();
 133 
 134             // Add appropriate "*.*" match block
 135             commands.add(cc);
 136             matchBlocks.put(md, commands);
 137         }
 138         if (isFileValid) {
 139             // Build states for each method according to match blocks
 140             for (Pair<Executable, Callable<?>> pair : METHODS) {
 141                 State state = getState(pair);
 142                 if (state != null) {
 143                     stateMap.put(pair.first, state);
 144                 }
 145             }
 146             return stateMap;
 147         } else {
 148             // return empty map because invalid file doesn't change states
 149             return new HashMap<>();
 150         }
 151     }
 152 
 153     private State getState(Pair<Executable, Callable<?>> pair) {
 154         State state = null;
 155         MethodDescriptor execDesc = MethodGenerator.commandDescriptor(
 156                 pair.first);
 157         boolean isMatchFound = false;
 158 
 159         if (stateMap.containsKey(pair.first)) {
 160             state = stateMap.get(pair.first);
 161         }
 162         for (MethodDescriptor matchDesc : matchBlocks.keySet()) {
 163             if (execDesc.getCanonicalString().matches(matchDesc.getRegexp())) {
 164                 /*
 165                  * if executable matches regex
 166                  * then apply commands from this match to the state
 167                  */
 168                 for (CompileCommand cc : matchBlocks.get(matchDesc)) {
 169                     if (state == null) {
 170                         state = new State();
 171                     }
 172                     if (!isMatchFound) {
 173                         // this is a first found match, apply all commands
 174                         state.apply(cc);
 175                     } else {
 176                         // apply only inline directives
 177                         switch (cc.command) {
 178                             case INLINE:
 179                             case DONTINLINE:
 180                                 state.apply(cc);
 181                                 break;
 182                         }
 183                     }
 184                 }
 185                 isMatchFound = true;
 186             }
 187         }
 188         return state;
 189     }
 190 
 191     @Override
 192     public List<String> getOptions() {
 193         return new ArrayList<>();
 194     }
 195 
 196     @Override
 197     public List<JcmdCommand> getCompileCommands() {
 198         if (isFileValid) {
 199             return matchBlocks.keySet().stream()
 200                     /* only method descriptor is required
 201                        to check print_directives */
 202                     .map(md -> new JcmdCommand(null, md, null, null,
 203                             Scenario.JcmdType.ADD))
 204                     .collect(Collectors.toList());
 205         } else {
 206             return new ArrayList<>();
 207         }
 208     }
 209 }