瀏覽代碼

demo: wasidom: basic call

mattias 3 年之前
父節點
當前提交
5fa7378e3c

+ 92 - 0
demo/wasienv/dom/BrowserDomTest1.lpi

@@ -0,0 +1,92 @@
+<?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="BrowserDomTest1"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="2">
+      <Item0 Name="MaintainHTML" Value="1"/>
+      <Item1 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="BrowserDomTest1.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="wadom_browser.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target FileExt=".js">
+      <Filename Value="BrowserDomTest1"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="js"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <AllowLabel Value="False"/>
+        <CPPInline Value="False"/>
+        <UseAnsiStrings 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>

+ 117 - 0
demo/wasienv/dom/BrowserDomTest1.lpr

@@ -0,0 +1,117 @@
+program BrowserDomTest1;
+
+{$mode objfpc}
+
+uses
+  BrowserConsole, BrowserApp, JS, Classes, SysUtils, Web, WebAssembly, Types,
+  wasienv, wadom_browser;
+
+Type
+
+  { TMyApplication }
+
+  TMyApplication = class(TBrowserApplication)
+  Private
+    FWasiEnv: TPas2JSWASIEnvironment;
+    FMemory : TJSWebAssemblyMemory; // Memory of webassembly
+    FTable : TJSWebAssemblyTable; // Table of exported functions
+    FWADomBridge : TWADomBridge;
+    function CreateWebAssembly(Path: string; ImportObject: TJSObject
+      ): TJSPromise;
+    procedure DoWrite(Sender: TObject; const aOutput: String);
+    function initEnv(aValue: JSValue): JSValue;
+    procedure InitWebAssembly;
+  Public
+    Constructor Create(aOwner : TComponent); override;
+    Destructor Destroy; override;
+    procedure DoRun; override;
+  end;
+
+function TMyApplication.InitEnv(aValue: JSValue): JSValue;
+Var
+  Module : TJSInstantiateResult absolute aValue;
+  Exps : TWASIExports;
+begin
+  Result:=True;
+  Exps := TWASIExports(TJSObject(Module.Instance.exports_));
+  FWasiEnv.Instance:=Module.Instance;
+  //  console.info('got exports', exps);
+  Exps.Start;
+end;
+
+procedure TMyApplication.DoWrite(Sender: TObject; const aOutput: String);
+begin
+  Writeln(aOutput);
+end;
+
+constructor TMyApplication.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FWasiEnv:=TPas2JSWASIEnvironment.Create;
+  FWasiEnv.OnStdErrorWrite:=@DoWrite;
+  FWasiEnv.OnStdOutputWrite:=@DoWrite;
+  FWADomBridge:=TWADomBridge.Create(FWasiEnv);
+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;
+end;
+
+procedure TMyApplication.DoRun;
+
+begin
+  // Your code here
+  Terminate;
+  InitWebAssembly;
+end;
+
+var
+  Application : TMyApplication;
+
+begin
+  Application:=TMyApplication.Create(nil);
+  Application.Initialize;
+  Application.Run;
+end.

+ 72 - 0
demo/wasienv/dom/WasiDomTest1.lpi

@@ -0,0 +1,72 @@
+<?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="WasiDomTest1"/>
+      <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="WasiDomTest1.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="wadom_wasm.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="WasiDomTest1.wasm" ApplyConventions="False"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <CodeGeneration>
+      <TargetCPU Value="wasm32"/>
+      <TargetOS Value="wasi"/>
+    </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="False"/>
+      </Debugging>
+    </Linking>
+    <Other>
+      <CompilerPath Value="/usr/lib/fpc/3.3.1/ppcrosswasm32"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 22 - 0
demo/wasienv/dom/WasiDomTest1.lpr

@@ -0,0 +1,22 @@
+program WasiDomTest1;
+
+{$mode objfpc}
+{$h+}
+{$codepage UTF8}
+
+uses
+  SysUtils, wadom_wasm;
+
+var
+  obj: TJSObject;
+  d: Double;
+  u: UnicodeString;
+begin
+  obj:=TJSObject.CreateFromID(-1);
+  d:=obj.InvokeJSDoubleResult('fly',[]);
+  writeln('AAA1 ',d);
+  u:='äbc';
+  d:=obj.InvokeJSDoubleResult('fly',[u,12345678901]);
+  writeln('AAA2 ',d);
+end.
+

文件差異過大導致無法顯示
+ 0 - 0
demo/wasienv/dom/bulma.min.css


+ 52 - 0
demo/wasienv/dom/index.html

@@ -0,0 +1,52 @@
+
+<!doctype html>
+<html lang="en">
+<head>
+  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <title>FPC-Webassembly accesing browser DOM through Pas2JS Demo</title>
+  <link href="bulma.min.css" rel="stylesheet">
+  <script src="BrowserDomTest1.js"></script>
+  <style>
+
+  .source {
+    /* width: 730px; */
+    margin: -45px auto;
+    font-size: 0.9em;
+  }
+
+  .source-inner {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    /* width: 482px; */
+  }
+  </style>
+</head>
+<body>
+  <div class="section py-4">
+    <h1 class="title is-3">Canvas</h1>
+    <div class="box" id="canvases"></div>
+  </div>
+  <div class="section py-4">
+    <h1 class="title is-3">Console output</h1>
+    <div class="box" id="pasjsconsole"></div>
+  </div>
+  <!-- <hr> -->
+  <div class="section">
+    <div class="source">
+      <div class="source-inner">
+        <div>
+          <p>Created using &nbsp; <a target="_blank" href="https://wiki.freepascal.org/pas2js">pas2js.</a> </p>
+          <p>Pas2JS Sources: &nbsp; <a target="new" href="demowasicanvas.lpr">Pas2JS Program</a></p>
+          <p>Webassembly Sources: &nbsp; <a target="new" href="canvasdraw.lpr">FPC Program</a></p>
+        </div>
+      </div>
+    </div>
+  </div>
+  <script>
+    rtl.showUncaughtExceptions=true;
+    rtl.run();
+  </script>
+</body>
+</html>

+ 227 - 0
demo/wasienv/dom/wadom_browser.pp

