SLOC in Squeak History

Boris Gaertner Boris.Gaertner at gmx.net
Tue May 11 12:10:53 UTC 2004


From: <michael.cole at nimiq.net> asked:
>
> So, I want to write a method that will count the SLOC in each version of
> squeak. My question is what object am I seeking to enumerate? It doesn't
> seem to be Message.

Most of the methods that you need to enumerate methods can be
found in the protocol of Behavior. Together with its subclasses, this
class implements the capablity of Smalltalk to describe itself.

Behavior is typically left unexplained in introductory
presentations of Smalltalk.

First a technical hint:
Iteration over all classes is time consuming. To quickly
test a new idea, you may prefer to iterate over one small
branch of the class tree. Good candidates are Set and Number

   Set withAllSubclasses size    16
   Number withAllSubclasses size    8

These are branches with 16 resp. 8 classes.

You can quickly count the methods:

    |  instanceMethods classMethods |
  instanceMethods := classMethods := 0.
  Number withAllSubclasses do:
      [:class |
       class isMeta
         ifFalse:
          [instanceMethods := class methodDictionary size
                                            + instanceMethods.
          classMethods  := class class methodDictionary size
                                            + classMethods].
      ].
  { instanceMethods. classMethods }

The  'class isMeta ifFalse:'  is not really necessary in this
example, it becomes necessary as soon as we iterate over
a class tree that contains class Class. The subclasses of
Class are the metaclasses (the entities that describe the
class proctocol of classes) and in our code we access
the metaclasses with "class class"

Now let us take a closer look at the methods.
Have you seen ClassDescription>>linesOfCode?
This is a good point to start with.

Try (with 'print it')
 Number linesOfCode
 Number class linesOfCode

Then try (with 'print it')

   |  instanceLoc classLoc |
  instanceLoc := classLoc := 0.
Number withAllSubclasses do:
    [:class |
      class isMeta
        ifFalse:
          [instanceLoc := class linesOfCode + instanceLoc.
           classLoc := class class linesOfCode + classLoc].
   ].
  { instanceLoc. classLoc }

What is that?
Here we count for Number and all its subclasses the number
of codelines. We do that separately for the instance methods
and for the class methods.


Keep in mind that the Smalltalk compiler is always at
your service. This simple fact gives you the possibility
to try metrics that are difficult to use in other languages.

So see how you can use the parser, please try this with
'inspect':

  | methodNode |
  methodNode:= Collection  compilerClass new
       parse: (Collection sourceCodeAt: #size)
       in: Collection
       notifying: nil.
  methodNode block

The inspector show you an instance of  BlockNode.
This is a structure that the parser creates for use by
the compiler. The block node has an instance variable
'statements' and when you follow it, you see that it
references an OrderedCollection with three elements.
Look at: Collection>size  This method has three
statements!

The expression

  | methodNode |
  methodNode:= Collection  compilerClass new
       parse: (Collection sourceCodeAt: #size)
       in: Collection
       notifying: nil.
  methodNode block statements size

answers the number of statements in  Collection>size.

We can do this for all methods in our image:


  |  stmts methodNode |
 stmts := 0.
ProtoObject withAllSubclasses do:
 [:cl |
 cl isMeta
   ifFalse:
     [(Array with: cl with: cl class)
     do:
      [:classOrMetaClass |
       classOrMetaClass selectorsDo:
          [:sel |
             methodNode:= classOrMetaClass compilerClass new
                                 parse: (classOrMetaClass sourceCodeAt: sel)
                                 in: classOrMetaClass
                                 notifying: nil.
             stmts := stmts + methodNode block statements size
        ].
    ].
 ]].

This is already quite nice, but it has the following
peculiarity: A structured statement like

  rec isFoo
    ifTrue: [rec doThis. rec doThat]
    ifFalse: [rec doSomethingDifferent]

is counted as one statement. Often you would
prefer to say that these are four statements.

Attached you find a change set that adds the instance
nroOfStatements to six kinds of ParseNodes. With
that addition you can count


  |  stmts methodNode |
 stmts := 0.
ProtoObject withAllSubclasses do:
  [:cl |
 cl isMeta
   ifFalse:
     [(Array with: cl with: cl class)
     do:
      [:classOrMetaClass |
    classOrMetaClass selectorsDo:
          [:sel |
                methodNode:= classOrMetaClass compilerClass new
                            parse: (classOrMetaClass sourceCodeAt: sel)
                             in: classOrMetaClass
                             notifying: nil.
               stmts := stmts + methodNode nroOfStatements
        ].
    ].
 ]].
  stmts

For a quite up-to-date 3.7beta image I get 273286 statements.
When I compare with the number of lines computed by Markus
I conclude that we care for readability: The rule "one statement
per line" is obviously followed.

This is a long mail, but I hope it gives you some ideas where
to start.
Greetings, Boris
-------------- next part --------------
A non-text attachment was scrubbed...
Name: metrics.2.cs.gz
Type: application/x-gzip
Size: 2091 bytes
Desc: not available
Url : http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20040511/01c64d0d/metrics.2.cs.bin


More information about the Squeak-dev mailing list