Browse Source

* Hosting example for webassembly module

Michaël Van Canneyt 1 year ago
parent
commit
ec21c75e05

+ 18 - 0
packages/wasm-job/examples/WebHost/README.md

@@ -0,0 +1,18 @@
+This directory contains a generic HTML loader page for a WebAssembly module
+that needs a WASI and JOB hosting environment.
+
+Compile the .lpr with pas2js, and load the page in your browser. 
+By default the page will attempt to load a demo.wasm module.
+
+You can specify the module to load in 1 of 3 ways:
+
+  1. from external variable wasmFilename. 
+     To this end, copy hostconfig-template.js to hostconfig.js.
+     In this file, change the name of the webassembly to load.
+
+  2. from the first part of hash:  #moduleName/
+
+  3. from query varable wasmmodule: ?wasmmodule=x
+
+the extension .wasm must be specified.
+

File diff suppressed because it is too large
+ 0 - 0
packages/wasm-job/examples/WebHost/bulma.min.css


+ 5 - 0
packages/wasm-job/examples/WebHost/hostconfig-template.js

@@ -0,0 +1,5 @@
+/*
+ *  copy this template as hostconfig.js and adapt to your situation.
+ */
+var
+  wasmFilename = 'promisedemo.wasm';

+ 26 - 0
packages/wasm-job/examples/WebHost/index.html

@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Wasm Javascript Object Bindings - Test bed</title>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> <!-- Prevents caching -->
+    <meta http-equiv="Pragma" content="no-cache"> <!-- Legacy HTTP 1.0 backward compatibility -->
+    <meta http-equiv="Expires" content="0"> <!-- Proxies -->
+    <link href="bulma.min.css" rel="stylesheet">
+    <link type="stylesheet" src="bulma.min.css">
+    <script src="hostconfig.js"></script>
+    <script src="webhost.js"></script>
+</head>
+<body>
+    <div class="box">
+      <p class="title is-3">Webassembly JOB test bed</p>
+      <p class="subtitle is-5">Console output:</p>
+      <div id="pasjsconsole">
+      </div>
+    </div>
+  <script>
+    rtl.showUncaughtExceptions=true;
+    rtl.run();
+  </script>
+</body>
+</html>

+ 4 - 0
packages/wasm-job/examples/WebHost/simpleserver.ini

@@ -0,0 +1,4 @@
+[Server]
+Port=3030
+Directory=/home/tixeo/fpc/packages/wasm-job/examples
+

+ 104 - 0
packages/wasm-job/examples/WebHost/webhost.lpi

@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+        <Runnable Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="Browser loader for Webassembly module with JOB functionality."/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="4">
+      <Item0 Name="MaintainHTML" Value="1"/>
+      <Item1 Name="Pas2JSProject" Value="1"/>
+      <Item2 Name="PasJSLocation" Value="BrowserTixeoDom"/>
+      <Item3 Name="PasJSWebBrowserProject" Value="1"/>
+    </CustomData>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="webhost.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="../index.html"/>
+        <IsPartOfProject Value="True"/>
+        <CustomData Count="1">
+          <Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
+        </CustomData>
+      </Unit>
+      <Unit>
+        <Filename Value="../../../job/job_browser.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="JOB_Browser"/>
+      </Unit>
+      <Unit>
+        <Filename Value="../../../job/job_shared.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="JOB_Shared"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target FileExt=".js">
+      <Filename Value="webhost"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../../../job"/>
+      <UnitOutputDirectory Value="js"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <AllowLabel Value="False"/>
+        <UseAnsiStrings Value="False"/>
+        <CPPInline Value="False"/>
+      </SyntaxOptions>
+    </Parsing>
+    <CodeGeneration>
+      <TargetOS Value="browser"/>
+    </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="False"/>
+        <UseLineInfoUnit Value="False"/>
+      </Debugging>
+    </Linking>
+    <Other>
+      <CustomOptions Value="-Jeutf-8
+-Jirtl.js
+-Jc
+-Jminclude"/>
+      <CompilerPath Value="$(pas2js)"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 74 - 0
packages/wasm-job/examples/WebHost/webhost.lpr

