text: // Copyright 2014 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.
  
  var global = this;
  var globalProto = Object.getPrototypeOf(global);
  
  // Number of objects being tested. There is an assert ensuring this is correct.
  var objectCount = 21;
  
  
  function runTest(f) {
    function restore(object, oldProto) {
      delete object[Symbol.unscopables];
      delete object.x;
      delete object.x_;
      delete object.y;
      delete object.z;
      Object.setPrototypeOf(object, oldProto);
    }
  
    function getObject(i) {
      var objects = [
        {},
        [],
        function() {},
        function() {
          return arguments;
        }(),
        function() {
          'use strict';
          return arguments;
        }(),
        Object(1),
        Object(true),
        Object('bla'),
        new Date,
        new RegExp,
        new Set,
        new Map,
        new WeakMap,
        new WeakSet,
        new ArrayBuffer(10),
        new Int32Array(5),
        Object,
        Function,
        Date,
        RegExp,
        global
      ];
  
      assertEquals(objectCount, objects.length);
      return objects[i];
    }
  
    // Tests depends on this not being there to start with.
    delete Array.prototype[Symbol.unscopables];
  
    if (f.length === 1) {
      for (var i = 0; i < objectCount; i++) {
        var object = getObject(i);
        var oldObjectProto = Object.getPrototypeOf(object);
        f(object);
        restore(object, oldObjectProto);
      }
    } else {
      for (var i = 0; i < objectCount; i++) {
        for (var j = 0; j < objectCount; j++) {
          var object = getObject(i);
          var proto = getObject(j);
          if (object === proto) {
            continue;
          }
          var oldObjectProto = Object.getPrototypeOf(object);
          var oldProtoProto = Object.getPrototypeOf(proto);
          f(object, proto);
          restore(object, oldObjectProto);
          restore(proto, oldProtoProto);
        }
      }
    }
  }
  
  // Test array first, since other tests are changing
  // Array.prototype[Symbol.unscopables].
  function TestArrayPrototypeUnscopables() {
    var descr = Object.getOwnPropertyDescriptor(Array.prototype,
                                                Symbol.unscopables);
    assertFalse(descr.enumerable);
    assertFalse(descr.writable);
    assertTrue(descr.configurable);
    assertEquals(null, Object.getPrototypeOf(descr.value));
  
    var copyWithin = 'local copyWithin';
    var entries = 'local entries';
    var fill = 'local fill';
    var find = 'local find';
    var findIndex = 'local findIndex';
    var keys = 'local keys';
    var values = 'local values';
  
    var array = [];
    array.toString = 42;
  
    with (array) {
      assertEquals('local copyWithin', copyWithin);
      assertEquals('local entries', entries);
      assertEquals('local fill', fill);
      assertEquals('local find', find);
      assertEquals('local findIndex', findIndex);
      assertEquals('local keys', keys);
      assertEquals('local values', values);
      assertEquals(42, toString);
    }
  }
  TestArrayPrototypeUnscopables();
  
  
  
  function TestBasics(object) {
    var x = 1;
    var y = 2;
    var z = 3;
    object.x = 4;
    object.y = 5;
  
    with (object) {
      assertEquals(4, x);
      assertEquals(5, y);
      assertEquals(3, z);
    }
  
    object[Symbol.unscopables] = {x: true};
    with (object) {
      assertEquals(1, x);
      assertEquals(5, y);
      assertEquals(3, z);
    }
  
    object[Symbol.unscopables] = {x: 0, y: true};
    with (object) {
      assertEquals(1, x);
      assertEquals(2, y);
      assertEquals(3, z);
    }
  
    object[Symbol.unscopables] = {x: 0, y: undefined};
    with (object) {
      assertEquals(1, x);
      assertEquals(5, y);
      assertEquals(3, z);
    }
  }
  runTest(TestBasics);
  
  
  function TestUnscopableChain(object) {
    var x = 1;
    object.x = 2;
  
    with (object) {
      assertEquals(2, x);
    }
  
    object[Symbol.unscopables] = {
      __proto__: {x: true}
    };
    with (object) {
      assertEquals(1, x);
    }
  
    object[Symbol.unscopables] = {
      __proto__: {x: undefined}
    };
    with (object) {
      assertEquals(2, x);
    }
  }
  runTest(TestUnscopableChain);
  
  
  function TestBasicsSet(object) {
    var x = 1;
    object.x = 2;
  
    with (object) {
      assertEquals(2, x);
    }
  
    object[Symbol.unscopables] = {x: true};
    with (object) {
      assertEquals(1, x);
      x = 3;
      assertEquals(3, x);
    }
  
    assertEquals(3, x);
    assertEquals(2, object.x);
  }
  runTest(TestBasicsSet);
  
  
  function TestOnProto(object, proto) {
    var x = 1;
    var y = 2;
    var z = 3;
    proto.x = 4;
  
    Object.setPrototypeOf(object, proto);
    object.y = 5;
  
    with (object) {
      assertEquals(4, x);
      assertEquals(5, y);
      assertEquals(3, z);
    }
  
    proto[Symbol.unscopables] = {x: true};
    with (object) {
      assertEquals(1, x);
      assertEquals(5, y);
      assertEquals(3, z);
    }
  
    object[Symbol.unscopables] = {y: true};
    with (object) {
      assertEquals(4, x);
      assertEquals(2, y);
      assertEquals(3, z);
    }
  
    proto[Symbol.unscopables] = {y: true};
    object[Symbol.unscopables] = {x: true};
    with (object) {
      assertEquals(1, x);
      assertEquals(5, y);
      assertEquals(3, z);
    }
  
    proto[Symbol.unscopables] = {y: true};
    object[Symbol.unscopables] = {x: true, y: undefined};
    with (object) {
      assertEquals(1, x);
      assertEquals(5, y);
      assertEquals(3, z);
    }
  }
  runTest(TestOnProto);
  
  
  function TestSetBlockedOnProto(object, proto) {
    var x = 1;
    object.x = 2;
  
    with (object) {
      assertEquals(2, x);
    }
  
    Object.setPrototypeOf(object, proto);
    proto[Symbol.unscopables] = {x: true};
    with (object) {
      assertEquals(1, x);
      x = 3;
      assertEquals(3, x);
    }
  
    assertEquals(3, x);
    assertEquals(2, object.x);
  }
  runTest(TestSetBlockedOnProto);
  
  
  function TestNonObject(object) {
    var x = 1;
    var y = 2;
    object.x = 3;
    object.y = 4;
  
    object[Symbol.unscopables] = 'xy';
    with (object) {
      assertEquals(3, x);
      assertEquals(4, y);
    }
  
    object[Symbol.unscopables] = null;
    with (object) {
      assertEquals(3, x);
      assertEquals(4, y);
    }
  }
  runTest(TestNonObject);
  
  
  function TestChangeDuringWith(object) {
    var x = 1;
    var y = 2;
    object.x = 3;
    object.y = 4;
  
    with (object) {
      assertEquals(3, x);
      assertEquals(4, y);
      object[Symbol.unscopables] = {x: true};
      assertEquals(1, x);
      assertEquals(4, y);
    }
  }
  runTest(TestChangeDuringWith);
  
  
  function TestChangeDuringWithWithPossibleOptimization(object) {
    var x = 1;
    object.x = 2;
    with (object) {
      for (var i = 0; i < 1000; i++) {
        if (i === 500) object[Symbol.unscopables] = {x: true};
        assertEquals(i < 500 ? 2: 1, x);
      }
    }
  }
  TestChangeDuringWithWithPossibleOptimization({});
  
  
  function TestChangeDuringWithWithPossibleOptimization2(object) {
    var x = 1;
    object.x = 2;
    object[Symbol.unscopables] = {x: true};
    with (object) {
      for (var i = 0; i < 1000; i++) {
        if (i === 500) delete object[Symbol.unscopables];
        assertEquals(i < 500 ? 1 : 2, x);
      }
    }
  }
  TestChangeDuringWithWithPossibleOptimization2({});
  
  
  function TestChangeDuringWithWithPossibleOptimization3(object) {
    var x = 1;
    object.x = 2;
    object[Symbol.unscopables] = {};
    with (object) {
      for (var i = 0; i < 1000; i++) {
        if (i === 500) object[Symbol.unscopables].x = true;
        assertEquals(i < 500 ? 2 : 1, x);
      }
    }
  }
  TestChangeDuringWithWithPossibleOptimization3({});
  
  
  function TestChangeDuringWithWithPossibleOptimization4(object) {
    var x = 1;
    object.x = 2;
    object[Symbol.unscopables] = {x: true};
    with (object) {
      for (var i = 0; i < 1000; i++) {
        if (i === 500) delete object[Symbol.unscopables].x;
        assertEquals(i < 500 ? 1 : 2, x);
      }
    }
  }
  TestChangeDuringWithWithPossibleOptimization4({});
  
  
  function TestChangeDuringWithWithPossibleOptimization4(object) {
    var x = 1;
    object.x = 2;
    object[Symbol.unscopables] = {x: true};
    with (object) {
      for (var i = 0; i < 1000; i++) {
        if (i === 500) object[Symbol.unscopables].x = undefined;
        assertEquals(i < 500 ? 1 : 2, x);
      }
    }
  }
  TestChangeDuringWithWithPossibleOptimization4({});
  
  
  function TestAccessorReceiver(object, proto) {
    var x = 'local';
  
    Object.defineProperty(proto, 'x', {
      get: function() {
        assertEquals(object, this);
        return this.x_;
      },
      configurable: true
    });
    proto.x_ = 'proto';
  
    Object.setPrototypeOf(object, proto);
    proto.x_ = 'object';
  
    with (object) {
      assertEquals('object', x);
    }
  }
  runTest(TestAccessorReceiver);
  
  
  function TestUnscopablesGetter(object) {
    // This test gets really messy when object is the global since the assert
    // functions are properties on the global object and the call count gets
    // completely different.
    if (object === global) return;
  
    var x = 'local';
    object.x = 'object';
  
    var callCount = 0;
    Object.defineProperty(object, Symbol.unscopables, {
      get: function() {
        callCount++;
        return {};
      },
      configurable: true
    });
    with (object) {
      assertEquals('object', x);
    }
    // Once for HasBinding
    assertEquals(1, callCount);
  
    callCount = 0;
    Object.defineProperty(object, Symbol.unscopables, {
      get: function() {
        callCount++;
        return {x: true};
      },
      configurable: true
    });
    with (object) {
      assertEquals('local', x);
    }
    // Once for HasBinding
    assertEquals(1, callCount);
  
    callCount = 0;
    Object.defineProperty(object, Symbol.unscopables, {
      get: function() {
        callCount++;
        return callCount == 1 ? {} : {x: true};
      },
      configurable: true
    });
    with (object) {
      x = 1;
    }
    // Once for HasBinding
    assertEquals(1, callCount);
    assertEquals(1, object.x);
    assertEquals('local', x);
    with (object) {
      x = 2;
    }
    // One more HasBinding.
    assertEquals(2, callCount);
    assertEquals(1, object.x);
    assertEquals(2, x);
  }
  runTest(TestUnscopablesGetter);
  
  
  var global = this;
  function TestUnscopablesGetter2() {
    var x = 'local';
  
    var globalProto = Object.getPrototypeOf(global);
    var protos = [{}, [], function() {}, global];
    var objects = [{}, [], function() {}];
  
    protos.forEach(function(proto) {
      objects.forEach(function(object) {
        Object.defineProperty(proto, 'x', {
          get: function() {
            assertEquals(object, this);
            return 'proto';
          },
          configurable: true
        });
  
        object.__proto__ = proto;
        Object.defineProperty(object, 'x', {
          get: function() {
            assertEquals(object, this);
            return 'object';
          },
          configurable: true
        });
  
        with (object) {
          assertEquals('object', x);
        }
  
        object[Symbol.unscopables] = {x: true};
        with (object) {
          assertEquals('local', x);
        }
  
        delete proto[Symbol.unscopables];
        delete object[Symbol.unscopables];
      });
    });
  
    delete global.x;
    Object.setPrototypeOf(global, globalProto);
  }
  TestUnscopablesGetter2();
  
  
  function TestSetterOnBlacklisted(object, proto) {
    var x = 'local';
    Object.defineProperty(proto, 'x', {
      set: function(x) {
        assertUnreachable();
      },
      get: function() {
        return 'proto';
      },
      configurable: true
    });
    Object.setPrototypeOf(object, proto);
    Object.defineProperty(object, 'x', {
      get: function() {
        return this.x_;
      },
      set: function(x) {
        this.x_ = x;
      },
      configurable: true
    });
    object.x_ = 1;
  
    with (object) {
      x = 2;
      assertEquals(2, x);
    }
  
    assertEquals(2, object.x);
  
    object[Symbol.unscopables] = {x: true};
  
    with (object) {
      x = 3;
      assertEquals(3, x);
    }
  
    assertEquals(2, object.x);
  }
  runTest(TestSetterOnBlacklisted);
  
  
  function TestObjectsAsUnscopables(object, unscopables) {
    var x = 1;
    object.x = 2;
  
    with (object) {
      assertEquals(2, x);
      object[Symbol.unscopables] = unscopables;
      assertEquals(2, x);
    }
  }
  runTest(TestObjectsAsUnscopables);
  
  
  function TestAccessorOnUnscopables(object) {
    var x = 1;
    object.x = 2;
  
    var calls = 0;
    var unscopables = {
      get x() {
        calls++;
        return calls === 1 ? true : undefined;
      }
    };
  
    with (object) {
      assertEquals(2, x);
      object[Symbol.unscopables] = unscopables;
      assertEquals(1, x);
      assertEquals(2, x);
    }
    assertEquals(2, calls);
  }
  runTest(TestAccessorOnUnscopables);
  
  
  function TestLengthUnscopables(object, proto) {
    var length = 2;
    with (object) {
      assertEquals(1, length);
      object[Symbol.unscopables] = {length: true};
      assertEquals(2, length);
      delete object[Symbol.unscopables];
      assertEquals(1, length);
    }
  }
  TestLengthUnscopables([1], Array.prototype);
  TestLengthUnscopables(function(x) {}, Function.prototype);
  TestLengthUnscopables(new String('x'), String.prototype);
  
  
  function TestFunctionNameUnscopables(object) {
    var name = 'local';
    with (object) {
      assertEquals('f', name);
      object[Symbol.unscopables] = {name: true};
      assertEquals('local', name);
      delete object[Symbol.unscopables];
      assertEquals('f', name);
    }
  }
  TestFunctionNameUnscopables(function f() {});
  
  
  function TestFunctionPrototypeUnscopables() {
    var prototype = 'local';
    var f = function() {};
    var g = function() {};
    Object.setPrototypeOf(f, g);
    var fp = f.prototype;
    var gp = g.prototype;
    with (f) {
      assertEquals(fp, prototype);
      f[Symbol.unscopables] = {prototype: true};
      assertEquals('local', prototype);
      delete f[Symbol.unscopables];
      assertEquals(fp, prototype);
    }
  }
  TestFunctionPrototypeUnscopables(function() {});
  
  
  function TestFunctionArgumentsUnscopables() {
    var func = function() {
      var arguments = 'local';
      var args = func.arguments;
      with (func) {
        assertEquals(args, arguments);
        func[Symbol.unscopables] = {arguments: true};
        assertEquals('local', arguments);
        delete func[Symbol.unscopables];
        assertEquals(args, arguments);
      }
    }
    func(1);
  }
  TestFunctionArgumentsUnscopables();
  
  
  function TestArgumentsLengthUnscopables() {
    var func = function() {
      var length = 'local';
      with (arguments) {
        assertEquals(1, length);
        arguments[Symbol.unscopables] = {length: true};
        assertEquals('local', length);
      }
    }
    func(1);
  }
  TestArgumentsLengthUnscopables();
  
  
  function TestFunctionCallerUnscopables() {
    var func = function() {
      var caller = 'local';
      with (func) {
        assertEquals(TestFunctionCallerUnscopables, caller);
        func[Symbol.unscopables] = {caller: true};
        assertEquals('local', caller);
        delete func[Symbol.unscopables];
        assertEquals(TestFunctionCallerUnscopables, caller);
      }
    }
    func(1);
  }
  TestFunctionCallerUnscopables();
  
  
  function TestGetUnscopablesGetterThrows() {
    var object = {
      get x() {
        assertUnreachable();
      }
    };
    function CustomError() {}
    Object.defineProperty(object, Symbol.unscopables, {
      get: function() {
        throw new CustomError();
      }
    });
    assertThrows(function() {
      with (object) {
        x;
      }
    }, CustomError);
  }
  TestGetUnscopablesGetterThrows();
  
  
  function TestGetUnscopablesGetterThrows2() {
    var object = {
      get x() {
        assertUnreachable();
      }
    };
    function CustomError() {}
  
    object[Symbol.unscopables] = {
      get x() {
        throw new CustomError();
      }
    };
    assertThrows(function() {
      with (object) {
        x;
      }
    }, CustomError);
  }
  TestGetUnscopablesGetterThrows();
  