Undocumented Visual Objects (part 2)

Objecten in Visual Objects: van Code to Garbage

 

In dit artikel wil ik jullie wat meer vertellen over de manier waarop Visual Objects met objecten omgaat. Ik wil daarbij aandacht besteden aan de rol van:

-         de Compiler

-         de Linker

-         de Runtime

 

Als voorbeeld neem ik de volgende code:

 

CLASS Person

            PROTECT _cName    AS STRING

            PROTECT _nAge     AS DWORD

            PROTECT _cGender  AS STRING

 

METHOD Init(cName, nAge, cGender) CLASS Person

            _cName      := cName

            _nAge      := nAge

            _cGender    := cGender

 

ACCESS Name       CLASS Person

RETURN _cName

 

ASSIGN Name(cName) CLASS Person

_cName := cName

return cName

 

De Compiler en klassen

Allereerst wil ik het hebben over de rol van de Compiler.

Compileren van de klasse definities

Wanneer deze een klassedefinitie als hierboven tegenkomt, doet de compiler een heleboel zaken:

-         Er wordt assembly code gegenereerd waarmee de klasse door de  Runtime kan worden aangemaakt. Deze sourcecode maakt gebruik van de (niet gedocumenteerde) functie DeclareClass() . De parameters voor deze functie zijn o.a. de klassenaam (als Symbol), de Parent klasse (ook een symbol), de grootte van elk object en een lijst van de typen en namen van de geexporteerde IVars. De grootte van de objecten is in dit geval 20 bytes: 12 bytes voor de eigenlijke data (strings vragen 4 bytes voor een pointer in het object ) en 8 bytes voor een pointer naar de VTable en een pointer naar de Class Structure.

-         De VTable is een structure van pointers naar de methoden en accesses/assigns en zal worden opgebouwd door de linker indien een klasse gebruik maakt van strong typing. De Class Structure wordt door de Runtime opgebouwd.

Compileren van de (access & assign) methoden

Bij het compileren van een methode zal de Compiler vanzelfsprekend controleren of de betreffende klasse aanwezig is.

Daarnaast zal de compiler de benodigde assembly code genereren. Omdat de argumenten van de methode niet getypeerd zijn, zal de compiler conversies van Usual argumenten naar het juiste type genereren. Vervolgens wordt de daadwerkelijke code uitgevoerd, en tenslotte wordt het resultaat weer vertaalt naar een Usual, zodat het in de vorm van een usual kan worden teruggegeven. Als het argument per Reference werd doorgegeven wordt die referentie opgeheven.

 

Zo ziet de assembly voor de Name assign er gedeeltelijk als volgt uit (zonder optimalisaties)

 

STARTUP Code

LOAD Parameter in Registers               // Usual (8 byte)

CALL Usual2String                         // Kan error geven !

STORE Register to Ivar                    // 4 bytes

DEREFERENCE Parameter                     //

STORE RESULT                              // 8 bytes IN EAX/EDX               CLEANUP CODE

RETURN

 

Tevens genereert de compiler code waarmee de methode bij het runtime systeem wordt geregistreerd, gebruik makend van de niet gedocumenteerde DeclareMethod() functie:

 

      PUSH 1

      PUSH Address method

      PUSH Name

      PUSH Person

      CALL DeclareMethod

 

Het converteren van de argumenten van usual naar het juiste type en terug is natuurlijk een stuk minder efficiënt dan wanneer je de argumenten van de methoden zou typeren. Dat kun je bereiken door de methode te typeren. Daarop komen we verderop terug.

Compileren van de aanroep van de methoden.

Stel we gaan de klasse als volgt gebruiken.

 

FUNCTION Start

Local oPerson as Person

oPerson := Person{“Mr Data”,40,”M”}

? oPerson:Name

wait

 

De compiler zal dan de volgende pseudo:code genereren om het object te maken:

 

      Push “M”                // as USUAL, 8 bytes

      Push 40                 // idem

      Push “Mr Data”          // idem

      Push #Person            // idem

      Push 4                  // # of Usual arguments on the stack

      CALL CreateInstance()

 

Voor het tonen van de Person:Name Access genereert de compiler de volgende pseudo code:

 

      Push #Name                    // as SYMBOL, 4 bytes

      Push oPerson                  // as Object, 4 bytes

      Call IVarGet()                // Lees property

      Store Result in temp var.     // 8 bytes, result = USUAL

      Push temp var                 // as USUAL so 8 bytes

      Push 1                        // # of arguments for Qout

      Call QOUT

 

Je ziet dat de compiler niet direct de access methode aanroept, maar gebruik maakt van IVarGet().

 

Het effect op de code van typeren van methoden

Om betere performance te krijgen en tevens het voordeel dat de compiler betere controle over je code geeft, kun je methoden en accesses/assigns typeren.

Dat zou er als volgt uit kunnen zien

 

ACCESS Name                   AS STRING PASCAL CLASS Person

.

.

ASSIGN Name(cName as STRING)  AS STRING PASCAL CLASS Person

