Browse Source

* Pas2JS side of web storage API

Michael Van Canneyt 2 tháng trước cách đây
mục cha
commit
566f2c2099

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
demo/wasienv/storage/bulma.min.css


+ 4 - 0
demo/wasienv/storage/hostconfig.js

@@ -0,0 +1,4 @@
+var hostConfig = {
+  wasmFilename : "demostorage.wasm",
+  logStorageAPI : false
+};

+ 42 - 0
demo/wasienv/storage/index.html

@@ -0,0 +1,42 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+  <title>Webassembly Storage Access demo</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link href="bulma.min.css" rel="stylesheet">
+  <script src="hostconfig.js"></script>
+  <script src="storagehost.js"></script>
+  <style>
+      #pasjsconsole {
+        border-style: solid;
+        border-width: 1px;
+        margin-left: 64px;
+        margin-right: 64px;
+        min-height: 75vh;
+      }
+    </style>
+</head>
+<body>
+  <div class="container">
+    <h3 class="title is-3">Webassembly program output</h3>
+    <p>
+    This program shows how to access the browser's local (or session) storage in a
+    webassembly program.
+    </p>
+    <div class="box">
+      <div id="pasjsconsole"></div>
+    </div>
+  <div>
+    <label for="cbLog">
+      <input id="cbLog" type="checkbox" checked autocomplete="off">
+      Show API log (needs reload)
+    </label>
+  </div>
+  </div>
+  <script>
+    rtl.showUncaughtExceptions=true;
+    window.addEventListener("load", rtl.run);
+  </script>
+</body>
+</html>

+ 91 - 0
demo/wasienv/storage/storagehost.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="Webassembly storage demo - host program"/>
+      <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="storagehost.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="storagehost"/>
+    </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>

+ 94 - 0
demo/wasienv/storage/storagehost.lpr

