michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Date: 28 May 2001 michael@0: * michael@0: * SUMMARY: Functions are scoped statically, not dynamically michael@0: * michael@0: * See ECMA Section 10.1.4 Scope Chain and Identifier Resolution michael@0: * (This section defines the scope chain of an execution context) michael@0: * michael@0: * See ECMA Section 12.10 The with Statement michael@0: * michael@0: * See ECMA Section 13 Function Definition michael@0: * (This section defines the scope chain of a function object as that michael@0: * of the running execution context when the function was declared) michael@0: * michael@0: * Like scope-001.js, but using assignment var f = function expression michael@0: * instead of a function declaration: function f() {} etc. michael@0: */ michael@0: //----------------------------------------------------------------------------- michael@0: var UBound = 0; michael@0: var BUGNUMBER = '(none)'; michael@0: var summary = 'Testing that functions are scoped statically, not dynamically'; michael@0: var self = this; // capture a reference to the global object michael@0: var status = ''; michael@0: var statusitems = [ ]; michael@0: var actual = ''; michael@0: var actualvalues = [ ]; michael@0: var expect= ''; michael@0: var expectedvalues = [ ]; michael@0: michael@0: michael@0: /* michael@0: * In this section the expected value is 1, not 2. michael@0: * michael@0: * Why? f captures its scope chain from when it's declared, and imposes that chain michael@0: * when it's executed. In other words, f's scope chain is from when it was compiled. michael@0: * Since f is a top-level function, this is the global object only. Hence 'a' resolves to 1. michael@0: */ michael@0: status = 'Section A of test'; michael@0: var a = 1; michael@0: var f = function () {return a;}; michael@0: var obj = {a:2}; michael@0: with (obj) michael@0: { michael@0: actual = f(); michael@0: } michael@0: expect = 1; michael@0: addThis(); michael@0: michael@0: michael@0: /* michael@0: * In this section the expected value is 2, not 1. That is because here michael@0: * f's associated scope chain now includes 'obj' before the global object. michael@0: */ michael@0: status = 'Section B of test'; michael@0: var a = 1; michael@0: var obj = {a:2}; michael@0: with (obj) michael@0: { michael@0: var f = function () {return a;}; michael@0: actual = f(); michael@0: } michael@0: expect = 2; michael@0: addThis(); michael@0: michael@0: michael@0: /* michael@0: * Like Section B , except that we call f outside the with block. michael@0: * By the principles explained above, we still expect 2 - michael@0: */ michael@0: status = 'Section C of test'; michael@0: var a = 1; michael@0: var obj = {a:2}; michael@0: with (obj) michael@0: { michael@0: var f = function () {return a;}; michael@0: } michael@0: actual = f(); michael@0: expect = 2; michael@0: addThis(); michael@0: michael@0: michael@0: /* michael@0: * Like Section C, but with one more level of indirection - michael@0: */ michael@0: status = 'Section D of test'; michael@0: var a = 1; michael@0: var obj = {a:2, obj:{a:3}}; michael@0: with (obj) michael@0: { michael@0: with (obj) michael@0: { michael@0: var f = function () {return a;}; michael@0: } michael@0: } michael@0: actual = f(); michael@0: expect = 3; michael@0: addThis(); michael@0: michael@0: michael@0: /* michael@0: * Like Section C, but here we actually delete obj before calling f. michael@0: * We still expect 2 - michael@0: */ michael@0: status = 'Section E of test'; michael@0: var a = 1; michael@0: var obj = {a:2}; michael@0: with (obj) michael@0: { michael@0: var f = function () {return a;}; michael@0: } michael@0: delete obj; michael@0: actual = f(); michael@0: expect = 2; michael@0: addThis(); michael@0: michael@0: michael@0: /* michael@0: * Like Section E. Here we redefine obj and call f under with (obj) - michael@0: * We still expect 2 - michael@0: */ michael@0: status = 'Section F of test'; michael@0: var a = 1; michael@0: var obj = {a:2}; michael@0: with (obj) michael@0: { michael@0: var f = function () {return a;}; michael@0: } michael@0: delete obj; michael@0: var obj = {a:3}; michael@0: with (obj) michael@0: { michael@0: actual = f(); michael@0: } michael@0: expect = 2; // NOT 3 !!! michael@0: addThis(); michael@0: michael@0: michael@0: /* michael@0: * Explicitly verify that f exists at global level, even though michael@0: * it was defined under the with(obj) block - michael@0: */ michael@0: status = 'Section G of test'; michael@0: var a = 1; michael@0: var obj = {a:2}; michael@0: with (obj) michael@0: { michael@0: var f = function () {return a;}; michael@0: } michael@0: actual = String([obj.hasOwnProperty('f'), self.hasOwnProperty('f')]); michael@0: expect = String([false, true]); michael@0: addThis(); michael@0: michael@0: michael@0: /* michael@0: * Explicitly verify that f exists at global level, even though michael@0: * it was defined under the with(obj) block - michael@0: */ michael@0: status = 'Section H of test'; michael@0: var a = 1; michael@0: var obj = {a:2}; michael@0: with (obj) michael@0: { michael@0: var f = function () {return a;}; michael@0: } michael@0: actual = String(['f' in obj, 'f' in self]); michael@0: expect = String([false, true]); michael@0: addThis(); michael@0: michael@0: michael@0: michael@0: //------------------------------------------------------------------------------------------------- michael@0: test(); michael@0: //------------------------------------------------------------------------------------------------- michael@0: michael@0: michael@0: function addThis() michael@0: { michael@0: statusitems[UBound] = status; michael@0: actualvalues[UBound] = actual; michael@0: expectedvalues[UBound] = expect; michael@0: UBound++; michael@0: resetTestVars(); michael@0: } michael@0: michael@0: michael@0: function resetTestVars() michael@0: { michael@0: delete a; michael@0: delete obj; michael@0: delete f; michael@0: } michael@0: michael@0: michael@0: function test() michael@0: { michael@0: enterFunc ('test'); michael@0: printBugNumber(BUGNUMBER); michael@0: printStatus (summary); michael@0: michael@0: for (var i = 0; i < UBound; i++) michael@0: { michael@0: reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); michael@0: } michael@0: michael@0: exitFunc ('test'); michael@0: }