.

.

 

Tevens moet je dan tevens de volgende regels toevoegen aan de klasse definitie:

DECLARE ASSIGN  Name

DECLARE ACCESS  Name

 

Als je dat doet ziet de gegenereerde pseudo code voor de Name Assign er als volgt uit

 

STARTUPCODE

STORE Parameter to IVAR                   // Schrijf IVar

MOVE EAX Parameter                        // Return waarde

CLEANUPCODE

RETURN

 

Je ziet dat de code een stuk efficiënter is dan hiervoor, waarbij ook nog de conversie en de dereferentie wordt uitgevoerd.

 

Bij het aanroepen van de Name Access ziet de code in de start functie er dan als volgt uit en wordt er geen gebruik meer gemaakt van de IvarGet().

 

      CALL PERSON:NAME:ACCESS

      Store Result in temp var.     // 8 bytes, result = USUAL

      Push temp var                 // as USUAL dus 8 bytes

      Push 1                        // Aantal argumenten voor Qout

      Call QOUT

 

Bij het compileren van getypeerde methoden wordt echter ook nog de volgende code gegenereerd (een methode zonder naam, dit wordt de stub-methode genoemd):

 

LOAD Parameter in Registers         // USUAL (8 byte)

CALL Usual2String                   // Kan error geven !

PUSH Result                         // STRING PTR

CALL PERSON:NAME:ASSIGN             // Resultaat in EAX

STORE STRING TYPE                   // in EDX,

      RETURN

 

Deze code wordt door de VO runtime aangeroepen als je de Assign Late-bound aanroept, zoals bijv in onderstaande code.


LOCAL oPerson as OBJECT

oPerson := FunctionThatReturnsAPerson()

oPerson:Name := “Robert”

 

De compiler maakt dus per getypeerde methode 2 methoden:

-         De Getypeerde methode zelf.

-         Een niet getypeerde stub methode.

 

De eerste methode wordt aangeroepen wanneer de compiler tijdens het compileren het type van een methode kan vaststellen

De tweede methode wordt aangeroepen vanuit de IvarGet()/IVarPut() en Send() functies.

 

Je kunt je voorstellen dat er VO gebruikers waren die dit een beetje teveel van het goede vonden.

Daarom is de ONLYEARLY compiler pragma bedacht.

 

Die heeft twee betekenissen:

1.                  Als je hem in je klasse definitie opneemt, zorg hij er voor dat er geen stubs worden gegenereerd. Tevens worden er geen DeclareMethod()meer gegenereerd. Dat kan dus aanzienlijk in grootte schelen voor grote projecten.
Je gerbuikt het pragma als volgt:

CLASS Person

            PROTECT _cName    AS STRING

            PROTECT _nAge     AS DWORD

            PROTECT _cGender  AS STRING

            ~”ONLYEARLY+”

DECLARE ASSIGN  Name

DECLARE ACCESS  Name

            ~”ONLYEARLY-”

 

2.                  Als je het ONLYEARLY pragma in je code opneemt, zorgt hij ervoor dat er geen late-bound calls (Ivar..() en Send() ) worden gegenereerd. Als je dat dan toch probeert, zal de compiler een waarschuwing geven. In je code ziet het er dan als volgt uit:

 

FUNCTION Start

~”ONLYEARLY+”

Local oPerson as Person

oPerson := Person{“Mr Data”,40,”M”}

? oPerson:Name

wait

~”ONLYEARLY+”

 

 

 

De Linker en klassen

In dit onderdeel zullen we kort stilstaan bij wat de rol van de Linker is m.b.t. de klassen.

Bouwen van de initialisatie code

Een van de rollen die de linker in VO heeft, is het verzamelen van alle code die door de compiler is gemaakt waarin de DeclareClass, DeclareMethod calls enz. staan. De linker maakt een speciale functie aan in elke EXE en DLL, waarin al deze code wordt aangeroepen. Deze functie heet __VoDllClassInit().

Bouwen van de VTable

Wanneer er getypeerde methoden in een klasse voorkomen, zal de Linker een tabel maken waarin pointers naar deze methoden komen te staan. Dit wordt de VTable genoemd. Een verwijzing naar deze tabel wordt in de code opgenomen die vanuit de __VoDllClassInit() wordt aangeroepen. Het adres van de VTable is nl. één van de argumenten voor de DeclareClass() functie.

De Runtime en klassen

Tenslotte moeten de klassen natuurlijk door de Runtime worden aangemaakt en beheerd.

We hebben al gezien dat de compiler en de linker de informatie over de klassen hebben klaargezet in de __VoDllClassInit() functie. Deze functie wordt automatisch aangeroepen door de Runtime wanneer een VO EXE of DLL wordt gestart.

Onderstaande figuur laat zien hoe de klassen (en de objecten) er in het geheugen ongeveer uitzien:

 

Schema object layout in geheugen

 

-        

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

