"use strict"; var createCustomGetterObject = $vm.createCustomGetterObject; function assert(a) { if (!a) throw new Error("Bad!"); } var Base = class Base { constructor() { this._name = "Name"; } get name() { return this._name; } // If this instead returns a static: return "Foo" things somewhat work. set name(x) { this._name = x; } }; var Subclass = class Subclass extends Base { get name() { return super.name; } }; function getterName(instance) { return instance.name; } noInline(getterName); function getterValue(instance) { return instance.value; } noInline(getterValue); const runTimes = 10000; // Base case var instance = new Subclass; for (let i = 0; i < runTimes; i++) assert(getterName(instance) == "Name"); // Polymorphic case class PolymorphicSubclass { get value() { return super.value; } }; let numPolymorphicClasses = 4; let subclasses = new Array(numPolymorphicClasses); for (let i = 0; i < numPolymorphicClasses; i++) { let BaseCode = ` (class Base${i} { get value() { return this._value; } }); `; let Base = eval(BaseCode); subclasses[i] = new PolymorphicSubclass(); subclasses[i]._value = i; Object.setPrototypeOf(subclasses[i], Base.prototype); } for (let i = 0; i < runTimes; i++) { let index = i % numPolymorphicClasses; let value = getterValue(subclasses[index]); assert(value == index); } // Megamorphic case let nClasses = 1000; class MegamorphicSubclass { get value() { return super.value; } }; subclasses = new Array(nClasses); for (let i = 0; i < nClasses; i++) { let BaseCode = ` (class Base${i + 4} { get value() { return this._value; } }); `; let Base = eval(BaseCode); subclasses[i] = new MegamorphicSubclass(); subclasses[i]._value = i; Object.setPrototypeOf(subclasses[i], Base.prototype); } for (let i = 0; i < runTimes; i++) { let index = i % nClasses; let value = getterValue(subclasses[index]); assert(value == index); } // CustomGetter case let customGetter = createCustomGetterObject(); Object.setPrototypeOf(customGetter, Object.prototype); let subObj = { __proto__: customGetter, get value () { return super.customGetterAccessor; } } for (let i = 0; i < runTimes; i++) { let value = getterValue(subObj); assert(value == 100); } subObj.shouldThrow = true; for (let i = 0; i < runTimes; i++) { try { getterValue(subObj); assert(false); } catch(e) { assert(e instanceof TypeError); }; } // CustomValue case customGetter = createCustomGetterObject(); Object.setPrototypeOf(customGetter, Object.prototype); subObj = { __proto__: customGetter, get value () { return super.customGetter; } } for (let i = 0; i < runTimes; i++) { let value = getterValue(subObj); assert(value == 100); } subObj.shouldThrow = true; for (let i = 0; i < runTimes; i++) { let value = getterValue(subObj); assert(value == 100); } // Exception handling case class BaseException { constructor() { this._name = "Name"; } get name() { if (this.shouldThrow) throw new Error("Forced Exception"); return this._name; } }; class SubclassException extends BaseException { get name() { return super.name; } }; let eObj = new SubclassException; for (let i = 0; i < runTimes; i++) assert(getterName(eObj) == "Name"); eObj.shouldThrow = true; for (let i = 0; i < runTimes; i++) { try { getterValue(eObj); assert(false); } catch(e) { eObj.shouldThrow = false; assert(getterName(eObj) == "Name"); }; } // In getter exception handling class BaseExceptionComplex { constructor() { this._name = "Name"; } foo () { if (this.shouldThrow) throw new Error("Forced Exception"); } get name() { this.foo(); return this._name; } }; class SubclassExceptionComplex extends BaseExceptionComplex { get name() { try { return super.name; } catch(e) { this.shouldThrow = false; return super.name; } } }; eObj = new SubclassExceptionComplex; for (let i = 0; i < runTimes; i++) assert(getterName(eObj) == "Name"); eObj.shouldThrow = true; for (let i = 0; i < runTimes; i++) assert(getterName(eObj) == "Name");