Skip to content

gmslOpcodeClass

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.


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:

cmtTypeDescription
DeleteRemoves the statement from the authored output
CommentOutPrecedes the statement with a comment identifying the causing component
and then comments the statement out.
DeprecatedPrecedes the statement with a Deprecated comment identifying the causing component
and then does not comment the statement out.
NotImplementedPrecedes the statement with a Not Implemented comment identifying the
causing component, and then comments the statement out.
MustCorrectPrecedes 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;

/>


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

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

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

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

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

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

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

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

**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:

OpcTypeslen Meaning
Arithmetic2 is an arithmetic operation with hierarchy
SingleByte1 consists of a single byte operation code
DoubleByte2 Consists of a 2-byte operation code
ShortValue3 Operation code followed by 2-byte value
SymbolAddr5 Operation code followed by 4-byte symbol address
StringAddr5 Operation code followed by 4-byte string address
OpcodeAddr5 Operation code followed by 4-byte pcode offset
BinaryType2 2-byte operation specifying binary type
LibraryAdr5 Operation code followed by 4-byte library offset
LibPattern5 Operation code followed by 4-byte pattern offset
ObjectType5 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

gmSL:


gmSL:


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

gmSL: