From ca417c93dc8c4d97b08284dd3c763e6389a7f2c1 Mon Sep 17 00:00:00 2001 From: Mark Miller Date: Wed, 7 Sep 2011 21:42:49 -0700 Subject: [PATCH] Now generating the format David Fugate and I agreed on this morning. The rewrite of "function testcase()..." now puts the assertTrue at the bottom. Preconditions, names, and ids are removed. And the path in the file is ignored, and is instead set accurately according to where the file is found. --- test/harness/sth.js | 94 ++++++------ tools/SputnikConverter/ES5TestCase.cs | 2 +- tools/converter/convert.js | 209 ++++++++++++++++---------- tools/converter/v8PosixPlatform.js | 118 +++++++++++---- 4 files changed, 270 insertions(+), 153 deletions(-) diff --git a/test/harness/sth.js b/test/harness/sth.js index 7ae7d20780..9053ea6427 100644 --- a/test/harness/sth.js +++ b/test/harness/sth.js @@ -1,14 +1,14 @@ -/// Copyright (c) 2009 Microsoft Corporation -/// +/// Copyright (c) 2009 Microsoft Corporation +/// /// Redistribution and use in source and binary forms, with or without modification, are permitted provided -/// that the following conditions are met: +/// that the following conditions are met: /// * Redistributions of source code must retain the above copyright notice, this list of conditions and -/// the following disclaimer. -/// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and -/// the following disclaimer in the documentation and/or other materials provided with the distribution. +/// the following disclaimer. +/// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and +/// the following disclaimer in the documentation and/or other materials provided with the distribution. /// * Neither the name of Microsoft nor the names of its contributors may be used to /// endorse or promote products derived from this software without specific prior written permission. -/// +/// /// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR /// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS /// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE @@ -16,7 +16,7 @@ /// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS /// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, /// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -/// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //Do not cache any JSON files - see https://bugs.ecmascript.org/show_bug.cgi?id=87 @@ -52,7 +52,7 @@ function BrowserRunner() { if(currentTest.error instanceof SputnikError) { currentTest.error = currentTest.message; } else { - currentTest.error = currentTest.error.name + ": " + currentTest.error.message + currentTest.error = currentTest.error.name + ": " + currentTest.error.message; } } @@ -94,7 +94,7 @@ function BrowserRunner() { // Set up some globals. win.testRun = testRun; win.testFinished = testFinished; - + //TODO: these should be moved to sta.js win.SputnikError = SputnikError; win.$ERROR = $ERROR; @@ -112,59 +112,59 @@ function BrowserRunner() { $.ajax({ async: false, url: 'resources/scripts/global/' + include, - success: function(s) { scriptCache[include] = s } - }) + success: function(s) { scriptCache[include] = s; } + }); } // Finally, write the required script to the window. doc.writeln(""); } } - + //Write out all of our helper functions doc.writeln(""); - - - + + + //--Scenario 1: we're dealing with a global scope test case if (GlobalScopeTests[id]!==undefined) { win.iframeError = undefined; win.onerror = undefined; win.onErrorHack = undefined; var testDescrip = GlobalScopeTests[id]; - + //Add an error handler doc.writeln(""); //Parse and execute the code doc.writeln(""); - + //validation if (testDescrip.negative!==undefined) { //An exception is expected if (win.onErrorHack===undefined) { //Hack for browsers not supporting window.onerror WRT early parse errors - testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', + testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', 'pass', 'Not parsable'); } else if (win.iframeError===undefined) { //no exception was thrown - testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', + testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', 'fail', 'No Exception Thrown'); } else if(! (new RegExp(testDescrip.negative, "i").test(win.iframeError))) { //wrong type of exception thrown - testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', + testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', 'fail', 'Wrong Type of Exception Thrown'); } else { - testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', + testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', 'pass', undefined); } } else if (win.iframeError!==undefined) { //Exception was not expected to be thrown - testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', + testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', 'fail', 'Unexpected Exception'); } else { - testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', + testRun(testDescrip.id, testDescrip.path, testDescrip.description, code, typeof testDescrip.precondition !== 'undefined' ? testDescrip.precondition.toString() : '', 'pass', undefined); - } + } } //--Scenario 2: we're dealing with a normal positive(?) test case else { - + // Write ES5Harness.registerTest and fnGlobalObject, which returns the global object, and the testFinished call. doc.writeln(""); doc.close(); - } + }; } /* Loads tests from the sections specified in testcaseslist.json. @@ -219,9 +219,9 @@ function TestLoader() { $.ajax({url: group.path, dataType: 'json', success: function(data) { group.tests = data.testsCollection.tests; loader.getNextTest(); - }, + }, error: function (XMLHttpRequest, textStatus, errorThrown) { - //alert(XMLHttpRequest); + //alert(XMLHttpRequest); } }); @@ -238,13 +238,13 @@ function TestLoader() { loader.version = data.version; loader.date = data.date; - loader.totalTests = data.numTests; + loader.totalTests = data.numTests; for (var i = 0; i < testSuite.length; i++) { testGroups[i] = { path: testSuite[i], tests: [] - } + }; } loader.onInitialized(loader.totalTests, loader.version, loader.date); getNextXML(); @@ -271,13 +271,13 @@ function TestLoader() { // We're done. loader.onTestsExhausted(); } - } + }; /* Start over at the beginning */ this.reset = function() { currentTestIndex = 0; testGroupIndex = 0; - } + }; } /* Controls test generation and running, and sends results to the presenter. */ @@ -294,22 +294,22 @@ function Controller() { if(state === 'running') setTimeout(loader.getNextTest, 10); - } + }; loader.onInitialized = function(totalTests, version, date) { presenter.setVersion(version); presenter.setDate(date); presenter.setTotalTests(totalTests); - } + }; loader.onLoadingNextSection = function(path) { presenter.updateStatus("Loading: " + path); - } + }; loader.onTestReady = function(id, test) { presenter.updateStatus("Executing Test: " + id); runner.run(id, test); - } + }; loader.onTestsExhausted = function() { state = 'stopped'; @@ -317,27 +317,27 @@ function Controller() { elapsed = elapsed/(1000*60); //minutes elapsed = elapsed.toFixed(1); presenter.finished(elapsed); - } + }; this.start = function() { state = 'running'; startTime = new Date(); loader.getNextTest(); presenter.started(); - } - + }; + this.pause = function() { elapsed += new Date() - startTime; state = 'paused'; presenter.paused(); - } + }; this.reset = function() { startTime = new Date(); elapsed = 0; loader.reset(); presenter.reset(); - } + }; this.toggle = function() { if(state === 'running') { @@ -345,16 +345,16 @@ function Controller() { } else { controller.start(); } - } + }; } -var controller = new Controller() +var controller = new Controller(); /* Helper function which shows if we're in the 'debug' mode of the Test262 site. - This mode is only useful for debugging issues with the test harness and + This mode is only useful for debugging issues with the test harness and website. */ function isSiteDebugMode() { - var str=window.location.href.substring(window.location.href.indexOf("?")+1) + var str=window.location.href.substring(window.location.href.indexOf("?")+1); if(str.indexOf("sitedebug") > -1) { return true; } @@ -385,7 +385,7 @@ $(function () { $(this).attr('targetDiv', '.content-browsers'); } - //Attaching the click event to the header tab that shows the respective div of header + //Attaching the click event to the header tab that shows the respective div of header $(this).click(function () { var target = $(this).attr('targetDiv'); $('#contentContainer > div:visible').hide(); diff --git a/tools/SputnikConverter/ES5TestCase.cs b/tools/SputnikConverter/ES5TestCase.cs index db39133a74..bbe4ba0463 100644 --- a/tools/SputnikConverter/ES5TestCase.cs +++ b/tools/SputnikConverter/ES5TestCase.cs @@ -139,7 +139,7 @@ namespace Microsoft.Sputnik.Interop.ParserEngine } FileStream fs = new FileStream(destination.Remove(destination.LastIndexOf("\\")) + globalScopeFileName, FileMode.Create, FileAccess.Write); StreamWriter sw = new StreamWriter(fs); - sw.Write("this.GlobalScopeTests = this.GlobalScopeTests || new Array();\n"); + sw.Write("this.GlobalScopeTests = this.GlobalScopeTests || {};\n"); sw.Flush(); sw.Close(); fs.Close(); diff --git a/tools/converter/convert.js b/tools/converter/convert.js index 7ec555e57c..c7be73ffdb 100644 --- a/tools/converter/convert.js +++ b/tools/converter/convert.js @@ -8,6 +8,10 @@ var t262 = global.t262; var platform = t262.platform; var regExp = platform.regExp; + var toRelPathStr = platform.toRelPathStr; + var toPathStr = platform.toPathStr; + var toRelPath = platform.toRelPath; + var toPath = platform.toPath; var headerPattern = /(?:(?:\/\/.*)?\s*\n)*/; var captureCommentPattern = /\/\*\*?((?:\s|\S)*?)\*\/\s*\n/; @@ -27,8 +31,9 @@ /\}\s*\)/, ')', /\s*;?(?:\s|\n)*$/); - var captureFuncBodyPattern = - regExp(/^function(?:\s+\w*)?\(\s*\)\s*\{/, + // Matches a named function. Captures both the name and the body. + var captureFuncNameBodyPattern = + regExp(/^function\s+(\w*)\(\s*\)\s*\{/, '(', anyPattern, ')', /;?/, blanksPattern, /\}$/); @@ -80,8 +85,10 @@ propTexts.forEach(function(propText) { var propName = propText.match(/^\w+/)[0]; var propVal = propText.substring(propName.length); - var propMatch = /^:?([^;]*);?\s*$/.exec(propVal); - if (propMatch) { propVal = propMatch[1]; } + // strip optional initial colon or final semicolon. + // The initial colon is only stripped if it comes immediately + // after the identifier with no intervening whitespace. + propVal = propVal.replace(/^:\s*/, '').replace(/;\s*$/, ''); propVal = stripStars(propVal); if (propName in envelope.testRecord) { throw new Error('duplicate: ' + propName); @@ -106,27 +113,49 @@ } /** - * Given a function, return the source for an expression that, when - * evaluated in the environment the function assumes, will behave - * the same as calling that function in that environment. + * Given a function that indicates success by returning a truthy + * value, return the source for a Program that, when evaluated in + * the environment the function assumes, will behave the same as + * calling that function in that environment and asserting the + * truthiness of the result. + * + *

