| "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"); |
| |