amber/js/boot.js in resin-0.2.1 vs amber/js/boot.js in resin-0.2.2

- old
+ new

@@ -43,26 +43,25 @@ debug: function() {}, error: function() {} }; } - /* Smalltalk constructors definition */ function SmalltalkObject(){} -function SmalltalkBehavior(){}; +function SmalltalkBehavior(){} function SmalltalkClass(){} -function SmalltalkPackage(){}; +function SmalltalkPackage(){} function SmalltalkMetaclass(){ this.meta = true; -}; -function SmalltalkMethod(){}; -function SmalltalkNil(){}; +} +function SmalltalkMethod(){} +function SmalltalkNil(){} function SmalltalkSymbol(string){ this.value = string; -}; +} function Smalltalk(){ var st = this; @@ -105,12 +104,12 @@ st.packages = {}; /* Smalltalk package creation. To add a Package, use smalltalk.addPackage() */ function pkg(spec) { - var that = new SmalltalkPackage(); - that.pkgName = spec.pkgName; + var that = new SmalltalkPackage(); + that.pkgName = spec.pkgName; that.properties = spec.properties || {}; return that; }; /* Smalltalk class creation. A class is an instance of an automatically @@ -118,32 +117,40 @@ should be added to the smalltalk object, see smalltalk.addClass(). Superclass linking is *not* handled here, see smalltalk.init() */ function klass(spec) { var spec = spec || {}; - var that; - if(spec.meta) { - that = new SmalltalkMetaclass(); - } else { - that = new (klass({meta: true})).fn; - that.klass.instanceClass = that; - that.className = spec.className; - that.klass.className = that.className + ' class'; + var meta = metaclass(); + var that = setupClass(meta.instanceClass, spec); + that.className = spec.className; + meta.className = spec.className + ' class'; + if(spec.superclass) { + that.superclass = spec.superclass; + meta.superclass = spec.superclass.klass; } - + return that; + } + + function metaclass() { + var meta = setupClass(new SmalltalkMetaclass(), {}); + meta.instanceClass = new meta.fn; + return meta; + } + + function setupClass(that, spec) { that.fn = spec.fn || function(){}; - that.superclass = spec.superclass; that.iVarNames = spec.iVarNames || []; - that.toString = function() {return 'Smalltalk ' + that.className}; - if(that.superclass) { - that.klass.superclass = that.superclass.klass; - } + Object.defineProperty(that, "toString", { + value: function() { return 'Smalltalk ' + this.className; }, + configurable: true // no writable - in par with ES6 methods + }); that.pkg = spec.pkg; - that.fn.prototype.methods = {}; - that.fn.prototype.inheritedMethods = {}; - that.fn.prototype.klass = that; - + Object.defineProperties(that.fn.prototype, { + methods: { value: {}, enumerable: false, configurable: true, writable: true }, + inheritedMethods: { value: {}, enumerable: false, configurable: true, writable: true }, + klass: { value: that, enumerable: false, configurable: true, writable: true } + }); return that; }; /* Smalltalk method object. To add a method to a class, use smalltalk.addMethod() */ @@ -156,40 +163,48 @@ that.category = spec.category; that.source = spec.source; that.messageSends = spec.messageSends || []; that.referencedClasses = spec.referencedClasses || []; that.fn = spec.fn; - return that + return that; }; /* Initialize a class in its class hierarchy. Handle both class and metaclasses. */ - + st.init = function(klass) { + st.initSubTree(klass); + if(klass.klass && !klass.meta) { + st.initSubTree(klass.klass); + } + }; + + st.initSubTree = function(klass) { var subclasses = st.subclasses(klass); - var methods; + var methods, proto = klass.fn.prototype; if(klass.superclass && klass.superclass !== nil) { methods = st.methods(klass.superclass); //Methods linking - for(var i in methods) { - if(!klass.fn.prototype.methods[i]) { - klass.fn.prototype.inheritedMethods[i] = methods[i]; - klass.fn.prototype[methods[i].jsSelector] = methods[i].fn; + for(var keys=Object.keys(methods),i=0,l=keys.length; i<l; ++i) { + var k = keys[i] + if(!proto.methods[k]) { + proto.inheritedMethods[k] = methods[k]; + Object.defineProperty(proto, methods[k].jsSelector, { + value: methods[k].fn, configurable: true // no writable - in par with ES6 methods + }); } } } for(var i=0;i<subclasses.length;i++) { - st.init(subclasses[i]); + st.initSubTree(subclasses[i]); } - if(klass.klass && !klass.meta) { - st.init(klass.klass); - } }; + /* Answer all registered Packages as Array */ st.packages.all = function() { var packages = []; for(var i in st.packages) { @@ -200,47 +215,54 @@ }; /* Answer all registered Smalltalk classes */ st.classes = function() { - var classes = []; - for(var i in st) { - if(i.search(/^[A-Z]/g) != -1) { - classes.push(st[i]); + var classes = [], names = Object.keys(st), l = names.length; + for (var i=0; i<l; ++i) { + var name = names[i]; + if (name.search(/^[A-Z]/) !== -1) { + classes.push(st[name]); } } - return classes + return classes; }; + /* Answer all methods (included inherited ones) of klass. */ st.methods = function(klass) { var methods = {}; - for(var i in klass.fn.prototype.methods) { - methods[i] = klass.fn.prototype.methods[i] + var copyFrom = klass.fn.prototype.methods; + for(var i=0, k=Object.keys(copyFrom), l=k.length; i<l; ++i) { + methods[k[i]] = copyFrom[k[i]]; } - for(var i in klass.fn.prototype.inheritedMethods) { - methods[i] = klass.fn.prototype.inheritedMethods[i] + copyFrom = klass.fn.prototype.inheritedMethods; + for(var i=0, k=Object.keys(copyFrom), l=k.length; i<l; ++i) { + methods[k[i]] = copyFrom[k[i]]; } return methods; - } + }; + /* Answer the direct subclasses of klass. */ st.subclasses = function(klass) { var subclasses = []; var classes = st.classes(); - for(var i in classes) { - if(classes[i].fn) { - //Metaclasses - if(classes[i].klass && classes[i].klass.superclass === klass) { - subclasses.push(classes[i].klass); - } + for(var i=0, l=classes.length; i<l; ++i) { + var c = classes[i] + if(c.fn) { //Classes - if(classes[i].superclass === klass) { - subclasses.push(classes[i]); + if(c.superclass === klass) { + subclasses.push(c); } + c = c.klass; + //Metaclasses + if(c && c.superclass === klass) { + subclasses.push(c); + } } } return subclasses; }; @@ -255,14 +277,14 @@ pkg: pkg, fn: fn }); }; - /* Create an alias for an existing class */ - st.alias = function(klass, alias) { - st[alias] = klass; - } + /* Create an alias for an existing class */ + st.alias = function(klass, alias) { + st[alias] = klass; + } /* Add a package to the smalltalk.packages object, creating a new one if needed. If pkgName is null or empty we return nil, which is an allowed package for a class. If package already exists we still update the properties of it. */ @@ -288,11 +310,11 @@ var pkg = st.addPackage(pkgName); if(st[className]) { st[className].superclass = superclass; st[className].iVarNames = iVarNames; st[className].pkg = pkg || st[className].pkg; - } else { + } else { st[className] = klass({ className: className, superclass: superclass, pkg: pkg, iVarNames: iVarNames @@ -301,35 +323,21 @@ }; /* Add a method to a class */ st.addMethod = function(jsSelector, method, klass) { - klass.fn.prototype[jsSelector] = method.fn; + Object.defineProperty(klass.fn.prototype, jsSelector, { + value: method.fn, configurable: true // not writable - in par with ES6 methods + }); klass.fn.prototype.methods[method.selector] = method; method.methodClass = klass; method.jsSelector = jsSelector; }; - /* Handles Smalltalk message send. Automatically converts undefined to the nil object. - If the receiver does not understand the selector, call its #doesNotUnderstand: method */ - - sendWithoutContext = function(receiver, selector, args, klass) { - if(receiver === undefined || receiver === null) { - receiver = nil; - } - if(!klass && receiver.klass && receiver[selector]) { - return receiver[selector].apply(receiver, args); - } else if(klass && klass.fn.prototype[selector]) { - return klass.fn.prototype[selector].apply(receiver, args) - } - return messageNotUnderstood(receiver, selector, args); - }; - - /* Handles unhandled errors during message sends */ - sendWithContext = function(receiver, selector, args, klass) { + st.send = function(receiver, selector, args, klass) { if(st.thisContext) { return withContextSend(receiver, selector, args, klass); } else { try {return withContextSend(receiver, selector, args, klass)} catch(error) { @@ -342,38 +350,33 @@ } } } }; - /* Same as sendWithoutContext but creates a methodContext. */ - - withContextSend = function(receiver, selector, args, klass) { - var call, context; - if(receiver === undefined || receiver === null) { + function withContextSend(receiver, selector, args, klass) { + var call, imp; + if(receiver == null) { receiver = nil; } - if(!klass && receiver.klass && receiver[selector]) { - context = pushContext(receiver, selector, args); - call = receiver[selector].apply(receiver, args); + imp = klass ? klass.fn.prototype[selector] : receiver.klass && receiver[selector]; + if(imp) { + var context = pushContext(receiver, selector, args); + call = imp.apply(receiver, args); popContext(context); return call; - } else if(klass && klass.fn.prototype[selector]) { - context = pushContext(receiver, selector, args); - call = klass.fn.prototype[selector].apply(receiver, args); - popContext(context); - return call; + } else { + return messageNotUnderstood(receiver, selector, args); } - return messageNotUnderstood(receiver, selector, args); }; /* Handles Smalltalk errors. Triggers the registered ErrorHandler (See the Smalltalk class ErrorHandler and its subclasses */ function handleError(error) { st.thisContext = undefined; smalltalk.ErrorHandler._current()._handleError_(error); - } + }; /* Handles #dnu: *and* JavaScript method calls. if the receiver has no klass, we consider it a JS object (outside of the Amber system). Else assume that the receiver understands #doesNotUnderstand: */ @@ -422,37 +425,42 @@ return st.send(st.JSObjectProxy._on_(receiver), selector, args); }; - /* Reuse old contexts stored in oldContexts */ + /* Reuse one old context stored in oldContext */ - st.oldContexts = []; + st.oldContext = null; /* Handle thisContext pseudo variable */ st.getThisContext = function() { if(st.thisContext) { return st.thisContext.copy(); - } else { + }/* else { // this is the default return undefined; - } - } + }*/ + }; - pushContext = function(receiver, selector, temps) { - if(st.thisContext) { - return st.thisContext = st.thisContext.newContext(receiver, selector, temps); - } else { - return st.thisContext = new SmalltalkMethodContext(receiver, selector, temps); + function pushContext(receiver, selector, temps) { + var c = st.oldContext, tc = st.thisContext; + if (!c) { + return st.thisContext = new SmalltalkMethodContext(receiver, selector, temps, tc); } + st.oldContext = null; + c.homeContext = tc; + c.receiver = receiver; + c.selector = selector; + c.temps = temps || {}; + return st.thisContext = c; }; - popContext = function(context) { - if(context) { - context.removeYourself(); - } + function popContext(context) { + st.thisContext = context.homeContext; + context.homeContext = undefined; + st.oldContext = context; }; /* Convert a string to a valid smalltalk selector. if you modify the following functions, also change String>>asSelector accordingly */ @@ -500,60 +508,28 @@ object[i] = st.readJSObject(js[i]); } } return object; }; +}; - /* Toggle deployment mode (no context will be handled during message send */ - st.setDeploymentMode = function() { - st.send = sendWithoutContext; - }; - - st.setDevelopmentMode = function() { - st.send = sendWithContext; - } - - /* Set development mode by default */ - st.setDevelopmentMode(); -} - function SmalltalkMethodContext(receiver, selector, temps, home) { - var that = this; - that.receiver = receiver; - that.selector = selector; - that.temps = temps || {}; - that.homeContext = home; + this.receiver = receiver; + this.selector = selector; + this.temps = temps || {}; + this.homeContext = home; +}; - that.copy = function() { - var home = that.homeContext; - if(home) {home = home.copy()} - return new SmalltalkMethodContext( - that.receiver, - that.selector, - that.temps, - home - ); - } - - that.newContext = function(receiver, selector, temps) { - var c = smalltalk.oldContexts.pop(); - if(c) { - c.homeContext = that; - c.receiver = receiver; - c.selector = selector; - c.temps = temps || {}; - } else { - c = new SmalltalkMethodContext(receiver, selector, temps, that); - } - return c; - } - - that.removeYourself = function() { - smalltalk.thisContext = that.homeContext; - that.homeContext = undefined; - smalltalk.oldContexts.push(that); - } -} +SmalltalkMethodContext.prototype.copy = function() { + var home = this.homeContext; + if(home) {home = home.copy()} + return new SmalltalkMethodContext( + this.receiver, + this.selector, + this.temps, + home + ); +}; /* Global Smalltalk objects. */ var nil = new SmalltalkNil(); var smalltalk = new Smalltalk();