@@ -0,0 +1,74 @@
+program webhost;
+
+{$mode objfpc}
+
+uses
+  BrowserConsole, JS, Types, Classes, SysUtils, Web, WasiEnv, WasiHostApp, JOB_Browser, JOB_Shared;
+
+
+var
+  wasmFilename : string; external name 'wasmFilename';
+
+Type
+
+  { TMyApplication }
+
+  TMyApplication = class(TBrowserWASIHostApplication)
+  Private
+    FJOB : TJSObjectBridge;
+    function GetWasmModuleName: String;
+  Public
+    constructor Create(aOwner : TComponent); override;
+    procedure DoRun; override;
+  end;
+
+{ TMyApplication }
+
+constructor TMyApplication.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FJOB:=TJSObjectBridge.Create(WasiEnvironment);
+  RunEntryFunction:='_initialize';
+end;
+
+function TMyApplication.GetWasmModuleName : String;
+{ Determine webassembly module to run
+  1. from external variable wasmFilename
+  2. from first part of hash:  #moduleName/
+  3. from query varable wasmmodule: ?wasmmodule=x
+  4. Hardcoded 'demo.wasm';
+}
+
+begin
+  Result:='';
+  if IsString(wasmFilename) then
+    Result:=wasmFilename;
+  if (Result='') then
+    Result:=ParamStr(1);
+  if (Result='') then
+    Result:=GetEnvironmentVar('wasmmodule');
+  if Result='' then
+    Result:='demo.wasm';
+end;
+
+procedure TMyApplication.DoRun;
+
+var
+  WasmModule : String;
+
+begin
+  Terminate;
+  WasmModule:=GetWasmModuleName;
+  Writeln('Loading & starting webassembly module :' ,WasmModule);
+  StartWebAssembly('WasmModule.wasm',true);
+end;
+
+var
+  Application : TMyApplication;
+begin
+  ConsoleStyle:=DefaultCRTConsoleStyle;
+  HookConsole;
+  Application:=TMyApplication.Create(nil);
+  Application.Initialize;
+  Application.Run;
+end.

+ 0 - 0
packages/wasm-job/examples/promisedemo.lpi → packages/wasm-job/examples/promise-1/promisedemo.lpi


+ 0 - 0
packages/wasm-job/examples/promisedemo.lpr → packages/wasm-job/examples/promise-1/promisedemo.lpr


+ 73 - 0
packages/wasm-job/examples/promise-2/promisedemo2.lpi

@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="promise demo: delayed resolve"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="promisedemo2.lpr"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="promisedemo"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="promisedemo2.wasm" ApplyConventions="False"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <CodeGeneration>
+      <TargetCPU Value="wasm32"/>
+      <TargetOS Value="wasi"/>
+    </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="False"/>
+      </Debugging>
+      <Options>
+        <ExecutableType Value="Library"/>
+      </Options>
+    </Linking>
+    <Other>
+      <CustomOptions Value="-Ur"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 134 - 0
packages/wasm-job/examples/promise-2/promisedemo2.lpr

