gmslOpcodeClass

The Opcode Service Class

The class Opcode works with the intermediate language streams. Its methods actively manage and reference the opcodes of the intermediate language. Their description and use assume a knowledge of that language and of its internal structure as applied by the gmBasic toolset. The primary context in which these methods are used is in Transform event handlers used in code migrations. The presentation here assumes knowledge of the material presented in the section on gmIL.

Opcode Class CommentOut Method

gmSL: int Opcode.CommentOut(int iStart,int nCode,int nDelete);


<Method id="CommentOut"   type="Integer" opcode="SCM.Opcode_CommentOut" >
   <Argument id="iStart"  type="Integer" status="ByVal" />
   <Argument id="cmtType" type="Integer" status="ByVal" />
</Method>
The method CommentOut comments out a block of intermediate operations so that they will either not appear in the target translation at all or at least will be commented out in the target translation. The parameter iStart is the offset in the global code block of the component whose presence is causing the need to comment out the statement, and the parameter cmtType specifies how the commented out code is to appear, or not appear, in the output:

cmtType Description
Delete Removes the statement from the authored output
CommentOut Precedes the statement with a comment identifying the causing component and then comments the statement out.
Deprecated Precedes the statement with a Deprecated comment identifying the causing component and then does not comment the statement out.
NotImplemented Precedes the statement with a Not Implemented comment identifying the causing component, and then comments the statement out.
MustCorrect Precedes the statement with a Must Correct comment identifying the causing component, and then comments the statement out.

The code samples below work with a simple statement


ConvertToString = CStr(v)
and then comment it out because it contains a reference to Cstr. When not commented out the operations for this statement look as follows.


 54 |           |           |                      | LEV    | Nest0
 56 |           |           |                      | BIF    | Component:CStr:196867
 61 |           |           |                      | LEV    | Nest1
 63 |      1.63 |      1.63 | Object               | LDA    | Variable:v:286901
 68 |      1.63 |           |                      | ARG    | Void
 70 |      1.63 |      1.70 | String               | VBF    | CStr
 72 |      1.63 |           |                      | ARG    | String
 74 |      2.74 |      1.74 | String               | LDA    | Subprogram:ConvertToString:286847
 79 |           |           |                      | STR    | AssignValue
Commenting it out using the Delete type changes the operations to this.


 54 |           |           |                      | LEV    | Nest0
 56 |           |           |                      | BIF    | Component:CStr:196867
 61 |           |           |                      | LEV    | Nest1
 63 |      1.63 |      1.63 | Object               | LDA    | Variable:v:286901
 68 |      1.63 |           |                      | ARG    | Void
 70 |      1.63 |      1.70 | String               | VBF    | CStr
 72 |      1.63 |           |                      | ARG    | String
 74 |      2.74 |      1.74 | String               | LDA    | Subprogram:ConvertToString:286847
 79 |           |           |                      | CMT    | Delete
The STR.AssignValue operation has been replaced by a CMT.Delete operation which will simply clear the string stack when it is authored. Commenting it out with CommentOut type changes the operations to this.


 54 |      1.54 |      1.54 | String               | LSP    | 16:Vb6Function.CStr
 59 |      1.54 |           |                      | CMT    | CommentOut
 61 |      1.54 |           |                      | LEV    | Nest0
 63 |      1.54 |           |                      | BIF    | Component:CStr:196867
 68 |      1.54 |           |                      | LEV    | Nest1
 70 |      2.70 |      1.70 | Object               | LDA    | Variable:v:286901
 75 |      2.70 |           |                      | ARG    | Void
 77 |      2.70 |      1.77 | String               | VBF    | CStr
 79 |      2.70 |           |                      | ARG    | String
 81 |      3.81 |      1.81 | String               | LDA    | Subprogram:ConvertToString:286847
 86 |           |           |                      | STR    | AssignValue
 88 |           |           |                      | CMT    | CommentOut
