Browse Source

kernel: fix traverseClassTree, call kernel from st for class traverse.

Herbert Vojčík 7 years ago
parent
commit
4db66a702b
5 changed files with 163 additions and 65 deletions
  1. 20 50
      src/Kernel-Classes.js
  2. 5 14
      src/Kernel-Classes.st
  3. 102 0
      src/Kernel-Tests.js
  4. 22 0
      src/Kernel-Tests.st
  5. 14 1
      support/boot.js

+ 20 - 50
src/Kernel-Classes.js

@@ -182,45 +182,36 @@ selector: "allSubclasses",
 protocol: 'accessing',
 fn: function (){
 var self=this;
-var subclasses,index;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-subclasses=self._subclasses();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["subclasses"]=1;
-//>>excludeEnd("ctx");
-index=(1);
-$recv((function(){
+return $recv($globals.Array)._streamContents_((function(str){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
-return $recv(index).__gt($recv(subclasses)._size());
+return self._allSubclassesDo_((function(each){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+return $core.withContext(function($ctx3) {
 //>>excludeEnd("ctx");
-}))._whileFalse_((function(){
+return $recv(str)._nextPut_(each);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx2,2)});
 //>>excludeEnd("ctx");
-$recv(subclasses)._addAll_($recv($recv(subclasses)._at_(index))._subclasses());
-index=$recv(index).__plus((1));
-return index;
+}));
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
+}, function($ctx2) {$ctx2.fillBlock({str:str},$ctx1,1)});
 //>>excludeEnd("ctx");
 }));
-return subclasses;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"allSubclasses",{subclasses:subclasses,index:index},$globals.Behavior)});
+}, function($ctx1) {$ctx1.fill(self,"allSubclasses",{},$globals.Behavior)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-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",
-referencedClasses: [],
+source: "allSubclasses\x0a\x09\x22Answer an collection of the receiver's and the receiver's descendent's subclasses. \x22\x0a\x0a\x09^ Array streamContents: [ :str | self allSubclassesDo: [ :each | str nextPut: each ] ]",
+referencedClasses: ["Array"],
 //>>excludeEnd("ide");
-messageSends: ["subclasses", "whileFalse:", ">", "size", "addAll:", "at:", "+"]
+messageSends: ["streamContents:", "allSubclassesDo:", "nextPut:"]
 }),
 $globals.Behavior);
 
@@ -233,15 +224,9 @@ var self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-$recv(self._allSubclasses())._do_((function(each){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(aBlock)._value_(each);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
+$core.traverseClassTree(self, function(subclass) {
+	if (subclass !== self) aBlock._value_(subclass);
+});
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"allSubclassesDo:",{aBlock:aBlock},$globals.Behavior)});
@@ -249,10 +234,10 @@ return self;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlock"],
-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 ]",
+source: "allSubclassesDo: aBlock\x0a\x09\x22Evaluate the argument, aBlock, for each of the receiver's subclasses.\x22\x0a\x0a<$core.traverseClassTree(self, function(subclass) {\x0a\x09if (subclass !== self) aBlock._value_(subclass);\x0a})>",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["do:", "allSubclasses", "value:"]
+messageSends: []
 }),
 $globals.Behavior);
 
@@ -2190,33 +2175,18 @@ var self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-return $recv($recv($recv(self._instanceClass())._subclasses())._select_((function(each){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv($recv(each)._isMetaclass())._not();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
-//>>excludeEnd("ctx");
-})))._collect_((function(each){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(each)._theMetaClass();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,2)});
-//>>excludeEnd("ctx");
-}));
+return $core.metaSubclasses(self);
+return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"subclasses",{},$globals.Metaclass)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "subclasses\x0a\x09^ (self instanceClass subclasses \x0a\x09\x09select: [ :each | each isMetaclass not ])\x0a\x09\x09collect: [ :each | each theMetaClass ]",
+source: "subclasses\x0a\x09<return $core.metaSubclasses(self)>",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["collect:", "select:", "subclasses", "instanceClass", "not", "isMetaclass", "theMetaClass"]
+messageSends: []
 }),
 $globals.Metaclass);
 

+ 5 - 14
src/Kernel-Classes.st

@@ -35,15 +35,7 @@ allSelectors
 allSubclasses
 	"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
+	^ Array streamContents: [ :str | self allSubclassesDo: [ :each | str nextPut: each ] ]
 !
 
 allSuperclasses
@@ -274,8 +266,9 @@ removeCompiledMethod: aMethod
 allSubclassesDo: aBlock
 	"Evaluate the argument, aBlock, for each of the receiver's subclasses."
 
-	self allSubclasses do: [ :each |
-    	aBlock value: each ]
+<$core.traverseClassTree(self, function(subclass) {
+	if (subclass !!== self) aBlock._value_(subclass);
+})>
 !
 
 protocolsDo: aBlock
