| 9 | |
| 10 | /* Generates random strings that are syntactically valid JavaScript */ |
| 11 | public class JavaScriptCodeGenerator extends Generator<String> { |
| 12 | public JavaScriptCodeGenerator() { |
| 13 | super(String.class); // Register type of generated object |
| 14 | } |
| 15 | |
| 16 | private GenerationStatus status; // saved state object when generating |
| 17 | private static final int MAX_IDENTIFIERS = 100; |
| 18 | private static final int MAX_EXPRESSION_DEPTH = 10; |
| 19 | private static final int MAX_STATEMENT_DEPTH = 6; |
| 20 | private static Set<String> identifiers; // Stores generated IDs, to promote re-use |
| 21 | private int statementDepth; // Keeps track of how deep the AST is at any point |
| 22 | private int expressionDepth; // Keeps track of how nested an expression is at any point |
| 23 | |
| 24 | private static final String[] UNARY_TOKENS = { |
| 25 | "!", "++", "--", "~", |
| 26 | "delete", "new", "typeof" |
| 27 | }; |
| 28 | |
| 29 | private static final String[] BINARY_TOKENS = { |
| 30 | "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+", "+=", ",", |
| 31 | "-", "-=", "/", "/=", "<", "<<", ">>=", "<=", "=", "==", "===", |
| 32 | ">", ">=", ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||", |
| 33 | "in", "instanceof" |
| 34 | }; |
| 35 | |
| 36 | /** Main entry point. Called once per test case. Returns a random JS program. */ |
| 37 | @Override |
| 38 | public String generate(SourceOfRandomness random, GenerationStatus status) { |
| 39 | this.status = status; // we save this so that we can pass it on to other generators |
| 40 | this.identifiers = new HashSet<>(); |
| 41 | this.statementDepth = 0; |
| 42 | this.expressionDepth = 0; |
| 43 | return generateStatement(random).toString(); |
| 44 | } |
| 45 | |
| 46 | /** Utility method for generating a random list of items (e.g. statements, arguments, attributes) */ |
| 47 | private static List<String> generateItems(Function<SourceOfRandomness, String> genMethod, SourceOfRandomness random, |
| 48 | int mean) { |
| 49 | int len = random.nextInt(mean*2); // Generate random number in [0, mean*2) |
| 50 | List<String> items = new ArrayList<>(len); |
| 51 | for (int i = 0; i < len; i++) { |
| 52 | items.add(genMethod.apply(random)); |
| 53 | } |
| 54 | return items; |
| 55 | } |
| 56 | |
| 57 | /** Generates a random JavaScript statement */ |
| 58 | private String generateStatement(SourceOfRandomness random) { |
| 59 | statementDepth++; |
| 60 | String result; |
| 61 | // If depth is too high, then generate only simple statements to prevent infinite recursion |
| 62 | // If not, generate simple statements after the flip of a coin |
| 63 | if (statementDepth >= MAX_STATEMENT_DEPTH || random.nextBoolean()) { |
| 64 | // Choose a random private method from this class, and then call it with `random` |
| 65 | result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList( |
| 66 | this::generateExpressionStatement, |
| 67 | this::generateBreakNode, |
| 68 | this::generateContinueNode, |
nothing calls this directly
no outgoing calls
no test coverage detected