Sign in Join
Wiki copytemporary

FPC Cleanroom: what changed

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

OldNewLine
1-In 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.
2-
3-The 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.
4-
5-## Contents
1+In 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.
62
7- * 1 Volunteers to implement the functions
8- * 2 Volunteers to write specs and test apps
9- * 3 Downloading the cleanroom branch
10- * 4 Affected functions
11- * 4.1 TDataset.CalculateFields
12- * 4.2 TDataset.DataEvent
13- * 4.2.1 Specifications
14- * 4.3 TDataset.EnableControls
15- * 4.4 TDataLink.CalcFirstRecord
16- * 4.4.1 Specifications
17- * 4.5 TField.CalcLookupValue
18- * 4.6 TField.RefreshLookupList
19- * 4.6.1 Specifications
20- * 5 TODO for the RTL
21- * 6 Variant streaming implementation
22- * 6.1 General considerations
23- * 6.1.1 Storing property of type Variant
24- * 6.1.2 Reading property of type Variant
25- * 7 Bug reports
3+The 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.
264
275## Volunteers to implement the functions
286
29- * [Vincent](</User:Vincent> "User:Vincent")
30- * Almindor
31- * *
7+- Vincent
8+- Almindor
9+-
10+-
3211
3312## Volunteers to write specs and test apps
3413
35- * [Sekelsenmat](</index.php?title=Sekelsenmat&action=edit&redlink=1> "Sekelsenmat \(page does not exist\)")
36- * Joost
14+- [[Sekelsenmat]]
15+- Joost
3716
3817## Downloading the cleanroom branch
3918
40-One can download it using subversion with:
41-```
42-svn co <http://svn.freepascal.org/svn/fpc/branches/cleanroom> cleanroom
43-```
19+One can download it using subversion with:
4420
21+`svn co `[`http://svn.freepascal.org/svn/fpc/branches/cleanroom`](http://svn.freepascal.org/svn/fpc/branches/cleanroom)` cleanroom`
22+
4523Or use viewvc to view it: <http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/cleanroom/>
4624
4725## Affected functions
4826
49-The affected functions are marked with "tainted" on the branch.
27+The affected functions are marked with "tainted" on the branch.
5028
5129### TDataset.CalculateFields
5230
5331**location**
54-```
55-cleanroom/packages/fpc-db/src/base/dataset.inc
56-```
5732
33+`cleanroom/packages/fpc-db/src/base/dataset.inc`
34+
5835**declaration**
59-```
60-(private) Procedure TDataset.CalculateFields(Buffer: PChar);
61-```
6236
37+`(private) Procedure TDataset.CalculateFields(Buffer: PChar);`
38+
6339**Implementor: Almindor (untested)**
6440
6541**Specifications**
6642
67-Basicly this procedure sets the CalcBuffer, clears it and calculates the lookupvalues for all lookup fields and then calls DoOnCalcFields.
43+Basicly this procedure sets the CalcBuffer, clears it and calculates the lookupvalues for all lookup fields and then calls DoOnCalcFields.
6844
69-So 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.
45+So 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.
7046
71-Finally DoOnCalcFields is called.
47+Finally DoOnCalcFields is called.
7248
7349### TDataset.DataEvent
7450
7551**location**
76-```
77-cleanroom/packages/fpc-db/src/base/dataset.inc
78-```
7952
53+`cleanroom/packages/fpc-db/src/base/dataset.inc`
54+
8055**declaration**
81-```
82-(private) Procedure TDataset.DataEvent(Event: TDataEvent; Info: Ptrint);
83-```
8456
85-**Impementor** : [Vincent](</User:Vincent> "User:Vincent")
57+`(private) Procedure TDataset.DataEvent(Event: TDataEvent; Info: Ptrint);`
8658
59+**Impementor**: Vincent
60+
8761#### Specifications
8862
89-Handles an event
63+Handles an event
9064
91-When 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'.
65+When 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'.
9266
93-If 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.
67+If 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.
9468
95-If the event is equal to deDatasetChange or deDatasetScroll and TDataset.State is not dsInsert then UpdateCursorPos is called.
69+If the event is equal to deDatasetChange or deDatasetScroll and TDataset.State is not dsInsert then UpdateCursorPos is called.
9670
97-Thereafter, 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.
71+Thereafter, 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.
9872
9973### TDataset.EnableControls
10074
10175**location**
102-```
103-cleanroom/packages/fpc-db/src/base/dataset.inc
104-```
10576
77+`cleanroom/packages/fpc-db/src/base/dataset.inc`
78+
10679**declaration**
107-```
108-(public) Procedure TDataset.EnableControls;
109-```
11080
81+`(public) Procedure TDataset.EnableControls;`
82+
11183**Implementor: Almindor (untested)**
11284
11385**Specifications**
11486
115-Reactivates the displaying of data on screen after a call to DisableControls.
87+Reactivates the displaying of data on screen after a call to DisableControls.
11688
117-EnableControls 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.
89+EnableControls 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.
11890
119-In 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.
91+In 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.
12092
12193### TDataLink.CalcFirstRecord
12294
12395**location**
124-```
125-cleanroom/packages/fpc-db/src/base/datasource.inc
126-```
12796
97+`cleanroom/packages/fpc-db/src/base/datasource.inc`
98+
12899**declaration**
129-```
130-(private) Function TDataLink.CalcFirstRecord(Index : Integer) : Integer;
131-```
132100
101+`(private) Function TDataLink.CalcFirstRecord(Index : Integer) : Integer;`
102+
133103**Implementor: Almindor (untested)**
134104
135105#### Specifications
136106
137-This 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.
107+This 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.
138108
139-This 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.
109+This 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.
140110
141-Finally the FFirstRecord is incremented by Index and the Result is added to it.
111+Finally the FFirstRecord is incremented by Index and the Result is added to it.
142112
143113### TField.CalcLookupValue
144114
145115**location**
146-```
147-cleanroom/packages/fpc-db/src/base/fields.inc
148-```
149116
117+`cleanroom/packages/fpc-db/src/base/fields.inc`
118+
150119**declaration**
151-```
152-(private) procedure TField.CalcLookupValue;
153-```
154120
121+`(private) procedure TField.CalcLookupValue;`
122+
155123**Implementor: Almindor (untested)**
156124
157125**Specifications**
158126
159-It sets 'value' to to the lookupvalue corresponding to the fieldvalues of the keyfields.
127+It sets 'value' to to the lookupvalue corresponding to the fieldvalues of the keyfields.
160128
161-If 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.
129+If 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.
162130
163131### TField.RefreshLookupList
164132
165133**location**
166-```
167-cleanroom/packages/fpc-db/src/base/fields.inc
168-```
169134
135+`cleanroom/packages/fpc-db/src/base/fields.inc`
136+
170137**declaration**
171-```
172-(public) procedure TField.RefreshLookupList;
173-```
174138
139+`(public) procedure TField.RefreshLookupList;`
140+
175141**Implementor: Almindor (untested)**
176142
177143#### Specifications
178144
179-This procedure does nothing if FLookupDataset is nil or if any of FLookupkeyFields, FLookupResultField or FKeyFields is an empty string.
145+This procedure does nothing if FLookupDataset is nil or if any of FLookupkeyFields, FLookupResultField or FKeyFields is an empty string.
180146
181-First 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)
147+First 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)
182148
183-Thereafter 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.
149+Thereafter 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.
184150
185-Then 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]
151+Then 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\]
186152
187-(As said, here Enablecontrols is called and the value of FLookupDataset.Active is restored.)
153+(As said, here Enablecontrols is called and the value of FLookupDataset.Active is restored.)
188154
189155**Related to**
190156
191- * TField.LookupCache
192- * TField.LookupDataSet
193- * TField.LookupKeyFields
194- * TField.LookupList
195- * TField.LookupResultField
157+- TField.LookupCache
158+- TField.LookupDataSet
159+- TField.LookupKeyFields
160+- TField.LookupList
161+- TField.LookupResultField
196162
197163## TODO for the RTL
198164
199- * [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.
200- * [Done] Fix test for streaming of enumeration with value equal to low(TMyEnum) and no explicit
165+- \[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.
166+- \[Done\] Fix test for streaming of enumeration with value equal to low(TMyEnum) and no explicit
201167
202-default value, which means a default value of low(TMyEnum). Currently fpc 2.2.0 behaves differently from Delphi, the tests don't reflect this.
168+default value, which means a default value of low(TMyEnum). Currently fpc 2.2.0 behaves differently from Delphi, the tests don't reflect this.
203169
204- * [Done] There is a memleak in TTESTRESOLVEREFERENCE tests. SetupARef1A is called but the
170+- \[Done\] There is a memleak in TTESTRESOLVEREFERENCE tests. SetupARef1A is called but the
205171
206-returned object is not freed.
172+returned object is not freed.
207173
208- * There are no tests for TReader.FindComponentClass
174+- There are no tests for TReader.FindComponentClass
209175
210176## Variant streaming implementation
211177
212-The 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.
178+The 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.
213179
214180### General considerations
215181
216-Variant 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.
182+Variant 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.
217183
218-One 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.
184+One 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.
219185
220-Current FPC-implemented Delphi-compatible writer (TWriter) works in such manner -
186+Current FPC-implemented Delphi-compatible writer (TWriter) works in such manner -
221187
222- * It enumerates properties, determining property type.
223- * Using the property type as selector it branches to specified property storage routine.
224- * 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).
225- * Using methods of internal TBinaryObjectWriter object, referred as Driver, Writer stores value in the stream.
188+- It enumerates properties, determining property type.
189+- Using the property type as selector it branches to specified property storage routine.
190+- 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).
191+- Using methods of internal TBinaryObjectWriter object, referred as Driver, Writer stores value in the stream.
226192
227193#### Storing property of type Variant
228194
229-For 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.
195+For 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.
230196
231-There 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:
197+There 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:
232198
233- * varShortInt,varSmallInt,varInteger,varInt64,varQWord, which can be stored using fDriver.WriteInteger()
234- * varBoolean, which can be stored using fDriver.WriteBoolean()
199+- varShortInt,varSmallInt,varInteger,varInt64,varQWord, which can be stored using fDriver.WriteInteger()
200+- varBoolean, which can be stored using fDriver.WriteBoolean()
235201
236-Floating-point variants include:
202+Floating-point variants include:
237203
238- * varSingle, which can be stored by fDriver.WriteSingle
239- * varDouble,varDate,varCurrency, which can be handled as double and stored using fDriver.WriteFloat
204+- varSingle, which can be stored by fDriver.WriteSingle
205+- varDouble,varDate,varCurrency, which can be handled as double and stored using fDriver.WriteFloat
240206
241-String-holding variants: All the flavours of strings. IMO the string should be extracted as widestring and then stored as UTF8 representation.
207+String-holding variants: All the flavours of strings. IMO the string should be extracted as widestring and then stored as UTF8 representation.
242208
243-Other variant flavours (varXXXX) should be considered for now as non-implemented.
209+Other variant flavours (varXXXX) should be considered for now as non-implemented.
244210
245-One 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.
211+One 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.
246212
247213#### Reading property of type Variant
248214
249-Reading of variants consists of the following:
215+Reading of variants consists of the following:
250216
251- * Once we have determined the property is variant, we have to read value type (ReadValue) from stream.
252- * 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.
253- * We have to initialise variant using RTL assignment magic ( Variant := SomeGenericValue; ), which would implicitly invoke corresponding variant initialiser)
254- * Once we got assigned variant, we return it
217+- Once we have determined the property is variant, we have to read value type (ReadValue) from stream.
218+- 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.
219+- We have to initialise variant using RTL assignment magic ( Variant := SomeGenericValue; ), which would implicitly invoke corresponding variant initialiser)
220+- Once we got assigned variant, we return it
255221
256-Special 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.
222+Special 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.
257223
258-The very special consideration should be done for string Zoo. Read string should be converted to WideString and stored in variant.
224+The very special consideration should be done for string Zoo. Read string should be converted to WideString and stored in variant.
259225
260226## Bug reports
261227
262- * [11060](<http://bugs.freepascal.org/view.php?id=11060>): Reader fails to assign class properties
263- * [11059](<http://bugs.freepascal.org/view.php?id=11059>): TComponent.DefineProperties not called
228+- [11060](http://bugs.freepascal.org/view.php?id=11060): Reader fails to assign class properties
229+- [11059](http://bugs.freepascal.org/view.php?id=11059): TComponent.DefineProperties not called
264230
265231---
266-*From the Free Pascal wiki, page "FPC Cleanroom", as the Internet Archive kept it on 2025-01-13. 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).*
232+*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).*
233+Category: Old or unknown pages

Back to history