Ver código fonte

* Propagate exception info from webassembly to javascript

Michael Van Canneyt 3 meses atrás
pai
commit
3e33aa39c9

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
demo/wasienv/exceptions/bulma.min.css


+ 40 - 0
demo/wasienv/exceptions/demolib.lpr

@@ -0,0 +1,40 @@
+library WorkW3DClient;
+
+{$mode objfpc}
+{$h+}
+
+uses
+   {$IFDEF FPC_DOTTEDUNITS}
+   System.SysUtils, 
+   {$ELSE}
+   SysUtils,
+   {$ENDIF}
+   wasm.exceptions;
+
+Type
+  EMyException = class(Exception);
+  EMyOtherException = class(TObject)
+    function toString : RTLString; override;
+  end;
+
+function EMyOtherException.toString : RTLString;
+begin
+  Result:='Some nice error';
+end;   
+
+procedure DoTest;
+
+begin
+  raise EMyException.Create('My Exception message');
+end;
+
+procedure DoTest2;
+
+begin
+  raise EMyOtherException.Create;
+end;
+
+exports DoTest,DoTest2;
+
+begin
+end.

+ 91 - 0
demo/wasienv/exceptions/exceptdemo.lpi

@@ -0,0 +1,91 @@
+<?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="exceptdemo"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="6">
+      <Item0 Name="BrowserConsole" Value="1"/>
+      <Item1 Name="MaintainHTML" Value="1"/>
+      <Item2 Name="Pas2JSProject" Value="1"/>
+      <Item3 Name="PasJSLocation" Value="$NameOnly($(ProjFile))"/>
+      <Item4 Name="PasJSWebBrowserProject" Value="1"/>
+      <Item5 Name="RunAtReady" 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="exceptdemo.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="index.html"/>
+        <IsPartOfProject Value="True"/>
+        <CustomData Count="1">
+          <Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
+        </CustomData>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target FileExt=".js">
+      <Filename Value="exceptdemo"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <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
demo/wasienv/exceptions/exceptdemo.lpr

@@ -0,0 +1,74 @@
+program exceptdemo;
+
+{$mode objfpc}
+
+uses
+  BrowserConsole, BrowserApp, WasiEnv,WASIHostApp, JS, Classes, SysUtils, Web, WebAssembly;
+
+type
+  { TMyApplication }
+
+  TMyApplication = class(TWASIHostApplication)
+  private
+    procedure DoAfter(Sender: TObject; aDescriptor: TWebAssemblyStartDescriptor);
+    procedure ShowInfo(aInfo: TLastExceptionInfo);
+  protected
+    procedure DoRun; override;
+  public
+  end;
+
+procedure TMyApplication.ShowInfo(aInfo : TLastExceptionInfo);
+
+begin
+  with aInfo do
+    Writeln('Got exception during DoTest: ',ClassName,': "',Message,'", more: ',More)
+end;
+
+procedure TMyApplication.DoAfter(Sender: TObject; aDescriptor: TWebAssemblyStartDescriptor);
+
+type
+  TTestProc = procedure;
+
+var
+  lInfo : TLastExceptionInfo;
+
+begin
+  try
+    TTestProc(aDescriptor.exported['DoTest'])();
+  except
+    on e : TJSWebAssemblyException do
+      begin
+      if Host.GetExceptionInfo(lInfo) then
+         ShowInfo(lInfo)
+      else
+        Raise
+      end;
+  end;
+  try
+    TTestProc(aDescriptor.exported['DoTest2'])();
+  except
+    on e : TJSWebAssemblyException do
+      begin
+      if Host.GetExceptionInfo(lInfo) then
+         ShowInfo(lInfo)
+      else
+        Raise
+      end;
+  end;
+end;
+
+procedure TMyApplication.DoRun;
+begin
+  // Let the wasi hosting environment know we'll handle exceptions.
+  Host.ConvertNativeExceptions:=False;
+  StartWebAssembly('demolib.wasm',True,Nil,@DoAfter);
+end;
+
+var
+  Application : TMyApplication;
+
+begin
+  Application:=TMyApplication.Create(nil);
+  Application.Initialize;
+  Application.Run;
+end.

+ 54 - 0
demo/wasienv/exceptions/index.html

@@ -0,0 +1,54 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+  <title>Project1</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link href="bulma.min.css" rel="stylesheet">
+  <script src="exceptdemo.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 pb-4">
+    <h1 class="title is-4">Exceptions: Getting webassembly Pascal exception info</h1>
+    <p>The <em>wasm.exceptions</em> unit in FPC exposes 2 functions which allow you to retrieve exception information from the free pascal RTL.
+    You can use this to check if a <em>Webassembly.Exception</em> exception is the result of a Free Pascal exception and retrieve the classname and message from the exception.
+    </p>
+    <p>
+    The output below shows the result of this.
+    </p>
+    <div class="box" id="pasjsconsole"></div>
+  </div>
+  <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="exceptdemo.lpr">Pas2JS Program</a></p>
+          <p>Webassembly Sources: &nbsp; <a target="new" href="demolib.lpr">FPC Program</a></p>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <script>
+    rtl.showUncaughtExceptions=true;
+    window.addEventListener("load", rtl.run);
+  </script>
+</body>
+</html>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
demo/wasienv/exceptions2/bulma.min.css


+ 40 - 0
demo/wasienv/exceptions2/demolib.lpr

@@ -0,0 +1,40 @@
+library WorkW3DClient;
+
+{$mode objfpc}
+{$h+}
+
+uses
+   {$IFDEF FPC_DOTTEDUNITS}
+   System.SysUtils, 
+   {$ELSE}
+   SysUtils,
+   {$ENDIF}
+   wasm.exceptions;
+
+Type
+  EMyException = class(Exception);
+  EMyOtherException = class(TObject)
+    function toString : RTLString; override;
+  end;
+
+function EMyOtherException.toString : RTLString;
+begin
+  Result:='Some nice error';
+end;   
+
+procedure DoTest;
+
+begin
+  raise EMyException.Create('My Exception message');
+end;
+
+procedure DoTest2;
+
+begin
+  raise EMyOtherException.Create;
+end;
+
+exports DoTest,DoTest2;
+
+begin
+end.

+ 91 - 0
demo/wasienv/exceptions2/exceptdemo2.lpi

@@ -0,0 +1,91 @@
+<?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="exceptdemo2"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="6">
+      <Item0 Name="BrowserConsole" Value="1"/>
+      <Item1 Name="MaintainHTML" Value="1"/>
+      <Item2 Name="Pas2JSProject" Value="1"/>
+      <Item3 Name="PasJSLocation" Value="$NameOnly($(ProjFile))"/>
+      <Item4 Name="PasJSWebBrowserProject" Value="1"/>
+      <Item5 Name="RunAtReady" 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="exceptdemo2.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="index.html"/>
+        <IsPartOfProject Value="True"/>
+        <CustomData Count="1">
+          <Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
+        </CustomData>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target FileExt=".js">
+      <Filename Value="exceptdemo2"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <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>

+ 63 - 0
demo/wasienv/exceptions2/exceptdemo2.lpr

@@ -0,0 +1,63 @@
+program exceptdemo2;
+
+{$mode objfpc}
+
+uses
+  BrowserConsole, BrowserApp, WasiEnv,WASIHostApp, JS, Classes, SysUtils, Web, WebAssembly;
+
+type
+  { TMyApplication }
+
+  TMyApplication = class(TWASIHostApplication)
+  private
+    procedure DoAfter(Sender: TObject; aDescriptor: TWebAssemblyStartDescriptor);
+    procedure ShowInfo(aInfo: TLastExceptionInfo);
+  protected
+    procedure DoRun; override;
+  public
+  end;
+
+procedure TMyApplication.ShowInfo(aInfo : TLastExceptionInfo);
+
+begin
+  with aInfo do
+    Writeln('Got exception during DoTest: ',ClassName,': "',Message,'", more: ',More)
+end;
+
+procedure TMyApplication.DoAfter(Sender: TObject; aDescriptor: TWebAssemblyStartDescriptor);
+
+type
+  TTestProc = procedure;
+
+var
+  lInfo : TLastExceptionInfo;
+
+begin
+  try
+    TTestProc(aDescriptor.exported['DoTest'])();
+  except
+    on e : EWasmNativeException do
+       Writeln('Exception ',E.NativeClass,' raised: ',e.NativeMessage);
+  end;
+  try
+    TTestProc(aDescriptor.exported['DoTest2'])();
+  except
+    on e : EWasmNativeException do
+       Writeln('Exception ',E.NativeClass,' raised: ',e.NativeMessage);
+  end;
+end;
+
+procedure TMyApplication.DoRun;
+begin
+  // Let the wasi hosting environment know we'll handle exceptions.
+  StartWebAssembly('demolib.wasm',True,Nil,@DoAfter);
+end;
+
+var
+  Application : TMyApplication;
+
+begin
+  Application:=TMyApplication.Create(nil);
+  Application.Initialize;
+  Application.Run;
+end.