@@ -0,0 +1,134 @@
+library promisedemo;
+{$mode objfpc}
+{$h+}
+uses nothreads, sysutils, job.js, variants;
+
+Type
+  TFunction = function (const arguments: Variant): Variant of object;
+
+  IJSWindow = interface(IJSObject)
+    function setTimeout(const aHandler: TFunction; aTimeout: LongInt; const aArguments: Variant): LongInt{; ToDo:varargs};
+    function setTimeout(const aHandler: TFunction): LongInt{; ToDo:varargs};
+    function setTimeout(const aHandler: UnicodeString; aTimeout: LongInt; const aUnused: Variant): LongInt{; ToDo:varargs};
+    function setTimeout(const aHandler: UnicodeString): LongInt{; ToDo:varargs};
+  end;
+
+  TJSWindow = class(TJSObject,IJSWindow)
+    function setTimeout(const aHandler: TFunction; aTimeout: LongInt; const aArguments: Variant): LongInt{; ToDo:varargs};
+    function setTimeout(const aHandler: TFunction): LongInt{; ToDo:varargs};
+    function setTimeout(const aHandler: UnicodeString; aTimeout: LongInt; const aUnused: Variant): LongInt{; ToDo:varargs};
+    function setTimeout(const aHandler: UnicodeString): LongInt{; ToDo:varargs};
+  end;
+
+  TApp = Class(TObject)
+  private
+    FOnResolve: TJSPromiseResolver;
+    FOnReject: TJSPromiseResolver;
+  public
+    function DoTimeout(const arguments: Variant): Variant;
+    function DoResolve(const aValue: Variant): Variant;
+    procedure DoPromiseExecutor(const OnResolve, OnReject:TJSPromiseResolver);
+    function ResolveTest: TJSPromise;
+    procedure Run;
+  end;
+
+var
+  JSWindow : IJSWindow;
+
+function JOBCallFunction_(const aMethod: TMethod; var H: TJOBCallbackHelper): PByte;
+var
+  arguments: Variant;
+begin
+  arguments:=H.GetVariant;
+  Result:=H.AllocVariant(TFunction(aMethod)(arguments));
+end;
+
+
+function TJSWindow.setTimeout(const aHandler: TFunction; aTimeout: LongInt; const aArguments: Variant): LongInt{; ToDo:varargs};
+var
+  m: TJOB_Method;
+begin
+  m:=TJOB_Method.Create(TMethod(aHandler),@JOBCallFunction_);
+  try
+    Result:=InvokeJSLongIntResult('setTimeout',[m,aTimeout,aArguments]);
+  finally
+    m.free;
+  end;
+end;
+function TJSWindow.setTimeout(const aHandler: TFunction): LongInt{; ToDo:varargs};
+var
+  m: TJOB_Method;
+begin
+  m:=TJOB_Method.Create(TMethod(aHandler),@JOBCallFunction_);
+  try
+    Result:=InvokeJSLongIntResult('setTimeout',[m]);
+  finally
+    m.free;
+  end;
+end;
+function TJSWindow.setTimeout(const aHandler: UnicodeString; aTimeout: LongInt; const aUnused: Variant): LongInt{; ToDo:varargs};
+begin
+  Result:=InvokeJSLongIntResult('setTimeout',[aHandler,aTimeout,aUnused]);
+end;
+function TJSWindow.setTimeout(const aHandler: UnicodeString): LongInt{; ToDo:varargs};
+begin
+  Result:=InvokeJSLongIntResult('setTimeout',[aHandler]);
+end;
+
+
+{ TApp }
+
+function TApp.DoTimeout(const arguments: Variant): Variant;
+begin
+  if not Assigned(FOnResolve) then
+    Writeln('Wasm ERROR: no resolve callback');
+  if not Assigned(FOnReject) then
+    Writeln('Wasm ERROR: no reject callback');
+
+  if Assigned(FOnResolve) then
+    FOnResolve('resolved');
+end;
+
+function TApp.DoResolve(const aValue: Variant): Variant;
+begin
+  Writeln('Wasm: in DoResolve: success. Argument vartype: ', vartype(aValue));
+  if vartype(aValue)=varOleStr then
+    Writeln('Wasm: DoResolve received value: ', VarToStr(aValue));
+  result:=unassigned;
+end;
+
+procedure TApp.DoPromiseExecutor(const OnResolve, OnReject: TJSPromiseResolver);
+begin
+  FOnResolve := OnResolve;
+  FOnReject := OnReject;
+
+  JSWindow.setTimeout(@DoTimeout, 1000, nil);
+end;
+
+function TApp.ResolveTest: TJSPromise;
+begin
+  Result:=TJSPromise.Create(@DoPromiseExecutor);
+end;
+
+procedure TApp.Run;
+
+Var
+  P : TJSPromise;
+
+begin
+  try
+    P:=ResolveTest;
+    P._then(@DoResolve);
+  except
+    on E: Exception do
+      Writeln(e.Message);
+  end;
+end;
+
+var
+  App : TApp;
+begin
+  JSWindow:=TJSWindow.JOBCreateGlobal('window');
+  App:=TApp.Create;
+  App.Run;
+end.

Some files were not shown because too many files changed in this diff