Sign in Join
Wiki copytemporary

FPC Cleanroom: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11In 2007 it was found that some functions could need a new implementation because they were too similar to the implementation from Delphi. FPC 2.2.2 will be the first release after the cleanroom implementation of those routines.
22
33The core FPC developers already know the old implementation, so impartial contributiors not familiar with the old code or the Delphi code need to be found. The functions alse need to be tested and specifications for them written.
44
55## Volunteers to implement the functions
66
77- Vincent
88- Almindor
99-
1010-
1111
1212## Volunteers to write specs and test apps
1313
1414- [[Sekelsenmat]]
1515- Joost
1616
1717## Downloading the cleanroom branch
1818
1919One can download it using subversion with:
2020
21-`svn co `[`http://svn.freepascal.org/svn/fpc/branches/cleanroom`](http://svn.freepascal.org/svn/fpc/branches/cleanroom)` cleanroom`
21+```text
22+svn co http://svn.freepascal.org/svn/fpc/branches/cleanroom cleanroom
23+```
2224
2325Or use viewvc to view it: <http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/cleanroom/>
2426
2527## Affected functions
2628
2729The affected functions are marked with "tainted" on the branch.
2830
2931### TDataset.CalculateFields
3032
3133**location**
3234
33-`cleanroom/packages/fpc-db/src/base/dataset.inc`
35+```text
36+cleanroom/packages/fpc-db/src/base/dataset.inc
37+```
3438
3539**declaration**
3640
37-`(private) Procedure TDataset.CalculateFields(Buffer: PChar);`
41+``` pascal
42+(private) Procedure TDataset.CalculateFields(Buffer: PChar);
43+```
3844
3945**Implementor: Almindor (untested)**
4046
4147**Specifications**
4248
4349Basicly this procedure sets the CalcBuffer, clears it and calculates the lookupvalues for all lookup fields and then calls DoOnCalcFields.
4450
4551So first it assigns the supplied Buffer to FCalcBuffer. Then if the state of the dataset is not dsInternalCalc and IsUniDirectional is False, ClearCalcFields is called to clear the CalcBuffer and for each field in the Fields collection of which the FieldKind is fkLookup, CalcLookupValue is called.
4652
4753Finally DoOnCalcFields is called.
4854
4955### TDataset.DataEvent
5056
5157**location**
5258
53-`cleanroom/packages/fpc-db/src/base/dataset.inc`
59+```text
60+cleanroom/packages/fpc-db/src/base/dataset.inc
61+```
5462
5563**declaration**
5664
57-`(private) Procedure TDataset.DataEvent(Event: TDataEvent; Info: Ptrint);`
65+``` pascal
66+(private) Procedure TDataset.DataEvent(Event: TDataEvent; Info: Ptrint);
67+```
5868
5969**Impementor**: Vincent
6070
6171#### Specifications
6272
6373Handles an event
6474
6575When the event is equal to deFieldChange then the provided 'Info' parameter is a TField. If it's fieldkind is fkData or fkInternalCalc then SetModified is called to set Modified to 'true'.
6676
6777If the event is deFieldChange and the state of the dataset is not dssetkey then there are first two checks: if FInternalCalcFields and info's FieldKind is equal to fkData, then RefreshInternalCalcfields is called with ActiveBuffer as parameter. Second check is that if the first check is not true but the info's fieldkind is equal to fkData, FAutoCalcFields is true and FCalcFieldsSize is not equal to 0, then CalculateFields is called for the ActiveBuffer. After those two checks, Info.Change is always called.
6878
6979If the event is equal to deDatasetChange or deDatasetScroll and TDataset.State is not dsInsert then UpdateCursorPos is called.
7080
7181Thereafter, if ControlsDisabled is false, the event is distributed to all datasources linked to this dataset by calling the datasource's ProcessEvent with 'Event' and 'Info' as parameters.
7282
7383### TDataset.EnableControls
7484
7585**location**
7686
77-`cleanroom/packages/fpc-db/src/base/dataset.inc`
87+```text
88+cleanroom/packages/fpc-db/src/base/dataset.inc
89+```
7890
7991**declaration**
8092
81-`(public) Procedure TDataset.EnableControls;`
93+``` pascal
94+(public) Procedure TDataset.EnableControls;
95+```
8296
8397**Implementor: Almindor (untested)**
8498
8599**Specifications**
86100
87101Reactivates the displaying of data on screen after a call to DisableControls.
88102
89103EnableControls should decrement the disabled count on the dataset, up to a minimum of zero. If it's zero, it will tell the controls using the dataset they can show their values on screen.
90104
91105In detail: If FDisableControlsCount is larger then zero then it's value is decremented by 1. If thereafter FDisabledControlsCount is equal to zero then there is a check if the state of the dataset is changed since the call to DisableControls. This is done by comparing the current state with FDisableControlsState. If the state is changed, a deUpdateState event is fired, using TDataset.DataEvent. If the current state and the FDisableControlState are both unequal to dsInactive, then an FEnablecontrolsEvent is fired.
92106
93107### TDataLink.CalcFirstRecord
94108
95109**location**
96110
97-`cleanroom/packages/fpc-db/src/base/datasource.inc`
111+```text
112+cleanroom/packages/fpc-db/src/base/datasource.inc
113+```
98114
99115**declaration**
100116
101-`(private) Function TDataLink.CalcFirstRecord(Index : Integer) : Integer;`
117+``` pascal
118+(private) Function TDataLink.CalcFirstRecord(Index : Integer) : Integer;
119+```
102120
103121**Implementor: Almindor (untested)**
104122
105123#### Specifications
106124
107125This procedure returns the number of records the buffer should be scrolled if the current cursor moves 'index' places. And it sets FFirstRecord to it's new place, taking the shift of the buffer into account.
108126
109127This function compare DataSource.Dataset.FActiveRecord with FFirstRecord +Index+FBuffercount-1. If the former is larger then the latter, then the result of the function is set to the difference between the two. Else if DataSource.Dataset.FActiveRecord is smaller then FFirstRecord +Index then the result is set to the difference between those two values. (The result is negative) If both evaluations are false, no shift of the buffer is necessary and the result is set to 0.
110128
111129Finally the FFirstRecord is incremented by Index and the Result is added to it.
112130
113131### TField.CalcLookupValue
114132
115133**location**
116134
117-`cleanroom/packages/fpc-db/src/base/fields.inc`
135+```text
136+cleanroom/packages/fpc-db/src/base/fields.inc
137+```
118138
119139**declaration**
120140
121-`(private) procedure TField.CalcLookupValue;`
141+``` pascal
142+(private) procedure TField.CalcLookupValue;
143+```
122144
123145**Implementor: Almindor (untested)**
124146
125147**Specifications**
126148
127149It sets 'value' to to the lookupvalue corresponding to the fieldvalues of the keyfields.
128150
129151If FLookupCache is true, then Value is set to the value of the key in 'LookupList' which corresponds to the 'FieldValues' of 'FKeyFields' of 'FDataset'. Or else if the FLookupDataSet is not nil and this dataset is active then then the value of 'Value' is determined using FLookupDataset.Lookup. The keyfields are the 'FLookupKeyFields', the keyvalues the FieldValues of the FDataset for the FKeyFields again, and the resultfield is the FLookupResultField.
130152
131153### TField.RefreshLookupList
132154
133155**location**
134156
135-`cleanroom/packages/fpc-db/src/base/fields.inc`
157+```text
158+cleanroom/packages/fpc-db/src/base/fields.inc
159+```
136160
137161**declaration**
138162
139-`(public) procedure TField.RefreshLookupList;`
163+``` pascal
164+(public) procedure TField.RefreshLookupList;
165+```
140166
141167**Implementor: Almindor (untested)**
142168
143169#### Specifications
144170
145171This procedure does nothing if FLookupDataset is nil or if any of FLookupkeyFields, FLookupResultField or FKeyFields is an empty string.
146172
147173First of all the value of FLookupDataset is stored. The rest of the procedure is executed in an expcetion-block and whatever happens, FLookupDataset.Active is set to the stored value before the procedure ends. (Even in case of an exception)
148174
149175Thereafter FLookupDataset.Active is set to true. Then FFields.CheckFieldNames is called for FLookupKeyFields and FLookupDataset.FieldByName(FLookupResultField) is called to check if that field does exist.
150176
151177Then LookupList.Clear is called and the controls are disabled. (DisableControle). Thereafter an exception-block is started, so that EnableControls is always called after the code in the exception block. In the exception block FLookupList.Add is called for each record in FLookupDataset. The first parameter of FLookupList.Add is FieldValues\[FLookupKeyFields\] and the second is FieldValues\[FLookupResultField\]
152178
153179(As said, here Enablecontrols is called and the value of FLookupDataset.Active is restored.)
154180
155181**Related to**
156182
157183- TField.LookupCache
158184- TField.LookupDataSet
159185- TField.LookupKeyFields
160186- TField.LookupList
161187- TField.LookupResultField
162188
163189## TODO for the RTL
164190
165191- \[Done\] There is a memleak in TReader.ReadPropValue. A FFixups object is created, but never destroyed. Where should it be freed. Possible places: in TReader.EndReference or TReader.Destroy.
166192- \[Done\] Fix test for streaming of enumeration with value equal to low(TMyEnum) and no explicit
167193
168194default value, which means a default value of low(TMyEnum). Currently fpc 2.2.0 behaves differently from Delphi, the tests don't reflect this.
169195
170196- \[Done\] There is a memleak in TTESTRESOLVEREFERENCE tests. SetupARef1A is called but the
171197
172198returned object is not freed.
173199
174200- There are no tests for TReader.FindComponentClass
175201
176202## Variant streaming implementation
177203
178204The variant streaming is very important not only for COM/CORBA compatibility, but also for general runtime access to the properties of object using GetPropValue/SetPropValue. Besides the variant is type, widely used in database components, and absence of Variant streaming limits the usage of variant as published properties.
179205
180206### General considerations
181207
182208Variant itself is a structure, holding the type selector for the data and the data itself (in some cases it holds a reference). Some very special cases include variant holding reverence to COM/Corba object, which streaming would be considered later.
183209
184210One of the powerful concept of FPC Object Pascal RTL is the presence of abstract writer and reader, which can be easily subclassed. For our task we have to extend the functionality of basic class TAbstractObjectReader/Writer, to allow in derived classes use the methods - ReadVariant/WriteVariant.
185211
186212Current FPC-implemented Delphi-compatible writer (TWriter) works in such manner -
187213
188214- It enumerates properties, determining property type.
189215- Using the property type as selector it branches to specified property storage routine.
190216- Using RTTI functions writer gets the property value, determines if the property have to be stored (is not default value and stored-checker method allows storage).
191217- Using methods of internal TBinaryObjectWriter object, referred as Driver, Writer stores value in the stream.
192218
193219#### Storing property of type Variant
194220
195221For variant value there should be one additional consideration: Once we got property type and have determined that it is variant - we have to extract from variant the kind of basic pascal type which variant holds.
196222
197223There is routine VarType(arg:Variant), which can help to separate if the variant is Ordinal, Floating-point or String. These three kinds of variants can be handled by corresponding separate routines. Ordinal variants include:
198224
199225- varShortInt,varSmallInt,varInteger,varInt64,varQWord, which can be stored using fDriver.WriteInteger()
200226- varBoolean, which can be stored using fDriver.WriteBoolean()
201227
202228Floating-point variants include:
203229
204230- varSingle, which can be stored by fDriver.WriteSingle
205231- varDouble,varDate,varCurrency, which can be handled as double and stored using fDriver.WriteFloat
206232
207233String-holding variants: All the flavours of strings. IMO the string should be extracted as widestring and then stored as UTF8 representation.
208234
209235Other variant flavours (varXXXX) should be considered for now as non-implemented.
210236
211237One special case - is a variant holding NULL reference, and variant holding none (empty variant). These are different cases, and special measures should be taken to distinguish such conditions. Empty variant should not be stored. NULL variant is the value, like other.
212238
213239#### Reading property of type Variant
214240
215241Reading of variants consists of the following:
216242
217243- Once we have determined the property is variant, we have to read value type (ReadValue) from stream.
218244- Depending of the value type we have to read using fDriver methods the value of specified type, to the variable of compatible basic type without precision loss.
219245- We have to initialise variant using RTL assignment magic ( Variant := SomeGenericValue; ), which would implicitly invoke corresponding variant initialiser)
220246- Once we got assigned variant, we return it
221247
222248Special consideration is made for vaTrue, vaFalse - stored in stream. Variant should contain boolean value set to true if we got vaTrue and set to false if we got vaFalse.
223249
224250The very special consideration should be done for string Zoo. Read string should be converted to WideString and stored in variant.
225251
226252## Bug reports
227253
228254- [11060](http://bugs.freepascal.org/view.php?id=11060): Reader fails to assign class properties
229255- [11059](http://bugs.freepascal.org/view.php?id=11059): TComponent.DefineProperties not called
230256
231257---
232258*From the Free Pascal wiki, page "FPC Cleanroom", as it stood on 2015-07-22 after a change by FTurtle, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
233259Category: Old or unknown pages

Back to history