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 private 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("\t", "\\t") 76 .replace("\b", "\\b") 77 .replace("\n", "\\n") 78 .replace("\r", "\\r") 79 .replace("\f", "\\f") 80 .replace("\f", "\\f") 81 .replace("\"", "\\\"") 82 .replace("'", "\\'") 83 .replace("\\", "\\\\"); 84 } 85 toDebugString()86 private String toDebugString() { 87 return groups.stream() 88 .map(group -> group.elements.stream().collect(Collectors.joining(", ", "[", "]"))) 89 .collect(Collectors.joining(", ", "[", "]")); 90 } 91 92 private static class Group { 93 private final String prefix; 94 private final String delimiter; 95 private final String suffix; 96 private final Stack<String> elements = new Stack<>(); 97 Group(String prefix, String delimiter, String suffix)98 Group(String prefix, String delimiter, String suffix) { 99 this.prefix = prefix; 100 this.delimiter = delimiter; 101 this.suffix = suffix; 102 } 103 push(String element)104 public void push(String element) { 105 elements.push(element); 106 } 107 pop()108 public void pop() { 109 elements.pop(); 110 } 111 112 @Override toString()113 public String toString() { 114 return elements.stream().collect(Collectors.joining(delimiter, prefix, suffix)); 115 } 116 } 117 } 118