@@ -0,0 +1,94 @@
+{
+    This file is part of the Free Component Library
+
+    Webassembly Storage API - Demo program
+    Copyright (c) 2025 by Michael Van Canneyt [email protected]
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+program storagehost;
+
+{$mode objfpc}
+{$modeswitch externalclass}
+
+uses
+  BrowserConsole, BrowserApp, WASIHostApp, JS, Classes, SysUtils, Web, wasm.pas2js.storage;
+
+type
+  THostConfig = class external name 'Object' (TJSObject)
+    wasmFilename : String;
+    logStorageAPI : Boolean;
+    logWasiAPI : Boolean;
+  end;
+
+var
+  HostConfig : THostConfig; external name 'hostConfig';
+
+Type
+  { TMyApplication }
+
+  TMyApplication = class(TWASIHostApplication)
+    cbLog:TJSHTMLInputElement;
+    FStorage : TStorageAPI;
+  private
+    procedure HandleLogClick(Event: TJSEvent);
+  protected
+    procedure DoRun; override;
+  public
+    Constructor Create(aOwner : TComponent); override;
+  end;
+
+procedure TMyApplication.DoRun;
+var
+  wasm : String;
+
+begin
+  Terminate;
+  if Assigned(HostConfig) and isString(HostConfig.wasmFilename) then
+    Wasm:=HostConfig.wasmFilename
+  else
+    begin
+    Wasm:=ParamStr(1);
+    if Wasm='' then
+      Wasm:='storagedemo.wasm';
+    end;
+  StartWebAssembly(wasm);
+end;
+
+procedure TMyApplication.HandleLogClick(Event : TJSEvent);
+
+begin
+  FStorage.LogAPI:=cbLog.Checked;
+  Window.localStorage.SetItem('showlog',IntToStr(Ord(cbLog.Checked)));
+end;
+
+constructor TMyApplication.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FStorage:=TStorageAPI.Create(WasiEnvironment);
+  if isDefined(hostConfig) and Assigned(hostConfig) then
+     begin
+     WasiEnvironment.LogAPI:=HostConfig.logWasiAPi;
+     FStorage.LogAPI:=HostConfig.logStorageAPI;
+     end;
+  FStorage.LogAPI:=FStorage.LogAPI or (Window.localStorage.getItem('showlog')='1');
+  cbLog:=TJSHTMLInputElement(GetHTMLElement('cbLog'));
+  cbLog.Checked:=FStorage.LogAPI;
+  cbLog.addEventListener('click',@HandleLogClick);
+end;
+
+var
+  Application : TMyApplication;
+
+begin
+  Application:=TMyApplication.Create(nil);
+  Application.Initialize;
+  Application.Run;
+end.

+ 207 - 0
packages/wasm-utils/src/wasm.pas2js.storage.pas

@@ -0,0 +1,207 @@
+unit wasm.pas2js.storage;
+
+{$mode ObjFPC}
+
+// Uncomment this if you want to remove all logging calls
+{ $DEFINE NOLOGAPICALLS}
+interface
+
+uses
+  js, web, wasienv, wasm.storage.shared;
+
+Type
+  
+  { TStorageAPI }
+
+  TStorageAPI = Class(TImportExtension)
+  Protected
+    function GetStorageName(aKind: Longint): string; virtual;
+    function GetStorage(aKind: Longint): TJSStorage; virtual;
+    function HandleClear(aStorageKind: Longint): longint; virtual;
+    function HandleKey(aStorageKind: Longint; aKey: integer; aResult, aResultLen: TWasmPointer): longint; virtual;
+    function HandleGetItem(aStorageKind: Longint; aKey: TWasmPointer; aKeyLen: Integer; aResult, aResultLen: TWasmPointer): longint; virtual;
+    function HandleLength(aStorageKind: Longint; aResult: TWasmPointer): longint; virtual;
+    function HandleRemoveItem(aStorageKind: Longint; aKey: TWasmPointer; aKeyLen: Integer): longint; virtual;
+    function HandleSetItem(aStorageKind: Longint; aKey: TWasmPointer; aKeyLen: Integer; aValue: TWasmPointer; aValueLen: Integer): longint; virtual;
+  public
+    procedure FillImportObject(aObject: TJSObject); override;
+    function ImportName : String; override;
+    property LogAPI;
+  end;
+
+implementation
+
+{ TLocalStorageAPI }
+
+function TStorageAPI.GetStorageName(aKind: Longint): string;
+begin
+  Case aKind of
+    STORAGE_LOCAL : Result:='localStorage';
+    STORAGE_SESSION : Result:='sessionStorage';
+  else
+    Result:='unknown';
+  end;
+end;
+
+function TStorageAPI.GetStorage(aKind: Longint): TJSStorage;
+
+begin
+  Case aKind of
+    STORAGE_LOCAL : Result:=window.localStorage;
+    STORAGE_SESSION : Result:=window.sessionStorage;
+  else
+    Result:=Nil;
+  end;
+end;
+
+function TStorageAPI.HandleKey(aStorageKind: Longint; aKey: integer; aResult, aResultLen: TWasmPointer): longint;
+var
+  lStorage : TJSStorage;
+  lValue : string;
+  lLen,lLoc : integer;
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  if LogAPI then
+    DoLog('Key(%s,%d)',[GetStorageName(aStorageKind),aKey]);
+  {$ENDIF}
+  lStorage:=GetStorage(aStorageKind);
+  if lStorage=Nil then
+    exit(ESTORAGE_INVALIDKIND);
+  lValue:=lStorage.key(aKey);
+  if isnull(lValue) or (Length(lValue)=0) then
+    begin
+    Env.SetMemInfoInt32(aResult,0);
+    Env.SetMemInfoInt32(aResultLen,0);
+    end
+  else
+    begin
+    lLen:=Length(lValue)*4;
+    lLoc:=InstanceExports.AllocMem(lLen);
+    lLen:=env.SetUTF8StringInMem(lLoc,lLen,lValue);
+    Env.SetMemInfoInt32(aResult,lLoc);
+    Env.SetMemInfoInt32(aResultLen,lLen);
+    end;
+  Result:=ESTORAGE_SUCCESS;
+end;
+
+function TStorageAPI.HandleGetItem(aStorageKind: Longint; aKey: TWasmPointer; aKeyLen: Integer; aResult, aResultLen: TWasmPointer
+  ): longint;
+var
+  lStorage : TJSStorage;
+  lKey : String;
+  lValue : String;
+  lLen,lLoc : Integer;
+begin
+  lStorage:=GetStorage(aStorageKind);
+  lKey:=env.GetUTF8StringFromMem(aKey,aKeyLen);
+  {$IFNDEF NOLOGAPICALLS}
+  if LogAPI then
+    DoLog('GetItem(%s,"%s")',[GetStorageName(aStorageKind),lKey]);
+  {$ENDIF}
+  if lStorage=Nil then
+    exit(ESTORAGE_INVALIDKIND);
+  lValue:=lStorage.getItem(lKey);
+  if isnull(lValue) or (Length(lValue)=0) then
+    begin
+    Env.SetMemInfoInt32(aResult,0);
+    Env.SetMemInfoInt32(aResultLen,0);
+    end
+  else
+    begin
+    lLen:=Length(lValue)*4;
+    lLoc:=InstanceExports.AllocMem(lLen);
+    lLen:=env.SetUTF8StringInMem(lLoc,lLen,lValue);
+    Env.SetMemInfoInt32(aResult,lLoc);
+    Env.SetMemInfoInt32(aResultLen,lLen);
+    end;
+  Result:=ESTORAGE_SUCCESS;
+end;
+
+function TStorageAPI.HandleLength(aStorageKind: Longint; aResult: TWasmPointer): longint;
+var
+  lStorage : TJSStorage;
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  if LogAPI then
+    DoLog('Length(%s)',[GetStorageName(aStorageKind)]);
+  {$ENDIF}
+  lStorage:=GetStorage(aStorageKind);
+  if lStorage=Nil then
+    exit(ESTORAGE_INVALIDKIND);
+  env.SetMemInfoInt32(aResult,lStorage.Length);
+  Result:=ESTORAGE_SUCCESS
+end;
+
+function TStorageAPI.HandleSetItem(aStorageKind: Longint; aKey: TWasmPointer; aKeyLen: Integer; aValue: TWasmPointer;
+  aValueLen: Integer): longint;
+
+var
+  lStorage : TJSStorage;
+  lKey : String;
+  lValue : String;
+begin
+  lKey:=env.GetUTF8StringFromMem(aKey,aKeyLen);
+  lValue:=env.GetUTF8StringFromMem(aValue,aValueLen);
+  {$IFNDEF NOLOGAPICALLS}
+  if LogAPI then
+    DoLog('SetItem(%s,"%s","%s")',[GetStorageName(aStorageKind),lKey,lValue]);
+  {$ENDIF}
+  lStorage:=GetStorage(aStorageKind);
+  if lStorage=Nil then
+    exit(ESTORAGE_INVALIDKIND);
+  lKey:=env.GetUTF8StringFromMem(aKey,aKeyLen);
+  lValue:=env.GetUTF8StringFromMem(aValue,aValueLen);
+  lStorage.setItem(lKey,lValue);
+  Result:=ESTORAGE_SUCCESS;
+end;
+
+function TStorageAPI.HandleRemoveItem(aStorageKind: Longint; aKey: TWasmPointer; aKeyLen: Integer): longint;
+
+var
+  lStorage : TJSStorage;
+  lKey : string;
+begin
+  lKey:=env.GetUTF8StringFromMem(aKey,aKeyLen);
+  {$IFNDEF NOLOGAPICALLS}
+  if LogAPI then
+    DoLog('RemoveItem(%s,"%s")',[GetStorageName(aStorageKind),lKey]);
+  {$ENDIF}
+  lStorage:=GetStorage(aStorageKind);
+  if lStorage=Nil then
+    exit(ESTORAGE_INVALIDKIND);
+  lStorage.removeItem(lKey);
+  Result:=ESTORAGE_SUCCESS;
+end;
+
+function TStorageAPI.HandleClear(aStorageKind: Longint): longint;
+
+var
+  lStorage : TJSStorage;
+begin
+  {$IFNDEF NOLOGAPICALLS}
+  if LogAPI then
+    DoLog('Clear(%s)',[GetStorageName(aStorageKind)]);
+  {$ENDIF}
+  lStorage:=GetStorage(aStorageKind);
+  if lStorage=Nil then
+    exit(ESTORAGE_INVALIDKIND);
+  lStorage.Clear;
+end;
+
+procedure TStorageAPI.FillImportObject(aObject: TJSObject);
+begin
+  aObject[storageFN_GetItem]:=@HandleGetItem;
+  aObject[storageFN_Key]:=@HandleKey;
+  aObject[storageFN_length]:=@HandleLength;
+  aObject[storageFN_SetItem]:=@HandleSetItem;
+  aObject[storageFN_RemoveItem]:=@HandleRemoveItem;
+  aObject[storageFN_Clear]:=@HandleRemoveItem;
+end;
+
+function TStorageAPI.ImportName: String;
+begin
+  result:=storageExportName
+end;
+
+end.
+

+ 26 - 0
packages/wasm-utils/src/wasm.storage.shared.pas

@@ -0,0 +1,26 @@
+unit wasm.storage.shared;
+
+{$mode ObjFPC}
+
+interface
+
+const
+  STORAGE_LOCAL   = 0;
+  STORAGE_SESSION = 1;
+
+  ESTORAGE_SUCCESS     = 0;
+  ESTORAGE_INVALIDKIND = -1;
+
+  storageExportName    = 'storage';
+  storageFN_GetItem    = 'storage_get_item';
+  storageFN_Key        = 'storage_key';
+  storageFN_length     = 'storage_length';
+  storageFN_SetItem    = 'storage_set_item';
+  storageFN_RemoveItem = 'storage_remove_item';
+  storageFN_Clear      = 'storage_clear';
+
+
+implementation
+
+end.
+

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác