Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #4060: base.js.2.diff

File base.js.2.diff, 10.0 kB (added by newmanb@stanford.edu, 2 years ago)

Important bugfix due to Dominic Clifton. Exceptions can no longer corrupt the call stack. Added to unit tests.

  • src/base.js

    old new  
    1 var Class = { 
    2   create: function() { 
    3     return function() {  
    4       this.initialize.apply(this, arguments); 
     1var Class = new (function() { 
     2 
     3  // You'll definitely want to check out 
     4  // http://groupspace.org/devben/proto-svn/demo/ 
     5  // to get a mile-high understanding of why I 
     6  // bothered with all of this. 
     7 
     8  this.NAME = "Class"; 
     9  this.VERSION = Prototype.Version || 2.0; 
     10  // put your name here if you've helped in any way! 
     11  this.AUTHORS = "Ben Newman (of groupspace.org), et al."; 
     12   
     13  this.__repr__ = function() { 
     14    return "[" + this.NAME + " " + this.VERSION + "]"; 
     15  }; 
     16 
     17  this.toString = function() { 
     18    return this.__repr__(); 
     19  }; 
     20 
     21  var __doNotInit = {}; 
     22 
     23  this.create = function(declarations) { 
     24 
     25    var ctor = function() { 
     26      if (arguments[0] !== __doNotInit) { 
     27        this._runTimeDeclarations = {}; 
     28        if (this.initialize) 
     29          this.initialize.apply(this, arguments); 
     30      } 
     31    }; 
     32 
     33    if (declarations) { 
     34      // runs when we call __inherit(ctor, ownPrototype) below 
     35      var ownPrototype = function() { 
     36        for (var i in declarations) 
     37          __attachClassProperty(i, declarations[i], this); 
     38        __ensureDefaults(this, declarations); 
     39        __ensureOwnPrototypeMemory(this, declarations); 
     40      }; 
     41 
     42      // every class extends another class or Object 
     43      var base = declarations.extending || {}; 
     44 
     45      // baseInstance is the prototype *of* our prototype 
     46      var baseInstance = __inherit(ownPrototype, base); 
     47      declarations['getBaseInstance'] = Class.encap(baseInstance); 
     48      declarations['sup'] = __makeSup(); 
     49       
     50      __markCriticalFunctions(declarations); 
     51      __setupAttachDelete(declarations); 
     52      __inherit(ctor, ownPrototype); 
    553    } 
    6   } 
    7 } 
    854 
     55    // Returns a constructor identical to the original, except that the 
     56    // specified object will become a part of the prototype chain of any 
     57    // instantitated objects.  The inheritance chain of the extending 
     58    // class has precedence over the prototype chain of obj, the extended 
     59    // object, so mimicking other Class.created objects will break those 
     60    // objects' existing inheritance relationships.  Time taken is linear 
     61    // in the number of properties of the extending class. 
     62    ctor['mimic'] = function(obj) { 
     63      // Unfortunately it seems that you just can't use 
     64      // DOM objects as prototypes for constructor functions 
     65      // in Safari... how braindead is that?  Ugh :( 
     66      var __ctor = this; 
     67      var fn = function(/*arguments*/) { 
     68        __ctor.apply(this, arguments); 
     69      }; 
     70      Object.extend(obj, __ctor.prototype); 
     71      __ensureDefaults(obj, __ctor.prototype); 
     72      fn.prototype = obj; 
     73      return fn; 
     74    }; 
     75 
     76    return ctor; 
     77  }; 
     78 
     79  // superclass can be constructor function or any existing object 
     80  var __inherit = function(subclass, superclass) { 
     81    if (typeof(superclass) == 'function') { 
     82      superclass.getInstance = superclass.getInstance || 
     83        Class.encap(new superclass(__doNotInit)); 
     84      subclass.prototype = superclass.getInstance(); 
     85    } else if (typeof(superclass) == 'object') { 
     86      subclass.prototype = superclass; 
     87      superclass.initialize = superclass.constructor; 
     88    } 
     89    return subclass.prototype; 
     90  }; 
     91   
     92  // Functions marked here will be denied this.sup and privacy benefits, 
     93  // but will be slightly faster because they will be called directly. 
     94  var __markCriticalFunctions = function(declarations) { 
     95    var critical = declarations.critical; 
     96    if (typeof(critical) == 'object') { 
     97      for (var i in critical) { 
     98        critical[i]._critical = true; 
     99        declarations[i] = critical[i]; 
     100      } 
     101    } 
     102  }; 
     103 
     104  var __setupAttachDelete = function(declarations) { 
     105    // these two methods allow runtime method attachment and deletion, 
     106    // with all the benefits of this.sup and privacy restrictions 
     107    declarations['attachMethod'] = function(fnName, func, 
     108                                            /*optional*/ critical) { 
     109      // can't replace existing methods 
     110      if (!this.ownPrototypeHas(fnName)) { 
     111        func._critical = !!critical; 
     112        this._runTimeDeclarations[fnName] = true; 
     113        __attachClassProperty(fnName, func, this); 
     114      } 
     115    }; 
     116    declarations['deleteMethod'] = function(fnName) { 
     117      // can only delete non-runtime additions 
     118      if (this._runTimeDeclarations[fnName]) { 
     119        __attachClassProperty(fnName, null, this, true); 
     120        this._runTimeDeclarations[fnName] = false; 
     121      } 
     122    }; 
     123  }; 
     124   
     125  var __attachClassProperty = function(propName, prop, obj, 
     126                                       /*optional*/ removeInstead) { 
     127    if (removeInstead) { 
     128      if (obj.ownPrototypeHas(propName)) delete obj[propName]; 
     129    } else { 
     130      obj[propName] = prop; 
     131      if (typeof(prop) == 'function') { 
     132        if (!prop._critical && typeof(prop.body) != 'function') 
     133          __addStackLogic(obj, propName); 
     134        var __origStr = prop.toString(); // never changes 
     135        prop.toString = obj[propName].toString = function() { 
     136          return __origStr.replace(/^\s*function/, propName); 
     137        }; 
     138      } 
     139    } 
     140  }; 
     141 
     142  // special-purpose array object (slightly faster in tests) 
     143  var __callStack = new (function() { 
     144    var stack = []; var index = 0; 
     145    this.push  = function(what) { stack[index++] = what; } 
     146    this.pop   = function()     { return stack[--index]; } 
     147    this.empty = function()     { return index == 0; } 
     148    this.peek  = function()     { return stack[index - 1]; } 
     149  })(); 
     150   
     151  var __lastOnCallStack = function() { 
     152    if (__callStack.empty()) return ['', null]; 
     153    else return __callStack.peek(); 
     154  }; 
     155     
     156  var __addStackLogic = function(obj, fnName) { 
     157    switch (fnName) { 
     158      case 'sup': case 'valueOf': 
     159      case 'attachMethod': case 'deleteMethod': 
     160      case 'getBaseInstance': return; 
     161      default: { 
     162        var __func = obj[fnName]; 
     163        obj[fnName] = function() { 
     164          // this privacy check can be omitted for slight performance 
     165          // gains in production code (privacy is a development tool) 
     166          var lastRef = __refOfLastCallingObject(); 
     167          if (Class.followsPrivateNamingConvention(fnName) && 
     168              (!lastRef || lastRef.constructor !== this.constructor)) { 
     169            Class.privateCallError(fnName); 
     170          } else { 
     171            __callStack.push([fnName, this]); 
     172            try { // don't let exceptions corrupt the callstack 
     173              var result = __func.apply(this, arguments); 
     174            } catch (exception) { 
     175              throw exception; 
     176            } finally { 
     177              __callStack.pop(); 
     178            } 
     179            return result; 
     180          } 
     181        }; 
     182      } 
     183      obj[fnName].body = __func; // preserve original 
     184      // e.g. obj[fnName].body.call(obj, arg1, ...) 
     185    } 
     186  }; 
     187     
     188  Function.prototype.grant = function(obj) { 
     189    var __func = this; 
     190    var __callStackMemory = __lastOnCallStack(); 
     191    return function() { 
     192      __callStack.push(__callStackMemory); 
     193      try { // don't let exceptions corrupt the callstack 
     194        var result = __func.apply(obj || this, arguments); 
     195      } catch (exception) { 
     196        throw exception; 
     197      } finally { 
     198        __callStack.pop(); 
     199      } 
     200      return result; 
     201    } 
     202  }; 
     203 
     204  // consider exposing these (they expose nothing but useful info) 
     205  var __nameOfLastCalledMethod = function() { 
     206    return __lastOnCallStack()[0]; 
     207  }; 
     208 
     209  var __refOfLastCallingObject = function() { 
     210    return __lastOnCallStack()[1]; 
     211  }; 
     212 
     213  // change this at will 
     214  this.followsPrivateNamingConvention = function(propName) { 
     215    return propName.charAt(0) == '_'; 
     216  }; 
     217   
     218  this.privateCallError = function(fnName) { 
     219    alert("Error: Tried to access private member " + fnName + 
     220          " from from non-member method " + 
     221          __nameOfLastCalledMethod()); 
     222  }; 
     223 
     224  var __ensureDefaults = function(ownPrototype, declarations) { 
     225    ['toString', 'toLocaleString', 'valueOf'].each(function(prop) { 
     226      __attachClassProperty(prop, declarations[prop], ownPrototype); 
     227    }); 
     228  }; 
     229 
     230  var __ensureOwnPrototypeMemory = function(ownPrototype, declarations) { 
     231    ownPrototype.ownPrototypeHas = function(property) { 
     232      return (typeof(declarations[property]) != 'undefined' || 
     233              (this._runTimeDeclarations && // present if instantiated 
     234               this._runTimeDeclarations[property])); 
     235    }; 
     236  }; 
     237 
     238  // convenient mechanism for creating accessor functions 
     239  this.encap = function(__val, mutable) { 
     240    if (!mutable) return function() { return __val; } 
     241    else return function(/* optional new value */) { 
     242      if (arguments.length > 0) __val = arguments[0]; 
     243      return __val; 
     244    } 
     245  }; 
     246   
     247  var __makeSup = function() { 
     248    var __platform = false; // where to start lookup 
     249     
     250    return function() { 
     251      var method = __nameOfLastCalledMethod(); 
     252      var curObj = __platform || this; 
     253 
     254      // walk up inheritance chain until we've found the first class that 
     255      // actually implements the method from which this.sup was called 
     256      while ( curObj.ownPrototypeHas && 
     257             !curObj.ownPrototypeHas(method) && 
     258              curObj.getBaseInstance) 
     259        curObj = curObj.getBaseInstance(); 
     260      curObj = curObj.getBaseInstance 
     261        ? curObj.getBaseInstance() 
     262        : {}; // the ultimate baseInstance 
     263 
     264      var prevBase = __platform; 
     265      __platform = curObj; 
     266      try { // don't let exceptions break the lookup 
     267        var result = curObj[method].apply(this, arguments); 
     268      } catch (exception) { 
     269        throw exception; 
     270      } finally { 
     271        __platform = prevBase; 
     272      } 
     273      return result; 
     274    }; 
     275  }; 
     276 
     277})(); // good place to pass in parameters that determine what features are 
     278      // enabled (production, development, debug, etc.) 
     279 
     280var createClass = Class.create; 
     281 
    9282var Abstract = new Object(); 
    10283 
    11284Object.extend = function(destination, source) {