123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- unit Rtl.UnitLoader;
- interface
- { $define DEBUGUNITLOADER}
- uses SysUtils, JS, Types;
- Type
- EUnitLoader = Class(Exception);
- TLoadedProcedure = Reference to Procedure(const aUnitNames : Array of String; aData : TObject);
- { TLoadTask }
- TLoadTask = Class(TObject)
- Private
- FUnitNames : TStringDynArray; // unit names case sensitive!
- FInitUnitNames : TStringDynArray; // unit names case sensitive!
- FOnLoaded : TLoadedProcedure;
- FData : TObject;
- function GetAllLoaded : Boolean;
- Protected
- Procedure CallLoaded;
- Public
- Constructor Create(Const aUnitNames : Array of string; aOnLoaded : TLoadedProcedure; aData : TObject);
- Procedure UnitLoaded(Const aUnitName : String);
- Property AllLoaded : Boolean Read GetAllLoaded;
- Property LoadUnitNames : TStringDynArray Read FUnitNames;
- Property OnLoaded : TLoadedProcedure Read FOnLoaded;
- Property Data : TObject Read FData;
- end;
- { TUnitLoader }
- TUnitLoader = Class(TObject)
- Private
- Class var FInstance : TUnitLoader;
- procedure DoDependenciesLoaded(const aUnitName: array of string;
- aData: TObject);
- Private
- FBaseURL : String;
- FLoadList : TStringDynArray; // unitnames case sensitive!
- function IndexOfLoadUnit(aUnitName : String): integer;
- protected
- Procedure AddToLoadList(aUnitName : String);
- Procedure RemoveFromLoadList(aUnitName : String);
- function IsInLoadList(aUnitName: String): Boolean;
- function GetUnitURL(const aUnitName: string): String; virtual;
- procedure InitModule(aTask: TLoadTask; const aName: String; aModule : JSValue); virtual;
- procedure DoLoadUnits(const aUnitNames: array of String; aOnLoaded: TLoadedProcedure; aData: TObject); virtual;
- function AreAllDependenciesLoaded(aTask: TLoadTask; const aName: String; AModule: JSValue): Boolean; virtual;
- function GetNeededDependencies(const aName: String; AModule: JSValue): TStringDynArray;
- procedure UnitSourcesLoaded(aData : TObject); virtual;
- Public
- Class Function Instance : TUnitLoader;
- function FindModule(aModuleName: string): JSValue;
- function HaveModule(aModuleName: string): Boolean;
- procedure LoadUnit(Const aUnitName : string; aOnLoaded : TLoadedProcedure = Nil; aData : TObject = Nil);
- procedure LoadUnits(Const aUnitNames : Array of String; aOnLoaded : TLoadedProcedure = Nil; aData : TObject = Nil);
- Property BaseURL : String Read FBaseUrl Write FBaseURL;
- end;
-
-
- Implementation
- uses Rtl.ScriptLoader;
- function IndexOfI(arr: TStringDynArray; Name: string): integer;
- begin
- Result:=length(arr)-1;
- while (Result>=0) and not SameText(arr[Result],Name) do
- dec(Result);
- end;
- function TLoadTask.GetAllLoaded: Boolean;
- begin
- Result:=Length(FInitunitNames)=0;
- end;
- procedure TLoadTask.CallLoaded;
- begin
- if Assigned(OnLoaded) then
- OnLoaded(LoadUnitNames,Data);
- end;
- constructor TLoadTask.Create(const aUnitNames: array of string;
- aOnLoaded: TLoadedProcedure; aData: TObject);
- Var
- I : Integer;
- begin
- SetLength(FunitNames,Length(aUnitNames));
- SetLength(FInitUnitNames,Length(aUnitNames));
- for I:=Low(aUnitNames) to High(aUnitNames) do
- begin
- FUnitNames[i]:=aUnitNames[i];
- FInitUnitNames[i]:=aUnitNames[i];
- end;
- FOnLoaded:=aOnLoaded;
- FData:=aData;
- end;
- procedure TLoadTask.UnitLoaded(const aUnitName: String);
- var
- Idx : integer;
- begin
- {$IFDEF DEBUGUNITLOADER}Writeln('Unit ',aUnitName,' loaded, removing from list');{$ENDIF}
- Idx:=IndexOfI(FInitUnitNames,aUnitName);
- if Idx>-1 then
- TJSArray(FInitUnitNames).splice(Idx,1);
- end;
- class function TUnitLoader.Instance: TUnitLoader;
- begin
- if (FInstance=Nil) then
- FInstance:=TUnitLoader.Create;
- Result:=FInstance;
- end;
- Procedure LoadIntf(aModule : JSValue); external name 'rtl.loadintf';
- Procedure LoadImpl(aModule : JSValue); external name 'rtl.loadimpl';
- var pas : TJSOBject; external name 'pas';
- function TUnitLoader.FindModule(aModuleName: string): JSValue;
- var
- Key: string;
- begin
- Result:=pas[aModuleName];
- if isModule(Result) then exit;
- for Key in pas do
- begin
- if not SameText(Key,aModuleName) then continue;
- Result:=pas[Key];
- if isModule(Result) then exit;
- end;
- Result:=nil;
- end;
- function TUnitLoader.HaveModule(aModuleName: string): Boolean;
- begin
- Result:=FindModule(aModuleName)<>nil;
- end;
- procedure TUnitLoader.InitModule(aTask: TLoadTask; const aName: String;
- aModule: JSValue);
- begin
- {$IFDEF DEBUGUNITLOADER} Writeln('Unit ',aName,' dependencies loaded. Initialising "',TJSObject(aModule)['$name'],'" ...');{$ENDIF}
- RemoveFromLoadList(aName);
- LoadIntf(aModule);
- LoadImpl(aModule);
- aTask.UnitLoaded(aName);
- end;
- function TUnitLoader.GetNeededDependencies(const aName: String; AModule: JSValue
- ): TStringDynArray;
- var
- l,u : TStringDynArray;
- m : String;
- begin
- SetLength(l,0);
- u:=TStringDynArray(TJSOBject(aModule)['$intfuseslist']);
- for m in u do
- if not (HaveModule(m) or IsInLoadList(m)) then
- TJSArray(l).push(m);
- u:=TStringDynArray(TJSOBject(aModule)['$impluseslist']);
- for m in u do
- if not (HaveModule(m) or IsInLoadList(m)) then
- TJSArray(l).push(m);
- Result:=l;
- end;
- function TUnitLoader.AreAllDependenciesLoaded(aTask: TLoadTask;
- const aName: String; AModule: JSValue): Boolean;
- begin
- Result:=Length(GetNeededDependencies(aName,aModule))=0;
- end;
- procedure TUnitLoader.DoDependenciesLoaded(const aUnitName : array of string; aData : TObject);
- begin
- UnitSourcesLoaded(aData);
- end;
- function TUnitLoader.IndexOfLoadUnit(aUnitName: String): integer;
- begin
- Result:=IndexOfI(FLoadList,aUnitName);
- end;
- procedure TUnitLoader.AddToLoadList(aUnitName: String);
- begin
- if IndexOfLoadUnit(aUnitName)<0 then
- TJSArray(FLoadList).Push(aUnitName);
- end;
- procedure TUnitLoader.RemoveFromLoadList(aUnitName: String);
- var
- idx : Integer;
- begin
- Idx:=IndexOfLoadUnit(aUnitName);
- if Idx>-1 then
- TJSArray(FLoadList).splice(Idx,1);
- end;
- function TUnitLoader.IsInLoadList(aUnitName: String): Boolean;
- begin
- Result:=IndexOfLoadUnit(aUnitName)>=0;
- end;
- procedure TUnitLoader.UnitSourcesLoaded(aData : TObject);
- Var
- aTask : TLoadTask;
- aModule : JSValue;
- aModuleName : String;
- Deps : TStringDynArray;
- begin
- {$IFDEF DEBUGUNITLOADER} Writeln('Succesfully loaded sources');{$ENDIF}
- aTask:=TLoadTask(aData);
- For aModuleName in aTask.LoadUnitNames do
- begin
- aModule:=FindModule(aModuleName);
- if aModule<>nil then
- begin
- {$IFDEF DEBUGUNITLOADER} Writeln(aModuleName+' is module. Loading interface');{$ENDIF}
- Deps:=GetNeededDependencies(aModuleName,aModule);
- if length(Deps)=0 then
- InitModule(aTask,aModuleName,aModule)
- else
- DoLoadUnits(Deps,@DoDependenciesLoaded,aData);
- end;
- end;
- if (aTask.AllLoaded) then
- aTask.CallLoaded;
- end;
- function TUnitLoader.GetUnitURL(const aUnitName: string): String;
- begin
- Result:=BaseURL;
- if (Result<>'') then
- Result:=Result+'/';
- Result:=Result+aUnitname+'.js';
- end;
- procedure TUnitLoader.LoadUnit(const aUnitName : string; aOnLoaded : TLoadedProcedure = Nil; aData : TObject = Nil);
- begin
- LoadUnits([aUnitName],aOnLoaded,aData);
- end;
- procedure TUnitLoader.LoadUnits(const aUnitNames: array of String; aOnLoaded: TLoadedProcedure; aData: TObject);
- begin
- if Length(FLoadList)>0 then
- Raise EUnitLoader.Create('Load operation in progress. Cannot load.');
- DoLoadUnits(aUnitNames,aOnLoaded,aData);
- end;
- procedure TUnitLoader.DoLoadUnits(const aUnitNames: array of String; aOnLoaded: TLoadedProcedure; aData: TObject);
- Var
- Scripts : TStringDynArray;
- aCount : Integer;
- S : String;
- begin
- aCount:=0;
- Setlength(Scripts,Length(aUnitNames));
- for s in aUnitNames do
- if Not HaveModule(S) then
- begin
- {$IFDEF DEBUGUNITLOADER} Writeln('Need to load unit: ',S);{$ENDIF}
- Scripts[aCount]:=GetUnitURl(S);
- AddToLoadList(S);
- inc(aCount);
- end;
- SetLength(S,aCount);
- if aCount=0 then
- begin
- // All is already loaded
- if Assigned(aOnLoaded) then
- aOnLoaded(aUnitNames,aData);
- end
- else
- LoadScripts(Scripts,@UnitSourcesLoaded,TLoadTask.Create(aUnitNames,aOnLoaded,aData));
- end;
- end.
|