Skip to content

Commit 66041d4

Browse files
author
owen-jones-diffblue
authored
Merge pull request diffblue#577 from diffblue/feature/per-call-struct-cache
Store struct cache in a state object
2 parents 3fb752d + 9c0e40c commit 66041d4

File tree

3 files changed

+158
-150
lines changed

3 files changed

+158
-150
lines changed

trace-transformer/src/ParseLogFile.ts

Lines changed: 142 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -3,136 +3,161 @@
33
import { compare } from "./compare";
44
import * as expressions from "./Expr";
55
import * as log from "./LogFile";
6+
import NameMap from "./NameMap";
67
import { TraceParseError } from "./TraceParseError";
78
import * as type from "./Type";
89
import { group } from "./utils";
910

10-
export function parseExpr(expr: log.Expr): expressions.Expr
11-
{
12-
const exprType = parseType(expr.namedSub.type);
13-
switch (expr.id) {
14-
case "constant":
15-
return new expressions.Constant(expr.namedSub.value.id, exprType);
16-
case "symbol":
17-
// Get base name
18-
if (expr.comment === undefined)
19-
throw new TraceParseError("No comment on symbol so can't get base_name");
20-
return new expressions.Symbol(expr.namedSub.identifier.id, expr.comment["#base_name"].id, exprType);
21-
case "member": {
22-
// Get the object we're getting a member from
23-
const objectExpr = parseExpr(expr.sub[0]);
24-
const objectType = objectExpr.type;
25-
// Get the name of the member
26-
const memberName = expr.namedSub.component_name.id;
27-
// Access the non-tainted primitive superclass
28-
if (memberName === primitiveTaintWrapperMemberName) {
29-
if (objectType.taintVariables.length === 0)
30-
// Shouldn't look for primitive superclass in non-tainted type
31-
throw new TraceParseError("Got a primitive taint wrapper parent member expression for something that wasn't a TaintedType");
32-
return objectExpr;
33-
}
34-
// Access a taint variable
35-
const taintToken = getTaintTokenName(memberName, exprType);
36-
if (taintToken !== undefined) {
37-
if (!objectType.taintVariables.some((taintVariable) => taintVariable === taintToken))
38-
throw new TraceParseError("Found taint variable that doesn't exist on type");
39-
return new expressions.Taint(objectExpr, taintToken, exprType);
11+
export default class LogFileParser {
12+
public parseExpr(expr: log.Expr): expressions.Expr
13+
{
14+
const exprType = this.parseType(expr.namedSub.type);
15+
switch (expr.id) {
16+
case "constant":
17+
return new expressions.Constant(expr.namedSub.value.id, exprType);
18+
case "symbol":
19+
// Get base name
20+
if (expr.comment === undefined)
21+
throw new TraceParseError("No comment on symbol so can't get base_name");
22+
return new expressions.Symbol(expr.namedSub.identifier.id, expr.comment["#base_name"].id, exprType);
23+
case "member": {
24+
// Get the object we're getting a member from
25+
const objectExpr = this.parseExpr(expr.sub[0]);
26+
const objectType = objectExpr.type;
27+
// Get the name of the member
28+
const memberName = expr.namedSub.component_name.id;
29+
// Access the non-tainted primitive superclass
30+
if (memberName === primitiveTaintWrapperMemberName) {
31+
if (objectType.taintVariables.length === 0)
32+
// Shouldn't look for primitive superclass in non-tainted type
33+
throw new TraceParseError("Got a primitive taint wrapper parent member expression for something that wasn't a TaintedType");
34+
return objectExpr;
35+
}
36+
// Access a taint variable
37+
const taintToken = getTaintTokenName(memberName, exprType);
38+
if (taintToken !== undefined) {
39+
if (!objectType.taintVariables.some((taintVariable) => taintVariable === taintToken))
40+
throw new TraceParseError("Found taint variable that doesn't exist on type");
41+
return new expressions.Taint(objectExpr, taintToken, exprType);
42+
}
43+
// Shouldn't look for members in non-struct
44+
if (!(objectType instanceof type.Struct))
45+
throw new TraceParseError("Got a member expression for something of non-struct type");
46+
// Check if this is an access to a base class, i.e. the memberName matches the result type
47+
const baseType = getBaseType(memberName, exprType);
48+
if (baseType !== undefined) {
49+
if (!objectType.bases.some((base) => base.compare(baseType) === 0))
50+
throw new TraceParseError("Found base accessor for non-base");
51+
return new expressions.Downcast(objectExpr, baseType);
52+
}
53+
return new expressions.Member(objectExpr, memberName, exprType);
4054
}
41-
// Shouldn't look for members in non-struct
42-
if (!(objectType instanceof type.Struct))
43-
throw new TraceParseError("Got a member expression for something of non-struct type");
44-
// Check if this is an access to a base class, i.e. the memberName matches the result type
45-
const baseType = getBaseType(memberName, exprType);
46-
if (baseType !== undefined) {
47-
if (!objectType.bases.some((base) => base.compare(baseType) === 0))
48-
throw new TraceParseError("Found base accessor for non-base");
49-
return new expressions.Downcast(objectExpr, baseType);
55+
case "index": {
56+
return new expressions.Index(this.parseExpr(expr.sub[0]), this.parseExpr(expr.sub[1]), exprType);
5057
}
51-
return new expressions.Member(objectExpr, memberName, exprType);
52-
}
53-
case "index": {
54-
return new expressions.Index(parseExpr(expr.sub[0]), parseExpr(expr.sub[1]), exprType);
5558
}
5659
}
57-
}
5860

59-
function parseType(logType: log.Type): type.Type {
60-
switch (logType.id) {
61-
case "bool": return type.booleanType;
62-
case "c_bool": return type.cBoolean;
63-
case "signedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), true, []);
64-
case "unsignedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), false, []);
65-
case "string": return type.stringType;
66-
case "pointer": return new type.Pointer(parseType(logType.sub[0]), []);
67-
case "struct": {
68-
// Get bases, fields and taint variables
69-
const members = group(
70-
logType.namedSub.components.sub
71-
.map((member) => new type.Field(member.namedSub.name.id, member.namedSub.pretty_name.id, parseType(member.namedSub.type))),
72-
(field) => {
73-
if (getTaintTokenName(field.name, field.type) !== undefined)
74-
return "taint";
75-
else if (getBaseType(field.name, field.type) !== undefined)
76-
return "base";
77-
else
78-
return "field";
79-
});
80-
// Get bases
81-
let bases = logType.namedSub.bases === undefined ? [] : logType.namedSub.bases.sub
82-
.filter((base) => type.Struct.has(base.namedSub.type.namedSub.identifier.id))
83-
.map((base) => type.Struct.get(base.namedSub.type.namedSub.identifier.id));
84-
const baseMembers = members.get("base");
85-
const extraBases = baseMembers === undefined ? []
86-
: baseMembers
87-
.map((field) => getBaseType(field.name, field.type) as type.Struct)
88-
.filter((extraBase) => bases.findIndex((base) => compare(extraBase, base) === 0) === -1);
89-
bases = bases.concat(extraBases);
90-
// Get fields
91-
const fieldMembers = members.get("field");
92-
const fields = fieldMembers === undefined ? [] : fieldMembers;
93-
// Get taint variables
94-
const taintVariableMembers = members.get("taint");
95-
const taintVariables = taintVariableMembers === undefined ? []
96-
: taintVariableMembers.map((field) => getTaintTokenName(field.name, field.type) as string);
97-
// Get struct name
98-
const structName = getStructName(logType);
99-
// Check for primitive type wrappers
100-
if (isPrimitiveTaintWrapper(logType)) {
101-
if (fields.length !== 1 || fields[0].name !== primitiveTaintWrapperMemberName)
102-
throw new TraceParseError(primitiveTaintWrapperPrefix + "* type doesn't have a single field called " + primitiveTaintWrapperMemberName);
103-
if (taintVariables.length === 0)
104-
throw new TraceParseError("A struct that contains a " + primitiveTaintWrapperMemberName + " member has no taint variables");
105-
const base = fields[0].type;
106-
if (bases.length === 1) {
107-
if (bases[0] !== base)
108-
throw new TraceParseError("Type of taint wrapper super was not the base type");
109-
} else if (bases.length !== 0)
110-
throw new TraceParseError(primitiveTaintWrapperPrefix + "* type has more than one base");
111-
if (base.taintVariables.length === 0)
112-
base.taintVariables = taintVariables;
113-
else if (compare(base.taintVariables, taintVariables) !== 0)
114-
throw new TraceParseError("Found taint variables didn't match those on the base");
115-
return base;
61+
private parseType(logType: log.Type): type.Type {
62+
switch (logType.id) {
63+
case "bool": return type.booleanType;
64+
case "c_bool": return type.cBoolean;
65+
case "signedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), true, []);
66+
case "unsignedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), false, []);
67+
case "string": return type.stringType;
68+
case "pointer": return new type.Pointer(this.parseType(logType.sub[0]), []);
69+
case "struct": {
70+
// Get bases, fields and taint variables
71+
const members = group(
72+
logType.namedSub.components.sub
73+
.map((member) => new type.Field(member.namedSub.name.id, member.namedSub.pretty_name.id, this.parseType(member.namedSub.type))),
74+
(field) => {
75+
if (getTaintTokenName(field.name, field.type) !== undefined)
76+
return "taint";
77+
else if (getBaseType(field.name, field.type) !== undefined)
78+
return "base";
79+
else
80+
return "field";
81+
});
82+
// Get bases
83+
let bases = logType.namedSub.bases === undefined ? [] : logType.namedSub.bases.sub
84+
.filter((base) => this.structExists(base.namedSub.type.namedSub.identifier.id))
85+
.map((base) => this.getStruct(base.namedSub.type.namedSub.identifier.id));
86+
const baseMembers = members.get("base");
87+
const extraBases = baseMembers === undefined ? []
88+
: baseMembers
89+
.map((field) => getBaseType(field.name, field.type) as type.Struct)
90+
.filter((extraBase) => bases.findIndex((base) => compare(extraBase, base) === 0) === -1);
91+
bases = bases.concat(extraBases);
92+
// Get fields
93+
const fieldMembers = members.get("field");
94+
const fields = fieldMembers === undefined ? [] : fieldMembers;
95+
// Get taint variables
96+
const taintVariableMembers = members.get("taint");
97+
const taintVariables = taintVariableMembers === undefined ? []
98+
: taintVariableMembers.map((field) => getTaintTokenName(field.name, field.type) as string);
99+
// Get struct name
100+
const structName = getStructName(logType);
101+
// Check for primitive type wrappers
102+
if (isPrimitiveTaintWrapper(logType)) {
103+
if (fields.length !== 1 || fields[0].name !== primitiveTaintWrapperMemberName)
104+
throw new TraceParseError(primitiveTaintWrapperPrefix + "* type doesn't have a single field called " + primitiveTaintWrapperMemberName);
105+
if (taintVariables.length === 0)
106+
throw new TraceParseError("A struct that contains a " + primitiveTaintWrapperMemberName + " member has no taint variables");
107+
const base = fields[0].type;
108+
if (bases.length === 1) {
109+
if (bases[0] !== base)
110+
throw new TraceParseError("Type of taint wrapper super was not the base type");
111+
} else if (bases.length !== 0)
112+
throw new TraceParseError(primitiveTaintWrapperPrefix + "* type has more than one base");
113+
if (base.taintVariables.length === 0)
114+
base.taintVariables = taintVariables;
115+
else if (compare(base.taintVariables, taintVariables) !== 0)
116+
throw new TraceParseError("Found taint variables didn't match those on the base");
117+
return base;
118+
}
119+
// Get existing element from map and initialize
120+
const struct = this.getStruct(structName.id);
121+
struct.initialize(bases, fields, taintVariables);
122+
// Check for primitive taint wrapper member field in non-taint structure
123+
if (struct.fields.get(primitiveTaintWrapperMemberName) !== undefined)
124+
throw new TraceParseError(struct.name + " contains a field called " + primitiveTaintWrapperMemberName);
125+
// Return the struct, with taint if applicable
126+
return struct;
116127
}
117-
// Get existing element from map and initialize
118-
const struct = type.Struct.get(structName.id);
119-
struct.initialize(bases, fields, taintVariables);
120-
// Check for primitive taint wrapper member field in non-taint structure
121-
if (struct.fields.get(primitiveTaintWrapperMemberName) !== undefined)
122-
throw new TraceParseError(struct.name + " contains a field called " + primitiveTaintWrapperMemberName);
123-
// Return the struct, with taint if applicable
124-
return struct;
128+
case "symbol_type":
129+
let name = logType.namedSub.identifier.id;
130+
if (name.startsWith(primitiveTaintWrapperPrefix))
131+
name = name.substr(primitiveTaintWrapperPrefix.length);
132+
return this.getStruct(name);
133+
// default:
134+
// throw new TraceParseError("Unexpected id on type: " + logType.id);
125135
}
126-
case "symbol_type":
127-
let name = logType.namedSub.identifier.id;
128-
if (name.startsWith(primitiveTaintWrapperPrefix))
129-
name = name.substr(primitiveTaintWrapperPrefix.length);
130-
return type.Struct.get(name);
131-
// default:
132-
// throw new TraceParseError("Unexpected id on type: " + logType.id);
133136
}
134-
}
135137

138+
private readonly structCache = new NameMap<type.Struct>();
139+
140+
public structExists(name: string): boolean {
141+
return this.structCache.has(name);
142+
}
143+
144+
public createStruct(name: string) {
145+
const struct = new type.Struct(name);
146+
this.structCache.add(struct);
147+
return struct;
148+
}
149+
150+
public getStruct(name: string): type.Struct {
151+
const result = this.structCache.get(name);
152+
if (result === undefined)
153+
throw new Error("Request for missing struct: " + name);
154+
return result;
155+
}
156+
157+
public get allStructs(): Iterable<type.Struct> {
158+
return this.structCache;
159+
}
160+
}
136161

137162
export const primitiveTaintWrapperPrefix = "@__CPROVER_primitive_taint_wrapper_";
138163
export function isPrimitiveTaintWrapper(logType: log.StructType) {

trace-transformer/src/TransformTrace.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
import IterableExt from "iterable-ext";
44
import * as expressions from "./Expr";
55
import * as log from "./LogFile";
6-
import {
6+
import LogFileParser, {
77
getStructName,
88
getTaintTokenName,
99
isPrimitiveTaintWrapper,
10-
parseExpr,
1110
primitiveTaintWrapperMemberName,
1211
primitiveTaintWrapperPrefix,
1312
taintFieldPrefix,
@@ -20,12 +19,13 @@ const symbolTypeMap = new Map<string, type.Type>();
2019

2120
export default function transformTrace(trace: log.Step[], verbose: boolean) {
2221
// Get the struct names
23-
trace.forEach(gatherStructs);
22+
const logFileParser = new LogFileParser();
23+
trace.forEach((step) => gatherStructs(step, logFileParser));
2424
// Transform the trace
25-
trace = (<log.Step[]>[]).concat(...trace.map(transformStep));
25+
trace = (<log.Step[]>[]).concat(...trace.map((step) => transformStep(step, logFileParser)));
2626
if (verbose) {
2727
// Output the structure map built up during transformations
28-
console.log(IterableExt.fromIterable(type.Struct.all).map((struct) => struct.definitionString).join("\n"));
28+
console.log(IterableExt.fromIterable(logFileParser.allStructs).map((struct) => struct.definitionString).join("\n"));
2929
// Output the symbol map built up during transformations
3030
console.log(IterableExt.fromIterable(symbolTypeMap)
3131
.map(([symbolName, type]) => symbolName + ": " + indentMultiLine(type.toString(), 1))
@@ -34,15 +34,15 @@ export default function transformTrace(trace: log.Step[], verbose: boolean) {
3434
return trace;
3535
}
3636

37-
function gatherStructs(step: log.Step): void {
37+
function gatherStructs(step: log.Step, logFileParser: LogFileParser): void {
3838
if (step.stepType === "assignment") {
3939
if (step.rawLhs === undefined)
4040
throw new TraceParseError("Trace does not contain rawLhs - enable this option to use this tool");
41-
gatherStructsFromType(step.rawLhs.namedSub.type);
41+
gatherStructsFromType(step.rawLhs.namedSub.type, logFileParser);
4242
}
4343
}
4444

45-
function gatherStructsFromType(logType: log.Type): void {
45+
function gatherStructsFromType(logType: log.Type, logFileParser: LogFileParser): void {
4646
switch (logType.id) {
4747
case "bool":
4848
case "c_bool":
@@ -52,28 +52,29 @@ function gatherStructsFromType(logType: log.Type): void {
5252
case "symbol_type":
5353
return;
5454
case "pointer":
55-
gatherStructsFromType(logType.sub[0]);
55+
gatherStructsFromType(logType.sub[0], logFileParser);
5656
return;
5757
case "struct": {
5858
// Check for structs in types of members
59-
logType.namedSub.components.sub.forEach((member) => gatherStructsFromType(member.namedSub.type));
59+
logType.namedSub.components.sub.forEach((member) => gatherStructsFromType(member.namedSub.type, logFileParser));
6060
// Add Struct to map if it doesn't already exist and isn't a primitive taint wrapper
6161
const structName = getStructName(logType);
6262
if (structName.id.startsWith(primitiveTaintWrapperPrefix) !== isPrimitiveTaintWrapper(logType))
6363
throw new TraceParseError("#is_taint_wrapper_type doesn't add up");
64-
if (!isPrimitiveTaintWrapper(logType) && !type.Struct.has(structName.id))
65-
new type.Struct(structName.id);
64+
if (!isPrimitiveTaintWrapper(logType) && !logFileParser.structExists(structName.id)) {
65+
logFileParser.createStruct(structName.id);
66+
}
6667
}
6768
}
6869
}
6970

70-
function transformStep(step: log.Step): log.Step[] {
71+
function transformStep(step: log.Step, logFileParser: LogFileParser): log.Step[] {
7172
const steps: log.Step[] = [];
7273
// Parse everything
7374
if (step.stepType === "assignment") {
7475
if (step.rawLhs === undefined)
7576
throw new TraceParseError("Trace does not contain rawLhs - enable the option that generates it to use this tool");
76-
const lhs = parseExpr(step.rawLhs);
77+
const lhs = logFileParser.parseExpr(step.rawLhs);
7778
// Create symbol type map
7879
if (step.rawLhs.id === "symbol")
7980
symbolTypeMap.set(step.rawLhs.namedSub.identifier.id, lhs.type);
@@ -102,7 +103,7 @@ function transformStep(step: log.Step): log.Step[] {
102103
// Separate out the taint variables
103104
const members = group(
104105
value.members,
105-
(member) => member.name.startsWith(taintFieldPrefix) && member.value.name == "integer");
106+
(member) => member.name.startsWith(taintFieldPrefix) && member.value.name === "integer");
106107
const taintVariables = members.get(true) || [];
107108
// Create extra steps to initialize taint variables based on the structure initialization
108109
const taintSteps = taintVariables.map((taintVariableMember) => (<log.TaintAssignmentStep>{

0 commit comments

Comments
 (0)