diff --git a/ts/birdseye/birdseye.html b/ts/birdseye/birdseye.html
new file mode 100644
index 0000000..6733d25
--- /dev/null
+++ b/ts/birdseye/birdseye.html
@@ -0,0 +1,309 @@
+
+
+ Augur -- Birdseye
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ts/src/abstractMachine/ExpressionMachine.ts b/ts/src/abstractMachine/ExpressionMachine.ts
index a37e56c..2a87ef8 100644
--- a/ts/src/abstractMachine/ExpressionMachine.ts
+++ b/ts/src/abstractMachine/ExpressionMachine.ts
@@ -3,6 +3,10 @@
import JSMachine from "./JSMachine";
import {StaticDescription} from "../types";
+import * as fs from 'fs';
+import * as path from 'path';
+import generateBirdseyeHTML from "../birdseye/birdseye";
+
export default class ExpressionMachine
extends JSMachine, [Set, StaticDescription]> {
@@ -21,10 +25,10 @@ export default class ExpressionMachine
// add them separately, since one of them might not be defined.
// TODO: this is a hack. they SHOULD always be defined. if they aren't
// we are losing information.
- if (a) {
+ if (a && a instanceof Set) {
a.forEach((v) => set.add(v));
}
- if (b) {
+ if (b && b instanceof Set) {
b.forEach((v) => set.add(v));
}
return set;
@@ -37,4 +41,19 @@ export default class ExpressionMachine
getUntaintedValue(): Set {
return new Set();
}
+
+ /**
+ * When execution ends in an ExpressionMachine,
+ * we will automatically generate a web page to
+ * visualize the resulting "program dependence graph".
+ */
+ endExecution() {
+ super.endExecution();
+
+ // Generate the Birdseye HTML.
+ let birdseyeHTML = generateBirdseyeHTML(this);
+
+ // Write it to the output directory.
+ fs.writeFileSync(path.dirname(fs.realpathSync(this.outputFilePath)) + `/${this.spec.main}.birdseye.html`, birdseyeHTML)
+ }
}
diff --git a/ts/src/birdseye/README.md b/ts/src/birdseye/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/ts/src/birdseye/birdseye.ts b/ts/src/birdseye/birdseye.ts
new file mode 100644
index 0000000..9b0307b
--- /dev/null
+++ b/ts/src/birdseye/birdseye.ts
@@ -0,0 +1,36 @@
+// This file contains the Birdseye auto-generator.
+// Given the flows and spec from an ExpressionMachine,
+// this file generates a self-contained HTML page
+// with a dynamic visualization of the
+// *program dependence graph* observed from instrumenting
+// the program with Augur.
+
+import { RunSpecification, StaticDescription } from "../types"
+import ExpressionMachine from "../abstractMachine/ExpressionMachine"
+import * as fs from 'fs'
+
+export default function generateBirdseyeHTML(m: ExpressionMachine): string {
+ // Step 1: Get the `birdseye.html` template.
+ let birdseyeHTML = fs.readFileSync('./birdseye/birdseye.html').toString()
+ console.error("Birdseye HTML TEMPLATE: " + birdseyeHTML)
+
+ // Step 2: Insert the flows into this HTML file.
+ // The template contains the marker `AUGUR REPLACE ME HERE`.
+ // The flows will be injected into here, as serialized JSON.
+ birdseyeHTML = birdseyeHTML.replace("AUGUR REPLACE ME HERE", JSON.stringify(m.flows, (key, value) =>
+ value instanceof Set ? [...value] : value, ))
+ birdseyeHTML = birdseyeHTML.replace("AUGUR REPLACE SPEC HERE", JSON.stringify(m.spec))
+ birdseyeHTML = birdseyeHTML.replace("AUGUR REPLACE PROJECT NAME HERE", m.spec.main)
+ birdseyeHTML = birdseyeHTML.replace("AUGUR REPLACE INSTRUCTIONS HERE", getNumberOfInstructions(m.outputFilePath).toString())
+ return birdseyeHTML
+}
+
+function getNumberOfInstructions(outputFilePath: string): number {
+ // Augur output files have some garbage before and after the instructionns
+ const numLines = fs.readFileSync(outputFilePath)
+ .toString()
+ .split("\n")
+ .length
+
+ return (numLines - 5) / 2
+}
\ No newline at end of file
diff --git a/ts/src/utils.ts b/ts/src/utils.ts
index b6babc0..7af6554 100644
--- a/ts/src/utils.ts
+++ b/ts/src/utils.ts
@@ -209,7 +209,7 @@ export function parseSpec(specPath: string): RunSpecification {
}
export function executeInstructionsFromFile(path: string, options: RunSpecification) {
- const abstractMachine = createAbstractMachine(options);
+ const abstractMachine = createAbstractMachine(options, false, path);
const compiledOutput = require(path);
compiledOutput.drive(abstractMachine);
return abstractMachine.getTaint();