De blauwe blokjes zijn structures die door de runtime in statisch geheugen worden gemaakt, naar aanleiding van de informatie die door de DeclareClass en DeclareMethod functies wordt doorgegeven.

-         De oranje blokjes zijn code die zich in de EXE/DLL bevindt.

-         De gele blokjes zijn daadwerkelijke objecten die zich in het dynamisch geheugen bevinden.

 

Het aanmaken van objecten door de Runtime

Op basis van de informatie die de Runtime in de klasse structuur vindt worden objecten aangemaakt in dynamisch geheugen. Zoals hierboven al vermeld hebben alle objecten een verwijzing naar de klasse structuur en naar de VTable. Runtime functies als ClassName(), IsInstanceOf() , IVarGet(), Send() enz. maken gebruik van de link naar de klasse structuur.

De runtime plaats pointers naar deze twee zaken in elk object.

 

Het opruimen van objecten door de Runtime

Dit is de verantwoordelijkheid van de Garbage Collector. In principe gaat dit heel eenvoudig. Het dynamisch geheugen van VO bestaat uit twee banken. Elke keer als de Garbage Collector actief wordt kopieert hij de gebruikte blokken geheugen (strings, objecten, arrays & floats) van de ene bank naar de andere en past de verwijzingen naar die blokken geheugen aan.

Wanneer er geen verwijzingen naar een object meer gevonden worden, wordt het niet meer gekopieerd en houdt het op te bestaan.

Als je zelf code wilt laten uitvoeren op het moment dat een object ophoud te bestaan, kun je gebruik maken van een zg. Axit() methode. Je dient het object dan te registreren bij de Garbage Collector via de RegisterAxit() functie. Dat zorgt voor de volgende twee zaken:

-         Er wordt een bit in de header van het object gezet, die aangeeft dat het object een Axit() behoeft

-         In een tabel wordt een verwijzing naar het object opgenomen.

De runtime functie UnRegisterAxit() doet precies het omgekeerde!

 

Nadat de Garbage Collector alle objecten in het dynamisch geheugen heeft verplaatst naar de nieuwe bank, wordt een functie aangeroepen die alle geregistreerde objecten langsloopt.

Als nu blijkt dat het object verplaatst is naar de andere bank, wordt de verwijzing in de tabel bijgewerkt met het nieuwe adres. Anders wordt er een bit in de header van het object gezet, zodat duidelijk is dat het object opgeruimd is, en worden alle verwijzingen naar dynamisch geheugen in het object ook opgeruimd en wordt de tabel bijgewerkt.

Om problemen te voorkomen (als je bv. iets zou doen in de Axit() methode dat een nieuwe run van de Garbage Collector zou activeren) wordt het dynamisch geheugen tijdens dit hele proces Gelockt middels een DynLock()/

 

Het aantal objecten dat je van een Axit() methode kunt voorzien is beperkt. Het aantal staat standaard op 16000. Je kunt het ook via het register instellen in de sleutel: HKEY_CURRENT_USER\Software\ComputerAssociates\CA-Visual Objects Applications\Runtime, de setting MaxRegisteredAxitMethods.  Je kunt de setting ook kwijt in je HKEY_LOCAL_MACHINE, wat handig kan zijn als je Services programmeert of een DCOM component dat anoniem wordt gestart.

Helaas is deze instelling globaal, d.w.z. dat hij geldt voor alle VO applicaties op het betreffende werkstation.

In VO 2.7 wordt het mogelijk deze instelling vanuit je applicatie dynamisch in te stellen (via de nieuwe functie SetMaxRegisteredAxitMethods), maar daar zul je nog even op moeten wachten.

 

 

Epe,

Juni 2003

Robert van der Hulst

Commentaar van anderen:
ChristianLouboutin op 17-8-2010 om 4:49
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Wedding Shoes, Louboutin Shoes, Christian Louboutin Discount copies of the mirror, so the angel of the practical activities of customers who activities. Christian Louboutin Evening, Manolo Blahnik Shoes, Christian, Louboutin, Christian Louboutin Sale, Louboutin Sale, Cheap Christian Louboutin Can you from your shoes, you give yourself into chargeless travel and bear your lifetime. Christian Louboutin Boots, Christian Louboutin Pumps, Christian Louboutin Sandals, Christian Louboutin Flats, Christian Louboutin Wedges, Christian Louboutin Sandals Do you agree to accept and hear the cases angled heel shoes forward or abstract, tear, buttons, and destruction, and the ability to reduce greenhouse gas emissions, it is exactly like daydreaming and shoes. Yves Saint Laurent Shoes, Christian Louboutin Boots, Manolo Blahnik Shoes, Yves Saint Laurent Boots, Miu Miu Shoes, Christian Dior Shoes this is an extra brand and shoes. However, you will never accept the amorous Louboutin christians through brand. Christian Louboutin Flats, Christian, Herve Leger V Neck Dress, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress You can visit in your configuration of the life of their shoes were forgiven a brace, air brief you accept defeat them later anniversary, they are careful bag dust, shoe box that you have to go.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar