Browse Source

Optimize subclasses access

Nicolas Petton 11 years ago
parent
commit
88ac1345ef
4 changed files with 190 additions and 72 deletions
  1. 53 18
      js/Kernel-Classes.deploy.js
  2. 67 22
      js/Kernel-Classes.js
  3. 26 11
      st/Kernel-Classes.st
  4. 44 21
      support/boot.js

+ 53 - 18
js/Kernel-Classes.deploy.js

@@ -81,13 +81,13 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
-$1=_st(self._allSuperclasses())._inject_into_(self._selectors(),(function(soFar,aBehavior){
+$1=_st(self._allSuperclasses())._inject_into_(self._selectors(),(function(acc,each){
 return smalltalk.withContext(function($ctx2) {
-$2=soFar;
-_st($2)._addAll_(_st(aBehavior)._selectors());
+$2=acc;
+_st($2)._addAll_(_st(each)._selectors());
 $3=_st($2)._yourself();
 return $3;
-}, function($ctx2) {$ctx2.fillBlock({soFar:soFar,aBehavior:aBehavior},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({acc:acc,each:each},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"allSelectors",{},smalltalk.Behavior)})},
 messageSends: ["inject:into:", "selectors", "addAll:", "yourself", "allSuperclasses"]}),
@@ -98,18 +98,24 @@ smalltalk.method({
 selector: "allSubclasses",
 fn: function (){
 var self=this;
-var result;
+var subclasses,index;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-result=self._subclasses();
-_st(self._subclasses())._do_((function(each){
+subclasses=self._subclasses();
+index=(1);
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
-return _st(result)._addAll_(_st(each)._allSubclasses());
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
-$1=result;
+return _st(index).__gt(_st(subclasses)._size());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
+_st(subclasses)._addAll_(_st(_st(subclasses)._at_(index))._subclasses());
+index=_st(index).__plus((1));
+return index;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$1=subclasses;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"allSubclasses",{result:result},smalltalk.Behavior)})},
-messageSends: ["subclasses", "do:", "addAll:", "allSubclasses"]}),
+}, function($ctx1) {$ctx1.fill(self,"allSubclasses",{subclasses:subclasses,index:index},smalltalk.Behavior)})},
+messageSends: ["subclasses", "whileFalse:", "addAll:", "at:", "+", ">", "size"]}),
 smalltalk.Behavior);
 
 smalltalk.addMethod(
@@ -118,13 +124,12 @@ selector: "allSubclassesDo:",
 fn: function (aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(self._subclasses())._do_((function(each){
+_st(self._allSubclasses())._do_((function(each){
 return smalltalk.withContext(function($ctx2) {
-_st(aBlock)._value_(each);
-return _st(each)._allSubclassesDo_(aBlock);
+return _st(aBlock)._value_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"allSubclassesDo:",{aBlock:aBlock},smalltalk.Behavior)})},
-messageSends: ["do:", "value:", "allSubclassesDo:", "subclasses"]}),
+messageSends: ["do:", "value:", "allSubclasses"]}),
 smalltalk.Behavior);
 
 smalltalk.addMethod(
@@ -645,9 +650,9 @@ selector: "subclasses",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-return smalltalk.subclasses(self);
+self._subclassResponsibility();
 return self}, function($ctx1) {$ctx1.fill(self,"subclasses",{},smalltalk.Behavior)})},
-messageSends: []}),
+messageSends: ["subclassResponsibility"]}),
 smalltalk.Behavior);
 
 smalltalk.addMethod(
@@ -903,6 +908,17 @@ return $1;
 messageSends: ["superclass:subclass:instanceVariableNames:package:", "asString", "new"]}),
 smalltalk.Class);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "subclasses",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self.subclasses._copy();
+return self}, function($ctx1) {$ctx1.fill(self,"subclasses",{},smalltalk.Class)})},
+messageSends: []}),
+smalltalk.Class);
+
 
 
 smalltalk.addClass('Metaclass', smalltalk.Behavior, [], 'Kernel-Classes');
@@ -995,6 +1011,25 @@ return self}, function($ctx1) {$ctx1.fill(self,"printOn:",{aStream:aStream},smal
 messageSends: ["nextPutAll:", "name", "instanceClass"]}),
 smalltalk.Metaclass);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "subclasses",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(self._instanceClass())._subclasses())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(each)._isMetaclass())._not();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._theMetaClass();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"subclasses",{},smalltalk.Metaclass)})},
