Skip to content

Commit f9b0dcd

Browse files
committed
Merge pull request #42 from jugglinmike/raw-5
Implement support for `raw` frontmatter flag
2 parents f0154d2 + 0f3b77a commit f9b0dcd

File tree

9 files changed

+137
-51
lines changed

9 files changed

+137
-51
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,11 @@ All runners have the same basic interface. Supporting new hosts, transpilers, or
123123
#### Runner(args)
124124
Runners are constructed by passing the current configuration object.
125125

126+
#### Runner.prototype.needsCtrlFlow
127+
Boolean flag indicating whether control-flow-related code should be injected during test compilation (see `Runner.prototype.compile`).
128+
126129
#### Runner.prototype.compile(test)
127-
Modifies the test contents to run in the target host. By default, it will append a call to $DONE if not already present, append any the environment dependencies (eg. $DONE, $LOG, etc) found in `this.deps`, append helpers, and add "use strict" if required.
130+
Modifies the test contents to run in the target host. By default, it will append a call to $DONE if not already present, append the appropriate environment dependencies (eg. `$DONE`, `$LOG`, `$ERROR`, etc), append helpers, and add "use strict" if required.
128131

129132
#### Runner.prototype.link(test)
130133
Recursively appends helpers required in the front-matter of the test.

lib/runner.js

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,86 @@ function Runner(opts) {
2424
throw new Error("Helper directory " + opts.includesDir + " not found");
2525
}
2626

27-
2827
this.helpers = loadHelpers(opts.includesDir);
28+
29+
this._errorSrc = this.test262ErrorSrc + "\n;" + this.errorFnSrc;
30+
31+
if (this.needsCtrlFlow) {
32+
if (!this.logFnSrc) {
33+
throw new Error('`$LOG` function not implemented.');
34+
}
35+
36+
if (!this.doneFnSrc) {
37+
throw new Error('`$DONE` function not implemented.');
38+
}
39+
40+
this._ctrlFlowSrc = this.logFnSrc + "\n;" + this.doneFnSrc;
41+
}
2942
};
3043

31-
Runner.prototype.deps = [];
44+
/**
45+
* Boolean attribute which controls whether the runner should inject code for
46+
* control flow. If true, the runner's `doneFnSrc` and `logFnSrc` will be
47+
* inserted into each test prior to execution.
48+
*/
49+
Runner.prototype.needsCtrlFlow = true;
50+
51+
/**
52+
* JavaScript source code that defines a function binding for the identifier
53+
* `$LOG`. This must be defined by Runner subclasses that require control flow.
54+
*/
55+
Runner.prototype.logFnSrc = null;
56+
57+
/**
58+
* JavaScript source code that defines a function binding for the identifier
59+
* `$DONE`. This must be defined by Runner subclasses that require control
60+
* flow.
61+
*/
62+
Runner.prototype.doneFnSrc = null;
63+
64+
/**
65+
* JavaScript source code that defines a constructor function bound to the
66+
* identifier `Test262Error`.
67+
*/
68+
Runner.prototype.test262ErrorSrc = function() {
69+
function Test262Error(message) {
70+
if (message) this.message = message;
71+
}
72+
73+
Test262Error.prototype.name = "Test262Error";
74+
75+
Test262Error.prototype.toString = function () {
76+
return "Test262Error: " + this.message;
77+
};
78+
}.toString().slice(14, -1);
79+
80+
/**
81+
* JavaScript source code that defines a function binding for the identifier
82+
* `$ERROR`.
83+
*/
84+
Runner.prototype.errorFnSrc = function $ERROR(err) {
85+
if(typeof err === "object" && err !== null && "name" in err)
86+
throw err;
87+
else throw new Test262Error(err);
88+
}.toString();
3289