There are two changes made. First a new comment is added in front of the statement code that will display "Vb6Function.CStr" to identify the component that caused the commenting out and the second that will comment the statement out itself. As an assign it would appear that the assignment statement would be written prior to the execution of the CMT.CommentOut so it would have nothing left to operate on; however, the string machine checks for following include comment operations and raw comment operations before writting statements. This is why this works. Commenting it out with Deprecated changes the operations to this.


 54 |      1.54 |      1.54 | String               | LSP    | 16:Vb6Function.CStr
 59 |      1.54 |           |                      | CMT    | Deprecated
 61 |      1.54 |           |                      | LEV    | Nest0
 63 |      1.54 |           |                      | BIF    | Component:CStr:196867
 68 |      1.54 |           |                      | LEV    | Nest1
 70 |      2.70 |      1.70 | Object               | LDA    | Variable:v:286901
 75 |      2.70 |           |                      | ARG    | Void
 77 |      2.70 |      1.77 | String               | VBF    | CStr
 79 |      2.70 |           |                      | ARG    | String
 81 |      3.81 |      1.81 | String               | LDA    | Subprogram:ConvertToString:286847
 86 |           |           |                      | STR    | AssignValue
Here only one change is made. A string is added in front of the statement code "Vb6Function.CStr" that identifies the component that caused the deprecation along with the CMT.Deprecate operation which will display an appropriate message. Commenting the statement out with one of the other types such as NotImplemented changes the operations to this.


 54 |      1.54 |      1.54 | String               | LSP    | 16:Vb6Function.CStr
 59 |      1.54 |           |                      | CMT    | NotImplemented
 61 |      1.54 |           |                      | LEV    | Nest0
 63 |      1.54 |           |                      | BIF    | Component:CStr:196867
 68 |      1.54 |           |                      | LEV    | Nest1
 70 |      2.70 |      1.70 | Object               | LDA    | Variable:v:286901
 75 |      2.70 |           |                      | ARG    | Void
 77 |      2.70 |      1.77 | String               | VBF    | CStr
 79 |      2.70 |           |                      | ARG    | String
 81 |      3.81 |      1.81 | String               | LDA    | Subprogram:ConvertToString:286847
 86 |           |           |                      | STR    | AssignValue
 88 |           |           |                      | CMT    | CommentOut
This is exactly like the change made for the simple CommentOut request except that the message surrounding the identifier of the causing component changes according to the detailed reason.

The following code isolates a Cstr operation in the demo code and then comments out the statement containing it using the various commenting out types. Notice that since the code is being changed it must be read in again after each change.


Select.CodeCommentOut = False;
subRoot = Symbol.FindIdentifier("FMStocks_DB.ConvertToString");
subInfo = Store.GetVector(subRoot);
codptr = Opcode.GetCode();
nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
Opcode.SetLength(nCode);
Write.Line("Authored code before commenting out statement");
Runtime.AuthorCode(0,nCode);

for(icode = 0; icode >= 0; icode = Opcode.GetNext(codptr,icode,nCode))
{
   oper = = Opcode.GetOperation(codptr,icode,subOper);
   if(oper == OPC.VBF && subOper == OPC.VBF.CStr) break;
}
Write.Line("Found VBF.Cstr at location " + icode);

Opcode.CommentOut(icode,OPC.CMT.Delete);
nCode = Opcode.GetLength();
Write.Line("Authored code after commenting out statement with Delete");
Runtime.AuthorCode(0,nCode);

nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
Opcode.SetLength(nCode);
Opcode.CommentOut(icode,OPC.CMT.CommentOut);
nCode = Opcode.GetLength();
Write.Line("Authored code after commenting out statement with CommentOut");
Runtime.AuthorCode(0,nCode);

nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
Opcode.SetLength(nCode);
Opcode.CommentOut(icode,OPC.CMT.Deprecated);
nCode = Opcode.GetLength();
Write.Line("Authored code after commenting out statement with Deprecated");
Runtime.AuthorCode(0,nCode);

nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
Opcode.SetLength(nCode);
Opcode.CommentOut(icode,OPC.CMT.NotImplemented);
nCode = Opcode.GetLength();
Write.Line("Authored code after commenting out statement with NotImplemented");
Runtime.AuthorCode(0,nCode);

nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
Opcode.SetLength(nCode);
Opcode.CommentOut(icode,OPC.CMT.MustCorrect);
nCode = Opcode.GetLength();
Write.Line("Authored code after commenting out statement with MustCorrect");
Runtime.AuthorCode(0,nCode);
The output is as expected.


Authored code before commenting out statement
if (VBNET.Information.IsNothing(v))
{
   Helpers.ConvertToString = "";
}
else
{
   Helpers.ConvertToString = Convert.ToString(v);
}
Found VBF.Cstr at location 70
Authored code after commenting out statement with Delete
if (VBNET.Information.IsNothing(v))
{
   Helpers.ConvertToString = "";
}
else
{
}
Authored code after commenting out statement with CommentOut
if (VBNET.Information.IsNothing(v))
{
   Helpers.ConvertToString = "";
}
else
{
   // Vb6Function.CStr
   // Helpers.ConvertToString = Convert.ToString(v);
}
Authored code after commenting out statement with Deprecated
if (VBNET.Information.IsNothing(v))
{
   Helpers.ConvertToString = "";
}
else
{
   #if !DEBUG
      #warning "UPGRADE WARNING: Vb6Function.CStr was upgraded, but is should be reviewed."
   #endif
   Helpers.ConvertToString = Convert.ToString(v);
}
Authored code after commenting out statement with NotImplemented
if (VBNET.Information.IsNothing(v))
{
   Helpers.ConvertToString = "";
}
else
{
   #if !DEBUG
      #warning "UPGRADE ISSUE: Vb6Function.CStr was not upgraded."
   #endif
   // Helpers.ConvertToString = Convert.ToString(v);
}
Authored code after commenting out statement with MustCorrect
if (VBNET.Information.IsNothing(v))
{
   Helpers.ConvertToString = "";
}
else
{
   #if !DEBUG
      #error "UPGRADE ISSUE: Vb6Function.CStr was not upgraded."
   #endif
   // Helpers.ConvertToString = Convert.ToString(v);
}
In addition to intrinsic functions control components can be marked as having a migration of NotImplemented or MustCorrect. In addition a longer description of the reason for the status can be added.


<property id="WordWrap" type="Boolean" status="ByVal" opcode="LAB.31"
   migStatus="NotImplemented"
   migComment=" WordWrap is now always on by default. See http://migration/finish_work/wordwrap.htm"
When a statement containing one of these components different CMT operations are used that display both the component identifier and extended description. Here is a code sample


2912 |           |           |                      | NEW    | 541 lblRedirect.WordWrap = True
2915 |    1.2915 |    1.2915 | String               | LSP    | 14:Label.WordWrap
2920 |    2.2920 |    2.2920 | String               | LSP    |  WordWrap is now always on by default. See http://migration/finish_work/wordwrap.htm
2925 |    1.2915 |           |                      | CMT    | IssueComment
2927 |    1.2915 |           |                      | LEV    | Nest0
2929 |    2.2929 |    1.2929 | Boolean              | LBC    | True
2931 |    2.2929 |           |                      | ARG    | Boolean
2933 |    3.2933 |    1.2933 | Label                | LDA    | Label:lblRedirect:5808347
2938 |    4.2938 |    1.2933 | Boolean              | LAB    | WordWrap
2940 |    3.2933 |    1.2933 | Boolean              | MEM    | Child
2942 |           |           |                      | STR    | AssignValue
2944 |           |           |                      | CMT    | CommentOut
The actual translation produced then is as follows.


 #if COMMENT
    #warning "UPGRADE ISSUE: Label.WordWrap was not upgraded. WordWrap is now always on by default. See http://migration/finish_work/wordwrap.htm"
 #endif
 // lblRedirect.WordWrap = true;
/>

Opcode.DeleteCode Method

gmSL: int Opcode.DeleteCode(int iStart,int nCode,int nDelete);


<Method id="DeleteCode"   type="Integer" opcode="SCM.Opcode_DeleteCode" >
   <Argument id="iStart"  type="Integer" status="ByVal" />
   <Argument id="nCode"   type="Integer" status="ByVal" />
   <Argument id="nDelete" type="Integer" status="ByVal" />
</Method>
The method Opcode.DeleteCode deletes a sequence of byte codes from the global code vector used to contain the compiled code associated with the various components. The parameter iStart is the starting offset of the code to be deleted. The parameter nCode is the overall length of the stored code. The parameter nDelete is the number of bytes to be deleted. The method returns the new overall length of the code storage area.

