function assert(b, m = "Bad!") { if (!b) { throw new Error(m); } } function test(f, iters = 1000) { for (let i = 0; i < iters; i++) f(); } function func(x) { return x; } noInline(func); test(function() { class A { constructor(x) { this._value = x; } set value(x) { this._value = x; } get value() { return this._value; } } class B extends A { set value(x) { super.value = x; } get value() { return super.value; } } let arr = []; for (let i = 0; i < 1000; i++) { arr.push(new B(20)); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === 20); } for (let i = 0; i < 1000; i++) { arr[i].value = i; } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } }, 2); test(function() { function value() { return 'value'; } noInline(value); class A { constructor(x, f = func) { this._value = x; this._func = f; } set value(x) { this._value = x; } get value() { return this._value; } get func() { return this._func; } } class B extends A { set value(x) { super[value()] = x; } get value() { return super[value()]; } inc() { return super[value()]++; } dec() { return super[value()]--; } preInc() { return ++super[value()]; } preDec() { return --super[value()]; } plusEq(x) { super[value()] += x; } minusEq(x) { super[value()] -= x; } timesEq(x) { super[value()] *= x; } divEq(x) { super[value()] /= x; } funcDot(x) { return super.func(x); } funcBracket(x) { return super.func(x); } } let arr = []; for (let i = 0; i < 1000; i++) { arr.push(new B(20)); } for (let i = 0; i < 1000; i++) { let t = arr[i].value; assert(t === 20); } for (let i = 0; i < 1000; i++) { arr[i].value = i; } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { let v = arr[i].inc(); assert(v === i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i+1); } for (let i = 0; i < 1000; i++) { let v = arr[i].dec(); assert(v === i+1); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { let v = arr[i].preInc(); assert(v === i+1); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i+1); } for (let i = 0; i < 1000; i++) { let v = arr[i].preDec(); assert(v === i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { arr[i].plusEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i+i); } for (let i = 0; i < 1000; i++) { arr[i].minusEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { arr[i].timesEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i*i); } for (let i = 0; i < 1000; i++) { if (i === 0) arr[i].value = 0; else arr[i].divEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { arr[i] = new B(0, function(a) { return i + a; }); } for (let i = 0; i < 1000; i++) { assert(arr[i].funcDot(i) === i + i); assert(arr[i].funcBracket(i*2) === i + i*2); } }, 2); test(function() { class A { constructor(x, f = func) { this._value = x; this._func = f; } set value(x) { this._value = x; } get value() { return this._value; } get func() { return this._func; } } class B extends A { set value(x) { (() => super.value = x)(); } get value() { return (() => super.value)(); } inc() { return (() => super.value++)(); } dec() { return (() => super.value--)(); } preInc() { return (() => ++super.value)(); } preDec() { return (() => --super.value)(); } plusEq(x) { (() => super.value += x)(); } minusEq(x) { (() => super.value -= x)(); } timesEq(x) { (() => super.value *= x)(); } divEq(x) { (() => super.value /= x)(); } funcDot(x) { return (() => super.func(x))(); } funcBracket(x) { return (() => super.func(x))(); } } let arr = []; for (let i = 0; i < 1000; i++) { arr.push(new B(20)); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === 20); } for (let i = 0; i < 1000; i++) { arr[i].value = i; } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { let v = arr[i].inc(); assert(v === i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i+1); } for (let i = 0; i < 1000; i++) { let v = arr[i].dec(); assert(v === i+1); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { let v = arr[i].preInc(); assert(v === i+1); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i+1); } for (let i = 0; i < 1000; i++) { let v = arr[i].preDec(); assert(v === i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { arr[i].plusEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i+i); } for (let i = 0; i < 1000; i++) { arr[i].minusEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { arr[i].timesEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i*i); } for (let i = 0; i < 1000; i++) { if (i === 0) arr[i].value = 0; else arr[i].divEq(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].value === i); } for (let i = 0; i < 1000; i++) { arr[i] = new B(0, function(a) { return i + a; }); } for (let i = 0; i < 1000; i++) { assert(arr[i].funcDot(i) === i + i); assert(arr[i].funcBracket(i*2) === i + i*2); } }, 2); test(function() { function foo() { return 'foo'; } noInline(foo); class A { } let obj = {}; A.prototype.foo = obj; A.prototype[0] = obj; class B extends A { baz() { return super[foo()]; } jaz() { return super.foo; } bar() { return super[0]; } } assert((new B).baz() === obj); assert((new B).jaz() === obj); assert((new B).bar() === obj); }); test(function() { class A { } for (let i = 0; i < 1000; i++) A.prototype[i] = i; class B extends A { index(i) { return super[i]; } } let b = new B; for (let i = 0; i < 1000; i++) { assert(b.index(i) === i); } }, 2); test(function() { let obj = {}; class A { constructor(r) { this._foo = r; } } Object.defineProperty(A.prototype, '0', { get: function() { return this._foo; } }); class B extends A { bar() { return super[0]; } } let rand = Math.random(); assert((new B(rand)).bar() === rand); }); test(function() { class A { constructor() { this._array = []; } set foo(x) { this._array.push(x); } get array() { return this._array; } } class B extends A { baz(i) { let o = {x:20, y:30, [i]:i}; for (super.foo in o) { } } } let arr = []; for (let i = 0; i < 20; i++) arr.push(new B); for (let i = 0; i < arr.length; i++) { let obj = arr[i]; obj.baz(i); } for (let i = 0; i < arr.length; i++) { let obj = arr[i].array; assert(obj.length === 3) assert(obj[0] === '' + i); assert(obj[1] === 'x') assert(obj[2] === 'y') } }, 100); test(function() { function foo() { return 'foo'; } noInline(foo); class A { constructor() { this._array = []; } set foo(x) { this._array.push(x); } get array() { return this._array; } } class B extends A { baz(i) { let o = {x:20, y:30, [i]:i}; for (super[foo()] in o) { } } } let arr = []; for (let i = 0; i < 20; i++) arr.push(new B); for (let i = 0; i < arr.length; i++) { let obj = arr[i]; obj.baz(i); } for (let i = 0; i < arr.length; i++) { let obj = arr[i].array; assert(obj.length === 3) assert(obj[0] === '' + i); assert(obj[1] === 'x') assert(obj[2] === 'y') } }, 100); test(function() { class A { constructor() { this._array = []; } set foo(x) { this._array.push(x); } get array() { return this._array; } } class B extends A { baz(i) { let o = ['' + i, "x", "y"]; for (super.foo of o) { } } } let arr = []; for (let i = 0; i < 20; i++) arr.push(new B); for (let i = 0; i < arr.length; i++) { let obj = arr[i]; obj.baz(i); } for (let i = 0; i < arr.length; i++) { let obj = arr[i].array; assert(obj.length === 3) assert(obj[0] === '' + i); assert(obj[1] === 'x') assert(obj[2] === 'y') } }, 100); test(function() { function foo() { return 'foo'; } class A { constructor() { this._array = []; } set foo(x) { this._array.push(x); } get array() { return this._array; } } class B extends A { baz(i) { let o = ['' + i, "x", "y"]; for (super[foo()] of o) { } } } let arr = []; for (let i = 0; i < 20; i++) arr.push(new B); for (let i = 0; i < arr.length; i++) { let obj = arr[i]; obj.baz(i); } for (let i = 0; i < arr.length; i++) { let obj = arr[i].array; assert(obj.length === 3) assert(obj[0] === '' + i); assert(obj[1] === 'x') assert(obj[2] === 'y') } }, 100); test(function() { class A { constructor() { this._foo = null; } set foo(x) { this._foo = x; } get foo() { return this._foo; } } function obj(i) { return {o: i}; } noInline(obj); class B extends A { baz(i) { ;({o: super.foo} = obj(i)); } } let arr = []; for (let i = 0; i < 1000; i++) { arr.push((new B)); } for (let i = 0; i < 1000; i++) { arr[i].baz(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].foo === i); } }, 10); test(function() { function foo() { return 'foo'; } noInline(foo); class A { constructor() { this._foo = null; } set foo(x) { this._foo = x; } get foo() { return this._foo; } } function obj(i) { return {o: i}; } noInline(obj); class B extends A { baz(i) { ;({o: super[foo()]} = obj(i)); } } let arr = []; for (let i = 0; i < 1000; i++) { arr.push((new B)); } for (let i = 0; i < 1000; i++) { arr[i].baz(i); } for (let i = 0; i < 1000; i++) { assert(arr[i].foo === i); } }, 10); test(function() { class A { constructor() { this._foo = null; } get call() { let ret = () => 'call'; noInline(ret); return ret; } get apply() { let ret = () => 'apply'; noInline(ret); return ret; } } class B extends A { foo() { return super.call({}, 20, 30); } bar() { return super.apply({}, [10, 20]); } } for (let i = 0; i < 1000; i++) { let b = new B; assert(b.foo() === 'call'); assert(b.bar() === 'apply'); } }, 2); test(function() { class A { constructor(i) { this._i = i; } get foo() { return function(strings, ...values) { return [strings, values]; } } get i() { return this._i; } } class B extends A { baz() { return super.foo`hello${super.i}world`; } } let arr = []; for (let i = 0; i < 1000; i++) { let b = new B(i); arr.push(b); } for (let i = 0; i < 1000; i++) { let r = arr[i].baz(); assert(r.length === 2); assert(r[0].length === 2); assert(r[0][0] === 'hello'); assert(r[0][1] === 'world'); assert(r[1].length === 1); assert(r[1][0] === i); } }, 10);