Programs do not conveniently return any value, even their + * completion value, so Programs in canonical test262 style instead + * indicate success simply by completing normally, i.e., without + * throwing anything. The convertion assumes a one argument + * assertTrue function which throws an indication of + * test failure iff given a falsy argument. + * + *

Unless it specifies otherwise, the Program source may be + * executed strict and/or non-strict, and it may be exeuted within + * the try block of a try/catch or try/catch finally, i.e., as a + * Block rather than as a Program. */ - function expressionize(func) { + function functionToProgramSrc(func) { var funcSrc = '' + func; - var cfbMatch = captureFuncBodyPattern.exec(funcSrc); - if (cfbMatch) { - // Look for special cases - var body = cfbMatch[1].trim(); - - var cebMatch = captureExprBodyPattern.exec(body); - if (cebMatch) { return '(' + cebMatch[1].trim() + ')'; } - - var cpMatch = capturePredicatePattern.exec(body); - if (cpMatch) { return '(' + cpMatch[1].trim() + ')'; } - - } else { - // signal an error? + var cfnbMatch = captureFuncNameBodyPattern.exec(funcSrc); + if (!cfnbMatch) { + throw new Error('Could not recognize: "' + funcSrc + '"'); } - return '(' + funcSrc + ').call(this)'; + var name = cfnbMatch[1].trim(); + var body = cfnbMatch[2].trim(); + + // Look for special cases + + var cebMatch = captureExprBodyPattern.exec(body); + if (cebMatch) { + return 'assertTrue(' + cebMatch[1].trim() + ');'; + } + + var cpMatch = capturePredicatePattern.exec(body); + if (cpMatch) { + return 'assertTrue(' + cpMatch[1].trim() + ');'; + } + + // General case + + return funcSrc + '\n' + + 'assertTrue(' + name + '.call(this));'; } /** @@ -158,27 +187,25 @@ 'forceNonStrict'); if (testRecords.length !== 1) { - // We plan to lift this restriction in order to support test + // We may lift this restriction in order to support test // generators. throw new Error('not singleton: ' + name); } var testRecord = testRecords[0]; if (typeof testRecord.test === 'function') { - testRecord.test = envelope.rest + - 'assertTrue(' + expressionize(testRecord.test) + ');\n'; + testRecord.test = envelope.rest + '\n' + + functionToProgramSrc(testRecord.test); } - if (typeof testRecord.precondition === 'function') { - var precondition = expressionize(testRecord.precondition); - if (precondition === '(true)') { - delete testRecord.precondition; - } else { - testRecord.precondition = precondition; - } + if ('precondition' in testRecord) { + // Only ietestcenter tests currently have preconditions, and they + // plan to drop them. So canonical test262 style omits + // them. + delete testRecord.precondition; } return testRecord; - }; + } /** * Normalizes the properties of testRecord to be the canonical @@ -186,11 +213,6 @@ * runners. */ function normalizeProps(testRecord) { - if (!testRecord.id && testRecord.name) { - testRecord.id = testRecord.name; - delete testRecord.name; - } - if (!('strict_only' in testRecord) && testRecord.strict === 1) { testRecord.strict_only = ''; delete testRecord.strict; @@ -206,6 +228,8 @@ } } + // Note that testRecord.negative is falsy whether negative is + // absent or empty. if (!testRecord.negative && 'errortype' in testRecord) { testRecord.negative = testRecord.errortype; delete testRecord.errortype; @@ -219,19 +243,22 @@ testRecord.comment = testRecord.assertion; delete testRecord.assertion; } - }; + } t262.normalizeProps = normalizeProps; /** * Parses the source of a test262 test case file into a normalized * JSON test record. */ - function parseTestRecord(path, name) { - var nextPath = path.concat([name]); + function parseTestRecord(inBase, relPath, name) { + var nextRelPath = relPath.concat([name]); + var nextPath = inBase.concat(nextRelPath); var src = platform.read(nextPath); var testRecord; - if (!src) { throw new Error('no src: ' + nextPath.join('/')); } + if (!src) { + throw new Error('no src: ' + toPathStr(nextPath)); + } var envelope = parseTestEnvelope(src, name); if (envelope.registerExpr) { @@ -242,16 +269,19 @@ testRecord.test = envelope.rest; } } + delete testRecord.id; + delete testRecord.name; + testRecord.path = toRelPathStr(nextRelPath); testRecord.header = envelope.header; testRecord.comment = envelope.comment; normalizeProps(testRecord); return testRecord; - }; + } t262.parseTestRecord = parseTestRecord; // The known ones will be rendered first, and in this order. - var KNOWN_PROPS = ['id', 'section', 'path', 'description', + var KNOWN_PROPS = ['section', 'path', 'description', 'strict_only', 'negative']; /** @@ -283,30 +313,31 @@ result += ' * ' + testRecord.comment.replace(/\n/g, '\n * ') + '\n *\n'; } delete testRecord.comment; - KNOWN_PROPS.concat(['precondition']).forEach(addProp); + KNOWN_PROPS.forEach(addProp); Object.keys(testRecord).forEach(addProp); result += ' */\n\n' + test; return result; - }; + } t262.formatTestRecord = formatTestRecord; /** * Reads the test case at pathStr and returns the source of that * test case converted to canonical test262 style. */ - function convertTest(pathStr) { - var path = platform.toPath(pathStr); - var name = path.pop(); - var testRecord = parseTestRecord(path, name); + function convertTest(inBaseStr, relPathStr) { + var inBase = toPath(inBaseStr); + var relPath = platform.toRelPath(relPathStr); + var name = relPath.pop(); + var testRecord = parseTestRecord(inBase, relPath, name); var result = formatTestRecord(testRecord); return result; - }; + } t262.convertTest = convertTest; var SRC_DIRS = [ ['test', 'suite', 'other'], - ['test', 'suite', 'sputnik', 'Conformance'], - ['test', 'suite', 'ietestcenter'] + ['test', 'suite', 'ietestcenter'], + ['test', 'suite', 'sputnik', 'Conformance'] ]; var CONV_DIR = ['test', 'suite', 'converted']; @@ -315,6 +346,8 @@ var ME_PATH = platform.CONVERTER_PATH.concat('convert.js'); + var writeSpawnFailures = []; + /** * Convert all the testcases found at inBase+relDir to test cases * in canonical test262 style, to be stored at corresponding @@ -329,27 +362,43 @@ if (platform.isDirectory(inBase.concat(nextRelPath))) { convertAll(inBase, outBase, nextRelPath); } else if (/\.js$/.test(name)) { - var inFilePath = inPath.concat([name]); var outFilePath = outPath.concat([name]); - platform.writeSpawn( - [ME_PATH], - 't262.convertTest("' + platform.toPathStr(inFilePath) + '")', - void 0, - outFilePath); + try { + platform.writeSpawn( + [ME_PATH], + 't262.convertTest("' + toPathStr(inBase) + + '", "' + toRelPathStr(nextRelPath) + '")', + void 0, + outFilePath); + } catch (err) { + writeSpawnFailures.push({ + error: err, + relPath: relPath + }); + } } }); - }; + } t262.convertAll = convertAll; /** * Do all the conversions (from sputnik style, ietestcenter style, * or other to canonical test262 style) matching relPath. */ - function convert(opt_relPath) { + function convert(opt_relPathStr) { + var relPath = opt_relPathStr ? toRelPath(opt_relPathStr) : []; + writeSpawnFailures = []; SRC_DIRS.forEach(function(srcDir) { - convertAll(srcDir, CONV_DIR, opt_relPath || []); + convertAll(srcDir, CONV_DIR, relPath); }); - }; + if (writeSpawnFailures.length >= 1) { + print('********* failures **********'); + writeSpawnFailures.forEach(function(failure) { + print(failure.error + ': ' + toRelPathStr(failure.relPath)); + }); + throw writeSpawnFailures[0].error; + } + } t262.convert = convert; /** @@ -359,7 +408,7 @@ * runner. */ function buildSection(pathStr) { - var path = platform.toPath(pathStr); + var path = toPath(pathStr); if (!platform.isDirectory(path)) { throw new Error('not dir: ' + path); } var jsFiles = platform.ls(path).filter(function(name) { @@ -382,7 +431,7 @@ tests: testRecords } }; - }; + } t262.buildSection = buildSection; /** @@ -403,14 +452,21 @@ if (hasJS) { var name = relPath[relPath.length -1] + '.json'; var outFilePath = outBase.concat([name]); - platform.writeSpawn( - [ME_PATH], - 't262.asJSONTxt(t262.buildSection("' + - platform.toPathStr(inPath) + '"))', - void 0, - outFilePath); + try { + platform.writeSpawn( + [ME_PATH], + 't262.asJSONTxt(t262.buildSection("' + + toPathStr(inPath) + '"))', + void 0, + outFilePath); + } catch (err) { + writeSpawnFailures.push({ + error: err, + path: relPath + }); + } } - }; + } t262.buildAll = buildAll; /** @@ -421,12 +477,13 @@ * files. Once we switch over to converted as the maintained * sources, we should change this. */ - function buildWebSite(opt_relPath) { + function buildWebSite(opt_relPathStr) { + var relPath = opt_relPathStr ? toRelPath(opt_relPathStr) : []; SRC_DIRS.forEach(function(srcDir) { - buildAll(srcDir, OUT_DIR, opt_relPath || []); + buildAll(srcDir, OUT_DIR, relPath); }); -// buildAll(CONV_DIR, OUT_DIR, opt_relPath || []); - }; +// buildAll(CONV_DIR, OUT_DIR, relPath); + } t262.buildWebSite = buildWebSite; })(this); diff --git a/tools/converter/v8PosixPlatform.js b/tools/converter/v8PosixPlatform.js index 069deb59c1..f9b01645d8 100644 --- a/tools/converter/v8PosixPlatform.js +++ b/tools/converter/v8PosixPlatform.js @@ -34,11 +34,16 @@ /** * Appends a bunch of RegExps together into a single RegExp, * solving both the RegExp-one-liner problem and the doubled - * backslash problem when composing literal string. + * backslash problem when composing literal strings. * - *

The arguments can be any mixture of RegExps and strings. The - * strings are added as is without escaping -- BEWARE. If - * arguments[0] is a RegExp, we use its flag on the resuting RegExp. + *

The arguments can be any mixture of RegExps and strings. By + * expressing the portions that should be well formed regexps as + * regexps, we catch well-formedness errors within such a portion + * separately. The strings are added as is without escaping -- + * BEWARE. By not escaping the strings, we can use them to + * represent the individually unbalanced fragments, like capturing + * parens, around other regexps. If arguments[0] is a RegExp, we + * use its flags on the resuting RegExp. * *

Not platform dependent, so does not really belong in this * file. @@ -95,23 +100,39 @@ return path; } + /** + * Converts a relPath to a relPathStr. + * + * A relPath is an array of filenames relative to some base onto + * which it will be concatenated before use. + */ + function toRelPathStr(relPath) { + validatePath(relPath); + return relPath.join('/'); + } + platform.toRelPathStr = toRelPathStr; + /** * Converts a path to a pathStr. * * A path is an array of filenames relative to TEST262_ROOT. A * pathStr is a (possibly fully qualified string) for referring to - * that string on the current platform, according to the operations + * that file on the current platform, according to the operations * in this *Platform.js file. */ function toPathStr(path) { validatePath(path); return TEST262_ROOT.concat(path).join('/'); - }; + } platform.toPathStr = toPathStr; /** * Returns the text found at path, with newlines normalized and * any initial BOM (Unicode Byte Order Mark) removed. + * + * Note: Don't simply revise this (without renamings) to follow the + * general pattern of also defining a local 'read' function, as it + * will mask the v8 shell's read function, which we use. */ platform.read = function(path) { var text = read(toPathStr(path)). @@ -153,12 +174,12 @@ * @returns If there is a target, then the null string. Otherwise, * the string result of evaluating opt_exprSrc. */ - platform.writeSpawn = function(scriptPaths, - opt_exprSrc, - opt_args, - opt_targetPath, - opt_spawn_required, - opt_forceNonStrict) { + function writeSpawn(scriptPaths, + opt_exprSrc, + opt_args, + opt_targetPath, + opt_spawn_required, + opt_forceNonStrict) { if (opt_exprSrc && !opt_targetPath && !opt_spawn_required) { var str = '(function(/*var_args*/) {'; if (opt_forceNonStrict !== 'forceNonStrict') { @@ -182,8 +203,21 @@ } if (VERBOSE || DRY_RUN) { print(cmd); } if (DRY_RUN) { return ''; } - return os.system('bash', ['-c', cmd]); - }; + try { + return os.system('bash', ['-c', cmd]); + } catch (err) { + if (opt_targetPath) { + // The error we catch is almost certainly less interesting + // than the one unfortunately written to the target file. + var message = 'failed: ' + cmd + '\n' + + platform.read(opt_targetPath); + os.system('rm', [toPathStr(opt_targetPath)]); + throw new Error(message); + } + throw err; + } + } + platform.writeSpawn = writeSpawn; ////////////////// Only needed for building ///////////////////// @@ -193,7 +227,7 @@ * On platforms (like SES) where this can be a safely confining * evaluation, it should be. The implementation here is not safe. */ - platform.evalExprIn = function(exprSrc, env, opt_forceNonStrict) { + function evalExprIn(exprSrc, env, opt_forceNonStrict) { var varNames = Object.getOwnPropertyNames(env); var str = '(function(' + varNames.join(',') + ') {'; if (opt_forceNonStrict !== 'forceNonStrict') { @@ -203,12 +237,23 @@ return (1,eval)(str).apply(void 0, varNames.map(function(varName) { return env[varName]; })); - }; + } + platform.evalExprIn = evalExprIn; + + /** + * Converts a relPathStr to a relPath. + * + *

See toRelPathStr. + */ + function toRelPath(relPathStr) { + return validatePath(relPathStr.split('/')); + } + platform.toRelPath = toRelPath; /** * Converts a pathStr to a path. * - * See toPathStr. + *

See toPathStr. */ function toPath(pathStr) { if (pathStr[0] === '/') { @@ -225,24 +270,38 @@ /** * Does path name a directory? */ - platform.isDirectory = function(path) { - var fileOut = os.system('file', [toPathStr(path)]); - var fileMatch = fileOut.match(/:\s*([^:]*)\s*$/); - if (!fileMatch) { return null; } - var fileType = fileMatch[1].trim(); - return fileType === 'directory'; - }; + function isDirectory(path) { +// var fileOut = os.system('file', [toPathStr(path)]); +// var fileMatch = fileOut.match(/:\s*([^:]*)\s*$/); +// if (!fileMatch) { return null; } +// var fileType = fileMatch[1].trim(); +// return fileType === 'directory'; + try { + os.system('test', ['-d', toPathStr(path)]); + return true; + } catch (x) { + return false; + } + } + platform.isDirectory = isDirectory; /** * A list of the filenames found in path, which must name a * directory. */ - platform.ls = function(path) { + function ls(path) { var pathStr = toPathStr(path); - var lines = os.system('ls', [pathStr]).trim(); + if (!isDirectory(path)) { return []; } + var lines; + try { + lines = os.system('ls', [pathStr]).trim(); + } catch (err) { + throw err; + } if (lines === '') { return []; } return lines.split('\n'); - }; + } + platform.ls = ls; /** * Emits the jsonRecord serialized as JSON, either compactly or @@ -257,7 +316,7 @@ } global.t262.asJSONTxt = platform.asJSONTxt = asJSONTxt; - platform.mkdir = function(path) { + function mkdir(path) { var pathStr = toPathStr(path); if (DRY_RUN) { print('mkdir ' + pathStr); @@ -269,7 +328,8 @@ print('***could not mkdir: ' + pathStr); throw err; } - }; + } + platform.mkdir = mkdir; ////////////////// Only needed for running //////////////////////