- changed status to resolved
Initialization order should honour dependencies
I propose that the unit initialization order be changed to follow the Delphi model where the interface dependency tree defines the order.
Consider the following three units, the first one being the main script module:
unit Unit1;
interface
uses
Unit2;
implementation
initialization
PrintLn(CurrentSourceCodeLocation.File);
finalization
end;
unit Unit2;
interface
uses
Unit3;
implementation
initialization
PrintLn(CurrentSourceCodeLocation.File);
finalization
end;
unit Unit3;
interface
implementation
initialization
PrintLn(CurrentSourceCodeLocation.File);
finalization
end;
The output of this script should be:
Unit3
Unit2
*MainModule*
but currently is:
*MainModule*
Unit2
Unit3
Comments (11)
-
repo owner -
reporter The current initialization order is just the order the units were added to the unit list (
TdwsCompiler.FProg.UnitMains
).The terms used in
TdwsCompiler.SetupInitializationFinalization
suggests that the list were, at some point, meant to be ordered by some criteria, but this never happens. Maybe the (unused)TUnitMainSymbol.InitializationRank
property had something to do with that.I'm now using the following hack to ensure that the initialization order is as I would expect it to (Added stuff marked with {*}):
function TdwsCompiler.HandleExplicitDependency(const unitName : UnicodeString) : TUnitSymbol; var HasBumpedUnit: boolean; {*} procedure BumpUnit(RefUnit: TUnitMainSymbol); {*} var i: integer; begin if (HasBumpedUnit) or (RefUnit = nil) then exit; HasBumpedUnit := True; // Move unit to end of unit list so it will be initialized after the units it references i := FProg.UnitMains.IndexOf(RefUnit); ASSERT(i <> -1); if (i <> FProg.UnitMains.Count-1) then begin FProg.UnitMains.Extract(i); FProg.UnitMains.Add(RefUnit); end; end; ... begin HasBumpedUnit := False; {*} ... i:=FUnits.IndexOfName(unitName); if i<0 then begin if Assigned(FOnNeedUnit) then unitResolved:=FOnNeedUnit(unitName, unitSource); if unitResolved<>nil then FUnits.Add(unitResolved) else begin if unitSource='' then unitSource:=GetScriptSource(unitName+'.pas'); if unitSource<>'' then begin srcUnit:=TSourceUnit.Create(unitName, FProg.Root.RootTable, FProg.UnitMains); unitResolved:=srcUnit; FUnits.Add(unitResolved); oldContext:=FSourceContextMap.SuspendContext; SwitchTokenizerToUnit(srcUnit, unitSource); FSourceContextMap.ResumeContext(oldContext); BumpUnit(srcUnit.Symbol); {*} end; end; ... end;
function TdwsCompiler.Compile(const aCodeText : UnicodeString; aConf : TdwsConfiguration; const mainFileName : String = '') : IdwsProgram; procedure BumpUnit(RefUnit: TUnitMainSymbol); {*} var i: integer; begin if (RefUnit = nil) then exit; // Move unit to end of unit list so it will be initialized after the units it references i := FProg.UnitMains.IndexOf(RefUnit); ASSERT(i <> -1); if (i <> FProg.UnitMains.Count-1) then begin FProg.UnitMains.Extract(i); FProg.UnitMains.Add(RefUnit); end; end; ... begin ... // Start compilation FProg.Expr:=ReadScript(sourceFile, stMain); ReadScriptImplementations; BumpUnit(FCurrentUnitSymbol); {*} ... end;
TdwsCompiler.RecompileInContext
needs the same treatment but I've not touched that (yet). -
reporter Our comments crossed. I'll try your fix, but I think you've missed some cases.
-
reporter - changed status to open
If you change the uses clause of Unit1 to this:
uses Unit3, Unit2;
then the initialization order should still be:
Unit3
Unit2
*MainModule*since unit2 uses unit3.
-
repo owner Hmm, I guess there also needs to be test cases for circular references and a few dependency graphs.
What is Delphi ordering behavior for unit references specified in the implementation?
-
reporter As far as I know it's undefined (or at least undocumented), but I believe my
fixhack handles it too.There are already checks for circular references. Search for CPE_UnitCircularReference.
-
repo owner Circular references in implementations are allowed, it's only in the interfaces they are not allowed.
Just made a few checks in Delphi, the initialization order does not seem to be affected whether the uses is in interface or implementation and it handles circular references "correctly".
F.i is Unit1 -> ( Unit2 <-> Unit 3 ) -> Unit4
1 & 4 will be in the appropriate position, 2 & 3 (circular dependency) do not have a well defined order (2 or 3 comes ahead, depending, which is ok)
-
repo owner Committed a graph-based resolution, more testing probably required :)
-
reporter Impressive. I'll give it a spin.
Note that it would probably be best to test both the original:
uses Unit2;
and the new:
uses Unit3, Unit2;
since they are exercising two different problems.
-
reporter Works fine here. Thanks.
-
repo owner - changed status to closed
- Log in to comment
Reversed initialization/finalization order to follow Delphi's order Fixed Issue
#28→ <<cset 2899ea919f36>>