Guy Sadoun
Guy Sadoun

Reputation: 535

squeak(smalltalk) how to get all the methods of an object (inherited methods too)

how can I get a list of all the methods an object can understand?

for example:

set := 8 getAllMethods

will give me all methods 8 can understand in set

Upvotes: 1

Views: 847

Answers (2)

JayK
JayK

Reputation: 3141

In code you can use allSelectors:

set := 8 class allSelectors

gives you a set of all the names of messages (a. k. a. selectors) that 8 can understand.

If you need the CompiledMethods instead of only the message names, you can use lookupSelector:

| class |
class := 8 class. "will be SmallInteger"
set := class allSelectors collect: [:each | class lookupSelector: each]

If you don't want to do this in code but rather find out in the IDE which messages an object can understand, then I propose to use the protocols browser (a. k. a. Lexicon tool). You can open it via "browse protocol" from the context menu of a class:

Screenshot showing browse protocol

I used it to find allSelectors and lookupSelector:, which are inherited from Behavior and not defined in Class itself.

Screenshot showing the protocol browser, showing the code of the allSelectors method

Upvotes: 6

Leandro Caniglia
Leandro Caniglia

Reputation: 14858

This is interesting because of the following. At first glance one might be tempted to consider an expression like this one

  anObject class withAllSuperclasses gather: [:class | class methodDictionary]

which gathers all the methods implemented in the class and its superclasses. However, if a method is defined in a class and in one of its superclasses, we should ignore the latter because anObject will use the one in the class.

To remedy this side-effect of the above script we need to gather only the methods that are defined in the class which is closer to anObject class. One way to do this is to enumerate the classes from top to bottom, adding all their methods to a Dictionary. Since the dictionary will only retain the last element added to a given key (in this case a selector), only the ones that belong in the protocol of anObject will survive:

methods := Dictionary new.
anObject class withAllSuperclasses reverseDo: [:class |
  methods addAll: class methodDictionary associations].

Note the use of reverseDo: for enumerating the classes downwards.

Another approach would be to enumerate the classes from bottom to top, checking whether the selector has already been visited:

methods := Dictionary new.
anObject class withAllSuperclasses do: [:class |
  class methodDictionary do: [:cm |
    methods at: cm selector ifAbsentPut: [cm]]]

(were cm stands for CompiledMethod)

The second version is a bit longer, more complex (it has two loops, one nested inside the other) and needs conditional logic (#at:ifAbsentPut:). In other words, it shouldn't be the one chosen.


Note

When looking for ways to create a collection (in this case the collection of all methods understood by an object), first make sure you really need such a collection. For instance, you will need the collection if you want to display it on the screen. However, if you are only going to use the collection for membership checking, there might be other ways to proceed. In your case, you could simply ask the object:

anObject respondsTo: <selector>

and in case the answer is true, recover the method using

anObject class lookupSelector: <selector>.

This is both simpler and more efficient because it doesn't create collections, etc.

Upvotes: 1

Related Questions