Written by

Senior Startups and Community Programs Manager at InterSystems Corporation
Question Evgeny Shvarov · Dec 10, 2021

Is it possible to change the code of a class programmatically?

Hi folks!

I'm curious if it is possible to change the class method to another class?

I call a method of a system class  to generate another class. But there is a bug/feature in a library class, so I need to tweak a bit the generated method, e.g. add additional parameter and change the line to "if" the parameter.

I hope to do this programmatically after class generation and change the code in a way I like.

What are the options?

Product version: IRIS 2021.1

Comments

Robert Cemper · Dec 10, 2021

Sure!  very condensed

  • set cls=##class(%Dictionary.ClassDefinition).%OpenId("<pkg>.<classname>")
  • set met=cls.Methods.GetAt(1)  ; may need some search
  • set imp=met.Implementation   ; a GlobalChar.Stream
  • write imp,OutputToDevice()

or you jump to the Method directly

  • set met=##class(%Dictionary.MethodDefinition).%OpenId("<pkg>.<classname>||<methodname>")

And these classes have tons of params to set or clear by specific methods

0
Evgeny Shvarov  Dec 10, 2021 to Robert Cemper

Thanks, Robert!

And what is the easiest way to change the particular line of code?

E.g. I need to add a parameter to a method definition.

0
Robert Cemper  Dec 10, 2021 to Evgeny Shvarov

No brilliant idea yet:

  • disassemble the char.stream to lines in steps of 10 to PPG
  • insert new lines in between
  • reassemble the stream by $o() from PPG

not so impressive.

0
Herman Slagman  Dec 12, 2021 to Evgeny Shvarov

If you mean an argument to the Method, use the MethDef.FormalSpec string:
The format is Arg1:Type,Arg2:Type=default,...

0
Herman Slagman · Dec 10, 2021

Open the ClassDefinition: Set ClassDef=##class(%Dictionalry.ClassDefinition).%OpenId(ClassName)
Add the Parameter

Set Parm=##class(%Dictionary.ParameterDefinition).%New(ClassName_":"_ParameterName)
Set Parm.Default=ParameterValue
Do ClassDef.Parameters.Insert(Parm)


Find the desired method in ClassDef.Methods collection by MethDef.Name
Use the MethDef.Implementation stream to manipulate the code
Compile the class.

0
Dmitry Maslennikov · Dec 10, 2021

Yeah, of course, you can change any class, if it's not a system and not deployed class stored in a read-only database

looks like you already know how to generate classes, so, to edit some method, you have to open a particular method by its id, which can be constructed from the class name and the method name.

USER>set method = ##class(%Dictionary.MethodDefinition).%OpenId("%Library.File||Exists") 

USER>write method.Implementation.Size
56
0
Evgeny Shvarov · Dec 16, 2021

Thanks @Dmitry Maslennikov, @Robert C. Cemper and @Herman Slagman !

Turn out it's not that hard with your help!

The result can be observed here - adding a parameter and the implementation to a method in csvgen.

And yes, here is the code itself:

ClassMethod AddAppendtoImport(pClassName As %String) As %Status
{
 
    set method = ##class(%Dictionary.MethodDefinition).%OpenId(pClassName_"||Import")

    if method.FormalSpec["pAppend" quit $$$OK
    set method.FormalSpec=method.FormalSpec_",pAppend:%Boolean=0"
    set imp=method.Implementation
    set newImp=##class(%Stream.TmpCharacter).%New()

    while 'imp.AtEnd {
        set line=imp.ReadLine() 
        if line'["%DeleteExtent" {do newImp.WriteLine(line)}
        else {
            do newImp.WriteLine($p(line,"do")_"if 'pAppend do ..%DeleteExtent(,.tDeleted,.tInstances,1)")
        }
        }
    set method.Implementation=newImp
    set sc=method.%Save() 
    if $$$ISERR(sc) return sc
    set sc=$System.OBJ.Compile(pClassName,"cuk")
    return sc
}


 

0