+ 56 - 0
demo/wasienv/exceptions2/index.html

@@ -0,0 +1,56 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+  <title>Project1</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link href="bulma.min.css" rel="stylesheet">
+  <script src="exceptdemo2.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 pb-4">
+    <h1 class="title is-4">Exceptions: Getting webassembly Pascal exception info</h1>
+    <p>
+    <p>The <em>wasm.exceptions</em> unit in FPC exposes 2 functions which allow you to retrieve exception information from the free pascal RTL.
+    The default behaviour of the WASI hosting environment is to wrap all calls to a WebAssembly function and use these functions transform the
+    exception to a <em>EWasmNativeException</em> exception.
+    </p>
+    <p>
+    The output below shows the result of catching this exception.
+    </p>
+    <div class="box" id="pasjsconsole"></div>
+  </div>
+  <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="exceptdemo.lpr">Pas2JS Program</a></p>
+          <p>Webassembly Sources: &nbsp; <a target="new" href="demolib.lpr">FPC Program</a></p>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <script>
+    rtl.showUncaughtExceptions=true;
+    window.addEventListener("load", rtl.run);
+  </script>
+</body>
+</html>

+ 139 - 2
packages/wasi/src/wasienv.pas

@@ -63,6 +63,18 @@ type
     loadcount : integer;
   end;
 
+  { EWasmNativeException }
+
+  EWasmNativeException = Class(Exception)
+  private
+    FNativeClass: String;
+    FNativeMessage: String;
+  Public
+    constructor create(const aNativeClass,aNativeMessage : string); reintroduce;
+    Property NativeClass : String read FNativeClass;
+    Property NativeMessage : String read FNativeMessage;
+  end;
+
   EWasiError = Class(Exception);
 
   EWasiFSError = class(Exception)
@@ -78,6 +90,13 @@ type
 
   TWASIWriteEvent = Reference to Procedure(Sender : TObject; Const aOutput : String);
 
+  TLastExceptionInfo = record
+    ClassName : string;
+    Message : string;
+    more : boolean;
+    doraise : boolean;
+  end;
+
   // Standard FPC exports.
   TWASIExports = Class External name 'Object' (TJSModulesExports)
   Public
@@ -338,7 +357,7 @@ type
   TConsoleReadEvent = Procedure(Sender : TObject; Var AInput : String) of object;
   TConsoleWriteEvent = Procedure (Sender : TObject; aOutput : string) of object;
   TCreateExtensionEvent = procedure (sender : TObject; aExtension : TImportExtension) of object;
-
+  TWasmExceptionEvent = procedure (Sender : TObject; var aInfo : TLastExceptionInfo) of object;
   { TWASIHost }
 
   TWASIHost = Class(TComponent)
@@ -348,6 +367,7 @@ type
     FAutoCreateExtensions: Boolean;
     FBeforeInstantation: TNotifyEvent;
     FBeforeStart: TBeforeStartEvent;
+    FConvertNativeExceptions: Boolean;
     FEnv: TPas2JSWASIEnvironment;
     FExcludeExtensions: TStrings;
     FExported: TWASIExports;
@@ -355,6 +375,7 @@ type
     FOnExtensionCreated: TCreateExtensionEvent;
     FOnInstantiateFail: TFailEvent;
     FOnLoadFail: TFailEvent;
+    FOnWasmException: TWasmExceptionEvent;
     FPreparedStartDescriptor: TWebAssemblyStartDescriptor;
     FMemoryDescriptor : TJSWebAssemblyMemoryDescriptor;
     FOnConsoleRead: TConsoleReadEvent;
@@ -374,6 +395,10 @@ type
     procedure SetUseSharedMemory(AValue: Boolean);
   protected
     class function NeedSharedMemory : Boolean; virtual;
+    // Calls GetExceptionInfo to get exception info and calls OnWasmException if assigned. Return true if exception must be reraised.
+    function ConvertWasmException: boolean;
+    // Wrap exported functions in a wrapper that converts native exceptions to actual exceptions.
+    function WrapExports(aExported: TWASIExports): TWASIExports;
     // Delete all created extensions
     procedure DeleteExtensions;
     // Create registered extensions
@@ -412,6 +437,8 @@ type
     Function FindExtension(const aExtension : string) : TImportExtension;
     // Get an extension by registered or class name. Raises exception if it does not exist or has wrong class
     Generic Function GetExtension<T : TImportExtension>(const aExtension : string) : T;
+    // Retrieves webassembly exception info. Pops exception object from the stack.
+    function GetExceptionInfo(var aInfo: TLastExceptionInfo): boolean;
     // Will call OnConsoleWrite or write to console
     procedure WriteOutput(const aOutput: String); virtual;
     // Prepare start descriptor
@@ -444,6 +471,8 @@ type
     Property IsProgram : Boolean Read GetIsProgram;
     // Name of function to run. If empty, the FPC default _start is used.
     Property RunEntryFunction : String Read FRunEntryFunction Write FRunEntryFunction;
+    // When calling a function and an exception is raised, attempt to get information on native FPC exceptions
+    Property ConvertNativeExceptions : Boolean Read FConvertNativeExceptions Write FConvertNativeExceptions;
     // Called after webassembly start was run. Not called if webassembly was not run.
     Property AfterStart : TAfterStartEvent Read FAfterStart Write FAfterStart;
     // Called before running webassembly. If aAllowRun is false, running is disabled
@@ -471,6 +500,8 @@ type
     Property OnExtensionCreated : TCreateExtensionEvent Read FOnExtensionCreated Write FOnExtensionCreated;
     // Called for each auto-created extension
     Property OnAllExtensionsCreated : TNotifyEvent Read FOnAllExtensionsCreated Write FOnAllExtensionsCreated;
+    // When a webassembly exception was found, this is called. Return true if it must be treated (i.e. raised) and false if it can be ignored
+    Property OnWasmException: TWasmExceptionEvent Read FOnWasmException Write FOnWasmException;
   end;
   TWASIHostClass = class of TWASIHost;
 
@@ -587,13 +618,109 @@ begin
     FOnInstantiateFail(Self,aError);
 end;
 
