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 jdk.test.lib.jittester.factories;
  25 
  26 import jdk.test.lib.jittester.Block;
  27 import jdk.test.lib.jittester.IRNode;
  28 import jdk.test.lib.jittester.If;
  29 import jdk.test.lib.jittester.ProductionFailedException;
  30 import jdk.test.lib.jittester.ProductionParams;
  31 import jdk.test.lib.jittester.Rule;
  32 import jdk.test.lib.jittester.Switch;
  33 import jdk.test.lib.jittester.SymbolTable;
  34 import jdk.test.lib.jittester.Type;
  35 import jdk.test.lib.jittester.TypeList;
  36 import jdk.test.lib.jittester.utils.TypeUtil;
  37 import jdk.test.lib.jittester.loops.DoWhile;
  38 import jdk.test.lib.jittester.loops.For;
  39 import jdk.test.lib.jittester.loops.While;
  40 import jdk.test.lib.jittester.types.TypeKlass;
  41 import jdk.test.lib.jittester.types.TypeVoid;
  42 import jdk.test.lib.jittester.utils.PseudoRandom;
  43 
  44 import java.util.ArrayList;
  45 import java.util.List;
  46 
  47 class BlockFactory extends Factory {
  48     private final Type returnType;
  49     private final long complexityLimit;
  50     private final int statementLimit;
  51     private final int operatorLimit;
  52     private final boolean subBlock;
  53     private final boolean canHaveBreaks;
  54     private final boolean canHaveContinues;
  55     private final boolean canHaveReturn;
  56     private final boolean canHaveThrow;
  57     private final int level;
  58     private final TypeKlass ownerClass;
  59 
  60     BlockFactory(TypeKlass klass, Type returnType, long complexityLimit, int statementLimit,
  61                  int operatorLimit, int level, boolean subBlock, boolean canHaveBreaks,
  62                  boolean canHaveContinues, boolean canHaveReturn, boolean canHaveThrows) {
  63         this.ownerClass = klass;
  64         this.returnType = returnType;
  65         this.complexityLimit = complexityLimit;
  66         this.statementLimit = statementLimit;
  67         this.operatorLimit = operatorLimit;
  68         this.level = level;
  69         this.subBlock = subBlock;
  70         this.canHaveBreaks = canHaveBreaks;
  71         this.canHaveContinues = canHaveContinues;
  72         this.canHaveReturn = canHaveReturn;
  73         this.canHaveThrow = canHaveThrows;
  74     }
  75 
  76     @Override
  77     public IRNode produce() throws ProductionFailedException {
  78         if (statementLimit > 0 && complexityLimit > 0) {
  79             List<IRNode> content = new ArrayList<>();
  80             int slimit = PseudoRandom.randomNotZero(statementLimit);
  81             long climit = complexityLimit;
  82             IRNodeBuilder builder = new IRNodeBuilder()
  83                     .setOperatorLimit(operatorLimit)
  84                     .setOwnerKlass(ownerClass)
  85                     .setResultType(returnType)
  86                     .setCanHaveReturn(canHaveReturn)
  87                     .setCanHaveThrow(canHaveThrow)
  88                     .setCanHaveBreaks(canHaveBreaks)
  89                     .setCanHaveContinues(canHaveContinues)
  90                     .setExceptionSafe(false)
  91                     .setNoConsts(false);
  92             Rule rule;
  93             SymbolTable.push();
  94             for (int i = 0; i < slimit && climit > 0; ) {
  95                 int subLimit = (int) (PseudoRandom.random() * (slimit - i - 1));
  96                 builder.setComplexityLimit((long) (PseudoRandom.random() * climit));
  97                 rule = new Rule("block");
  98                 rule.add("statement", builder.getStatementFactory(), 5);
  99                 if (!ProductionParams.disableVarsInBlock.value()) {
 100                     rule.add("decl", builder.setIsLocal(true).getDeclarationFactory());
 101                 }
 102                 if (subLimit > 0) {
 103                     builder.setStatementLimit(subLimit).setLevel(level + 1);
 104                     if (!ProductionParams.disableNestedBlocks.value()) {
 105                         rule.add("block", builder.setCanHaveReturn(false)
 106                                 .setCanHaveThrow(false)
 107                                 .setCanHaveBreaks(false)
 108                                 .setCanHaveContinues(false)
 109                                 .getBlockFactory());
 110                         rule.add("try-catch", builder.getTryCatchBlockFactory(), 0.3);
 111                         builder.setCanHaveReturn(canHaveReturn)
 112                                 .setCanHaveThrow(canHaveThrow)
 113                                 .setCanHaveBreaks(canHaveBreaks)
 114                                 .setCanHaveContinues(canHaveContinues);
 115                     }
 116                     addControlFlowDeviation(rule, builder);
 117                 }
 118                 try {
 119                     IRNode choiceResult = rule.produce();
 120                     if (choiceResult instanceof If || choiceResult instanceof While || choiceResult instanceof DoWhile
 121                             || choiceResult instanceof For || choiceResult instanceof Switch) {
 122                         i += subLimit;
 123                     } else {
 124                         i++;
 125                     }
 126                     //climit -= subBlockComplLimit; // very approximate. to obnain a precise value, change to p.complexity()
 127                     climit -= choiceResult.complexity();
 128                     content.add(choiceResult);
 129                 } catch (ProductionFailedException e) {
 130                     i++;
 131                 }
 132             }
 133             // Ok, if the block can end with break and continue. Generate the appropriate productions.
 134             rule = new Rule("block_ending");
 135             if (canHaveBreaks && !subBlock) {
 136                 rule.add("break", builder.getBreakFactory());
 137             }
 138             if (canHaveContinues && !subBlock) {
 139                 rule.add("continue", builder.getContinueFactory());
 140             }
 141             if (canHaveReturn && !subBlock && !returnType.equals(new TypeVoid())) {
 142                 rule.add("return", builder.setComplexityLimit(climit).getReturnFactory());
 143             }
 144             if (canHaveThrow && !subBlock) {
 145                 Type rtException = TypeList.find("java.lang.RuntimeException");
 146                 rtException = PseudoRandom.randomElement(TypeUtil.getImplicitlyCastable(TypeList.getAll(), rtException));
 147                 rule.add("throw", builder.setResultType(rtException)
 148                         .setComplexityLimit(Math.max(climit, 5))
 149                         .setOperatorLimit(Math.max(operatorLimit, 5))
 150                         .getThrowFactory());
 151             }
 152 
 153             try {
 154                 if (rule.size() > 0) {
 155                     content.add(rule.produce());
 156                 }
 157             } catch (ProductionFailedException e) {
 158             }
 159             if (!subBlock) {
 160                 SymbolTable.pop();
 161             } else {
 162                 SymbolTable.merge();
 163             }
 164             return new Block(ownerClass, returnType, content, level);
 165         }
 166         throw new ProductionFailedException();
 167     }
 168 
 169     private void addControlFlowDeviation(Rule rule, IRNodeBuilder builder) {
 170         if (!ProductionParams.disableIf.value()) {
 171             rule.add("if", builder.getIfFactory());
 172         }
 173         if (!ProductionParams.disableWhile.value()) {
 174             rule.add("while", builder.getWhileFactory());
 175         }
 176         if (!ProductionParams.disableDoWhile.value()) {
 177             rule.add("do_while", builder.getDoWhileFactory());
 178         }
 179         if (!ProductionParams.disableFor.value()) {
 180             rule.add("for", builder.getForFactory());
 181         }
 182         if (!ProductionParams.disableSwitch.value()) {
 183             rule.add("switch", builder.getSwitchFactory(), 0.1);
 184         }
 185     }
 186 }