diff --git a/src/avm2/runtime.js b/src/avm2/runtime.js index 9bc7867e45..44fde713fe 100644 --- a/src/avm2/runtime.js +++ b/src/avm2/runtime.js @@ -1627,7 +1627,7 @@ var Runtime = (function () { }; function makeQualifiedNameTraitMap(traits) { - var map = {}; + var map = Object.create(null); for (var i = 0; i < traits.length; i++) { map[Multiname.getQualifiedName(traits[i].name)] = traits[i]; } @@ -1650,7 +1650,9 @@ var Runtime = (function () { var baseOpenMethods = base[VM_OPEN_METHODS]; for (var i = 0; i < baseBindings.length; i++) { var qn = baseBindings[i]; - if (!traitMap[qn]) { + // TODO: Make sure we don't add overriden methods as patch targets. This may be + // broken for getters / setters. + if (!traitMap[qn] || traitMap[qn].isGetter() || traitMap[qn].isSetter()) { var baseBindingDescriptor = Object.getOwnPropertyDescriptor(base, qn); Object.defineProperty(obj, qn, baseBindingDescriptor); if (baseOpenMethods.hasOwnProperty(qn)) { diff --git a/src/avm2/tests/regress/correctness/pass/callsuper8.as b/src/avm2/tests/regress/correctness/pass/callsuper8.as new file mode 100644 index 0000000000..be693bcbcc --- /dev/null +++ b/src/avm2/tests/regress/correctness/pass/callsuper8.as @@ -0,0 +1,31 @@ +package { + + interface I { + function get x (); + } + + class A implements I { + public function get x () { + return 1; + } + } + + class B extends A implements I { + public function set x (v) { + trace("set"); + } + } + + class C extends B implements I { + public override function get x () { + return 1 + super.x; + } + } + + // Overriding a getter that is defined two levels up and which has a + // setter in between. + + trace(new C().x); + + trace("--"); +} diff --git a/src/avm2/util.js b/src/avm2/util.js index 60e67e325e..441ac37526 100644 --- a/src/avm2/util.js +++ b/src/avm2/util.js @@ -85,12 +85,32 @@ function defineReadOnlyProperty(obj, name, value) { enumerable: false }); } +function getLatestGetterOrSetterPropertyDescriptor(obj, name) { + var descriptor = {}; + while (obj) { + var tmp = Object.getOwnPropertyDescriptor(obj, name); + if (tmp) { + descriptor.get = descriptor.get || tmp.get; + descriptor.set = descriptor.set || tmp.set; + } + if (descriptor.get && descriptor.set) { + break; + } + obj = Object.getPrototypeOf(obj); + } + return descriptor; +} + function defineNonEnumerableGetterOrSetter(obj, name, value, isGetter) { + var descriptor = getLatestGetterOrSetterPropertyDescriptor(obj, name); + descriptor.configurable = true; + descriptor.enumerable = false; if (isGetter) { - defineNonEnumerableGetter(obj, name, value); + descriptor.get = value; } else { - defineNonEnumerableSetter(obj, name, value); + descriptor.set = value; } + Object.defineProperty(obj, name, descriptor); } function defineNonEnumerableGetter(obj, name, getter) {