The following shows a code block containing a REF operation that can be deleted. Note the use of the sizeof intrinsic to determine the number of bytes to remove.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   Opcode.SetLength(nCode);
   Execute.Output(,,,"on");
   Write.Line("Code Dump before deleting REF");
   Opcode.DumpCode(0,nCode);
   nCode = Opcode.DeleteCode(12,nCode,sizeof(OPC.REF));
   Write.Line("Code Dump after deleting REF");
   Opcode.DumpCode(0,nCode);
The result shows the code before and after the deletion.


Code Dump before deleting REF
Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
    17 |       1.5 |           |                      | ARG    | String
    19 |           |           |                      | EXI    | Function
Code Dump after deleting REF
Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |           |                      | ARG    | String
    14 |           |           |                      | EXI    | Function

Opcode.DumpCode Method

gmSL: void DumpCode(int iStart,int iEnd);


<Method id="DumpCode" type="void" opcode="SCM.Opcode_DumpCode" >
   <Argument id="iStart" type="Integer" status="ByVal" />
   <Argument id="iEnd" type="Integer" status="ByVal" />
</Method>
The method Opcode.DumpCode gives a code dump display from the global code vector used to contain the compiled code associated with the various components. The parameter iStart contains the starting offset of the code to be displayed and the parameter iEnd contains the ending offset. The method checks that the value of iEnd does not exceed the current length of the code storage area. Therefore, if that length has been changed it is important to call the method Opcode.SetLength before calling this method.

The following example reads the code associated with a method in the FmStocks sample code, sets the length, and than dumps the code itself.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   Opcode.SetLength(nCode);
   Execute.Output(,,,"on");
   Opcode.DumpCode(0,nCode);
The output is as follows.


Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
    17 |       1.5 |           |                      | ARG    | String
    19 |           |           |                      | EXI    | Function

Opcode.ExpandCode Method

gmSL: int ExpandCode(int iStart,int nCode,int nExpand);


<Method id="ExpandCode"   type="Integer" opcode="SCM.Opcode_ExpandCode" >
   <Argument id="iStart"  type="Integer" status="ByVal" />
   <Argument id="nCode"   type="Integer" status="ByVal" />
   <Argument id="nExpand" type="Integer" status="ByVal" />
</Method>
The method Opcode.ExpandCode expands a sequence of byte codes from the global code vector used to contain the compiled code associated with the various components. The parameter iStart is the starting offset of the code to be expanded. The parameter nCode is the overall length of the stored code. The parameter nExpand is the number of bytes to be expanded by. The method returns the new overall length of the code storage area.

The following shows a code block containing a method call in the context of an argument the expects a string type. A CNV.ToString is inserted to ensure that the correct type is present. Before this can be done, the code has to be expanded by the size of the CNV operation.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   Opcode.SetLength(nCode);
   Execute.Output(,,,"on");
   Write.Line("Code Dump before inserting CNV");
   Opcode.DumpCode(0,nCode);
   nCode = Opcode.ExpandCode(12,nCode,sizeof(OPC.CNV));
   Opcode.SetLength(nCode);
   Opcode.SetOperation(codptr,12,OPC.CNV,OPC.CNV.ToString);
   Write.Line("Code Dump after inserting CNV");
   Opcode.DumpCode(0,nCode);
The result shows the code before and after the insertion.


Code Dump before inserting CNV
Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
    17 |       1.5 |           |                      | ARG    | String
    19 |           |           |                      | EXI    | Function
Code Dump after inserting CNV
Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |       1.5 | String               | CNV    | ToString
    14 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
    19 |       1.5 |           |                      | ARG    | String
    21 |           |           |                      | EXI    | Function

Opcode.FindArgumentEnd Method

gmSL: int FindArgumentEnd(tCodeBlock userCode,int iStart,int iEnd);


Method id="FindArgumentEnd" type="Integer" opcode="SCM_Opcode_FindArgumentEnd" >
  <Argument id="userCode" type="tCodeBlock" status="ByVal" />
  <Argument id="iStart"  type="Integer" status="ByVal" />
  <Argument id="iEnd"  type="Integer" status="ByVal" />
