Clone engine262 in /engine262

This commit is contained in:
2020-09-07 10:19:00 +05:30
parent 7688de2e5c
commit 66c1aeb9e0
313 changed files with 46014 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
'use strict';
const readline = require('readline');
const os = require('os');
process.on('unhandledRejection', (reason) => {
require('fs').writeSync(0, `\n${require('util').inspect(reason)}\n`);
process.exit(1);
});
const CI = !!process.env.CONTINUOUS_INTEGRATION;
const ANSI = CI ? {
reset: '',
red: '',
green: '',
yellow: '',
blue: '',
} : {
reset: '\u001b[0m',
red: '\u001b[31m',
green: '\u001b[32m',
yellow: '\u001b[33m',
blue: '\u001b[34m',
};
const CPU_COUNT = os.cpus().length;
let skipped = 0;
let passed = 0;
let failed = 0;
let total = 0;
const start = Date.now();
const handledPerSecLast5 = [];
const pad = (n, l, c = '0') => n.toString().padStart(l, c);
const average = (array) => (array.reduce((a, b) => a + b, 0) / array.length) || 0;
const printStatusLine = () => {
const elapsed = Math.floor((Date.now() - start) / 1000);
const min = Math.floor(elapsed / 60);
const sec = elapsed % 60;
const time = `${pad(min, 2)}:${pad(sec, 2)}`;
const found = `${ANSI.blue}:${pad(total, 5, ' ')}${ANSI.reset}`;
const p = `${ANSI.green}+${pad(passed, 5, ' ')}${ANSI.reset}`;
const f = `${ANSI.red}-${pad(failed, 5, ' ')}${ANSI.reset}`;
const s = `${ANSI.yellow}»${pad(skipped, 5, ' ')}${ANSI.reset}`;
const testsPerSec = average(handledPerSecLast5);
const line = `[${time}|${found}|${p}|${f}|${s}] (${testsPerSec.toFixed(2)}/s)`;
if (!CI) {
readline.clearLine(process.stdout, 0);
readline.cursorTo(process.stdout, 0);
}
process.stdout.write(`${line}${CI ? '\n' : ''}`);
};
let handledPerSecCounter = 0;
setInterval(() => {
handledPerSecLast5.unshift(handledPerSecCounter);
handledPerSecCounter = 0;
if (handledPerSecLast5.length > 5) {
handledPerSecLast5.length = 5;
}
}, 1000).unref();
module.exports = {
total() {
total += 1;
},
pass() {
passed += 1;
handledPerSecCounter += 1;
},
fail(name, error) {
failed += 1;
handledPerSecCounter += 1;
process.exitCode = 1;
process.stderr.write(`\nFAILURE! ${name}\n${error}\n`);
},
skip() {
skipped += 1;
handledPerSecCounter += 1;
},
CPU_COUNT,
CI,
};
process.stdout.write(`
#######################
engine262 Test Runner
Detected ${CPU_COUNT} CPUs
${CI ? 'Running' : 'Not running'} on CI
#######################
`);
printStatusLine();
setInterval(() => {
printStatusLine();
}, CI ? 5000 : 500).unref();
process.on('exit', () => {
printStatusLine();
process.stdout.write('\n');
});
@@ -0,0 +1,9 @@
'use strict';
module.exports = {
rules: {
'no-use-in-def': require('./no-use-in-def'),
'valid-feature': require('./valid-feature'),
'valid-throw': require('./valid-throw'),
},
};
@@ -0,0 +1,68 @@
'use strict';
// https://github.com/eslint/eslint/blob/master/lib/rules/no-use-before-define.js
const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;
function isInRange(node, location) {
return node && node.range[0] <= location && location <= node.range[1];
}
function isUsedInDef(reference) {
const variable = reference.resolved;
if (!variable || variable.scope !== reference.from) {
return false;
}
let node = variable.identifiers[0].parent;
const location = reference.identifier.range[1];
while (node) {
if (node.type === 'VariableDeclarator') {
if (isInRange(node.init, location)) {
return true;
}
if (FOR_IN_OF_TYPE.test(node.parent.parent.type) && isInRange(node.parent.parent.right, location)) {
return true;
}
break;
}
if (node.type === 'AssignmentPattern' && isInRange(node.right, location)) {
return true;
}
if (SENTINEL_TYPE.test(node.type)) {
break;
}
node = node.parent;
}
return false;
}
module.exports = {
create(context) {
function findVariablesInScope(scope) {
scope.references.forEach((reference) => {
if (isUsedInDef(reference)) {
context.report({
node: reference.identifier,
message: '{{name}} was used in its own definition',
data: reference.identifier,
});
}
});
scope.childScopes.forEach((s) => findVariablesInScope(s));
}
return {
Program() {
findVariablesInScope(context.getScope());
},
};
},
};
@@ -0,0 +1,32 @@
'use strict';
let features;
try {
features = require('../..').FEATURES.map((f) => f.flag);
} catch {}
function isFeatureCall(node) {
return node.callee.type === 'MemberExpression'
&& node.callee.computed === false
&& node.callee.property.name === 'feature';
}
module.exports = {
create(context) {
return {
CallExpression(node) {
if (!isFeatureCall(node)) {
return;
}
if (node.arguments.length !== 1) {
context.report(node, 'Invalid arguments passed to feature()');
return;
}
const featureName = node.arguments[0].value;
if (!features.includes(featureName)) {
context.report(node.arguments[0], `'${featureName}' is not a valid feature. Check src/engine.mjs.`);
}
},
};
},
};
@@ -0,0 +1,63 @@
'use strict';
const fs = require('fs');
const path = require('path');
const acorn = require('acorn');
function isThrowCall(node) {
return node.callee.type === 'MemberExpression'
&& node.callee.computed === false
&& node.callee.object.type === 'Identifier'
&& node.callee.object.name === 'surroundingAgent'
&& node.callee.property.type === 'Identifier'
&& node.callee.property.name === 'Throw';
}
const templates = {};
{
const source = fs.readFileSync(path.join(__dirname, '../../src/messages.mjs'), 'utf8');
const ast = acorn.parse(source, { ecmaVersion: 2020, sourceType: 'module' });
ast.body.forEach((n) => {
if (n.type !== 'ExportNamedDeclaration') {
return;
}
const [v] = n.declaration.declarations;
const name = v.id.name;
const length = v.init.params.length;
templates[name] = length;
});
}
module.exports = {
create(context) {
return {
CallExpression(node) {
if (!isThrowCall(node)) {
return;
}
if (node.arguments.length === 1 && node.arguments[0].type !== 'Literal') {
return;
}
const [type, template, ...templateArgs] = node.arguments;
if (!type || type.type !== 'Literal') {
context.report(node, 'Throw must use a valid error constructor');
return;
}
if (!template || template.type !== 'Literal') {
context.report(node, 'Throw must use a valid message template');
return;
}
const tfn = templates[template.value];
if (tfn === undefined) {
context.report(template, `'${template.value}' is not a valid message template`);
return;
}
if (tfn !== templateArgs.length) {
context.report(node, `Template expects ${tfn} args`);
}
},
};
},
};
+64
View File
@@ -0,0 +1,64 @@
'use strict';
/* eslint-disable no-await-in-loop */
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const {
pass, fail, skip, total,
} = require('../base');
const {
Agent,
setSurroundingAgent,
ManagedRealm,
AbruptCompletion,
inspect,
} = require('../..');
const BASE_DIR = path.resolve(__dirname, 'JSONTestSuite');
const agent = new Agent();
setSurroundingAgent(agent);
function test(filename) {
const realm = new ManagedRealm();
const source = fs.readFileSync(filename, 'utf8');
let result;
try {
result = realm.evaluateScript(`'use strict';
const source = ${JSON.stringify(source)};
JSON.parse(source);
`);
} catch {
// ...
}
const testName = path.basename(filename);
if (!result || result instanceof AbruptCompletion) {
if (testName.startsWith('n_')) {
pass();
} else if (testName.startsWith('i_')) {
skip();
} else {
fail(testName, inspect(result));
}
} else {
if (testName.startsWith('n_')) {
fail(testName, 'JSON parsed but should have failed!');
} else {
pass();
}
}
}
const tests = glob.sync(`${path.resolve(BASE_DIR, 'test_parsing')}/**/*.json`)
.concat(glob.sync(`${path.resolve(BASE_DIR, 'test_transform')}/**/*.json`));
tests.forEach((t) => {
total();
test(t);
});
+66
View File
@@ -0,0 +1,66 @@
'use strict';
require('@snek/source-map-support/register');
const {
isMainThread, parentPort, workerData, Worker,
} = require('worker_threads');
const fs = require('fs');
// eslint-disable-next-line import/no-extraneous-dependencies
const { codeFrameColumns } = require('@babel/code-frame');
if (isMainThread) {
const shared = new SharedArrayBuffer(4);
const shared32 = new Int32Array(shared);
const source = fs.readFileSync(process.argv[2], 'utf8');
const worker = new Worker(__filename, {
workerData: { shared, source },
});
process.stdin.on('data', () => {
const old = Atomics.compareExchange(shared32, 0, 0, 1);
if (old === 0) {
Atomics.notify(shared32, 0, 1);
}
});
worker.on('message', (data) => {
const node = JSON.parse(data);
const frame = codeFrameColumns(source, node.location, {
highlightCode: true,
message: node.type,
});
process.stdout.write(`${frame}\n\n\n`);
});
worker.on('exit', () => {
process.exit(0);
});
} else {
const {
Agent,
setSurroundingAgent,
ManagedRealm,
AbruptCompletion,
inspect,
} = require('..');
const shared32 = new Int32Array(workerData.shared);
setSurroundingAgent(new Agent({
onNodeEvaluation(node) {
if (node.type === 'ExpressionStatement') {
return;
}
parentPort.postMessage(JSON.stringify(node));
Atomics.wait(shared32, 0, 0);
Atomics.store(shared32, 0, 0);
},
}));
const realm = new ManagedRealm();
realm.scope(() => {
const completion = realm.evaluateScript(workerData.source);
if (completion instanceof AbruptCompletion) {
process.stdout.write(`${inspect(completion, realm)}\n`);
}
});
process.exit(0);
}
+267
View File
@@ -0,0 +1,267 @@
'use strict';
require('@snek/source-map-support/register');
const assert = require('assert');
const {
Agent,
setSurroundingAgent,
ManagedRealm,
Value,
FEATURES,
Get,
CreateArrayFromList,
CreateDataProperty,
} = require('..');
const test262realm = require('../bin/test262_realm');
const { total, pass, fail } = require('./base');
// Features that cannot be tested by test262 should go here.
[
() => {
const agent = new Agent();
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript('debugger;');
assert.strictEqual(result.Value, Value.undefined);
},
() => {
const agent = new Agent({
onDebugger() {
return new Value(42);
},
});
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript('debugger;');
assert.strictEqual(result.Value.numberValue(), 42);
},
() => {
const agent = new Agent();
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript(`\
function x() { throw new Error('owo'); }
function y() { x(); }
try {
y();
} catch (e) {
e.stack;
}
`);
assert.strictEqual(result.Value.stringValue(), `\
Error: owo
at x (<anonymous>:1:32)
at y (<anonymous>:2:16)
at <anonymous>:4:3`);
},
() => {
const agent = new Agent();
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript(`\
async function x() { await 1; throw new Error('owo'); }
async function y() { await x(); }
y().catch((e) => e.stack);
`);
assert.strictEqual(result.Value.PromiseResult.stringValue(), `\
Error: owo
at async x (<anonymous>:1:47)
at async y (<anonymous>:2:28)`);
},
() => {
const agent = new Agent();
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript(`\
function x() { Reflect.get(); }
try {
x();
} catch (e) {
e.stack;
}
`);
assert.strictEqual(result.Value.stringValue(), `\
TypeError: undefined is not an object
at get (native)
at x (<anonymous>:1:16)
at <anonymous>:3:3`);
},
() => {
const agent = new Agent();
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript(`\
function Y() { throw new Error('owo'); }
function x() { new Y(); }
try {
x();
} catch (e) {
e.stack;
}
`);
assert.strictEqual(result.Value.stringValue(), `\
Error: owo
at new Y (<anonymous>:1:32)
at x (<anonymous>:2:20)
at <anonymous>:4:3`);
},
() => {
const agent = new Agent();
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript(`\
let e;
new Promise(() => {
e = new Error('owo');
});
e.stack;
`);
assert.strictEqual(result.Value.stringValue(), `\
Error: owo
at <anonymous> (<anonymous>:3:17)
at new Promise (native)
at <anonymous>:2:13`);
},
() => {
const agent = new Agent({
features: ['WeakRefs'],
});
setSurroundingAgent(agent);
const realm = new ManagedRealm();
const result = realm.evaluateScript(`
const w = new WeakRef({});
Promise.resolve()
.then(() => {
if (typeof w.deref() !== 'object') {
throw new Error();
}
})
.then(() => {
if (typeof w.deref() !== 'undefined') {
throw new Error();
}
})
.then(() => 'pass');
`);
assert.strictEqual(result.Value.PromiseResult.stringValue(), 'pass');
},
() => {
const agent = new Agent({
features: ['WeakRefs'],
});
setSurroundingAgent(agent);
const realm = new ManagedRealm();
realm.scope(() => {
const module = realm.createSourceTextModule('test.mjs', `
const w = new WeakRef({});
globalThis.result = Promise.resolve()
.then(() => {
if (typeof w.deref() !== 'object') {
throw new Error('should be object');
}
})
.then(() => {
if (typeof w.deref() !== 'undefined') {
throw new Error('should be undefined');
}
})
.then(() => 'pass');
`);
module.Link();
module.Evaluate();
const result = Get(realm.GlobalObject, new Value('result'));
assert.strictEqual(result.Value.PromiseResult.stringValue(), 'pass');
});
},
() => {
const agent = new Agent({
features: FEATURES.map((f) => f.name),
});
setSurroundingAgent(agent);
const { realm } = test262realm.createRealm();
realm.scope(() => {
CreateDataProperty(
realm.GlobalObject,
new Value('fail'),
new Value(([path]) => {
throw new Error(`${path.stringValue()} did not have a section`);
}),
);
const targets = [];
Object.entries(realm.Intrinsics)
.forEach(([k, v]) => {
targets.push(CreateArrayFromList([new Value(k), v]));
});
CreateDataProperty(
realm.GlobalObject,
new Value('targets'),
CreateArrayFromList(targets),
);
});
const result = realm.evaluateScript(`
'use strict';
{
const targets = globalThis.targets;
delete globalThis.targets;
const fail = globalThis.fail;
delete globalThis.fail;
const topQueue = new Set();
const scanned = new Set();
const scan = (ns, path) => {
if (scanned.has(ns)) {
return;
}
scanned.add(ns);
if (typeof ns === 'function') {
if ($262.spec(ns) === undefined) {
fail(path);
}
}
if (typeof ns !== 'function' && (typeof ns !== 'object' || ns === null)) {
return;
}
const descriptors = Object.getOwnPropertyDescriptors(ns);
Reflect.ownKeys(descriptors)
.forEach((name) => {
const desc = descriptors[name];
const p = typeof name === 'symbol'
? path + '[Symbol(' + name.description + ')]'
: path + '.' + name;
if ('value' in desc) {
if (!topQueue.has(desc.value)) {
scan(desc.value, p);
}
} else {
if (!topQueue.has(desc.get)) {
scan(desc.get, p);
}
if (!topQueue.has(desc.set)) {
scan(desc.set, p);
}
}
});
};
targets.forEach((t) => {
topQueue.add(t[1]);
});
targets.forEach((t) => {
scan(t[1], t[0]);
});
}
`);
assert.strictEqual(result.Value, Value.undefined);
},
].forEach((test) => {
total();
try {
test();
pass();
} catch (e) {
fail('', e.stack || e);
}
});
+35
View File
@@ -0,0 +1,35 @@
# https://github.com/tc39/test262/blob/master/features.txt
# Start with `-` to skip feature
# Map feature to engine262 feature using `feature = engine262featurename`
-Atomics
-Atomics.waitAsync
-caller
-SharedArrayBuffer
-tail-call-optimization
-class-fields-public
-class-fields-private
-class-methods-private
-class-static-fields-public
-class-static-fields-private
-class-static-methods-private
# https://github.com/tc39/proposal-top-level-await
top-level-await = top-level-await
# https://github.com/tc39/proposal-hashbang
hashbang = hashbang
# https://github.com/tc39/proposal-numeric-separator
numeric-separator-literal = numeric-separators
# https://github.com/tc39/proposal-regexp-match-indices
regexp-match-indices = regexp-match-indices
# https://github.com/tc39/proposal-cleanup-some
cleanupSome = cleanup-some
+59
View File
@@ -0,0 +1,59 @@
#####################
### Skipped Tests ###
#####################
# Comments start with `#`.
# Paths are relative to the `test262/test` directory.
# Paths may contain globs.
# Spec bug
built-ins/Date/prototype/setMonth/this-value-invalid-date.js
# Spec bug
built-ins/Function/prototype/bind/instance-length-tointeger.js
# https://github.com/tc39/test262/issues/427
language/expressions/prefix-increment/S11.4.4_A5_T4.js
language/expressions/prefix-increment/S11.4.4_A5_T5.js
language/expressions/postfix-increment/S11.3.1_A5_T4.js
language/expressions/postfix-increment/S11.3.1_A5_T5.js
language/expressions/prefix-decrement/S11.4.5_A5_T4.js
language/expressions/prefix-decrement/S11.4.5_A5_T5.js
language/expressions/postfix-decrement/S11.3.2_A5_T4.js
language/expressions/postfix-decrement/S11.3.2_A5_T5.js
language/expressions/compound-assignment/S11.13.2_A5.1_T4.js
language/expressions/compound-assignment/S11.13.2_A5.4_T4.js
language/expressions/compound-assignment/S11.13.2_A5.7_T4.js
language/expressions/compound-assignment/S11.13.2_A5.10_T4.js
language/expressions/compound-assignment/S11.13.2_A5.11_T4.js
language/expressions/compound-assignment/S11.13.2_A5.2_T4.js
language/expressions/compound-assignment/S11.13.2_A5.3_T4.js
language/expressions/compound-assignment/S11.13.2_A5.5_T4.js
language/expressions/compound-assignment/S11.13.2_A5.6_T4.js
language/expressions/compound-assignment/S11.13.2_A5.9_T4.js
language/expressions/compound-assignment/S11.13.2_A5.8_T4.js
language/expressions/compound-assignment/S11.13.2_A5.1_T5.js
language/expressions/compound-assignment/S11.13.2_A5.2_T5.js
language/expressions/compound-assignment/S11.13.2_A5.3_T5.js
language/expressions/compound-assignment/S11.13.2_A5.4_T5.js
language/expressions/compound-assignment/S11.13.2_A5.5_T5.js
language/expressions/compound-assignment/S11.13.2_A5.6_T5.js
language/expressions/compound-assignment/S11.13.2_A5.7_T5.js
language/expressions/compound-assignment/S11.13.2_A5.8_T5.js
language/expressions/compound-assignment/S11.13.2_A5.9_T5.js
language/expressions/compound-assignment/S11.13.2_A5.10_T5.js
language/expressions/compound-assignment/S11.13.2_A5.11_T5.js
language/expressions/assignment/S11.13.1_A5_T5.js
language/expressions/assignment/S11.13.1_A5_T4.js
# https://github.com/tc39/ecma262/issues/1426
built-ins/String/prototype/replace/S15.5.4.11_A3_T*.js
# We need our own date parser
built-ins/Date/parse/without-utc-offset.js
# TODO: RegExp
built-ins/RegExp/property-escapes/**/*.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier.js
+26
View File
@@ -0,0 +1,26 @@
##################
### Slow Tests ###
##################
# Comments start with `#`.
# Paths are relative to the `test262/test` directory.
# Paths may contain globs.
built-ins/RegExp/character-class-escape-non-whitespace.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-flags-u.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-plus-quantifier-flags-u.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-flags-u.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-plus-quantifier-flags-u.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-flags-u.js
built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-plus-quantifier-flags-u.js
language/literals/regexp/S7.8.5_A1.1_T2.js
language/literals/regexp/S7.8.5_A1.4_T2.js
language/literals/regexp/S7.8.5_A2.1_T2.js
language/literals/regexp/S7.8.5_A2.4_T2.js
language/comments/S7.4_A5.js
language/comments/S7.4_A6.js
built-ins/{encode,decode}URI?(Component)/**/*.js
built-ins/Function/prototype/toString/built-in-function-object.js
+324
View File
@@ -0,0 +1,324 @@
'use strict';
try {
require('@snek/source-map-support/register');
} catch {}
const path = require('path');
const fs = require('fs');
const util = require('util');
const glob = require('glob');
const readList = (name) => {
const source = fs.readFileSync(path.resolve(__dirname, name), 'utf8');
return source.split('\n').filter((l) => l && !l.startsWith('#'));
};
const readListPaths = (name) => readList(name)
.flatMap((t) => glob.sync(path.resolve(__dirname, 'test262', 'test', t)))
.map((f) => path.relative(path.resolve(__dirname, 'test262'), f));
const disabledFeatures = [];
const featureMap = {};
readList('features')
.forEach((f) => {
if (f.startsWith('-')) {
disabledFeatures.push(f.slice(1));
}
if (f.includes('=')) {
const [k, v] = f.split('=');
featureMap[k.trim()] = v.trim();
}
});
if (!process.send) {
// supervisor
const childProcess = require('child_process');
const TestStream = require('test262-stream');
const {
pass,
fail,
skip,
total,
CPU_COUNT,
} = require('../base');
const override = process.argv.find((e, i) => i > 1 && !e.startsWith('-'));
const NUM_WORKERS = process.env.NUM_WORKERS
? Number.parseInt(process.env.NUM_WORKERS, 10)
: Math.round(CPU_COUNT * 0.75);
const RUN_SLOW_TESTS = process.argv.includes('--run-slow-tests');
const createWorker = () => {
const c = childProcess.fork(__filename);
c.on('message', (message) => {
const { description, status, error } = message;
switch (status) {
case 'PASS':
pass();
break;
case 'FAIL':
fail(description, error);
break;
case 'SKIP':
skip();
break;
default:
throw new RangeError(JSON.stringify(message));
}
});
c.on('exit', (code) => {
if (code !== 0) {
process.exit(1);
}
});
return c;
};
const workers = Array.from({ length: NUM_WORKERS }, () => createWorker());
let longRunningWorker;
if (RUN_SLOW_TESTS) {
longRunningWorker = createWorker();
}
const slowlist = readListPaths('slowlist');
const skiplist = readListPaths('skiplist');
const stream = new TestStream(path.resolve(__dirname, 'test262'), {
paths: [override || 'test'],
omitRuntime: true,
});
let workerIndex = 0;
stream.on('data', (test) => {
if (test.attrs.flags.module && test.scenario !== 'default') {
// test262-stream duplicates module tests, deduplicate here
return;
}
if (/annexB|intl402/.test(test.file)) {
return;
}
total();
if ((test.attrs.features && test.attrs.features.some((feature) => disabledFeatures.includes(feature)))
|| skiplist.includes(test.file)) {
skip();
return;
}
if (slowlist.includes(test.file)) {
if (RUN_SLOW_TESTS) {
longRunningWorker.send(test);
} else {
skip();
}
} else {
workers[workerIndex].send(test);
workerIndex += 1;
if (workerIndex >= workers.length) {
workerIndex = 0;
}
}
});
stream.on('end', () => {
workers.forEach((w) => {
w.send('DONE');
});
if (RUN_SLOW_TESTS) {
longRunningWorker.send('DONE');
}
});
} else {
// worker
const {
Agent,
setSurroundingAgent,
inspect,
Value,
IsCallable,
IsDataDescriptor,
Type,
AbruptCompletion,
Throw,
} = require('../..');
const { createRealm } = require('../../bin/test262_realm');
const isError = (type, value) => {
if (Type(value) !== 'Object') {
return false;
}
const proto = value.Prototype;
if (!proto || Type(proto) !== 'Object') {
return false;
}
const ctorDesc = proto.properties.get(new Value('constructor'));
if (!ctorDesc || !IsDataDescriptor(ctorDesc)) {
return false;
}
const ctor = ctorDesc.Value;
if (Type(ctor) !== 'Object' || IsCallable(ctor) !== Value.true) {
return false;
}
const namePropDesc = ctor.properties.get(new Value('name'));
if (!namePropDesc || !IsDataDescriptor(namePropDesc)) {
return false;
}
const nameProp = namePropDesc.Value;
return Type(nameProp) === 'String' && nameProp.stringValue() === type;
};
const includeCache = {};
const run = (test) => {
const features = [];
if (test.attrs.features) {
test.attrs.features.forEach((f) => {
if (featureMap[f]) {
features.push(featureMap[f]);
}
});
}
const agent = new Agent({
features,
});
setSurroundingAgent(agent);
const {
realm, trackedPromises,
resolverCache, setPrintHandle,
} = createRealm({ file: test.file });
const r = realm.scope(() => {
test.attrs.includes.unshift('assert.js', 'sta.js');
if (test.attrs.flags.async) {
test.attrs.includes.unshift('doneprintHandle.js');
}
for (const include of test.attrs.includes) {
if (includeCache[include] === undefined) {
const p = path.resolve(__dirname, `./test262/harness/${include}`);
includeCache[include] = {
source: fs.readFileSync(p, 'utf8'),
specifier: p,
};
}
const entry = includeCache[include];
const completion = realm.evaluateScript(entry.source, { specifier: entry.specifier });
if (completion instanceof AbruptCompletion) {
return { status: 'FAIL', error: inspect(completion) };
}
}
{
const completion = realm.evaluateScript(`\
var Test262Error = class Test262Error extends Error {};
function $DONE(error) {
if (error) {
if (typeof error === 'object' && error !== null && 'stack' in error) {
__consolePrintHandle__('Test262:AsyncTestFailure:' + error.stack);
} else {
__consolePrintHandle__('Test262:AsyncTestFailure:Test262Error: ' + error);
}
} else {
__consolePrintHandle__('Test262:AsyncTestComplete');
}
}`);
if (completion instanceof AbruptCompletion) {
return { status: 'FAIL', error: inspect(completion) };
}
}
let asyncResult;
if (test.attrs.flags.async) {
setPrintHandle((m) => {
if (m.stringValue && m.stringValue() === 'Test262:AsyncTestComplete') {
asyncResult = { status: 'PASS' };
} else {
asyncResult = { status: 'FAIL', error: m.stringValue ? m.stringValue() : inspect(m) };
}
setPrintHandle(undefined);
});
}
const specifier = path.resolve(__dirname, 'test262', test.file);
let completion;
if (test.attrs.flags.module) {
completion = realm.createSourceTextModule(specifier, test.contents);
if (!(completion instanceof AbruptCompletion)) {
const module = completion;
resolverCache.set(specifier, module);
completion = module.Link();
if (!(completion instanceof AbruptCompletion)) {
completion = module.Evaluate();
}
if (!(completion instanceof AbruptCompletion)) {
if (completion.PromiseState === 'rejected') {
completion = Throw(completion.PromiseResult);
}
}
}
} else {
completion = realm.evaluateScript(test.contents, { specifier });
}
if (completion instanceof AbruptCompletion) {
if (test.attrs.negative && isError(test.attrs.negative.type, completion.Value)) {
return { status: 'PASS' };
} else {
return { status: 'FAIL', error: inspect(completion) };
}
}
if (test.attrs.flags.async) {
if (!asyncResult) {
throw new Error('missing async result');
}
return asyncResult;
}
if (trackedPromises.length > 0) {
return { status: 'FAIL', error: inspect(trackedPromises[0]) };
}
if (test.attrs.negative) {
return { status: 'FAIL', error: `Expected ${test.attrs.negative.type} during ${test.attrs.negative.phase}` };
} else {
return { status: 'PASS' };
}
});
return r;
};
let p = Promise.resolve();
const handleSendError = (e) => {
if (e) {
process.exit(1);
}
};
process.on('message', (test) => {
if (test === 'DONE') {
p.then(() => process.exit(0));
p = undefined;
} else {
const description = `${test.file}\n${test.attrs.description}`;
p = p
.then(() => run(test))
.then((r) => {
process.send({ description, ...r }, handleSendError);
})
.catch((e) => {
process.send({ description, status: 'FAIL', error: util.inspect(e) }, handleSendError);
});
}
});
}
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
set -x
E=0
npm run test:test262 || E=$?
npm run test:json || E=$?
npm run test:supplemental || E=$?
exit $E