mirror of
https://gitlab.com/freepascal.org/fpc/pas2js.git
synced 2025-04-26 23:43:36 +02:00
297 lines
8.0 KiB
ObjectPascal
297 lines
8.0 KiB
ObjectPascal
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.
|