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