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 java.util.ArrayList;
  27 import java.util.List;
  28 import java.util.stream.Collectors;
  29 import jdk.test.lib.jittester.Block;
  30 import jdk.test.lib.jittester.IRNode;
  31 import jdk.test.lib.jittester.ProductionFailedException;
  32 import jdk.test.lib.jittester.ProductionParams;
  33 import jdk.test.lib.jittester.Symbol;
  34 import jdk.test.lib.jittester.SymbolTable;
  35 import jdk.test.lib.jittester.Type;
  36 import jdk.test.lib.jittester.TypeList;
  37 import jdk.test.lib.jittester.VariableInfo;
  38 import jdk.test.lib.jittester.classes.MainKlass;
  39 import jdk.test.lib.jittester.functions.FunctionInfo;
  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 class MainKlassFactory extends Factory {
  45     private final String name;
  46     private final long complexityLimit;
  47     private final int statementsInTestFunctionLimit;
  48     private final int statementsInFunctionLimit;
  49     private final int operatorLimit;
  50     private final int memberFunctionsLimit;
  51     private final int memberFunctionsArgLimit;
  52     private TypeKlass thisKlass;
  53 
  54     MainKlassFactory(String name, long complexityLimit, int memberFunctionsLimit,
  55             int memberFunctionsArgLimit, int statementsInFunctionLimit,
  56             int statementsInTestFunctionLimit, int operatorLimit) {
  57         this.name = name;
  58         this.complexityLimit = complexityLimit;
  59         this.memberFunctionsLimit = memberFunctionsLimit;
  60         this.memberFunctionsArgLimit = memberFunctionsArgLimit;
  61         this.statementsInFunctionLimit = statementsInFunctionLimit;
  62         this.statementsInTestFunctionLimit = statementsInTestFunctionLimit;
  63         this.operatorLimit = operatorLimit;
  64     }
  65 
  66     @Override
  67     public IRNode produce() throws ProductionFailedException {
  68         TypeKlass parent = (TypeKlass) TypeList.find("java.lang.Object");
  69         thisKlass = new TypeKlass(name);
  70         thisKlass.addParent(parent.getName());
  71         thisKlass.setParent(parent);
  72         parent.addChild(name);
  73         parent.addChild(thisKlass);
  74         SymbolTable.add(new VariableInfo("this", thisKlass, thisKlass,
  75                 VariableInfo.FINAL | VariableInfo.LOCAL | VariableInfo.INITIALIZED));
  76         IRNodeBuilder builder = new IRNodeBuilder()
  77                 .setOwnerKlass(thisKlass)
  78                 .setOperatorLimit(operatorLimit)
  79                 .setMemberFunctionsLimit(memberFunctionsLimit)
  80                 .setMemberFunctionsArgLimit(memberFunctionsArgLimit)
  81                 .setStatementLimit(statementsInFunctionLimit)
  82                 .setLevel(1)
  83                 .setExceptionSafe(true)
  84                 .setPrinterName("Printer");
  85         IRNode variableDeclarations = builder
  86                 .setComplexityLimit((long) (complexityLimit * 0.05))
  87                 .getVariableDeclarationBlockFactory().produce();
  88         IRNode functionDefinitions = null;
  89         if (!ProductionParams.disableFunctions.value()) {
  90             functionDefinitions = builder
  91                     .setComplexityLimit((long) (complexityLimit * 0.01 * PseudoRandom.random()))
  92                     .setFlags(FunctionInfo.NONRECURSIVE)
  93                     .getFunctionDefinitionBlockFactory()
  94                     .produce();
  95         }
  96         IRNode testFunction = builder.setResultType(new TypeVoid())
  97                 .setComplexityLimit(complexityLimit)
  98                 .setStatementLimit(statementsInTestFunctionLimit)
  99                 .getBlockFactory()
 100                 .produce();
 101         SymbolTable.remove(new Symbol("this", thisKlass, thisKlass, VariableInfo.NONE));
 102         IRNode printVariables = builder.setLevel(2)
 103                 .getPrintVariablesFactory()
 104                 .produce();
 105         List<IRNode> childs = new ArrayList<>();
 106         childs.add(variableDeclarations);
 107         childs.add(functionDefinitions);
 108         childs.add(testFunction);
 109         childs.add(printVariables);
 110         ensureMinDepth(childs, builder);
 111         ensureMaxDepth(childs);
 112         return new MainKlass(name, thisKlass, variableDeclarations,
 113                 functionDefinitions, testFunction, printVariables);
 114     }
 115 
 116     private void ensureMaxDepth(List<IRNode> childs) {
 117         int maxDepth = ProductionParams.maxCfgDepth.value();
 118         List<IRNode> filtered = childs.stream()
 119             .filter(c -> c.isCFDeviation() && c.countDepth() > maxDepth)
 120             .collect(Collectors.toList());
 121         for (IRNode child : filtered) {
 122             List<IRNode> leaves = null;
 123             do {
 124                 long depth = Math.max(child.countDepth(), maxDepth + 1);
 125                 leaves = child.getDeviantBlocks(depth);
 126                 leaves.get(0).removeSelf();
 127             } while (!leaves.isEmpty() && child.countDepth() > maxDepth);
 128         }
 129     }
 130 
 131     private void ensureMinDepth(List<IRNode> childs, IRNodeBuilder builder)
 132             throws ProductionFailedException {
 133         int minDepth = ProductionParams.minCfgDepth.value();
 134         List<IRNode> filtered = new ArrayList<>(childs);
 135         addMoreChildren(filtered, minDepth, builder);
 136     }
 137 
 138     private void addMoreChildren(List<IRNode> childs, int minDepth, IRNodeBuilder builder)
 139             throws ProductionFailedException {
 140         while (!childs.isEmpty() && IRNode.countDepth(childs) < minDepth) {
 141             PseudoRandom.shuffle(childs);
 142             IRNode randomChild = childs.get(0);
 143             List<IRNode> leaves = randomChild.getStackableLeaves();
 144             if (!leaves.isEmpty()) {
 145                 PseudoRandom.shuffle(leaves);
 146                 Block randomLeaf = (Block) leaves.get(0);
 147                 TypeKlass klass = (TypeKlass) randomChild.getKlass();
 148                 int newLevel = randomLeaf.getLevel() + 1;
 149                 Type retType = randomLeaf.getReturnType();
 150                 IRNode newBlock = builder.setOwnerKlass(klass)
 151                         .setResultType(retType)
 152                         .setComplexityLimit(complexityLimit)
 153                         .setStatementLimit(statementsInFunctionLimit)
 154                         .setLevel(newLevel)
 155                         .getBlockFactory()
 156                         .produce();
 157                 List<IRNode> siblings = randomLeaf.getChildren();
 158                 // to avoid break;
 159                 int index = PseudoRandom.randomNotZero(siblings.size() - 1);
 160                 siblings.add(index, newBlock);
 161             }
 162         }
 163     }
 164 }