|  | @@ -100,6 +100,7 @@ function inherits(child, parent) {
 | 
											
												
													
														|  |  		constructor: { value: child,
 |  |  		constructor: { value: child,
 | 
											
												
													
														|  |  			enumerable: false, configurable: true, writable: true }
 |  |  			enumerable: false, configurable: true, writable: true }
 | 
											
												
													
														|  |  	});
 |  |  	});
 | 
											
												
													
														|  | 
 |  | +	return child;
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  inherits(SmalltalkBehavior, SmalltalkObject);
 |  |  inherits(SmalltalkBehavior, SmalltalkObject);
 | 
											
										
											
												
													
														|  | @@ -156,6 +157,7 @@ function Smalltalk() {
 | 
											
												
													
														|  |  	var dnu = {
 |  |  	var dnu = {
 | 
											
												
													
														|  |  		methods: [],
 |  |  		methods: [],
 | 
											
												
													
														|  |  		selectors: [],
 |  |  		selectors: [],
 | 
											
												
													
														|  | 
 |  | +		checker: Object.create(null),
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		get: function (string) {
 |  |  		get: function (string) {
 | 
											
												
													
														|  |  			var index = this.selectors.indexOf(string);
 |  |  			var index = this.selectors.indexOf(string);
 | 
											
										
											
												
													
														|  | @@ -164,11 +166,16 @@ function Smalltalk() {
 | 
											
												
													
														|  |  			}
 |  |  			}
 | 
											
												
													
														|  |  			this.selectors.push(string);
 |  |  			this.selectors.push(string);
 | 
											
												
													
														|  |  			var selector = st.selector(string);
 |  |  			var selector = st.selector(string);
 | 
											
												
													
														|  | 
 |  | +			this.checker[selector] = true;
 | 
											
												
													
														|  |  			var method = {jsSelector: selector, fn: this.createHandler(selector)};
 |  |  			var method = {jsSelector: selector, fn: this.createHandler(selector)};
 | 
											
												
													
														|  |  			this.methods.push(method);
 |  |  			this.methods.push(method);
 | 
											
												
													
														|  |  			return method;
 |  |  			return method;
 | 
											
												
													
														|  |  		},
 |  |  		},
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +		isSelector: function (selector) {
 | 
											
												
													
														|  | 
 |  | +			return this.checker[selector];
 | 
											
												
													
														|  | 
 |  | +		},
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  		/* Dnu handler method */
 |  |  		/* Dnu handler method */
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		createHandler: function (selector) {
 |  |  		createHandler: function (selector) {
 | 
											
										
											
												
													
														|  | @@ -218,7 +225,7 @@ function Smalltalk() {
 | 
											
												
													
														|  |  		spec = spec || {};
 |  |  		spec = spec || {};
 | 
											
												
													
														|  |  		var meta = metaclass(spec);
 |  |  		var meta = metaclass(spec);
 | 
											
												
													
														|  |  		var that = meta.instanceClass;
 |  |  		var that = meta.instanceClass;
 | 
											
												
													
														|  | -		that.fn = spec.fn || function() {};
 |  | 
 | 
											
												
													
														|  | 
 |  | +		that.fn = spec.fn || inherits(function () {}, spec.superclass.fn);
 | 
											
												
													
														|  |  		setupClass(that, spec);
 |  |  		setupClass(that, spec);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		that.className = spec.className;
 |  |  		that.className = spec.className;
 | 
											
										
											
												
													
														|  | @@ -234,10 +241,7 @@ function Smalltalk() {
 | 
											
												
													
														|  |  	function metaclass(spec) {
 |  |  	function metaclass(spec) {
 | 
											
												
													
														|  |  		spec = spec || {};
 |  |  		spec = spec || {};
 | 
											
												
													
														|  |  		var that = new SmalltalkMetaclass();
 |  |  		var that = new SmalltalkMetaclass();
 | 
											
												
													
														|  | -		inherits(
 |  | 
 | 
											
												
													
														|  | -			that.fn = function() {},
 |  | 
 | 
											
												
													
														|  | -			spec.superclass ? spec.superclass.klass.fn : SmalltalkClass
 |  | 
 | 
											
												
													
														|  | -		);
 |  | 
 | 
											
												
													
														|  | 
 |  | +		that.fn = inherits(function () {}, spec.superclass ? spec.superclass.klass.fn : SmalltalkClass);
 | 
											
												
													
														|  |  		that.instanceClass = new that.fn();
 |  |  		that.instanceClass = new that.fn();
 | 
											
												
													
														|  |  		setupClass(that);
 |  |  		setupClass(that);
 | 
											
												
													
														|  |  		return that;
 |  |  		return that;
 | 
											
										
											
												
													
														|  | @@ -291,10 +295,7 @@ function Smalltalk() {
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	st.initClass = function(klass) {
 |  |  	st.initClass = function(klass) {
 | 
											
												
													
														|  |  		if(klass.wrapped) {
 |  |  		if(klass.wrapped) {
 | 
											
												
													
														|  | -			klass.inheritedMethods = {};
 |  | 
 | 
											
												
													
														|  |  			copySuperclass(klass);
 |  |  			copySuperclass(klass);
 | 
											
												
													
														|  | -		} else {
 |  | 
 | 
											
												
													
														|  | -			installSuperclass(klass);
 |  | 
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		if(klass === st.Object || klass.wrapped) {
 |  |  		if(klass === st.Object || klass.wrapped) {
 | 
											
										
											
												
													
														|  | @@ -309,25 +310,31 @@ function Smalltalk() {
 | 
											
												
													
														|  |  		});
 |  |  		});
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	function installSuperclass(klass) {
 |  | 
 | 
											
												
													
														|  | -		// only if the klass has not been initialized yet.
 |  | 
 | 
											
												
													
														|  | -		if(klass.fn.prototype._yourself) { return; }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -		if(klass.superclass && klass.superclass !== nil) {
 |  | 
 | 
											
												
													
														|  | -			inherits(klass.fn, klass.superclass.fn);
 |  | 
 | 
											
												
													
														|  | -			wireKlass(klass);
 |  | 
 | 
											
												
													
														|  | -			reinstallMethods(klass);
 |  | 
 | 
											
												
													
														|  | -		}
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |  	function copySuperclass(klass, superclass) {
 |  |  	function copySuperclass(klass, superclass) {
 | 
											
												
													
														|  | 
 |  | +		var inheritedMethods = {};
 | 
											
												
													
														|  | 
 |  | +		deinstallAllMethods(klass);
 | 
											
												
													
														|  |  		for (superclass = superclass || klass.superclass;
 |  |  		for (superclass = superclass || klass.superclass;
 | 
											
												
													
														|  |  			superclass && superclass !== nil;
 |  |  			superclass && superclass !== nil;
 | 
											
												
													
														|  |  			superclass = superclass.superclass) {
 |  |  			superclass = superclass.superclass) {
 | 
											
												
													
														|  |  			for (var keys = Object.keys(superclass.methods), i = 0; i < keys.length; i++) {
 |  |  			for (var keys = Object.keys(superclass.methods), i = 0; i < keys.length; i++) {
 | 
											
												
													
														|  | -				inheritMethodIfAbsent(superclass.methods[keys[i]], klass);
 |  | 
 | 
											
												
													
														|  | 
 |  | +				inheritMethodIfAbsent(superclass.methods[keys[i]]);
 | 
											
												
													
														|  |  			}
 |  |  			}
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  | 
 |  | +		reinstallMethods(klass);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		function inheritMethodIfAbsent(method) {
 | 
											
												
													
														|  | 
 |  | +			var selector = method.selector;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +			//TODO: prepare klass methods into inheritedMethods to only test once
 | 
											
												
													
														|  | 
 |  | +			//TODO: Object.create(null) to ditch hasOwnProperty call (very slow)
 | 
											
												
													
														|  | 
 |  | +			if(klass.methods.hasOwnProperty(selector) || inheritedMethods.hasOwnProperty(selector)) {
 | 
											
												
													
														|  | 
 |  | +				return;
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +			installMethod(method, klass);
 | 
											
												
													
														|  | 
 |  | +			inheritedMethods[method.selector] = true;
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	function installMethod(method, klass) {
 |  |  	function installMethod(method, klass) {
 | 
											
										
											
												
													
														|  | @@ -337,15 +344,14 @@ function Smalltalk() {
 | 
											
												
													
														|  |  		});
 |  |  		});
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	function inheritMethodIfAbsent(method, klass) {
 |  | 
 | 
											
												
													
														|  | -		var selector = method.selector;
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -		if(klass.methods.hasOwnProperty(selector) || klass.inheritedMethods.hasOwnProperty(selector)) {
 |  | 
 | 
											
												
													
														|  | -			return;
 |  | 
 | 
											
												
													
														|  | 
 |  | +	function deinstallAllMethods(klass) {
 | 
											
												
													
														|  | 
 |  | +		var proto = klass.fn.prototype;
 | 
											
												
													
														|  | 
 |  | +		for(var keys = Object.getOwnPropertyNames(proto), i=0; i<keys.length; i++) {
 | 
											
												
													
														|  | 
 |  | +			var key = keys[i];
 | 
											
												
													
														|  | 
 |  | +			if (dnu.isSelector(key)) {
 | 
											
												
													
														|  | 
 |  | +				proto[key] = null;
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -		installMethod(method, klass);
 |  | 
 | 
											
												
													
														|  | -		klass.inheritedMethods[method.selector] = true;
 |  | 
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	function reinstallMethods(klass) {
 |  |  	function reinstallMethods(klass) {
 | 
											
										
											
												
													
														|  | @@ -375,6 +381,20 @@ function Smalltalk() {
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +	function propagateMethodChange(klass) {
 | 
											
												
													
														|  | 
 |  | +		// If already initialized (else it will be done later anyway),
 | 
											
												
													
														|  | 
 |  | +		// re-initialize all subclasses to ensure the method change
 | 
											
												
													
														|  | 
 |  | +		// propagation (for wrapped classes, not using the prototype
 | 
											
												
													
														|  | 
 |  | +		// chain).
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		//TODO: optimize, only one method need to be updated, not all of them
 | 
											
												
													
														|  | 
 |  | +		if (initialized) {
 | 
											
												
													
														|  | 
 |  | +			st.allSubclasses(klass).forEach(function (subclass) {
 | 
											
												
													
														|  | 
 |  | +				st.initClass(subclass);
 | 
											
												
													
														|  | 
 |  | +			});
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  	/* Answer all registered Packages as Array */
 |  |  	/* Answer all registered Packages as Array */
 | 
											
												
													
														|  |  	// TODO: Remove this hack
 |  |  	// TODO: Remove this hack
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -541,15 +561,7 @@ function Smalltalk() {
 | 
											
												
													
														|  |  		// Therefore we populate the organizer here too
 |  |  		// Therefore we populate the organizer here too
 | 
											
												
													
														|  |  		klass.organization.elements.addElement(method.category);
 |  |  		klass.organization.elements.addElement(method.category);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -		// If already initialized (else it will be done later anyway),
 |  | 
 | 
											
												
													
														|  | -		// re-initialize all subclasses to ensure the new method
 |  | 
 | 
											
												
													
														|  | -		// propagation (for wrapped classes, not using the prototype
 |  | 
 | 
											
												
													
														|  | -		// chain.
 |  | 
 | 
											
												
													
														|  | -		if(initialized) {
 |  | 
 | 
											
												
													
														|  | -			st.allSubclasses(klass).forEach(function(subclass) {
 |  | 
 | 
											
												
													
														|  | -				st.initClass(subclass);
 |  | 
 | 
											
												
													
														|  | -			});
 |  | 
 | 
											
												
													
														|  | -		}
 |  | 
 | 
											
												
													
														|  | 
 |  | +		propagateMethodChange(klass);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		for(var i=0; i<method.messageSends.length; i++) {
 |  |  		for(var i=0; i<method.messageSends.length; i++) {
 | 
											
												
													
														|  |  			var dnuHandler = dnu.get(method.messageSends[i]);
 |  |  			var dnuHandler = dnu.get(method.messageSends[i]);
 | 
											
										
											
												
													
														|  | @@ -561,16 +573,19 @@ function Smalltalk() {
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	st.removeMethod = function(method, klass) {
 |  |  	st.removeMethod = function(method, klass) {
 | 
											
												
													
														|  |  		if (klass !== method.methodClass) {
 |  |  		if (klass !== method.methodClass) {
 | 
											
												
													
														|  | -            throw new Error(
 |  | 
 | 
											
												
													
														|  | -                "Refusing to remove method "
 |  | 
 | 
											
												
													
														|  | -                    + method.methodClass.className+">>"+method.selector
 |  | 
 | 
											
												
													
														|  | -                    + " from different class "
 |  | 
 | 
											
												
													
														|  | -                    + klass.className);
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | 
 |  | +			throw new Error(
 | 
											
												
													
														|  | 
 |  | +				"Refusing to remove method "
 | 
											
												
													
														|  | 
 |  | +					+ method.methodClass.className+">>"+method.selector
 | 
											
												
													
														|  | 
 |  | +					+ " from different class "
 | 
											
												
													
														|  | 
 |  | +					+ klass.className);
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		delete klass.fn.prototype[st.selector(method.selector)];
 |  |  		delete klass.fn.prototype[st.selector(method.selector)];
 | 
											
												
													
														|  |  		delete klass.methods[method.selector];
 |  |  		delete klass.methods[method.selector];
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +		st.initClass(klass);
 | 
											
												
													
														|  | 
 |  | +		propagateMethodChange(klass);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  		// Do *not* delete protocols from here.
 |  |  		// Do *not* delete protocols from here.
 | 
											
												
													
														|  |  		// This is handled by #removeCompiledMethod
 |  |  		// This is handled by #removeCompiledMethod
 | 
											
												
													
														|  |  	};
 |  |  	};
 |