gmPL Refactoring Statements
Within
gmBasic many of the changes made to the code as it moves from its VB6/ASP source code form
into its ultimate .NET target code form can most easily be formulated as refactoring operations. These include:
- Renaming symbols either to avoid clashes or to make them easier to maintain.
- Reauthoring a subprogram because its original approach is inappropriate or
unworkable in the .NET environment
- Changing the type of a symbol which was either undefined or too weakly
specified.
- Changing the status of a symbol to change its scope or behavior
- Changing the structure of a symbol to clarify its size and dimensionality.
- Removing a symbol because it is not needed or wanted.
- Changing logic flow and/or program operations to conform to new requirements.
- Replacing references to external libraries with references to native libraries
All of these operations involve manipulations of the symbol table and of the intermediate code produced.
They do not directly reference the source or target code. The refactoring statements themselves are:
Statement | Description of use
|
Refactor | Introduces a set of refactoring statements
|
CallByName | Changes symbol-related code events that yield CallByName late binding calls into
direct boxed calls.
|
Extend | Extends the content of a class by adding new components.
|
FixType | Changes the binary type of a component or group of components
|
Generic | Specifies that individual subprogram parameters be implemented
as being generic to accept a list of different types.
|
Implements | Specifies that a VB6 class implements another class or interface.
|
MigClass | Introduces a new class that contains related refactoring information used for complex
migration operations, especially as related to designer code.
|
Migrate | Specifies migration of a specific symbol introduced via an external library description.
|
OverGeneric | Specifies that individual subprogram parameters be implemented
as being generic in an overloaded method to accept a list of different types.
|
Overload | Specifies that individual subprogram arguments be overloaded to accept a list of
different types.
|
Reauthor | Replaces the content of a subprogram with a completely rewritten block of code
|
Remove | Prevents a component from being authored
|
Rename | Changes the authored name of components
|
Replace | Replaces either the members of an external class or the patterns of opcodes via
replacement declarations.
|
Within
gmBasic the portation process proceeds in 8 steps:
- Loading the VB6/ASP source code.
- Fixing the VB6/ASP source code.
- Building the symbol table from the source code
- Compiling the source code into intermediate code
- Analysing the symbol table and intermediate code
- Authoring the target code from the intermediate code and symbol table
- Fixing the target code
- Deploying the target code
During this process there are 3 points at which refactoring operations can be made. First after step 3,
which builds the symbol table. This is a very effective place to strengthen the specifications of the symbols
so that the compiler can take advantage of this additional information while generating the intermediate code.
Second after step 4 when the compiler has completed, but the code analyser has not yet run. This is an excellent
time to introduce .NET types explicitly and to do any removals. Third after step 5, when the analyser has run, but
before the author has executed. This is the best time to do symbol renaming and code reauthoring.
Refactoring tends to effect the entire code base. It is not intended to make individual changes in actual code. The
source code fixing operations are intended for this. The rule of thumb for using a refactoring operation as opposed
to a fix editing operation is that the specification for the final recipient of the change is a symbol as opposed to a
line/block of code. In general, portation is an art and the selection of which approach to use will clearly vary by
individual and application.
Whenever possible, refactoring should be done via "shallow" changes -- changes which are applied to the actual
surface or source form of the code using the
Fix statement. These shallow changes can be applied to both
the source code before it is translated and to the target code after it is produced, but before it is published.
Such changes are easy to visualize and to specify. Unfortunately many of the changes needed cannot be specified
in this way -- refactoring is required.
Refactoring Property Specifications
Many of the refactoring statements are used to deal with the transformation of the VB6 property specifications into
.NET designer code. Much of the code within VB6 forms deals with controls. These controls all have complex sets of
properties that must be initialized within the VB6 code. This initialization code contains nested sets of "name=value"
pairs organized into nested blocks started off by
BEGIN or
BEGINPROPERTY statements and ending with
END or
ENDPROPERTY statements.
Here is a simple example of such a specification.
Begin VB.Form VB0001Form
Caption = "VB0001"
ClientHeight = 5115
ClientLeft = 60
ClientTop = 345
ClientWidth = 5280
LinkTopic = "Form1"
ScaleHeight = 5115
ScaleWidth = 5280
StartUpPosition = 3 'Windows Default
Begin VB.CommandButton Command1
Caption = "Run VB0001 Test"
Height = 375
Left = 1440
TabIndex = 0
Top = 1320
Width = 1935
End
End
In the above, the
Begin has the syntax "Begin controltype identifier" where
controltype is a defined
control type and
identifier is the identifier of the control object in the user code that is being defined.
In the
name=value pairs the
name is the name of a property defined for the control type class and the
value is the value to be assigned to that property for the identified instance of the control type.
There are of course properties of controls that are themselves object types rather than simple value types.
The most common of these is the
Font property. Here is an example of a specification of this type.
Begin VB.Label lblFirst
Alignment = 2 'Center
BorderStyle = 1 'Fixed Single
Caption = "VB is fun"
BeginProperty Font
Name = "MS Sans Serif"
Size = 24
Charset = 0
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 615
Left = 2040
TabIndex = 0
Top = 480
Width = 2655
End
In the above the
BeginProperty statement has the syntax "BeginProperty property" where
property is
the identifier of a property in the containing control type that has an object type. There is no additional identifier
here because the values are part of the higher instance.
BeginProperty is semantically equivalent to a
simple
name=value pair except that multiple values are being supplied.
All of the above is an oversimplification in every possible way. Even in the above "simplest of all examples", for
example, the
VB.Label control type does not have a property called
Font; rather it has properties
like
FontName,
FontSize etc. When the above syntax is extended to
COM CONTROLS all formal
relationships are ignored in every possible way. The specifications look like the above, but there truly are no
real rules. Each
COM property specification has to be examined carefully and converted into a specification that
associates values with a set of user-defined controls in a consistent manner.
Converting the input property specifications into a consistent form is a difficult, but trivial problem when compared
to the problem of reauthoring those specifications in .NET. The two primary problems are that in .NET controls are
not nested, they have a linear structure and then there are later instructions that add children into the scope
of the parents. How and when and with what adornments are needed to make these scope specifications vary widely.
In addition in .NET some values are assigned to properties directly in the code and others are assigned via external
resource files.
An important point to be remembered about the property specification code is that though it uses opcodes,
though opcodes are not authored via a string-machine. It has code fragments in it that can be evaluated and viewed
in the same way as procedural code, but it is used simply as a data-store for the information initially obtained from
the VB6 specifications and then as changed to supply information to the .NET control author. New properties must be added, old
properties must be removed or restructured or radically migrated. There are no
dependable simplistic correspondences
between properties defined for the classes and references to those properties. The goal is that the eventual
authored .NET property specifications will accurately reflect the original intent of the VB6 source; however, that goal
is achieved via the interaction of diverse components and not directly in the property code blocks.