/Method>
The method Opcode.FindArgumentEnd finds the end of the code associated with a given argument within a larger code block. The parameter userCode is the code block that contains the argument code. The parameter iStart is the offset of the start of the argument code, and the parameter iEnd is the offset of the end of the search region. This is normally simply set to the overall length of the code block. Note that argument code always begins with a LEV operation which specifies the nesting level of the argument code, and ends with an ARG which specifies the type of the parameter that is to receive the argument. The end of the argument code is the offset of the first code following the ARG operation.

The following example reads the code associated with a method in the FmStocks sample code, does a code dump, and then looks for arguments and displays their ending offsets.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;
int        icode;
int        iEnd;
int        opcd;
int        subc;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   Opcode.SetLength(nCode);
   Execute.Output(,,,"on");
   Opcode.DumpCode(0,nCode);
   for(icode = 0; icode >= 0; icode = Opcode.GetNext(codptr,icode,nCode))
   {
      opcd = Opcode.GetOperation(codptr,icode,subc);
      if(opcd == OPC.LEV)
      {
         iEnd = Opcode.FindArgumentEnd(codptr,icode,nCode);
         Write.Line("The argument starting at " + icode + " ends at " + iEnd);
      }
   }
The result shows the end as being 19 which is the offset of the EXI code that immediately follows the ARG.


Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
    17 |       1.5 |           |                      | ARG    | String
    19 |           |           |                      | EXI    | Function
The argument starting at 3 ends at 19

Opcode.GetArgumentType Method

gmSL: int GetArgumentType(int subRoot,int icode,int lcode);


<Method id="GetArgumentType" type="Integer" opcode="SCM.Opcode_GetArgumentType" >
   <Argument id="subRoot"  type="Integer" status="ByVal" />
   <Argument id="icode"   type="Integer" status="ByVal" />
   <Argument id="lcode" type="Integer" status="ByVal" />
</Method>
The method Opcode.GetArgumentType evaluates the binary type of a code subsegment within the global code vector used to contain the compiled code associated with the various components. The parameter subRoot is the root offset of the component to which the code belongs. The parameter icode is the offset of the start of the code subsegment whose binary type is needed, This may or may not be an actual argument description. The parameter lcode defines the end of the subsegment. If it is nonzero, then it simply specifies the length of the subsegment. If it is zero, and the opcode at the start is LEV, then the entire argument code is evaluated. This is the typical use of this method. If lcode is zero and the starting opcode is not LEV, then only that one opcode is evaluated.

The following example reads the code associated with a method in the FmStocks sample code, does a code dump, and then looks for arguments and displays their binary types.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;
int        icode;
int        itype;
int        opcd;
int        subc;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   Opcode.SetLength(nCode);
   Execute.Output(,,,"on");
   Opcode.DumpCode(0,nCode);
   for(icode = 0; icode >= 0; icode = Opcode.GetNext(codptr,icode,nCode))
   {
      opcd = Opcode.GetOperation(codptr,icode,subc);
      if(opcd == OPC.LEV)
      {
         iType = Opcode.GetArgumentType(subRoot,icode,0);
         Write.Line("The argument starting at " + icode + " has type " + Opcode.GetSubcodeLabel(OPC.TYP,iType));
      }
   }
The result shows that the type of the argument to the return, which is the type of the subprogram GetVersionNumber is String which is correct despite the wording of the identifier.


Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
    17 |       1.5 |           |                      | ARG    | String
    19 |           |           |                      | EXI    | Function
The argument starting at 3 has type String

Opcode.GetCode Method

gmSL: tCodeBlock GetCode();


<Method id="GetCode" type="tCodeBlock" opcode="SCM_Opcode_GetCode" />
The method Opcode.GetCode returns the tCodeBlock handle to the global vector used to contain the compiled code associated with the various components. This handle is needed when methods need to explicitly access this code block.

For example the following code uses this method so that it can read a block of compiled code into it and then report on its length.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   System.LogMessage("The method FMStocks_DB.Version.Version has " + nCode + " Bytes of analysed code");
The output shows that this method end up with 21 bytes of code.


The method FMStocks_DB.Version.Version has 21 Bytes of analysed code

Opcode.GetInfo Method

gmSL: tOpcInfo GetInfo(int opcValue);


<Method id="GetInfo" type="tOpcInfo" opcode="SCM.Opcode_GetInfo" >
   <Argument id="opcValue"  type="Integer" status="ByVal" />
</Method>
The method Opcode.GetInfo returns a handle to a tOpcInfo attribute class instance in the language file. The instances of this class contain the basic information for particular opcodes. The parameter opcValue contains the code value for the opcode for which information is desired. The best way to specify this code value is by use of the OPC intrinsic enumeration. See the description of the tOpcInfo attribute class for details.

The following code accesses the information for three different opcodes and displays the attribute values.


tOpcInfo opcInfo;
int      pass;

   for(pass = 0; pass < 3; pass = pass + 1)
   {
      if(pass == 0) opcInfo = Opcode.GetInfo(OPC.REM);
      else if (pass == 1) opcInfo = Opcode.GetInfo(OPC.ADD);
      else opcInfo = Opcode.GetInfo(OPC.USC);
      System.LogMessage("The Opcode " + opcInfo.ident + " has value " + opcInfo.value);
      System.LogMessage("   length    = " + opcInfo.length);
      System.LogMessage("   ident     = " + opcInfo.ident);
      System.LogMessage("   value     = " + opcInfo.value);
      System.LogMessage("   type      = " + Symbol.NamedEntryLabel("opcTypes",opcInfo.type));
      System.LogMessage("   role      = " + Symbol.NamedEntryLabel("opcRoles",opcInfo.role));
      System.LogMessage("   subcodes  = " + opcInfo.subcodes);
      System.LogMessage("   sublist   = " + opcInfo.sublist);
      System.LogMessage("   interface = " + opcInfo.interface);
   }
The output is as follows.


The Opcode REM has value 3
   length    = 17
   ident     = REM
   value     = 3
   type      = StringAddr
   role      = Comment
   subcodes  = 0
   sublist   = 0
   interface = 0
The Opcode ADD has value 13
   length    = 17
   ident     = ADD
   value     = 13
   type      = Arithmetic
   role      = Binary
   subcodes  = 2
   sublist   = 3855
   interface = 0
The Opcode USC has value 170
   length    = 17
   ident     = USC
   value     = 170
   type      = DoubleByte
   role      = Clsref
   subcodes  = 128
   sublist   = 58475
   interface = 319790

Opcode.GetLength Method

gmSL: int GetLength();


<Method id="GetLength" type="Integer" opcode="SCM_Opcode_GetLength" />
The method Opcode.GetLength sets the overall length of intermediate code stored in the global code storage area. This value is needed by other methods that access this code storage area to ensure that they do not exceed its bounds.

The following code shows this length value before and after it is set.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   System.LogMessage("Code length before set is " + Opcode.GetLength());
   Opcode.SetLength(nCode);
   System.LogMessage("Code length after set is " + Opcode.GetLength());
The result shows the initial value of zero followed by the value as set.


Code length before set is 0
Code length after set is 21

Opcode.GetNext Method

gmSL:int GetNext(tCodeBlock codptr,int icode,int nCode)


<Method id="GetNext" type="Integer" opcode="SCM_Opcode_GetNext" >
   <Argument id="codptr" type="Object" status="ByVal" />
   <Argument id="icode"  type="Integer" status="ByVal" />
   <Argument id="nCode"  type="Integer" status="ByVal" />
</Method>
The method Opcode.GetNext service returns the offset of the start of the next operation code in the specified code storage area following the operation code offset specified. This method uses the operation type codes specified in the OPCODES section of the meta language description file to move through the code. The opcTypes codes used are as follows:

OpcTypes len Meaning
Arithmetic 2 is an arithmetic operation with hierarchy
SingleByte 1 consists of a single byte operation code
DoubleByte 2 Consists of a 2-byte operation code
ShortValue 3 Operation code followed by 2-byte value
SymbolAddr 5 Operation code followed by 4-byte symbol address
StringAddr 5 Operation code followed by 4-byte string address
OpcodeAddr 5 Operation code followed by 4-byte pcode offset
BinaryType 2 2-byte operation specifying binary type
LibraryAdr 5 Operation code followed by 4-byte library offset
LibPattern 5 Operation code followed by 4-byte pattern offset
ObjectType 5 Operation code followed by 4-byte object type code