+messageSends: ["collect:", "theMetaClass", "select:", "not", "isMetaclass", "subclasses", "instanceClass"]}),
+smalltalk.Metaclass);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "theMetaClass",

+ 67 - 22
js/Kernel-Classes.js

@@ -98,17 +98,17 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
-$1=_st(self._allSuperclasses())._inject_into_(self._selectors(),(function(soFar,aBehavior){
+$1=_st(self._allSuperclasses())._inject_into_(self._selectors(),(function(acc,each){
 return smalltalk.withContext(function($ctx2) {
-$2=soFar;
-_st($2)._addAll_(_st(aBehavior)._selectors());
+$2=acc;
+_st($2)._addAll_(_st(each)._selectors());
 $3=_st($2)._yourself();
 return $3;
-}, function($ctx2) {$ctx2.fillBlock({soFar:soFar,aBehavior:aBehavior},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({acc:acc,each:each},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"allSelectors",{},smalltalk.Behavior)})},
 args: [],
-source: "allSelectors\x0a\x09^self allSuperclasses\x0a\x09\x09inject: self selectors\x0a\x09\x09into: [ :soFar :aBehavior | soFar addAll: aBehavior selectors; yourself ]",
+source: "allSelectors\x0a\x09^ self allSuperclasses\x0a\x09\x09inject: self selectors\x0a\x09\x09into: [ :acc :each | acc addAll: each selectors; yourself ]",
 messageSends: ["inject:into:", "selectors", "addAll:", "yourself", "allSuperclasses"],
 referencedClasses: []
 }),
@@ -120,20 +120,26 @@ selector: "allSubclasses",
 category: 'accessing',
 fn: function (){
 var self=this;
-var result;
+var subclasses,index;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-result=self._subclasses();
-_st(self._subclasses())._do_((function(each){
+subclasses=self._subclasses();
+index=(1);
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
-return _st(result)._addAll_(_st(each)._allSubclasses());
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
-$1=result;
+return _st(index).__gt(_st(subclasses)._size());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
+_st(subclasses)._addAll_(_st(_st(subclasses)._at_(index))._subclasses());
+index=_st(index).__plus((1));
+return index;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$1=subclasses;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"allSubclasses",{result:result},smalltalk.Behavior)})},
+}, function($ctx1) {$ctx1.fill(self,"allSubclasses",{subclasses:subclasses,index:index},smalltalk.Behavior)})},
 args: [],
-source: "allSubclasses\x0a\x09| result |\x0a\x09result := self subclasses.\x0a\x09self subclasses do: [:each |\x0a\x09\x09result addAll: each allSubclasses].\x0a\x09^result",
-messageSends: ["subclasses", "do:", "addAll:", "allSubclasses"],
+source: "allSubclasses\x0a\x09\x22Answer an collection of the receiver's and the receiver's descendent's subclasses. \x22\x0a\x0a\x09| subclasses index |\x0a\x09\x0a\x09subclasses := self subclasses.\x0a\x09index := 1.\x0a\x09[ index > subclasses size ]\x0a\x09\x09whileFalse: [ subclasses addAll: (subclasses at: index) subclasses.\x0a\x09\x09\x09index := index + 1 ].\x0a\x0a\x09^ subclasses",
+messageSends: ["subclasses", "whileFalse:", "addAll:", "at:", "+", ">", "size"],
 referencedClasses: []
 }),
 smalltalk.Behavior);
