1 // Copyright 2021 Code Intelligence GmbH 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.code_intelligence.jazzer.autofuzz; 16 17 import java.util.Stack; 18 import java.util.stream.Collectors; 19 20 public class AutofuzzCodegenVisitor { 21 private final Stack<Group> groups = new Stack<>(); 22 private int variableCounter = 0; 23 AutofuzzCodegenVisitor()24 AutofuzzCodegenVisitor() { 25 init(); 26 } 27 init()28 private void init() { 29 pushGroup("", "", ""); 30 } 31 pushGroup(String prefix, String delimiter, String suffix)32 public void pushGroup(String prefix, String delimiter, String suffix) { 33 groups.push(new Group(prefix, delimiter, suffix)); 34 } 35 pushElement(String element)36 public void pushElement(String element) { 37 groups.peek().push(element); 38 } 39 popElement()40 public void popElement() { 41 groups.peek().pop(); 42 } 43 popGroup()44 public void popGroup() { 45 if (groups.size() == 1) { 46 throw new AutofuzzError( 47 "popGroup must be called exactly once for every pushGroup: " + toDebugString()); 48 } 49 pushElement(groups.pop().toString()); 50 } 51 generate()52 public String generate() { 53 if (groups.size() != 1) { 54 throw new AutofuzzError( 55 "popGroup must be called exactly once for every pushGroup: " + toDebugString()); 56 } 57 return groups.pop().toString(); 58 } 59 addCharLiteral(char c)60 public void addCharLiteral(char c) { 61 pushElement("'" + escapeForLiteral(Character.toString(c)) + "'"); 62 } 63 addStringLiteral(String string)64 public void addStringLiteral(String string) { 65 pushElement('"' + escapeForLiteral(string) + '"'); 66 } 67 uniqueVariableName()68 public String uniqueVariableName() { 69 return String.format("autofuzzVariable%s", variableCounter++); 70 } 71 escapeForLiteral(String string)72 static String escapeForLiteral(String string) { 73 // The list of escape sequences is taken from: 74 // https://docs.oracle.com/javase/tutorial/java/data/characters.html 75 return string.replace("\\", "\\\\") 76 .replace("\t", "\\t") 77 .replace("\b", "\\b") 78 .replace("\n", "\\n") 79 .replace("\r", "\\r") 80 .replace("\f", "\\f") 81 .replace("\"", "\\\"") 82 .replace("'", "\\'"); 83 } 84 toDebugString()85 private String toDebugString() { 86 return groups.stream() 87 .map(group -> group.elements.stream().collect(Collectors.joining(", ", "[", "]"))) 88 .collect(Collectors.joining(", ", "[", "]")); 89 } 90 91 private static class Group { 92 private final String prefix; 93 private final String delimiter; 94 private final String suffix; 95 private final Stack<String> elements = new Stack<>(); 96 Group(String prefix, String delimiter, String suffix)97 Group(String prefix, String delimiter, String suffix) { 98 this.prefix = prefix; 99 this.delimiter = delimiter; 100 this.suffix = suffix; 101 } 102 push(String element)103 public void push(String element) { 104 elements.push(element); 105 } 106 pop()107 public void pop() { 108 elements.pop(); 109 } 110 111 @Override toString()112 public String toString() { 113 return elements.stream().collect(Collectors.joining(delimiter, prefix, suffix)); 114 } 115 } 116 } 117