Bladeren bron

Merge branch 'gh-544' of https://github.com/herby/amber into herby-gh-544

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
Nicolas Petton 10 jaren geleden
bovenliggende
commit
cddd612873
7 gewijzigde bestanden met toevoegingen van 106 en 68 verwijderingen
  1. 1 4
      js/Kernel-Classes.deploy.js
  2. 2 5
      js/Kernel-Classes.js
  3. 19 2
      js/Kernel-Tests.deploy.js
  4. 21 4
      js/Kernel-Tests.js
  5. 1 4
      st/Kernel-Classes.st
  6. 4 6
      st/Kernel-Tests.st
  7. 58 43
      support/boot.js

+ 1 - 4
js/Kernel-Classes.deploy.js

@@ -178,10 +178,7 @@ selector: "basicRemoveCompiledMethod:",
 fn: function (aMethod){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-
-		smalltalk.removeMethod(aMethod,self);
-		smalltalk.init(self);
-	;
+smalltalk.removeMethod(aMethod,self);
 return self}, function($ctx1) {$ctx1.fill(self,"basicRemoveCompiledMethod:",{aMethod:aMethod},smalltalk.Behavior)})},
 messageSends: []}),
 smalltalk.Behavior);

+ 2 - 5
js/Kernel-Classes.js

@@ -225,13 +225,10 @@ category: 'private',
 fn: function (aMethod){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-
-		smalltalk.removeMethod(aMethod,self);
-		smalltalk.init(self);
-	;
+smalltalk.removeMethod(aMethod,self);
 return self}, function($ctx1) {$ctx1.fill(self,"basicRemoveCompiledMethod:",{aMethod:aMethod},smalltalk.Behavior)})},
 args: ["aMethod"],
-source: "basicRemoveCompiledMethod: aMethod\x0a\x09<\x0a\x09\x09smalltalk.removeMethod(aMethod,self);\x0a\x09\x09smalltalk.init(self);\x0a\x09>",
+source: "basicRemoveCompiledMethod: aMethod\x0a\x09<smalltalk.removeMethod(aMethod,self)>",
 messageSends: [],
 referencedClasses: []
 }),

+ 19 - 2
js/Kernel-Tests.deploy.js

@@ -2601,8 +2601,17 @@ selector: "testMNU11",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+self["@performBlock"]=(function(x){
+return smalltalk.withContext(function($ctx2) {
+return _st(x)._foo();
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1)})});
+self._shouldMNU();
+self._installTop_("foo ^ false");
+self._installTop_("foo ^ true");
+self._deinstallTop();
+self._shouldMNU();
 return self}, function($ctx1) {$ctx1.fill(self,"testMNU11",{},smalltalk.MethodInheritanceTest)})},
-messageSends: []}),
+messageSends: ["foo", "shouldMNU", "installTop:", "deinstallTop"]}),
 smalltalk.MethodInheritanceTest);
 
 smalltalk.addMethod(
@@ -2630,8 +2639,16 @@ selector: "testReturns1",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+self["@performBlock"]=(function(x){
+return smalltalk.withContext(function($ctx2) {
+return _st(x)._foo();
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1)})});
+self._installTop_("foo ^ false");
+self._shouldReturn_(false);
+self._installTop_("foo ^ true");
+self._shouldReturn_(true);
 return self}, function($ctx1) {$ctx1.fill(self,"testReturns1",{},smalltalk.MethodInheritanceTest)})},
-messageSends: []}),
+messageSends: ["foo", "installTop:", "shouldReturn:"]}),
 smalltalk.MethodInheritanceTest);
 
 

+ 21 - 4
js/Kernel-Tests.js

@@ -3292,10 +3292,19 @@ category: 'tests',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+self["@performBlock"]=(function(x){
+return smalltalk.withContext(function($ctx2) {
+return _st(x)._foo();
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1)})});
+self._shouldMNU();
+self._installTop_("foo ^ false");
+self._installTop_("foo ^ true");
+self._deinstallTop();
+self._shouldMNU();
 return self}, function($ctx1) {$ctx1.fill(self,"testMNU11",{},smalltalk.MethodInheritanceTest)})},
 args: [],
-source: "testMNU11\x0a\x09\x22Breaks other tests. Commenting out\x22\x0a\x09\x22performBlock := [ :x | x foo ].\x0a\x09self shouldMNU.\x0a\x09self installTop: 'foo ^ false'.\x0a\x09self installTop: 'foo ^ true'.\x0a\x09self deinstallTop.\x0a\x09self shouldMNU\x22",
-messageSends: [],
+source: "testMNU11\x0a\x09performBlock := [ :x | x foo ].\x0a\x09self shouldMNU.\x0a\x09self installTop: 'foo ^ false'.\x0a\x09self installTop: 'foo ^ true'.\x0a\x09self deinstallTop.\x0a\x09self shouldMNU",
+messageSends: ["foo", "shouldMNU", "installTop:", "deinstallTop"],
 referencedClasses: []
 }),
 smalltalk.MethodInheritanceTest);
@@ -3331,10 +3340,18 @@ category: 'tests',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+self["@performBlock"]=(function(x){
+return smalltalk.withContext(function($ctx2) {
+return _st(x)._foo();
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1)})});
+self._installTop_("foo ^ false");
+self._shouldReturn_(false);
+self._installTop_("foo ^ true");
+self._shouldReturn_(true);
 return self}, function($ctx1) {$ctx1.fill(self,"testReturns1",{},smalltalk.MethodInheritanceTest)})},
 args: [],
-source: "testReturns1\x0a\x09\x22Breaks other tests. Commenting out\x22\x0a\x09\x22performBlock := [ :x | x foo ].\x0a\x09self installTop: 'foo ^ false'.\x0a\x09self shouldReturn: false.\x0a\x09self installTop: 'foo ^ true'.\x0a\x09self shouldReturn: true\x22",
-messageSends: [],
+source: "testReturns1\x0a\x09performBlock := [ :x | x foo ].\x0a\x09self installTop: 'foo ^ false'.\x0a\x09self shouldReturn: false.\x0a\x09self installTop: 'foo ^ true'.\x0a\x09self shouldReturn: true",
+messageSends: ["foo", "installTop:", "shouldReturn:"],
 referencedClasses: []
 }),
 smalltalk.MethodInheritanceTest);

+ 1 - 4
st/Kernel-Classes.st

@@ -267,10 +267,7 @@ basicAddCompiledMethod: aMethod
 !
 
 basicRemoveCompiledMethod: aMethod
-	<
-		smalltalk.removeMethod(aMethod,self);
-		smalltalk.init(self);
-	>
+	<smalltalk.removeMethod(aMethod,self)>
 ! !
 
 !Behavior methodsFor: 'testing'!

+ 4 - 6
st/Kernel-Tests.st

@@ -1222,13 +1222,12 @@ shouldReturn: anObject and: anObject2 and: anObject3
 !MethodInheritanceTest methodsFor: 'tests'!
 
 testMNU11
-	"Breaks other tests. Commenting out"
-	"performBlock := [ :x | x foo ].
+	performBlock := [ :x | x foo ].
 	self shouldMNU.
 	self installTop: 'foo ^ false'.
 	self installTop: 'foo ^ true'.
 	self deinstallTop.
-	self shouldMNU"
+	self shouldMNU
 !
 
 testMNU22
@@ -1241,12 +1240,11 @@ testMNU22
 !
 
 testReturns1
-	"Breaks other tests. Commenting out"
-	"performBlock := [ :x | x foo ].
+	performBlock := [ :x | x foo ].
 	self installTop: 'foo ^ false'.
 	self shouldReturn: false.
 	self installTop: 'foo ^ true'.
-	self shouldReturn: true"
+	self shouldReturn: true
 ! !
 
 TestCase subclass: #NumberTest

+ 58 - 43
support/boot.js

@@ -100,6 +100,7 @@ function inherits(child, parent) {
 		constructor: { value: child,
 			enumerable: false, configurable: true, writable: true }
 	});
+	return child;
 }
 
 inherits(SmalltalkBehavior, SmalltalkObject);
@@ -156,6 +157,7 @@ function Smalltalk() {
 	var dnu = {
 		methods: [],
 		selectors: [],
+		checker: Object.create(null),
 
 		get: function (string) {
 			var index = this.selectors.indexOf(string);
@@ -164,11 +166,16 @@ function Smalltalk() {
 			}
 			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;
 		},
 
+		isSelector: function (selector) {
+			return this.checker[selector];
+		},
+
 		/* Dnu handler method */
 
 		createHandler: function (selector) {
@@ -218,7 +225,7 @@ function Smalltalk() {
 		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;
@@ -234,10 +241,7 @@ function Smalltalk() {
 	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;
@@ -291,10 +295,7 @@ function Smalltalk() {
 
 	st.initClass = function(klass) {
 		if(klass.wrapped) {
-			klass.inheritedMethods = {};
 			copySuperclass(klass);
-		} else {
-			installSuperclass(klass);
 		}
 
 		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) {
+		var inheritedMethods = {};
+		deinstallAllMethods(klass);
 		for (superclass = superclass || klass.superclass;
 			superclass && superclass !== nil;
 			superclass = superclass.superclass) {
 			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) {
@@ -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) {
@@ -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 */
 	// TODO: Remove this hack
 
@@ -541,15 +561,7 @@ function Smalltalk() {
 		// Therefore we populate the organizer here too
 		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++) {
 			var dnuHandler = dnu.get(method.messageSends[i]);
@@ -561,16 +573,19 @@ function Smalltalk() {
 
 	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
 	};