@@ -145,15 +151,14 @@ category: 'enumerating',
 fn: function (aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(self._subclasses())._do_((function(each){
+_st(self._allSubclasses())._do_((function(each){
 return smalltalk.withContext(function($ctx2) {
-_st(aBlock)._value_(each);
-return _st(each)._allSubclassesDo_(aBlock);
+return _st(aBlock)._value_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"allSubclassesDo:",{aBlock:aBlock},smalltalk.Behavior)})},
 args: ["aBlock"],
-source: "allSubclassesDo: aBlock\x0a\x09\x22Evaluate the argument, aBlock, for each of the receiver's subclasses.\x22\x0a\x0a\x09self subclasses do: [ :each |\x0a    \x09aBlock value: each.\x0a        each allSubclassesDo: aBlock ].",
-messageSends: ["do:", "value:", "allSubclassesDo:", "subclasses"],
+source: "allSubclassesDo: aBlock\x0a\x09\x22Evaluate the argument, aBlock, for each of the receiver's subclasses.\x22\x0a\x0a\x09self allSubclasses do: [ :each |\x0a    \x09aBlock value: each ]",
+messageSends: ["do:", "value:", "allSubclasses"],
 referencedClasses: []
 }),
 smalltalk.Behavior);
@@ -842,11 +847,11 @@ category: 'accessing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-return smalltalk.subclasses(self);
+self._subclassResponsibility();
 return self}, function($ctx1) {$ctx1.fill(self,"subclasses",{},smalltalk.Behavior)})},
 args: [],
-source: "subclasses\x0a\x09<return smalltalk.subclasses(self)>",
-messageSends: [],
+source: "subclasses\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
 smalltalk.Behavior);
@@ -1185,6 +1190,22 @@ referencedClasses: ["ClassBuilder"]
 }),
 smalltalk.Class);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "subclasses",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self.subclasses._copy();
+return self}, function($ctx1) {$ctx1.fill(self,"subclasses",{},smalltalk.Class)})},
+args: [],
+source: "subclasses\x0a\x09<return self.subclasses._copy()>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Class);
+
 
 
 smalltalk.addClass('Metaclass', smalltalk.Behavior, [], 'Kernel-Classes');
@@ -1308,6 +1329,30 @@ referencedClasses: []
 }),
 smalltalk.Metaclass);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "subclasses",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(self._instanceClass())._subclasses())._select_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(each)._isMetaclass())._not();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})))._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._theMetaClass();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"subclasses",{},smalltalk.Metaclass)})},
+args: [],
+source: "subclasses\x0a\x09^ (self instanceClass subclasses \x0a\x09\x09select: [ :each | each isMetaclass not ])\x0a\x09\x09collect: [ :each | each theMetaClass ]",
+messageSends: ["collect:", "theMetaClass", "select:", "not", "isMetaclass", "subclasses", "instanceClass"],
+referencedClasses: []
+}),
+smalltalk.Metaclass);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "theMetaClass",

+ 26 - 11
st/Kernel-Classes.st

@@ -27,17 +27,23 @@ allInstanceVariableNames
 !
 
 allSelectors
-	^self allSuperclasses
+	^ self allSuperclasses
 		inject: self selectors
-		into: [ :soFar :aBehavior | soFar addAll: aBehavior selectors; yourself ]
+		into: [ :acc :each | acc addAll: each selectors; yourself ]
 !
 
 allSubclasses
-	| result |
-	result := self subclasses.
-	self subclasses do: [:each |
-		result addAll: each allSubclasses].
-	^result
+	"Answer an collection of the receiver's and the receiver's descendent's subclasses. "
+
+	| subclasses index |
+	
+	subclasses := self subclasses.
+	index := 1.
+	[ index > subclasses size ]
+		whileFalse: [ subclasses addAll: (subclasses at: index) subclasses.
+			index := index + 1 ].
+
+	^ subclasses
 !
 
 allSuperclasses
@@ -150,7 +156,7 @@ selectors
 !
 
 subclasses
-	<return smalltalk.subclasses(self)>
+	self subclassResponsibility
 !
 
 superclass
@@ -232,9 +238,8 @@ removeCompiledMethod: aMethod
 allSubclassesDo: aBlock
 	"Evaluate the argument, aBlock, for each of the receiver's subclasses."
 
-	self subclasses do: [ :each |
-    	aBlock value: each.
-        each allSubclassesDo: aBlock ].
+	self allSubclasses do: [ :each |
+    	aBlock value: each ]
 !
 
 protocolsDo: aBlock
@@ -353,6 +358,10 @@ package: aPackage
 
 rename: aString
 	ClassBuilder new renameClass: self to: aString
+!
+
+subclasses
+	<return self.subclasses._copy()>
 ! !
 
 !Class methodsFor: 'class creation'!
