Use WebAssembly

Share your advanced PureBasic knowledge/code with the community.
User avatar
Stefan Schnell
User
User
Posts: 85
Joined: Wed May 07, 2003 2:53 pm
Location: Germany - Oberirsen
Contact:

Use WebAssembly

Post by Stefan Schnell »

Hello Community,
WebAssembly (Wasm) is a very interesting format. It is s designed as a portable compilation target for different programming languages. It is very easy to use Wasm from the Extism PDKs via C-API of it in PureBasic.
Enjoy it.
Stefan

Code: Select all

#EXTISM_SUCCESS = 0

Procedure ErrorHandler()
  PrintN("ERROR: " + ErrorMessage())
EndProcedure

Procedure countVowels(input.s)

  fileName.s = "..//c-pdk//count_vowels.wasm"

  libExtism.i = #Null
  CompilerSelect  #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
      libExtism = OpenLibrary(#PB_Any, ".//libextism.so")
    CompilerCase #PB_OS_MacOS
      libExtism = OpenLibrary(#PB_Any, ".//libextism.dylib")
    CompilerCase #PB_OS_Windows
      libExtism = OpenLibrary(#PB_Any, ".//extism.dll")
  CompilerEndSelect  

  If libExtism
    IncludeFile ".//getLibExtismFunctions.pbi"
  Else
    PrintN("ERROR: Can not open library")
    ProcedureReturn
  EndIf

  wasmFile.i = OpenFile(#PB_Any, fileName)
  If Not wasmFile
    CloseLibrary(libExtism)
    PrintN("ERROR: Can not open wasm file")
    ProcedureReturn
  EndIf

  *wasm = AllocateMemory(Lof(wasmFile))
  If *wasm And ReadData(wasmFile, *wasm, Lof(wasmFile))

    errmsg.s = #Null$
    *errmsg = @errmsg

    plugin.i = CallCFunctionFast(*extismPluginNew, *wasm, MemorySize(*wasm), #Null, 0, #True, @*errmsg)
    If plugin
      functionName.s = "count_vowels"
      functionExists.b = CallCFunctionFast(*extismPluginFunctionExists, plugin, UTF8(functionName))
      If functionExists

        rc.i = CallCFunctionFast(*extismPluginCall, plugin, UTF8(functionName), UTF8(input), Len(input))
        If rc = #EXTISM_SUCCESS
          outLen.i = CallCFunctionFast(*extismPluginOutputLength, plugin)
          out.i = CallCFunctionFast(*extismPluginOutputData, plugin)
          PrintN(PeekS(out, outLen, #PB_UTF8))
        Else
          *error = CallCFunctionFast(*extismPluginError, plugin)
          PrintN("ERROR: " + PeekS(*error, -1, #PB_UTF8))
        EndIf
        CallCFunctionFast(*extismPluginFree, plugin)

      EndIf
    Else
      PrintN("ERROR: " + PeekS(*errmsg, -1, #PB_UTF8))
      CallCFunctionFast(*extismPluginNewErrorFree, *errmsg)
    EndIf

    FreeMemory(*wasm)
  EndIf

  CloseFile(wasmFile)
  CloseLibrary(libExtism)

EndProcedure

Procedure Main()
  If OpenConsole()
    OnErrorCall(@ErrorHandler())
    countVowels(~"Hello World from PureBasic via WebAssembly")
    ; Expected output: {"count":13,"vowels":"aeiouAEIOU"}
    PrintN("Press return to exit")
    Input()
    CloseConsole()
  EndIf
EndProcedure

Main()

End

getLibExtismFunctions.pbi:

Code: Select all

*extismCompiledPluginFree            = GetFunction(libExtism, "extism_compiled_plugin_free")
*extismCompiledPluginNew             = GetFunction(libExtism, "extism_compiled_plugin_new")
*extismCurrentPluginHostContext      = GetFunction(libExtism, "extism_current_plugin_host_context")
*extismCurrentPluginMemory           = GetFunction(libExtism, "extism_current_plugin_memory")
*extismCurrentPluginMemoryAlloc      = GetFunction(libExtism, "extism_current_plugin_memory_alloc")
*extismCurrentPluginMemoryFree       = GetFunction(libExtism, "extism_current_plugin_memory_free")
*extismCurrentPluginMemoryLength     = GetFunction(libExtism, "extism_current_plugin_memory_length")
*extismError                         = GetFunction(libExtism, "extism_error")
*extismFunctionFree                  = GetFunction(libExtism, "extism_function_free")
*extismFunctionNew                   = GetFunction(libExtism, "extism_function_new")
*extismFunctionSetNamespace          = GetFunction(libExtism, "extism_function_set_namespace")
*extismLogCustom                     = GetFunction(libExtism, "extism_log_custom")
*extismLogDrain                      = GetFunction(libExtism, "extism_log_drain")
*extismLogFile                       = GetFunction(libExtism, "extism_log_file")
*extismPluginAllowHttpResponseHeader = GetFunction(libExtism, "extism_plugin_allow_http_response_headers")
*extismPluginCall                    = GetFunction(libExtism, "extism_plugin_call")
*extismPluginCallWithHostContext     = GetFunction(libExtism, "extism_plugin_call_with_host_context")
*extismPluginCancel                  = GetFunction(libExtism, "extism_plugin_cancel")
*extismPluginCancelHandle            = GetFunction(libExtism, "extism_plugin_cancel_handle")
*extismPluginConfig                  = GetFunction(libExtism, "extism_plugin_config")
*extismPluginError                   = GetFunction(libExtism, "extism_plugin_error")
*extismPluginFree                    = GetFunction(libExtism, "extism_plugin_free")
*extismPluginFunctionExists          = GetFunction(libExtism, "extism_plugin_function_exists")
*extismPluginId                      = GetFunction(libExtism, "extism_plugin_id")
*extismPluginNew                     = GetFunction(libExtism, "extism_plugin_new")
*extismPluginNewErrorFree            = GetFunction(libExtism, "extism_plugin_new_error_free")
*extismPluginNewFromCompiled         = GetFunction(libExtism, "extism_plugin_new_from_compiled")
*extismPluginNewWithFuelLimit        = GetFunction(libExtism, "extism_plugin_new_with_fuel_limit")
*extismPluginOutputData              = GetFunction(libExtism, "extism_plugin_output_data")
*extismPluginOutputLength            = GetFunction(libExtism, "extism_plugin_output_length")
*extismPluginReset                   = GetFunction(libExtism, "extism_plugin_reset")
*extismVersion                       = GetFunction(libExtism, "extism_version")
Quin
Addict
Addict
Posts: 1127
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Use WebAssembly

Post by Quin »

Whoa, that's cool. Since we've gotten it pretty much confirmed by Fred that WebAssembly isn't planned for PB/SB anytime soon if ever, this is even cooler. Thanks for sharing!
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Use WebAssembly

Post by skywalk »

Interesting, but this is far from a working snippet. :(
Where to get/generate count_vowels.wasm?
I get a manifest error.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Use WebAssembly

Post by infratec »

c-pdk:
https://github.com/extism/c-pdk

But this is only the source code.

Maybe this:
https://github.com/extism/plugins/releases
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Use WebAssembly

Post by skywalk »

Thanks, I got the binary dll for Windows, but the count_vowels.wasm file is stumping me.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Stefan Schnell
User
User
Posts: 85
Joined: Wed May 07, 2003 2:53 pm
Location: Germany - Oberirsen
Contact:

Re: Use WebAssembly

Post by Stefan Schnell »

Hello skywalk,

you can download the binary files of Extism here:
https://github.com/extism/extism/releases

And count_vowels.wasm is available here:
https://github.com/extism/plugins/relea ... owels.wasm

A great explanation of Extism is available here:
https://extism.org/docs/overview

Best regards
Stefan
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Use WebAssembly

Post by skywalk »

Thanks! That was the missing piece.
Have to read more. Questions on memory safety and threadsafe possible?

Your example uses callfunctions, but you could use prototypes and p-utf8 to avoid the UTF8() calls.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Use WebAssembly

Post by Caronte3D »

Very interesting! 8)
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Use WebAssembly

Post by infratec »

I modified Stefans code to use pseudo types, which makes the handling a bit easier.

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf


;
; An enumeration of all possible value types in WebAssembly.
;
Enumeration ExtismValType
  ;
  ; Signed 32 bit integer.
  ;
  #ExtismValType_I32
  ;
  ; Signed 64 bit integer.
  ;
  #ExtismValType_I64
  ;
  ; Floating point 32 bit integer.
  ;
  #ExtismValType_F32
  ;
  ; Floating point 64 bit integer.
  ;
  #ExtismValType_F64
  ;
  ; A 128 bit number.
  ;
  #ExtismValType_V128
  ;
  ; A reference to a Wasm function.
  ;
  #ExtismValType_FuncRef
  ;
  ; A reference to opaque data in the Wasm instance.
  ;
  #ExtismValType_ExternRef
EndEnumeration


; The return code from extism_plugin_call used to signal a successful call with no errors
#EXTISM_SUCCESS = 0

; An alias for I64 to signify an extism pointer
#EXTISM_PTR = #ExtismValType_I64


Structure ExtismValUnion
  StructureUnion
    i32.l
    i64.q
    f32.f
    f64.d
  EndStructureUnion
EndStructure

Structure ExtismVal
  t.i
  v.ExtismValUnion
EndStructure


Global extism_lib.i


Prototype Prototype_extism_free_user_data_cb()

Prototype Prototype_ExtismFunctionType(*plugin, *inputs.ExtismVal, n_inputs.i, *outputs.ExtismVal, n_outputs.i, *Data)

Prototype ExtismLogDrainFunctionType(Data_.p-UTF8, Size.i)





Prototype Prototype_extism_compiled_plugin_free(*plugin)
Prototype.i Prototype_extism_compiled_plugin_new(*wasm, wasm_size.i, *functions, n_functions.i, with_wasi.i, *errmsg)
Prototype Prototype_extism_current_plugin_host_context(*plugin)
Prototype Prototype_extism_current_plugin_memory(*plugin)
Prototype Prototype_extism_current_plugin_memory_alloc(*plugin, n.i)
Prototype Prototype_extism_current_plugin_memory_free(*plugin, ptr.i)
Prototype Prototype_extism_current_plugin_memory_length(*plugin, n.i)
Prototype.i Prototype_extism_error(*plugin)
Prototype Prototype_extism_function_free(*f)
Prototype.i Prototype_extism_function_new(name.p-utf8, *inputs, n_inputs.i, *outputs, n_outputs.i, func.i, *user_data, free_user_data.Prototype_extism_free_user_data_cb)
Prototype Prototype_extism_function_set_namespace(*ptr, namespace_.p-UTF8)
Prototype.i Prototype_extism_log_custom(log_level.p-UTF8)
Prototype Prototype_extism_log_drain(*handler)
Prototype.i Prototype_extism_log_file(filename.p-UTF8, log_level.p-UTF8)
Prototype Prototype_extism_plugin_allow_http_response_headers(*plugin)
Prototype.l Prototype_extism_plugin_call(*plugin, func_name.p-utf8, *Data, data_len.i)
Prototype.l Prototype_extism_plugin_call_with_host_context(*plugin, func_name.p-utf8, *Data, data_len.i, *host_context)
Prototype.i Prototype_extism_plugin_cancel(*handle)
Prototype.i Prototype_extism_plugin_cancel_handle(*plugin)
Prototype.i Prototype_extism_plugin_config(*plugin, *json, json_size.i)
Prototype.i Prototype_extism_plugin_error(*plugin)
Prototype Prototype_extism_plugin_free(*plugin)
Prototype.i Prototype_extism_plugin_function_exists(*plugin, func_name.p-utf8)
Prototype.i Prototype_extism_plugin_id(*plugin)
Prototype.i Prototype_extism_plugin_new(*wasm, wasm_size.i, *functions, n_functions.i, with_wasi.i, *errmsg)
Prototype Prototype_extism_plugin_new_error_free(*err)
Prototype.i Prototype_extism_plugin_new_from_compiled(*compiled, *errmsg)
Prototype.i Prototype_extism_plugin_new_with_fuel_limit(*wasm, wasm_size.i, *functions, n_functions.i, with_wasi.i, fuel_limit.i, *errmsg)
Prototype.i Prototype_extism_plugin_output_data(*plugin)
Prototype.i Prototype_extism_plugin_output_length(*plugin)
Prototype.i Prototype_extism_plugin_reset(*plugin)
Prototype.i Prototype_extism_version()



Global extism_compiled_plugin_free.Prototype_extism_compiled_plugin_free
Global extism_compiled_plugin_new.Prototype_extism_compiled_plugin_new
Global extism_current_plugin_host_context.Prototype_extism_current_plugin_host_context
Global extism_current_plugin_memory.Prototype_extism_current_plugin_memory
Global extism_current_plugin_memory_alloc.Prototype_extism_current_plugin_memory_alloc
Global extism_current_plugin_memory_free.Prototype_extism_current_plugin_memory_free
Global extism_current_plugin_memory_length.Prototype_extism_current_plugin_memory_length
Global extism_error_.Prototype_extism_error
Global extism_function_free.Prototype_extism_function_free
Global extism_function_new.Prototype_extism_function_new
Global extism_function_set_namespace.Prototype_extism_function_set_namespace
Global extism_log_custom.Prototype_extism_log_custom
Global extism_log_drain.Prototype_extism_log_drain
Global extism_log_file.Prototype_extism_log_file
Global extism_plugin_allow_http_response_headers.Prototype_extism_plugin_allow_http_response_headers
Global extism_plugin_call.Prototype_extism_plugin_call
Global extism_plugin_call_with_host_context.Prototype_extism_plugin_call_with_host_context
Global extism_plugin_cancel.Prototype_extism_plugin_cancel
Global extism_plugin_cancel_handle.Prototype_extism_plugin_cancel_handle
Global extism_plugin_config.Prototype_extism_plugin_config
Global extism_plugin_error_.Prototype_extism_plugin_error
Global extism_plugin_free.Prototype_extism_plugin_free
Global extism_plugin_function_exists.Prototype_extism_plugin_function_exists
Global extism_plugin_id.Prototype_extism_plugin_id
Global extism_plugin_new.Prototype_extism_plugin_new
Global extism_plugin_new_error_free.Prototype_extism_plugin_new_error_free
Global extism_plugin_new_from_compiled.Prototype_extism_plugin_new_from_compiled
Global extism_plugin_new_with_fuel_limit.Prototype_extism_plugin_new_with_fuel_limit
Global extism_plugin_output_data.Prototype_extism_plugin_output_data
Global extism_plugin_output_length.Prototype_extism_plugin_output_length
Global extism_plugin_reset.Prototype_extism_plugin_reset
Global extism_version_.Prototype_extism_version


Macro extism_error(plugin)
  PeekS(extism_error_(plugin), -1, #PB_UTF8)
EndMacro


Macro extism_plugin_error(plugin)
  PeekS(extism_plugin_error_(plugin), -1, #PB_UTF8)
EndMacro


Macro extism_version()
  PeekS(extism_version_(), -1, #PB_UTF8)
EndMacro


Procedure.i UseExtism()
  
  If Not extism_lib
    
    CompilerSelect  #PB_Compiler_OS
      CompilerCase #PB_OS_Linux
        extism_lib = OpenLibrary(#PB_Any, "./extism_lib.so")
      CompilerCase #PB_OS_MacOS
        extism_lib = OpenLibrary(#PB_Any, "./extism_lib.dylib")
      CompilerCase #PB_OS_Windows
        extism_lib = OpenLibrary(#PB_Any, "./extism.dll")
    CompilerEndSelect  
    
    If extism_lib
      extism_compiled_plugin_free               = GetFunction(extism_lib, "extism_compiled_plugin_free")
      extism_compiled_plugin_new                = GetFunction(extism_lib, "extism_compiled_plugin_new")
      extism_current_plugin_host_context        = GetFunction(extism_lib, "extism_current_plugin_host_context")
      extism_current_plugin_memory              = GetFunction(extism_lib, "extism_current_plugin_memory")
      extism_current_plugin_memory_alloc        = GetFunction(extism_lib, "extism_current_plugin_memory_alloc")
      extism_current_plugin_memory_free         = GetFunction(extism_lib, "extism_current_plugin_memory_free")
      extism_current_plugin_memory_length       = GetFunction(extism_lib, "extism_current_plugin_memory_length")
      extism_error_                             = GetFunction(extism_lib, "extism_error")
      extism_function_free                      = GetFunction(extism_lib, "extism_function_free")
      extism_function_new                       = GetFunction(extism_lib, "extism_function_new")
      extism_function_set_namespace             = GetFunction(extism_lib, "extism_function_set_namespace")
      extism_log_custom                         = GetFunction(extism_lib, "extism_log_custom")
      extism_log_drain                          = GetFunction(extism_lib, "extism_log_drain")
      extism_log_file                           = GetFunction(extism_lib, "extism_log_file")
      extism_plugin_allow_http_response_headers = GetFunction(extism_lib, "extism_plugin_allow_http_response_headers")
      extism_plugin_call                        = GetFunction(extism_lib, "extism_plugin_call")
      extism_plugin_call_with_host_context      = GetFunction(extism_lib, "extism_plugin_call_with_host_context")
      extism_plugin_cancel                      = GetFunction(extism_lib, "extism_plugin_cancel")
      extism_plugin_cancel_handle               = GetFunction(extism_lib, "extism_plugin_cancel_handle")
      extism_plugin_config                      = GetFunction(extism_lib, "extism_plugin_config")
      extism_plugin_error_                      = GetFunction(extism_lib, "extism_plugin_error")
      extism_plugin_free                        = GetFunction(extism_lib, "extism_plugin_free")
      extism_plugin_function_exists             = GetFunction(extism_lib, "extism_plugin_function_exists")
      extism_plugin_id                          = GetFunction(extism_lib, "extism_plugin_id")
      extism_plugin_new                         = GetFunction(extism_lib, "extism_plugin_new")
      extism_plugin_new_error_free              = GetFunction(extism_lib, "extism_plugin_new_error_free")
      extism_plugin_new_from_compiled           = GetFunction(extism_lib, "extism_plugin_new_from_compiled")
      extism_plugin_new_with_fuel_limit         = GetFunction(extism_lib, "extism_plugin_new_with_fuel_limit")
      extism_plugin_output_data                 = GetFunction(extism_lib, "extism_plugin_output_data")
      extism_plugin_output_length               = GetFunction(extism_lib, "extism_plugin_output_length")
      extism_plugin_reset                       = GetFunction(extism_lib, "extism_plugin_reset")
      extism_version_                           = GetFunction(extism_lib, "extism_version")
    EndIf
    
  EndIf
  
  ProcedureReturn extism_lib
  
EndProcedure


Procedure FreeExtism()
  If extism_lib
    CloseLibrary(extism_lib)
    extism_lib = #Null
  EndIf
EndProcedure



CompilerIf #PB_Compiler_IsMainFile
  
  Procedure countVowels(input.s)
    
    Protected.i wasmFile, functionExists, rc, outLen
    Protected *wasm, *plugin, *errmsg, *input, *out
    Protected errmsg.s, functionName.s
    
    
    wasmFile = OpenFile(#PB_Any, "count_vowels.wasm")
    If Not wasmFile
      PrintN("ERROR: Can not open wasm file")
      ProcedureReturn
    EndIf
    
    *wasm = AllocateMemory(Lof(wasmFile), #PB_Memory_NoClear)
    If *wasm And ReadData(wasmFile, *wasm, Lof(wasmFile)) = Lof(wasmFile)
      
      errmsg = #Null$
      *errmsg = @errmsg
      
      *plugin = extism_plugin_new(*wasm, MemorySize(*wasm), #Null, 0, #True, @*errmsg)
      If *plugin
        functionName = "count_vowels"
        functionExists = extism_plugin_function_exists(*plugin, functionName)
        If functionExists
          
          *input = UTF8(input)
          If *input
            rc = extism_plugin_call(*plugin, functionName, *input, MemorySize(*input))
            If rc = #EXTISM_SUCCESS
              outLen = extism_plugin_output_length(*plugin)
              *out = extism_plugin_output_data(*plugin)
              PrintN(PeekS(*out, outLen, #PB_UTF8|#PB_ByteLength))
            Else
              PrintN("ERROR: " + extism_plugin_error(*plugin))
            EndIf
            FreeMemory(*input)
          EndIf
          
          
        EndIf
        extism_plugin_free(*plugin)
      Else
        PrintN("ERROR: " + PeekS(*errmsg, -1, #PB_UTF8))
        extism_plugin_new_error_free(*errmsg)
      EndIf
      
      FreeMemory(*wasm)
    EndIf
    
    CloseFile(wasmFile)
    
  EndProcedure
  
  
  
  
  If UseExtism()
    
    If OpenConsole()
      
      Debug extism_version()
      
      countVowels(~"Hello World from PureBasic via WebAssembly")
      ; Expected output: {"count":13,"vowels":"aeiouAEIOU"}
      PrintN("Press return to exit")
      Input()
      CloseConsole()
    EndIf
    
    FreeExtism()
    
  EndIf
  
CompilerEndIf
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Use WebAssembly

Post by skywalk »

Thanks infratec!
My question is how was count_vowels.wasm compiled?
That is where we could obfuscate our PB and/or Spiderbasic code within a binary object.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Use WebAssembly

Post by infratec »

Post Reply