diff --git a/ecmascript-testcases/test-stmt-throw.js b/ecmascript-testcases/test-stmt-throw.js index 5be4cf9d..e39d5a83 100644 --- a/ecmascript-testcases/test-stmt-throw.js +++ b/ecmascript-testcases/test-stmt-throw.js @@ -1,5 +1,5 @@ /* - * Throw staement (E5 Section 12.13). + * Throw statement (E5 Section 12.13). */ /*--- diff --git a/ecmascript-testcases/test-stmt-try.js b/ecmascript-testcases/test-stmt-try.js index 90f5228a..bbcca378 100644 --- a/ecmascript-testcases/test-stmt-try.js +++ b/ecmascript-testcases/test-stmt-try.js @@ -5,7 +5,7 @@ /*=== ===*/ -/* FIXME: test for control flow -> normal, canceling with continue, etc */ +/* XXX: test for control flow -> normal, canceling with continue, etc */ /*=== foo @@ -15,6 +15,12 @@ try finished foo bar foo +123 234 +foo 234 +123 234 +function +string +function ===*/ /* The catch variable has a "let scope", i.e. it uses a temporary declarative @@ -26,30 +32,84 @@ foo try { /* -> foo, Error, foo, try finished */ eval("var e='foo'; print(e);\n" + - "try { throw new Error('error') } catch(e) { print(e.name) };\n" + + "try { throw new Error('error') } catch (e) { print(e.name) };\n" + "print(e);"); print("try finished"); -} catch(e) { +} catch (e) { print(e.name); } try { /* multiple shadowing -> foo, bar, foo */ - eval("try { throw 'foo' } catch(e) { print(e); try { throw 'bar' } catch(e) { print(e) }; print(e) }"); -} catch(e) { + eval("try { throw 'foo' } catch (e) { print(e); try { throw 'bar' } catch (e) { print(e) }; print(e) }"); +} catch (e) { print(e.name); } -/* XXX: test shadowing of arguments and function declarations too */ +try { + /* shadow arguments temporarily */ + eval("(function (x, y) { print(x, y); try { throw 'foo' } catch (x) { print(x, y); }; print(x, y); })(123, 234)"); +} catch (e) { + print(e.name); +} + +try { + /* shadow function declarations temporarily */ + eval("(function (x, y) { function fn() {} print(typeof fn); try { throw 'foo' } catch (fn) { print(typeof fn) }; print(typeof fn); })(123, 234)"); +} catch (e) { + print(e.name); +} /*=== +10 +catch-binding-3 +123 +123 ===*/ /* The catch variable scope may be accessible through a function expression * created inside the catch clause. The closure may persist indefinitely. + * One can even eval() code from inside a returned scope! */ -/* FIXME */ +function createCatchScopeFunctions() { + var res = []; + var i; + + for (i = 0; i < 10; i++) { + try { + throw 'catch-binding-' + i; + } catch (e) { + res.push(function myfunc(code) { + return eval(code); + }); + } + } + + return res; +} + +function testCatchScope() { + var funcs = createCatchScopeFunctions(); + + // 'i' is not 'let bound' so it is 10 here + print(funcs[3]('i')); + + // 'e' is 'let' bound + print(funcs[3]('e')); + + // we can assign to 'i' + print(funcs[3]('i=123')); + + // and read it back through another closure + print(funcs[7]('i')); +} + +try { + testCatchScope(); +} catch (e) { + print(e.stack || e); +} /*=== SyntaxError @@ -138,35 +198,35 @@ SyntaxError */ try { - eval("try { print('try'); } catch(if) { print('catch'); }"); + eval("try { print('try'); } catch (if) { print('catch'); }"); print("try finished"); } catch (e) { print(e.name); } try { - eval("try { print('try'); } catch(eval) { print('catch'); }"); + eval("try { print('try'); } catch (eval) { print('catch'); }"); print("try finished"); } catch (e) { print(e.name); } try { - eval("try { print('try'); } catch(arguments) { print('catch'); }"); + eval("try { print('try'); } catch (arguments) { print('catch'); }"); print("try finished"); } catch (e) { print(e.name); } try { - eval("'use strict'; try { print('try'); } catch(eval) { print('catch'); }"); + eval("'use strict'; try { print('try'); } catch (eval) { print('catch'); }"); print("try finished"); } catch (e) { print(e.name); } try { - eval("'use strict'; try { print('try'); } catch(arguments) { print('catch'); }"); + eval("'use strict'; try { print('try'); } catch (arguments) { print('catch'); }"); print("try finished"); } catch (e) { print(e.name); @@ -196,5 +256,3 @@ try { } catch (e) { print(e.name); } - - diff --git a/ecmascript-testcases/test-stmt-var.js b/ecmascript-testcases/test-stmt-var.js index e0010bea..27cfd928 100644 --- a/ecmascript-testcases/test-stmt-var.js +++ b/ecmascript-testcases/test-stmt-var.js @@ -2,14 +2,118 @@ * Variable statement (E5 Section 12.2). */ -/*--- -{ - "skip": true -} ----*/ +/* XXX: Add semantics tests in various contexts (strict eval, non-strict eval, + * global code, function, etc. + */ -/*FIXME*/ +/* Test value for scope tests. */ +var globalValue = 'this is global'; + +/* Test variable declaration inside a dummy function. */ +function testDeclFunc(decl) { + var func; + try { + func = eval('(function dummy() {\n' + decl + '\n})'); + print(JSON.stringify(decl) + ' -> success'); + try { + print('func call -> ' + func()); + } catch (e) { + print('func call -> ' + e.name); + } + } catch (e) { + print(JSON.stringify(decl) + ' -> ' + e.name); + } +} /*=== +"var;" -> SyntaxError +"var x;" -> success +func call -> undefined +"var x = 123;" -> success +func call -> undefined +"var x,y;" -> success +func call -> undefined +"var x = 123, y=234;" -> success +func call -> undefined +"var x, y = 234,z,w=345, foo = 456, bar, quux;" -> success +func call -> undefined +"var\n x = 123\n ,y = 234;" -> success +func call -> undefined +"var\n x = 123\n ,y = 234\nprint(\"howdy\");\nreturn \"foo\";" -> success +howdy +func call -> foo +"var x = globalValue, y = x; print(\"x:\", x, \"y:\", y);" -> success +x: this is global y: this is global +func call -> undefined +"var x = globalValue; var y = x; print(\"x:\", x, \"y:\", y);" -> success +x: this is global y: this is global +func call -> undefined +"var y = x, x = globalValue; print(\"x:\", x, \"y:\", y);" -> success +x: this is global y: undefined +func call -> undefined +"var y = x; var x = globalValue; print(\"x:\", x, \"y:\", y);" -> success +x: this is global y: undefined +func call -> undefined +"var x = globalValue; print(\"x:\", x, \"y:\", y); var y = x;" -> success +x: this is global y: undefined +func call -> undefined +"var x = globalValue; print(\"x:\", x, \"globalValue:\", globalValue); var globalValue = \"shadow\";" -> success +x: undefined globalValue: undefined +func call -> undefined +"var x = globalValue; var globalValue = \"shadow\"; print(\"x:\", x, \"globalValue:\", globalValue);" -> success +x: undefined globalValue: shadow +func call -> undefined +"var globalValue = \"shadow\"; var x = globalValue; print(\"x:\", x, \"globalValue:\", globalValue);" -> success +x: shadow globalValue: shadow +func call -> undefined ===*/ +function variableDeclarationTest() { + var test = testDeclFunc; + + /* Variable declaration cannot have zero variable. */ + test('var;'); + + /* Various lengths, with and without initializer, various whitespace. */ + test('var x;'); + test('var x = 123;'); + test('var x,y;'); + test('var x = 123, y=234;'); + test('var x, y = 234,z,w=345, foo = 456, bar, quux;'); + + /* Newlines within declaration are OK. */ + test('var\n' + + ' x = 123\n' + + ' ,y = 234;'); + + /* Semicolon insertion. */ + test('var\n' + + ' x = 123\n' + + ' ,y = 234\n' + + 'print("howdy");\n' + + 'return "foo";'); + + /* Variable initializers are executed in sequence. All bindings in + * the function exist from the beginning and have 'undefined' values + * until assigned to. All bindings in the a function shadow any outer + * values of the same name. + */ + + test('var x = globalValue, y = x; print("x:", x, "y:", y);'); + test('var x = globalValue; var y = x; print("x:", x, "y:", y);'); + + test('var y = x, x = globalValue; print("x:", x, "y:", y);'); + test('var y = x; var x = globalValue; print("x:", x, "y:", y);'); + + test('var x = globalValue; print("x:", x, "y:", y); var y = x;'); + + test('var x = globalValue; print("x:", x, "globalValue:", globalValue); var globalValue = "shadow";'); + test('var x = globalValue; var globalValue = "shadow"; print("x:", x, "globalValue:", globalValue);'); + test('var globalValue = "shadow"; var x = globalValue; print("x:", x, "globalValue:", globalValue);'); +} + +try { + variableDeclarationTest(); +} catch (e) { + print(e.stack || e); +}