gmslStoreClass
- Mark Juras
Owned by Mark Juras
The Store Service Class
The Store Service Class contains the methods needed to manage data organized into hierarchical fields and records. When dealing with language processing, program storage must be viewed as containing a wide variety of different structures; indexes, lists, fixed records, long unformatted records, variable length strings, and so on. In addition, it may be purely memory bound or it may be stored in a persistent file system. The role of this service class is to provide a single interface for storing and retrieving information. It is sufficiently robust to allow for the management of large files in the multiple gigabyte size range, while it is still efficient for use by purely memory bound applications. Physically, a storage area consists of a series of relatively large fixed sized blocks whose boundaries cannot be crossed without software support. It is this unalterable property of persistent storage that must be dealt with explicitly if an efficient information management system is desired. The manner in which these blocks are created, maintained, and accessed is determined when the storage area is created or opened. At a logical level the storage area is viewed as consisting of a simple sequence of binary words -- long memory. All information is stored on a word boundary and all offsets are expressed in terms of words. The following methods are in the Store Service class.Store_AddGlobal Method
gmSL: int Store_AddGlobal(int root, int global)gmVB: Function Store_AddGlobal(ByVal root As Integer, ByVal global As Integer) As Integer
gmNI: int Store_AddGlobal(int root, int global)
The method Store_AddGlobal adds the root offset of a component, indexed by its parent to a global index maintained within the storage area. This entry declares that the child component has global scope if and only if the parent component is an active reference. The integer parameter root is the root offset of a component that may have global scope and the integer parameter global is the offset of the parent external library or class whose active reference status triggers the global scope. The gmBasic symbol table has a tree structure. The symbols within the tree need be unique along one branch only. As a result of this there can be many duplicates in the symbol table. A critical operation that must be performed by the compiler is to search the symbol tree for the correct occurrence of an identifier given the current context. This turns out to be a very difficult problem. The simplest approach is to go through each branch on the symbol table in current scope order and see if the identifier can be found on that branch. This approach would use the method StoreFindVector() to make this determination. Though this method is highly optimized and designed to be largely independent of the size of the symbol table, this approach degrades as the symbol table expands in direct proportion to the number of active branches in the tree. This expansion is made even worse because symbols that are relatively low in the symbol tree can be referenced directly, so the tool must be constantly traversing these low level branches for identifiers. An alternative approach is to pre search the symbol tree so that questions like "Does the library X expose a public symbol Y in context Z" can be asked directly without requiring a sequential search. Internally a sorted index structure is used. The phrase "component Y is posted to host X" means that a index-value pair is added to the identifier of Y whose index is the root offset of the host component X and whose value is the root offset of the base component Y. At compile time when a given identifier is encountered the compiler can then very quickly move through the active host libraries in their precedence order and ask the question "Is this identifier present as a global within the scope of this host". This method adds a global - root to the sorted index structure associated with the identifier of the root. As an example consider the VB6 shared module problem. In Vb6 only class files, not module files, are exposed in libraries; consequently, when multiple code projects use the same module file each project loads its own copy of that file. In moving these code projects to .NET there is no problem with exposing the static classes form by modules within a library; therefore, a common migration approach is to identify these shared source files, to include them in only one of the source files, and to reference them as external components in the other source files. The identification of the shared files is performed via the gmPL SharedFile utility statement. The problem with referencing the module components as external components is that module components have global scope; while external components do not. Changing that status is precisely what the method Store_AddGlobal does. The following code is a copy of the SharedFile_MakeModuleGlobalgmSL implemented method in the language file that actually performs this operation. It has been changed to display a log message describing the pairs that it is adding.
<gmSL namespace="RuntimeDll" class="SharedFile" dialect="gms"> void SharedFile_MakeModuleGlobal(int parent, int globalHost) { int childRoot; childRoot = Store_GetFirst(parent); while(childRoot) { Store_AddGlobal(childRoot,-globalHost); Write_LogMessage("Store_AddGlobal(" + Symbol_FullName(childRoot,-1) + ", " + Symbol_FullName(globalHost,-1) + ")" ); childRoot = Store_GetNext(childRoot); } } </gmsl>
Store_AddGlobal(SharedHost.modSharedModule.myClass, SharedHostDLL.dll.SharedHost) Store_AddGlobal(SharedHost.modSharedModule.myAsnewClass, SharedHostDLL.dll.SharedHost) Store_AddGlobal(SharedHost.modSharedModule.pubFunc, SharedHostDLL.dll.SharedHost) Store_AddGlobal(SharedHost.modSharedModule.arrFunc, SharedHostDLL.dll.SharedHost) Store_AddGlobal(SharedHost.modSharedModule.useForm, SharedHostDLL.dll.SharedHost) Store_AddGlobal(SharedHost.Form1.whatForm, SharedHostDLL.dll.SharedHost)
Store_Close Method
gmSL: void Store_Close()gmVB: Sub Store_Close()
gmNI: void StoreClose(void);
The Store_Close method closes the current storage area if that area is currently open. It does not change the currently selected storage area. For example the following command code can be used
<gmsl>Store_Close()</gmsl>
<Storage Action="Close" />
Store_Create Method
gmSL: int Store_Create(string filename, int iUnit)gmVB: Function Store_Create(ByVal filename As String, ByVal iUnit As Integer) As Integer
gmNI: int Store_Create(char* filename, int iUnit);
The Store_Create method selects the indicated storage unit and initializes it as a new empty area whose content will be optionally paged into a specified file. Any information currently in the storage are will be lost. Until another storage unit is selected via one of the three methods -- Store_Select, Store_Create, or Store_Open all storage requests will be directed to this currently selected storage unit. If the storage is to be paged into a file, then the filename parameter contains the name of that file (without an extension). If the storage is to be purely memory-based, this parameter should be NULL. The integer iUnit parameter is the number of the storage unit to be used. This must be a value between 1 and 16. Note that gmBasic uses units one through 5 internally. There is an enumeration StorageUnit that defines these units. When the intent is to refer to one of these gmBasi units then an entry from this enumeration should be used. They are as follows:
Entry | Description of content |
USER | This is the storage area that is managed via the gmPL Storage statement. |
LANG | This is the storage area used to contain the language file controlling the translation. |
TEMP | This is a temporary storage area used within specific tasks. It is never left open, between tasks. |
INFO | This is a temporary storage area used by the gmPL utility statements like IncludeOrder to contain information being gathered, |
CLIENT | This is the storage area used to contain the GlobalSettings information. |
For most applications the USER storage area should be used, as many of the authoring service methods in particular assume that they are using this area. If all goes well this method returns a one (true). If there is a problem creating the storage unit, a zero (false) is returned. As an example the following command mode code
<pBasic> <gmsl> if(Select.UseLocalMemory) { Write_LogMessage("Using Local Memory") Store_Create(NULL, StorageUnit.USER) } else { Write_LogMessage("Using FmStock1.vbi") Store_Create("FmStock1", StorageUnit.USER) } </gmsl> <Select DevEnv="VS2010" /> ...
<pBasic> <Storage Action="Create" Identifier="FmStock1" /> <Select DevEnv="VS2010" /> ...
Store_DeltaVector Method
gmSL: Handle Store_DeltaVector(int root)gmVB: Function Store_DeltaVector(ByVal root As Integer) As Handle
gmNI: void* Store_DeltaVector(int root);
The Store_DeltaVector method gets a handle to a component information vector in the current storage area that it intends to change. In particular, this method gives read/write access to the information vector associated with the component whose root offset is specified via the integer root parameter. If there is any question about the type of component being accessed or about the validity of the root offset value, use the Store_GetObjectType method first (see the discussion there). Any changes made to the information will generally not be saved. Use the Store_GetVector method if the vector is simply to be read. The actual handle is optimized to make the retrieval of the component information as efficient as possible. As such it should only be used locally and should be refreshed after any other storage intensive operations have been performed. To use Store_DeltaVector the storage area being accessed have been opened using a CREATE or APPEND action. The method returns a semi-permanent handle to the information vector. It is up to the user to ensure that the storage object type of the component is compatible with the structure type of the variable that is receiving the handle. The following command code
<gmBasic> <gmSL> int varRoot; tVariable varInfo; Store_Open("fmstock1.vbi", StorageUnit.USER, True) varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0) Write_OpenFile(1, "Demo021.out", True, OutStyle.Text); AuditVbi.LocVariable(varRoot); varInfo = Store_DeltaVector(varRoot) varInfo.Private = False varInfo.Public = True varInfo.migComment = "Changed status to Public" AuditVbi.LocVariable(varRoot); Store_Close(); </gmSL> gmBasic>
Detailed Description of Variable FMStocks_DB.TxNew.m_dbh with root address 254799: Property | Content -------- | ------- Migrate Status | Referenced Migrate Flags | none Status Flags | Private | FixedType | Changed VB_Name | TxNew:11307 Binary Type | User Reference | DBHelper:11875 Dimensions | 0 Number References | 3 Detailed Description of Variable FMStocks_DB.TxNew.m_dbh with root address 254799: Property | Content -------- | ------- Migrate Status | Referenced Migrate Flags | none Migrate Comment | Changed status to Public:360422 Status Flags | Public | FixedType | Changed VB_Name | TxNew:11307 Binary Type | User Reference | DBHelper:11875 Dimensions | 0 Number References | 3
Store_FindFirstChild Method
gmSL: int Store_FindFirstChild(int levels[], int iRoot)gmVB: Function Store_FindFirstChild(ByVal levels As Integer[], ByVal iRoot As Integer) As Integer
gmNI: int Store_FindFirstChild(int* levels, int iRoot);
The Store_FindFirst method initiates a depth first or preorder search of the symbol tree. If the iRoot parameter is non-zero, then the node with this offset is visited first. If the iRoot parameter is zero, then the first node in the tree is visited first. See the description of the StoreFindNext for information on how the next node visited is determined. The method returns the offset of the first node visited or zero, if the symbol tree is empty. The integer vector levels is used internally by the search algorithm. It must have at least two plus the maximum levels in the tree entries. Normal code structures have no more than four levels -- project, class, subprogram, and local variable; however, controls defined via forms can often be nested more deeply. A size of 20 is always safe. As an example consider the following code
<gmBasic> <gmSL> int iRoot; int levels[19]; tVariable varInfo; Store_Open("fmstock1.vbi", StorageUnit.USER, False) Write_OpenFile(1, "Demo023.out", True, OutStyle.Text); Write_Line("Listing of ByRef arguments in " + Store_GetDataName() + " Storage Area") iRoot = Store_FindFirstChild(levels,0) while(iRoot != 0) { if(Store_GetObjectType(iRoot) == ObjectType.VARIABLE) { varInfo = Store_GetVector(iRoot); if(VarInfo.ByRef) { Write_Integer(levels(0) - 1, 3) Write_Integer(iRoot,8) Write_Text(" " + Symbol_FullName(iRoot,-1)) Write_Record() } } iRoot = Store_FindNextChild(levels) } Store_Close() </gmSL> </gmBasic>
Store_FindLibVector Method
gmSL: int Store_FindLibVector(string identifier, int parent, int iCall)gmVB: Function Store_FindLibVector(ByVal Identifier As String, ByVal parent As Integer, ByVal iCall As Integer) As Integer
gmNI: int Store_FindLibVector(char* identifier, int parent, int iCall)
The method Store_FindLibVector determines if an identifier is a member of a component in an external library. The symbol structure generated by Visual Basic source code is a fairly straight-forward tree structure. The external libraries, however, like MSADO have very complicated structures using interrelated coclasses and interfaces with sometimes intertwined parent-child relationships. This method searches an external library component whose root offset is specified via the integer parameter parent to determine if it has a member specified by the string attribute identifier. If so, it returns the root offset of that member. If not, it returns 0. The integer parameter iCall is a tracing value used to identify the particular call within the compilation process. Its value is arbitrary and is usually simply set to zero. Here is a sample search
<gmBasic> <!-- Unit test for Store_FindLibVector --> <Storage Action="Open" Identifier="fmstock1.vbi" /> <Output Status="New" Filename="demo042.out" /> <gmsl> int coClassRoot; int iRoot; coClassRoot = Symbol_FindIdentifier("ADODB.RecordSet", 0); iRoot = Store_FindLibVector("DataSource",coClassRoot,0); Write_Line("The component " + Symbol_FullName(iRoot,-1) + " is in the coClass " + Symbol_FullName(coClassRoot,-1)); </gmsl> <Storage Action="Close" /> </gmBasic>
The component ADODB.Recordset20.DataSource is in the coClass ADODB.Recordset
Store_FindNextChild Method
gmSL: int Store_FindNextChild(int levels[])gmVB: Function Store_FindNextChild(ByVal levels As Integer[]) As Integer
gmNI: int Store_FindNextChild(int* levels);
The Store_FindNextChild method continues a depth first or preorder search of the symbol tree. The levels parameter must have been initialized by the Store_FindFirstChild method. In a depth first search, a visit to a node is followed immediately by visits to all descendants of the node. There is a simple recursive algorithm for describing a depth first search.
void depth_first_tree_search(node v, int level, void* info) { node u; visit(v,level,info); level++; for(each child u of v) depth_first_tree_search(u,level,info); }
<gmBasic> <gmSL> int iRoot; int levels[19]; tVariable varInfo; Store_Open("fmstock1.vbi", StorageUnit.USER, False) Write_OpenFile(1, "Demo023.out", True, OutStyle.Text); Write_Line("Listing of ByRef arguments in " + Store_GetDataName() + " Storage Area") iRoot = Store_FindFirstChild(levels,0) while(iRoot != 0) { if(Store_GetObjectType(iRoot) == ObjectType.VARIABLE) { varInfo = Store_GetVector(iRoot); if(VarInfo.ByRef) { Write_Integer(levels(0) - 1, 3) Write_Integer(iRoot,8) Write_Text(" " + Symbol_FullName(iRoot,-1)) Write_Record() } } iRoot = Store_FindNextChild(levels) } Store_Close() </gmSL> </gmBasic>
Listing of ByRef arguments in fmstock1.vbi Storage Area 4 233070 FMStocks_DB.Helpers.ReportEvent.plpStrings 4 233096 FMStocks_DB.Helpers.ReportEvent.lpRawData 4 248267 FMStocks_DB.Helpers.CopyMemory.hpvDest 4 248353 FMStocks_DB.Helpers.CopyMemory.hpvSource 4 248782 FMStocks_DB.Helpers.GetComputerNameAPI.nSize 4 254306 FMStocks_DB.Account.VerifyUser.AccountID 4 254337 FMStocks_DB.Account.VerifyUser.FullName 4 254601 FMStocks_DB.Account.GetAccountInfo.FullName 4 254627 FMStocks_DB.Account.GetAccountInfo.Email 4 254652 FMStocks_DB.Account.GetAccountInfo.Balance 4 260018 FMStocks_DB.DBHelper.collectParams.cmd
Store_FindRoot Method
gmSL: int Store_FindRoot(string identifier)gmVB: Function Store_FindRoot(ByVal identifier As String) As Integer
gmNI: int Store_FindRoot(char* identifier);
The Store_FindRoot method finds an identified symbol root. This method searches the base index of the symbol table for a specified identifier and returns its root. This root is unique for each identifier. If no symbol exists in the base index with the specified identifier then a zero is returned. The gmBasic symbol table has a tree structure. The symbols within the tree need be unique along one branch only. As a result of this, there can be many duplicates in the symbol table. The roles of the symbol retrieval methods are to search the symbol tree for the correct occurrence of an identifier given a current parent context -- i.e., the typical question asked is "Does a given parent node have a child with the specified identifier". This question must be asked for every parent in the current scope in scope order. This turns out to be a very difficult problem. The simplest approach is to go through each branch on the symbol table in current scope order and see if the identifier can be found on that branch. But there are many branches, especially if a global symbol is being sought, so this process can require many individual searches. To make matters even worse the required searches are string searches which require time consuming string comparisons as opposed to simple integer comparisons. The approach used by gmBasic separates the search into two parts. First there is a base index in the symbol table that contains one entry for each unique symbol defined anywhere in the current code set. This is the only index that uses string keys. Associated with this entry for each symbol there is a secondary index that uses integer parent node roots as the key and the root of the actual information vector for the identifier in its form within that parent. The example here shows how it can be used to determine if a given identifier is defined within a storage area.
<gmBasic> <gmSL> Store_Open("fmstock1.vbi", StorageUnit.USER, False) if(Store_FindRoot("hEventLog")) Write_LogMessage("There is a symbol hEventLog defined."); else Write_LogMessage("There is no symbol hEventLog defined."); if(Store_FindRoot("dwBytes")) Write_LogMessage("There is a symbol dwBytes defined."); else Write_LogMessage("There is no symbol dwBytes defined."); if(Store_FindRoot("byteCount")) Write_LogMessage("There is a symbol byteCount defined."); else Write_LogMessage("There is no symbol byteCount defined."); Store_Close() </gmSL> </gmBasic>
There is a symbol hEventLog defined. There is a symbol dwBytes defined. There is no symbol byteCount defined.
Store_FindVector Method
gmSL: int Store_FindVector(string identifier, int parent)gmVB: Function Store_FindVector(ByVal identifier As String, ByVal parent As Integer) As Integer
gmNI: int Store_FindVector(char* identifier, int parent);
The method Store_FindVector searches a single branch within the hierarchical symbol table for a for a component with the identifier specified by the string identifier parameter. If the integer parent parameter is zero, then the root branch of the tree is searched; else the branch dominated by the parent is searched. If a component with the specified identifier is found within the specified branch, then the root offset of that component is returned. If no component can be found in the branch, then a zero is returned. As an example here is an explicit search down a symbol tree for a subprogram argument
<gmBasic> <gmSL> int project; int module; int method; int argument; Store_Open("fmstock1.vbi", StorageUnit.USER, False) project = Store_FindVector("C:\gmSrc\fmstocks\vb6\FMStocks_DB\FMStocks_DB.vbp", 0); module = Store_FindVector("C:\gmSrc\fmstocks\vb6\FMStocks_DB\Helpers.bas", project); method = Store_FindVector("ReportEvent", module); argument = Store_FindVector("plpStrings", method); if(argument) Write_LogMessage("The argument plpStrings has root offset " + argument); else Write_LogMessage("The argument plpStrings was not found."); Store_Close(); </gmSL> </gmBasic>
The argument plpStrings has root offset 233070
Store_FirstReference Method
gmSL: tReference Store_FirstReference(int symbolRoot, int[] PostSet)gmVB: Function Store_FirstReference(ByVal symbolRoot As Integer, ByVal PostSet As Integer[]) As tReference
gmNI: int* Store_FirstReference(int symbolRoot, tPosting* PostSet);
The method Store_FirstReference finds the first reference made to a symbol in the compiled code. During compilation symbol references are stored in a reference record set sorted in ascending order by the root of the symbol being referenced. The reference record, type tReference, has the following fields:
Field | Description of content |
MakesRef | The root of the component containing the reference |
BeingRefd | The root of the component being referenced |
RecNumber | The text record number in the host file of the reference |
CodeOffset | The offset in the compiled code of the referencing code |
HostFile | The root of the information file containing the subprogram |
Terminal | If the reference treated the component as terminal then one, else zero |
What Store_FirstReference actually returns is the first record whose BeingRefd field is greater than or equal to the value of the integer parameter symbolRoot. The integer vector PostSet is a private storage vector used by the retrieval methods. It must contain at least four entries. Note that the method Store_NextReference, which is used to iterate forward in the record set must be passed this same storage vector. The simplest use of Store_FirstReference is to determine if a symbol is used at all, as shown here
<gmBasic> <gmSL> int iRoot; int postSet[4]; int levels[19]; tReference refInfo; tVariable varInfo; Store_Open("fmstock1.vbi", StorageUnit.USER, False) Write_OpenFile(1, "Demo026.out", True, OutStyle.Text); iRoot = Store_FindFirstChild(levels,0) while(iRoot != 0) { if(Store_GetObjectType(iRoot) == ObjectType.VARIABLE) { varInfo = Store_GetVector(iRoot); if(VarInfo.ByRef) { refInfo = Store_FirstReference(iRoot,postSet); if(refInfo.BeingRefd == iRoot) Write_Line("The ByRef argument " + Symbol_FullName(iRoot,-1) + " is Used") else Write_Line("The ByRef argument " + Symbol_FullName(iRoot,-1) + " is Not Used") } } iRoot = Store_FindNextChild(levels) } Store_Close() </gmSL> </gmBasic>
The ByRef argument FMStocks_DB.Helpers.ReportEvent.plpStrings is Not Used The ByRef argument FMStocks_DB.Helpers.ReportEvent.lpRawData is Used The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvDest is Used The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvSource is Used The ByRef argument FMStocks_DB.Helpers.GetComputerNameAPI.nSize is Not Used The ByRef argument FMStocks_DB.Account.VerifyUser.AccountID is Used The ByRef argument FMStocks_DB.Account.VerifyUser.FullName is Used The ByRef argument FMStocks_DB.Account.GetAccountInfo.FullName is Used The ByRef argument FMStocks_DB.Account.GetAccountInfo.Email is Used The ByRef argument FMStocks_DB.Account.GetAccountInfo.Balance is Used The ByRef argument FMStocks_DB.DBHelper.collectParams.cmd is Used
Store_GetDataName Method
gmSL: string Store_GetDataName()gmVB: Function Store_GetDataName() As String
gmNI: char* StoreGetDataName(void);
The method Store_GetDataName gets the name of the current storage area. When a file-based storage area is created or opened its name is stored in the storage control structure. This method returns that name. If the storage area is local memory based, then a null-string is returned. As an example, consider this simple code
<gmBasic> <!-- Unit test for Store_GetDataName --> <Storage Action="Create" Identifier="Demo028.vbi" /> <gmsl> if(Store_GetDataName()) Write_LogMessage("The storage area " + Store_GetDataName() + " has been created.") else Write_LogMessage("The Application is using local memory") </gmsl> <Storage Action="Close" /> </gmBasic>
The storage area Demo028.vbi has been created.
The Application is using local memory
Store_GetFirst Method
gmSL: int Store_GetFirst(int parent)gmVB: Function Store_GetFirst(ByVal parent As Integer) As Integer
gmNI: int Store_GetFirst(int parent);
The method Store_GetFirst returns the root of the first child of a parent component or of the root branch. This is the first component entered for the parent. In general, using this method in conjunction with the Store_GetNext method, will generate the sequence of components stored within some branch of the symbol table in their defined order. The integer parameter parent contains the root offset of the parent component or zero if the first member of the root branch in the symbol table is desired. If there is a first child of the specified component, then this method returns its root offset. If there is no first child, then this method returns a zero. As the following code shows the simplest use of Store_GetFirst is to determine whether a given component has any children or is nonterminal.
<gmBasic> <!-- Unit test for Store_GetFirst --> <gmSL> int iRoot; int levels[19]; Store_Open("fmstock1.vbi", StorageUnit.USER, False) Write_OpenFile(1, "Demo029.out", True, OutStyle.Text); Write_Line("Listing of Class declares with children " + Store_GetDataName() + " Storage Area") iRoot = Store_FindFirstChild(levels,0) while(iRoot != 0) { if(levels(0) == 4 && Store_GetFirst(iRoot) && Store_GetObjectType(iRoot) == ObjectType.Declaration) { Write_Text("The ") Symbol_DisplayInfo(iRoot,DisplayType.OBJECT_TYPE) Write_Line(" " + Symbol_FullName(iRoot,-1) + " Has children") } iRoot = Store_FindNextChild(levels) } Store_Close() </gmSL> </gmBasic>
Listing of Class declares with children fmstock1.vbi Storage Area The Declaration FMStocks_DB.Helpers.RegisterEventSource Has children The Declaration FMStocks_DB.Helpers.DeregisterEventSource Has children The Declaration FMStocks_DB.Helpers.ReportEvent Has children The Declaration FMStocks_DB.Helpers.CopyMemory Has children The Declaration FMStocks_DB.Helpers.GlobalAlloc Has children The Declaration FMStocks_DB.Helpers.GlobalFree Has children The Declaration FMStocks_DB.Helpers.GetComputerNameAPI Has children
Store_GetGlobals Method
gmSL: int Store_GetGlobals(int symbolRoot,int[] globals,int nGlobal)gmVB: Function Store_GetGlobals(ByVal symbolRoot As Integer, ByVal globals As Integer[], ByVal nGlobal As Integer) As Integer
gmNI: int Store_GetGlobals(int symbolRoot,int* globals,int nGlobal);
The method Store_GetGlobals returns the global, root pairs added to a symbol identifier via the Store_AddGlobal method. The description of that method contains a detailed description of these pairs. The integer parameter symbolRoot contains a root offset in the base symbol index as returned by the Store_FindRoot method. The integer vector parameter globals returns the pairs added to the specified symbol, and the integer parameter nGlobal defines the length of the globals vector. No more than nGlobal entries will be returned. The method returns the number of entries returned in the vector. This is two times the number of pairs. The code sample here continues the VB6 shared module problem from the Store_AddGlobal description. It opens a shared module file and retrieves the global information associated with one of the symbols.
<gmBasic> <!-- Unit test for Store_GetGlobals --> <gmSL> int symbolRoot; int globals[32]; int nGlobal; int iGlobal; int iRoot; Store_Open("SharedModule1.vbi", StorageUnit.USER, False) Write_OpenFile(1, "Demo045.out", True, OutStyle.Text); symbolRoot = Store_FindRoot("myClass"); Write_Line("The symbol root for <myClass> is " + symbolRoot); nGlobal = Store_GetGlobals(symbolRoot,globals,32); Write_Line("The global vector for <myClass> contains " + nGlobal + " entries"); iGlobal = 0; while(iGlobal < nGlobal) { iRoot = globals(iGlobal); if(iRoot < 0) iRoot = - iRoot; Write_Text(" Globals(" + iGlobal + ") = " + globals(iGlobal) + " : "); Symbol_DisplayInfo(iRoot,DisplayType.OBJECT_TYPE); Write_Line(" " + Symbol_FullName(iRoot,-1)); iGlobal = iGlobal + 1; } Store_Close(); </gmSL> </gmBasic>
The symbol root for <myClass> is 22987 The global vector for <myClass> contains 2 entries Globals(0) = -3515 : Library SharedHostDLL.dll.SharedHost Globals(1) = 22992 : Lib_Property SharedHost.modSharedModule.myClass
Store_GetHandle Method
gmSL: Handle StoreGetHandle()gmVB: Function Store_GetHandle() As Handle
gmNI: void* Store_GetHandle(void);
The method StoreGetHandle gets the long memory handle of the current storage area. All storage areas are maintained within a long memory area. Though most access needs for that memory area are provided internally, it is occasionally necessary to use one of the specialized stream access methods directly. If there is a current storage area open, then the handle of its long memory provider is returned; else a NULL is returned. Text streams are one of the specialized streams that require a long memory handle. The following code shows how it is obtained and used.
<gmBasic> <!-- Unit test for Store_GetHandle --> <gmSL> int textRoot; Handle textStream; Store_Create(NULL, StorageUnit.USER); Write_OpenFile(1, "Demo030.out", True, OutStyle.Text); Write_OpenStream(); #TextStart Hello World This is a test of Store_GetHandle() GoodBye World #TextEnd textRoot = Write_CloseStream(); textStream = Text_Open(Store_GetHandle(),textRoot); Write_TextStream(textStream,0,0); Text_Close(textStream); Store_Close(); </gmSL> </gmBasic>
Hello World This is a test of Store_GetHandle() GoodBye World
Store_GetIdent Method
gmSL: string Store_GetIdent(int root)gmVB: Function Store_GetIdent(ByVal root As Integer) As String
gmNI: char* Store_GetIdent(int root,int* nIdent);
The method Store_GetIdent gets the source identifier of component via its root address. By definition, the "identifier" is the original identifier that was associated with the component when it was first placed in the symbol table by Store_Vector. See the Store_SetName and Store_GetName methods for information about changing how components are assigned alternative target identifiers. As the following code shows the source identifier of a component remains unchanged when it is renamed and is always the identifier used to retrieve it.
<gmBasic> <!-- Unit test for Store_GetIdent and Store_GetName --> <Storage Action="Append" Identifier="fmstock1.vbi" /> <gmsl> int varRoot; varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0); Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot)); Write_LogMessage("The variable name is " + Store_GetName(varRoot)); </gmsl> <Refactor> <Rename Identifier="FMStocks_DB.TxNew.m_dbh" Content="m_databaseHandle" /> </Refactor> <gmsl> int varRoot; varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0); Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot)); Write_LogMessage("The variable name is " + Store_GetName(varRoot)); </gmsl> <Storage Action="Close" /> </gmBasic>
The variable ident is m_dbh The variable name is m_dbh The variable ident is m_dbh The variable name is m_databaseHandle
Store_GetInfo Method
gmSL: int Store_GetInfo(int offset, Handle info)gmVB: Function Store_GetInfo(ByVal offset As Integer, ByVal info As Handle) As Integer
gmNI: int Store_GetInfo(int offset, void* info);
The method Store_GetInfo reads short blocks of information that were initially written using the method Store_String. A physical read is not required, since these short blocks do not cross block boundaries; however, in some instances reading them is needed to avoid downstream memory page faults. The most common use of this method is to read the list of interfaces associated with a class. The handle parameter info is a handle to the area in memory to receive the information read, and the integer parameter offset is the offset of the start of the information to be read. This offset must be a value previously returned by the Store_String method. The sample code mimics an almost identical code used to demonstrate the Store_WriteInfo method. It uses the Store_String to write a matrix of values and then uses this method to read them back in.
<gmBasic> <!-- Unit test for Store_GetInfo --> <gmSL> int levels[5]; int offsets[5]; int iOff; int iLev; int bootCell; Store_Create(NULL, StorageUnit.USER) Write_OpenFile(1, "Demo048.out", True, OutStyle.Text); iOff = 0; while(iOff < 5) { iLev = 0; while(iLev < 5) { levels(iLev) = (iOff+1) * 10 + iLev + 1; iLev = iLev + 1; } offsets(iOff) = Store_String(levels); iOff = iOff + 1; } iOff = 0; while(iOff < 5) { Store_GetInfo(offsets(iOff),levels); iLev = 0; while(iLev < 5) { Write_Integer(levels(iLev),5); iLev = iLev + 1; } Write_Record(); iOff = iOff + 1; } Store_Close(); </gmSL> </gmBasic>
11 12 13 14 15 21 22 23 24 25 31 32 33 34 35 41 42 43 44 45 51 52 53 54 55
Store_GetLength Method
gmSL: int Store_GetLength(int offset)gmVB: Function Store_GetLength(ByVal offset As Integer)
gmNI: int Store_GetLength(int offset);
The method Store_GetLength returns the overall length in words of the current storage area, if the integer parameter offset is zero. If offset is not zero, then the length in bytes of the information stored at the specified offset is returned. Regardless of the value of the parameter offset, if the current storage area is empty, then this method returns 0. Logic of the form If(Store_GetLength(0)) Then, is the standard way of checking if the storage area is empty. Here is a simple example
<gmBasic> <!-- Unit test for Store_GetLength --> <Storage Action="Open" Identifier="fmstock1.vbi" /> <gmsl> int subRoot; tVbSub subInfo; subRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.SetTxType", 0); subInfo = Store_GetVector(subRoot); Write_LogMessage("Length of storage area = " + Store_GetLength(0)); Write_LogMessage("Bytes in compiled code = " + Store_GetLength(subInfo.cmpCodeStart)); Write_LogMessage("Bytes in analysed code = " + Store_GetLength(subInfo.anaCodeStart)); </gmsl> <Storage Action="Close" /> </gmBasic>
Length of storage area = 372736 Bytes in compiled code = 321 Bytes in analysed code = 298
Store_GetName Method
gmSL: string Store_GetName(int root)gmVB: Function Store_GetName(ByVal root As Integer) As String
gmNI: char* Store_GetName(int root,int* nIdent);
The method Store_GetName gets the target identifier of a component via its root address. Every component has two string representations -- its source "identifier" which the original identifier that was associated with the component when it was first placed in the symbol table by Store_Vector and its target "name" that was assigned to it directly via the Store_SetName method. If no name has been assigned to the component, then its name is the same as its identifier. Whenever a reference to a component is authored its name is used, but any retrieval of the component is always done via its identifier. As the following code shows the source identifier of a component remains unchanged when it is renamed and is always the identifier used to retrieve it.
<gmBasic> <!-- Unit test for Store_GetIdent and Store_GetName --> <Storage Action="Append" Identifier="fmstock1.vbi" /> <gmsl> int varRoot; varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0); Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot)); Write_LogMessage("The variable name is " + Store_GetName(varRoot)); </gmsl> <Refactor> <Rename Identifier="FMStocks_DB.TxNew.m_dbh" Content="m_databaseHandle" /> </Refactor> <gmsl> int varRoot; varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0); Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot)); Write_LogMessage("The variable name is " + Store_GetName(varRoot)); </gmsl> <Storage Action="Close" /> </gmBasic>
The variable ident is m_dbh The variable name is m_dbh The variable ident is m_dbh The variable name is m_databaseHandle
Store_GetNext Method
gmSL: int Store_GetNext(int iChild)gmVB: Function Store_GetNext(ByVal iChild As Integer) As Integer
gmNI: int Store_GetNext(int iChild);
The method Store_GetNext returns the next child of a parent component or of the root branch. This is the next component entered for the parent. In general, using this method in conjunction with the Store_GetFirst method, will generate the sequence of components stored within some branch of the symbol table in their defined order. The integer parameter iChild contains the root offset of the current child component as returned by Store_GetFirst or a previous call to Store_GetNext. If there is a next child of the parent of the specified component then this method returns its root offset. If there is no next child then this method returns a zero. The following code shows how to find all of the children of a subprogram
<gmBasic> <!-- Unit test for Store_GetNext --> <gmSL> int subRoot; int iChild; Store_Open("fmstock1.vbi", StorageUnit.USER, False) Write_OpenFile(1, "Demo033.out", True, OutStyle.Text); subRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.SetTxType", 0); Write_Line("The following are the children of " + Symbol_FullName(subRoot,-1)); iChild = Store_GetFirst(subRoot); while(iChild) { Symbol_DisplayInfo(iChild, DisplayType.OBJECT_TYPE); Write_Line(" " + Symbol_FullName(iChild, -1)); iChild = Store_GetNext(iChild); } Store_Close() </gmSL> </gmBasic>
The following are the children of FMStocks_DB.TxNew.SetTxType Variable FMStocks_DB.TxNew.SetTxType.TxID Variable FMStocks_DB.TxNew.SetTxType.TxTypeId StatementLabel FMStocks_DB.TxNew.SetTxType.errorHandler
Store_GetObjectType Method
gmSL: int Store_GetObjectType(int root)gmVB: Function Store_GetObjectType(ByVal root As Integer) As Integer
gmNI: int Store_GetObjectType(int root);
The method Store_GetObjectType returns the symbol table object type code of a component. Whenever a component is entered into the symbol table, it is assigned an integer object type code. Each object type code has associated with it a fixed-length information vector used to describe components of the specified type. In the case of gmBasic the type code is either an entry number from the ObjectType enumeration in the language file, the opcode value of a built-in class if the component is a built-in control, or the root offset of an external class if the object is a control of that external class. The integer parameter root contains the root offset of the component in storage. If root has a value less than or equal to zero, then this method displays the following message and returns a zero.
SYSERR#5001: Object Type requested for invalid root %root%
SYSERR#5002: Object Type requested for invalid root %root%
<gmBasic> <!-- Unit test for Store_GetObjectType --> <gmSL> int iRoot; int levels[10]; int objType; Write_OpenFile(1, "Demo039.out", True, OutStyle.Text); Write_Line("Listing of Components in this gmSL command specification"); Write_ChangeMargin(1); Store_Select(StorageUnit.TEMP); iRoot = Store_FindFirstChild(levels,0); while(iRoot != 0) { objType = Store_GetObjectType(iRoot); Symbol_DisplayInfo(iRoot,DisplayType.OBJECT_TYPE); Write_Line(" " + Symbol_FullName(iRoot,-1) + " has object type code " + objType); iRoot = Store_FindNextChild(levels); } </gmSL> </gmBasic>
Listing of Components in this gmSL command specification ProjectFile temp.slp has object type code 2 ClassFile temp.cls has object type code 5 Subprogram none.temp.Inline has object type code 7 Variable none.temp.Inline.iRoot has object type code 8 Variable none.temp.Inline.levels has object type code 8 Variable none.temp.Inline.objType has object type code 8 Vb_Name none.temp has object type code 17 Vb_Name none has object type code 17 Vb_Name temp.exe has object type code 17
Store_GetParent Method
gmSL: int Store_GetParent(int root)gmVB: Function Store_GetParent(ByVal root As Integer) As Integer
gmNI: int Store_GetParent(int root);
The method Store_GetParent returns the root offset of the parent of a component whose root offset is specified in the parameter. If the component is in the root branch of the symbol table, then a zero is returned. The following code shows how the parent property moves up the symbol table
<gmBasic> <!-- Unit test for Store_GetParent --> <gmSL> int subRoot; int parent; Store_Open("fmstock1.vbi", StorageUnit.USER, False) Write_OpenFile(1, "Demo034.out", True, OutStyle.Text); subRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.SetTxType", 0); Write_Line("The following are the Parents of " + Symbol_FullName(subRoot,-1)); Write_ChangeMargin(1); parent = Store_GetParent(subRoot); while(parent) { Symbol_DisplayInfo(parent, DisplayType.OBJECT_TYPE); Write_Line(" " + Symbol_FullName(parent, -1)); parent = Store_GetParent(parent); } Store_Close() </gmSL> </gmBasic>
The following are the Parents of FMStocks_DB.TxNew.SetTxType ClassFile TxNew.cls ProjectFile FMStocks_DB.vbp
Store_GetString Method
gmSL: string Store_GetString(int offset)gmVB: Function Store_GetString(ByVal offset As Integer) As String
gmNI: char* Store_GetString(int offset,int* nString);
The method StoreGetString returns a string from the current storage area via its offset specified in the parameter. This offset must have been obtained via an earlier call to the method Store_String(). The sample code shows how three store strings can be retrieved via this method.
<gmBasic> <!-- Unit test for Store_String, Store_GetString, Store_SetString --> <gmSL> int offset1; int offset2; int offset3; Store_Create(NULL, StorageUnit.USER); Write_OpenFile(1, "Demo035.out", True, OutStyle.Text); offset1 = Store_String("Hello World"); offset2 = Store_String("This is Fred"); offset3 = Store_String("GoodBye World"); Write_Line("Content of strings before change"); Write_ChangeMargin(1); Write_Line("String1 is: " + Store_GetString(offset1)); Write_Line("String2 is: " + Store_GetString(offset2)); Write_Line("String3 is: " + Store_GetString(offset3)); Write_ChangeMargin(-1); Store_SetString(offset2, "This is Mark"); Write_Line("Content of strings after change"); Write_ChangeMargin(1); Write_Line("String1 is: " + Store_GetString(offset1)); Write_Line("String2 is: " + Store_GetString(offset2)); Write_Line("String3 is: " + Store_GetString(offset3)); Store_Close(); </gmSL> </gmBasic>
Content of strings before change String1 is: Hello World String2 is: This is Fred String3 is: GoodBye World Content of strings after change String1 is: Hello World String2 is: This is Mark String3 is: GoodBye World
Store_GetSymbols Method
gmSL: int Store_GetSymbols(string identifier)gmVB: Function Store_GetSymbols(ByVal identifier As String) As Integer
gmNI: int Store_GetSymbols(char* identifier);
The method Store_GetSymbols returns the root offset of a sequence that contains the root offsets of all symbols whose identifier matches the string parameter identifier. The gmBasic symbol table has a tree structure. The symbols within the tree need be unique along one branch only. As a result of this, there can be many duplicates in the symbol table. The roles of the symbol retrieval methods are to search the symbol tree for the correct occurrence of an identifier given a current parent context -- i.e., the typical question asked is "Does a given parent node have a child with the specified identifier". This question must be asked for every parent in the current scope in scope order. This turns out to be a very difficult problem. The simplest approach is to go through each branch on the symbol table in current scope order and see if the identifier can be found on that branch. But there are many branches, especially if a global symbol is being sought, so this process can require many individual searches. To make matters even worse the required searches are string searches which require time consuming string comparisons as opposed to simple integer comparisons. The approach used by gmBasic separates the search into two parts. First there is a base index in the symbol table that contains one entry for each unique symbol defined anywhere in the current code set. This is the only index that uses string keys. Associated with this entry for each symbol there is a secondary index that uses integer parent node roots as the key and the root of the actual information vector for the identifier in its form within that parent. The method Store_GetSymbols searches the base index for a specified identifier. If found, it returns the root offset of a sequence maintained there that contains the root offsets of the symbols with that name stored in the order that they were defined. If no symbol with the specified name is present, a zero is returned. The following code lists all symbols with the identifier "name" in one of the FmStocks codes.
<gmBasic> <!-- Unit test for Store_GetSymbols --> <Storage Action="Open" Identifier="fmstock1.vbi" /> <Output Status="New" Filename="demo040.out" /> <gmsl> int symbolRoot; Handle symbols; int nChild; int iChild; int iRoot; symbolRoot = Store_GetSymbols("Name"); symbols = Sequence_Open(symbolRoot); nChild = Sequence_GetLength(symbols); Write_Line("Listing of " + nChild + " symbols with the identifier Name"); Write_ChangeMargin(1); iChild = 1; while(iChild <= nChild) { iRoot = Sequence_Access(symbols, iChild); Write_Integer(iChild, 3); Write_Character(" "); Symbol_DisplayInfo(iRoot, DisplayType.OBJECT_TYPE); Write_Line(" " + Symbol_FullName(iRoot ,-1)); iChild = iChild + 1; } Sequence_Close(symbols); </gmsl> <Storage Action="Close" /> </gmBasic>
Listing of 20 symbols with the identifier Name 1 Lib_Property stdole.IFont.Name 2 Lib_Property stdole.Font.Name 3 Lib_Property ADODB.Property.Name 4 Lib_Property ADODB.Command15.Name 5 Lib_Argument ADODB.Command15.CreateParameter.Name 6 Lib_Argument ADODB.Fields20._Append.Name 7 Lib_Argument ADODB.Fields.Append.Name 8 Lib_Property ADODB.Field20.Name 9 Lib_Property ADODB._Parameter.Name 10 Lib_Property ADODB.Field15.Name 11 Lib_Argument COMSVCSLib.ISecurityCertificateColl.Item.name 12 Lib_Argument COMSVCSLib.ISecurityIdentityColl.Item.name 13 Lib_Argument COMSVCSLib.ISecurityCallContext.Item.name 14 Lib_Argument COMSVCSLib.ObjectContext.Item.name 15 Lib_Argument COMSVCSLib.ISharedPropertyGroup.CreateProperty.name 16 Lib_Argument COMSVCSLib.ISharedPropertyGroup.Property.name 17 Lib_Argument COMSVCSLib.ISharedPropertyGroupManager.CreatePropertyGroup.name 18 Lib_Argument COMSVCSLib.ISharedPropertyGroupManager.Group.name 19 Lib_Argument MigrationSupport.Utils.FontChangeName.Name 20 Lib_Property MigrationSupport.Line.Name
Store_GetVector Method
gmSL: Handle Store_GetVector(int root)gmVB: Function Store_GetVector(ByVal root As Integer) As Handle
gmNI: void* Store_GetVector(int root);
The Store_GetVector method gets a handle to a component information vector in the current storage area. In particular, this method gives read only access to the information vector associated with the component whose root offset is specified via the integer root parameter. If there is any question about the type of component being accessed or about the validity of the root offset value, use the Store_GetObjectType method first (see the discussion there). Any changes made to the information will generally not be saved. Use the Store_DeltaVector method if the vector is to be changed. The actual handle is optimized to make the retrieval of the component information as efficient as possible. As such it should only be used locally and should be refreshed after any other storage intensive operations have been performed. The method returns a semi-permanent handle to the information vector. It is up to the user to ensure that the storage object type of the component is compatible with the structure type of the variable that is receiving the handle. The following command code
<gmBasic> <gmSL> int varRoot; tVariable varInfo; Store_Open("fmstock1.vbi", StorageUnit.USER, False) varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0) if(varRoot) { if(Store_GetObjectType(varRoot) == ObjectType.VARIABLE) { varInfo = Store_GetVector(varRoot); if(varInfo.Referenced) { Write_LogMessage("The variable " + Store_GetName(varRoot) + " has " + varInfo.nReference + " references") } } } Store_Close() </gmSL> </gmBasic>
The variable m_dbh has 3 references
Store_NextReference Method
gmSL: tReference Store_NextReference(int[] PostSet)gmVB: Function Store_NextReference(ByVal PostSet As Integer[]) As tReference
gmNI: int* Store_NextReference(tPosting* PostSet);
The method Store_NextReference iterates forward in the references record set once a call to Store_FirstReference has established a starting record. During compilation symbol references are stored in a reference record set sorted in ascending order by the root of the symbol being referenced. The reference record, type tReference, has the following fields:
Field | Description of content |
MakesRef | The root of the component containing the reference |
BeingRefd | The root of the component being referenced |
RecNumber | The text record number in the host file of the reference |
CodeOffset | The offset in the compiled code of the referencing code |
HostFile | The root of the information file containing the subprogram |
Terminal | If the reference treated the component as terminal then one, else zero |
The integer vector PostSet is a private storage vector used by the retrieval methods. It must contain at least four entries and be initialized via a call to Store_FirstReference. The Store_FirstReference method introduced code to find first references the ByRef arguments. The code here expands the inner loop of that code to use Store_NextReference to count the references.
refInfo = Store_FirstReference(iRoot,postSet); if(refInfo.BeingRefd == iRoot) { count = 0; while(refInfo.BeingRefd == iRoot) { count = count + 1; refInfo = Store_NextReference(postSet); } Write_Line("The ByRef argument " + Symbol_FullName(iRoot,-1) + " is Used " + count + " Times") }
The ByRef argument FMStocks_DB.Helpers.ReportEvent.plpStrings is Not Used The ByRef argument FMStocks_DB.Helpers.ReportEvent.lpRawData is Used 1 Times The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvDest is Used 1 Times The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvSource is Used 1 Times The ByRef argument FMStocks_DB.Helpers.GetComputerNameAPI.nSize is Not Used The ByRef argument FMStocks_DB.Account.VerifyUser.AccountID is Used 1 Times The ByRef argument FMStocks_DB.Account.VerifyUser.FullName is Used 1 Times The ByRef argument FMStocks_DB.Account.GetAccountInfo.FullName is Used 1 Times The ByRef argument FMStocks_DB.Account.GetAccountInfo.Email is Used 1 Times The ByRef argument FMStocks_DB.Account.GetAccountInfo.Balance is Used 1 Times The ByRef argument FMStocks_DB.DBHelper.collectParams.cmd is Used 2 Times
Store_Open Method
gmSL: int Store_Open(string fileName, int iUnit, bool append)gmVB: Function Store_Open(ByVal fileName As String, ByVal iUnit As Integer, ByVal append As Boolean) As Integer
gmNI: int Store_Open(char* fileName, int iUnit, int append);
The StoreOpen opens an exiting file into a storage area. In particular it makes the indicated storage area the current one and initializes it with the contents of an existing file. Any information currently in the storage area will be lost. Until another storage area is selected via one of the three methods Store_Select, Store_Create, or Store_Open all storage requests will be directed to this currently selected storage area. The string fileName parameter contains the name of the file (optionally, without an extension) to be opened for the storage area. The integer iUnit parameter is the number of the storage unit to be used. This must be a value between 1 and 16. Note that gmBasic uses units one through 5 internally. There is an enumeration StorageUnit that defines these units. When the intent is to refer to one of these gmBasi units then an entry from this enumeration should be used. They are as follows:
Entry | Description of content |
USER | This is the storage area that is managed via the gmPL Storage statement. |
LANG | This is the storage area used to contain the language file controlling the translation. |
TEMP | This is a temporary storage area used within specific tasks. It is never left open, between tasks. |
INFO | This is a temporary storage area used by the gmPL utility statements like IncludeOrder to contain information being gathered, |
CLIENT | This is the storage area used to contain the GlobalSettings information. |
For most applications the USER storage area should be used, as many of the authoring service methods, in particular, assume that they are using this area. The Boolean append, if True, then the file is opened with read/write access permission. If False, then the file is opened for read only access. If all goes well a one (true) is returned. If there is a problem opening the storage area -- usually because the file does not exist -- a zero (false) is returned. For example the following command code can be used
<gmsl>Store_Open("fmstock1.vbi", StorageUnit.USER, False)</gmsl>
<Storage Action="Open" Identifier="fmstock1.vbi" />
Store_ReadInfo Method
gmSL: int Store_ReadInfo(Handle info, int offset)gmVB: Function Store_ReadInfo(ByVal info As Handle, ByVal offset As Integer) As Integer
gmNI: int Store_ReadInfo(void* info, int offset);
The method Store_ReadInfo reads long blocks of information that were initially written using the method Store_WriteInfo. A physical read is required, since these information vectors may cross block boundaries. The most common use of this method is to read the intermediate code associated with a component, though it may be used for any type of information. The handle parameter info is a handle to the area in memory to receive the information read, and the integer parameter offset is the offset of the start of the information to be read. This offset must be a value previously returned by the Store_WriteInfo method. The following code reads the analysed intermediate code associated with a method in the FmStocks application and dumps its content.
<gmBasic> <!-- Unit test for Store_ReadInfo --> <Storage Action="Open" Identifier="fmstock1.vbi" /> <Output Status="New" Filename="demo041.out" /> <gmsl> int subRoot; tVbSub subInfo; Handle codptr; int nCode; 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:"); Opcode_SetLength(nCode); Opcode_DumpCode(0,nCode); </gmsl> <Storage Action="Close" /> </gmBasic>
The method FMStocks_DB.Version.Version has 21 Bytes of analysed code 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
Store_Reference Method
gmSL: void Store_Reference(tReference reference)gmVB: Sub Store_Reference(ByVal reference As tReference)
gmNI: void Store_Reference(int* reference);
The method Store_Reference stores a reference record in the current storage area. The storage area maintains a sorted list of reference records sorted by the offset of the symbol being referenced. During the compilation process symbols are encountered by a referencing component. This method is used to order them by the component being referenced so that those references can be used by the analyser. This method stores an individual reference record stored in the parameter reference, which has the following content:
Field | Description of content |
MakesRef | The root of the component containing the reference |
BeingRefd | The root of the component being referenced |
RecNumber | The text record number in the host file of the reference |
CodeOffset | The offset in the compiled code of the referencing code |
HostFile | The root of the information file containing the subprogram |
Terminal | If the reference treated the component as terminal then one, else zero |
The code below shows the actual code used by gmBasic to locate and then write reference records. It is contained in the Utility class of the language file.
void Vb6StoreReferences(int iStart,int nCode,int subRoot,int hostFile) { tReference reference int icode; int nRef; int opc; int subc; tOpcInfo opcInfo; Handle codptr; int opcType; int icd; reference.MakesRef = subRoot; reference.BeingRefd = 0; reference.RecNumber = 0; reference.CodeOffset = 0; reference.HostFile = hostFile; reference.Terminal = 0; nRef = 0; icode = iStart; codptr = Opcode_GetCode(); while(icode >= 0) { opc = Opcode_GetOperation(codptr,icode,subc); opcInfo = Opcode_GetInfo(opc); opcType = opcInfo.Type; if(opc != Opcode.DPS && opc != Opcode.BIF && opc != Opcode.REF && subc > 0) { if(opc == Opcode.NEW) { reference.RecNumber = subc; } else if(opcInfo.type == opcTypes.SymbolAddr || opcType == opcTypes.LibraryAdr || opcType == opcTypes.LibPattern) { reference.BeingRefd = subc; reference.CodeOffset = icode - iStart; reference.Terminal = 0; icd = Opcode_GetNext(codptr,icode,nCode); if(icd > 0) { opc = Opcode_GetOperation(codptr,icd,subc); if(opc == Opcode.MEM) icd = Opcode_GetNext(codptr,icd,nCode); if(icd > 0) { opc = Opcode_GetOperation(codptr,icd,subc); opcInfo = Opcode_GetInfo(opc); opcType = opcInfo.Type; if(opcType != opcTypes.SymbolAddr && opcType != opcTypes.LibraryAdr && opcType != opcTypes.LibPattern) reference.Terminal = 1; } } Store_Reference(reference); nRef = nRef + 1; } } icode = Opcode_GetNext(codptr,icode,nCode); } }
Store_RewriteInfo Method
gmSL: void Store_RewriteInfo(Handle info, int nByte, int offset)gmVB: Sub Store_RewriteInfo(ByVal info As Handle, ByVal nByte As Integer, ByVal offset As Integer)
gmNI: void Store_RewriteInfo(void* info, int nByte, int offset);
The method Store_RewriteInfo rewrites long information vectors that were initially written using the method StoreWriteInfo. The number of bytes in the new vector may not exceed the original length of this information. If they do then the following system error warning is displayed.
SYSERROR#50003: Rewriting "current" bytes over "previous" bytes.
<gmBasic> <!-- Unit test for Store_RewriteInfo --> <gmSL> int levels[5]; int offsets[5]; int iOff; int iLev; int iRoot; int bootCell; Store_Open("Demo043.vbi", StorageUnit.USER, True) Write_OpenFile(1, "Demo044.out", True, OutStyle.Text); iRoot = Store_FindVector("offsets",0); bootCell = Store_GetObjectType(iRoot); Store_ReadInfo(offsets,bootCell); iOff = 0; while(iOff < 5) { Store_ReadInfo(levels, offsets(iOff)); iLev = 0; while(iLev < 5) { Write_Integer(levels(iLev),5); levels(iLev) = levels(iLev) * 2; iLev = iLev + 1; } Write_Record(); Store_RewriteInfo(levels,20,offsets(iOff)); iOff = iOff + 1; } Store_Close(); </gmSL> </gmBasic>
11 12 13 14 15 21 22 23 24 25 31 32 33 34 35 41 42 43 44 45 51 52 53 54 55
22 24 26 28 30 42 44 46 48 50 62 64 66 68 70 82 84 86 88 90 102 104 106 108 110
Store_Select Method
gmSL: void Store_Select(int iUnit)gmVB: Sub Store_Select(ByVal iUnit As Integer)
gmNI: void Store_Select(int iUnit);
The method Store_Select selects one the available storage areas as the current storage area. All successive storage area accesses will be performed to this area until another call to this method is made. Note that this service only selects the area, it does not change it in any way. Note also that when made via gmSL only the storage area used within subsequent gmSL code is effected. The storage area being used by mainline gmBasic is not effected. The integer parameter iUnit is the number of the storage unit to be used. This must be a value between 1 and 16. Note that gmBasic uses units one through five internally. There is an enumeration StorageUnit that defines these units. When the intent is to refer to one of these gmBasic units then an entry from this enumeration should be used. They are as follows:
Entry | Description of content |
USER | This is the storage area that is managed via the gmPL Storage statement. |
LANG | This is the storage area used to contain the language file controlling the translation. |
TEMP | This is a temporary storage area used within specific tasks. It is never left open, between tasks. It is also the unit used to store command mode gmSL code. |
INFO | This is a temporary storage area used by the gmPL utility statements like IncludeOrder to contain information being gathered, |
CLIENT | This is the storage area used to contain the GlobalSettings information. |
The following code moves through the various storage areas and displays their lengths.
<gmBasic> <!-- Unit test for Store_Select --> <Storage Action="Open" Identifier="fmstock1.vbi" /> <Select GlobalSettings="GmslCode.vbi" /> <LoadEnvironment /> <Output Status="New" Filename="demo038.out" /> <gmsl> Store_Select(StorageUnit.USER); Write_Line("Length of user storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); Store_Select(StorageUnit.LANG); Write_Line("Length of language storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); Store_Select(StorageUnit.TEMP); Write_Line("Length of temporary storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); Store_Select(StorageUnit.INFO); Write_Line("Length of Information storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); Store_Select(StorageUnit.CLIENT); Write_Line("Length of client storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); Store_Select(6); Write_Line("Length of user(6) storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); Store_Select(15); Write_Line("Length of user(15) storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); </gmsl> <Storage Action="Close" /> </gmBasic>
Length of user storage area(1) = 372736 Length of language storage area(2) = 509123 Length of temporary storage area(3) = 20577 Length of Information storage area(4) = 0 Length of client storage area(5) = 37016 Length of user(6) storage area(6) = 0 Length of user(15) storage area(15) = 0
Store_SetName Method
gmSL: void Store_SetName(int root, string* Name)gmVB: Sub Store_SetName(ByVal root As Integer, ByVal Name As String)
gmNI: void Store_SetName(int root, char* Name, int nName);
The method Store_SetName sets the target name of a component to the string parameter Name. The component is specified via its root offset. Whenever a component is stored in the symbol table using the Store_Vector method that component is given an identifier by the caller. This identifier can then used to retrieve the component, and by default is used by the author whenever references to the component are being written in the target code. If references are to be authored with a alternate identifier (referred to as the name of the component) then this method is used to associate that name with the component. The following shows the distinction between the identifier of the component and its name.
<gmBasic> <!-- Unit test for Store_SetName --> <Storage Action="Append" Identifier="fmstock1.vbi" /> <Output Status="New" Filename="demo036.out" /> <gmsl> int varRoot; varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0); Write_Line("Ident and Name before Store_SetName"); Write_ChangeMargin(1); Write_Line("The variable ident is " + Store_GetIdent(varRoot)); Write_Line("The variable name is " + Store_GetName(varRoot)); Write_ChangeMargin(-1); Store_SetName(varRoot, "m_databaseHandle"); Write_Line("Ident and Name after Store_SetName"); Write_ChangeMargin(1); Write_Line("The variable ident is " + Store_GetIdent(varRoot)); Write_Line("The variable name is " + Store_GetName(varRoot)); </gmsl> <Storage Action="Close" /> </gmBasic>
<Refactor> <Rename Identifier="FMStocks_DB.TxNew.m_dbh" Content="m_databaseHandle" /> </Refactor>
Ident and Name before Store_SetName The variable ident is m_dbh The variable name is m_dbh Ident and Name after Store_SetName The variable ident is m_dbh The variable name is m_databaseHandle
Store_SetObjectType
gmSL:gmVB:
gmNI:
Store_SetString Method
gmSL: void Store_SetString(int offset, string strValue)gmVB: Sub Store_SetString(ByVal offset As Integer, ByVal strValue As String)
gmNI: void Store_SetString(int offset, char* strValue, int length)
The method Store_SetString changes the value of an existing string whose length has not increased. The following code shows this in operation.
<gmBasic> <!-- Unit test for Store_String, Store_GetString, Store_SetString --> <gmSL> int offset1; int offset2; int offset3; Store_Create(NULL, StorageUnit.USER); Write_OpenFile(1, "Demo035.out", True, OutStyle.Text); offset1 = Store_String("Hello World"); offset2 = Store_String("This is Fred"); offset3 = Store_String("GoodBye World"); Write_Line("Content of strings before change"); Write_ChangeMargin(1); Write_Line("String1 is: " + Store_GetString(offset1)); Write_Line("String2 is: " + Store_GetString(offset2)); Write_Line("String3 is: " + Store_GetString(offset3)); Write_ChangeMargin(-1); Store_SetString(offset2, "This is Mark"); Write_Line("Content of strings after change"); Write_ChangeMargin(1); Write_Line("String1 is: " + Store_GetString(offset1)); Write_Line("String2 is: " + Store_GetString(offset2)); Write_Line("String3 is: " + Store_GetString(offset3)); Store_Close(); </gmSL> </gmBasic>
Content of strings before change String1 is: Hello World String2 is: This is Fred String3 is: GoodBye World Content of strings after change String1 is: Hello World String2 is: This is Mark String3 is: GoodBye World
Store_String Method
gmSL: int Store_String(string strValue)gmVB: Function Store_String(ByVal strValue As String) As Integer
gmNI: int Store_String(char* strValue, int nString);
The method StoreString stores a character string in the current storage area and returns the offset in the storage area of the string. This offset can be used later to retrieve the string. The following code shows this process.
<gmBasic> <!-- Unit test for Store_String, Store_GetString, Store_SetString --> <gmSL> int offset1; int offset2; int offset3; Store_Create(NULL, StorageUnit.USER); Write_OpenFile(1, "Demo035.out", True, OutStyle.Text); offset1 = Store_String("Hello World"); offset2 = Store_String("This is Fred"); offset3 = Store_String("GoodBye World"); Write_Line("Content of strings before change"); Write_ChangeMargin(1); Write_Line("String1 is: " + Store_GetString(offset1)); Write_Line("String2 is: " + Store_GetString(offset2)); Write_Line("String3 is: " + Store_GetString(offset3)); Write_ChangeMargin(-1); Store_SetString(offset2, "This is Mark"); Write_Line("Content of strings after change"); Write_ChangeMargin(1); Write_Line("String1 is: " + Store_GetString(offset1)); Write_Line("String2 is: " + Store_GetString(offset2)); Write_Line("String3 is: " + Store_GetString(offset3)); Store_Close(); </gmSL> </gmBasic>
Content of strings before change String1 is: Hello World String2 is: This is Fred String3 is: GoodBye World Content of strings after change String1 is: Hello World String2 is: This is Mark String3 is: GoodBye World
Store_Unit Method
gmSL: int Store_Unit()gmVB: Function Store_Unit() As Integer
gmNI: int Store_Unit(void);
The method Store_Unit returns the unit number of the current storage area. This value is often gotten before moving to another storage area. It can then be used to return to this current area. The code below shows the number and length of the currently open user storage area
<gmBasic> <!-- Unit test for Store_GetUnit --> <Storage Action="Open" Identifier="fmstock1.vbi" /> <Output Status="New" Filename="demo037.out" /> <gmsl> Write_Line("Length of storage area(" + Store_Unit() + ") = " + Store_GetLength(0)); </gmsl> <Storage Action="Close" /> </gmBasic>
Length of storage area(1) = 372736
Store_Vector Method
gmSL: int Store_Vector(string identifier, int parent, int length, int type)gmVB: Function Store_Vector(ByVal identifier As String, ByVal parent As Integer, ByVal length As Integer, ByVal type As Integer) As Integer
gmNI: int Store_Vector(char* identifier, int parent, int length, int type);
The StoreVector method stores a component information vector associated with an identifier as a child of a specified parent. It is the primary method used to enter components into the hierarchical symbol table. To enter a symbol, four things must be known. First, it must have a unique identifier relative to the parent of the symbol; as specified by the string parameter identifier. Second, it must have a parent which can either be another symbol already entered into storage or can be the root branch of the symbol tree; as specified by the integer parameter parent which is either the root offset of the parent or zero if being entered into the root branch of the tree. Third, it must have a information storage vector length, which may be zero; as specified by the integer parameter length. If non-zero, a zero-filled area of storage is allocated to the component with the specified length. Fourth. it must have an object type code, which specifies the type of component being entered; as specified by the integer parameter type. If there already is a component with the same identifier stored in the specified branch of the symbol table, then a zero is returned; else the nonzero root offset of the component is returned. It is this root offset which must be used to manipulate or retrieve any information for the component. The actual values of the parameter type are a gmBasic convention. The type code is either an entry number from the ObjectType enumeration in the language file, the opcode value of a built-in class if the component is a built-in control, or the root offset of an external class if the object is a control of that external class. Other than storing it and returning its value via the Store_GetObjectType method this code has no significance within the Store service class. To emphasize the above point, the example below uses a code also used by the Store_WriteInfo method.
<gmBasic> <!-- Unit test for Store_WriteInfo and Store_Vector --> <gmSL> int levels[5]; int offsets[5]; int iOff; int iLev; int bootCell; Store_Create("Demo043", StorageUnit.USER) Write_OpenFile(1, "Demo043.out", True, OutStyle.Text); iOff = 0; while(iOff < 5) { iLev = 0; while(iLev < 5) { levels(iLev) = (iOff+1) * 10 + iLev + 1; iLev = iLev + 1; } offsets(iOff) = Store_WriteInfo(levels,20); iOff = iOff + 1; } iOff = 0; bootCell = Store_WriteInfo(offsets,20); Store_Vector("Offsets",0,0,bootCell); while(iOff < 5) { Store_ReadInfo(levels, offsets(iOff)); iLev = 0; while(iLev < 5) { Write_Integer(levels(iLev),5); iLev = iLev + 1; } Write_Record(); iOff = iOff + 1; } Store_Close(); </gmSL> </gmBasic>
iRoot = Store_FindVector("offsets",0); bootCell = Store_GetObjectType(iRoot);
Store_WriteInfo Method
gmSL: int Store_WriteInfo(Handle info, int nByte)gmVB: Function Store_WriteInfo(ByVal info As Handle, ByVal nByte As Integer) As Integer
gmNI: int Store_WriteInfo(void* info, int nByte);
The method Store_WriteInfo writes long information vectors into storage. There is no limit to the length of this information. Since block boundaries may occur within the storage area used, pointers should not be used to access the record, rather the methods Store_ReadInfo and Store_RewriteInfo should be used. The most common use of this method is to write the intermediate code associated with a component, though it may be used for any type of information. The handle parameter info is a handle to the area in memory that contains the information to be written, and the integer parameter nByte is the number of bytes to be written. If the number of bytes to be written is not well-formed -- i.e., is less than or equal to zero, this method displays the following message and returns a zero.
SYSERR#5004: Empty or negative length (%nbyte%) record being written.
<gmBasic> <!-- Unit test for Store_WriteInfo and Store_Vector --> <gmSL> int levels[5]; int offsets[5]; int iOff; int iLev; int bootCell; Store_Create("Demo043", StorageUnit.USER) Write_OpenFile(1, "Demo043.out", True, OutStyle.Text); iOff = 0; while(iOff < 5) { iLev = 0; while(iLev < 5) { levels(iLev) = (iOff+1) * 10 + iLev + 1; iLev = iLev + 1; } offsets(iOff) = Store_WriteInfo(levels,20); iOff = iOff + 1; } iOff = 0; bootCell = Store_WriteInfo(offsets,20); Store_Vector("Offsets",0,0,bootCell); while(iOff < 5) { Store_ReadInfo(levels, offsets(iOff)); iLev = 0; while(iLev < 5) { Write_Integer(levels(iLev),5); iLev = iLev + 1; } Write_Record(); iOff = iOff + 1; } Store_Close(); </gmSL> </gmBasic>
11 12 13 14 15 21 22 23 24 25 31 32 33 34 35 41 42 43 44 45 51 52 53 54 55
Table of Contents