Skip to content

Commit 3ea9d1e

Browse files
author
Arsene Perard-Gayot
committed
Refactor
1 parent b59822e commit 3ea9d1e

17 files changed

+755
-213
lines changed

CMakeLists.txt

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 2.8.1 FATAL_ERROR)
1+
cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR)
22

33
project(SLANG)
44

@@ -20,6 +20,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PROJ_LIBRARY_DIR})
2020

2121
set(CMAKE_MODULE_PATH ${PROJ_ROOT_DIR} ${CMAKE_MODULE_PATH})
2222

23+
set(CMAKE_CXX_STANDARD 11)
24+
2325
include_directories(${PROJ_SOURCE_DIR})
2426

2527
set(COLOR_TTY_AVAILABLE TRUE)
@@ -33,4 +35,6 @@ if(COLORIZE_OUTPUT)
3335
add_definitions(-DCOLORIZE_OUTPUT)
3436
endif()
3537

36-
add_subdirectory(${PROJ_SOURCE_DIR})
38+
add_subdirectory(${PROJ_SOURCE_DIR})
39+
enable_testing()
40+
add_subdirectory(examples)

Doxyfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ EXCLUDE_SYMBOLS =
811811
# that contain example code fragments that are included (see the \include
812812
# command).
813813

814-
EXAMPLE_PATH =
814+
EXAMPLE_PATH = examples
815815

816816
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
817817
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,23 @@ bool parse_glsl(const std::string& filename) {
6363
## Documentation
6464
The documentation can be generated using doxygen.
6565

66+
## Examples
67+
68+
### User defined macros
69+
70+
This [example](examples/user_macros.cpp) describes how to register custom macros into the preprocessor.
71+
Two new macro are introduced: One with a traditional production rule (i.e. identical to a macro
72+
defined with `#define MACRO(x) ...`), and a second macro which executes custom code.
73+
74+
### Pattern matching on the AST
75+
76+
This [example](examples/pattern_matching.cpp) describes how to perform pattern matching on the AST.
77+
The program looks for expressions of the form `A * B + C` and replaces them by calls to `fma()`.
78+
79+
### Code obfuscation by renaming
80+
81+
This [example](examples/obfuscate.cpp) describes how to perform a simple renaming pass on the AST.
82+
The code is read from a string and it is then printed on the screen.
83+
6684
## License
6785
The code is distributed under the LGPL license.

examples/CMakeLists.txt

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
link_libraries(slang)
2+
3+
add_executable(user_macros user_macros.cpp)
4+
add_executable(pattern_matching pattern_matching.cpp)
5+
add_executable(obfuscate obfuscate.cpp)
6+
7+
add_test(NAME test_user_macros COMMAND user_macros)
8+
add_test(NAME test_pattern_matching COMMAND pattern_matching)
9+
add_test(NAME test_obfuscate COMMAND obfuscate)

examples/obfuscate.cpp

+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/// This example demonstrates how to obfuscate the code by renaming all identifiers.
2+
/// The example goes through the AST and replaces every occurence of a symbol with
3+
/// a fresh new symbol, generated randomly, while preserving the correctness of the code.
4+
/// The code uses the Parser and Lexer class to read the contents of a string. The result
5+
/// is printed to a stream using the Printer class.
6+
7+
#include <unordered_map>
8+
#include <iostream>
9+
#include <sstream>
10+
#include <string>
11+
12+
#include <slang/parser.h>
13+
#include <slang/sema.h>
14+
#include <slang/lexer.h>
15+
#include <slang/logger.h>
16+
#include <slang/ptr.h>
17+
#include <slang/ast.h>
18+
#include <slang/print.h>
19+
20+
using namespace slang;
21+
22+
struct Scope {
23+
std::unordered_map<std::string, std::string> names;
24+
std::unordered_set<std::string> new_names;
25+
};
26+
27+
struct Obfuscator {
28+
std::vector<Scope> scopes;
29+
30+
Obfuscator() { push_scope(); }
31+
32+
unsigned int rnd() {
33+
static int c;
34+
c = c * 1103515245 + 12345;
35+
return c;
36+
}
37+
38+
const std::string* find_name(const std::string& name) {
39+
for (int i = scopes.size() - 1; i >= 0; i--) {
40+
auto& s = scopes[i];
41+
auto it = s.names.find(name);
42+
if (it != s.names.end()) { return &it->second; }
43+
}
44+
return nullptr;
45+
}
46+
47+
bool name_exists(const std::string& name) {
48+
for (int i = scopes.size() - 1; i >= 0; i--) {
49+
if (scopes[i].new_names.count(name) > 0) return true;
50+
}
51+
return false;
52+
}
53+
54+
std::string new_name(const std::string& name) {
55+
std::string cur;
56+
do {
57+
cur += 'a' + rnd() % 5;
58+
} while (name_exists(cur));
59+
scopes.back().names[name] = cur;
60+
scopes.back().new_names.emplace(cur);
61+
return cur;
62+
}
63+
64+
void pop_scope() { scopes.pop_back(); }
65+
void push_scope() { scopes.emplace_back(); }
66+
67+
void obfuscate(Ptr<ast::Module>& mod) {
68+
for (auto& decl : mod->decls()) obfuscate(decl);
69+
}
70+
71+
void obfuscate(Ptr<ast::Decl>& decl) {
72+
// Look inside function declarations
73+
if (auto func_decl = decl->isa<ast::FunctionDecl>()) {
74+
// The function can be only a prototype, so there may be no body
75+
func_decl->set_name(new_name(func_decl->name()));
76+
push_scope();
77+
for (auto& arg : func_decl->args()) obfuscate(arg);
78+
if (func_decl->body()) obfuscate(func_decl->body());
79+
pop_scope();
80+
} else if (auto var_decl = decl->isa<ast::VariableDecl>()) {
81+
obfuscate(var_decl->type());
82+
for (auto& var : var_decl->vars()) obfuscate(var);
83+
}
84+
}
85+
86+
void obfuscate(Ptr<ast::Type>& type) {
87+
if (auto named_type = type->isa<ast::NamedType>()) {
88+
auto name = find_name(named_type->name());
89+
if (name) named_type->set_name(*name);
90+
} else if (auto compound = type->isa<ast::CompoundType>()) {
91+
compound->set_name(new_name(compound->name()));
92+
for (auto& field : compound->fields()) {
93+
obfuscate(field->type());
94+
for (auto& var : field->vars()) obfuscate(var);
95+
}
96+
}
97+
}
98+
99+
void obfuscate(Ptr<ast::Arg>& arg) {
100+
obfuscate(arg->type());
101+
arg->set_name(new_name(arg->name()));
102+
}
103+
104+
void obfuscate(Ptr<ast::Variable>& var) {
105+
obfuscate(var->init());
106+
var->set_name(new_name(var->name()));
107+
}
108+
109+
void obfuscate(Ptr<ast::StmtList>& list) {
110+
push_scope();
111+
for (auto& stmt : list->stmts()) obfuscate(stmt);
112+
pop_scope();
113+
}
114+
115+
void obfuscate(Ptr<ast::Stmt>& stmt) {
116+
if (auto list = stmt->isa<ast::StmtList>()) {
117+
// List of statements
118+
push_scope();
119+
for (auto& stmt : list->stmts()) obfuscate(stmt);
120+
pop_scope();
121+
} else if (auto if_stmt = stmt->isa<ast::IfStmt>()) {
122+
// If statements
123+
obfuscate(if_stmt->cond());
124+
obfuscate(if_stmt->if_true());
125+
obfuscate(if_stmt->if_false());
126+
} else if (auto switch_stmt = stmt->isa<ast::SwitchStmt>()) {
127+
// Switch statements
128+
obfuscate(switch_stmt->expr());
129+
obfuscate(switch_stmt->list());
130+
} else if (auto for_stmt = stmt->isa<ast::ForLoopStmt>()) {
131+
// For loops
132+
obfuscate(for_stmt->init());
133+
obfuscate(for_stmt->iter());
134+
obfuscate(for_stmt->cond());
135+
obfuscate(for_stmt->body());
136+
} else if (auto loop_stmt = stmt->isa<ast::LoopStmt>()) {
137+
// Other loops: while and do...while
138+
obfuscate(loop_stmt->cond());
139+
obfuscate(loop_stmt->body());
140+
} else if (auto expr_stmt = stmt->isa<ast::ExprStmt>()) {
141+
// Expressions
142+
obfuscate(expr_stmt->expr());
143+
} else if (auto decl_stmt = stmt->isa<ast::DeclStmt>()) {
144+
// Declarations
145+
obfuscate(decl_stmt->decl());
146+
} else if (auto ret_stmt = stmt->isa<ast::ReturnStmt>()) {
147+
// Return statements
148+
obfuscate(ret_stmt->value());
149+
}
150+
}
151+
152+
void obfuscate(Ptr<ast::LoopCond>& cond) {
153+
if (cond->expr()) obfuscate(cond->expr());
154+
}
155+
156+
void obfuscate(Ptr<ast::Expr>& expr) {
157+
if (auto bin_op = expr->isa<ast::BinOpExpr>()) {
158+
// First, descend in the tree
159+
obfuscate(bin_op->left());
160+
obfuscate(bin_op->right());
161+
} else if (auto un_op = expr->isa<ast::UnOpExpr>()) {
162+
// Unary operation
163+
obfuscate(un_op->operand());
164+
} else if (auto assign_op = expr->isa<ast::AssignOpExpr>()) {
165+
// Operation with assignment
166+
obfuscate(assign_op->left());
167+
obfuscate(assign_op->right());
168+
} else if (auto cond = expr->isa<ast::CondExpr>()) {
169+
// Ternary operator
170+
obfuscate(cond->cond());
171+
obfuscate(cond->if_true());
172+
obfuscate(cond->if_false());
173+
} else if (auto call = expr->isa<ast::CallExpr>()) {
174+
if (call->function()) obfuscate(call->function());
175+
if (call->constructor()) obfuscate(call->constructor());
176+
for (auto& arg : call->args()) obfuscate(arg);
177+
} else if (auto index = expr->isa<ast::IndexExpr>()) {
178+
obfuscate(index->left());
179+
obfuscate(index->index());
180+
} else if (auto list = expr->isa<ast::ExprList>()) {
181+
for (auto& expr : list->exprs()) obfuscate(expr);
182+
} else if (auto ident = expr->isa<ast::IdentExpr>()) {
183+
// Find the obfuscated version of the identifier if it exists
184+
auto name = find_name(ident->ident());
185+
if (name) ident->set_ident(*name);
186+
}
187+
}
188+
};
189+
190+
int main(int argc, char** argv) {
191+
std::string glsl =
192+
R"(
193+
struct Node {
194+
int left, right;
195+
float value;
196+
};
197+
float pow(float x, int i) {
198+
if (i == 0) return 1.0;
199+
else if (i % 2 == 0) return x * pow(x, i - 1);
200+
else return pow(x, i / 2);
201+
}
202+
float test() {
203+
float unit = 42.0;
204+
int array[10];
205+
Node element;
206+
for (int i = 0; i < 10; i++) {
207+
array[i] = pow(unit, i);
208+
}
209+
}
210+
)";
211+
std::istringstream is(glsl);
212+
213+
// Create a logger object to report errors
214+
Logger log("inline_string.glsl");
215+
// Create a set of keywords
216+
Keywords keys;
217+
keys.add_all_keywords();
218+
// Create a lexer to generate a stream of tokens
219+
Lexer lex(is, keys, log);
220+
// Create a semantic analyser (type checker) object
221+
Sema sema(log);
222+
// Create a parser object
223+
Parser parser([&] { return lex.lex(); }, sema, log);
224+
225+
auto module = parser.parse();
226+
227+
Obfuscator obfuscator;
228+
obfuscator.obfuscate(module);
229+
230+
std::ostringstream out;
231+
Printer printer(out, " ", 0, false, false);
232+
module->print(printer);
233+
234+
std::cout << out.str();
235+
236+
return out.str() ==
237+
R"(struct a {
238+
int e, d;
239+
float c;
240+
};
241+
float db(float ea, int b) {
242+
if (b == 0) return 1.0;
243+
else if (b % 2 == 0) return ea * db(ea, b - 1);
244+
else return db(ea, b / 2);
245+
}
246+
float aa() {
247+
float ea = 42.0;
248+
int cb[10];
249+
a da;
250+
for (int eaa = 0; eaa < 10; eaa++) {
251+
cb[eaa] = db(ea, eaa);
252+
}
253+
}
254+
)" ? 0 : 1;
255+
}

0 commit comments

Comments
 (0)