wasmjob: global objects via names

This commit is contained in:
mattias 2022-07-13 18:10:14 +02:00
parent 264336babc
commit 25172f1dd1
8 changed files with 93 additions and 144 deletions

View File

@ -3,8 +3,7 @@ program BrowserButton1;
{$mode objfpc}
uses
BrowserConsole, BrowserApp, JS, Classes, SysUtils, Web, WebAssembly, Types,
WasiEnv, WasiHostApp, JOB_Shared, JOB_Browser;
BrowserConsole, JS, Classes, SysUtils, Web, WasiEnv, WasiHostApp, JOB_Browser;
Type
@ -16,7 +15,7 @@ Type
function OnBeforeStart(Sender: TObject;
aDescriptor: TWebAssemblyStartDescriptor): Boolean;
Public
Constructor Create(aOwner : TComponent); override;
constructor Create(aOwner : TComponent); override;
procedure DoRun; override;
end;
@ -35,7 +34,6 @@ begin
end;
procedure TMyApplication.DoRun;
begin
// Your code here
StartWebAssembly('WasiButton1.wasm',true,@OnBeforeStart);

View File

@ -14,10 +14,11 @@
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<CustomData Count="3">
<CustomData Count="4">
<Item0 Name="MaintainHTML" Value="1"/>
<Item1 Name="Pas2JSProject" Value="1"/>
<Item2 Name="PasJSWebBrowserProject" Value="1"/>
<Item2 Name="PasJSLocation" Value="BrowserDomTest1"/>
<Item3 Name="PasJSWebBrowserProject" Value="1"/>
</CustomData>
<BuildModes>
<Item Name="Default" Default="True"/>

View File

@ -3,8 +3,7 @@ program BrowserDomTest1;
{$mode objfpc}
uses
BrowserConsole, BrowserApp, JS, Classes, SysUtils, Web, WebAssembly, Types,
wasienv, job_shared, JOB_Browser;
BrowserConsole, JS, Classes, SysUtils, Web, WasiEnv, WasiHostApp, JOB_Browser, JOB_Shared;
Type
@ -28,20 +27,13 @@ Type
{ TMyApplication }
TMyApplication = class(TBrowserApplication)
TMyApplication = class(TBrowserWASIHostApplication)
Private
FWasiEnv: TPas2JSWASIEnvironment;
FMemory : TJSWebAssemblyMemory; // Memory of webassembly
FTable : TJSWebAssemblyTable; // Table of exported functions
FWADomBridge : TJSObjectBridge;
function CreateWebAssembly(Path: string; ImportObject: TJSObject
): TJSPromise;
procedure DoWrite(Sender: TObject; const aOutput: String);
function InitEnv(aValue: JSValue): JSValue;
procedure InitWebAssembly;
function OnBeforeStart(Sender: TObject;
aDescriptor: TWebAssemblyStartDescriptor): Boolean;
Public
Constructor Create(aOwner : TComponent); override;
Destructor Destroy; override;
constructor Create(aOwner : TComponent); override;
procedure DoRun; override;
end;
@ -119,86 +111,23 @@ begin
Result:=Result+']';
end;
function TMyApplication.InitEnv(aValue: JSValue): JSValue;
Var
Module : TJSInstantiateResult absolute aValue;
Exps : TWASIExports;
InitFunc: TProc;
begin
Result:=True;
FWasiEnv.Instance:=Module.Instance;
Exps := TWASIExports(TJSObject(Module.Instance.exports_));
//writeln('TMyApplication.InitEnv wasm exports=',TJSObject.keys(Exps));
FWADomBridge.WasiExports:=Exps;
// init the library
InitFunc:=TProc(Exps.functions['_initialize']);
InitFunc();
end;
{ TMyApplication }
procedure TMyApplication.DoWrite(Sender: TObject; const aOutput: String);
function TMyApplication.OnBeforeStart(Sender: TObject;
aDescriptor: TWebAssemblyStartDescriptor): Boolean;
begin
Writeln(aOutput);
FWADomBridge.WasiExports:=aDescriptor.Exported;
Result:=true;
end;
constructor TMyApplication.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
FWasiEnv:=TPas2JSWASIEnvironment.Create;
FWasiEnv.OnStdErrorWrite:=@DoWrite;
FWasiEnv.OnStdOutputWrite:=@DoWrite;
FWADomBridge:=TJSObjectBridge.Create(FWasiEnv);
FWADomBridge:=TJSObjectBridge.Create(WasiEnvironment);
RunEntryFunction:='_initialize';
if FWADomBridge.RegisterGlobalObject(TJSObject(TBird.Create('Root')))<>JObjIdBird then
raise Exception.Create('Root TBird wrong number');
end;
function TMyApplication.CreateWebAssembly(Path: string; ImportObject: TJSObject): TJSPromise;
begin
Result:=window.fetch(Path)._then(Function (res : jsValue) : JSValue
begin
Result:=TJSResponse(Res).arrayBuffer._then(Function (res2 : jsValue) : JSValue
begin
Result:=TJSWebAssembly.instantiate(TJSArrayBuffer(res2),ImportObject);
end,Nil)
end,Nil
);
end;
procedure TMyApplication.InitWebAssembly;
Var
mDesc : TJSWebAssemblyMemoryDescriptor;
tDesc: TJSWebAssemblyTableDescriptor;
ImportObj : TJSObject;
begin
// Setup memory
mDesc.initial:=256;
mDesc.maximum:=256;
FMemory:=TJSWebAssemblyMemory.New(mDesc);
// Setup table
tDesc.initial:=0;
tDesc.maximum:=0;
tDesc.element:='anyfunc';
FTable:=TJSWebAssemblyTable.New(tDesc);
// Setup ImportObject
ImportObj:=new([
'js', new([
'mem', FMemory,
'tbl', FTable
])
]);
FWasiEnv.AddImports(ImportObj);
CreateWebAssembly('WasiDomTest1.wasm',ImportObj)._then(@InitEnv);
end;
destructor TMyApplication.Destroy;
begin
FreeAndNil(FWasiEnv);
inherited Destroy;
if FWADomBridge.RegisterGlobalObject(TBird.Create('Root'),'Bird')=0 then
raise Exception.Create('failed to register TBird');
end;
procedure TMyApplication.DoRun;
@ -206,7 +135,7 @@ procedure TMyApplication.DoRun;
begin
// Your code here
Terminate;
InitWebAssembly;
StartWebAssembly('WasiDomTest1.wasm',true,@OnBeforeStart);
end;
var