@@ -426,6 +435,12 @@ instanceVariableNames: aCollection
 		class: self instanceVariableNames: aCollection
 !
 
+subclasses
+	^ (self instanceClass subclasses 
+		select: [ :each | each isMetaclass not ])
+		collect: [ :each | each theMetaClass ]
+!
+
 theMetaClass
 	^ self
 !

+ 44 - 21
support/boot.js

@@ -317,6 +317,7 @@ function ClassesBrik(brikz, st) {
 		st.wrapClassName("Class", "Kernel-Classes", SmalltalkClass, st.Behavior, false);
 
 		st.Object.klass.superclass = st.Class;
+		addSubclass(st.Object.klass);
 
 		st.wrapClassName("Package", "Kernel-Infrastructure", SmalltalkPackage, st.Object, false);
 	};
@@ -349,7 +350,10 @@ function ClassesBrik(brikz, st) {
 		spec = spec || {};
 		var meta = metaclass(spec);
 		var that = meta.instanceClass;
+
 		that.fn = spec.fn || inherits(function () {}, spec.superclass.fn);
+		that.subclasses = [];
+
 		setupClass(that, spec);
 
 		that.className = spec.className;
@@ -420,7 +424,11 @@ function ClassesBrik(brikz, st) {
 
 	function rawAddClass(pkgName, className, superclass, iVarNames, wrapped, fn) {
 		var pkg = st.packages[pkgName];
-		if (!pkg) { throw new Error("Missing package "+pkgName); }
+
+		if (!pkg) { 
+			throw new Error("Missing package "+pkgName); 
+		}
+
 		if(st[className] && st[className].superclass == superclass) {
 //            st[className].superclass = superclass;
 			st[className].iVarNames = iVarNames || [];
@@ -442,6 +450,8 @@ function ClassesBrik(brikz, st) {
 				fn: fn,
 				wrapped: wrapped
 			});
+
+			addSubclass(st[className]);
 		}
 
 		classes.addElement(st[className]);
@@ -451,9 +461,22 @@ function ClassesBrik(brikz, st) {
 	st.removeClass = function(klass) {
 		org.removeOrganizationElement(klass.pkg, klass);
 		classes.removeElement(klass);
+		removeSubclass(klass);
 		delete st[klass.className];
 	};
 
+	function addSubclass(klass) {
+		if(klass.superclass) {
+			klass.superclass.subclasses.addElement(klass);
+		}
+	}
+
+	function removeSubclass(klass) {
+		if(klass.superclass) {
+			klass.superclass.subclasses.removeElement(klass);
+		}
+	}
+
 	/* Create a new class wrapping a JavaScript constructor, and add it to the
 	 global smalltalk object. Package is lazily created if it does not exist with given name. */
 
@@ -496,29 +519,29 @@ function ClassesBrik(brikz, st) {
 
 	/* Answer the direct subclasses of klass. */
 
-	st.subclasses = function(klass) {
-		var subclasses = [];
-		var classes = st.classes();
-		for(var i=0; i < classes.length; i++) {
-			var c = classes[i];
-			if(c.fn) {
-				//Classes
-				if(c.superclass === klass) {
-					subclasses.push(c);
-				}
-				c = c.klass;
-				//Metaclasses
-				if(c && c.superclass === klass) {
-					subclasses.push(c);
-				}
-			}
-		}
-		return subclasses;
-	};
+	// st.subclasses = function(klass) {
+	// 	var subclasses = [];
+	// 	var classes = st.classes();
+	// 	for(var i=0; i < classes.length; i++) {
+	// 		var c = classes[i];
+	// 		if(c.fn) {
+	// 			//Classes
+	// 			if(c.superclass === klass) {
+	// 				subclasses.push(c);
+	// 			}
+	// 			c = c.klass;
+	// 			//Metaclasses
+	// 			if(c && c.superclass === klass) {
+	// 				subclasses.push(c);
+	// 			}
+	// 		}
+	// 	}
+	// 	return subclasses;
+	// };
 
 	st.allSubclasses = function(klass) {
 		var result, subclasses;
-		result = subclasses = st.subclasses(klass);
+		result = subclasses = klass.subclasses;
 		subclasses.forEach(function(subclass) {
 			result.push.apply(result, st.allSubclasses(subclass));
 		});