+function TWASIHost.GetExceptionInfo(var aInfo : TLastExceptionInfo) : boolean;
+
+type
+  TGetExceptionInfoProc = function : TWasmPointer;
+  TReleaseExceptionInfoProc = procedure(aInfo : TWasmPointer);
+var
+  lPtr,lPointer,lString : TWasmPointer;
+  lLen : integer;
+  lVal,lVal2 : JSValue;
+  lProc : TGetExceptionInfoProc absolute lVal;
+  lProc2 : TReleaseExceptionInfoProc absolute lVal2;
+begin
+  Result:=False;
+  lVal:=Exported['GetLastExceptionInfo'];
+  lVal2:=Exported['FreeLastExceptionInfo'];
+  if not (IsDefined(lVal) and IsDefined(lVal2)) then
+    exit;
+  lPointer:=lProc();
+  if lPointer=0 then
+    exit;
+  lPtr:=lPointer;
+  lString:=WasiEnvironment.GetMemInfoInt32(lPtr);
+  inc(lPtr,SizeInt32);
+  lLen:=WasiEnvironment.GetMemInfoInt32(lPtr);
+  inc(lPtr,SizeInt32);
+  aInfo.ClassName:=WasiEnvironment.GetUTF8StringFromMem(lString,lLen);
+  lString:=WasiEnvironment.GetMemInfoInt32(lPtr);
+  inc(lPtr,SizeInt32);
+  lLen:=WasiEnvironment.GetMemInfoInt32(lPtr);
+  inc(lPtr,SizeInt32);
+  aInfo.Message:=WasiEnvironment.GetUTF8StringFromMem(lString,lLen);
+  aInfo.More:=WasiEnvironment.GetMemInfoInt8(lPtr)<>0;
+  lProc2(lPointer);
+  Result:=True;
+end;
+
+function TWASIHost.ConvertWasmException : boolean;
+
+var
+  lInfo : TLastExceptionInfo;
+
+begin
+  // if there is no info, we must raise
+  lInfo:=Default(TLastExceptionInfo);
+  Result:=not GetExceptionInfo(lInfo);
+  if Result then
+    exit;
+  lInfo.doraise:=true;
+  if Assigned(OnWasmException) then
+    FOnWasmException(Self,lInfo);
+  if lInfo.DoRaise then
+    Raise EWasmNativeException.Create(lInfo.ClassName,lInfo.Message);
+end;
+
+function TWASIHost.WrapExports(aExported : TWASIExports) : TWASIExports;
+
+  function createwrapper(aFunc : jsValue) : jsvalue;
+  begin
+    Result:=function() : jsvalue
+        begin
+          Result:=undefined;
+          try
+            asm
+              Result=aFunc(arguments);
+            end;
+          except
+            on E : TJSWebAssemblyException do
+              begin
+              if ConvertWasmException then // will raise
+                Raise;
+              end;
+          end;
+        end;
+  end;
+
+var
+  S : String;
+  lFunc : JSValue;
+  LNew : TWASIExports;
+
+begin
+  LNew:=TWASIExports.new;
+  For S in TJSObject.getOwnPropertyNames(aExported) do
+    begin
+    lFunc:=aExported[s];
+    if (not isFunction(lFunc)) or (S='GetLastExceptionInfo') or (S='FreeLastExceptionInfo') then
+      lNew.Properties[s]:=lFunc
+    else
+      lNew.Properties[s]:=CreateWrapper(lFunc);
+  end;
+  Result:=LNew;
+end;
+
 procedure TWASIHost.PrepareWebAssemblyInstance(aDescr: TWebAssemblyStartDescriptor);
 begin
+  if ConvertNativeExceptions then
+    aDescr.Exported:=WrapExports(aDescr.Exported);
   FPreparedStartDescriptor:=aDescr;
-  FExported:=aDescr.Exported;
+  FExported:=FPreparedStartDescriptor.Exported;
   WasiEnvironment.Instance:=aDescr.Instance;
   WasiEnvironment.SetMemory(aDescr.Memory);
   WasiEnvironment.SetExports(FExported);
+  //if ConvertExceptions then
   // We do this here, so in the event, the FPreparedStartDescriptor Is ready.
   DoAfterInstantiate;
 end;
@@ -732,6 +859,7 @@ begin
   FTableDescriptor.element:='anyfunc';
   FPredefinedConsoleInput:=TStringList.Create;
   FExcludeExtensions:=TStringList.Create;
+  FConvertNativeExceptions:=True;
 end;
 
 destructor TWASIHost.Destroy;
@@ -900,6 +1028,15 @@ begin
   Result.Imports:=aImportObj;
 end;
 
+{ EWasmNativeException }
+
+constructor EWasmNativeException.create(const aNativeClass, aNativeMessage: string);
+begin
+  Inherited createFmt('Webassembly code raised an exception %s : %s',[aNativeClass,aNativeMessage]);
+  FNativeClass:=aNativeClass;
+  FNativeMessage:=aNativeMessage;
+end;
+
 { EWasiFSError }
 
 constructor EWasiFSError.Create(const aErrorCode: Integer; aMsg: String);

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff