diff --git a/packages/ember-metal/lib/mixin.js b/packages/ember-metal/lib/mixin.js index f38fa44fbfb..061d8bb92a3 100644 --- a/packages/ember-metal/lib/mixin.js +++ b/packages/ember-metal/lib/mixin.js @@ -39,6 +39,9 @@ import { } from 'ember-metal/events'; import { isStream } from 'ember-metal/streams/utils'; +function ROOT() {} +ROOT.__hasSuper = false; + var REQUIRED; var a_slice = [].slice; @@ -185,7 +188,7 @@ function applyMergedProperties(obj, key, value, values) { } if (hasFunction) { - newBase._super = function () {}; + newBase._super = ROOT; } return newBase; @@ -372,7 +375,7 @@ function applyMixin(obj, mixins, partial) { var keys = []; var key, value, desc; - obj._super = function () {}; + obj._super = ROOT; // Go through all mixins and hashes passed in, and: // diff --git a/packages/ember-metal/lib/utils.js b/packages/ember-metal/lib/utils.js index cabca5d679e..c9958b5ce11 100644 --- a/packages/ember-metal/lib/utils.js +++ b/packages/ember-metal/lib/utils.js @@ -254,9 +254,31 @@ export function guidFor(obj) { } -var sourceAvailable = (function() { - return this; -}).toString().indexOf('return this;') > -1; +const checkHasSuper = (function () { + let sourceAvailable = (function() { + return this; + }).toString().indexOf('return this;') > -1; + + if (sourceAvailable) { + return function checkHasSuper(func) { + return func.toString().indexOf('_super') > -1; + }; + } + + return function checkHasSuper() { + return true; + }; +}()); + +function ROOT() {} +ROOT.__hasSuper = false; + +function hasSuper(func) { + if (func.__hasSuper === undefined) { + func.__hasSuper = checkHasSuper(func); + } + return func.__hasSuper; +} /** Wraps the passed function so that `this._super` will point to the superFunc @@ -270,43 +292,39 @@ var sourceAvailable = (function() { @param {Function} superFunc The super function. @return {Function} wrapped function. */ -export function wrap(func, _superFunc) { - var superFunc = _superFunc; - var hasSuper; - if (sourceAvailable) { - hasSuper = func.__hasSuper; - - if (hasSuper === undefined) { - hasSuper = func.toString().indexOf('_super') > -1; - func.__hasSuper = hasSuper; - } - - if (!hasSuper) { - return func; - } +export function wrap(func, superFunc) { + if (!hasSuper(func)) { + return func; } - - if (superFunc.wrappedFunction === undefined) { - // terminate _super to prevent infinite recursion - superFunc = wrap(superFunc, function () {}); + // ensure an unwrapped super that calls _super is wrapped with a terminal _super + if (!superFunc.wrappedFunction && hasSuper(superFunc)) { + return _wrap(func, _wrap(superFunc, ROOT)); } - return _wrap(func, superFunc); } function _wrap(func, superFunc) { function superWrapper() { - var ret; - var orig = this._super; + let orig = this._super; + let length = arguments.length; + let ret; this._super = superFunc; - switch (arguments.length) { + switch (length) { case 0: ret = func.call(this); break; case 1: ret = func.call(this, arguments[0]); break; case 2: ret = func.call(this, arguments[0], arguments[1]); break; case 3: ret = func.call(this, arguments[0], arguments[1], arguments[2]); break; case 4: ret = func.call(this, arguments[0], arguments[1], arguments[2], arguments[3]); break; case 5: ret = func.call(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]); break; - default: ret = func.apply(this, arguments); break; + default: + // v8 bug potentially incorrectly deopts this function: https://code.google.com/p/v8/issues/detail?id=3709 + // we may want to keep this around till this ages out on mobile + let args = new Array(length); + for (var x = 0; x < length; x++) { + args[x] = arguments[x]; + } + ret = func.apply(this, args); + break; } this._super = orig; return ret;