|  | @@ -92,6 +92,7 @@ function inherits(child, parent) {
 | 
	
		
			
				|  |  |  		constructor: { value: child,
 | 
	
		
			
				|  |  |  			enumerable: false, configurable: true, writable: true }
 | 
	
		
			
				|  |  |  	});
 | 
	
		
			
				|  |  | +	return child;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Smalltalk foundational objects */
 | 
	
	
		
			
				|  | @@ -169,6 +170,7 @@ function DNUBrik(brikz, st) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	this.methods = [];
 | 
	
		
			
				|  |  |  	this.selectors = [];
 | 
	
		
			
				|  |  | +	this.checker = Object.create(null);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	this.get = function (string) {
 | 
	
		
			
				|  |  |  		var index = this.selectors.indexOf(string);
 | 
	
	
		
			
				|  | @@ -177,11 +179,16 @@ function DNUBrik(brikz, st) {
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  		this.selectors.push(string);
 | 
	
		
			
				|  |  |  		var selector = st.selector(string);
 | 
	
		
			
				|  |  | +		this.checker[selector] = true;
 | 
	
		
			
				|  |  |  		var method = {jsSelector: selector, fn: this.createHandler(selector)};
 | 
	
		
			
				|  |  |  		this.methods.push(method);
 | 
	
		
			
				|  |  |  		return method;
 | 
	
		
			
				|  |  |  	};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	this.isSelector = function (selector) {
 | 
	
		
			
				|  |  | +		return this.checker[selector];
 | 
	
		
			
				|  |  | +	};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/* Dnu handler method */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	this.createHandler = function (selector) {
 | 
	
	
		
			
				|  | @@ -219,10 +226,7 @@ function ClassInitBrik(brikz, st) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	st.initClass = function(klass) {
 | 
	
		
			
				|  |  |  		if(klass.wrapped) {
 | 
	
		
			
				|  |  | -			klass.inheritedMethods = {};
 | 
	
		
			
				|  |  |  			copySuperclass(klass);
 | 
	
		
			
				|  |  | -		} else {
 | 
	
		
			
				|  |  | -			installSuperclass(klass);
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		if(klass === st.Object || klass.wrapped) {
 | 
	
	
		
			
				|  | @@ -230,18 +234,9 @@ function ClassInitBrik(brikz, st) {
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	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);
 | 
	
		
			
				|  |  | -			manip.wireKlass(klass);
 | 
	
		
			
				|  |  | -			manip.reinstallMethods(klass);
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  	function copySuperclass(klass, superclass) {
 | 
	
		
			
				|  |  | +		var inheritedMethods = {};
 | 
	
		
			
				|  |  | +		deinstallAllMethods(klass);
 | 
	
		
			
				|  |  |  		for (superclass = superclass || klass.superclass;
 | 
	
		
			
				|  |  |  			 superclass && superclass !== nil;
 | 
	
		
			
				|  |  |  			 superclass = superclass.superclass) {
 | 
	
	
		
			
				|  | @@ -249,17 +244,31 @@ function ClassInitBrik(brikz, st) {
 | 
	
		
			
				|  |  |  				inheritMethodIfAbsent(superclass.methods[keys[i]], klass);
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +		manip.reinstallMethods(klass);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	function inheritMethodIfAbsent(method, klass) {
 | 
	
		
			
				|  |  | -		var selector = method.selector;
 | 
	
		
			
				|  |  | +		function inheritMethodIfAbsent(method) {
 | 
	
		
			
				|  |  | +			var selector = method.selector;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		if(klass.methods.hasOwnProperty(selector) || klass.inheritedMethods.hasOwnProperty(selector)) {
 | 
	
		
			
				|  |  | -			return;
 | 
	
		
			
				|  |  | +			//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;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			manip.installMethod(method, klass);
 | 
	
		
			
				|  |  | +			inheritedMethods[method.selector] = true;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		manip.installMethod(method, klass);
 | 
	
		
			
				|  |  | -		klass.inheritedMethods[method.selector] = true;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	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;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -351,7 +360,7 @@ function ClassesBrik(brikz, st) {
 | 
	
		
			
				|  |  |  		spec = spec || {};
 | 
	
		
			
				|  |  |  		var meta = metaclass(spec);
 | 
	
		
			
				|  |  |  		var that = meta.instanceClass;
 | 
	
		
			
				|  |  | -		that.fn = spec.fn || function() {};
 | 
	
		
			
				|  |  | +		that.fn = spec.fn || inherits(function () {}, spec.superclass.fn);
 | 
	
		
			
				|  |  |  		setupClass(that, spec);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		that.className = spec.className;
 | 
	
	
		
			
				|  | @@ -367,10 +376,7 @@ function ClassesBrik(brikz, st) {
 | 
	
		
			
				|  |  |  	function metaclass(spec) {
 | 
	
		
			
				|  |  |  		spec = spec || {};
 | 
	
		
			
				|  |  |  		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();
 | 
	
		
			
				|  |  |  		setupClass(that);
 | 
	
		
			
				|  |  |  		return that;
 | 
	
	
		
			
				|  | @@ -595,15 +601,7 @@ function MethodsBrik(brikz, st) {
 | 
	
		
			
				|  |  |  		// Therefore we populate the organizer here too
 | 
	
		
			
				|  |  |  		org.addOrganizationElement(klass, 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(stInit.initialized()) {
 | 
	
		
			
				|  |  | -			st.allSubclasses(klass).forEach(function(subclass) {
 | 
	
		
			
				|  |  | -				st.initClass(subclass);
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +		propagateMethodChange(klass);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		for(var i=0; i<method.messageSends.length; i++) {
 | 
	
		
			
				|  |  |  			var dnuHandler = dnu.get(method.messageSends[i]);
 | 
	
	
		
			
				|  | @@ -613,18 +611,35 @@ function MethodsBrik(brikz, st) {
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	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 (stInit.initialized()) {
 | 
	
		
			
				|  |  | +			st.allSubclasses(klass).forEach(function (subclass) {
 | 
	
		
			
				|  |  | +				st.initClass(subclass);
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	st.removeMethod = function(method, klass) {
 | 
	
		
			
				|  |  |  		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.methods[method.selector];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +		st.initClass(klass);
 | 
	
		
			
				|  |  | +		propagateMethodChange(klass);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		// Do *not* delete protocols from here.
 | 
	
		
			
				|  |  |  		// This is handled by #removeCompiledMethod
 | 
	
		
			
				|  |  |  	};
 |