The argument pcode controls the byte storage area that contains the intermediate code being traversed. It is typically obtained via the method Opcode_GetCode. The argument opadr contains offset in the byte storage area of the current operation. The argument codeEnd contains the length of the code being traversed. This method returns the offset of the following operation code if there is a following code. If the current operation is the last one, then a minus one is returned.


<gmBasic> <!-- Unit test for Opcode_GetOperation() and Opcode_GetNext() Methods -->
<Storage Action="Open" Identifier="..\fmstocks\fmstock1.vbi" />
<Output Status="New" Filename="demo046.out" />
<gmsl>
   int         subRoot;
   tVbSub      subInfo;
   tCodeBlock  codptr;
   int         nCode;
   int         icode;
   int         opc;
   int         subc;
   tOpcInfo    opcInfo;

   subRoot = Symbol_FindIdentifier("FMStocks_DB.Version.Version", 0);
   subInfo = Store_GetVector(subRoot);
   codptr = Opcode_GetCode();
   nCode = Store_ReadInfo(codptr,subInfo.anaCodeStart);
   Write_Line("The method FMStocks_DB.Version.Version has " + nCode + " Bytes of analysed code as follows:");
   icode = 0;
   while(icode >= 0)
   {
      opc = Opcode_GetOperation(codptr,icode,subc);
      opcInfo = Opcode_GetInfo(opc);
      Write_Line("codptr(" + icode + ") " + opcInfo.ident + " has subcode " + subc);
      icode = Opcode_GetNext(codptr,icode,nCode);
   }
</gmsl>
<Storage Action="Close" />
</gmBasic>
The output of this code is as follows.


The method FMStocks_DB.Version.Version has 21 Bytes of analysed code as follows:
codptr(0) NEW has subcode 30
codptr(3) LEV has subcode 0
codptr(5) LDA has subcode 252074
codptr(10) CUF has subcode 0
codptr(12) REF has subcode 252074
codptr(17) ARG has subcode 8
codptr(19) EXI has subcode 2


Opcode.GetOperation Method

gmSL:

Opcode.GetSubcodeLabel Method

gmSL:

Opcode.SetLength Method

gmSL: void SetLength(int nCode);


<Method id="SetLength" type="void" opcode="SCM.Opcode_SetLength" >
   <Argument id="nCode" type="Integer" status="ByVal" />
</Method>
The method Opcode.SetLength sets the overall length of intermediate code stored in the global code storage area. This value is needed by other methods that access this code storage area to ensure that they do not exceed its bounds. The parameter nCode specifies the length in bytes.

One of the methods that requires that the length be set is Opcode.DumpCode. The following code reads the analysed code for a short method from the fmstock1 information file and then attempts to dump its content.


int        subRoot;
tVbSub     subInfo;
tCodeBlock codptr;
int        nCode;

   Execute.Storage("Open","\fkgtest\fmstocks\fmstock1");
   subRoot = Symbol.FindIdentifier("FMStocks_DB.Version.Version");
   subInfo = Store.GetVector(subRoot);
   codptr = Opcode.GetCode();
   nCode = Store.ReadInfo(codptr,subInfo.anaCodeStart);
   Execute.Output(,,,"on");
   Write.Line("Code Dump before setting length");
   Opcode.DumpCode(0,nCode);
   Opcode.SetLength(nCode);
   Write.Line("Code Dump after setting length");
   Opcode.DumpCode(0,nCode);
As the following output shows the initial code dump fails because that method does not have the correct code length set.


Code Dump before setting length
Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
Code Dump after setting length
Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
------ |  -------- |  -------- | -------------        | ------ | -----------------------------
     0 |           |           |                      | NEW    | 30
     3 |           |           |                      | LEV    | Nest0
     5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
    10 |       1.5 |       1.5 | String               | CUF    | Args0
    12 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
    17 |       1.5 |           |                      | ARG    | String
    19 |           |           |                      | EXI    | Function

Opcode.SetOperation Method

gmSL:
Table of Contents