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