@@ -497,9 +490,7 @@ package
 !
 
 subclasses
-	^ (self instanceClass subclasses 
-		select: [ :each | each isMetaclass not ])
-		collect: [ :each | each theMetaClass ]
+	<return $core.metaSubclasses(self)>
 !
 
 theMetaClass

+ 102 - 0
src/Kernel-Tests.js

@@ -2324,6 +2324,108 @@ messageSends: ["ifNotNil:", "removeClass:"]
 }),
 $globals.ClassTest);
 
+$core.addMethod(
+$core.method({
+selector: "testAllSubclasses",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var subclasses,index;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+subclasses=$recv($globals.Object)._subclasses();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["subclasses"]=1;
+//>>excludeEnd("ctx");
+index=(1);
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(index).__gt($recv(subclasses)._size());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._whileFalse_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$recv(subclasses)._addAll_($recv($recv(subclasses)._at_(index))._subclasses());
+index=$recv(index).__plus((1));
+return index;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+self._assert_equals_($recv($globals.Object)._allSubclasses(),subclasses);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testAllSubclasses",{subclasses:subclasses,index:index},$globals.ClassTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testAllSubclasses\x0a\x09| subclasses index |\x0a\x0a\x09subclasses := Object 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\x09self assert: Object allSubclasses equals: subclasses",
+referencedClasses: ["Object"],
+//>>excludeEnd("ide");
+messageSends: ["subclasses", "whileFalse:", ">", "size", "addAll:", "at:", "+", "assert:equals:", "allSubclasses"]
+}),
+$globals.ClassTest);
+
+$core.addMethod(
+$core.method({
+selector: "testMetaclassSubclasses",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var subclasses;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $4,$3,$2,$1;
+$4=$recv($globals.Object)._class();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["class"]=1;
+//>>excludeEnd("ctx");
+$3=$recv($4)._instanceClass();
+$2=$recv($3)._subclasses();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["subclasses"]=1;
+//>>excludeEnd("ctx");
+$1=$recv($2)._select_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($recv(each)._isMetaclass())._not();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+subclasses=$recv($1)._collect_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(each)._theMetaClass();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+self._assert_equals_($recv($recv($globals.Object)._class())._subclasses(),subclasses);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testMetaclassSubclasses",{subclasses:subclasses},$globals.ClassTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testMetaclassSubclasses\x0a\x09| subclasses |\x0a\x0a\x09subclasses := (Object class instanceClass subclasses \x0a\x09\x09select: [ :each | each isMetaclass not ])\x0a\x09\x09collect: [ :each | each theMetaClass ].\x0a\x0a\x09self assert: Object class subclasses equals: subclasses",
+referencedClasses: ["Object"],
+//>>excludeEnd("ide");
+messageSends: ["collect:", "select:", "subclasses", "instanceClass", "class", "not", "isMetaclass", "theMetaClass", "assert:equals:"]
+}),
+$globals.ClassTest);
+
 $core.addMethod(
 $core.method({
 selector: "testSetJavaScriptConstructor",

+ 22 - 0
src/Kernel-Tests.st

@@ -460,6 +460,28 @@ trickyJsConstructor
 
 !ClassTest methodsFor: 'tests'!
 
+testAllSubclasses
+	| subclasses index |
+
+	subclasses := Object subclasses.
+	index := 1.
+	[ index > subclasses size ]
+		whileFalse: [ subclasses addAll: (subclasses at: index) subclasses.
+			index := index + 1 ].
+
+	self assert: Object allSubclasses equals: subclasses
+!
+
+testMetaclassSubclasses
+	| subclasses |
+
+	subclasses := (Object class instanceClass subclasses 
+		select: [ :each | each isMetaclass not ])
+		collect: [ :each | each theMetaClass ].
+
+	self assert: Object class subclasses equals: subclasses
+!
+
 testSetJavaScriptConstructor
 	| instance |
 	theClass := builder copyClass: ObjectMock named: 'ObjectMock2'.

+ 14 - 1
support/boot.js

@@ -537,12 +537,25 @@ define(['require', './brikz.umd', './compatibility'], function (require, Brikz)
             return detachedRootClasses;
         };
 
+        function metaSubclasses(metaclass) {
+            return metaclass.instanceClass.subclasses
+                .filter(function (each) {
+                    return !each.meta;
+                })
+                .map(function (each) {
+                    return each.klass;
+                });
+        }
+
+        st.metaSubclasses = metaSubclasses;
+
         st.traverseClassTree = function (klass, fn) {
             var queue = [klass];
             for (var i = 0; i < queue.length; ++i) {
                 var item = queue[i];
                 fn(item);
-                queue.push.apply(queue, item.subclasses);
+                var subclasses = item.meta ? metaSubclasses(item) : item.subclasses;
+                queue.push.apply(queue, subclasses);
             }
         }
     });