v8/test/mjsunit/es6/block-sloppy-function.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

695 lines
14 KiB
JavaScript
Raw Normal View History

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Test Annex B 3.3 semantics for functions declared in blocks in sloppy mode.
// http://www.ecma-international.org/ecma-262/6.0/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
(function overridingLocalFunction() {
var x = [];
assertEquals('function', typeof f);
function f() {
x.push(1);
}
f();
{
f();
function f() {
x.push(2);
}
f();
}
f();
{
f();
function f() {
x.push(3);
}
f();
}
f();
assertArrayEquals([1, 2, 2, 2, 3, 3, 3], x);
})();
(function newFunctionBinding() {
var x = [];
assertEquals('undefined', typeof f);
{
f();
function f() {
x.push(2);
}
f();
}
f();
{
f();
function f() {
x.push(3);
}
f();
}
f();
assertArrayEquals([2, 2, 2, 3, 3, 3], x);
})();
(function shadowingLetDoesntBind() {
let f = 1;
assertEquals(1, f);
{
let y = 3;
function f() {
y = 2;
}
f();
assertEquals(2, y);
}
assertEquals(1, f);
})();
(function shadowingLetDoesntBindGenerator() {
let f = function *f() {
while(true) {
yield 1;
}
};
assertEquals(1, f().next().value);
{
function *f() {
while(true) {
yield 2;
}
}
assertEquals(2, f().next().value);
}
assertEquals(1, f().next().value);
})();
(function shadowingClassDoesntBind() {
class f { }
assertEquals('class f { }', f.toString());
{
let y = 3;
function f() {
y = 2;
}
f();
assertEquals(2, y);
}
assertEquals('class f { }', f.toString());
})();
(function shadowingConstDoesntBind() {
const f = 1;
assertEquals(1, f);
{
let y = 3;
function f() {
y = 2;
}
f();
assertEquals(2, y);
}
assertEquals(1, f);
})();
(function shadowingVarBinds() {
var f = 1;
assertEquals(1, f);
{
let y = 3;
function f() {
y = 2;
}
f();
assertEquals(2, y);
}
assertEquals('function', typeof f);
})();
(function complexParams(a = 0) {
{
let y = 3;
function f(b = 0) {
y = 2;
}
f();
assertEquals(2, y);
}
assertEquals('function', typeof f);
})();
(function complexVarParams(a = 0) {
var f;
{
let y = 3;
function f(b = 0) {
y = 2;
}
f();
assertEquals(2, y);
}
assertEquals('function', typeof f);
})();
(function conditional() {
if (true) {
function f() { return 1; }
} else {
function f() { return 2; }
}
assertEquals(1, f());
if (false) {
function g() { return 1; }
} else {
function g() { return 2; }
}
assertEquals(2, g());
})();
(function skipExecution() {
{
function f() { return 1; }
}
assertEquals(1, f());
{
function f() { return 2; }
}
assertEquals(2, f());
L: {
assertEquals(3, f());
break L;
function f() { return 3; }
}
assertEquals(2, f());
})();
(function executionOrder() {
function getOuter() {
return f;
}
assertEquals('undefined', typeof getOuter());
{
assertEquals('function', typeof f);
assertEquals('undefined', typeof getOuter());
function f () {}
assertEquals('function', typeof f);
assertEquals('function', typeof getOuter());
}
assertEquals('function', typeof getOuter());
})();
(function reassignBindings() {
function getOuter() {
return f;
}
assertEquals('undefined', typeof getOuter());
{
assertEquals('function', typeof f);
assertEquals('undefined', typeof getOuter());
f = 1;
assertEquals('number', typeof f);
assertEquals('undefined', typeof getOuter());
function f () {}
assertEquals('number', typeof f);
assertEquals('number', typeof getOuter());
f = '';
assertEquals('string', typeof f);
assertEquals('number', typeof getOuter());
}
assertEquals('number', typeof getOuter());
})();
// Test that shadowing arguments is fine
(function shadowArguments(x) {
assertArrayEquals([1], arguments);
{
assertEquals('function', typeof arguments);
function arguments() {}
assertEquals('function', typeof arguments);
}
assertEquals('function', typeof arguments);
})(1);
// Don't shadow simple parameter
(function shadowingParameterDoesntBind(x) {
assertEquals(1, x);
{
function x() {}
}
assertEquals(1, x);
})(1);
// Don't shadow complex parameter
(function shadowingDefaultParameterDoesntBind(x = 0) {
assertEquals(1, x);
{
function x() {}
}
assertEquals(1, x);
})(1);
// Don't shadow nested complex parameter
(function shadowingNestedParameterDoesntBind([[x]]) {
assertEquals(1, x);
{
function x() {}
}
assertEquals(1, x);
})([[1]]);
// Don't shadow rest parameter
(function shadowingRestParameterDoesntBind(...x) {
assertArrayEquals([1], x);
{
function x() {}
}
assertArrayEquals([1], x);
})(1);
// Don't shadow complex rest parameter
(function shadowingComplexRestParameterDoesntBind(...[x]) {
assertArrayEquals(1, x);
{
function x() {}
}
assertArrayEquals(1, x);
})(1);
// Previous tests with a var declaration thrown in.
// Don't shadow simple parameter
(function shadowingVarParameterDoesntBind(x) {
var x;
assertEquals(1, x);
{
function x() {}
}
assertEquals(1, x);
})(1);
// Don't shadow complex parameter
(function shadowingVarDefaultParameterDoesntBind(x = 0) {
var x;
assertEquals(1, x);
{
function x() {}
}
assertEquals(1, x);
})(1);
// Don't shadow nested complex parameter
(function shadowingVarNestedParameterDoesntBind([[x]]) {
var x;
assertEquals(1, x);
{
function x() {}
}
assertEquals(1, x);
})([[1]]);
// Don't shadow rest parameter
(function shadowingVarRestParameterDoesntBind(...x) {
var x;
assertArrayEquals([1], x);
{
function x() {}
}
assertArrayEquals([1], x);
})(1);
// Don't shadow complex rest parameter
(function shadowingVarComplexRestParameterDoesntBind(...[x]) {
var x;
assertArrayEquals(1, x);
{
function x() {}
}
assertArrayEquals(1, x);
})(1);
// Hoisting is not affected by other simple parameters
(function irrelevantParameterBinds(y, z) {
assertEquals(undefined, x);
{
function x() {}
}
assertEquals('function', typeof x);
})(1);
// Hoisting is not affected by other complex parameters
(function irrelevantComplexParameterBinds([y] = [], z) {
assertEquals(undefined, x);
{
function x() {}
}
assertEquals('function', typeof x);
})();
// Hoisting is not affected by rest parameters
(function irrelevantRestParameterBinds(y, ...z) {
assertEquals(undefined, x);
{
function x() {}
}
assertEquals('function', typeof x);
})();
// Hoisting is not affected by complex rest parameters
(function irrelevantRestParameterBinds(y, ...[z]) {
assertEquals(undefined, x);
{
function x() {}
}
assertEquals('function', typeof x);
})();
// Test that shadowing function name is fine
{
let called = false;
(function shadowFunctionName() {
if (called) assertUnreachable();
called = true;
{
function shadowFunctionName() {
return 0;
}
assertEquals(0, shadowFunctionName());
}
assertEquals(0, shadowFunctionName());
})();
}
{
let called = false;
(function shadowFunctionNameWithComplexParameter(...r) {
if (called) assertUnreachable();
called = true;
{
function shadowFunctionNameWithComplexParameter() {
return 0;
}
assertEquals(0, shadowFunctionNameWithComplexParameter());
}
assertEquals(0, shadowFunctionNameWithComplexParameter());
})();
}
(function shadowOuterVariable() {
{
let f = 0;
(function () {
assertEquals(undefined, f);
{
assertEquals(1, f());
function f() { return 1; }
assertEquals(1, f());
}
assertEquals(1, f());
})();
assertEquals(0, f);
}
})();
(function notInDefaultScope() {
var y = 1;
(function innerNotInDefaultScope(x = y) {
assertEquals('undefined', typeof y);
{
function y() {}
}
assertEquals('function', typeof y);
assertEquals(1, x);
})();
})();
(function noHoistingThroughNestedLexical() {
{
let f = 2;
{
let y = 3;
function f() {
y = 2;
}
f();
assertEquals(2, y);
}
assertEquals(2, f);
}
assertThrows(()=>f, ReferenceError);
})();
// Only the first function is hoisted; the second is blocked by the first.
// Contrast overridingLocalFunction, in which the outer function declaration
// is not lexical and so the inner declaration is hoisted.
(function noHoistingThroughNestedFunctions() {
assertEquals(undefined, f); // Also checks that the var-binding exists
{
assertEquals(4, f());
function f() {
return 4;
}
{
assertEquals(5, f());
function f() {
return 5;
}
assertEquals(5, f());
}
assertEquals(4, f());
}
assertEquals(4, f());
})();
Reland of Add errors for declarations which conflict with catch parameters. (patchset #1 id:1 of https://codereview.chromium.org/2112223002/ ) Reason for revert: Correcting issue. Original issue's description: > Revert of Add errors for declarations which conflict with catch parameters. (patchset #6 id:100001 of https://codereview.chromium.org/2109733003/ ) > > Reason for revert: > Fuzzer claims `try { \"\" ; } catch(x) { let x1 = [1,,], x = x; }` causes a crash. > > Original issue's description: > > Add errors for declarations which conflict with catch parameters. > > > > Catch parameters are largely treated as lexical declarations in the > > block which contains their body for the purposes of early syntax errors, > > with some exceptions outlined in B.3.5. This patch introduces most of > > those errors, except those from `eval('for (var e of ...);')` inside of > > a catch with a simple parameter named 'e'. > > > > Note that annex B.3.5 allows var declarations to conflict with simple > > catch parameters, except when the variable declaration is the init of a > > for-of statement. > > > > BUG=v8:5112,v8:4231 > > > > Committed: https://crrev.com/2907c726b2bb5cf20b2bec639ca9e6a521585406 > > Cr-Commit-Position: refs/heads/master@{#37462} > > TBR=littledan@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > BUG=v8:5112,v8:4231 > > Committed: https://crrev.com/8834d5ecb559001c87c42322969471da60574a8c > Cr-Commit-Position: refs/heads/master@{#37464} R=littledan@chromium.org BUG=v8:5112,v8:4231 Review-Url: https://codereview.chromium.org/2119933002 Cr-Commit-Position: refs/heads/master@{#37728}
2016-07-13 19:28:56 +00:00
// B.3.5 interacts with B.3.3 to allow this.
(function hoistingThroughSimpleCatch() {
assertEquals(undefined, f);
try {
throw 0;
} catch (f) {
Reland of Add errors for declarations which conflict with catch parameters. (patchset #1 id:1 of https://codereview.chromium.org/2112223002/ ) Reason for revert: Correcting issue. Original issue's description: > Revert of Add errors for declarations which conflict with catch parameters. (patchset #6 id:100001 of https://codereview.chromium.org/2109733003/ ) > > Reason for revert: > Fuzzer claims `try { \"\" ; } catch(x) { let x1 = [1,,], x = x; }` causes a crash. > > Original issue's description: > > Add errors for declarations which conflict with catch parameters. > > > > Catch parameters are largely treated as lexical declarations in the > > block which contains their body for the purposes of early syntax errors, > > with some exceptions outlined in B.3.5. This patch introduces most of > > those errors, except those from `eval('for (var e of ...);')` inside of > > a catch with a simple parameter named 'e'. > > > > Note that annex B.3.5 allows var declarations to conflict with simple > > catch parameters, except when the variable declaration is the init of a > > for-of statement. > > > > BUG=v8:5112,v8:4231 > > > > Committed: https://crrev.com/2907c726b2bb5cf20b2bec639ca9e6a521585406 > > Cr-Commit-Position: refs/heads/master@{#37462} > > TBR=littledan@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > BUG=v8:5112,v8:4231 > > Committed: https://crrev.com/8834d5ecb559001c87c42322969471da60574a8c > Cr-Commit-Position: refs/heads/master@{#37464} R=littledan@chromium.org BUG=v8:5112,v8:4231 Review-Url: https://codereview.chromium.org/2119933002 Cr-Commit-Position: refs/heads/master@{#37728}
2016-07-13 19:28:56 +00:00
{
assertEquals(4, f());
function f() {
return 4;
}
assertEquals(4, f());
}
assertEquals(0, f);
Reland of Add errors for declarations which conflict with catch parameters. (patchset #1 id:1 of https://codereview.chromium.org/2112223002/ ) Reason for revert: Correcting issue. Original issue's description: > Revert of Add errors for declarations which conflict with catch parameters. (patchset #6 id:100001 of https://codereview.chromium.org/2109733003/ ) > > Reason for revert: > Fuzzer claims `try { \"\" ; } catch(x) { let x1 = [1,,], x = x; }` causes a crash. > > Original issue's description: > > Add errors for declarations which conflict with catch parameters. > > > > Catch parameters are largely treated as lexical declarations in the > > block which contains their body for the purposes of early syntax errors, > > with some exceptions outlined in B.3.5. This patch introduces most of > > those errors, except those from `eval('for (var e of ...);')` inside of > > a catch with a simple parameter named 'e'. > > > > Note that annex B.3.5 allows var declarations to conflict with simple > > catch parameters, except when the variable declaration is the init of a > > for-of statement. > > > > BUG=v8:5112,v8:4231 > > > > Committed: https://crrev.com/2907c726b2bb5cf20b2bec639ca9e6a521585406 > > Cr-Commit-Position: refs/heads/master@{#37462} > > TBR=littledan@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > BUG=v8:5112,v8:4231 > > Committed: https://crrev.com/8834d5ecb559001c87c42322969471da60574a8c > Cr-Commit-Position: refs/heads/master@{#37464} R=littledan@chromium.org BUG=v8:5112,v8:4231 Review-Url: https://codereview.chromium.org/2119933002 Cr-Commit-Position: refs/heads/master@{#37728}
2016-07-13 19:28:56 +00:00
}
assertEquals(4, f());
})();
[parser] Use non-eval decl scope's parent for caching We use the compilation entry point as a caching scope for deserializing lookups, to avoid redundantly iterating over parent scopes when accessing the same variable multiple times. However, this caching scope messes with lookups that are looking for lexical name conflicts, as opposed to just resolving variables. In particular, it messes with name conflict lookups and sloppy block function hoisting checks, when there are other scopes in the way, e.g. function f() { let x; try { throw 0; } catch (x) { // This catch is the entry scope // Naive use of caches will find the catch-bound x (which is // a VAR), and declare 'no conflict'. eval("var x;"); // Naive use of caches will find the catch-bound x (which is // a VAR), and determine that this function can be hoisted. eval("{ function x() {} }"); } } Previously, we worked around this by avoiding cache uses for these lookups, but this had the issue of instead caching the same variable multiple times, on different scopes. In particular, we saw: function f() { with ({}) { // This with is the entry scope, any other scope would do // though. // The conflict check on `var f` caches the function name // variable on the function scope, the subsequent 'real' // lookup of `f` caches the function name variable on the // entry i.e. with scope. eval("var f; f;"); } } With this patch, we change the caching behaviour to cache on the first non-eval declaration scope above the eval -- in the above examples, this becomes the parent function "f". For compilations with no intermediate non-decl scopes (no with or catch scopes between the function and eval) this becomes equivalent to the existing entry-point-based caching. This means that normal lookups do have to (sometimes) iterate more scopes, and we do have to be careful when using the cache to not use it for lookups in these intermediate scopes (a new IsOuterScope DCHECK guards against this), but we can now safely ignore the cache scope when doing the name-collision lookups, as they only iterate up to the outer non-eval declaration scope anyway. Bug: chromium:1026603 Bug: chromium:1029461 Change-Id: I9e7a96ce4b8adbc7ed47a49fba6fba58b526235b Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1955731 Commit-Queue: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#65391}
2019-12-09 14:33:12 +00:00
(function noHoistingIfLetOutsideSimpleCatch() {
assertThrows(()=>f, ReferenceError);
let f = 2;
assertEquals(2, f);
try {
throw 0;
} catch (f) {
{
assertEquals(4, f());
function f() {
return 4;
}
assertEquals(4, f());
}
assertEquals(0, f);
}
assertEquals(2, f);
})();
Reland of Add errors for declarations which conflict with catch parameters. (patchset #1 id:1 of https://codereview.chromium.org/2112223002/ ) Reason for revert: Correcting issue. Original issue's description: > Revert of Add errors for declarations which conflict with catch parameters. (patchset #6 id:100001 of https://codereview.chromium.org/2109733003/ ) > > Reason for revert: > Fuzzer claims `try { \"\" ; } catch(x) { let x1 = [1,,], x = x; }` causes a crash. > > Original issue's description: > > Add errors for declarations which conflict with catch parameters. > > > > Catch parameters are largely treated as lexical declarations in the > > block which contains their body for the purposes of early syntax errors, > > with some exceptions outlined in B.3.5. This patch introduces most of > > those errors, except those from `eval('for (var e of ...);')` inside of > > a catch with a simple parameter named 'e'. > > > > Note that annex B.3.5 allows var declarations to conflict with simple > > catch parameters, except when the variable declaration is the init of a > > for-of statement. > > > > BUG=v8:5112,v8:4231 > > > > Committed: https://crrev.com/2907c726b2bb5cf20b2bec639ca9e6a521585406 > > Cr-Commit-Position: refs/heads/master@{#37462} > > TBR=littledan@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > BUG=v8:5112,v8:4231 > > Committed: https://crrev.com/8834d5ecb559001c87c42322969471da60574a8c > Cr-Commit-Position: refs/heads/master@{#37464} R=littledan@chromium.org BUG=v8:5112,v8:4231 Review-Url: https://codereview.chromium.org/2119933002 Cr-Commit-Position: refs/heads/master@{#37728}
2016-07-13 19:28:56 +00:00
(function noHoistingThroughComplexCatch() {
try {
throw 0;
} catch ({f}) {
Reland of Add errors for declarations which conflict with catch parameters. (patchset #1 id:1 of https://codereview.chromium.org/2112223002/ ) Reason for revert: Correcting issue. Original issue's description: > Revert of Add errors for declarations which conflict with catch parameters. (patchset #6 id:100001 of https://codereview.chromium.org/2109733003/ ) > > Reason for revert: > Fuzzer claims `try { \"\" ; } catch(x) { let x1 = [1,,], x = x; }` causes a crash. > > Original issue's description: > > Add errors for declarations which conflict with catch parameters. > > > > Catch parameters are largely treated as lexical declarations in the > > block which contains their body for the purposes of early syntax errors, > > with some exceptions outlined in B.3.5. This patch introduces most of > > those errors, except those from `eval('for (var e of ...);')` inside of > > a catch with a simple parameter named 'e'. > > > > Note that annex B.3.5 allows var declarations to conflict with simple > > catch parameters, except when the variable declaration is the init of a > > for-of statement. > > > > BUG=v8:5112,v8:4231 > > > > Committed: https://crrev.com/2907c726b2bb5cf20b2bec639ca9e6a521585406 > > Cr-Commit-Position: refs/heads/master@{#37462} > > TBR=littledan@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > BUG=v8:5112,v8:4231 > > Committed: https://crrev.com/8834d5ecb559001c87c42322969471da60574a8c > Cr-Commit-Position: refs/heads/master@{#37464} R=littledan@chromium.org BUG=v8:5112,v8:4231 Review-Url: https://codereview.chromium.org/2119933002 Cr-Commit-Position: refs/heads/master@{#37728}
2016-07-13 19:28:56 +00:00
{
assertEquals(4, f());
function f() {
return 4;
}
assertEquals(4, f());
}
}
assertThrows(()=>f, ReferenceError);
})();
(function hoistingThroughWith() {
with ({f: 0}) {
assertEquals(0, f);
{
assertEquals(4, f());
function f() {
return 4;
}
assertEquals(4, f());
}
assertEquals(0, f);
}
assertEquals(4, f());
})();
// Test that hoisting from blocks does happen in global scope
function globalHoisted() { return 0; }
{
function globalHoisted() { return 1; }
}
assertEquals(1, globalHoisted());
// Also happens when not previously defined
assertEquals(undefined, globalUndefinedHoisted);
{
function globalUndefinedHoisted() { return 1; }
}
assertEquals(1, globalUndefinedHoisted());
var globalUndefinedHoistedDescriptor =
Object.getOwnPropertyDescriptor(this, "globalUndefinedHoisted");
assertFalse(globalUndefinedHoistedDescriptor.configurable);
assertTrue(globalUndefinedHoistedDescriptor.writable);
assertTrue(globalUndefinedHoistedDescriptor.enumerable);
assertEquals(1, globalUndefinedHoistedDescriptor.value());
// When a function property is hoisted, it should be
// made enumerable.
// BUG(v8:4451)
Object.defineProperty(this, "globalNonEnumerable", {
value: false,
configurable: true,
writable: true,
enumerable: false
});
eval("{function globalNonEnumerable() { return 1; }}");
var globalNonEnumerableDescriptor
= Object.getOwnPropertyDescriptor(this, "globalNonEnumerable");
// BUG(v8:4451): Should be made non-configurable
assertTrue(globalNonEnumerableDescriptor.configurable);
assertTrue(globalNonEnumerableDescriptor.writable);
// BUG(v8:4451): Should be made enumerable
assertFalse(globalNonEnumerableDescriptor.enumerable);
assertEquals(1, globalNonEnumerableDescriptor.value());
// When a function property is hoisted, it should be overwritten and
// made writable and overwritten, even if the property was non-writable.
Object.defineProperty(this, "globalNonWritable", {
value: false,
configurable: true,
writable: false,
enumerable: true
});
eval("{function globalNonWritable() { return 1; }}");
var globalNonWritableDescriptor
= Object.getOwnPropertyDescriptor(this, "globalNonWritable");
// BUG(v8:4451): Should be made non-configurable
assertTrue(globalNonWritableDescriptor.configurable);
// BUG(v8:4451): Should be made writable
assertFalse(globalNonWritableDescriptor.writable);
assertFalse(globalNonEnumerableDescriptor.enumerable);
// BUG(v8:4451): Should be overwritten
assertEquals(false, globalNonWritableDescriptor.value);
// Test that hoisting from blocks does happen in an eval
eval(`
function evalHoisted() { return 0; }
{
function evalHoisted() { return 1; }
}
assertEquals(1, evalHoisted());
`);
// Test that hoisting from blocks happens from eval in a function
!function() {
eval(`
function evalInFunctionHoisted() { return 0; }
{
function evalInFunctionHoisted() { return 1; }
}
assertEquals(1, evalInFunctionHoisted());
`);
}();
(function evalHoistingThroughSimpleCatch() {
try {
throw 0;
} catch (f) {
eval(`{ function f() {
return 4;
} }`);
assertEquals(0, f);
}
assertEquals(4, f());
})();
(function evalHoistingThroughWith() {
with ({f: 0}) {
eval(`{ function f() {
return 4;
} }`);
assertEquals(0, f);
}
assertEquals(4, f());
})();
let dontHoistGlobal;
{ function dontHoistGlobal() {} }
assertEquals(undefined, dontHoistGlobal);
let dontHoistEval;
var throws = false;
try {
eval("{ function dontHoistEval() {} }");
} catch (e) {
throws = true;
}
assertFalse(throws);
// When the global object is frozen, silently don't hoist
// Currently this actually throws BUG(v8:4452)
Object.freeze(this);
{
let throws = false;
try {
eval('{ function hoistWhenFrozen() {} }');
} catch (e) {
throws = true;
}
assertFalse(this.hasOwnProperty("hoistWhenFrozen"));
assertThrows(() => hoistWhenFrozen, ReferenceError);
// Should be assertFalse BUG(v8:4452)
assertTrue(throws);
}