@@ -0,0 +1,227 @@
+unit wadom_browser;
+
+{$mode objfpc}
+{$h+}
+
+interface
+
+uses sysutils, types, js, web, wasienv, wadom_shared;
+
+Type
+
+  { TWADomBridge }
+
+  TWADomBridge = class(TImportExtension)
+  Private
+    FGlobalObjects: TJSArray;
+    FLocalObjects: TJSArray;
+  Protected
+    function FindObject(ObjId: TWasiDomObjectID): TJSObject; virtual;
+    function Invoke_JSResult(ObjId: TWasiDomObjectID; FuncNameP, FuncNameLen, ArgsP: NativeInt; out JSResult: JSValue): TWasiDomResult; virtual;
+    function Invoke_BooleanResult(ObjId: TWasiDomObjectID; FuncNameP, FuncNameLen, ArgsP, ResultP: NativeInt): TWasiDomResult; virtual;
+    function Invoke_DoubleResult(ObjId: TWasiDomObjectID; FuncNameP, FuncNameLen, ArgsP, ResultP: NativeInt): TWasiDomResult; virtual;
+    function GetInvokeArguments(View: TJSDataView; ArgsP: NativeInt): TJSValueDynArray; virtual;
+    function GetWasiDomResult(const v: jsvalue): TWasiDomResult;
+  Public
+    Constructor Create(aEnv: TPas2JSWASIEnvironment); override;
+    Procedure FillImportObject(aObject: TJSObject); override;
+    Function ImportName: String; override;
+  end;
+
+Implementation
+
+function TypedArrayToString(const a: TJSTypedArray): string; assembler;
+asm
+  return String.fromCharCode.apply(null,a);
+end;
+
+constructor TWADomBridge.Create(aEnv: TPas2JSWASIEnvironment);
+begin
+  Inherited Create(aEnv);
+  FGlobalObjects:=TJSArray.new;
+  FGlobalObjects[-WasiObjIdDocument]:=document;
+  FGlobalObjects[-WasiObjIdWindow]:=window;
+  FGlobalObjects[-WasiObjIdConsole]:=console;
+  FGlobalObjects[-WasiObjIdCaches]:=caches;
+  FLocalObjects:=TJSArray.new;
+end;
+
+function TWADomBridge.ImportName: String;
+begin
+  Result:=WasiDomExtName;
+end;
+
+procedure TWADomBridge.FillImportObject(aObject: TJSObject);
+begin
+  aObject[WasiDomInvokeBooleanResult]:=@invoke_booleanresult;
+  aObject[WasiDomInvokeDoubleResult]:=@invoke_doubleresult;
+end;
+
+function TWADomBridge.FindObject(ObjId: TWasiDomObjectID): TJSObject;
+begin
+  if ObjId<0 then
+    Result:=TJSObject(FGlobalObjects[-ObjId])
+  else
+    Result:=TJSObject(FLocalObjects[ObjId]);
+  if isUndefined(Result) then
+    Result:=nil;
+end;
+
+function TWADomBridge.Invoke_JSResult(ObjId: TWasiDomObjectID; FuncNameP,
+  FuncNameLen, ArgsP: NativeInt; out JSResult: JSValue): TWasiDomResult;
+var
+  View: TJSDataView;
+  aBytes: TJSUint8Array;
+  FuncName: String;
+  Args: TJSValueDynArray;
+  Obj: TJSObject;
+  fn: JSValue;
+begin
+  writeln('TWADomBridge.Invoke_JSResult ObjId=',ObjId,' FuncNameP=',FuncNameP,' FuncNameLen=',FuncNameLen,' ArgsP=',ArgsP);
+
+  Obj:=FindObject(ObjId);
+  if Obj=nil then
+    exit(WasiDomResult_UnknownObjId);
+
+  View:=getModuleMemoryDataView();
+  aBytes:=TJSUint8Array.New(View.buffer, FuncNameP, FuncNameLen);
+  writeln('TWADomBridge.Invoke_JSResult aBytes=',aBytes);
+  FuncName:=TypedArrayToString(aBytes);
+  writeln('TWADomBridge.Invoke_JSResult FuncName="',FuncName,'"');
+
+  fn:=Obj[FuncName];
+  if jstypeof(fn)<>'function' then
+    exit(WasiDomResult_NotAFunction);
+
+  if ArgsP=0 then
+    JSResult:=TJSFunction(fn).call(Obj)
+  else begin
+    Args:=GetInvokeArguments(View,ArgsP);
+    JSResult:=TJSFunction(fn).apply(Obj,Args);
+  end;
+
+  exit(WasiDomResult_Success);
+end;
+
+function TWADomBridge.Invoke_BooleanResult(ObjId: TWasiDomObjectID; FuncNameP,
+  FuncNameLen, ArgsP, ResultP: NativeInt): TWasiDomResult;
+var
+  JSResult: JSValue;
+  b: byte;
+begin
+  // invoke
+  Result:=Invoke_JSResult(ObjId,FuncNameP,FuncNameLen,ArgsP,JSResult);
+  if Result<>WasiDomResult_Success then
+    exit;
+  // check result type
+  if jstypeof(JSResult)<>'boolean' then
+    exit(GetWasiDomResult(JSResult));
+  if JSResult then
+    b:=1
+  else
+    b:=0;
+  // set result
+  getModuleMemoryDataView().setUint8(ResultP, b);
+  Result:=WasiDomResult_Success;
+end;
+
+function TWADomBridge.Invoke_DoubleResult(ObjId: TWasiDomObjectID; FuncNameP,
+  FuncNameLen, ArgsP, ResultP: NativeInt): TWasiDomResult;
+var
+  JSResult: JSValue;
+begin
+  // invoke
+  Result:=Invoke_JSResult(ObjId,FuncNameP,FuncNameLen,ArgsP,JSResult);
+  if Result<>WasiDomResult_Success then
+    exit;
+  // check result type
+  if jstypeof(JSResult)<>'number' then
+    exit(GetWasiDomResult(JSResult));
+  // set result
+  getModuleMemoryDataView().setFloat64(ResultP, double(JSResult), env.IsLittleEndian);
+  Result:=WasiDomResult_Success;
+end;
+
+function TWADomBridge.GetInvokeArguments(View: TJSDataView; ArgsP: NativeInt
+  ): TJSValueDynArray;
+var
+  Cnt, aType: Byte;
+  i: Integer;
+  p: NativeInt;
+  Len, Ptr: LongWord;
+  aBytes: TJSUint8Array;
+  aWords: TJSUint16Array;
+begin
+  p:=ArgsP;
+  Cnt:=View.getUInt8(p);
+  inc(p);
+  for i:=0 to Cnt-1 do
+  begin
+    aType:=View.getUInt8(p);
+    inc(p);
+    case aType of
+    WasiArgLongint:
+      begin
+        Result[i]:=View.getInt32(p,env.IsLittleEndian);
+        inc(p,4);
+      end;
+    WasiArgDouble:
+      begin
+        Result[i]:=View.getFloat64(p,env.IsLittleEndian);
+        inc(p,8);
+      end;
+    WasiArgTrue:
+      Result[i]:=true;
+    WasiArgFalse:
+      Result[i]:=false;
+    WasiArgChar:
+      begin
+        Result[i]:=chr(View.getUint16(p,env.IsLittleEndian));
+        inc(p,2);
+      end;
+    WasiArgUTF8String:
+      begin
+        Len:=View.getUint32(p,env.IsLittleEndian);
+        inc(p,4);
+        Ptr:=View.getUint32(p,env.IsLittleEndian);
+        inc(p,4);
+        aBytes:=TJSUint8Array.New(View.buffer, Ptr,Len);
+        Result[i]:=TypedArrayToString(aBytes);
+      end;
+    WasiArgUnicodeString:
+      begin
+        Len:=View.getUint32(p,env.IsLittleEndian);
+        inc(p,4);
+        Ptr:=View.getUint32(p,env.IsLittleEndian);
+        inc(p,4);
+        aWords:=TJSUint16Array.New(View.buffer, Ptr,Len);
+        Result[i]:=TypedArrayToString(aWords);
+      end;
+    WasiArgPointer:
+      begin
+        Result[i]:=View.getUint32(p,env.IsLittleEndian);
+        inc(p,4);
+      end
+    else
+      raise Exception.Create('unknown arg type '+IntToStr(aType));
+    end;
+    writeln('TWADomBridge.GetInvokeArguments ',i,'/',Cnt,' = ',Result[i]);
+  end;
+end;
+
+function TWADomBridge.GetWasiDomResult(const v: jsvalue): TWasiDomResult;
+begin
+  case jstypeof(v) of
+  'undefined': Result:=WasiDomResult_Undefined;
+  'boolean': Result:=WasiDomResult_Boolean;
+  'number': Result:=WasiDomResult_Number;
+  'string': Result:=WasiDomResult_String;
+  'symbol': Result:=WasiDomResult_Symbol;
+  'bigint': Result:=WasiDomResult_BigInt;
+  'function': Result:=WasiDomResult_Function;
+  'object': Result:=WasiDomResult_Object;
+  else Result:=WasiDomResult_None;
+  end;
+end;
+
+end.  

