|  | // Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import 'dart:io'; | 
|  | import 'dart:math'; | 
|  |  | 
|  | import 'package:args/args.dart'; | 
|  |  | 
|  | import 'dartfuzz_api_table.dart'; | 
|  | import 'dartfuzz_ffi_api.dart'; | 
|  | import 'dartfuzz_type_table.dart'; | 
|  |  | 
|  | // Version of DartFuzz. Increase this each time changes are made | 
|  | // to preserve the property that a given version of DartFuzz yields | 
|  | // the same fuzzed program for a deterministic random seed. | 
|  | const String version = '1.88'; | 
|  |  | 
|  | // Restriction on statements and expressions. | 
|  | const int stmtDepth = 1; | 
|  | const int exprDepth = 2; | 
|  | const int nestDepth = 1; | 
|  | const int numStatements = 2; | 
|  | const int numGlobalVars = 4; | 
|  | const int numLocalVars = 4; | 
|  | const int numGlobalMethods = 4; | 
|  | const int numMethodParams = 4; | 
|  | const int numClasses = 4; | 
|  | const int numExtensionMethodsPerClass = 3; | 
|  | const int numDartTypeExtensions = 5; | 
|  |  | 
|  | // Naming conventions. | 
|  | const varName = 'var'; | 
|  | const paramName = 'par'; | 
|  | const localName = 'loc'; | 
|  | const fieldName = 'fld'; | 
|  | const methodName = 'foo'; | 
|  |  | 
|  | /// Enum for the different types of methods generated by the fuzzer. | 
|  | enum MethodType { | 
|  | globalMethod, | 
|  | ffiMethod, | 
|  | instanceMethod, | 
|  | callMethod, | 
|  | extensionMethod | 
|  | } | 
|  |  | 
|  | /// Base class for all methods in the program generated by DartFuzz. | 
|  | abstract class Method { | 
|  | Method(this.name, this.parameters, this.fuzzer) | 
|  | : recursionAllowed = fuzzer.coinFlip() { | 
|  | if (recursionAllowed) { | 
|  | // Add the recursion depth parameter. | 
|  | parameters.add(DartType.INT); | 
|  | } | 
|  | } | 
|  |  | 
|  | String get recursionDepthParamName => '${paramName}${parameters.length - 1}'; | 
|  | DartType get returnType => parameters[0]; | 
|  |  | 
|  | void emitCallParameters(int depth, bool isRecursiveCall, | 
|  | {RhsFilter rhsFilter}) { | 
|  | if (isRecursiveCall && !recursionAllowed) { | 
|  | throw StateError('Did not expect a recursive call in $name'); | 
|  | } | 
|  | fuzzer.emitParenWrapped(() => fuzzer.emitCommaSeparated((int i) { | 
|  | // If this function can be called recursively, the last parameter is the | 
|  | // recursion depth parameter. | 
|  | if (i == parameters.length - 1 && recursionAllowed) { | 
|  | if (isRecursiveCall) { | 
|  | // This is a recursive call, so we increase the recursion depth | 
|  | // parameter. | 
|  | fuzzer.emit('$recursionDepthParamName + 1'); | 
|  | } else { | 
|  | // This isn't a recursive call. Our recursion depth is 0. | 
|  | fuzzer.emit('0'); | 
|  | } | 
|  | } else { | 
|  | fuzzer.emitExpr(depth, parameters[i], rhsFilter: rhsFilter); | 
|  | } | 
|  | }, parameters.length, start: 1)); | 
|  | } | 
|  |  | 
|  | void disableRecursionScope(Function callback) { | 
|  | final originalState = recursionAllowed; | 
|  | recursionAllowed = false; | 
|  | callback(); | 
|  | recursionAllowed = originalState; | 
|  | } | 
|  |  | 
|  | void emitRecursionBaseCase() { | 
|  | fuzzer.emitIfStatement(() { | 
|  | fuzzer.emit("$recursionDepthParamName >= "); | 
|  | fuzzer.emitSmallPositiveInt(); | 
|  | }, () { | 
|  | // Temporarily set recursionAllowed to false so that we don't have a | 
|  | // recursive call in the return statement of the base case. | 
|  | disableRecursionScope(fuzzer.emitReturn); | 
|  | return false; | 
|  | }); | 
|  | } | 
|  |  | 
|  | void emitFunctionBody() { | 
|  | if (!recursionAllowed && fuzzer.rollDice(10)) { | 
|  | // Emit a method using "=>" syntax. | 
|  | fuzzer.emit(' => '); | 
|  | fuzzer.emitExpr(0, returnType, includeSemicolon: true); | 
|  | } else { | 
|  | fuzzer.emitBraceWrapped(() { | 
|  | assert(fuzzer.localVars.isEmpty); | 
|  | if (recursionAllowed) { | 
|  | emitRecursionBaseCase(); | 
|  | } | 
|  | if (fuzzer.emitStatements(0)) { | 
|  | fuzzer.emitReturn(); | 
|  | } | 
|  | assert(fuzzer.localVars.isEmpty); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitFunctionDefinition() { | 
|  | final type = returnType.name; | 
|  | fuzzer.emitLn('$type ${name}', newline: false); | 
|  | fuzzer.emitParenWrapped(() => fuzzer.emitParDecls(parameters)); | 
|  | emitFunctionBody(); | 
|  | fuzzer.emitNewline(); | 
|  | fuzzer.emitNewline(); | 
|  | } | 
|  |  | 
|  | String emitCall(int depth, | 
|  | {RhsFilter rhsFilter, | 
|  | bool includeSemicolon = false, | 
|  | bool isRecursiveCall = false}) { | 
|  | String outputName = name; | 
|  | fuzzer.emitLn(outputName, newline: false); | 
|  | emitCallParameters(depth + 1, isRecursiveCall, rhsFilter: rhsFilter); | 
|  | if (includeSemicolon) { | 
|  | fuzzer.emit(';'); | 
|  | } | 
|  | return outputName; | 
|  | } | 
|  |  | 
|  | // Main emitter for a method. | 
|  | void emit() { | 
|  | emitFunctionDefinition(); | 
|  | } | 
|  |  | 
|  | final String name; | 
|  | final List<DartType> parameters; | 
|  | final DartFuzz fuzzer; | 
|  | bool recursionAllowed; | 
|  | } | 
|  |  | 
|  | /// Class for global methods generated by DartFuzz. | 
|  | class GlobalMethod extends Method { | 
|  | GlobalMethod(String name, List<DartType> parameters, DartFuzz fuzzer) | 
|  | : super(name, parameters, fuzzer); | 
|  | } | 
|  |  | 
|  | /// Class for ffi methods generated by DartFuzz. | 
|  | class FfiMethod extends Method { | 
|  | FfiMethod( | 
|  | String namePrefix, int index, List<DartType> parameters, DartFuzz fuzzer) | 
|  | : ffiCastName = "${namePrefix}FfiCast${index}", | 
|  | super("${namePrefix}Ffi${index}", parameters, fuzzer) {} | 
|  |  | 
|  | @override | 
|  | void emitFunctionBody() { | 
|  | fuzzer.emitBraceWrapped(() { | 
|  | assert(fuzzer.localVars.isEmpty); | 
|  | if (recursionAllowed) { | 
|  | emitRecursionBaseCase(); | 
|  | } | 
|  | if (fuzzer.emitStatements(0)) { | 
|  | fuzzer.emitReturn(); | 
|  | } | 
|  | assert(fuzzer.localVars.isEmpty); | 
|  | }); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void emit() { | 
|  | fuzzer.emitFfiTypedef(this); | 
|  | emitFunctionDefinition(); | 
|  | fuzzer.emitFfiCast("${ffiCastName}", "${name}", "${name}Type", parameters); | 
|  | fuzzer.emitNewline(); | 
|  | fuzzer.emitNewline(); | 
|  | } | 
|  |  | 
|  | final String ffiCastName; | 
|  | } | 
|  |  | 
|  | /// Class for instance methods generated by DartFuzz. | 
|  | class InstanceMethod extends Method { | 
|  | InstanceMethod( | 
|  | String name, List<DartType> parameters, DartFuzz fuzzer, this.className) | 
|  | : super(name, parameters, fuzzer) {} | 
|  |  | 
|  | @override | 
|  | String emitCall(int depth, | 
|  | {RhsFilter rhsFilter, | 
|  | bool includeSemicolon = false, | 
|  | bool isRecursiveCall = false}) { | 
|  | String outputName = name; | 
|  | if (fuzzer.currentClassIndex == null) { | 
|  | // If we're calling an instance method from outside the class, then we | 
|  | // should construct an object of that class first. | 
|  | outputName = "${className}().${name}"; | 
|  | } | 
|  | fuzzer.emitLn(outputName, newline: false); | 
|  | emitCallParameters(depth + 1, isRecursiveCall, rhsFilter: rhsFilter); | 
|  | if (includeSemicolon) { | 
|  | fuzzer.emit(';'); | 
|  | } | 
|  | return outputName; | 
|  | } | 
|  |  | 
|  | final String className; | 
|  | } | 
|  |  | 
|  | class CallMethod extends Method { | 
|  | CallMethod(List<DartType> parameters, DartFuzz fuzzer, this.className) | 
|  | : super("call", parameters, fuzzer) {} | 
|  |  | 
|  | @override | 
|  | String emitCall(int depth, | 
|  | {RhsFilter rhsFilter, | 
|  | bool includeSemicolon = false, | 
|  | bool isRecursiveCall = false}) { | 
|  | String outputName; | 
|  | outputName = "${className}()"; | 
|  | fuzzer.emitLn(outputName, newline: false); | 
|  | emitCallParameters(depth + 1, isRecursiveCall, rhsFilter: rhsFilter); | 
|  | if (includeSemicolon) { | 
|  | fuzzer.emit(';'); | 
|  | } | 
|  | return outputName; | 
|  | } | 
|  |  | 
|  | @override | 
|  | void emit() { | 
|  | // Override the parent class' call method. | 
|  | fuzzer.emit("@override"); | 
|  | fuzzer.emitNewline(); | 
|  | emitFunctionDefinition(); | 
|  | } | 
|  |  | 
|  | final String className; | 
|  | } | 
|  |  | 
|  | // Class for extension methods generated by DartFuzz. | 
|  | class ExtensionMethod extends Method { | 
|  | ExtensionMethod(String name, List<DartType> parameters, DartFuzz fuzzer, | 
|  | this.className, this.extensionName, this.type) | 
|  | : super(name, parameters, fuzzer) {} | 
|  | @override | 
|  | String emitCall( | 
|  | int depth, { | 
|  | RhsFilter rhsFilter, | 
|  | bool includeSemicolon = false, | 
|  | bool isRecursiveCall = false, | 
|  | }) { | 
|  | String outputName = name; | 
|  |  | 
|  | // If we're calling an extension method on a class from outside the class, | 
|  | // we must call it using an object of the class or using explicit extension | 
|  | // application. Extension methods on Dart types are presently always called | 
|  | // using a variable of the Dart type or using explicit extension | 
|  | // application. | 
|  | if (fuzzer.currentClassIndex == null || type != null) { | 
|  | String invokingObject = | 
|  | type != null ? fuzzer.pickScalarVar(type) : "${className}()"; | 
|  |  | 
|  | if (extensionName != null && fuzzer.coinFlip()) { | 
|  | outputName = "${extensionName}(${invokingObject}).${name}"; | 
|  | } else { | 
|  | outputName = "${invokingObject}.${name}"; | 
|  | } | 
|  | } | 
|  | fuzzer.emitLn(outputName, newline: false); | 
|  | emitCallParameters(depth + 1, isRecursiveCall, rhsFilter: rhsFilter); | 
|  | if (includeSemicolon) { | 
|  | fuzzer.emit(';'); | 
|  | } | 
|  | return outputName; | 
|  | } | 
|  |  | 
|  | final String className; | 
|  | final String extensionName; | 
|  | final DartType type; | 
|  | } | 
|  |  | 
|  | // Class that tracks the state of the filter applied to the | 
|  | // right-hand-side of an assignment in order to avoid generating | 
|  | // left-hand-side variables. | 
|  | class RhsFilter { | 
|  | RhsFilter(this._remaining, this.lhsVar); | 
|  | factory RhsFilter.fromDartType(DartType tp, String lhsVar) { | 
|  | if (DartType.isGrowableType(tp)) { | 
|  | return RhsFilter(1, lhsVar); | 
|  | } | 
|  | return null; | 
|  | } | 
|  | // Clone the current RhsFilter instance and set remaining to 0. | 
|  | // This is used for parameter expressions. | 
|  | factory RhsFilter.cloneEmpty(RhsFilter rhsFilter) => | 
|  | rhsFilter == null ? null : RhsFilter(0, rhsFilter.lhsVar); | 
|  | void consume() => _remaining--; | 
|  | bool get shouldFilter => _remaining <= 0; | 
|  | // Number of times the lhs variable can still be used on the rhs. | 
|  | int _remaining; | 
|  | // The name of the lhs variable to be filtered from the rhs. | 
|  | final String lhsVar; | 
|  | } | 
|  |  | 
|  | /// Class that specifies the api for calling library and ffi functions (if | 
|  | /// enabled). | 
|  | class DartApi { | 
|  | DartApi(bool ffi) : typeToLibraryMethods = DartLib.typeToLibraryMethods { | 
|  | if (ffi) { | 
|  | typeToLibraryMethods[DartType.INT] = [ | 
|  | ...[ | 
|  | DartLib('intComputation', | 
|  | [DartType.VOID, ...List<DartType>.filled(4, DartType.INT)], true), | 
|  | DartLib('takeMaxUint16', [DartType.VOID, DartType.INT], true), | 
|  | DartLib( | 
|  | 'sumPlus42', [DartType.VOID, DartType.INT, DartType.INT], true), | 
|  | DartLib('returnMaxUint8', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('returnMaxUint16', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('returnMaxUint32', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('returnMinInt8', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('returnMinInt16', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('returnMinInt32', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('takeMinInt16', [DartType.VOID, DartType.INT], true), | 
|  | DartLib('takeMinInt32', [DartType.VOID, DartType.INT], true), | 
|  | DartLib('uintComputation', | 
|  | [DartType.VOID, ...List<DartType>.filled(4, DartType.INT)], true), | 
|  | DartLib('sumSmallNumbers', | 
|  | [DartType.VOID, ...List<DartType>.filled(6, DartType.INT)], true), | 
|  | DartLib('takeMinInt8', [DartType.VOID, DartType.INT], true), | 
|  | DartLib('takeMaxUint32', [DartType.VOID, DartType.INT], true), | 
|  | DartLib('takeMaxUint8', [DartType.VOID, DartType.INT], true), | 
|  | DartLib('minInt64', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('minInt32', [DartType.VOID, DartType.VOID], true), | 
|  | // Use small int to avoid overflow divergences due to size | 
|  | // differences in intptr_t on 32-bit and 64-bit platforms. | 
|  | DartLib('sumManyIntsOdd', | 
|  | [DartType.VOID, ...List<DartType>.filled(11, DartType.INT)], true, | 
|  | restrictions: [ | 
|  | Restriction.none, | 
|  | ...List<Restriction>.filled(11, Restriction.small) | 
|  | ]), | 
|  | DartLib('sumManyInts', | 
|  | [DartType.VOID, ...List<DartType>.filled(10, DartType.INT)], true, | 
|  | restrictions: [ | 
|  | Restriction.none, | 
|  | ...List<Restriction>.filled(10, Restriction.small) | 
|  | ]), | 
|  | DartLib('regress37069', | 
|  | [DartType.VOID, ...List<DartType>.filled(11, DartType.INT)], true, | 
|  | restrictions: [ | 
|  | Restriction.none, | 
|  | ...List<Restriction>.filled(11, Restriction.small) | 
|  | ]), | 
|  | ], | 
|  | ...DartLib.intLibs, | 
|  | ]; | 
|  |  | 
|  | typeToLibraryMethods[DartType.DOUBLE] = [ | 
|  | if (ffi) ...[ | 
|  | DartLib('times1_337Float', [DartType.VOID, DartType.DOUBLE], true), | 
|  | DartLib( | 
|  | 'sumManyDoubles', | 
|  | [DartType.VOID, ...List<DartType>.filled(10, DartType.DOUBLE)], | 
|  | true), | 
|  | DartLib('times1_337Double', [DartType.VOID, DartType.DOUBLE], true), | 
|  | DartLib( | 
|  | 'sumManyNumbers', | 
|  | [ | 
|  | DartType.VOID, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE, | 
|  | DartType.INT, | 
|  | DartType.DOUBLE | 
|  | ], | 
|  | true), | 
|  | DartLib('inventFloatValue', [DartType.VOID, DartType.VOID], true), | 
|  | DartLib('smallDouble', [DartType.VOID, DartType.VOID], true), | 
|  | ], | 
|  | ...DartLib.doubleLibs, | 
|  | ]; | 
|  | } | 
|  | } | 
|  |  | 
|  | final typeToLibraryMethods; | 
|  | } | 
|  |  | 
|  | /// Class that generates a random, but runnable Dart program for fuzz testing. | 
|  | class DartFuzz { | 
|  | DartFuzz(this.seed, this.fp, this.ffi, this.flatTp, this.file, | 
|  | {this.minimize = false, this.smask, this.emask}); | 
|  |  | 
|  | void run() { | 
|  | // Initialize program variables. | 
|  | rand = Random(seed); | 
|  | indent = 0; | 
|  | nest = 0; | 
|  | currentClassIndex = null; | 
|  | currentMethod = null; | 
|  | currentMethodIndex = null; | 
|  | // Setup Dart types. | 
|  | dartType = DartType.fromDartConfig(enableFp: fp, disableNesting: flatTp); | 
|  | // Setup minimization parameters. | 
|  | initMinimization(); | 
|  | // Setup the library and ffi api. | 
|  | api = DartApi(ffi); | 
|  | // Setup the types. | 
|  | localVars = <DartType>[]; | 
|  | iterVars = <String>[]; | 
|  | globalVars = fillTypes1(limit: numGlobalVars); | 
|  | globalVars.addAll(dartType.allTypes); | 
|  | globalMethods = getMethods( | 
|  | numGlobalMethods, numMethodParams, MethodType.globalMethod, | 
|  | namePrefix: methodName); | 
|  | classFields = fillTypes2(limit2: numClasses, limit1: numLocalVars); | 
|  | final int numClassMethods = 1 + numClasses - classFields.length; | 
|  | classMethods = getClassMethods(classFields.length, numClassMethods, | 
|  | numMethodParams, fillTypes1(limit: numMethodParams)); | 
|  | virtualClassMethods = <Map<int, List<int>>>[]; | 
|  | classParents = <int>[]; | 
|  | // Setup optional ffi methods and types. | 
|  | if (ffi) { | 
|  | globalMethods.addAll(getMethods( | 
|  | numGlobalMethods, numMethodParams, MethodType.ffiMethod, | 
|  | namePrefix: methodName)); | 
|  | } | 
|  | // Generate. | 
|  | emitHeader(); | 
|  | emitVariableDeclarations(varName, globalVars); | 
|  | emitMethods(globalMethods); | 
|  | for (int i = 0; i < numDartTypeExtensions; ++i) { | 
|  | DartType type = oneOfSet(dartType.allTypes); | 
|  | emitAndAddExtensionMethods(globalMethods, type.name, "${methodName}E${i}", | 
|  | "${methodName}${i}_Extension", | 
|  | type: type); | 
|  | } | 
|  | emitClasses(); | 
|  | emitMain(); | 
|  | // Sanity. | 
|  | assert(currentClassIndex == null); | 
|  | assert(currentMethod == null); | 
|  | assert(currentMethodIndex == null); | 
|  | assert(indent == 0); | 
|  | assert(nest == 0); | 
|  | assert(localVars.isEmpty); | 
|  | } | 
|  |  | 
|  | List<Method> getMethods(int maxMethods, int maxParams, MethodType methodType, | 
|  | {String className, | 
|  | String extensionName, | 
|  | String namePrefix, | 
|  | DartType type}) { | 
|  | final List<Method> list = <Method>[]; | 
|  | for (int i = 0, n = chooseOneUpTo(maxMethods); i < n; i++) { | 
|  | final List<DartType> params = fillTypes1( | 
|  | limit: maxParams, isFfi: methodType == MethodType.ffiMethod); | 
|  | switch (methodType) { | 
|  | case MethodType.globalMethod: | 
|  | list.add(GlobalMethod("${namePrefix}${i}", params, this)); | 
|  | break; | 
|  | case MethodType.ffiMethod: | 
|  | list.add(FfiMethod(namePrefix, i, params, this)); | 
|  | break; | 
|  | case MethodType.instanceMethod: | 
|  | list.add( | 
|  | InstanceMethod("${namePrefix}${i}", params, this, className)); | 
|  | break; | 
|  | case MethodType.extensionMethod: | 
|  | list.add(ExtensionMethod("${namePrefix}${i}", params, this, className, | 
|  | extensionName, type)); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | return list; | 
|  | } | 
|  |  | 
|  | List<List<Method>> getClassMethods(int numClasses, int maxMethods, | 
|  | int maxParams, List<DartType> callMethodParams) { | 
|  | final methodsForAllClasses = <List<Method>>[]; | 
|  | for (int i = 0; i < numClasses; i++) { | 
|  | final methodsForCurrentClass = getMethods( | 
|  | maxMethods, maxParams, MethodType.instanceMethod, | 
|  | className: "X${i}", namePrefix: "${methodName}${i}_"); | 
|  | // Add the call method for the current class. The prototype for the call | 
|  | // method is pre-decided. This is because we are possibly overriding the | 
|  | // parent class' call method, in which case the prototype should be the | 
|  | // same. To simplify the dartfuzz logic, all call methods have the same | 
|  | // prototype. | 
|  | methodsForCurrentClass.add(CallMethod(callMethodParams, this, "X${i}")); | 
|  | methodsForAllClasses.add(methodsForCurrentClass); | 
|  | } | 
|  | return methodsForAllClasses; | 
|  | } | 
|  |  | 
|  | // | 
|  | // General Helpers. | 
|  | // | 
|  |  | 
|  | BigInt genMask(int m) => BigInt.from(1) << m; | 
|  | int choose(int c) => rand.nextInt(c); | 
|  | int chooseOneUpTo(int c) => choose(c) + 1; | 
|  | bool coinFlip() => rand.nextBool(); | 
|  | bool rollDice(int c) => (choose(c) == 0); | 
|  | double uniform() => rand.nextDouble(); | 
|  |  | 
|  | // Picks one of the given choices. | 
|  | T oneOf<T>(List<T> choices) => choices[choose(choices.length)]; | 
|  | T oneOfSet<T>(Set<T> choices) => choices.elementAt(choose(choices.length)); | 
|  |  | 
|  | // | 
|  | // Code Structure Helpers. | 
|  | // | 
|  |  | 
|  | void incIndent() => indent += 2; | 
|  | void decIndent() => indent -= 2; | 
|  |  | 
|  | void emitZero() => emit('0'); | 
|  |  | 
|  | void emitTryCatchFinally(Function tryBody, Function catchBody, | 
|  | {Function finallyBody}) { | 
|  | emitLn('try ', newline: false); | 
|  | emitBraceWrapped(() => tryBody()); | 
|  | emit(' on OutOfMemoryError '); | 
|  | emitBraceWrapped(() => emitLn("exit(${oomExitCode});", newline: false)); | 
|  | emit(' catch (e, st) '); | 
|  | emitBraceWrapped(catchBody); | 
|  | if (finallyBody != null) { | 
|  | emit(' finally '); | 
|  | emitBraceWrapped(finallyBody); | 
|  | } | 
|  | } | 
|  |  | 
|  | dynamic emitWrapped(List<String> pair, Function exprEmitter, | 
|  | {bool shouldIndent = true}) { | 
|  | assert(pair.length == 2); | 
|  | emit(pair[0]); | 
|  | if (shouldIndent) { | 
|  | incIndent(); | 
|  | emitNewline(); | 
|  | } | 
|  | final result = exprEmitter(); | 
|  | if (shouldIndent) { | 
|  | decIndent(); | 
|  | emitNewline(); | 
|  | emitLn(pair[1], newline: false); | 
|  | } else { | 
|  | emit(pair[1]); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | dynamic emitParenWrapped(Function exprEmitter, | 
|  | {bool includeSemicolon = false}) { | 
|  | final result = | 
|  | emitWrapped(const ['(', ')'], exprEmitter, shouldIndent: false); | 
|  | if (includeSemicolon) { | 
|  | emit(';'); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | dynamic emitBraceWrapped(Function exprEmitter, {bool shouldIndent = true}) => | 
|  | emitWrapped(const ['{', '}'], exprEmitter, shouldIndent: shouldIndent); | 
|  |  | 
|  | dynamic emitSquareBraceWrapped(Function exprEmitter) => | 
|  | emitWrapped(const ['[', ']'], exprEmitter, shouldIndent: false); | 
|  |  | 
|  | dynamic emitCommaSeparated(Function(int) elementBuilder, int length, | 
|  | {int start = 0, bool newline = false}) { | 
|  | for (int i = start; i < length; ++i) { | 
|  | elementBuilder(i); | 
|  | if (i + 1 != length) { | 
|  | emit(',', newline: newline); | 
|  | if (!newline) { | 
|  | emit(' '); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitFunctionDefinition(String name, Function body, {String retType}) { | 
|  | emitIndentation(); | 
|  | if (retType != null) { | 
|  | emit(retType + ' '); | 
|  | } | 
|  | emit(name); | 
|  | emit('() '); // TODO(bkonyi): handle params | 
|  |  | 
|  | emitBraceWrapped(body); | 
|  | } | 
|  |  | 
|  | bool emitIfStatement( | 
|  | Function ifConditionEmitter, bool Function() ifBodyEmitter, | 
|  | {bool Function() elseBodyEmitter}) { | 
|  | emitLn('if ', newline: false); | 
|  | emitParenWrapped(ifConditionEmitter); | 
|  | emit(' '); | 
|  | final bool b1 = emitBraceWrapped(ifBodyEmitter); | 
|  | bool b2 = false; | 
|  | if (elseBodyEmitter != null) { | 
|  | emit(' else '); | 
|  | b2 = emitBraceWrapped(elseBodyEmitter); | 
|  | } | 
|  | return b1 || b2; | 
|  | } | 
|  |  | 
|  | void emitImport(String library, {String asName}) => (asName == null) | 
|  | ? emitLn("import '$library';") | 
|  | : emitLn("import '$library' as $asName;"); | 
|  |  | 
|  | void emitBinaryComparison(Function e1, String op, Function e2, | 
|  | {bool includeSemicolon = false}) { | 
|  | e1(); | 
|  | emit(' $op '); | 
|  | e2(); | 
|  | if (includeSemicolon) { | 
|  | emit(';'); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Randomly specialize interface if possible. E.g. num to int. | 
|  | DartType maybeSpecializeInterface(DartType tp) { | 
|  | if (!dartType.isSpecializable(tp)) return tp; | 
|  | DartType resolvedTp = oneOfSet(dartType.interfaces(tp)); | 
|  | return resolvedTp; | 
|  | } | 
|  |  | 
|  | // | 
|  | // Minimization components. | 
|  | // | 
|  |  | 
|  | void initMinimization() { | 
|  | stmtCntr = 0; | 
|  | exprCntr = 0; | 
|  | skipStmt = false; | 
|  | skipExpr = false; | 
|  | } | 
|  |  | 
|  | void emitMinimizedLiteral(DartType tp) { | 
|  | switch (tp) { | 
|  | case DartType.BOOL: | 
|  | emit('true'); | 
|  | break; | 
|  | case DartType.INT: | 
|  | emit('1'); | 
|  | break; | 
|  | case DartType.DOUBLE: | 
|  | emit('1.0'); | 
|  | break; | 
|  | case DartType.STRING: | 
|  | emit('"a"'); | 
|  | break; | 
|  | case DartType.LIST_INT: | 
|  | emit('[1]'); | 
|  | break; | 
|  | case DartType.SET_INT: | 
|  | emit('{1}'); | 
|  | break; | 
|  | case DartType.MAP_INT_STRING: | 
|  | emit('{1: "a"}'); | 
|  | break; | 
|  | default: | 
|  | throw 'Unknown DartType ${tp}'; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Process the opening of a statement. | 
|  | // Determine whether the statement should be skipped based on the | 
|  | // statement index stored in stmtCntr and the statement mask stored | 
|  | // in smask. | 
|  | // Returns true if the statement should be skipped. | 
|  | bool processStmtOpen() { | 
|  | // Do nothing if we are not in minimization mode. | 
|  | if (!minimize) { | 
|  | return false; | 
|  | } | 
|  | // Check whether the bit for the current statement number is set in the | 
|  | // statement bitmap. If so skip this statement. | 
|  | final newMask = genMask(stmtCntr); | 
|  | final maskBitSet = (smask & newMask) != BigInt.zero; | 
|  | // Statements are nested, therefore masking one statement like e.g. | 
|  | // a for loop leads to other statements being omitted. | 
|  | // Here we update the statement mask to include the additionally | 
|  | // omitted statements. | 
|  | if (skipStmt) { | 
|  | smask |= newMask; | 
|  | } | 
|  | // Increase the statement counter. | 
|  | stmtCntr++; | 
|  | if (!skipStmt && maskBitSet) { | 
|  | skipStmt = true; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Process the closing of a statement. | 
|  | // The variable resetSkipStmt indicates whether this | 
|  | // statement closes the sequence of skipped statement. | 
|  | // E.g. the end of a loop where all contained statements | 
|  | // were skipped. | 
|  | void processStmtClose(bool resetSkipStmt) { | 
|  | if (!minimize) { | 
|  | return; | 
|  | } | 
|  | if (resetSkipStmt) { | 
|  | skipStmt = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Process the opening of an expression. | 
|  | // Determine whether the expression should be skipped based on the | 
|  | // expression index stored in exprCntr and the expression mask stored | 
|  | // in emask. | 
|  | // Returns true is the expression is skipped. | 
|  | bool processExprOpen(DartType tp) { | 
|  | // Do nothing if we are not in minimization mode. | 
|  | if (!minimize) { | 
|  | return false; | 
|  | } | 
|  | // Check whether the bit for the current expression number is set in the | 
|  | // expression bitmap. If so skip this expression. | 
|  | final newMask = genMask(exprCntr); | 
|  | final maskBitSet = (emask & newMask) != BigInt.zero; | 
|  | // Expressions are nested, therefore masking one expression like e.g. | 
|  | // a for loop leads to other expressions being omitted. | 
|  | // Similarly, if the whole statement is skipped, all the expressions | 
|  | // within that statement are implicitly masked. | 
|  | // Here we update the expression mask to include the additionally | 
|  | // omitted expressions. | 
|  | if (skipExpr || skipStmt) { | 
|  | emask |= newMask; | 
|  | } | 
|  | exprCntr++; | 
|  | if (skipStmt) { | 
|  | return false; | 
|  | } | 
|  | if (!skipExpr && maskBitSet) { | 
|  | emitMinimizedLiteral(tp); | 
|  | skipExpr = true; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void processExprClose(bool resetExprStmt) { | 
|  | if (!minimize || skipStmt) { | 
|  | return; | 
|  | } | 
|  | if (resetExprStmt) { | 
|  | skipExpr = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // | 
|  | // Program components. | 
|  | // | 
|  |  | 
|  | void emitHeader() { | 
|  | emitLn('// The Dart Project Fuzz Tester ($version).'); | 
|  | emitLn('// Program generated as:'); | 
|  | emitLn('//   dart dartfuzz.dart --seed $seed --${fp ? "" : "no-"}fp ' | 
|  | '--${ffi ? "" : "no-"}ffi --${flatTp ? "" : "no-"}flat'); | 
|  | emitNewline(); | 
|  | emitImport('dart:async'); | 
|  | emitImport('dart:cli'); | 
|  | emitImport('dart:collection'); | 
|  | emitImport('dart:convert'); | 
|  | emitImport('dart:core'); | 
|  | emitImport('dart:io'); | 
|  | emitImport('dart:isolate'); | 
|  | emitImport('dart:math'); | 
|  | emitImport('dart:typed_data'); | 
|  | if (ffi) { | 
|  | emitImport('dart:ffi', asName: 'ffi'); | 
|  | emitLn(DartFuzzFfiApi.ffiapi); | 
|  | } | 
|  | emitNewline(); | 
|  | } | 
|  |  | 
|  | void emitFfiCast(String dartFuncName, String ffiFuncName, String typeName, | 
|  | List<DartType> pars) { | 
|  | emit("${pars[0].name} Function"); | 
|  | emitParenWrapped(() => emitCommaSeparated( | 
|  | (int i) => emit('${pars[i].name}'), pars.length, | 
|  | start: 1)); | 
|  | emit(' ${dartFuncName} = ffi.Pointer.fromFunction<${typeName}>'); | 
|  | emitParenWrapped(() { | 
|  | emit('${ffiFuncName}, '); | 
|  | emitLiteral(0, pars[0], smallPositiveValue: true); | 
|  | }); | 
|  | emit('.cast<ffi.NativeFunction<${typeName}>>().asFunction();'); | 
|  | emitNewline(); | 
|  | } | 
|  |  | 
|  | void emitMethods(List<Method> methods) { | 
|  | for (int i = 0; i < methods.length; i++) { | 
|  | currentMethod = methods[i]; | 
|  | currentMethodIndex = i; | 
|  | currentMethod.emit(); | 
|  | currentMethod = null; | 
|  | currentMethodIndex = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Randomly overwrite some methods from the parent classes. | 
|  | void emitVirtualMethods() { | 
|  | final currentClassTmp = currentClassIndex; | 
|  | int parentClass = classParents[currentClassIndex]; | 
|  | final vcm = <int, List<int>>{}; | 
|  | // Chase randomly up in class hierarchy. | 
|  | while (parentClass >= 0) { | 
|  | vcm[parentClass] = <int>[]; | 
|  | for (int j = 0, n = classMethods[parentClass].length; j < n; j++) { | 
|  | // Call methods are presently always already overriden, so we | 
|  | // shouldn't override them here. | 
|  | if (rollDice(8) && !(classMethods[parentClass][j] is CallMethod)) { | 
|  | currentClassIndex = parentClass; | 
|  | currentMethod = classMethods[parentClass][j]; | 
|  | currentMethodIndex = j; | 
|  | classMethods[parentClass][j].emit(); | 
|  | vcm[parentClass].add(currentMethodIndex); | 
|  | currentClassIndex = null; | 
|  | currentMethod = null; | 
|  | currentMethodIndex = null; | 
|  | } | 
|  | } | 
|  | if (coinFlip() || classParents.length > parentClass) { | 
|  | break; | 
|  | } else { | 
|  | parentClass = classParents[parentClass]; | 
|  | } | 
|  | } | 
|  | currentClassIndex = currentClassTmp; | 
|  | virtualClassMethods.add(vcm); | 
|  | } | 
|  |  | 
|  | void emitClasses() { | 
|  | assert(classFields.length == classMethods.length); | 
|  | for (int i = 0; i < classFields.length; i++) { | 
|  | if (i == 0) { | 
|  | classParents.add(-1); | 
|  | emit('class X0 '); | 
|  | } else { | 
|  | final int parentClass = choose(i); | 
|  | classParents.add(parentClass); | 
|  | if (coinFlip()) { | 
|  | // Inheritance | 
|  | emit('class X$i extends X${parentClass} '); | 
|  | } else { | 
|  | // Mixin | 
|  | if (classParents[parentClass] >= 0) { | 
|  | emit( | 
|  | 'class X$i extends X${classParents[parentClass]} with X${parentClass} '); | 
|  | } else { | 
|  | emit('class X$i with X${parentClass} '); | 
|  | } | 
|  | } | 
|  | } | 
|  | emitBraceWrapped(() { | 
|  | emitVariableDeclarations('$fieldName${i}_', classFields[i]); | 
|  | currentClassIndex = i; | 
|  | emitVirtualMethods(); | 
|  | emitMethods(classMethods[currentClassIndex]); | 
|  | emitFunctionDefinition('run', () { | 
|  | if (i > 0) { | 
|  | // FIXME(bkonyi): fix potential issue where we try to apply a class | 
|  | // as a mixin when it calls super. | 
|  | emitLn('super.run();'); | 
|  | } | 
|  | assert(localVars.isEmpty); | 
|  | emitStatements(0); | 
|  | assert(localVars.isEmpty); | 
|  | }, retType: 'void'); | 
|  | }); | 
|  | emitNewline(); | 
|  | emitNewline(); | 
|  | emitAndAddExtensionMethods( | 
|  | classMethods[currentClassIndex], | 
|  | "X${currentClassIndex}", | 
|  | "XE${currentClassIndex}", | 
|  | "${methodName}${currentClassIndex}_Extension"); | 
|  | currentClassIndex = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitAndAddExtensionMethods( | 
|  | List<Method> methodList, className, extensionName, namePrefix, | 
|  | {DartType type}) { | 
|  | int endIndex = methodList.length; | 
|  | methodList.addAll(getMethods(numExtensionMethodsPerClass, numMethodParams, | 
|  | MethodType.extensionMethod, | 
|  | className: className, | 
|  | // Randomly select between named and anonymous extensions. | 
|  | extensionName: coinFlip() ? extensionName : "", | 
|  | namePrefix: namePrefix, | 
|  | type: type)); | 
|  | emit("extension $extensionName on $className "); | 
|  | emitBraceWrapped(() { | 
|  | // Emit the newly added methods. | 
|  | for (int i = endIndex; i < methodList.length; i++) { | 
|  | currentMethod = methodList[i]; | 
|  | currentMethodIndex = i; | 
|  | methodList[i].emit(); | 
|  | currentMethod = null; | 
|  | currentMethodIndex = null; | 
|  | } | 
|  | }); | 
|  | emitNewline(); | 
|  | emitNewline(); | 
|  | } | 
|  |  | 
|  | void emitLoadFfiLib() { | 
|  | if (ffi) { | 
|  | emitLn( | 
|  | '// The following throws an uncaught exception if the ffi library ' + | 
|  | 'is not found.'); | 
|  | emitLn( | 
|  | '// By not catching this exception, we terminate the program with ' + | 
|  | 'a full stack trace'); | 
|  | emitLn('// which, in turn, flags the problem prominently'); | 
|  | emitIfStatement(() => emit('ffiTestFunctions == null'), | 
|  | () => emitPrint('Did not load ffi test functions')); | 
|  | emitNewline(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitMain() => emitFunctionDefinition('main', () { | 
|  | emitLoadFfiLib(); | 
|  |  | 
|  | // Call each global method once. | 
|  | for (int i = 0; i < globalMethods.length; i++) { | 
|  | String outputName; | 
|  | emitTryCatchFinally( | 
|  | () => outputName = | 
|  | globalMethods[i].emitCall(1, includeSemicolon: true), | 
|  | () => emitPrint('$outputName() throws')); | 
|  | emitNewline(); | 
|  | } | 
|  |  | 
|  | // Call each class method once. | 
|  | for (int i = 0; i < classMethods.length; i++) { | 
|  | for (int j = 0; j < classMethods[i].length; j++) { | 
|  | String outputName; | 
|  | emitNewline(); | 
|  | emitTryCatchFinally( | 
|  | () => outputName = | 
|  | classMethods[i][j].emitCall(1, includeSemicolon: true), | 
|  | () => emitPrint('${outputName} throws')); | 
|  | } | 
|  | // Call each virtual class method once. | 
|  | int parentClass = classParents[i]; | 
|  | while (parentClass >= 0) { | 
|  | if (virtualClassMethods[i].containsKey(parentClass)) { | 
|  | for (int j = 0; | 
|  | j < virtualClassMethods[i][parentClass].length; | 
|  | j++) { | 
|  | String outputName; | 
|  | emitNewline(); | 
|  | emitTryCatchFinally( | 
|  | () => outputName = classMethods[parentClass][j] | 
|  | .emitCall(1, includeSemicolon: true), | 
|  | () => emitPrint('${outputName} throws')); | 
|  | } | 
|  | } | 
|  | parentClass = classParents[parentClass]; | 
|  | } | 
|  | } | 
|  |  | 
|  | emitNewline(); | 
|  | emitTryCatchFinally(() { | 
|  | emitLn('X${classFields.length - 1}().run();', newline: false); | 
|  | }, () { | 
|  | emitPrint('X${classFields.length - 1}().run() throws'); | 
|  | }); | 
|  |  | 
|  | emitNewline(); | 
|  | emitTryCatchFinally(() { | 
|  | String body = ''; | 
|  | for (int i = 0; i < globalVars.length; i++) { | 
|  | body += '\$$varName$i\\n'; | 
|  | } | 
|  | emitPrint('$body'); | 
|  | }, () => emitPrint('print() throws')); | 
|  | }); | 
|  |  | 
|  | // | 
|  | // Declarations. | 
|  | // | 
|  |  | 
|  | void emitVariableDeclarations(String name, List<DartType> vars) { | 
|  | for (int i = 0; i < vars.length; i++) { | 
|  | DartType tp = vars[i]; | 
|  | final varName = '$name$i'; | 
|  | emitVariableDeclaration(varName, tp, | 
|  | initializerEmitter: () => emitConstructorOrLiteral(0, tp)); | 
|  | } | 
|  | emitNewline(); | 
|  | } | 
|  |  | 
|  | void emitVariableDeclaration(String name, DartType tp, | 
|  | {Function initializerEmitter, | 
|  | bool indent = true, | 
|  | bool newline = true, | 
|  | bool includeSemicolon = true}) { | 
|  | final typeName = tp.name; | 
|  | if (indent) { | 
|  | emitIndentation(); | 
|  | } | 
|  | emit('$typeName $name', newline: false); | 
|  | if (initializerEmitter != null) { | 
|  | emit(' = '); | 
|  | initializerEmitter(); | 
|  | } | 
|  | if (includeSemicolon) { | 
|  | emit(';', newline: newline); | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitParDecls(List<DartType> pars) => emitCommaSeparated((int i) { | 
|  | DartType tp = pars[i]; | 
|  | emit('${tp.name} $paramName$i'); | 
|  | }, pars.length, start: 1); | 
|  |  | 
|  | // | 
|  | // Comments (for FE and analysis tools). | 
|  | // | 
|  |  | 
|  | void emitComment() { | 
|  | switch (choose(4)) { | 
|  | case 0: | 
|  | emitLn('// Single-line comment.'); | 
|  | break; | 
|  | case 1: | 
|  | emitLn('/// Single-line documentation comment.'); | 
|  | break; | 
|  | case 2: | 
|  | emitLn('/*'); | 
|  | emitLn(' * Multi-line'); | 
|  | emitLn(' * comment.'); | 
|  | emitLn(' */'); | 
|  | break; | 
|  | default: | 
|  | emitLn('/**'); | 
|  | emitLn(' ** Multi-line'); | 
|  | emitLn(' ** documentation comment.'); | 
|  | emitLn(' */'); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // | 
|  | // Statements. | 
|  | // | 
|  |  | 
|  | // Emit an assignment statement. | 
|  | bool emitAssign() { | 
|  | final tp = oneOfSet(dartType.allTypes); | 
|  | String assignOp; | 
|  | if (DartType.isGrowableType(tp)) { | 
|  | // Assignments like *= and += on growable types (String, List, ...) | 
|  | // may lead to OOM, especially within loops. | 
|  | // TODO: Implement a more specific heuristic that selectively allows | 
|  | // modifying assignment operators (like += *=) for growable types. | 
|  | assignOp = '='; | 
|  | } else { | 
|  | // Select one of the assign operations for the given type. | 
|  | assignOp = oneOfSet(dartType.assignOps(tp)); | 
|  | if (assignOp == null) { | 
|  | throw 'No assign operation for ${tp.name}'; | 
|  | } | 
|  | } | 
|  | emitIndentation(); | 
|  | // Emit a variable of the lhs type. | 
|  | final emittedVar = emitVar(0, tp, isLhs: true); | 
|  | RhsFilter rhsFilter = RhsFilter.fromDartType(tp, emittedVar); | 
|  | emit(" $assignOp "); | 
|  | // Select one of the possible rhs types for the given lhs type and assign | 
|  | // operation. | 
|  | DartType rhsType = oneOfSet(dartType.assignOpRhs(tp, assignOp)); | 
|  | if (rhsType == null) { | 
|  | throw 'No rhs type for assign ${tp.name} $assignOp'; | 
|  | } | 
|  |  | 
|  | emitExpr(0, rhsType, rhsFilter: rhsFilter, includeSemicolon: true); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a print statement. | 
|  | bool emitPrint([String body]) { | 
|  | emitLn('print', newline: false); | 
|  | emitParenWrapped(() { | 
|  | if (body != null) { | 
|  | emit("'$body'"); | 
|  | } else { | 
|  | DartType tp = oneOfSet(dartType.allTypes); | 
|  | emitExpr(0, tp); | 
|  | } | 
|  | }, includeSemicolon: true); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a return statement. | 
|  | bool emitReturn() { | 
|  | List<DartType> proto = getCurrentProto(); | 
|  | if (proto == null) { | 
|  | emitLn('return;'); | 
|  | } else { | 
|  | emitLn('return ', newline: false); | 
|  | emitExpr(0, proto[0], includeSemicolon: true); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Emit a throw statement. | 
|  | bool emitThrow() { | 
|  | DartType tp = oneOfSet(dartType.allTypes); | 
|  | emitLn('throw ', newline: false); | 
|  | emitExpr(0, tp, includeSemicolon: true); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Emit a one-way if statement. | 
|  | bool emitIf1(int depth) => emitIfStatement( | 
|  | () => emitExpr(0, DartType.BOOL), () => emitStatements(depth + 1)); | 
|  |  | 
|  | // Emit a two-way if statement. | 
|  | bool emitIf2(int depth) => emitIfStatement( | 
|  | () => emitExpr(0, DartType.BOOL), () => emitStatements(depth + 1), | 
|  | elseBodyEmitter: () => emitStatements(depth + 1)); | 
|  |  | 
|  | // Emit a simple increasing for-loop. | 
|  | bool emitFor(int depth) { | 
|  | // Make deep nesting of loops increasingly unlikely. | 
|  | if (choose(nest + 1) > nestDepth) { | 
|  | return emitAssign(); | 
|  | } | 
|  | final int i = localVars.length; | 
|  | emitLn('for ', newline: false); | 
|  | emitParenWrapped(() { | 
|  | final name = '$localName$i'; | 
|  | emitVariableDeclaration(name, DartType.INT, | 
|  | initializerEmitter: emitZero, newline: false, indent: false); | 
|  | emitBinaryComparison(() => emit(name), '<', emitSmallPositiveInt, | 
|  | includeSemicolon: true); | 
|  | emit('$name++'); | 
|  | }); | 
|  | emitBraceWrapped(() { | 
|  | nest++; | 
|  | iterVars.add('$localName$i'); | 
|  | localVars.add(DartType.INT); | 
|  | emitStatements(depth + 1); | 
|  | localVars.removeLast(); | 
|  | iterVars.removeLast(); | 
|  | nest--; | 
|  | }); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a simple membership for-in-loop. | 
|  | bool emitForIn(int depth) { | 
|  | // Make deep nesting of loops increasingly unlikely. | 
|  | if (choose(nest + 1) > nestDepth) { | 
|  | return emitAssign(); | 
|  | } | 
|  | final int i = localVars.length; | 
|  | // Select one iterable type to be used in 'for in' statement. | 
|  | final iterType = oneOfSet(dartType.iterableTypes1); | 
|  | // Get the element type contained within the iterable type. | 
|  | final elementType = dartType.elementType(iterType); | 
|  | if (elementType == null) { | 
|  | throw 'No element type for iteration type ${iterType.name}'; | 
|  | } | 
|  | emitLn('for ', newline: false); | 
|  | emitParenWrapped(() { | 
|  | emit('${elementType.name} $localName$i in '); | 
|  | localVars.add(null); // declared, but don't use | 
|  | emitExpr(0, iterType); | 
|  | localVars.removeLast(); // will get type | 
|  | }); | 
|  | emitBraceWrapped(() { | 
|  | nest++; | 
|  | localVars.add(elementType); | 
|  | emitStatements(depth + 1); | 
|  | localVars.removeLast(); | 
|  | nest--; | 
|  | }); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a simple membership forEach loop. | 
|  | bool emitForEach(int depth) { | 
|  | // Make deep nesting of loops increasingly unlikely. | 
|  | if (choose(nest + 1) > nestDepth) { | 
|  | return emitAssign(); | 
|  | } | 
|  | emitIndentation(); | 
|  |  | 
|  | // Select one map type to be used in forEach loop. | 
|  | final mapType = oneOfSet(dartType.mapTypes); | 
|  | final emittedVar = emitScalarVar(mapType, isLhs: false); | 
|  | iterVars.add(emittedVar); | 
|  | emit('.forEach'); | 
|  | final i = localVars.length; | 
|  | final j = i + 1; | 
|  | emitParenWrapped(() { | 
|  | emitParenWrapped(() => emit('$localName$i, $localName$j')); | 
|  | emitBraceWrapped(() { | 
|  | final int nestTmp = nest; | 
|  | // Reset, since forEach cannot break out of own or enclosing context. | 
|  | nest = 0; | 
|  | // Get the type of the map key and add it to the local variables. | 
|  | localVars.add(dartType.indexType(mapType)); | 
|  | // Get the type of the map values and add it to the local variables. | 
|  | localVars.add(dartType.elementType(mapType)); | 
|  | emitStatements(depth + 1); | 
|  | localVars.removeLast(); | 
|  | localVars.removeLast(); | 
|  | nest = nestTmp; | 
|  | }); | 
|  | }, includeSemicolon: true); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a while-loop. | 
|  | bool emitWhile(int depth) { | 
|  | // Make deep nesting of loops increasingly unlikely. | 
|  | if (choose(nest + 1) > nestDepth) { | 
|  | return emitAssign(); | 
|  | } | 
|  | final int i = localVars.length; | 
|  | emitIndentation(); | 
|  | emitBraceWrapped(() { | 
|  | final name = '$localName$i'; | 
|  | emitVariableDeclaration(name, DartType.INT, | 
|  | initializerEmitter: () => emitSmallPositiveInt()); | 
|  | emitLn('while ', newline: false); | 
|  | emitParenWrapped( | 
|  | () => emitBinaryComparison(() => emit('--$name'), '>', emitZero)); | 
|  | emitBraceWrapped(() { | 
|  | nest++; | 
|  | iterVars.add(name); | 
|  | localVars.add(DartType.INT); | 
|  | emitStatements(depth + 1); | 
|  | localVars.removeLast(); | 
|  | iterVars.removeLast(); | 
|  | nest--; | 
|  | }); | 
|  | }); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a do-while-loop. | 
|  | bool emitDoWhile(int depth) { | 
|  | // Make deep nesting of loops increasingly unlikely. | 
|  | if (choose(nest + 1) > nestDepth) { | 
|  | return emitAssign(); | 
|  | } | 
|  | final int i = localVars.length; | 
|  | emitIndentation(); | 
|  | emitBraceWrapped(() { | 
|  | final name = '$localName$i'; | 
|  | emitVariableDeclaration(name, DartType.INT, initializerEmitter: emitZero); | 
|  | emitLn('do ', newline: false); | 
|  | emitBraceWrapped(() { | 
|  | nest++; | 
|  | iterVars.add(name); | 
|  | localVars.add(DartType.INT); | 
|  | emitStatements(depth + 1); | 
|  | localVars.removeLast(); | 
|  | iterVars.removeLast(); | 
|  | nest--; | 
|  | }); | 
|  | emit(' while '); | 
|  | emitParenWrapped( | 
|  | () => emitBinaryComparison( | 
|  | () => emit('++$name'), '<', emitSmallPositiveInt), | 
|  | includeSemicolon: true); | 
|  | }); | 
|  | emitNewline(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a break/continue when inside iteration. | 
|  | bool emitBreakOrContinue(int depth) { | 
|  | if (nest > 0) { | 
|  | switch (choose(2)) { | 
|  | case 0: | 
|  | emitLn('continue;'); | 
|  | return false; | 
|  | default: | 
|  | emitLn('break;'); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return emitAssign(); // resort to assignment | 
|  | } | 
|  |  | 
|  | // Emit a switch statement. | 
|  | bool emitSwitch(int depth) { | 
|  | emitCase(Function bodyEmitter, {int kase}) { | 
|  | if (kase == null) { | 
|  | emitLn('default: ', newline: false); | 
|  | } else { | 
|  | emitLn('case $kase: ', newline: false); | 
|  | } | 
|  | emitBraceWrapped(() { | 
|  | bodyEmitter(); | 
|  | emitNewline(); | 
|  | emitLn('break;', | 
|  | newline: false); // always generate, avoid FE complaints | 
|  | }); | 
|  | } | 
|  |  | 
|  | emitLn('switch ', newline: false); | 
|  | emitParenWrapped(() => emitExpr(0, DartType.INT)); | 
|  | emitBraceWrapped(() { | 
|  | int start = choose(1 << 32); | 
|  | int step = chooseOneUpTo(10); | 
|  | final maxCases = 3; | 
|  | for (int i = 0; i < maxCases; i++, start += step) { | 
|  | emitCase(() => emitStatement(depth + 1), | 
|  | kase: (i == 2 && coinFlip()) ? null : start); | 
|  | if (i + 1 != maxCases) { | 
|  | emitNewline(); | 
|  | } | 
|  | } | 
|  | }); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a new program scope that introduces a new local variable. | 
|  | bool emitScope(int depth) { | 
|  | emitIndentation(); | 
|  | emitBraceWrapped(() { | 
|  | DartType tp = oneOfSet(dartType.allTypes); | 
|  | final int i = localVars.length; | 
|  | final name = '$localName$i'; | 
|  | emitVariableDeclaration(name, tp, initializerEmitter: () { | 
|  | localVars.add(null); // declared, but don't use | 
|  | emitExpr(0, tp); | 
|  | localVars.removeLast(); // will get type | 
|  | }); | 
|  | localVars.add(tp); | 
|  | emitStatements(depth + 1); | 
|  | localVars.removeLast(); | 
|  | }); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit try/catch/finally. | 
|  | bool emitTryCatch(int depth) { | 
|  | final emitStatementsClosure = () => emitStatements(depth + 1); | 
|  | emitLn('try ', newline: false); | 
|  | emitBraceWrapped(emitStatementsClosure); | 
|  | emit(' on OutOfMemoryError '); | 
|  | emitBraceWrapped(() => emitLn("exit(${oomExitCode});", newline: false)); | 
|  | emit(' catch (exception, stackTrace) ', newline: false); | 
|  | emitBraceWrapped(emitStatementsClosure); | 
|  | if (coinFlip()) { | 
|  | emit(' finally ', newline: false); | 
|  | emitBraceWrapped(emitStatementsClosure); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Emit a single statement. | 
|  | bool emitSingleStatement(int depth) { | 
|  | // Throw in a comment every once in a while. | 
|  | if (rollDice(10)) { | 
|  | emitComment(); | 
|  | } | 
|  | // Continuing nested statements becomes less likely as the depth grows. | 
|  | if (choose(depth + 1) > stmtDepth) { | 
|  | return emitAssign(); | 
|  | } | 
|  | // Possibly nested statement. | 
|  | switch (choose(17)) { | 
|  | // Favors assignment. | 
|  | case 0: | 
|  | return emitPrint(); | 
|  | case 1: | 
|  | return emitReturn(); | 
|  | case 2: | 
|  | return emitThrow(); | 
|  | case 3: | 
|  | return emitIf1(depth); | 
|  | case 4: | 
|  | return emitIf2(depth); | 
|  | case 5: | 
|  | return emitFor(depth); | 
|  | case 6: | 
|  | return emitForIn(depth); | 
|  | case 7: | 
|  | return emitWhile(depth); | 
|  | case 8: | 
|  | return emitDoWhile(depth); | 
|  | case 9: | 
|  | return emitBreakOrContinue(depth); | 
|  | case 10: | 
|  | return emitSwitch(depth); | 
|  | case 11: | 
|  | return emitScope(depth); | 
|  | case 12: | 
|  | return emitTryCatch(depth); | 
|  | case 13: | 
|  | return emitForEach(depth); | 
|  | case 14: | 
|  | emitLibraryCall(depth, DartType.VOID, includeSemicolon: true); | 
|  | return true; | 
|  | default: | 
|  | return emitAssign(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Emit a statement (main entry). | 
|  | // Returns true if code *may* fall-through | 
|  | // (not made too advanced to avoid FE complaints). | 
|  | bool emitStatement(int depth) { | 
|  | final resetSkipStmt = processStmtOpen(); | 
|  | bool ret = emitSingleStatement(depth); | 
|  | processStmtClose(resetSkipStmt); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | // Emit statements. Returns true if code may fall-through. | 
|  | bool emitStatements(int depth) { | 
|  | int s = chooseOneUpTo(numStatements); | 
|  | for (int i = 0; i < s; i++) { | 
|  | if (!emitStatement(depth)) { | 
|  | return false; // rest would be dead code | 
|  | } | 
|  | if (i + 1 != s) { | 
|  | emitNewline(); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // | 
|  | // Expressions. | 
|  | // | 
|  |  | 
|  | void emitBool() => emit(coinFlip() ? 'true' : 'false'); | 
|  |  | 
|  | void emitNull() => emit("null"); | 
|  |  | 
|  | void emitSmallPositiveInt({int limit = 50, bool includeSemicolon = false}) { | 
|  | emit('${choose(limit)}'); | 
|  | if (includeSemicolon) { | 
|  | emit(';'); | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitSmallNegativeInt() => emit('-${choose(100)}'); | 
|  |  | 
|  | void emitInt() { | 
|  | switch (choose(7)) { | 
|  | // Favors small ints. | 
|  | case 0: | 
|  | case 1: | 
|  | case 2: | 
|  | emitSmallPositiveInt(); | 
|  | break; | 
|  | case 3: | 
|  | case 4: | 
|  | case 5: | 
|  | emitSmallNegativeInt(); | 
|  | break; | 
|  | default: | 
|  | emit('${oneOf(interestingIntegers)}'); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitDouble() => emit('${uniform()}'); | 
|  |  | 
|  | void emitNum({bool smallPositiveValue = false}) { | 
|  | if (!fp || coinFlip()) { | 
|  | if (smallPositiveValue) { | 
|  | emitSmallPositiveInt(); | 
|  | } else { | 
|  | emitInt(); | 
|  | } | 
|  | } else { | 
|  | emitDouble(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitChar() { | 
|  | switch (choose(10)) { | 
|  | // Favors regular char. | 
|  | case 0: | 
|  | emit(oneOf(interestingChars)); | 
|  | break; | 
|  | default: | 
|  | emit(regularChars[choose(regularChars.length)]); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitString({int length = 8}) { | 
|  | final n = choose(length); | 
|  | emit("'"); | 
|  | for (int i = 0; i < n; i++) { | 
|  | emitChar(); | 
|  | } | 
|  | emit("'"); | 
|  | } | 
|  |  | 
|  | void emitElementExpr(int depth, DartType tp, | 
|  | {RhsFilter rhsFilter, bool isConst = false}) { | 
|  | // Inside a constant collection, keep collection elements constants too. | 
|  | if (isConst) { | 
|  | if (DartType.isCollectionType(tp)) { | 
|  | emitConstCollection(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | return; | 
|  | } else if (tp == DartType.DOUBLE) { | 
|  | // Dart does not like floating-point elements. A key would, for | 
|  | // example, yield "The key does not have a primitive operator '=='". | 
|  | emit('null'); | 
|  | return; | 
|  | } | 
|  | } | 
|  | // Use literals outside a class or inside a constant collection. | 
|  | if (currentMethodIndex == null || isConst) { | 
|  | emitLiteral(depth, tp, rhsFilter: rhsFilter); | 
|  | } else { | 
|  | emitExpr(depth, tp, rhsFilter: rhsFilter); | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitElement(int depth, DartType tp, | 
|  | {RhsFilter rhsFilter, bool isConst = false}) { | 
|  | // Get the element type contained in type tp. | 
|  | // E.g. element type of List<String> is String. | 
|  | final elementType = dartType.elementType(tp); | 
|  | // Decide whether we need to generate Map or List/Set type elements. | 
|  | if (DartType.isMapType(tp)) { | 
|  | // Emit construct for the map key type. | 
|  | final indexType = dartType.indexType(tp); | 
|  | emitIndentation(); | 
|  | emitElementExpr(depth, indexType, rhsFilter: rhsFilter, isConst: isConst); | 
|  | emit(' : '); | 
|  | // Emit construct for the map value type. | 
|  | emitElementExpr(depth, elementType, | 
|  | rhsFilter: rhsFilter, isConst: isConst); | 
|  | } else { | 
|  | // List and Set types. | 
|  | emitElementExpr(depth, elementType, | 
|  | rhsFilter: rhsFilter, isConst: isConst); | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitCollectionElement(int depth, DartType tp, {RhsFilter rhsFilter}) { | 
|  | int r = (depth <= exprDepth) ? choose(10) : 10; | 
|  | // TODO (felih): disable complex collection constructs for new types for | 
|  | // now. | 
|  | if (!{DartType.MAP_INT_STRING, DartType.LIST_INT, DartType.SET_INT} | 
|  | .contains(tp)) { | 
|  | emitElement(depth, tp, rhsFilter: rhsFilter); | 
|  | return; | 
|  | } | 
|  | switch (r) { | 
|  | // Favors elements over control-flow collections. | 
|  | case 0: | 
|  | emitLn('...', newline: false); // spread | 
|  | emitCollection(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | case 1: | 
|  | emitLn('if ', newline: false); | 
|  | emitParenWrapped(() => | 
|  | emitElementExpr(depth + 1, DartType.BOOL, rhsFilter: rhsFilter)); | 
|  | emitCollectionElement(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | if (coinFlip()) { | 
|  | emitNewline(); | 
|  | emitLn('else ', newline: false); | 
|  | emitCollectionElement(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | } | 
|  | break; | 
|  | case 2: | 
|  | { | 
|  | final int i = localVars.length; | 
|  | // TODO (felih): update to use new type system. Add types like | 
|  | // LIST_STRING etc. | 
|  | emitLn('for ', newline: false); | 
|  | emitParenWrapped(() { | 
|  | final local = '$localName$i'; | 
|  | iterVars.add(local); | 
|  | // For-loop (induction, list, set). | 
|  | localVars.add(null); // declared, but don't use | 
|  | switch (choose(3)) { | 
|  | case 0: | 
|  | emitVariableDeclaration(local, DartType.INT, | 
|  | initializerEmitter: emitZero, indent: false); | 
|  | emitBinaryComparison(() => emit(local), '<', | 
|  | () => emitSmallPositiveInt(limit: 16), | 
|  | includeSemicolon: true); | 
|  | emit('$local++'); | 
|  | break; | 
|  | case 1: | 
|  | emitVariableDeclaration(local, DartType.INT, | 
|  | includeSemicolon: false, indent: false); | 
|  | emit(' in '); | 
|  | emitCollection(depth + 1, DartType.LIST_INT, | 
|  | rhsFilter: rhsFilter); | 
|  | break; | 
|  | default: | 
|  | emitVariableDeclaration(local, DartType.INT, | 
|  | includeSemicolon: false, indent: false); | 
|  | emit(' in '); | 
|  | emitCollection(depth + 1, DartType.SET_INT, | 
|  | rhsFilter: rhsFilter); | 
|  | break; | 
|  | } | 
|  | localVars.removeLast(); // will get type | 
|  | }); | 
|  | nest++; | 
|  | localVars.add(DartType.INT); | 
|  | emitNewline(); | 
|  | emitCollectionElement(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | localVars.removeLast(); | 
|  | iterVars.removeLast(); | 
|  | nest--; | 
|  | break; | 
|  | } | 
|  | default: | 
|  | emitElement(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitCollection(int depth, DartType tp, {RhsFilter rhsFilter}) => | 
|  | emitWrapped(DartType.isListType(tp) ? const ['[', ']'] : const ['{', '}'], | 
|  | () { | 
|  | // Collection length decreases as depth increases. | 
|  | int collectionLength = max(1, 8 - depth); | 
|  | emitCommaSeparated( | 
|  | (int _) => emitCollectionElement(depth, tp, rhsFilter: rhsFilter), | 
|  | chooseOneUpTo(collectionLength), | 
|  | newline: DartType.isMapType(tp)); | 
|  | }, shouldIndent: DartType.isMapType(tp)); | 
|  |  | 
|  | void emitConstCollection(int depth, DartType tp, {RhsFilter rhsFilter}) => | 
|  | emitWrapped( | 
|  | DartType.isListType(tp) | 
|  | ? const ['const [', ']'] | 
|  | : const ['const {', '}'], | 
|  | () => emitElement(depth, tp, rhsFilter: rhsFilter, isConst: true), | 
|  | shouldIndent: DartType.isMapType(tp)); | 
|  |  | 
|  | void emitLiteral(int depth, DartType tp, | 
|  | {bool smallPositiveValue = false, RhsFilter rhsFilter}) { | 
|  | // Randomly specialize interface if possible. E.g. num to int. | 
|  | tp = maybeSpecializeInterface(tp); | 
|  | if (tp == DartType.NULL) { | 
|  | emitNull(); | 
|  | } else if (tp == DartType.BOOL) { | 
|  | emitBool(); | 
|  | } else if (tp == DartType.INT) { | 
|  | if (smallPositiveValue) { | 
|  | emitSmallPositiveInt(); | 
|  | } else { | 
|  | emitInt(); | 
|  | } | 
|  | } else if (tp == DartType.DOUBLE) { | 
|  | emitDouble(); | 
|  | } else if (tp == DartType.NUM) { | 
|  | emitNum(smallPositiveValue: smallPositiveValue); | 
|  | } else if (tp == DartType.STRING) { | 
|  | emitString(); | 
|  | } else if (tp == DartType.ENDIAN) { | 
|  | // The Endian type doesn't have a public constructor, so we emit a | 
|  | // library call instead. | 
|  | emitLibraryCall(depth, tp); | 
|  | } else if (dartType.constructors(tp).isNotEmpty) { | 
|  | // Constructors serve as literals for non trivially constructable types. | 
|  | // Important note: We have to test for existence of a non trivial | 
|  | // constructor before testing for list type assiciation, since some types | 
|  | // like ListInt32 are of list type but can not be constructed | 
|  | // from a literal. | 
|  | emitConstructorOrLiteral(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | } else if (DartType.isCollectionType(tp)) { | 
|  | final resetExprStmt = processExprOpen(tp); | 
|  | emitCollection(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | processExprClose(resetExprStmt); | 
|  | } else { | 
|  | throw 'Can not emit literal for type ${tp.name}'; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Emit a constructor for a type, this can either be a trivial constructor | 
|  | // (i.e. parsed from a literal) or an actual function invocation. | 
|  | void emitConstructorOrLiteral(int depth, DartType tp, | 
|  | {RhsFilter rhsFilter, bool includeSemicolon = false}) { | 
|  | // If there is at least one non trivial constructor for the type tp | 
|  | // select one of these constructors. | 
|  | if (dartType.hasConstructor(tp)) { | 
|  | String constructor = oneOfSet(dartType.constructors(tp)); | 
|  | // There are two types of constructors, named constructors and 'empty' | 
|  | // constructors (e.g. X.fromList(...) and new X(...) respectively). | 
|  | // Empty constructors are invoked with new + type name, non-empty | 
|  | // constructors are static functions of the type. | 
|  | if (constructor.isNotEmpty) { | 
|  | emit('${tp.name}.${constructor}'); | 
|  | } else { | 
|  | // New is no longer necessary as of Dart 2, but is still supported. | 
|  | // Emit a `new` once in a while to ensure it's covered. | 
|  | emit('${rollDice(10) ? "new " : ""}${tp.name}'); | 
|  | } | 
|  | emitParenWrapped(() { | 
|  | // Iterate over constructor parameters. | 
|  | List<DartType> constructorParameters = | 
|  | dartType.constructorParameters(tp, constructor); | 
|  | if (constructorParameters == null) { | 
|  | throw 'No constructor parameters for ${tp.name}.$constructor'; | 
|  | } | 
|  | emitCommaSeparated((int i) { | 
|  | // If we are emitting a constructor parameter, we want to use small | 
|  | // values to avoid programs that run out of memory. | 
|  | // TODO (felih): maybe allow occasionally? | 
|  | emitLiteral(depth + 1, constructorParameters[i], | 
|  | smallPositiveValue: true, rhsFilter: rhsFilter); | 
|  | }, constructorParameters.length); | 
|  | }); | 
|  | } else { | 
|  | // Otherwise type can be constructed from a literal. | 
|  | emitLiteral(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | } | 
|  | if (includeSemicolon) { | 
|  | emit(';'); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Pick an existing variable of the given type. | 
|  | String pickScalarVar(DartType tp, {bool isLhs = false, RhsFilter rhsFilter}) { | 
|  | // Randomly specialize interface type, unless emitting left hand side. | 
|  | if (!isLhs) tp = maybeSpecializeInterface(tp); | 
|  | // Collect all choices from globals, fields, locals, and parameters. | 
|  | Set<String> choices = <String>{}; | 
|  | for (int i = 0; i < globalVars.length; i++) { | 
|  | if (tp == globalVars[i]) choices.add('$varName$i'); | 
|  | } | 
|  | for (int i = 0; i < localVars.length; i++) { | 
|  | if (tp == localVars[i]) choices.add('$localName$i'); | 
|  | } | 
|  | List<DartType> fields = getCurrentFields(); | 
|  | if (fields != null) { | 
|  | for (int i = 0; i < fields.length; i++) { | 
|  | if (tp == fields[i]) choices.add('$fieldName${currentClassIndex}_$i'); | 
|  | } | 
|  | } | 
|  | List<DartType> proto = getCurrentProto(); | 
|  | if (proto != null) { | 
|  | // If recursion is allowed on the current method, the last parameter is | 
|  | // the recursion depth parameter. We should avoid reassigning the | 
|  | // recursion depth parameter to prevent infinite recursion. | 
|  | final int endIndex = | 
|  | currentMethod.recursionAllowed ? proto.length - 1 : proto.length; | 
|  | for (int i = 1; i < endIndex; i++) { | 
|  | if (tp == proto[i]) choices.add('$paramName$i'); | 
|  | } | 
|  | } | 
|  | // Make modification of the iteration variable from the loop | 
|  | // body less likely. | 
|  | if (isLhs) { | 
|  | if (!rollDice(100)) { | 
|  | Set<String> cleanChoices = choices.difference(Set.from(iterVars)); | 
|  | if (cleanChoices.isNotEmpty) { | 
|  | choices = cleanChoices; | 
|  | } | 
|  | } | 
|  | } | 
|  | // Filter out the current lhs of the expression to avoid recursive | 
|  | // assignments of the form x = x * x. | 
|  | if (rhsFilter != null && rhsFilter.shouldFilter) { | 
|  | Set<String> cleanChoices = choices.difference({rhsFilter.lhsVar}); | 
|  | // If we have other choices of variables, use those. | 
|  | if (cleanChoices.isNotEmpty) { | 
|  | choices = cleanChoices; | 
|  | } else if (!isLhs) { | 
|  | // If we are emitting an rhs variable, we can emit a terminal. | 
|  | // note that if the variable type is a collection, this might | 
|  | // still result in a recursion. | 
|  | emitLiteral(0, tp); | 
|  | return null; | 
|  | } | 
|  | // Otherwise we have to risk creating a recursion. | 
|  | } | 
|  | // Then pick one. | 
|  | if (choices.isEmpty) { | 
|  | throw 'No variable to emit for type ${tp.name}'; | 
|  | } | 
|  |  | 
|  | return '${choices.elementAt(choose(choices.length))}'; | 
|  | } | 
|  |  | 
|  | String emitScalarVar(DartType tp, {bool isLhs = false, RhsFilter rhsFilter}) { | 
|  | final emittedVar = pickScalarVar(tp, isLhs: isLhs, rhsFilter: rhsFilter); | 
|  | if (emittedVar != null) { | 
|  | if (rhsFilter != null && (emittedVar == rhsFilter.lhsVar)) { | 
|  | rhsFilter.consume(); | 
|  | } | 
|  | emit(emittedVar); | 
|  | } | 
|  | return emittedVar; | 
|  | } | 
|  |  | 
|  | String emitSubscriptedVar(int depth, DartType tp, | 
|  | {bool isLhs = false, RhsFilter rhsFilter}) { | 
|  | String ret; | 
|  | // Check if type tp is an indexable element of some other type. | 
|  | if (dartType.isIndexableElementType(tp)) { | 
|  | // Select a list or map type that contains elements of type tp. | 
|  | final iterType = oneOfSet(dartType.indexableElementTypes(tp)); | 
|  | // Get the index type for the respective list or map type. | 
|  | final indexType = dartType.indexType(iterType); | 
|  | // Emit a variable of the selected list or map type. | 
|  | ret = emitScalarVar(iterType, isLhs: isLhs, rhsFilter: rhsFilter); | 
|  | emitSquareBraceWrapped(() => | 
|  | // Emit an expression resolving into the index type. For | 
|  | // collection type, only constant collections are used | 
|  | // to avoid rehashing the same value into many entries. | 
|  | DartType.isCollectionType(indexType) | 
|  | ? emitConstCollection(depth + 1, indexType) | 
|  | : emitExpr(depth + 1, indexType)); | 
|  | } else { | 
|  | ret = emitScalarVar(tp, | 
|  | isLhs: isLhs, rhsFilter: rhsFilter); // resort to scalar | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | String emitVar(int depth, DartType tp, | 
|  | {bool isLhs = false, RhsFilter rhsFilter}) { | 
|  | switch (choose(2)) { | 
|  | case 0: | 
|  | return emitScalarVar(tp, isLhs: isLhs, rhsFilter: rhsFilter); | 
|  | break; | 
|  | default: | 
|  | return emitSubscriptedVar(depth, tp, | 
|  | isLhs: isLhs, rhsFilter: rhsFilter); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitTerminal(int depth, DartType tp, {RhsFilter rhsFilter}) { | 
|  | switch (choose(2)) { | 
|  | case 0: | 
|  | emitLiteral(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | default: | 
|  | emitVar(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Emit expression with unary operator: (~(x)) | 
|  | void emitUnaryExpr(int depth, DartType tp, {RhsFilter rhsFilter}) { | 
|  | if (dartType.uniOps(tp).isEmpty) { | 
|  | return emitTerminal(depth, tp, rhsFilter: rhsFilter); | 
|  | } | 
|  | emitParenWrapped(() { | 
|  | emit(oneOfSet(dartType.uniOps(tp))); | 
|  | emitParenWrapped(() => emitExpr(depth + 1, tp, rhsFilter: rhsFilter)); | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Emit expression with binary operator: (x + y) | 
|  | void emitBinaryExpr(int depth, DartType tp, {RhsFilter rhsFilter}) { | 
|  | if (dartType.binOps(tp).isEmpty) { | 
|  | return emitLiteral(depth, tp); | 
|  | } | 
|  |  | 
|  | String binop = oneOfSet(dartType.binOps(tp)); | 
|  | List<DartType> binOpParams = oneOfSet(dartType.binOpParameters(tp, binop)); | 
|  |  | 
|  | // Avoid recursive assignments of the form a = a * 100. | 
|  | if (binop == "*") { | 
|  | rhsFilter?.consume(); | 
|  | } | 
|  |  | 
|  | // Reduce the number of operations of type growable * large value | 
|  | // as these might lead to timeouts and/or oom errors. | 
|  | if (binop == "*" && | 
|  | DartType.isGrowableType(binOpParams[0]) && | 
|  | dartType.isInterfaceOfType(binOpParams[1], DartType.NUM)) { | 
|  | emitParenWrapped(() { | 
|  | emitExpr(depth + 1, binOpParams[0], rhsFilter: rhsFilter); | 
|  | emit(' $binop '); | 
|  | emitLiteral(depth + 1, binOpParams[1], smallPositiveValue: true); | 
|  | }); | 
|  | } else if (binop == "*" && | 
|  | DartType.isGrowableType(binOpParams[1]) && | 
|  | dartType.isInterfaceOfType(binOpParams[0], DartType.NUM)) { | 
|  | emitParenWrapped(() { | 
|  | emitLiteral(depth + 1, binOpParams[0], smallPositiveValue: true); | 
|  | emit(' $binop '); | 
|  | emitExpr(depth + 1, binOpParams[1], rhsFilter: rhsFilter); | 
|  | }); | 
|  | } else { | 
|  | emitParenWrapped(() { | 
|  | emitExpr(depth + 1, binOpParams[0], rhsFilter: rhsFilter); | 
|  | emit(' $binop '); | 
|  | emitExpr(depth + 1, binOpParams[1], rhsFilter: rhsFilter); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Emit expression with ternary operator: (b ? x : y) | 
|  | void emitTernaryExpr(int depth, DartType tp, {RhsFilter rhsFilter}) => | 
|  | emitParenWrapped(() { | 
|  | emitExpr(depth + 1, DartType.BOOL, rhsFilter: rhsFilter); | 
|  | emit(' ? '); | 
|  | emitExpr(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | emit(' : '); | 
|  | emitExpr(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | }); | 
|  |  | 
|  | // Emit expression with pre/post-increment/decrement operator: (x++) | 
|  | void emitPreOrPostExpr(int depth, DartType tp, {RhsFilter rhsFilter}) { | 
|  | if (tp == DartType.INT) { | 
|  | emitParenWrapped(() { | 
|  | bool pre = coinFlip(); | 
|  | if (pre) { | 
|  | emitPreOrPostOp(tp); | 
|  | } | 
|  | emitScalarVar(tp, isLhs: true); | 
|  | if (!pre) { | 
|  | emitPreOrPostOp(tp); | 
|  | } | 
|  | }); | 
|  | } else { | 
|  | emitTerminal(depth, tp, rhsFilter: rhsFilter); // resort to terminal | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isTypedDataFloatType(List<DartType> proto) { | 
|  | for (int i = 0; i < proto.length; ++i) { | 
|  | if (DartLib.typedDataFloatTypes.contains(proto[i])) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Emit library call. | 
|  | void emitLibraryCall(int depth, DartType tp, | 
|  | {RhsFilter rhsFilter, bool includeSemicolon = false}) { | 
|  | DartLib lib = getLibraryMethod(tp); | 
|  | if (lib == null || (!fp && isTypedDataFloatType(lib.proto))) { | 
|  | // We cannot find a library method, or we found a library method but its | 
|  | // prototype has a floating point type, and fp is disabled. This can only | 
|  | // happen for non-void types. In those cases, we resort to a literal. | 
|  | assert(tp != DartType.VOID); | 
|  | emitLiteral(depth + 1, tp, rhsFilter: rhsFilter); | 
|  | return; | 
|  | } | 
|  | emitParenWrapped(() { | 
|  | List<DartType> prototype = lib.proto; | 
|  | // Receiver. | 
|  | if (prototype[0] != DartType.VOID) { | 
|  | emitParenWrapped(() => emitArg( | 
|  | depth + 1, prototype[0], lib.getRestriction(0), | 
|  | rhsFilter: rhsFilter)); | 
|  | emit('.'); | 
|  | } | 
|  | // Call. | 
|  | emit('${lib.name}'); | 
|  | // Parameters. | 
|  | if (lib.isMethod) { | 
|  | emitParenWrapped(() { | 
|  | if (prototype[1] != DartType.VOID) { | 
|  | emitCommaSeparated( | 
|  | (int i) => emitArg( | 
|  | depth + 1, prototype[i], lib.getRestriction(i), | 
|  | rhsFilter: rhsFilter), | 
|  | prototype.length, | 
|  | start: 1); | 
|  | } | 
|  | }); | 
|  | } | 
|  | // Add cast to avoid error of double or int being interpreted as num. | 
|  | if (dartType.isInterfaceOfType(tp, DartType.NUM)) { | 
|  | emit(' as ${tp.name}'); | 
|  | } | 
|  | }); | 
|  | if (includeSemicolon) { | 
|  | emit(';'); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Helper for a method call. | 
|  | bool pickedCall(int depth, DartType tp, List<Method> methods, int m, | 
|  | {RhsFilter rhsFilter}) { | 
|  | for (int i = m - 1; i >= 0; i--) { | 
|  | if (tp == methods[i].returnType) { | 
|  | bool isRecursiveCall = (currentMethod == methods[i]); | 
|  | if (isRecursiveCall && !methods[i].recursionAllowed) { | 
|  | continue; | 
|  | } | 
|  | methods[i].emitCall(depth + 1, | 
|  | rhsFilter: rhsFilter, isRecursiveCall: isRecursiveCall); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Emit method call within the program. | 
|  | void emitMethodCall(int depth, DartType tp, {RhsFilter rhsFilter}) { | 
|  | // Only call an already emitted method or the current method to avoid mutual recursion. | 
|  | if (currentClassIndex == null) { | 
|  | // Outside a class but inside a method: call backward in global methods. | 
|  | if (currentMethodIndex != null && | 
|  | pickedCall(depth, tp, globalMethods, currentMethodIndex + 1, | 
|  | rhsFilter: rhsFilter)) { | 
|  | return; | 
|  | } | 
|  | } else { | 
|  | int classIndex = currentClassIndex; | 
|  | // Chase randomly up in class hierarchy. | 
|  | while (classParents[classIndex] > 0) { | 
|  | if (coinFlip()) { | 
|  | break; | 
|  | } | 
|  | classIndex = classParents[classIndex]; | 
|  | } | 
|  | int m1 = 0; | 
|  | // Inside a class: try to call backwards into current or parent class | 
|  | // methods first. | 
|  | if (currentMethodIndex == null || classIndex != currentClassIndex) { | 
|  | // If currently emitting the 'run' method or calling into a parent class | 
|  | // pick any of the current or parent class methods respectively. | 
|  | m1 = classMethods[classIndex].length; | 
|  | } else { | 
|  | // If calling into the current class from any method other than 'run' | 
|  | // pick one of the already emitted methods or the current method. | 
|  | // We allow simple recursion but avoid mutual recursion. | 
|  | // (to avoid infinite recursions). | 
|  | m1 = currentMethodIndex + 1; | 
|  | } | 
|  | final int m2 = globalMethods.length; | 
|  | if (pickedCall(depth, tp, classMethods[classIndex], m1, | 
|  | rhsFilter: rhsFilter) || | 
|  | pickedCall(depth, tp, globalMethods, m2, rhsFilter: rhsFilter)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  | emitTerminal(depth, tp, rhsFilter: rhsFilter); // resort to terminal. | 
|  | } | 
|  |  | 
|  | // Emit expression. | 
|  | void emitExpr(int depth, DartType tp, | 
|  | {RhsFilter rhsFilter, bool includeSemicolon = false}) { | 
|  | final resetExprStmt = processExprOpen(tp); | 
|  | // Continuing nested expressions becomes less likely as the depth grows. | 
|  | if (choose(depth + 1) > exprDepth) { | 
|  | emitTerminal(depth, tp, rhsFilter: rhsFilter); | 
|  | } else { | 
|  | // Possibly nested expression. | 
|  | switch (choose(7)) { | 
|  | case 0: | 
|  | emitUnaryExpr(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | case 1: | 
|  | emitBinaryExpr(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | case 2: | 
|  | emitTernaryExpr(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | case 3: | 
|  | emitPreOrPostExpr(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | case 4: | 
|  | emitLibraryCall(depth, tp, | 
|  | rhsFilter: RhsFilter.cloneEmpty(rhsFilter)); | 
|  | break; | 
|  | case 5: | 
|  | emitMethodCall(depth, tp, rhsFilter: RhsFilter.cloneEmpty(rhsFilter)); | 
|  | break; | 
|  | default: | 
|  | emitTerminal(depth, tp, rhsFilter: rhsFilter); | 
|  | break; | 
|  | } | 
|  | } | 
|  | processExprClose(resetExprStmt); | 
|  | if (includeSemicolon) { | 
|  | emit(';'); | 
|  | } | 
|  | } | 
|  |  | 
|  | // | 
|  | // Operators. | 
|  | // | 
|  |  | 
|  | // Emit same type in-out increment operator. | 
|  | void emitPreOrPostOp(DartType tp) { | 
|  | assert(tp == DartType.INT); | 
|  | emit(oneOf(const <String>['++', '--'])); | 
|  | } | 
|  |  | 
|  | // Emit one type in, boolean out operator. | 
|  | void emitRelOp(DartType tp) { | 
|  | if (tp == DartType.INT || tp == DartType.DOUBLE) { | 
|  | emit(oneOf(const <String>[' > ', ' >= ', ' < ', ' <= ', ' != ', ' == '])); | 
|  | } else { | 
|  | emit(oneOf(const <String>[' != ', ' == '])); | 
|  | } | 
|  | } | 
|  |  | 
|  | // | 
|  | // Library methods. | 
|  | // | 
|  |  | 
|  | // Get a library method that returns given type. | 
|  | DartLib getLibraryMethod(DartType tp) => | 
|  | api.typeToLibraryMethods.containsKey(tp) | 
|  | ? oneOf(api.typeToLibraryMethods[tp]) | 
|  | : null; | 
|  |  | 
|  | // Emit a library argument, possibly subject to restrictions. | 
|  | void emitArg(int depth, DartType type, Restriction restriction, | 
|  | {RhsFilter rhsFilter}) { | 
|  | if ((type == DartType.INT) && (restriction == Restriction.small)) { | 
|  | emitSmallPositiveInt(); | 
|  | } else if ((type == DartType.DOUBLE) && !fp) { | 
|  | // resort to INT if floating point is disabled. | 
|  | emitExpr(depth, DartType.INT, rhsFilter: rhsFilter); | 
|  | } else if ((type == DartType.STRING) && | 
|  | (restriction == Restriction.small)) { | 
|  | emitString(length: 2); | 
|  | } else { | 
|  | emitExpr(depth, type, rhsFilter: rhsFilter); | 
|  | } | 
|  | } | 
|  |  | 
|  | // | 
|  | // Types. | 
|  | // | 
|  |  | 
|  | List<DartType> fillTypes1({int limit = 4, bool isFfi = false}) { | 
|  | final list = <DartType>[]; | 
|  | for (int i = 0, n = chooseOneUpTo(limit); i < n; i++) { | 
|  | if (isFfi) { | 
|  | list.add(fp ? oneOf([DartType.INT, DartType.DOUBLE]) : DartType.INT); | 
|  | } else { | 
|  | list.add(oneOfSet(dartType.allTypes)); | 
|  | } | 
|  | } | 
|  | return list; | 
|  | } | 
|  |  | 
|  | List<List<DartType>> fillTypes2( | 
|  | {bool isFfi = false, int limit2 = 4, int limit1 = 4}) { | 
|  | final list = <List<DartType>>[]; | 
|  | for (int i = 0, n = chooseOneUpTo(limit2); i < n; i++) { | 
|  | list.add(fillTypes1(limit: limit1, isFfi: isFfi)); | 
|  | } | 
|  | return list; | 
|  | } | 
|  |  | 
|  | List<DartType> getCurrentProto() { | 
|  | if (currentClassIndex != null) { | 
|  | if (currentMethodIndex != null) { | 
|  | return classMethods[currentClassIndex][currentMethodIndex].parameters; | 
|  | } | 
|  | } else if (currentMethodIndex != null) { | 
|  | return globalMethods[currentMethodIndex].parameters; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | List<DartType> getCurrentFields() { | 
|  | if (currentClassIndex != null) { | 
|  | return classFields[currentClassIndex]; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | void emitFfiType(DartType tp) { | 
|  | if (tp == DartType.INT) { | 
|  | emit(oneOf( | 
|  | const <String>['ffi.Int8', 'ffi.Int16', 'ffi.Int32', 'ffi.Int64'])); | 
|  | } else if (tp == DartType.DOUBLE) { | 
|  | emit(oneOf(const <String>['ffi.Float', 'ffi.Double'])); | 
|  | } else { | 
|  | throw 'Invalid FFI type ${tp.name}'; | 
|  | } | 
|  | } | 
|  |  | 
|  | void emitFfiTypedef(Method method) { | 
|  | List<DartType> pars = method.parameters; | 
|  | emit("typedef ${method.name}Type = "); | 
|  | emitFfiType(pars[0]); | 
|  | emit(' Function'); | 
|  | emitParenWrapped( | 
|  | () => emitCommaSeparated((int i) => emitFfiType(pars[i]), pars.length, | 
|  | start: 1), | 
|  | includeSemicolon: true); | 
|  | emitNewline(); | 
|  | emitNewline(); | 
|  | } | 
|  |  | 
|  | // | 
|  | // Output. | 
|  | // | 
|  |  | 
|  | // Emits a newline to the program. | 
|  | void emitNewline() => emit('', newline: true); | 
|  |  | 
|  | // Emits indentation based on the current indentation count. | 
|  | void emitIndentation() => file.writeStringSync(' ' * indent); | 
|  |  | 
|  | // Emits indented line to append to program. | 
|  | void emitLn(String line, {bool newline = true}) { | 
|  | emitIndentation(); | 
|  | emit(line, newline: newline); | 
|  | } | 
|  |  | 
|  | // Emits text to append to program. | 
|  | void emit(String txt, {bool newline = false}) { | 
|  | if (skipStmt || skipExpr) { | 
|  | return; | 
|  | } | 
|  | file.writeStringSync(txt); | 
|  | if (newline) { | 
|  | file.writeStringSync('\n'); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Special return code to handle oom errors. | 
|  | static const oomExitCode = 254; | 
|  |  | 
|  | // Random seed used to generate program. | 
|  | final int seed; | 
|  |  | 
|  | // Enables floating-point operations. | 
|  | final bool fp; | 
|  |  | 
|  | // Enables FFI method calls. | 
|  | final bool ffi; | 
|  |  | 
|  | // Disables nested types. | 
|  | final bool flatTp; | 
|  |  | 
|  | // File used for output. | 
|  | final RandomAccessFile file; | 
|  |  | 
|  | // Library and ffi api. | 
|  | DartApi api; | 
|  |  | 
|  | // Program variables. | 
|  | Random rand; | 
|  | int indent; | 
|  | int nest; | 
|  | int currentClassIndex; | 
|  | Method currentMethod; | 
|  | int currentMethodIndex; | 
|  |  | 
|  | // DartType instance. | 
|  | DartType dartType; | 
|  |  | 
|  | // Types of local variables currently in scope. | 
|  | List<DartType> localVars; | 
|  |  | 
|  | // Types of global variables. | 
|  | List<DartType> globalVars; | 
|  |  | 
|  | // Names of currently active iterator variables. | 
|  | // These are tracked to avoid modifications within the loop body, | 
|  | // which can lead to infinite loops. | 
|  | List<String> iterVars; | 
|  |  | 
|  | // List of all global methods. | 
|  | List<Method> globalMethods; | 
|  |  | 
|  | // Types of fields over all classes. | 
|  | List<List<DartType>> classFields; | 
|  |  | 
|  | // List of methods over all classes. | 
|  | List<List<Method>> classMethods; | 
|  |  | 
|  | // List of virtual functions per class. Map is from parent class index to List | 
|  | // of overloaded functions from that parent. | 
|  | List<Map<int, List<int>>> virtualClassMethods; | 
|  |  | 
|  | // Parent class indices for all classes. | 
|  | List<int> classParents; | 
|  |  | 
|  | // Minimization mode extensions. | 
|  | final bool minimize; | 
|  | BigInt smask; | 
|  | BigInt emask; | 
|  | bool skipStmt; | 
|  | bool skipExpr; | 
|  | bool skipExprCntr; | 
|  | int stmtCntr; | 
|  | int exprCntr; | 
|  |  | 
|  | // Interesting characters. | 
|  | static const List<String> interestingChars = [ | 
|  | '\\u2665', | 
|  | '\\u{1f600}', // rune | 
|  | ]; | 
|  |  | 
|  | // Regular characters. | 
|  | static const regularChars = | 
|  | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#&()+- '; | 
|  |  | 
|  | // Interesting integer values. | 
|  | static const List<int> interestingIntegers = [ | 
|  | 0x0000000000000000, | 
|  | 0x0000000000000001, | 
|  | 0x000000007fffffff, | 
|  | 0x0000000080000000, | 
|  | 0x0000000080000001, | 
|  | 0x00000000ffffffff, | 
|  | 0x0000000100000000, | 
|  | 0x0000000100000001, | 
|  | 0x000000017fffffff, | 
|  | 0x0000000180000000, | 
|  | 0x0000000180000001, | 
|  | 0x00000001ffffffff, | 
|  | 0x7fffffff00000000, | 
|  | 0x7fffffff00000001, | 
|  | 0x7fffffff7fffffff, | 
|  | 0x7fffffff80000000, | 
|  | 0x7fffffff80000001, | 
|  | 0x7fffffffffffffff, | 
|  | 0x8000000000000000, | 
|  | 0x8000000000000001, | 
|  | 0x800000007fffffff, | 
|  | 0x8000000080000000, | 
|  | 0x8000000080000001, | 
|  | 0x80000000ffffffff, | 
|  | 0x8000000100000000, | 
|  | 0x8000000100000001, | 
|  | 0x800000017fffffff, | 
|  | 0x8000000180000000, | 
|  | 0x8000000180000001, | 
|  | 0x80000001ffffffff, | 
|  | 0xffffffff00000000, | 
|  | 0xffffffff00000001, | 
|  | 0xffffffff7fffffff, | 
|  | 0xffffffff80000000, | 
|  | 0xffffffff80000001, | 
|  | 0xffffffffffffffff | 
|  | ]; | 
|  | } | 
|  |  | 
|  | // Generate seed. By default (no user-defined nonzero seed given), | 
|  | // pick the system's best way of seeding randomness and then pick | 
|  | // a user-visible nonzero seed. | 
|  | int getSeed(String userSeed) { | 
|  | int seed = int.parse(userSeed); | 
|  | if (seed == 0) { | 
|  | final rand = Random(); | 
|  | while (seed == 0) { | 
|  | seed = rand.nextInt(1 << 32); | 
|  | } | 
|  | } | 
|  | return seed; | 
|  | } | 
|  |  | 
|  | /// Main driver when dartfuzz.dart is run stand-alone. | 
|  | main(List<String> arguments) { | 
|  | const kSeed = 'seed'; | 
|  | const kFp = 'fp'; | 
|  | const kFfi = 'ffi'; | 
|  | const kFlat = 'flat'; | 
|  | const kMini = 'mini'; | 
|  | const kSMask = 'smask'; | 
|  | const kEMask = 'emask'; | 
|  | final parser = ArgParser() | 
|  | ..addOption(kSeed, | 
|  | help: 'random seed (0 forces time-based seed)', defaultsTo: '0') | 
|  | ..addFlag(kFp, help: 'enables floating-point operations', defaultsTo: true) | 
|  | ..addFlag(kFfi, | 
|  | help: 'enables FFI method calls (default: off)', defaultsTo: false) | 
|  | ..addFlag(kFlat, | 
|  | help: 'enables flat types (default: off)', defaultsTo: false) | 
|  | // Minimization mode extensions. | 
|  | ..addFlag(kMini, | 
|  | help: 'enables minimization mode (default: off)', defaultsTo: false) | 
|  | ..addOption(kSMask, | 
|  | help: 'Bitmask indicating which statements to omit' | 
|  | '(Bit=1 omits)', | 
|  | defaultsTo: '0') | 
|  | ..addOption(kEMask, | 
|  | help: 'Bitmask indicating which expressions to omit' | 
|  | '(Bit=1 omits)', | 
|  | defaultsTo: '0'); | 
|  | try { | 
|  | final results = parser.parse(arguments); | 
|  | final seed = getSeed(results[kSeed]); | 
|  | final fp = results[kFp]; | 
|  | final ffi = results[kFfi]; | 
|  | final flatTp = results[kFlat]; | 
|  | final file = File(results.rest.single).openSync(mode: FileMode.write); | 
|  | final minimize = results[kMini]; | 
|  | final smask = BigInt.parse(results[kSMask]); | 
|  | final emask = BigInt.parse(results[kEMask]); | 
|  | final dartFuzz = DartFuzz(seed, fp, ffi, flatTp, file, | 
|  | minimize: minimize, smask: smask, emask: emask); | 
|  | dartFuzz.run(); | 
|  | file.closeSync(); | 
|  | // Print information that will be parsed by minimize.py | 
|  | if (minimize) { | 
|  | // Updated statement mask. | 
|  | // This might be different from the input parameter --smask | 
|  | // since masking a statement that contains nested statements leads to | 
|  | // those being masked as well. | 
|  | print(dartFuzz.smask.toRadixString(10)); | 
|  | // Total number of statements in the generated program. | 
|  | print(dartFuzz.stmtCntr); | 
|  | // Updated expression mask. | 
|  | // This might be different from the input parameter --emask | 
|  | // since masking a statement that contains expressions or | 
|  | // an expression that contains nested expressions leads to | 
|  | // those being masked as well. | 
|  | print(dartFuzz.emask.toRadixString(10)); | 
|  | // Total number of expressions in the generated program. | 
|  | print(dartFuzz.exprCntr); | 
|  | } | 
|  | } catch (e) { | 
|  | print('Usage: dart dartfuzz.dart [OPTIONS] FILENAME\n${parser.usage}\n$e'); | 
|  | exitCode = 255; | 
|  | } | 
|  | } |