Page 1 of 1

Dump Object Methods

Posted: Sat Dec 28, 2019 2:54 pm
by mk-soft
It is sometimes difficult to get the correct method name from a class (Object) for CocoaMessage.

Update v1.06

Update v1.08.1
- Get SuperClass with level (SuperLevel = 0 .. x)
- Format of output

Update v1.08.2
- Fixed object is nil

Update v1.09.1
- String return value now optional

Code: Select all

;-TOP Dump Object Methods

; by mk-soft, 29.12.2019 - 06.10.2023, v1.09.1

Declare.s DumpObjectMethods(*Object, SuperLevel = 0, HidePrivate = #True, ShowEncoding = #False, FirstArgument = 2)

Structure ArrayOfMethods
  i.i[0]
EndStructure

ImportC ""
  class_copyMethodList(*Class, *p_methodCount)
  ; -> An array of pointers of type Method describing
  ;    the instance methods implemented by the class
  ;    Any instance methods implemented by superclasses are Not included
  ;    You must free the array with free()
  class_getName(*Class) ; -> UnsafePointer<Int8> -> *string
  sel_getName(*Selector); -> const char *
  method_getName(*Method) ; -> Selector
  method_getTypeEncoding(*Method) ; -> const char *
  method_getReturnType(*Method, *dst, dst_len) ; -> void
  method_getNumberOfArguments(*Method)         ; -> unsigned int
  method_getArgumentType(*Method, index, *dst, dst_len) ; -> void
  
  NSGetSizeAndAlignment(*StringPtr, *p_size, *p_align) 
  ; -> const char *
  ;    Obtains the actual size and the aligned size of an encoded type.
EndImport

; ----

Procedure.s GetArgumentType(*String)
  Protected r1.s, arg.s, size.i, ofs.i
  
  arg = PeekS(*String, -1, #PB_UTF8)
  r1 + arg + " - "
  If Left(arg, 1) = "^"
    r1 + "A pointer to type of "
    arg = Mid(arg, 2)
  EndIf
  Select arg
    Case "c" : r1 + "A char "
    Case "i" : r1 + "An int "
    Case "s" : r1 + "A short "
    Case "l" : r1 + "A long "
    Case "q" : r1 + "A long long"
    Case "C" : r1 + "An unsigned char "
    Case "I" : r1 + "An unsigned int "
    Case "S" : r1 + "An unsigned short "
    Case "L" : r1 + "An unsigned long "
    Case "Q" : r1 + "An unsigned long long "
    Case "f" : r1 + "A float "
    Case "d" : r1 + "A double "
    Case "B" : r1 + "A C++ bool Or a C99 _Bool "
    Case "v" : r1 + "A void"
    Case "*" : r1 + "A character string (char *) "
    Case "@" : r1 + "An object (whether statically typed Or typed id) "
    Case "#" : r1 + "A class object (Class) "
    Case ":" : r1 + "A method selector (SEL) "
    Default:
      NSGetSizeAndAlignment(*String, @size, @ofs)
      r1 + "[" + Str(size) + " bytes]"
  EndSelect
  If Right(arg, 1) = "?"
    r1 + "An unknown type (e.g. function pointer)"
  EndIf
  ProcedureReturn r1
EndProcedure

; ----

Procedure.s DumpObjectMethods(*Object, SuperLevel = 0, HidePrivate = #True, ShowEncoding = #False, FirstArgument = 2)
  Protected result.s, r1.s, i, c, n, methodCount, Method.s
  Protected *Class, *SuperClass, *Method, *Methods.ArrayOfMethods
  Protected *String
  
  *Class = object_getclass_(*Object)
  If *Class
    *String = AllocateMemory(1024)
    r1 = PeekS(class_getName(*Class), -1, #PB_UTF8)
    If SuperLevel
      For i = 1 To SuperLevel
        *SuperClass = class_getsuperclass_(*Class)
        If *SuperClass
          *Class = *SuperClass
          r1 + " -> " + PeekS(class_getName(*Class), -1, #PB_UTF8)
        Else
          Break
        EndIf
      Next
    EndIf
    *Methods = class_copyMethodList(*Class, @methodCount)
    r1 + #LF$ + #LF$ + "Count of Methods: " + methodCount + #LF$
    result = r1 + #LF$
    Debug r1
    r1 = ""
    For i = 0 To methodCount - 1
      *Method = *Methods\i[i];
      Method = PeekS(sel_getName(method_getName(*Method)), -1, #PB_UTF8)
      If HidePrivate And Left(Method, 1) = "_"
        Continue
      EndIf
      r1 + "Method " + Method + #LF$
      If ShowEncoding
        r1 + " * Encoding " + PeekS(method_getTypeEncoding(*Method), -1, #PB_UTF8) + #LF$
      EndIf
      method_getReturnType(*Method, *String, 1024)
      r1 + " -- ReturnType = " + GetArgumentType(*String) + #LF$
      c = method_getNumberOfArguments(*Method)
      For n = FirstArgument To c - 1
        method_getArgumentType(*Method, n, *String, 1024)
        r1 + " -- Argument " + Str(n - FirstArgument + 1) + " = " + GetArgumentType(*String) + #LF$
      Next
      result + r1 + #LF$
      Debug r1
      r1 = ""
    Next
    r1 + "End Class" + #LF$
    result + r1 + #LF$
    Debug r1
    If *Methods
      free_(*Methods)
    EndIf
    FreeMemory(*String)
  Else
    r1 = "Object is nil" + #LF$
    result = r1
    Debug r1
  EndIf
  ProcedureReturn result
EndProcedure

; ****

;- Example

CompilerIf #PB_Compiler_IsMainFile
  
  
  ;  Define NSAffineTransform = CocoaMessage(0, 0, "NSAffineTransform transform")
  ;  DumpObjectMethods(NSAffineTransform)
  ;
  ;  Define NSWorkspace = CocoaMessage(0, 0, "NSWorkspace sharedWorkspace")
  ;  DumpObjectMethods(NSWorkspace, 1)
  ;   
  ;  Define NSApplication = CocoaMessage(0, 0, "NSApplication sharedApplication")
  ;  DumpObjectMethods(NSApplication)
  ;  DumpObjectMethods(NSApplication, 1)
  
  If OpenWindow(0, #PB_Ignore, #PB_Ignore, 800, 600, "Dump Object Methods", #PB_Window_SystemMenu)
    EditorGadget(0, 0, 0, 800, 600, #PB_Editor_ReadOnly)
    
    ;r1.s = DumpObjectMethods(GadgetID(0), 0)
    r1.s = DumpObjectMethods(GadgetID(0), 1)
    SetGadgetText(0, r1)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
          
      EndSelect
      
    ForEver
    
  EndIf
  
CompilerEndIf

Re: Dump Object Methods

Posted: Sat Dec 28, 2019 6:04 pm
by wilbert
Type encodings are explained here
https://developer.apple.com/library/arc ... dings.html

You could consider using api functions to get the return type and arguments for a method one by one.
That might be easier to view compared to a string describing the return type and all arguments at once.

Re: Dump Object Methods

Posted: Sat Dec 28, 2019 7:54 pm
by mk-soft
Update v1.03

Thanks Wilbert :wink:

Re: Dump Object Methods

Posted: Sat Dec 28, 2019 8:26 pm
by wilbert
Much better. :)

It would also be nice if you could add an option to hide the methods starting with an underscore _ since they most likely are all private methods which are undocumented.

Argument 0 and 1 are hidden arguments which are the same for every objective c call.

If you have for example [NSMutableArray arrayWithCapacity:0]
Argument 0 is the object the message is send to (in this case the class NSMutableArray)
Argument 1 is the selector that was used (in this case arrayWithCapacity:)
Argument 2 is the first real argument (in this case the capacity value)

Re: Dump Object Methods

Posted: Sat Dec 28, 2019 9:02 pm
by mk-soft
Update v1.04

Ok Wilbert...
Now with more parameters. :wink:

Re: Dump Object Methods

Posted: Sun Dec 29, 2019 7:45 am
by wilbert
Great :)

One more thing that might be worth showing is how many bytes are required for structures.
For normal types like float or int it isn't that important but for structures is can be convenient to verify the structures you are passing are of the correct size.
For example if you are dumping an object like

Code: Select all

  Define NSAffineTransform = CocoaMessage(0, 0, "NSAffineTransform transform")
  Debug DumpObjectMethods(NSAffineTransform)
If the functionality seems useful to you, add the following function to the imports

Code: Select all

NSGetSizeAndAlignment(*typePtr, *sizep, *alignp)
Once imported, you can use it in your code for example by changing your GetArgumentType procedure like this

Code: Select all

Procedure.s GetArgumentType(*String)
  Protected r1.s, arg.s, size.i
  arg = PeekS(*String, -1, #PB_UTF8)
  r1 + arg + " - "
  
  If Left(arg, 1) = "^"
    r1 + "A pointer to type of "
    arg = Mid(arg, 2)
  EndIf
  
  Select arg
    Case "c" : r1 + "A char"
    Case "i" : r1 + "An int"
    Case "s" : r1 + "A short"
    Case "l" : r1 + "A long"
    Case "q" : r1 + "A long long"
    Case "C" : r1 + "An unsigned char"
    Case "I" : r1 + "An unsigned int"
    Case "S" : r1 + "An unsigned short"
    Case "L" : r1 + "An unsigned long"
    Case "Q" : r1 + "An unsigned long long"
    Case "f" : r1 + "A float"
    Case "d" : r1 + "A double"
    Case "B" : r1 + "A C++ bool Or a C99 _Bool"
    Case "v" : r1 + "A void"
    Case "*" : r1 + "A character string (char *)"
    Case "@" : r1 + "An object (whether statically typed Or typed id)"
    Case "#" : r1 + "A class object (Class)"
    Case ":" : r1 + "A method selector (SEL)"
    Default:
      NSGetSizeAndAlignment(*String, @size, #Null)
      r1 + " [" + Str(size) + " bytes] "
  EndSelect
  
  If Right(arg, 1) = "?"
    r1 + "An unknown type (e.g. function pointer)"
  EndIf
  
  ProcedureReturn r1
  
EndProcedure

Re: Dump Object Methods

Posted: Sun Dec 29, 2019 11:55 am
by mk-soft
Update v1.06

So the output is already very helpful. So I know how to name the methods correctly and where to look in the developer help. :wink:

Re: Dump Object Methods

Posted: Sun Dec 29, 2019 12:24 pm
by wilbert
mk-soft wrote:So the output is already very helpful. So I know how to name the methods correctly and where to look in the developer help. :wink:
It sure is :)
And it's also an easy way to see what methods PureBasic adds in its custom classes.

Re: Dump Object Methods

Posted: Sun Oct 31, 2021 3:34 pm
by mk-soft
Update v1.08.1
- Get SuperClass with level (Super = 0 .. x)
- Format of output

:wink:

Re: Dump Object Methods

Posted: Wed Apr 06, 2022 7:49 am
by fsw
The Dump Object Methods code doesn't as is on my MacBook with M1.
It doesn't matter if I choose the intel or Apple Silicon version of PB.
(with C backend...)

The error message is:
PureBasic - Assembler error
error: implicit declaration of function
'SYS_FastAllocateString4' is invalid in C99
[-Werror,-Wimplicit-function-declaration]
SYS_FastAllocateString4(&pb_select1,v_arg);
^
1 error generated.
macOS Monterey 12.3.1

Am I doing something wrong?
(wouldn't be the first time...)

Re: Dump Object Methods

Posted: Wed Apr 06, 2022 8:43 am
by mk-soft
Works on Intel with C-Backend.

Please move to Bugreport C-Backend for macOS M1 with my code.

Re: Dump Object Methods

Posted: Sun Nov 06, 2022 12:47 pm
by mk-soft
Update v1.08.2
- Fixed object is nil

Re: Dump Object Methods

Posted: Fri Oct 06, 2023 5:11 pm
by mk-soft
Update v1.09.0
- String return value now optional