+ 68 - 0
demo/wasienv/dom/wadom_shared.pp

@@ -0,0 +1,68 @@
+{
+  These types and constants are shared between pas2js and webassembly.
+}
+unit wadom_shared;
+
+interface
+
+type
+  TWasiDomObjectID = NativeInt;
+
+// invoke results
+type
+  TWasiDomResult = longint;
+const
+  WasiDomResult_None = 0;
+  WasiDomResult_Success = 1;
+  WasiDomResult_UnknownObjId = 2;
+  WasiDomResult_NotAFunction = 3;
+  WasiDomResult_Undefined = 4;
+  WasiDomResult_Boolean = 5;
+  WasiDomResult_Number = 6;
+  WasiDomResult_Double = 7;
+  WasiDomResult_String = 8;
+  WasiDomResult_Function = 9;
+  WasiDomResult_Object = 10;
+  WasiDomResult_BigInt = 11;
+  WasiDomResult_Symbol = 12;
+
+  WasiDomResultLast = 12;
+
+  WasiDomResult_Names: array[0..WasiDomResultLast] of string = (
+    'None',
+    'Success',
+    'UnknownObjId',
+    'NotAFunction',
+    'Undefined',
+    'Null',
+    'Boolean',
+    'Double',
+    'String',
+    'Function',
+    'Object',
+    'BigInt',
+    'Symbol'
+    );
+
+  WasiDomExtName = 'wasi_dom';
+  WasiDomInvokeBooleanResult = 'invoke_boolresult';
+  WasiDomInvokeDoubleResult = 'invoke_doubleresult';
+
+  WasiArgNone = 0;
+  WasiArgLongint = 1;
+  WasiArgDouble = 2;
+  WasiArgTrue = 3;
+  WasiArgFalse = 4;
+  WasiArgChar = 5; // followed by a word
+  WasiArgUTF8String = 6; // followed by length and pointer
+  WasiArgUnicodeString = 7; // followed by length and pointer
+  WasiArgPointer = 8;
+
+  WasiObjIdDocument = -1;
+  WasiObjIdWindow = -2;
+  WasiObjIdConsole = -3;
+  WasiObjIdCaches = -4;
+
+implementation
+
+end.

+ 428 - 0
demo/wasienv/dom/wadom_wasm.pas

@@ -0,0 +1,428 @@
+{
+  Webassembly unit giving access to the browser DOM.
+
+  see https://wiki.freepascal.org/WebAssembly/DOM
+}
+unit wadom_wasm;
+
+{$mode ObjFPC}{$H+}
+
+{$define VerboseWasiDom}
+
+interface
+
+uses
+  SysUtils, Types, Math, Classes, wadom_shared;
+
+const
+  MinSafeIntDouble = -$1fffffffffffff; // -9007199254740991 54 bits (52 plus signed bit plus implicit highest bit)
+  MaxSafeIntDouble =  $1fffffffffffff; //  9007199254740991
+
+Type
+  EJSObject = class(Exception);
+  EJSInvoke = class(EJSObject)
+  public
+    ObjectID: TWasiDomObjectID;
+    FuncName: string;
+  end;
+
+  TJSObject = class;
+  //TJSObjectClass = class of TJSObject;
+
+  { TJSObject }
+
+  TJSObject = class(TInterfacedObject)
+  private
+    FObjectID: TWasiDomObjectID;
+  protected
+    procedure WasiInvokeRaise(const aName, Msg: string); virtual;
+    procedure WasiInvokeRaiseResultMismatch(const aName: string; Expected, Actual: TWasiDomResult); virtual;
+    function CreateInvokeJSArgs(const Args: array of const): PByte; virtual;
+  public
+    constructor CreateFromID(aID: TWasiDomObjectID); reintroduce;
+    destructor Destroy; override;
+    property ObjectID: TWasiDomObjectID read FObjectID;
+    function InvokeJSBooleanResult(const aName: string; Const args: Array of const): Boolean;
+    function InvokeJSDoubleResult(const aName: string; Const Args: Array of const): Double;
+    //function InvokeJSUnicodeStringResult(const aName: string; Const args: Array of const): UnicodeString;
+    //function InvokeJSUtf8StringResult(const aName: string; Const args: Array of const): String;
+    //function InvokeJSObjResult(const aName: string; aResultClass: TJSObjectClass; Const args: Array of const): TJSObject;
+  end;
+
+function __wasidom_invoke_boolresult(
+  ObjID: TWasiDomObjectID;
+  FuncNameP: PChar;
+  FuncNameLen: longint;
+  ArgP: PByte;
+  ResultP: PByteBool
+): TWasiDomResult; external WasiDomExtName name WasiDomInvokeBooleanResult;
+
+function __wasidom_invoke_doubleresult(
+  ObjID: TWasiDomObjectID;
+  FuncNameP: PChar;
+  FuncNameLen: longint;
+  ArgP: PByte;
+  ResultP: PDouble
+): TWasiDomResult; external WasiDomExtName name WasiDomInvokeDoubleResult;
+
+implementation
+
+{$IFDEF VerboseWasiDom}
+function GetVarRecName(vt: word): string;
+begin
+  case vt of
+    vtInteger: Result:='vtInteger';
+    vtBoolean: Result:='vtBoolean';
+    vtChar: Result:='vtChar';
+    {$ifndef FPUNONE}
+    vtExtended: Result:='vtExtended';
+    {$endif}
+    vtString: Result:='vtString';
+    vtPointer: Result:='vtPointer';
+    vtPChar: Result:='vtPChar';
+    vtObject: Result:='vtObject';
+    vtClass: Result:='vtClass';
+    vtWideChar: Result:='vtWideChar';
+    vtPWideChar: Result:='vtPWideChar';
+    vtAnsiString: Result:='vtAnsiString';
+    vtCurrency: Result:='vtCurrency';
+    vtVariant: Result:='vtVariant';
+    vtInterface: Result:='vtInterface';
+    vtWideString: Result:='vtWideString';
+    vtInt64: Result:='vtInt64';
+    vtQWord: Result:='vtQWord';
+    vtUnicodeString: Result:='vtUnicodeString';
+  else
+    Result:='vt?';
+  end;
+end;
+{$ENDIF}
+
+{ TJSObject }
+
+procedure TJSObject.WasiInvokeRaise(const aName, Msg: string);
+var
+  E: EJSInvoke;
+begin
+  E:=EJSInvoke.Create(Msg);
+  E.ObjectID:=ObjectID;
+  E.FuncName:=aName;
+  raise E;
+end;
+
+procedure TJSObject.WasiInvokeRaiseResultMismatch(const aName: string;
+  Expected, Actual: TWasiDomResult);
+begin
+  case Actual of
+  WasiDomResult_UnknownObjId: WasiInvokeRaise(aName,'unknown object id '+IntToStr(ObjectID));
+  WasiDomResult_NotAFunction: WasiInvokeRaise(aName,'object '+IntToStr(ObjectID)+' does not have a function "'+aName+'"');
+  else
+    WasiInvokeRaise(aName,'expected '+WasiDomResult_Names[Expected]+', but got '+WasiDomResult_Names[Actual]+' from object '+IntToStr(ObjectID)+' function "'+aName+'"');
+  end;
+end;
+
+function TJSObject.CreateInvokeJSArgs(const Args: array of const): PByte;
+
+  procedure RaiseNotSupported(const Msg: string);
+  begin
+    raise EJSInvoke.Create('Invoke js: type not supported '+Msg);
+  end;
+
+  procedure RaiseRange;
+  begin
+    raise ERangeError.Create('Invoke js: number out of bounds');
+  end;
+
+var
+  i, Len: Integer;
+  qw: QWord;
+  i64: Int64;
+  p, h: PByte;
+  s: String;
+  ws: WideString;
+  us: UnicodeString;
+  d: Double;
+begin
+  Result:=nil;
+  if length(Args)>255 then
+    raise EJSInvoke.Create('Invoke js: too many args');
+
+  Len:=1;
+  for i:=0 to high(Args) do
+  begin
+    case Args[i].VType of
+    vtInteger       : inc(Len,5);
+    vtBoolean       : inc(Len);
+    vtChar,
+    vtWideChar      : inc(Len,3);
+    {$ifndef FPUNONE}
+    vtExtended      :
+      begin
+        d:=double(Args[i].VExtended^);
+        if d=0 then ;
+        inc(Len,9);
+      end;
+    {$endif}
+    vtString        : inc(Len,1+SizeOf(NativeInt)+SizeOf(PByte));
+    vtPointer,
+    vtPChar         :
+      begin
+        strlen(Args[i].VPChar);
+        inc(Len,1+SizeOf(NativeInt)+SizeOf(PByte));
+      end;
+    vtObject        : RaiseNotSupported('object');
+    vtClass         : RaiseNotSupported('class');
+    vtPWideChar     : RaiseNotSupported('pwidechar');
+    vtAnsiString    : inc(Len,1+SizeOf(NativeInt)+SizeOf(PByte));
+    vtCurrency      : RaiseNotSupported('currency');
+    {$ifdef FPC_HAS_FEATURE_VARIANTS}
+    vtVariant       : RaiseNotSupported('variant');
+    {$endif FPC_HAS_FEATURE_VARIANTS}
+    vtInterface     : RaiseNotSupported('interface');
+    vtWideString    : inc(Len,1+SizeOf(NativeInt)+SizeOf(PByte));
+    vtInt64         :
+      begin
+        i64:=Args[i].VInt64^;
+        if (i64<MinSafeIntDouble) or (i64>MaxSafeIntDouble) then
+          RaiseRange;
+        if (i64>=low(longint)) and (i64<=high(longint)) then
+          inc(Len,5)
+        else
+          inc(Len,9);
+      end;
+    vtUnicodeString : inc(Len,1+SizeOf(NativeInt)+SizeOf(PByte));
+    vtQWord         :
+      begin
+        qw:=Args[i].VQWord^;
+        if (qw>MaxSafeIntDouble) then
+          RaiseRange;
+        if (qw<=high(longint)) then
+          inc(Len,5)
+        else
+          inc(Len,9);
+      end;
+    end;
+  end;
+
+  Result:=GetMem(Len);
+  p:=Result;
+  p^:=length(Args);
+  inc(p);
+  for i:=0 to high(Args) do
+  begin
+    case Args[i].VType of
+    vtInteger       :
+      begin
+        p^:=WasiArgLongint;
+        inc(p);
+        PLongint(p)^:=Args[i].VInteger;
+        inc(p,4);
+      end;
+    vtBoolean       :
+      begin
+        if Args[i].VBoolean then
+          p^:=WasiArgTrue
+        else
+          p^:=WasiArgFalse;
+        inc(p);
+      end;
+    {$ifndef FPUNONE}
+    vtExtended      :
+      begin
+        p^:=WasiArgDouble;
+        inc(p);
+        PDouble(p)^:=double(Args[i].VExtended^);
+        inc(p,8);
+      end;
+    {$endif}
+    vtChar:
+      begin
+        p^:=WasiArgChar;
+        inc(p);
+        PWord(p)^:=ord(Args[i].VChar);
+        inc(p,2);
+      end;
+    vtWideChar      :
+      begin
+        p^:=WasiArgChar;
+        inc(p);
+        PWord(p)^:=ord(Args[i].VWideChar);
+        inc(p,2);
+      end;
+    vtString        :
+      begin
+        // shortstring
+        p^:=WasiArgUTF8String;
+        inc(p);
+        h:=PByte(Args[i].VString);
+        PNativeInt(p)^:=h^;
+        inc(h);
+        inc(p,sizeof(NativeInt));
+        PPointer(p)^:=h;
+        inc(p,sizeof(Pointer));
+      end;
+    vtPointer:
+      begin
+        p^:=WasiArgPointer;
+        inc(p);
+        PPointer(p)^:=Args[i].VPointer;
+        inc(p,sizeof(Pointer));
+      end;
+    vtPChar         :
+      begin
+        p^:=WasiArgUTF8String;
+        inc(p);
+        h:=PByte(Args[i].VPChar);
+        PNativeInt(p)^:=strlen(PChar(h));
+        inc(p,sizeof(NativeInt));
+        PPointer(p)^:=h;
+        inc(p,sizeof(Pointer));
+      end;
+    vtObject        : ;
+    vtClass         : ;
+    vtPWideChar     : ;
+    vtAnsiString    :
+      begin
+        p^:=WasiArgUTF8String;
+        inc(p);
+        h:=Args[i].VAnsiString;
+        s:=AnsiString(h);
+        PNativeInt(p)^:=length(s);
+        inc(p,sizeof(NativeInt));
+        PPointer(p)^:=h;
+        inc(p,sizeof(Pointer));
+      end;
+    vtCurrency      : ;
+    {$ifdef FPC_HAS_FEATURE_VARIANTS}
+    vtVariant       : ;
+    {$endif FPC_HAS_FEATURE_VARIANTS}
+    vtInterface     : ;
+    vtWideString    :
+      begin
+        p^:=WasiArgUnicodeString;
+        inc(p);
+        h:=Args[i].VWideString;
+        ws:=WideString(h);
+        PNativeInt(p)^:=length(ws);
+        inc(p,sizeof(NativeInt));
+        PPointer(p)^:=h;
+        inc(p,sizeof(Pointer));
+      end;
+    vtInt64         :
+      begin
+        i64:=Args[i].VInt64^;
+        if (i64>=low(longint)) and (i64<=high(longint)) then
+        begin
+          p^:=WasiArgLongint;
+          inc(p);
+          PLongint(p)^:=i64;
+          inc(p,4);
+        end else begin
+          p^:=WasiArgDouble;
+          inc(p);
+          PDouble(p)^:=i64;
+          inc(p,8);
+        end;
+      end;
+    vtUnicodeString :
+      begin
+        p^:=WasiArgUnicodeString;
+        inc(p);
+        h:=Args[i].VUnicodeString;
+        us:=UnicodeString(h);
+        PNativeInt(p)^:=length(us);
+        inc(p,sizeof(NativeInt));
+        PPointer(p)^:=h;
+        inc(p,sizeof(Pointer));
+      end;
+    vtQWord         :
+      begin
+        qw:=Args[i].VQWord^;
+        if (qw<=high(longint)) then
+        begin
+          p^:=WasiArgLongint;
+          inc(p);
+          PLongint(p)^:=qw;
+          inc(p,4);
+        end else begin
+          p^:=WasiArgDouble;
+          inc(p);
+          PDouble(p)^:=qw;
+          inc(p,8);
+        end;
+      end;
+    end;
+  end;
+
+  {$IFDEF VerboseInvokeJSArgs}
+  s:='TJSObject.CreateInvokeJSArgs ArgCnt='+IntToStr(length(Args));
+  for i:=0 to high(Args) do
+    s:=s+' '+GetVarRecName(Args[i].VType);
+  s:=s+' Len='+IntToStr(Len);
+  s:=s+' Bytes=';
+  for i:=0 to Len-1 do
+    s:=s+HexStr(Result[i],2);
+  writeln(s);
+  {$ENDIF}
+end;
+
+constructor TJSObject.CreateFromID(aID: TWasiDomObjectID);
+begin
+  FObjectID:=aID;
+end;
+
+destructor TJSObject.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TJSObject.InvokeJSBooleanResult(const aName: string;
+  const args: array of const): Boolean;
+var
+  aError: TWasiDomResult;
+  InvokeArgs: PByte;
+  b: bytebool;
+begin
+  b:=false;
+  if length(Args)=0 then
+    aError:=__wasidom_invoke_boolresult(ObjectID,PChar(aName),length(aName),nil,@b)
+  else begin
+    InvokeArgs:=CreateInvokeJSArgs(Args);
+    try
+      aError:=__wasidom_invoke_boolresult(ObjectID,PChar(aName),length(aName),
+                                               InvokeArgs,@b);
+    finally
+      if InvokeArgs<>nil then
+        FreeMem(InvokeArgs);
+    end;
+  end;
+  if aError<>WasiDomResult_Boolean then
+    WasiInvokeRaiseResultMismatch(aName,WasiDomResult_Boolean,aError);
+  Result:=b;
+end;
+
+function TJSObject.InvokeJSDoubleResult(const aName: string;
+  const Args: array of const): Double;
+var
+  aError: TWasiDomResult;
+  InvokeArgs: PByte;
+begin
+  Result:=NaN;
+  if length(Args)=0 then
+    aError:=__wasidom_invoke_doubleresult(ObjectID,PChar(aName),length(aName),nil,@Result)
+  else begin
+    InvokeArgs:=CreateInvokeJSArgs(Args);
+    try
+      aError:=__wasidom_invoke_doubleresult(ObjectID,PChar(aName),length(aName),
+                                               InvokeArgs,@Result);
+    finally
+      if InvokeArgs<>nil then
+        FreeMem(InvokeArgs);
+    end;
+  end;
+  if aError<>WasiDomResult_Double then
+    WasiInvokeRaiseResultMismatch(aName,WasiDomResult_Double,aError);
+end;
+
+end.
+

部分文件因文件數量過多而無法顯示