blob: 298a8f67b8349a92e2e9fec50034e34fe1124a74 [file] [log] [blame]
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.test.lib.jittester.factories;
import jdk.test.lib.jittester.Block;
import jdk.test.lib.jittester.IRNode;
import jdk.test.lib.jittester.If;
import jdk.test.lib.jittester.ProductionFailedException;
import jdk.test.lib.jittester.ProductionParams;
import jdk.test.lib.jittester.Rule;
import jdk.test.lib.jittester.Switch;
import jdk.test.lib.jittester.SymbolTable;
import jdk.test.lib.jittester.Type;
import jdk.test.lib.jittester.TypeList;
import jdk.test.lib.jittester.utils.TypeUtil;
import jdk.test.lib.jittester.loops.DoWhile;
import jdk.test.lib.jittester.loops.For;
import jdk.test.lib.jittester.loops.While;
import jdk.test.lib.jittester.types.TypeKlass;
import jdk.test.lib.jittester.utils.PseudoRandom;
import java.util.ArrayList;
import java.util.List;
class BlockFactory extends Factory<Block> {
private final Type returnType;
private final long complexityLimit;
private final int statementLimit;
private final int operatorLimit;
private final boolean subBlock;
private final boolean canHaveBreaks;
private final boolean canHaveContinues;
private final boolean canHaveReturn;
private final boolean canHaveThrow;
private final int level;
private final TypeKlass ownerClass;
BlockFactory(TypeKlass klass, Type returnType, long complexityLimit, int statementLimit,
int operatorLimit, int level, boolean subBlock, boolean canHaveBreaks,
boolean canHaveContinues, boolean canHaveReturn, boolean canHaveThrows) {
this.ownerClass = klass;
this.returnType = returnType;
this.complexityLimit = complexityLimit;
this.statementLimit = statementLimit;
this.operatorLimit = operatorLimit;
this.level = level;
this.subBlock = subBlock;
this.canHaveBreaks = canHaveBreaks;
this.canHaveContinues = canHaveContinues;
this.canHaveReturn = canHaveReturn;
this.canHaveThrow = canHaveThrows;
}
@Override
public Block produce() throws ProductionFailedException {
if (statementLimit > 0 && complexityLimit > 0) {
List<IRNode> content = new ArrayList<>();
int slimit = PseudoRandom.randomNotZero(statementLimit);
long climit = complexityLimit;
IRNodeBuilder builder = new IRNodeBuilder()
.setOperatorLimit(operatorLimit)
.setOwnerKlass(ownerClass)
.setResultType(returnType)
.setCanHaveReturn(canHaveReturn)
.setCanHaveThrow(canHaveThrow)
.setCanHaveBreaks(canHaveBreaks)
.setCanHaveContinues(canHaveContinues)
.setExceptionSafe(false)
.setNoConsts(false);
Rule<IRNode> rule;
SymbolTable.push();
for (int i = 0; i < slimit && climit > 0; ) {
int subLimit = (int) (PseudoRandom.random() * (slimit - i - 1));
builder.setComplexityLimit((long) (PseudoRandom.random() * climit));
rule = new Rule<>("block");
rule.add("statement", builder.getStatementFactory(), 5);
if (!ProductionParams.disableVarsInBlock.value()) {
rule.add("decl", builder.setIsLocal(true).getDeclarationFactory());
}
if (subLimit > 0) {
builder.setStatementLimit(subLimit).setLevel(level + 1);
if (!ProductionParams.disableNestedBlocks.value()) {
rule.add("block", builder.setCanHaveReturn(false)
.setCanHaveThrow(false)
.setCanHaveBreaks(false)
.setCanHaveContinues(false)
.getBlockFactory());
rule.add("try-catch", builder.getTryCatchBlockFactory(), 0.3);
builder.setCanHaveReturn(canHaveReturn)
.setCanHaveThrow(canHaveThrow)
.setCanHaveBreaks(canHaveBreaks)
.setCanHaveContinues(canHaveContinues);
}
addControlFlowDeviation(rule, builder);
}
try {
IRNode choiceResult = rule.produce();
if (choiceResult instanceof If || choiceResult instanceof While || choiceResult instanceof DoWhile
|| choiceResult instanceof For || choiceResult instanceof Switch) {
i += subLimit;
} else {
i++;
}
//climit -= subBlockComplLimit; // very approximate. to obnain a precise value, change to p.complexity()
climit -= choiceResult.complexity();
content.add(choiceResult);
} catch (ProductionFailedException e) {
i++;
}
}
// Ok, if the block can end with break and continue. Generate the appropriate productions.
rule = new Rule<>("block_ending");
if (canHaveBreaks && !subBlock) {
rule.add("break", builder.getBreakFactory());
}
if (canHaveContinues && !subBlock) {
rule.add("continue", builder.getContinueFactory());
}
if (canHaveReturn && !subBlock && !returnType.equals(TypeList.VOID)) {
rule.add("return", builder.setComplexityLimit(climit).getReturnFactory());
}
if (canHaveThrow && !subBlock) {
Type rtException = TypeList.find("java.lang.RuntimeException");
rtException = PseudoRandom.randomElement(TypeUtil.getImplicitlyCastable(TypeList.getAll(), rtException));
rule.add("throw", builder.setResultType(rtException)
.setComplexityLimit(Math.max(climit, 5))
.setOperatorLimit(Math.max(operatorLimit, 5))
.getThrowFactory());
}
try {
if (rule.size() > 0) {
content.add(rule.produce());
}
} catch (ProductionFailedException e) {
}
if (!subBlock) {
SymbolTable.pop();
} else {
SymbolTable.merge();
}
return new Block(ownerClass, returnType, content, level);
}
throw new ProductionFailedException();
}
private void addControlFlowDeviation(Rule<IRNode> rule, IRNodeBuilder builder) {
if (!ProductionParams.disableIf.value()) {
rule.add("if", builder.getIfFactory());
}
if (!ProductionParams.disableWhile.value()) {
rule.add("while", builder.getWhileFactory());
}
if (!ProductionParams.disableDoWhile.value()) {
rule.add("do_while", builder.getDoWhileFactory());
}
if (!ProductionParams.disableFor.value()) {
rule.add("for", builder.getForFactory());
}
if (!ProductionParams.disableSwitch.value()) {
rule.add("switch", builder.getSwitchFactory(), 0.1);
}
}
}