View File

@ -66,7 +66,7 @@ begin
exit;
obj:=TJSObject.JOBCreateFromID(JObjIdBird);
obj:=TJSObject.JOBCreateGlobal('Bird');
obj.WriteJSPropertyUnicodeString('Caption','Root');
writeln('AAA1 ');
//u:='äbc';

View File

@ -292,6 +292,7 @@ type
public
constructor JOBCast(Intf: IJSObject); overload;
constructor JOBCreateFromID(aID: TJOBObjectID); virtual; // use this only for the owner (it will release it on free)
constructor JOBCreateGlobal(const aID: UnicodeString); virtual;
class function Cast(Intf: IJSObject): IJSObject; overload;
destructor Destroy; override;
property JOBObjectID: TJOBObjectID read FJOBObjectID;
@ -439,6 +440,11 @@ function __job_invoke_arraystringresult(
ResultLenP: PByte // nativeint
): TJOBResult; external JOBExportName name JOBFn_InvokeArrayStringResult;
function __job_get_global(
NameP: PWideChar;
NameLen: longint
): TJOBObjectID; external JOBExportName name JOBFn_GetGlobal;
function JOBCallback(const Func: TJOBCallback; Data, Code: Pointer; Args: PByte): PByte;
function VarRecToJSValue(const V: TVarRec): TJOB_JSValue;
@ -1658,6 +1664,13 @@ begin
FJOBObjectID:=aID;
end;
constructor TJSObject.JOBCreateGlobal(const aID: UnicodeString);
begin
FJOBObjectID:=__job_get_global(PWideChar(aID),length(aID));
if FJOBObjectID=0 then
raise EJSObject.Create('JS object "'+String(aID)+'" is not registered');
end;
class function TJSObject.Cast(Intf: IJSObject): IJSObject;
begin
Result:=JOBCast(Intf);
@ -1989,8 +2002,8 @@ begin
end;
initialization
JSObject:=TJSObject.JOBCreateFromID(JOBObjIdObject) as IJSObject;
JSDate:=TJSDate.JOBCreateFromID(JOBObjIdDate) as IJSDate;
JSObject:=TJSObject.JOBCreateGlobal('Object') as IJSObject;
JSDate:=TJSDate.JOBCreateGlobal('Date') as IJSDate;
end.

View File

@ -6,7 +6,7 @@ unit JOB_Web;
interface
uses
Classes, SysUtils, JOB_Shared, JOB_WAsm, JOB_JS;
Classes, SysUtils, JOB_Shared, JOB_JS;
type
IJSEvent = interface;
@ -476,8 +476,8 @@ begin
end;
initialization
JSDocument:=TJSDocument.JOBCreateFromID(JOBObjIdDocument);
JSWindow:=TJSWindow.JOBCreateFromID(JOBObjIdWindow);
JSDocument:=TJSDocument.JOBCreateGlobal('document');
JSWindow:=TJSWindow.JOBCreateGlobal('window');
finalization
JSDocument.Free;
JSWindow.Free;