3390
Runner.prototype.compile = function(test) {
91+
if (test.attrs.flags.raw) {
92+
return;
93+
}
94+
3495
// add call to $DONE at the bottom of the test file if it's not
3596
// present.
3697
if(test.contents.indexOf("$DONE") === -1) {
3798
// lead with a semicolon to prevent ASI nonsense.
3899
test.contents += "\n;$DONE();\n"
39100
}
40101

41-
test.contents = [
42-
this.test262ErrorSrc, this.errorFnSrc, test.contents
43-
].join(";\n");
102+
test.contents = this._errorSrc + "\n;" + test.contents;
44103

45-
this.deps.forEach(function(dep) {
46-
test.contents = dep + "\n" + test.contents;
47-
})
104+
if (this.needsCtrlFlow) {
105+
test.contents = this._ctrlFlowSrc + "\n;" + test.contents;
106+
}
48107

49108
this.link(test);
50109

@@ -88,24 +147,6 @@ Runner.prototype.link = function(test) {
88147
test.contents = includeContent + test.contents;
89148
}
90149

91-
Runner.prototype.test262ErrorSrc = function() {
92-
function Test262Error(message) {
93-
if (message) this.message = message;
94-
}
95-
96-
Test262Error.prototype.name = "Test262Error";
97-
98-
Test262Error.prototype.toString = function () {
99-
return "Test262Error: " + this.message;
100-
};
101-
}.toString().slice(14, -1);
102-
103-
Runner.prototype.errorFnSrc = function $ERROR(err) {
104-
if(typeof err === "object" && err !== null && "name" in err)
105-
throw err;
106-
else throw new Test262Error(err);
107-
}.toString();
108-
109150
var errorLogRe = /^test262\/error (.*)$/;
110151
// Result is expected to have the following keys:
111152
// errorString: Error of the form ErrorName: ErrorMessage. Optional.
@@ -178,7 +219,7 @@ Runner.prototype.validateResult = function(test, result) {
178219
}
179220
} else {
180221
// ensure $DONE was called if there wasn't an error reported
181-
if(!result.doneCalled) {
222+
if(!result.doneCalled && !test.attrs.flags.raw) {
182223
test.pass = false;
183224
test.errorName = "Test262 Error";
184225
test.errorMessage = "Test did not run to completion ($DONE not called)";

lib/runners/console.js

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ var fs = require('fs');
88
var cp = require('child_process');
99
var counter = 0;
1010

11-
var doneFn = function $DONE(err) {
12-
if(err) $ERROR(err);
13-
$LOG('test262/done');
14-
}.toString()
15-
1611
var batchDoneFn = function $DONE(err) {
1712
if(err) {
1813
if(typeof err === "object" && err !== null && "name" in err) {
@@ -32,22 +27,16 @@ function ConsoleRunner(args) {
3227
this.printCommand = args.consolePrintCommand || "console.log";
3328

3429
if(args.batch) {
35-
// Done comes from the parent context
36-
this.deps = [
37-
this.logFn
38-
]
30+
// Control flow functions are defined by the parent context
31+
this.needsCtrlFlow = false;
3932

4033
if(args.batchConfig) {
4134
this._createEnv = args.batchConfig.createEnv;
4235
this._runBatched = args.batchConfig.runBatched;
4336
this._setRealmValue = args.batchConfig.setRealmValue;
4437
}
45-
} else {
46-
this.deps = [
47-
doneFn,
48-
this.logFn
49-
]
5038
}
39+
5140
if(!this.command) throw "--consoleCommand option required for console runner";
5241
if (!this._setRealmValue) {
5342
this._setRealmValue = function(env, property, value) {
@@ -58,10 +47,16 @@ function ConsoleRunner(args) {
5847
Runner.apply(this, arguments);
5948
}
6049
ConsoleRunner.prototype = Object.create(Runner.prototype);
50+
51+
ConsoleRunner.prototype.doneFnSrc = function $DONE(err) {
52+
if(err) $ERROR(err);
53+
$LOG('test262/done');
54+
}.toString();
55+
6156
ConsoleRunner.prototype._print = function(str) {
6257
return this.printCommand + '(' + str + ');\n';
6358
}
64-
Object.defineProperty(ConsoleRunner.prototype, 'logFn', {
59+
Object.defineProperty(ConsoleRunner.prototype, 'logFnSrc', {
6560
get: memoize(function() {
6661
return 'function $LOG(str) { ' + this._print('str') + '}';
6762
})
@@ -73,13 +68,17 @@ Object.defineProperty(ConsoleRunner.prototype, 'runNextFn', {
7368
if(!this._runBatched) throw "Don't know how to run a batched tests";
7469

7570
var runNextFn = function runNext() {
76-
var test = tests.shift();
71+
var testInfo = tests.shift();
72+
var test = testInfo.contents;
7773
var env = $1;
7874
$setRealmValue(env, "$DONE", $DONE);
7975

8076
try {
8177
$LOG('test262/test-start')
8278
$2;
79+
if (testInfo.attrs.flags.raw) {
80+
$DONE();
81+
}
8382
} catch(e) {
8483
$DONE(e);
8584
}
@@ -115,13 +114,13 @@ ConsoleRunner.prototype.execute = function(test, cb) {
115114
ConsoleRunner.prototype.executeBatch = function(batch, batchDone) {
116115
var runner = this;
117116
var scriptFile = '__tmp' + counter++ + '.js';
118-
var script = this.logFn + '\n' +
117+
var script = this.logFnSrc + '\n' +
119118
batchDoneFn + '\n' +
120119
'var $setRealmValue = ' + this._setRealmValue + ';\n' +
121120
this.runNextFn + '\n';
122121

123122
script += 'var tests = ' + JSON.stringify(batch.map(function(test, i) {
124-
return test.contents
123+
return test
125124
})).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") + '\n';
126125

127126
script += 'runNext();'

lib/runners/node-ip.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ var Runner = require('../runner');
88

99
function NodeRunner() { Runner.apply(this, arguments); }
1010
NodeRunner.prototype = Object.create(Runner.prototype);
11+
12+
NodeRunner.prototype.needsCtrlFlow = false;
13+
1114
NodeRunner.prototype.execute = function(test, cb) {
1215
var contents = test.contents;
1316
var error;
@@ -41,6 +44,8 @@ NodeRunner.prototype.execute = function(test, cb) {
4144
result.errorName = "Error";
4245
result.errorMessage = error;
4346
}
47+
} else if (test.attrs.flags.raw) {
48+
context.$DONE();
4449
}
4550

4651
this.validateResult(test, result);

lib/runners/node.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ function NodeRunner(args) {
99
args.consoleCommand = args.consoleCommand || "node";
1010
var runner = this;
1111

12+
this.needsCtrlFlow = !!args.compileOnly;
13+
1214
ConsoleRunner.apply(this, arguments);
1315

1416
if(!args.compileOnly) {
15-
this.deps = []; // all env deps provided by nodehost.js
16-
1717
// HACK: Probably doesn't handle quoted arguments and other
1818
// complexities.
1919
var parts = args.consoleCommand.split(" ");
@@ -42,7 +42,7 @@ NodeRunner.prototype = Object.create(ConsoleRunner.prototype);
4242
NodeRunner.prototype.execute = function(test, cb) {
4343
this._test = test;
4444
this._testDone = cb;
45-
this._instance.stdin.write(test.contents);
45+
this._instance.stdin.write(JSON.stringify(test));
4646
}
4747

4848
NodeRunner.prototype.end = function() {

lib/runners/nodehost.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
var vm = require('vm');
22

33
process.stdin.resume();
4-
process.stdin.on('data', function(test) {
4+
process.stdin.on('data', function(testJSON) {
5+
var test = JSON.parse(testJSON);
56
var result = { log: [] }
67
var context = {
78
$DONE: function(error) {
@@ -25,7 +26,11 @@ process.stdin.on('data', function(test) {
2526
};
2627

2728
try {
28-
vm.runInNewContext(test, context, {displayErrors: false});
29+
vm.runInNewContext(test.contents, context, {displayErrors: false});
30+
31+
if (test.attrs.flags.raw) {
32+
context.$DONE();
33+
}
2934
} catch(e) {
3035
context.$DONE(e);
3136
}

test/collateral/rawNoStrict.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*---
2+
description: Should not test in strict mode
3+
flags: [raw]
4+
---*/
5+
var seemsStrict;
6+
try {
7+
x = 1;
8+
} catch (err) {
9+
seemsStrict = err.constructor === ReferenceError;
10+
}
11+
12+
if (seemsStrict) {
13+
throw new Error('Script erroneously interpreted in strict mode.');
14+
}

test/collateral/rawStrict.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*---
2+
description: Should not test in strict mode
3+
flags: [raw]
4+
---*/
5+
'use strict';
6+
var seemsStrict;
7+
try {
8+
x = 1;
9+
} catch (err) {
10+
seemsStrict = err.constructor === ReferenceError;
11+
}
12+
13+
if (!seemsStrict) {
14+
throw new Error('Script erroneously not interpreted in strict mode.');
15+
}

test/expected.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ var all = [
77
{ file: 'test/collateral/bothStrict.js', strictMode: true, pass: true },
88
{ file: 'test/collateral/strict.js', strictMode: true, pass: true },
99
{ file: 'test/collateral/noStrict.js', strictMode: false, pass: true },
10+
{ file: 'test/collateral/rawStrict.js', strictMode: false, pass: true },
11+
{ file: 'test/collateral/rawStrict.js', strictMode: true, pass: true },
12+
{ file: 'test/collateral/rawNoStrict.js', strictMode: false, pass: true },
13+
{ file: 'test/collateral/rawNoStrict.js', strictMode: true, pass: true },
1014
{ file: 'test/collateral/error.js', strictMode: false, pass: false, errorMessage: 'failure message', errorName: 'Test262Error' },
1115
{ file: 'test/collateral/error.js', strictMode: true, pass: false, errorMessage: 'failure message', errorName: 'Test262Error' },
1216
{ file: 'test/collateral/thrownError.js', strictMode: false, pass: false, errorMessage: 'failure message', errorName: 'Error', topOfStack: "foo" },

0 commit comments

Comments
 (0)