You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1362 lines
39 KiB
1362 lines
39 KiB
/*
|
|
* Copyright 2016, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <cmath>
|
|
#include <random>
|
|
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
namespace {
|
|
|
|
/*
|
|
* Operators.
|
|
*/
|
|
|
|
static constexpr const char* kIncDecOps[] = { "++", "--" };
|
|
static constexpr const char* kIntUnaryOps[] = { "+", "-", "~" };
|
|
static constexpr const char* kFpUnaryOps[] = { "+", "-" };
|
|
|
|
static constexpr const char* kBoolBinOps[] = { "&&", "||", "&", "|", "^" }; // few less common
|
|
static constexpr const char* kIntBinOps[] = { "+", "-", "*", "/", "%",
|
|
">>", ">>>", "<<", "&", "|", "^" };
|
|
static constexpr const char* kFpBinOps[] = { "+", "-", "*", "/" };
|
|
|
|
static constexpr const char* kBoolAssignOps[] = { "=", "&=" , "|=", "^=" }; // few less common
|
|
static constexpr const char* kIntAssignOps[] = { "=", "+=", "-=", "*=", "/=", "%=",
|
|
">>=", ">>>=", "<<=", "&=", "|=", "^=" };
|
|
static constexpr const char* kFpAssignOps[] = { "=", "+=", "-=", "*=", "/=" };
|
|
|
|
static constexpr const char* kBoolRelOps[] = { "==", "!=" };
|
|
static constexpr const char* kRelOps[] = { "==", "!=", ">", ">=", "<", "<=" };
|
|
|
|
/*
|
|
* Exceptions.
|
|
*/
|
|
static const char* kExceptionTypes[] = {
|
|
"IllegalStateException",
|
|
"NullPointerException",
|
|
"IllegalArgumentException",
|
|
"ArrayIndexOutOfBoundsException"
|
|
};
|
|
|
|
/*
|
|
* Version of JFuzz. Increase this each time changes are made to the program
|
|
* to preserve the property that a given version of JFuzz yields the same
|
|
* fuzzed program for a deterministic random seed.
|
|
*/
|
|
const char* VERSION = "1.5";
|
|
|
|
/*
|
|
* Maximum number of array dimensions, together with corresponding maximum size
|
|
* within each dimension (to keep memory/runtime requirements roughly the same).
|
|
*/
|
|
static const uint32_t kMaxDim = 10;
|
|
static const uint32_t kMaxDimSize[kMaxDim + 1] = { 0, 1000, 32, 10, 6, 4, 3, 3, 2, 2, 2 };
|
|
|
|
/*
|
|
* Utility function to return the number of elements in an array.
|
|
*/
|
|
template <typename T, uint32_t N>
|
|
constexpr uint32_t countof(T const (&)[N]) {
|
|
return N;
|
|
}
|
|
|
|
/**
|
|
* A class that generates a random program that compiles correctly. The program
|
|
* is generated using rules that generate various programming constructs. Each rule
|
|
* has a fixed probability to "fire". Running a generated program yields deterministic
|
|
* output, making it suited to test various modes of execution (e.g an interpreter vs.
|
|
* an compiler or two different run times) for divergences.
|
|
*/
|
|
class JFuzz {
|
|
public:
|
|
JFuzz(FILE* out,
|
|
uint32_t seed,
|
|
uint32_t expr_depth,
|
|
uint32_t stmt_length,
|
|
uint32_t if_nest,
|
|
uint32_t loop_nest,
|
|
uint32_t try_nest)
|
|
: out_(out),
|
|
fuzz_random_engine_(seed),
|
|
fuzz_seed_(seed),
|
|
fuzz_expr_depth_(expr_depth),
|
|
fuzz_stmt_length_(stmt_length),
|
|
fuzz_if_nest_(if_nest),
|
|
fuzz_loop_nest_(loop_nest),
|
|
fuzz_try_nest_(try_nest),
|
|
return_type_(randomType()),
|
|
array_type_(randomType()),
|
|
array_dim_(random1(kMaxDim)),
|
|
array_size_(random1(kMaxDimSize[array_dim_])),
|
|
indentation_(0),
|
|
expr_depth_(0),
|
|
stmt_length_(0),
|
|
if_nest_(0),
|
|
loop_nest_(0),
|
|
switch_nest_(0),
|
|
do_nest_(0),
|
|
try_nest_(0),
|
|
boolean_local_(0),
|
|
int_local_(0),
|
|
long_local_(0),
|
|
float_local_(0),
|
|
double_local_(0),
|
|
in_inner_(false) { }
|
|
|
|
~JFuzz() { }
|
|
|
|
void emitProgram() {
|
|
emitHeader();
|
|
emitTestClassWithMain();
|
|
}
|
|
|
|
private:
|
|
//
|
|
// Types.
|
|
//
|
|
|
|
// Current type of each expression during generation.
|
|
enum Type {
|
|
kBoolean,
|
|
kInt,
|
|
kLong,
|
|
kFloat,
|
|
kDouble
|
|
};
|
|
|
|
// Test for an integral type.
|
|
static bool isInteger(Type tp) {
|
|
return tp == kInt || tp == kLong;
|
|
}
|
|
|
|
// Test for a floating-point type.
|
|
static bool isFP(Type tp) {
|
|
return tp == kFloat || tp == kDouble;
|
|
}
|
|
|
|
// Emit type.
|
|
void emitType(Type tp) const {
|
|
switch (tp) {
|
|
case kBoolean: fputs("boolean", out_); break;
|
|
case kInt: fputs("int", out_); break;
|
|
case kLong: fputs("long", out_); break;
|
|
case kFloat: fputs("float", out_); break;
|
|
case kDouble: fputs("double", out_); break;
|
|
}
|
|
}
|
|
|
|
// Emit type class.
|
|
void emitTypeClass(Type tp) const {
|
|
switch (tp) {
|
|
case kBoolean: fputs("Boolean", out_); break;
|
|
case kInt: fputs("Integer", out_); break;
|
|
case kLong: fputs("Long", out_); break;
|
|
case kFloat: fputs("Float", out_); break;
|
|
case kDouble: fputs("Double", out_); break;
|
|
}
|
|
}
|
|
|
|
// Return a random type.
|
|
Type randomType() {
|
|
switch (random1(5)) {
|
|
case 1: return kBoolean;
|
|
case 2: return kInt;
|
|
case 3: return kLong;
|
|
case 4: return kFloat;
|
|
default: return kDouble;
|
|
}
|
|
}
|
|
|
|
// Emits a random strong selected from an array of operator strings.
|
|
template <std::uint32_t N>
|
|
inline void emitOneOf(const char* const (&ops)[N]) {
|
|
fputs(ops[random0(N)], out_);
|
|
}
|
|
|
|
//
|
|
// Expressions.
|
|
//
|
|
|
|
// Emit an unary operator (same type in-out).
|
|
void emitUnaryOp(Type tp) {
|
|
if (tp == kBoolean) {
|
|
fputc('!', out_);
|
|
} else if (isInteger(tp)) {
|
|
emitOneOf(kIntUnaryOps);
|
|
} else { // isFP(tp)
|
|
emitOneOf(kFpUnaryOps);
|
|
}
|
|
}
|
|
|
|
// Emit a pre/post-increment/decrement operator (same type in-out).
|
|
void emitIncDecOp(Type tp) {
|
|
if (tp == kBoolean) {
|
|
// Not applicable, just leave "as is".
|
|
} else { // isInteger(tp) || isFP(tp)
|
|
emitOneOf(kIncDecOps);
|
|
}
|
|
}
|
|
|
|
// Emit a binary operator (same type in-out).
|
|
void emitBinaryOp(Type tp) {
|
|
if (tp == kBoolean) {
|
|
emitOneOf(kBoolBinOps);
|
|
} else if (isInteger(tp)) {
|
|
emitOneOf(kIntBinOps);
|
|
} else { // isFP(tp)
|
|
emitOneOf(kFpBinOps);
|
|
}
|
|
}
|
|
|
|
// Emit an assignment operator (same type in-out).
|
|
void emitAssignmentOp(Type tp) {
|
|
if (tp == kBoolean) {
|
|
emitOneOf(kBoolAssignOps);
|
|
} else if (isInteger(tp)) {
|
|
emitOneOf(kIntAssignOps);
|
|
} else { // isFP(tp)
|
|
emitOneOf(kFpAssignOps);
|
|
}
|
|
}
|
|
|
|
// Emit a relational operator (one type in, boolean out).
|
|
void emitRelationalOp(Type tp) {
|
|
if (tp == kBoolean) {
|
|
emitOneOf(kBoolRelOps);
|
|
} else { // isInteger(tp) || isFP(tp)
|
|
emitOneOf(kRelOps);
|
|
}
|
|
}
|
|
|
|
// Emit a type conversion operator sequence (out type given, new suitable in type picked).
|
|
Type emitTypeConversionOp(Type tp) {
|
|
if (tp == kInt) {
|
|
switch (random1(5)) {
|
|
case 1: fputs("(int)", out_); return kLong;
|
|
case 2: fputs("(int)", out_); return kFloat;
|
|
case 3: fputs("(int)", out_); return kDouble;
|
|
// Narrowing-widening.
|
|
case 4: fputs("(int)(byte)(int)", out_); return kInt;
|
|
case 5: fputs("(int)(short)(int)", out_); return kInt;
|
|
}
|
|
} else if (tp == kLong) {
|
|
switch (random1(6)) {
|
|
case 1: /* implicit */ return kInt;
|
|
case 2: fputs("(long)", out_); return kFloat;
|
|
case 3: fputs("(long)", out_); return kDouble;
|
|
// Narrowing-widening.
|
|
case 4: fputs("(long)(byte)(long)", out_); return kLong;
|
|
case 5: fputs("(long)(short)(long)", out_); return kLong;
|
|
case 6: fputs("(long)(int)(long)", out_); return kLong;
|
|
}
|
|
} else if (tp == kFloat) {
|
|
switch (random1(4)) {
|
|
case 1: fputs("(float)", out_); return kInt;
|
|
case 2: fputs("(float)", out_); return kLong;
|
|
case 3: fputs("(float)", out_); return kDouble;
|
|
// Narrowing-widening.
|
|
case 4: fputs("(float)(int)(float)", out_); return kFloat;
|
|
}
|
|
} else if (tp == kDouble) {
|
|
switch (random1(5)) {
|
|
case 1: fputs("(double)", out_); return kInt;
|
|
case 2: fputs("(double)", out_); return kLong;
|
|
case 3: fputs("(double)", out_); return kFloat;
|
|
// Narrowing-widening.
|
|
case 4: fputs("(double)(int)(double)", out_); return kDouble;
|
|
case 5: fputs("(double)(float)(double)", out_); return kDouble;
|
|
}
|
|
}
|
|
return tp; // nothing suitable, just keep type
|
|
}
|
|
|
|
// Emit a type conversion (out type given, new suitable in type picked).
|
|
void emitTypeConversion(Type tp) {
|
|
if (tp == kBoolean) {
|
|
Type tp = randomType();
|
|
emitExpression(tp);
|
|
fputc(' ', out_);
|
|
emitRelationalOp(tp);
|
|
fputc(' ', out_);
|
|
emitExpression(tp);
|
|
} else {
|
|
tp = emitTypeConversionOp(tp);
|
|
fputc(' ', out_);
|
|
emitExpression(tp);
|
|
}
|
|
}
|
|
|
|
// Emit an unary intrinsic (out type given, new suitable in type picked).
|
|
Type emitIntrinsic1(Type tp) {
|
|
if (tp == kBoolean) {
|
|
switch (random1(6)) {
|
|
case 1: fputs("Float.isNaN", out_); return kFloat;
|
|
case 2: fputs("Float.isFinite", out_); return kFloat;
|
|
case 3: fputs("Float.isInfinite", out_); return kFloat;
|
|
case 4: fputs("Double.isNaN", out_); return kDouble;
|
|
case 5: fputs("Double.isFinite", out_); return kDouble;
|
|
case 6: fputs("Double.isInfinite", out_); return kDouble;
|
|
}
|
|
} else if (isInteger(tp)) {
|
|
const char* prefix = tp == kLong ? "Long" : "Integer";
|
|
switch (random1(13)) {
|
|
case 1: fprintf(out_, "%s.highestOneBit", prefix); break;
|
|
case 2: fprintf(out_, "%s.lowestOneBit", prefix); break;
|
|
case 3: fprintf(out_, "%s.numberOfLeadingZeros", prefix); break;
|
|
case 4: fprintf(out_, "%s.numberOfTrailingZeros", prefix); break;
|
|
case 5: fprintf(out_, "%s.bitCount", prefix); break;
|
|
case 6: fprintf(out_, "%s.signum", prefix); break;
|
|
case 7: fprintf(out_, "%s.reverse", prefix); break;
|
|
case 8: fprintf(out_, "%s.reverseBytes", prefix); break;
|
|
case 9: fputs("Math.incrementExact", out_); break;
|
|
case 10: fputs("Math.decrementExact", out_); break;
|
|
case 11: fputs("Math.negateExact", out_); break;
|
|
case 12: fputs("Math.abs", out_); break;
|
|
case 13: fputs("Math.round", out_);
|
|
return tp == kLong ? kDouble : kFloat;
|
|
}
|
|
} else { // isFP(tp)
|
|
switch (random1(6)) {
|
|
case 1: fputs("Math.abs", out_); break;
|
|
case 2: fputs("Math.ulp", out_); break;
|
|
case 3: fputs("Math.signum", out_); break;
|
|
case 4: fputs("Math.nextUp", out_); break;
|
|
case 5: fputs("Math.nextDown", out_); break;
|
|
case 6: if (tp == kDouble) {
|
|
fputs("Double.longBitsToDouble", out_);
|
|
return kLong;
|
|
} else {
|
|
fputs("Float.intBitsToFloat", out_);
|
|
return kInt;
|
|
}
|
|
}
|
|
}
|
|
return tp; // same type in-out
|
|
}
|
|
|
|
// Emit a binary intrinsic (out type given, new suitable in type picked).
|
|
Type emitIntrinsic2(Type tp) {
|
|
if (tp == kBoolean) {
|
|
switch (random1(3)) {
|
|
case 1: fputs("Boolean.logicalAnd", out_); break;
|
|
case 2: fputs("Boolean.logicalOr", out_); break;
|
|
case 3: fputs("Boolean.logicalXor", out_); break;
|
|
}
|
|
} else if (isInteger(tp)) {
|
|
const char* prefix = tp == kLong ? "Long" : "Integer";
|
|
switch (random1(11)) {
|
|
case 1: fprintf(out_, "%s.compare", prefix); break;
|
|
case 2: fprintf(out_, "%s.sum", prefix); break;
|
|
case 3: fprintf(out_, "%s.min", prefix); break;
|
|
case 4: fprintf(out_, "%s.max", prefix); break;
|
|
case 5: fputs("Math.min", out_); break;
|
|
case 6: fputs("Math.max", out_); break;
|
|
case 7: fputs("Math.floorDiv", out_); break;
|
|
case 8: fputs("Math.floorMod", out_); break;
|
|
case 9: fputs("Math.addExact", out_); break;
|
|
case 10: fputs("Math.subtractExact", out_); break;
|
|
case 11: fputs("Math.multiplyExact", out_); break;
|
|
}
|
|
} else { // isFP(tp)
|
|
const char* prefix = tp == kDouble ? "Double" : "Float";
|
|
switch (random1(5)) {
|
|
case 1: fprintf(out_, "%s.sum", prefix); break;
|
|
case 2: fprintf(out_, "%s.min", prefix); break;
|
|
case 3: fprintf(out_, "%s.max", prefix); break;
|
|
case 4: fputs("Math.min", out_); break;
|
|
case 5: fputs("Math.max", out_); break;
|
|
}
|
|
}
|
|
return tp; // same type in-out
|
|
}
|
|
|
|
// Emit an intrinsic (out type given, new suitable in type picked).
|
|
void emitIntrinsic(Type tp) {
|
|
if (random1(2) == 1) {
|
|
tp = emitIntrinsic1(tp);
|
|
fputc('(', out_);
|
|
emitExpression(tp);
|
|
fputc(')', out_);
|
|
} else {
|
|
tp = emitIntrinsic2(tp);
|
|
fputc('(', out_);
|
|
emitExpression(tp);
|
|
fputs(", ", out_);
|
|
emitExpression(tp);
|
|
fputc(')', out_);
|
|
}
|
|
}
|
|
|
|
// Emit a method call (out type given).
|
|
void emitMethodCall(Type tp) {
|
|
if (tp != kBoolean && !in_inner_) {
|
|
// Accept all numerical types (implicit conversion) and when not
|
|
// declaring inner classes (to avoid infinite recursion).
|
|
switch (random1(8)) {
|
|
case 1: fputs("mA.a()", out_); break;
|
|
case 2: fputs("mB.a()", out_); break;
|
|
case 3: fputs("mB.x()", out_); break;
|
|
case 4: fputs("mBX.x()", out_); break;
|
|
case 5: fputs("mC.s()", out_); break;
|
|
case 6: fputs("mC.c()", out_); break;
|
|
case 7: fputs("mC.x()", out_); break;
|
|
case 8: fputs("mCX.x()", out_); break;
|
|
}
|
|
} else {
|
|
// Fall back to intrinsic.
|
|
emitIntrinsic(tp);
|
|
}
|
|
}
|
|
|
|
// Emit unboxing boxed object.
|
|
void emitUnbox(Type tp) {
|
|
fputc('(', out_);
|
|
emitType(tp);
|
|
fputs(") new ", out_);
|
|
emitTypeClass(tp);
|
|
fputc('(', out_);
|
|
emitExpression(tp);
|
|
fputc(')', out_);
|
|
}
|
|
|
|
// Emit miscellaneous constructs.
|
|
void emitMisc(Type tp) {
|
|
if (tp == kBoolean) {
|
|
fprintf(out_, "this instanceof %s", in_inner_ ? "X" : "Test");
|
|
} else if (isInteger(tp)) {
|
|
const char* prefix = tp == kLong ? "Long" : "Integer";
|
|
switch (random1(2)) {
|
|
case 1: fprintf(out_, "%s.MIN_VALUE", prefix); break;
|
|
case 2: fprintf(out_, "%s.MAX_VALUE", prefix); break;
|
|
}
|
|
} else { // isFP(tp)
|
|
const char* prefix = tp == kDouble ? "Double" : "Float";
|
|
switch (random1(6)) {
|
|
case 1: fprintf(out_, "%s.MIN_NORMAL", prefix); break;
|
|
case 2: fprintf(out_, "%s.MIN_VALUE", prefix); break;
|
|
case 3: fprintf(out_, "%s.MAX_VALUE", prefix); break;
|
|
case 4: fprintf(out_, "%s.POSITIVE_INFINITY", prefix); break;
|
|
case 5: fprintf(out_, "%s.NEGATIVE_INFINITY", prefix); break;
|
|
case 6: fprintf(out_, "%s.NaN", prefix); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adjust local of given type and return adjusted value.
|
|
uint32_t adjustLocal(Type tp, int32_t a) {
|
|
switch (tp) {
|
|
case kBoolean: boolean_local_ += a; return boolean_local_;
|
|
case kInt: int_local_ += a; return int_local_;
|
|
case kLong: long_local_ += a; return long_local_;
|
|
case kFloat: float_local_ += a; return float_local_;
|
|
default: double_local_ += a; return double_local_;
|
|
}
|
|
}
|
|
|
|
// Emit an expression that is a strict upper bound for an array index.
|
|
void emitUpperBound() {
|
|
if (random1(8) == 1) {
|
|
fputs("mArray.length", out_);
|
|
} else if (random1(8) == 1) {
|
|
fprintf(out_, "%u", random1(array_size_)); // random in range
|
|
} else {
|
|
fprintf(out_, "%u", array_size_);
|
|
}
|
|
}
|
|
|
|
// Emit an array index, usually within proper range.
|
|
void emitArrayIndex() {
|
|
if (loop_nest_ > 0 && random1(2) == 1) {
|
|
fprintf(out_, "i%u", random0(loop_nest_));
|
|
} else if (random1(8) == 1) {
|
|
fputs("mArray.length - 1", out_);
|
|
} else {
|
|
fprintf(out_, "%u", random0(array_size_)); // random in range
|
|
}
|
|
// Introduce potential off by one errors with low probability.
|
|
if (random1(100) == 1) {
|
|
if (random1(2) == 1) {
|
|
fputs(" - 1", out_);
|
|
} else {
|
|
fputs(" + 1", out_);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit a literal.
|
|
void emitLiteral(Type tp) {
|
|
switch (tp) {
|
|
case kBoolean: fputs(random1(2) == 1 ? "true" : "false", out_); break;
|
|
case kInt: fprintf(out_, "%d", random()); break;
|
|
case kLong: fprintf(out_, "%dL", random()); break;
|
|
case kFloat: fprintf(out_, "%d.0f", random()); break;
|
|
case kDouble: fprintf(out_, "%d.0", random()); break;
|
|
}
|
|
}
|
|
|
|
// Emit array variable, if available.
|
|
bool emitArrayVariable(Type tp) {
|
|
if (tp == array_type_) {
|
|
fputs("mArray", out_);
|
|
for (uint32_t i = 0; i < array_dim_; i++) {
|
|
fputc('[', out_);
|
|
emitArrayIndex();
|
|
fputc(']', out_);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Emit a local variable, if available.
|
|
bool emitLocalVariable(Type tp) {
|
|
uint32_t locals = adjustLocal(tp, 0);
|
|
if (locals > 0) {
|
|
uint32_t local = random0(locals);
|
|
switch (tp) {
|
|
case kBoolean: fprintf(out_, "lZ%u", local); break;
|
|
case kInt: fprintf(out_, "lI%u", local); break;
|
|
case kLong: fprintf(out_, "lJ%u", local); break;
|
|
case kFloat: fprintf(out_, "lF%u", local); break;
|
|
case kDouble: fprintf(out_, "lD%u", local); break;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Emit a field variable.
|
|
void emitFieldVariable(Type tp) {
|
|
switch (tp) {
|
|
case kBoolean:fputs("mZ", out_); break;
|
|
case kInt: fputs("mI", out_); break;
|
|
case kLong: fputs("mJ", out_); break;
|
|
case kFloat: fputs("mF", out_); break;
|
|
case kDouble: fputs("mD", out_); break;
|
|
}
|
|
}
|
|
|
|
// Emit a variable.
|
|
void emitVariable(Type tp) {
|
|
switch (random1(4)) {
|
|
case 1:
|
|
if (emitArrayVariable(tp))
|
|
return;
|
|
[[fallthrough]];
|
|
case 2:
|
|
if (emitLocalVariable(tp))
|
|
return;
|
|
[[fallthrough]];
|
|
default:
|
|
emitFieldVariable(tp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Emit an expression.
|
|
void emitExpression(Type tp) {
|
|
// Continuing expression becomes less likely as the depth grows.
|
|
if (random1(expr_depth_ + 1) > fuzz_expr_depth_) {
|
|
if (random1(2) == 1) {
|
|
emitLiteral(tp);
|
|
} else {
|
|
emitVariable(tp);
|
|
}
|
|
return;
|
|
}
|
|
|
|
expr_depth_++;
|
|
|
|
fputc('(', out_);
|
|
switch (random1(12)) { // favor binary operations
|
|
case 1:
|
|
// Unary operator: ~ x
|
|
emitUnaryOp(tp);
|
|
fputc(' ', out_);
|
|
emitExpression(tp);
|
|
break;
|
|
case 2:
|
|
// Pre-increment: ++x
|
|
emitIncDecOp(tp);
|
|
emitVariable(tp);
|
|
break;
|
|
case 3:
|
|
// Post-increment: x++
|
|
emitVariable(tp);
|
|
emitIncDecOp(tp);
|
|
break;
|
|
case 4:
|
|
// Ternary operator: b ? x : y
|
|
emitExpression(kBoolean);
|
|
fputs(" ? ", out_);
|
|
emitExpression(tp);
|
|
fputs(" : ", out_);
|
|
emitExpression(tp);
|
|
break;
|
|
case 5:
|
|
// Type conversion: (float) x
|
|
emitTypeConversion(tp);
|
|
break;
|
|
case 6:
|
|
// Intrinsic: foo(x)
|
|
emitIntrinsic(tp);
|
|
break;
|
|
case 7:
|
|
// Method call: mA.a()
|
|
emitMethodCall(tp);
|
|
break;
|
|
case 8:
|
|
// Emit unboxing boxed value: (int) Integer(x)
|
|
emitUnbox(tp);
|
|
break;
|
|
case 9:
|
|
// Miscellaneous constructs: a.length
|
|
emitMisc(tp);
|
|
break;
|
|
default:
|
|
// Binary operator: x + y
|
|
emitExpression(tp);
|
|
fputc(' ', out_);
|
|
emitBinaryOp(tp);
|
|
fputc(' ', out_);
|
|
emitExpression(tp);
|
|
break;
|
|
}
|
|
fputc(')', out_);
|
|
|
|
--expr_depth_;
|
|
}
|
|
|
|
//
|
|
// Statements.
|
|
//
|
|
|
|
// Emit current indentation.
|
|
void emitIndentation() const {
|
|
for (uint32_t i = 0; i < indentation_; i++) {
|
|
fputc(' ', out_);
|
|
}
|
|
}
|
|
|
|
// Emit a return statement.
|
|
bool emitReturn(bool mustEmit) {
|
|
// Only emit when we must, or with low probability inside ifs/loops,
|
|
// but outside do-while to avoid confusing the may follow status.
|
|
if (mustEmit || ((if_nest_ + loop_nest_) > 0 && do_nest_ == 0 && random1(10) == 1)) {
|
|
fputs("return ", out_);
|
|
emitExpression(return_type_);
|
|
fputs(";\n", out_);
|
|
return false;
|
|
}
|
|
// Fall back to assignment.
|
|
return emitAssignment();
|
|
}
|
|
|
|
// Emit a continue statement.
|
|
bool emitContinue() {
|
|
// Only emit with low probability inside loops.
|
|
if (loop_nest_ > 0 && random1(10) == 1) {
|
|
fputs("continue;\n", out_);
|
|
return false;
|
|
}
|
|
// Fall back to assignment.
|
|
return emitAssignment();
|
|
}
|
|
|
|
// Emit a break statement.
|
|
bool emitBreak() {
|
|
// Only emit with low probability inside loops, but outside switches
|
|
// to avoid confusing the may follow status.
|
|
if (loop_nest_ > 0 && switch_nest_ == 0 && random1(10) == 1) {
|
|
fputs("break;\n", out_);
|
|
return false;
|
|
}
|
|
// Fall back to assignment.
|
|
return emitAssignment();
|
|
}
|
|
|
|
// Emit a new scope with a local variable declaration statement.
|
|
bool emitScope() {
|
|
Type tp = randomType();
|
|
fputs("{\n", out_);
|
|
indentation_ += 2;
|
|
emitIndentation();
|
|
emitType(tp);
|
|
switch (tp) {
|
|
case kBoolean: fprintf(out_, " lZ%u = ", boolean_local_); break;
|
|
case kInt: fprintf(out_, " lI%u = ", int_local_); break;
|
|
case kLong: fprintf(out_, " lJ%u = ", long_local_); break;
|
|
case kFloat: fprintf(out_, " lF%u = ", float_local_); break;
|
|
case kDouble: fprintf(out_, " lD%u = ", double_local_); break;
|
|
}
|
|
emitExpression(tp);
|
|
fputs(";\n", out_);
|
|
|
|
adjustLocal(tp, 1); // local now visible
|
|
|
|
bool mayFollow = emitStatementList();
|
|
|
|
adjustLocal(tp, -1); // local no longer visible
|
|
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputs("}\n", out_);
|
|
return mayFollow;
|
|
}
|
|
|
|
// Emit one dimension of an array initializer, where parameter dim >= 1
|
|
// denotes the number of remaining dimensions that should be emitted.
|
|
void emitArrayInitDim(int dim) {
|
|
if (dim == 1) {
|
|
// Last dimension: set of values.
|
|
fputs("{ ", out_);
|
|
for (uint32_t i = 0; i < array_size_; i++) {
|
|
emitExpression(array_type_);
|
|
fputs(", ", out_);
|
|
}
|
|
fputs("}", out_);
|
|
|
|
} else {
|
|
// Outer dimensions: set of sets.
|
|
fputs("{\n", out_);
|
|
indentation_ += 2;
|
|
emitIndentation();
|
|
|
|
for (uint32_t i = 0; i < array_size_; i++) {
|
|
emitArrayInitDim(dim - 1);
|
|
if (i != array_size_ - 1) {
|
|
fputs(",\n", out_);
|
|
emitIndentation();
|
|
}
|
|
}
|
|
|
|
fputs(",\n", out_);
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputs("}", out_);
|
|
}
|
|
}
|
|
|
|
// Emit an array initializer of the following form.
|
|
// {
|
|
// type[]..[] tmp = { .. };
|
|
// mArray = tmp;
|
|
// }
|
|
bool emitArrayInit() {
|
|
// Avoid elaborate array initializers.
|
|
uint64_t p = pow(array_size_, array_dim_);
|
|
if (p > 20) {
|
|
return emitAssignment(); // fall back
|
|
}
|
|
|
|
fputs("{\n", out_);
|
|
|
|
indentation_ += 2;
|
|
emitIndentation();
|
|
emitType(array_type_);
|
|
for (uint32_t i = 0; i < array_dim_; i++) {
|
|
fputs("[]", out_);
|
|
}
|
|
fputs(" tmp = ", out_);
|
|
emitArrayInitDim(array_dim_);
|
|
fputs(";\n", out_);
|
|
|
|
emitIndentation();
|
|
fputs("mArray = tmp;\n", out_);
|
|
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputs("}\n", out_);
|
|
return true;
|
|
}
|
|
|
|
// Emit a for loop.
|
|
bool emitForLoop() {
|
|
// Continuing loop nest becomes less likely as the depth grows.
|
|
if (random1(loop_nest_ + 1) > fuzz_loop_nest_) {
|
|
return emitAssignment(); // fall back
|
|
}
|
|
|
|
bool goesUp = random1(2) == 1;
|
|
fprintf(out_, "for (int i%u = ", loop_nest_);
|
|
if (goesUp) {
|
|
fprintf(out_, "0; i%u < ", loop_nest_);
|
|
emitUpperBound();
|
|
fprintf(out_, "; i%u++) {\n", loop_nest_);
|
|
} else {
|
|
emitUpperBound();
|
|
fprintf(out_, " - 1; i%d >= 0", loop_nest_);
|
|
fprintf(out_, "; i%d--) {\n", loop_nest_);
|
|
}
|
|
|
|
++loop_nest_; // now in loop
|
|
|
|
indentation_ += 2;
|
|
emitStatementList();
|
|
|
|
--loop_nest_; // no longer in loop
|
|
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fprintf(out_, "}\n");
|
|
return true; // loop-body does not block flow
|
|
}
|
|
|
|
// Emit while or do-while loop.
|
|
bool emitDoLoop() {
|
|
// Continuing loop nest becomes less likely as the depth grows.
|
|
if (random1(loop_nest_ + 1) > fuzz_loop_nest_) {
|
|
return emitAssignment(); // fall back
|
|
}
|
|
|
|
bool isWhile = random1(2) == 1;
|
|
fputs("{\n", out_);
|
|
indentation_ += 2;
|
|
emitIndentation();
|
|
fprintf(out_, "int i%u = %d;\n", loop_nest_, isWhile ? -1 : 0);
|
|
emitIndentation();
|
|
if (isWhile) {
|
|
fprintf(out_, "while (++i%u < ", loop_nest_);
|
|
emitUpperBound();
|
|
fputs(") {\n", out_);
|
|
} else {
|
|
fputs("do {\n", out_);
|
|
do_nest_++;
|
|
}
|
|
|
|
++loop_nest_; // now in loop
|
|
|
|
indentation_ += 2;
|
|
emitStatementList();
|
|
|
|
--loop_nest_; // no longer in loop
|
|
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
if (isWhile) {
|
|
fputs("}\n", out_);
|
|
} else {
|
|
fprintf(out_, "} while (++i%u < ", loop_nest_);
|
|
emitUpperBound();
|
|
fputs(");\n", out_);
|
|
--do_nest_;
|
|
}
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputs("}\n", out_);
|
|
return true; // loop-body does not block flow
|
|
}
|
|
|
|
// Emit an if statement.
|
|
bool emitIfStmt() {
|
|
// Continuing if nest becomes less likely as the depth grows.
|
|
if (random1(if_nest_ + 1) > fuzz_if_nest_) {
|
|
return emitAssignment(); // fall back
|
|
}
|
|
|
|
fputs("if (", out_);
|
|
emitExpression(kBoolean);
|
|
fputs(") {\n", out_);
|
|
|
|
++if_nest_; // now in if
|
|
|
|
indentation_ += 2;
|
|
bool mayFollowTrue = emitStatementList();
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fprintf(out_, "} else {\n");
|
|
indentation_ += 2;
|
|
bool mayFollowFalse = emitStatementList();
|
|
|
|
--if_nest_; // no longer in if
|
|
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fprintf(out_, "}\n");
|
|
return mayFollowTrue || mayFollowFalse;
|
|
}
|
|
|
|
bool emitTry() {
|
|
fputs("try {\n", out_);
|
|
indentation_ += 2;
|
|
bool mayFollow = emitStatementList();
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputc('}', out_);
|
|
return mayFollow;
|
|
}
|
|
|
|
bool emitCatch() {
|
|
uint32_t count = random1(countof(kExceptionTypes));
|
|
bool mayFollow = false;
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
fprintf(out_, " catch (%s ex%u_%u) {\n", kExceptionTypes[i], try_nest_, i);
|
|
indentation_ += 2;
|
|
mayFollow |= emitStatementList();
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputc('}', out_);
|
|
}
|
|
return mayFollow;
|
|
}
|
|
|
|
bool emitFinally() {
|
|
fputs(" finally {\n", out_);
|
|
indentation_ += 2;
|
|
bool mayFollow = emitStatementList();
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputc('}', out_);
|
|
return mayFollow;
|
|
}
|
|
|
|
// Emit a try-catch-finally block.
|
|
bool emitTryCatchFinally() {
|
|
// Apply a hard limit on the number of catch blocks. This is for
|
|
// javac which fails if blocks within try-catch-finally are too
|
|
// large (much less than you'd expect).
|
|
if (try_nest_ > fuzz_try_nest_) {
|
|
return emitAssignment(); // fall back
|
|
}
|
|
|
|
++try_nest_; // Entering try-catch-finally
|
|
|
|
bool mayFollow = emitTry();
|
|
switch (random0(3)) {
|
|
case 0: // try..catch
|
|
mayFollow |= emitCatch();
|
|
break;
|
|
case 1: // try..finally
|
|
mayFollow &= emitFinally();
|
|
break;
|
|
case 2: // try..catch..finally
|
|
// When determining whether code may follow, we observe that a
|
|
// finally block always follows after try and catch
|
|
// block. Code may only follow if the finally block permits
|
|
// and either the try or catch block allows code to follow.
|
|
mayFollow = (mayFollow | emitCatch()) & emitFinally();
|
|
break;
|
|
}
|
|
fputc('\n', out_);
|
|
|
|
--try_nest_; // Leaving try-catch-finally
|
|
return mayFollow;
|
|
}
|
|
|
|
// Emit a switch statement.
|
|
bool emitSwitch() {
|
|
// Continuing if nest becomes less likely as the depth grows.
|
|
if (random1(if_nest_ + 1) > fuzz_if_nest_) {
|
|
return emitAssignment(); // fall back
|
|
}
|
|
|
|
bool mayFollow = false;
|
|
fputs("switch (", out_);
|
|
emitArrayIndex(); // restrict its range
|
|
fputs(") {\n", out_);
|
|
|
|
++if_nest_;
|
|
++switch_nest_; // now in switch
|
|
|
|
indentation_ += 2;
|
|
for (uint32_t i = 0; i < 2; i++) {
|
|
emitIndentation();
|
|
if (i == 0) {
|
|
fprintf(out_, "case %u: {\n", random0(array_size_));
|
|
} else {
|
|
fprintf(out_, "default: {\n");
|
|
}
|
|
indentation_ += 2;
|
|
if (emitStatementList()) {
|
|
// Must end with break.
|
|
emitIndentation();
|
|
fputs("break;\n", out_);
|
|
mayFollow = true;
|
|
}
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputs("}\n", out_);
|
|
}
|
|
|
|
--if_nest_;
|
|
--switch_nest_; // no longer in switch
|
|
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fprintf(out_, "}\n");
|
|
return mayFollow;
|
|
}
|
|
|
|
bool emitNopCall() {
|
|
fputs("nop();\n", out_);
|
|
return true;
|
|
}
|
|
|
|
// Emit an assignment statement.
|
|
bool emitAssignment() {
|
|
Type tp = randomType();
|
|
emitVariable(tp);
|
|
fputc(' ', out_);
|
|
emitAssignmentOp(tp);
|
|
fputc(' ', out_);
|
|
emitExpression(tp);
|
|
fputs(";\n", out_);
|
|
return true;
|
|
}
|
|
|
|
// Emit a single statement. Returns true if statements may follow.
|
|
bool emitStatement() {
|
|
switch (random1(16)) { // favor assignments
|
|
case 1: return emitReturn(false); break;
|
|
case 2: return emitContinue(); break;
|
|
case 3: return emitBreak(); break;
|
|
case 4: return emitScope(); break;
|
|
case 5: return emitArrayInit(); break;
|
|
case 6: return emitForLoop(); break;
|
|
case 7: return emitDoLoop(); break;
|
|
case 8: return emitIfStmt(); break;
|
|
case 9: return emitSwitch(); break;
|
|
case 10: return emitTryCatchFinally(); break;
|
|
case 11: return emitNopCall(); break;
|
|
default: return emitAssignment(); break;
|
|
}
|
|
}
|
|
|
|
// Emit a statement list. Returns true if statements may follow.
|
|
bool emitStatementList() {
|
|
while (stmt_length_ < 1000) { // avoid run-away
|
|
stmt_length_++;
|
|
emitIndentation();
|
|
if (!emitStatement()) {
|
|
return false; // rest would be dead code
|
|
}
|
|
// Continuing this list becomes less likely as the total statement list grows.
|
|
if (random1(stmt_length_) > fuzz_stmt_length_) {
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Emit interface and class declarations.
|
|
void emitClassDecls() {
|
|
in_inner_ = true;
|
|
fputs(" private interface X {\n", out_);
|
|
fputs(" int x();\n", out_);
|
|
fputs(" }\n\n", out_);
|
|
fputs(" private class A {\n", out_);
|
|
fputs(" public int a() {\n", out_);
|
|
fputs(" return ", out_);
|
|
emitExpression(kInt);
|
|
fputs(";\n }\n", out_);
|
|
fputs(" }\n\n", out_);
|
|
fputs(" private class B extends A implements X {\n", out_);
|
|
fputs(" public int a() {\n", out_);
|
|
fputs(" return super.a() + ", out_);
|
|
emitExpression(kInt);
|
|
fputs(";\n }\n", out_);
|
|
fputs(" public int x() {\n", out_);
|
|
fputs(" return ", out_);
|
|
emitExpression(kInt);
|
|
fputs(";\n }\n", out_);
|
|
fputs(" }\n\n", out_);
|
|
fputs(" private static class C implements X {\n", out_);
|
|
fputs(" public static int s() {\n", out_);
|
|
fputs(" return ", out_);
|
|
emitLiteral(kInt);
|
|
fputs(";\n }\n", out_);
|
|
fputs(" public int c() {\n", out_);
|
|
fputs(" return ", out_);
|
|
emitLiteral(kInt);
|
|
fputs(";\n }\n", out_);
|
|
fputs(" public int x() {\n", out_);
|
|
fputs(" return ", out_);
|
|
emitLiteral(kInt);
|
|
fputs(";\n }\n", out_);
|
|
fputs(" }\n\n", out_);
|
|
in_inner_ = false;
|
|
}
|
|
|
|
// Emit field declarations.
|
|
void emitFieldDecls() {
|
|
fputs(" private A mA = new B();\n", out_);
|
|
fputs(" private B mB = new B();\n", out_);
|
|
fputs(" private X mBX = new B();\n", out_);
|
|
fputs(" private C mC = new C();\n", out_);
|
|
fputs(" private X mCX = new C();\n\n", out_);
|
|
fputs(" private boolean mZ = false;\n", out_);
|
|
fputs(" private int mI = 0;\n", out_);
|
|
fputs(" private long mJ = 0;\n", out_);
|
|
fputs(" private float mF = 0;\n", out_);
|
|
fputs(" private double mD = 0;\n\n", out_);
|
|
}
|
|
|
|
// Emit array declaration.
|
|
void emitArrayDecl() {
|
|
fputs(" private ", out_);
|
|
emitType(array_type_);
|
|
for (uint32_t i = 0; i < array_dim_; i++) {
|
|
fputs("[]", out_);
|
|
}
|
|
fputs(" mArray = new ", out_);
|
|
emitType(array_type_);
|
|
for (uint32_t i = 0; i < array_dim_; i++) {
|
|
fprintf(out_, "[%d]", array_size_);
|
|
}
|
|
fputs(";\n\n", out_);
|
|
}
|
|
|
|
// Emit test constructor.
|
|
void emitTestConstructor() {
|
|
fputs(" private Test() {\n", out_);
|
|
indentation_ += 2;
|
|
emitIndentation();
|
|
emitType(array_type_);
|
|
fputs(" a = ", out_);
|
|
emitLiteral(array_type_);
|
|
fputs(";\n", out_);
|
|
for (uint32_t i = 0; i < array_dim_; i++) {
|
|
emitIndentation();
|
|
fprintf(out_, "for (int i%u = 0; i%u < %u; i%u++) {\n", i, i, array_size_, i);
|
|
indentation_ += 2;
|
|
}
|
|
emitIndentation();
|
|
fputs("mArray", out_);
|
|
for (uint32_t i = 0; i < array_dim_; i++) {
|
|
fprintf(out_, "[i%u]", i);
|
|
}
|
|
fputs(" = a;\n", out_);
|
|
emitIndentation();
|
|
if (array_type_ == kBoolean) {
|
|
fputs("a = !a;\n", out_);
|
|
} else {
|
|
fputs("a++;\n", out_);
|
|
}
|
|
for (uint32_t i = 0; i < array_dim_; i++) {
|
|
indentation_ -= 2;
|
|
emitIndentation();
|
|
fputs("}\n", out_);
|
|
}
|
|
indentation_ -= 2;
|
|
fputs(" }\n\n", out_);
|
|
}
|
|
|
|
// Emit test method.
|
|
void emitTestMethod() {
|
|
fputs(" private ", out_);
|
|
emitType(return_type_);
|
|
fputs(" testMethod() {\n", out_);
|
|
indentation_ += 2;
|
|
if (emitStatementList()) {
|
|
// Must end with return.
|
|
emitIndentation();
|
|
emitReturn(true);
|
|
}
|
|
indentation_ -= 2;
|
|
fputs(" }\n\n", out_);
|
|
}
|
|
|
|
// Emit main method driver.
|
|
void emitMainMethod() {
|
|
fputs(" public static void main(String[] args) {\n", out_);
|
|
indentation_ += 2;
|
|
fputs(" Test t = new Test();\n ", out_);
|
|
emitType(return_type_);
|
|
fputs(" r = ", out_);
|
|
emitLiteral(return_type_);
|
|
fputs(";\n", out_);
|
|
fputs(" try {\n", out_);
|
|
fputs(" r = t.testMethod();\n", out_);
|
|
fputs(" } catch (Exception e) {\n", out_);
|
|
fputs(" // Arithmetic, null pointer, index out of bounds, etc.\n", out_);
|
|
fputs(" System.out.println(\"An exception was caught.\");\n", out_);
|
|
fputs(" }\n", out_);
|
|
fputs(" System.out.println(\"r = \" + r);\n", out_);
|
|
fputs(" System.out.println(\"mZ = \" + t.mZ);\n", out_);
|
|
fputs(" System.out.println(\"mI = \" + t.mI);\n", out_);
|
|
fputs(" System.out.println(\"mJ = \" + t.mJ);\n", out_);
|
|
fputs(" System.out.println(\"mF = \" + t.mF);\n", out_);
|
|
fputs(" System.out.println(\"mD = \" + t.mD);\n", out_);
|
|
fputs(" System.out.println(\"mArray = \" + ", out_);
|
|
if (array_dim_ == 1) {
|
|
fputs("Arrays.toString(t.mArray)", out_);
|
|
} else {
|
|
fputs("Arrays.deepToString(t.mArray)", out_);
|
|
}
|
|
fputs(");\n", out_);
|
|
indentation_ -= 2;
|
|
fputs(" }\n", out_);
|
|
}
|
|
|
|
// Emit a static void method.
|
|
void emitStaticNopMethod() {
|
|
fputs(" public static void nop() {}\n\n", out_);
|
|
}
|
|
|
|
// Emit program header. Emit command line options in the comments.
|
|
void emitHeader() {
|
|
fputs("\n/**\n * AOSP JFuzz Tester.\n", out_);
|
|
fputs(" * Automatically generated program.\n", out_);
|
|
fprintf(out_,
|
|
" * jfuzz -s %u -d %u -l %u -i %u -n %u (version %s)\n */\n\n",
|
|
fuzz_seed_,
|
|
fuzz_expr_depth_,
|
|
fuzz_stmt_length_,
|
|
fuzz_if_nest_,
|
|
fuzz_loop_nest_,
|
|
VERSION);
|
|
fputs("import java.util.Arrays;\n\n", out_);
|
|
}
|
|
|
|
// Emit single test class with main driver.
|
|
void emitTestClassWithMain() {
|
|
fputs("public class Test {\n\n", out_);
|
|
indentation_ += 2;
|
|
emitClassDecls();
|
|
emitFieldDecls();
|
|
emitArrayDecl();
|
|
emitTestConstructor();
|
|
emitTestMethod();
|
|
emitStaticNopMethod();
|
|
emitMainMethod();
|
|
indentation_ -= 2;
|
|
fputs("}\n\n", out_);
|
|
}
|
|
|
|
//
|
|
// Random integers.
|
|
//
|
|
|
|
// Return random integer.
|
|
int32_t random() {
|
|
return fuzz_random_engine_();
|
|
}
|
|
|
|
// Return random integer in range [0,max).
|
|
uint32_t random0(uint32_t max) {
|
|
std::uniform_int_distribution<uint32_t> gen(0, max - 1);
|
|
return gen(fuzz_random_engine_);
|
|
}
|
|
|
|
// Return random integer in range [1,max].
|
|
uint32_t random1(uint32_t max) {
|
|
std::uniform_int_distribution<uint32_t> gen(1, max);
|
|
return gen(fuzz_random_engine_);
|
|
}
|
|
|
|
// Fuzzing parameters.
|
|
FILE* out_;
|
|
std::mt19937 fuzz_random_engine_;
|
|
const uint32_t fuzz_seed_;
|
|
const uint32_t fuzz_expr_depth_;
|
|
const uint32_t fuzz_stmt_length_;
|
|
const uint32_t fuzz_if_nest_;
|
|
const uint32_t fuzz_loop_nest_;
|
|
const uint32_t fuzz_try_nest_;
|
|
|
|
// Return and array setup.
|
|
const Type return_type_;
|
|
const Type array_type_;
|
|
const uint32_t array_dim_;
|
|
const uint32_t array_size_;
|
|
|
|
// Current context.
|
|
uint32_t indentation_;
|
|
uint32_t expr_depth_;
|
|
uint32_t stmt_length_;
|
|
uint32_t if_nest_;
|
|
uint32_t loop_nest_;
|
|
uint32_t switch_nest_;
|
|
uint32_t do_nest_;
|
|
uint32_t try_nest_;
|
|
uint32_t boolean_local_;
|
|
uint32_t int_local_;
|
|
uint32_t long_local_;
|
|
uint32_t float_local_;
|
|
uint32_t double_local_;
|
|
bool in_inner_;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
int32_t main(int32_t argc, char** argv) {
|
|
// Time-based seed.
|
|
struct timeval tp;
|
|
gettimeofday(&tp, nullptr);
|
|
|
|
// Defaults.
|
|
uint32_t seed = (tp.tv_sec * 1000000 + tp.tv_usec);
|
|
uint32_t expr_depth = 1;
|
|
uint32_t stmt_length = 8;
|
|
uint32_t if_nest = 2;
|
|
uint32_t loop_nest = 3;
|
|
uint32_t try_nest = 2;
|
|
|
|
// Parse options.
|
|
while (1) {
|
|
int32_t option = getopt(argc, argv, "s:d:l:i:n:vh");
|
|
if (option < 0) {
|
|
break; // done
|
|
}
|
|
switch (option) {
|
|
case 's':
|
|
seed = strtoul(optarg, nullptr, 0); // deterministic seed
|
|
break;
|
|
case 'd':
|
|
expr_depth = strtoul(optarg, nullptr, 0);
|
|
break;
|
|
case 'l':
|
|
stmt_length = strtoul(optarg, nullptr, 0);
|
|
break;
|
|
case 'i':
|
|
if_nest = strtoul(optarg, nullptr, 0);
|
|
break;
|
|
case 'n':
|
|
loop_nest = strtoul(optarg, nullptr, 0);
|
|
break;
|
|
case 't':
|
|
try_nest = strtoul(optarg, nullptr, 0);
|
|
break;
|
|
case 'v':
|
|
fprintf(stderr, "jfuzz version %s\n", VERSION);
|
|
return 0;
|
|
case 'h':
|
|
default:
|
|
fprintf(stderr,
|
|
"usage: %s [-s seed] "
|
|
"[-d expr-depth] [-l stmt-length] "
|
|
"[-i if-nest] [-n loop-nest] [-t try-nest] [-v] [-h]\n",
|
|
argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Seed global random generator.
|
|
srand(seed);
|
|
|
|
// Generate fuzzed program.
|
|
JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest, try_nest);
|
|
fuzz.emitProgram();
|
|
return 0;
|
|
}
|