| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- /// <reference path="protolib.js" />
- if (this.WScript && this.WScript.LoadScriptFile) {
- WScript.LoadScriptFile("protolib.js");
- }
- function printProtoChain(obj) {
- var known = [
- Number.prototype, Boolean.prototype, String.prototype, Object.prototype, Array.prototype, Function.prototype,
- ];
- var names = [
- "Number.prototype", "Boolean.prototype", "String.prototype", "Object.prototype", "Array.prototype", "Function.prototype",
- ];
- var s = "";
- while (obj) {
- var i = known.indexOf(obj);
- var name = (i >= 0 ? names[i] : JSON.stringify(obj));
- if (s == "") {
- s = name;
- } else {
- s += " -> " + name;
- }
- obj = Object.getPrototypeOf(obj);
- }
- helpers.writeln(s);
- }
- function make_point(_x, _y) {
- return { x: _x, y: _y };
- }
- var tests;
- if (helpers.isVersion10OrLater) { // TODO: Change to _11_OrLater
- tests = [
- {
- name: "Test Object.prototype.__proto__ attributes",
- body: function () {
- var desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
- helpers.printObject(desc);
- }
- },
- {
- name: "Read built-in __proto__",
- body: function () {
- assert.isTrue(12["__proto__"] === Number.prototype, "int __proto__");
- assert.isTrue(12.3["__proto__"] === Number.prototype, "float __proto__");
- assert.isTrue((new Number(12)).__proto__ === Number.prototype, "Number Object __proto__");
- assert.isTrue(true.__proto__ === Boolean.prototype, "Boolean Value __proto__");
- assert.isTrue(false.__proto__ === Boolean.prototype, "Boolean Value __proto__");
- assert.isTrue((new Boolean(true)).__proto__ === Boolean.prototype, "Boolean Object __proto__");
- assert.isTrue((new Boolean(false)).__proto__ === Boolean.prototype, "Boolean Object __proto__");
- assert.isTrue("hello".__proto__ === String.prototype, "String Value __proto__");
- assert.isTrue(new String("hello").__proto__ === String.prototype, "String Object __proto__");
- assert.isTrue({}.__proto__ === Object.prototype, "{} __proto__");
- assert.isTrue([].__proto__ === Array.prototype, "[] __proto__");
- assert.isTrue(Array.prototype.__proto__ === Object.prototype, "Array.prototype.__proto__");
- assert.isTrue(Object.prototype.__proto__ === null, "Object.prototype.__proto__");
- assert.isTrue(Array.__proto__ === Function.prototype, "Array.__proto__");
- assert.isTrue(Function.__proto__ === Function.prototype, "Function.__proto__");
- assert.isTrue(Function.prototype.__proto__ === Object.prototype, "Function.prototype.__proto__");
- var o = { a: 0 };
- assert.isTrue(o.__proto__ === Object.prototype, "o.__proto__");
- assert.isTrue(o.__proto__.__proto__ === null, "o.__proto__.__proto__");
- }
- },
- {
- name: "Change built-in's __proto__ chain",
- body: function () {
- function changeProtoAndTest(obj, newProto, test) {
- var oldProto = obj.__proto__;
- obj.__proto__ = newProto;
- printProtoChain(obj);
- try {
- test();
- } finally {
- obj.__proto__ = oldProto; // Restore old __proto__
- }
- }
- // Number doesn't have sort method, should throw
- assert.throws(function () { 12["sort"]() }, TypeError, "Object doesn't support property or method 'sort'");
- // Number gets Array methods from new prototype in the chain
- changeProtoAndTest(Number.prototype, Array.prototype, function () {
- 12["sort"]();
- var o = new Number(34);
- o[0] = 8;
- o[1] = 3;
- o[2] = 9;
- o[3] = 5;
- o.length = 4;
- assert.areEqual("8 3 9 5", o.join(" "));
- o.sort();
- assert.areEqual("3 5 8 9", o.join(" "));
- });
- // Boolean doesn't have String methods
- assert.throws(function () { true.toUpperCase() }, TypeError, "Object doesn't support property or method 'toUpperCase'");
- // Boolean gets String methods from new prototype in the chain
- changeProtoAndTest(Boolean.prototype, new String("abc"), function () {
- assert.areEqual("TRUE", true.toUpperCase());
- assert.areEqual("FALSE", false.toUpperCase());
- assert.areEqual("TRUE", (new Boolean(true)).toUpperCase());
- assert.areEqual("FALSE", (new Boolean(false)).toUpperCase());
- });
- }
- },
- {
- name: "Change __proto__ to null/undefined",
- body: function () {
- var a = {};
- // Set an object's prototype to undefined
- {
- var a = {};
- assert.isTrue(a.__proto__ === Object.prototype);
- assert.isTrue(!a.isPrototypeOf(a));
- a.__proto__ = undefined;
- assert.isTrue(a.__proto__ === Object.prototype);
- assert.isTrue(!a.isPrototypeOf(a));
- assert.throws__proto__ArgNotObjectOrNull(function () { Object.setPrototypeOf(a, undefined); }, "Object.setPrototypeOf");
- }
- // Set an object's prototype to null
- function f1(O, P) { O.__proto__ = P; }
- function f2(O, P) { Object.setPrototypeOf(O, P); }
- [f1, f2].forEach(function (f) {
- var a = {};
- assert.isTrue(a.__proto__ === Object.prototype);
- assert.isTrue(!a.isPrototypeOf(a));
- f(a, null);
- assert.isTrue(Object.getPrototypeOf(a) === null);
- assert.isTrue(a.__proto__ === undefined, "lost Object.prototype __proto__ getter");
- assert.isTrue(a.isPrototypeOf === undefined, "lost Object.prototype methods");
- });
- // side test -- try to change null/undefined's __proto__ should throw
- var __proto__setter = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").set;
- [null, undefined].forEach(function (o) {
- assert.throws__proto__ThisNotObject(function () {
- __proto__setter.apply(o, [{}]);
- });
- assert.throws__proto__ArgNotObject(function () {
- Object.setPrototypeOf(o, {});
- });
- });
- }
- },
- {
- name: "Change __proto__ to neither Object nor null, should throw",
- body: function () {
- function assertThrowAndNoChange(proto) {
- var a = {};
- a.__proto__ = proto;
- assert.isTrue(Object.getPrototypeOf(a) === Object.prototype, "Change __proto__ to: " + proto);
- assert.throws__proto__ArgNotObjectOrNull(function () {
- Object.setPrototypeOf(a, proto);
- }, "Object.setPrototypeOf");
- assert.isTrue(Object.getPrototypeOf(a) === Object.prototype, "Change __proto__ to: " + proto);
- }
- var __proto__setter = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").set;
- [
- undefined, 0, 123, -12.3, NaN, Infinity, true, false, "str"
- ].forEach(function (proto) {
- assertThrowAndNoChange(proto);
- // side test -- try to change these primitive's __proto__ should pass (but has no effect)
- if (proto !== undefined) {
- proto.__proto__ = {}; // This is ok
- __proto__setter.apply(proto, [{}]); // This is ok
- Object.setPrototypeOf(proto, {});
- }
- });
- }
- },
- {
- name: "Change object.__proto__ to an object",
- body: function () {
- var a = { a: 100, b: 100 };
- var b = { b: 200, c: 200 };
- b.__proto__ = a;
- helpers.printObject(b);
- helpers.writeln("\n-- delete b.b --");
- delete b.b;
- helpers.printObject(b);
- }
- },
- {
- name: "Change object.__proto__ to an array",
- body: function () {
- var a = [8, 3, 9, 5];
- var b = { b: 200 };
- // b.sort not available
- assert.throws(function () { b.sort(); });
- b.__proto__ = a;
- assert.isTrue(!Array.isArray(b), "Still not Array");
- helpers.printObject(b);
- helpers.writeln("\n-- a.sort --");
- a.sort();
- helpers.printObject(b);
- helpers.writeln("\n-- b.sort --");
- b.sort();
- helpers.printObject(b);
- }
- },
- {
- name: "Change array.__proto__ to an object",
- body: function () {
- var a = { a: 100 };
- var b = [8, 3, 9, 5];
- b.__proto__ = a;
- assert.isTrue(Array.isArray(b), "Still is Array");
- helpers.printObject(b);
- // But now b.sort is gone
- assert.throws(function () { b.sort(); });
- // We can sort by apply
- helpers.writeln("\n-- sort.apply --");
- Array.prototype.sort.apply(b);
- helpers.printObject(b);
- }
- },
- {
- name: "Attempt to change DOM object __proto__",
- body: function () {
- function test(domObj, expected) {
- var p0 = Object.getPrototypeOf(domObj);
- assert.isTrue(p0 === domObj.__proto__);
- var p1 = { __proto__: p0 };
- domObj.__proto__ = p1;
- assert.areEqual(expected, Object.getPrototypeOf(domObj) === p1);
- assert.isTrue(expected || domObj.document !== null); // expected succeed or otherwise window
- }
- if (helpers.isInBrowser()) {
- test(this, false);
- test(window, false);
- test(window.frames[0], false);
- test(document, true);
- test(document.body, true);
- test(document.createElement("div"), true);
- // test form scope
- var f = document.getElementById("form1");
- var p0 = f.__proto__;
- f.__proto__ = { form1_injected: "form1_injected_value", __proto__: p0 };
- var button1 = document.getElementById("button1");
- button1.click();
- assert.areEqual("form1_injected_value", button1.form1_injected_copy);
- }
- }
- },
- {
- name: "__proto__ and instanceof/isPrototypeOf",
- body: function () {
- function A() { }
- function B() { }
- var a = new A();
- var b = new B();
- assert.isTrue(!(b instanceof A), "Initially b is not instance of A");
- assert.isTrue(!(A.prototype.isPrototypeOf(b)));
- assert.isTrue(b instanceof B, "Initially b is instance of B");
- assert.isTrue(B.prototype.isPrototypeOf(b));
- b.__proto__ = A.prototype;
- assert.isTrue(b instanceof A, "Now b is instance of A");
- assert.isTrue(A.prototype.isPrototypeOf(b));
- assert.isTrue(!(b instanceof B), "Now b is not instance of B");
- assert.isTrue(!(B.prototype.isPrototypeOf(b)));
- b.__proto__ = a;
- assert.isTrue(b instanceof A, "b is still instance of A");
- assert.isTrue(A.prototype.isPrototypeOf(b));
- assert.isTrue(!(b instanceof B), "b is still not instance of B");
- assert.isTrue(!(B.prototype.isPrototypeOf(b)));
- b.__proto__ = B.prototype;
- assert.isTrue(!(b instanceof A), "b is back not instance of A");
- assert.isTrue(!(A.prototype.isPrototypeOf(b)));
- assert.isTrue(b instanceof B, "b is back instance of B");
- assert.isTrue(B.prototype.isPrototypeOf(b));
- }
- },
- {
- name: "Verify we reject simple __proto__ cycle",
- body: function () {
- var a = {};
- var b = {};
- var c = {};
- a.__proto__ = b;
- b.__proto__ = c;
- assert.throws__proto__Cyclic(function () { c.__proto__ = a });
- assert.throws__proto__Cyclic(function () { a.__proto__ = a });
- assert.throws__proto__Cyclic(function () { Array.prototype.__proto__ = [] });
- }
- },
- {
- name: "Verify proto cache is discarded",
- body: function () {
- var a = make_point(1, 2);
- var b = make_point(3, 4);
- //
- // Create prototype chain:
- // x -> y -> a
- // where x inherits properties from prototype a.
- //
- var y = Object.create(a);
- var x = Object.create(y);
- function print(o) {
- helpers.writeln(o.x + ", " + o.y);
- }
- helpers.writeln("Before change");
- print(x);
- //
- // Now change prototype chain:
- // x -> y -> b
- // While changing y.__proto__, we should discard inline proto cache in original chain.
- // x should now get changed properties from prototype b.
- //
- helpers.writeln("After change");
- y.__proto__ = b;
- print(x);
- }
- },
- {
- // Copy of above proto cache test and change a getter
- name: "Verify proto getter cache is discarded",
- body: function () {
- var a = make_point(1, 2);
- var b = make_point(3, 4);
- Object.defineProperty(a, "y", { get: function () { return 7; }, enumerable: true, configurable: true });
- //
- // Create prototype chain:
- // x -> y -> a
- // where x inherits properties from prototype a.
- //
- var y = Object.create(a);
- var x = Object.create(y);
- function print(o) {
- helpers.writeln(o.x + ", " + o.y);
- }
- helpers.writeln("Before change");
- print(x);
- //
- // Now change prototype chain:
- // x -> y -> b
- // While changing y.__proto__, we should discard inline proto cache in original chain.
- // x should now get changed properties from prototype b.
- //
- helpers.writeln("After change");
- y.__proto__ = b;
- print(x);
- },
- },
- {
- name: "Verify the new proto object is marked as proto, so that changing the proto object invalidates related proto cache",
- body: function () {
- var b = make_point(3, 4);
- function print(o) {
- helpers.writeln(o.x + ", " + o.y);
- }
- var x = {};
- helpers.writeln("Before change");
- print(x);
- helpers.writeln("After change");
- x.__proto__ = b; // This should mark "b" as a prototype object
- print(x);
- //
- // Now we make certain change to "b". If b is marked on prototype correctly, the following should invalidate related proto cache.
- //
- helpers.writeln("After change proto property");
- Object.defineProperty(b, "x", { get: function () { return 9; }, enumerable: true, configurable: true });
- print(x);
- }
- },
- {
- name: "Verify changing __proto__ works safely with ObjTypeSpec",
- body: function () {
- function f(o, a) {
- var m = o.x;
- if (a > 0)
- {
- o.__proto__ = {}; // ObjTypeSpec won't generate 2nd type check for the next "o.x" load. But implicit call bailout should bailout right here.
- }
- var n = o.x;
- assert.areEqual(1, m);
- assert.areEqual(a > 0 ? undefined : 1, n);
- }
- var p = { x: 1 };
- var o = Object.create(p);
- f(o, 0); // no __proto__ change in interpreter.
- f(o, 0); // -maxInterpretCount:1, still no __proto__ change in jit.
- f(o, 1); // -maxInterpretCount:1, now with __proto__ change in jit.
- }
- },
- {
- name: "Verify PathTypeHandler successor Types continue to work, case 1",
- body: function () {
- function f() {
- return { x_100: 1 }; // Use x_100 to start a unique path
- }
- var o1 = f();
- o1.__proto__ = { a: 2 };
- o1.y = 1; // This has path "x" -> "x, y"
- var o2 = f();
- o2.y = 1; // This also has path "x" -> "x, y"
- // But o1 and o2 can't go to the same Type. If they do, they have the same [[prototype]], which is wrong.
- assert.areEqual(2, o1.a, "from prototype");
- assert.areEqual(undefined, o2.a, "should be undefined");
- }
- },
- {
- name: "Verify PathTypeHandler successor Types continue to work, case 2",
- body: function () {
- function f() {
- return { x_101: 1 }; // Use x_101 to start a unique path
- }
- var o1 = f();
- o1.y = 1; // This has path "x" -> "x, y"
- var o2 = f();
- o2.__proto__ = { a: 2 };
- o2.y = 1; // This also has path "x" -> "x, y"
- // But o1 and o2 can't go to the same Type. If they do, they have the same [[prototype]], which is wrong.
- assert.areEqual(undefined, o1.a, "should be undefined");
- assert.areEqual(2, o2.a, "from prototype");
- }
- },
- {
- name: "Verify that we can shadow __proto__ property",
- body: function () {
- var p = {};
- var o = {};
- o.__proto__ = p;
- Object.defineProperty(p, "__proto__", { value: 10, writable: true, enumerable: true, configurable: true });
- assert.areEqual(10, o.__proto__);
- o.__proto__ = -100;
- assert.areEqual(-100, o.__proto__);
- delete o.__proto__;
- assert.areEqual(10, o.__proto__);
- delete p.__proto__;
- assert.areEqual(p, o.__proto__);
- assert.areEqual(Object.prototype, p.__proto__);
- o.__proto__ = { a: 123 };
- assert.areEqual(123, o.a);
- }
- },
- {
- name: "Test fast path o[i] with changed prototype",
- body: function () {
- var o = []; o.length = 10;
- var p = [123];
- o.__proto__ = p; // Now o's prototype != Array.prototype
- assert.areEqual(123, o[0]);
- var p0 = { "1": 4 };
- p.__proto__ = p0; // Now p's prototype != Object.prototype
- assert.areEqual(4, o[1]);
- p0.__proto__ = null;
- assert.areEqual(undefined, o[2]);
- }
- },
- {
- name: "Test fast path o[i] when o.__proto__ == null",
- body: function () {
- var o = [];
- o.__proto__ = null;
- assert.areEqual(undefined, o[1]);
- }
- },
- {
- name: "Test fast path o[i] when o.__proto__.__proto__ == null",
- body: function () {
- var o = [];
- o.__proto__ = [12, 34];
- o.__proto__.__proto__ = null;
- assert.areEqual(undefined, o[5]);
- }
- },
- {
- name: "Test Array methods with changed prototype",
- body: function () {
- function make_array() {
- var arr = [];
- for (var p in arguments) {
- var i = arguments[p];
- arr[i] = i;
- }
- return arr;
- }
- var o = []; o.length = 10;
- o.__proto__ = make_array(0, 1, 2);
- o.__proto__.__proto__ = make_array(3, 4);
- o.__proto__.__proto__.__proto__ = { "5": 5, "6": 6 };
- o.__proto__.__proto__.__proto__.__proto__ = Array.prototype;
- var a = o.slice();
- assert.areEqual("0,1,2,3,4,5,6,,,", a.toString());
- a.__proto__ = make_array(8, 9);
- assert.areEqual("0,1,2,3,4,5,6,,8,9", a.toString());
- }
- },
- {
- name: "Test cross-site change prototype",
- body: function () {
- if (this.WScript.LoadScriptFile) {
- var eng = make_engine();
- var other = eng.eval('var o = { get name() {return this.v}, set name(value) {this.v = value}, v: "a name" }; o');
- var o = { __proto__: other };
- assert.areEqual("a name", o.name);
- }
- }
- },
- {
- name: "Test change prototype of global object",
- body: function () {
- if (this.WScript.LoadScriptFile) {
- make_engine().run(function () {
- function test(x) {
- var p = { a: 1 };
- // Should go through general change prototype code path and mark "p" as prototype. Then changes to "p" should invalidate proto cache.
- x.__proto__ = p;
- assert.areEqual(1, x.a);
- Object.defineProperty(p, "a", { get: function () { return 2; }, configurable: true });
- assert.areEqual(2, x.a);
- }
-
- assert.isTrue(__proto__ === Object.prototype); // default binds to global this.__proto__
- test(this);
- __proto__ = { b: 3, __proto__: __proto__ };
- assert.areEqual(2, a);
- assert.areEqual(3, b);
- });
- }
- }
- },
- {
- name: "Blue 62526: __proto__: ArrayElementEnumerator does not expect non-TypeIds_Object on prototype",
- body: function () {
- var a = new String();
- a[1] = "a1";
- a[2] = "a2";
- a.__proto__ = [];
- var b = [0];
- b.__proto__ = a;
- b.length = 5;
- b = b.concat([]);
- assert.areEqual('[0,"a1","a2",null,null]', JSON.stringify(b));
- }
- },
- {
- name: "Blue 114364: __proto__: Object.preventExtensions should make [[prototype]] immutable",
- body: function () {
- var a = {};
- Object.preventExtensions(a);
-
- assert.throws(
- function () { a.__proto__ = { x: 1 } },
- TypeError);
- assert.isTrue(a.__proto__ === Object.prototype, "__proto__ should remain unchanged");
- }
- },
- {
- name: "Blue 245453: __proto__: Invalid has-only-writable-data-property cache",
- body: function () {
- var a = { __proto__: {} };
- a.x = 0; // populate cache -- whole prototype chain has only writable data properties
- a.__proto__.__proto__ = /a_regex/;
- a.source = 1;
- assert.areEqual("a_regex", a.source, "prototype chain should NOT has-only-writable-data-properties");
- }
- },
- {
- name: "Blue 245453: __proto__: Invalid has-only-writable-data-property cache -- verify cross-context",
- body: function () {
- if (this.WScript.LoadScriptFile) {
- var eng = make_engine();
- var other = eng.eval('({b: 1})');
- var a = { __proto__: other };
- assert.areEqual(1, a.b, "a.b from prototype");
- a.x = 0; // try to populate cache -- prototype chain has only writable data properties -- our code actually would not cache cross-context
- Object.defineProperty(other, "y", { value: 2, enumerable: true, writable: false });
- a.y = 1234;
- assert.areEqual(2, a.y, "prototype chain should NOT be has-only-writable-data-properties");
- }
- }
- },
- ];
- } else {
- tests = [
- {
- name: "Test Object.prototype.__proto__",
- body: function () {
- assert.isTrue(Object.prototype.__proto__ === undefined, "Object.prototype.__proto__ only supported on IE11 or later");
- assert.isTrue(Object.hasOwnProperty("getPrototypeOf") && !Object.hasOwnProperty("setPrototypeOf"), "Object.setPrototypeOf only supported on IE11 or later");
- var o = { __proto__: { pp: 123 } }; // compat mode: not working in object literal
- assert.areEqual(undefined, o.pp);
- }
- }
- ];
- }
- testRunner.run(tests);
|