View File

@ -21,7 +21,8 @@ Type
TJSObjectBridge = class(TImportExtension)
Private
FCallbackHandler: TJOBCallback;
FGlobalObjects: TJSArray;
FGlobalObjects: TJSArray; // id to TJSObject
FGlobalNames: TJSObject; // name to id
FLocalObjects: TJSArray;
FFreeLocalIds: TJSArray; // free positions in FLocalObjects
FStringResult: string;
@ -33,6 +34,7 @@ Type
function CreateCallbackArgs(View: TJSDataView; const Args: TJSFunctionArguments): TWasmNativeInt; virtual;
function EatCallbackResult(View: TJSDataView; ResultP: TWasmNativeInt): jsvalue; virtual;
// exports
function Get_GlobalID(NameP, NameLen: NativeInt): TJOBObjectID; virtual;
function Invoke_NoResult(ObjId: TJOBObjectID; NameP, NameLen, Invoke, ArgsP: NativeInt): TJOBResult; virtual;
function Invoke_BooleanResult(ObjId: TJOBObjectID; NameP, NameLen, Invoke, ArgsP, ResultP: NativeInt): TJOBResult; virtual;
function Invoke_DoubleResult(ObjId: TJOBObjectID; NameP, NameLen, Invoke, ArgsP, ResultP: NativeInt): TJOBResult; virtual;
@ -48,8 +50,9 @@ Type
Procedure FillImportObject(aObject: TJSObject); override;
Function ImportName: String; override;
function FindObject(ObjId: TJOBObjectID): TJSObject; virtual;
function FindGlobalObject(const aName: string): TJOBObjectID; virtual; // 0=not found
function RegisterLocalObject(Obj: TJSObject): TJOBObjectID; virtual;
Function RegisterGlobalObject(Obj: TJSObject): TJOBObjectID; virtual;
Function RegisterGlobalObject(Obj: JSValue; const aName: string): TJOBObjectID; virtual;
Function GetJOBResult(v: jsvalue): TJOBResult;
property CallbackHandler: TJOBCallback read FCallbackHandler write FCallbackHandler;
property WasiExports: TWASIExports read FWasiExports write SetWasiExports;
@ -99,27 +102,30 @@ constructor TJSObjectBridge.Create(aEnv: TPas2JSWASIEnvironment);
begin
Inherited Create(aEnv);
FGlobalObjects:=TJSArray.new;
FGlobalObjects[-JOBObjIdDocument]:=document;
FGlobalObjects[-JOBObjIdWindow]:=window;
FGlobalObjects[-JOBObjIdConsole]:=console;
FGlobalObjects[-JOBObjIdCaches]:=caches;
FGlobalObjects[-JOBObjIdObject]:=TJSObject;
FGlobalObjects[-JOBObjIdFunction]:=TJSFunction;
FGlobalObjects[-JOBObjIdDate]:=TJSDate;
FGlobalObjects[-JOBObjIdString]:=TJSString;
FGlobalObjects[-JOBObjIdArray]:=TJSArray;
FGlobalObjects[-JOBObjIdArrayBuffer]:=TJSArrayBuffer;
FGlobalObjects[-JOBObjIdInt8Array]:=TJSInt8Array;
FGlobalObjects[-JOBObjIdUint8Array]:=TJSUint8Array;
FGlobalObjects[-JOBObjIdUint8ClampedArray]:=TJSUint8ClampedArray;
FGlobalObjects[-JOBObjIdInt16Array]:=TJSInt16Array;
FGlobalObjects[-JOBObjIdUint16Array]:=TJSUint16Array;
FGlobalObjects[-JOBObjIdInt32Array]:=TJSUint32Array;
FGlobalObjects[-JOBObjIdFloat32Array]:=TJSFloat32Array;
FGlobalObjects[-JOBObjIdFloat64Array]:=TJSFloat64Array;
FGlobalObjects[-JOBObjIdJSON]:=TJSJSON;
FGlobalObjects[-JOBObjIdPromise]:=TJSPromise;
FGlobalObjects.push(nil); // allocate FGlobalObjects[0]
FGlobalNames:=TJSObject.new;
RegisterGlobalObject(document,'document');
RegisterGlobalObject(window,'window');
RegisterGlobalObject(console,'console');
RegisterGlobalObject(caches,'caches');
RegisterGlobalObject(TJSObject,'Object');
RegisterGlobalObject(TJSFunction,'Function');
RegisterGlobalObject(TJSDate,'Date');
RegisterGlobalObject(TJSString,'String');
RegisterGlobalObject(TJSArray,'Array');
RegisterGlobalObject(TJSArrayBuffer,'ArrayBuffer');
RegisterGlobalObject(TJSInt8Array,'Int8Array');
RegisterGlobalObject(TJSUint8Array,'Uint8Array');
RegisterGlobalObject(TJSUint8ClampedArray,'Uint8ClampedArray');
RegisterGlobalObject(TJSInt16Array,'Int16Array');
RegisterGlobalObject(TJSUint16Array,'Uint16Array');
RegisterGlobalObject(TJSUint32Array,'Uint32Array');
RegisterGlobalObject(TJSFloat32Array,'Float32Array');
RegisterGlobalObject(TJSFloat64Array,'Float64Array');
RegisterGlobalObject(TJSJSON,'JSON');
RegisterGlobalObject(TJSPromise,'Promise');
FLocalObjects:=TJSArray.new;
FLocalObjects.push(nil); // allocate FLocalObjects[0]
FFreeLocalIds:=TJSArray.new;
end;
@ -128,13 +134,18 @@ begin
Result:=JOBExportName;
end;
function TJSObjectBridge.RegisterGlobalObject(Obj: TJSObject): TJOBObjectID;
function TJSObjectBridge.RegisterGlobalObject(Obj: JSValue; const aName: string
): TJOBObjectID;
begin
if FGlobalNames.hasOwnProperty(aName) then
raise EJOBBridge.Create('duplicate "'+aName+'"');
Result:=-(FGlobalObjects.push(Obj)-1);
FGlobalNames[aName]:=Result;
end;
procedure TJSObjectBridge.FillImportObject(aObject: TJSObject);
begin
aObject[JOBFn_GetGlobal]:=@Get_GlobalID;
aObject[JOBFn_InvokeNoResult]:=@Invoke_NoResult;
aObject[JOBFn_InvokeBooleanResult]:=@Invoke_BooleanResult;
aObject[JOBFn_InvokeDoubleResult]:=@Invoke_DoubleResult;
@ -157,6 +168,13 @@ begin
Result:=nil;
end;
function TJSObjectBridge.FindGlobalObject(const aName: string): TJOBObjectID;
begin
if not FGlobalNames.hasOwnProperty(aName) then
exit(0);
Result:=NativeInt(FGlobalNames[aName]);
end;
function TJSObjectBridge.RegisterLocalObject(Obj: TJSObject): TJOBObjectID;
var
NewId: JSValue;
@ -770,6 +788,19 @@ begin
end;
end;
function TJSObjectBridge.Get_GlobalID(NameP, NameLen: NativeInt
): TJOBObjectID;
var
View: TJSDataView;
aWords: TJSUint16Array;
aName: String;
begin
View:=getModuleMemoryDataView();
aWords:=TJSUint16Array.New(View.buffer, NameP, NameLen);
aName:=TypedArrayToString(aWords);
Result:=FindGlobalObject(aName);
end;
function TJSObjectBridge.GetJOBResult(v: jsvalue): TJOBResult;
begin
case jstypeof(v) of

View File

@ -51,6 +51,7 @@ const
);
JOBExportName = 'job';
JOBFn_GetGlobal = 'get_registered';
JOBFn_InvokeNoResult = 'invoke_noresult';
JOBFn_InvokeBooleanResult = 'invoke_boolresult';
JOBFn_InvokeDoubleResult = 'invoke_doubleresult';
@ -110,30 +111,6 @@ const
'New'
);
// JS base classes
JOBObjIdDocument = -1;
JOBObjIdWindow = -2;
JOBObjIdConsole = -3;
JOBObjIdCaches = -4;
JOBObjIdObject = -5;
JOBObjIdFunction = -6;
JOBObjIdDate = -7;
JOBObjIdString = -8;
JOBObjIdArray = -9;
JOBObjIdArrayBuffer = -10;
JOBObjIdInt8Array = -11;
JOBObjIdUint8Array = -12;
JOBObjIdUint8ClampedArray = -13;
JOBObjIdInt16Array = -13;
JOBObjIdUint16Array = -14;
JOBObjIdInt32Array = -16;
JOBObjIdFloat32Array = -17;
JOBObjIdFloat64Array = -18;
JOBObjIdJSON = -19;
JOBObjIdPromise = -20;
JObjIdBird = -21;
implementation
end.