FreshLib object oriented programming library.
1. Introduction
The latest versions of FreshLib, silently migrated to a new object oriented programming macro implementation. Now it implements real OOP architecture with inheritance, polymorphism, virtual methods and properties.
The implementation uses jump tables, so it is pretty fast, especially compared to the previous implementation.
The syntax is based on pseudo-instructions and is compatible with assembly language programming style.
The main target for this library is the programming of GUI interfaces, because the structure of GUI, usually fit very well in the OOP paradigm. But of course, the library can be used for general purpose OOP programming.
2. Object definition. Encapsulation
The definition of the object is provided by object
/ endobj
macros. The syntax is very similar to struct
macros,
but the definition can be inherited and the body may contain more elements:
object NAME[, PARENT]
BODY
endobj
PARENT
can be either other object type name or not be specified at all. If PARENT
is missing the object has no parent and does not inherits any fields.
If PARENT
is specified, the object inherits all fields from the parent object.
All field names in the object definition must be local names related to the object name, i.e. must start with dot character.
The body may contain following elements:
General FASM data definition fields:
object TAnimal
.length dd ?
.height dd ?
.weight dd ?
endobj
The fields are aimed to provide storage of private for the object data. Of course, it is assembly language and the fields are not hidden from the user, but a good practice is to not use them as an interface. (There are other members that are to be used as an interface - the parameters and methods).
object parameter definition through param
directive. Will be described in details later.
object method definition through method
directive. Will be described in details later.
2.1. Object parameters; "param" directive;
The parameters (aka "properties") are object data fields, that are not necessary memory cells. Instead their values can be read and write by several flexible ways. These ways are transparent for the programmer. The FreshLib implementation allows 3 ways to reading writing parameter values:
By direct read/write to a memory fields.
By calling object method that to read or write parameter value using any software method, for example computing the value, or reading it from a file or even from the network.
Not reading/writing at all - the parameters can be read-only and/or write-only.
The syntax of the definition is:
param PARAM_NAME, GET_TARGET, SET_TARGET
PARAM_NAME
is the name of the parameter. It must be local related to the object name (to start with dot).
GET_TARGET
defines how the value of the parameter will be get.
This parameters can be either NONE
, object field name or object method name.
In the first case, the parameter can't be read. In the second, the read of the parameter becomes simple structure field read and in the third, the read of the parameter will
cause the object method to be call and the return value of the method will be accepted as a value of the parameter.
The get method must have no arguments and must be defined in the object definition as normal method.
SET_TARGET
defines how the value will be write to the parameter. Again we can use NONE
, field name or method name. The only difference with the GET_TARGET
is that
the set method has one argument - the new value of the parameter.
2.2. Object methods; "method" directive; "abstract" directive;
This directive defines a method field in the object. The syntax is very similar to the proc
macro directive:
method NAME, [ARGUMENTS]
or
abstract NAME, [ARGUMENTS]
NAME
must be local related to the object name. The arguments can be arbitrary count and also must start with dot (local).
Besides the explicitly defined arguments, one hidden argument .self
is defined for every method, that contains a pointer to the object instance in the moment when the method is
called. The .self
argument is the first of all arguments in the method.
The only difference between method
and abstract
variants is that the methods defined with abstract
keyword does not need to be implemented in the given class. The abstract methods are simply placeholders for the children object methods. They allow polymorphic calls of the method in the children objects.
Every non-abstract method, defined in the object need to be implemented later in the code. The implementation looks just like proc
procedure definition, with the difference that method
keyword is used. In the implementation, there is no arguments, because they are already defined in the object type definition.
The methods can be override in the children classes. The inherited method is still accessible through inherided
command. This way the methods chains can be called when needed.
method OBJ_NAME.METHOD_NAME
begin
CODE
endp
As is seen from the above description, the object type itself contains in the same container (capsule) all needed data and the code, needed to process this data. This property of the objects is called Encapsulation.
Some think that "encapsulation" is something about hiding data or code inside the objects, but this is not the case in the common case.
And of course in assembly language hiding data or code is impossible at all. The user have always access to all data fields and code in the object. There is no private fields or methods. It is simply not the assembly way.
3. Example of object definition with inheritance.
In the following example can be seen all the above syntax in practice. The type TAnimal
defines several common
data fields, the parameter .Energy
that is read-only and gives the current kinetic energy of the animal and
the abstract method .Jump
that is not implemented for TAnimal, as long as the different animals jumps differently
and need special implementation.
The children types TCat
and TDog
implements their own methods .Jump
.
object TAnimal
.length dd ?
.height dd ?
.weight dd ?
.speed dd ?
param .Energy, .GetEnergy, NONE
method .GetEnergy
abstract .Jump, .height
endobj
object TCat, TAnimal
method .Jump, .height
endobj
object TDog, TAnimal
method .Jump, .height
endobj
method TAnimal.GetEnergy
begin
push ecx
mov ecx, [.self]
mov eax, [ecx+TAnimal.speed]
imul eax, eax
imul eax, [ecx+TAnimal.weight]
sar eax, 1 ; E = m.v^2/2
pop ecx
return
endp
method TCat.Jump
begin
; code that makes the cat jump not as other animals.
return
endp
method TDog.Jump
begin
; code that makes the dog jump not as other animals.
return
endp
4. Object manipulation macros.
4.1. Creating objects
Once the object is defined, the user can create object instances, using the macros create
. The syntax is:
create DST, TYPE
The macro allocates memory for the object instance, fills needed fields and executes the method Create if such is defined for the given object type.
DST
is the destination where we want the pointer to the object instance to be stored. Can be register or memory location. Same as for mov
instruction.
TYPE
is an object type, previously defined with object
macro.
4.2. Destroy object.
The allocated object instance must be destroyed when not needed with the macro destroy
. The syntax is:
destroy SRC
SRC
is a pointer to the object instance. Can be register or memory content.
The macro will first call the Destroy method of the object if such is defined and then will free the allocated dynamic memory.
4.3. Set/Get object parameters.
The value of object parameter can be get with the macro get
. The syntax is:
get DST, PTR_OBJ, TYPE:PARAM
The macro gets the value of TYPE:PARAM
parameter of the object PTR_OBJ
and store it in DST
.
DST
and PTR_OBJ
can be register or memory location, TYPE must be already defined object type and PARAM parameter, defined in this object or any of its predecessors.
Notice, the TYPE
and PARAM
must be separated by colon ":", not a dot.
The value of object parameter can be set to new value with the macto set
. The syntax is similar to the previous, only in different order:
set PTR_OBJ, TYPE:PARAM, VALUE
PTR_OBJ
is the object which parameter have to be set.
TYPE:PARAM
specifies the parameter name.
VALUE
is the new value of the parameter.
4.4. Executing object methods.
The macro exec
executes some object method. The syntax is:
exec PTR_OBJ, TYPE:METHOD, [ARG]
The macros has various count of arguments, depending on the arguments the method accepts.
TYPE:METHOD
must be valid combination of defined object type and method name, separated by colon ":".
4.5. Check object type
The user can check whether the object belongs to some type by using "istype" macro:
istype OBJECT, TYPE
This macro returns ZF flag set if the object belongs to the type or ZF cleared if not. It will not change any registers.
For example, see following code:
create eax, TCat ; create object instance.
istype eax, TCat
je .yes_cat ; ZF=1 It is a TCat...
int3
.yes_cat:
istype eax, TAnimal
je .yes_animal ; ZF=1 ...also, it is TAnimal...
int3
.yes_animal:
istype eax, TDog
je .yes_dog ; ZF=0 ...but is it NOT TDog.
int3 ; will stop here
.yes_dog:
5. Polymorphism
Polymorphism is one of the main properties of the OOP. It is the ability of the object to execute different code on the same method call, depending on its type.
In the context of our library, as long as the types inherit all the methods of its parents and
can redefine it, it means, that in the above example, TCat
and TDog
both have method .Jump
,
inherited from TAnimal
. (In this sense, they are both of type TAnimal
), but the implementation of
TCat.Jump
and TDog.Jump
differs.
This way, we can use the same code to process both types.
create eax, TCat
create ebx, TDog
exec eax, TAnimal:Jump, 100
exec ebx, TAnimal:Jump, 100
You can see, that the same code will call in the first case TCat.Jump
and in the second case
TDog.Jump
methods.
Last modified on: 05.07.2018 15:56:08