Skip to content

Commit

Permalink
introducing QuickJS low-level static binding for FPC and Delphi
Browse files Browse the repository at this point in the history
- with basic regression tests, and integration in the mormot.script.core shared unit
  • Loading branch information
Arnaud Bouchez committed Mar 27, 2021
1 parent 846632e commit 5394857
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 59 deletions.
1 change: 1 addition & 0 deletions src/lib/mormot.lib.quickjs.pas
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ interface
// we supply https://github.com/c-smile/quickjspp (uncompatible) fork statics
// - 64-bit JSValue on all platforms, JSX, debugger, Windows/Delphi compatible
// - amalgamation file with for static integration (malloc, assert)
// - see res/static/libquickjs for patched source and build instructions
{$define JS_STRICT_NAN_BOXING}
{$define LIBQUICKJS}
{$endif LIBQUICKJSSTATIC}
Expand Down
32 changes: 31 additions & 1 deletion src/mormot.defines.inc
Original file line number Diff line number Diff line change
Expand Up @@ -513,16 +513,46 @@
// https://github.com/synopse/mORMot2/releases

{$ifdef FPC}
// Delphi doesn't accept GCC object files and libdeflate requires GCC
{$if defined(OSOPENBSD) and defined(FPC_CROSSCOMPILING)}
{$define NOSQLITE3STATIC} // OpenBSD problems with fpcupdeluxe libgcc.a
{$ifend}
{$ifdef OSLINUX}
{$ifdef CPUINTEL}
{$define LIBDEFLATESTATIC} // libdeflate static binding
{$define LIBQUICKJSSTATIC} // quickjs static binding
{$endif CPUINTEL}
{$ifdef CPUARM}
{.$define LIBDEFLATESTATIC} // compiles, but untested
{.$define LIBQUICKJSSTATIC} // compiles, but untested
{$endif CPUARM}
{$ifdef CPUAARCH64}
{.$define LIBDEFLATESTATIC} // compiles, but untested
{.$define LIBQUICKJSSTATIC} // compiles, but untested
{$endif CPUAARCH64}
{$endif OSLINUX}
{$ifdef OSWINDOWS}
{$ifdef CPUX86}
{$define LIBDEFLATESTATIC}
{$define LIBQUICKJSSTATIC}
{$endif CPUX86}
{$ifdef CPUX64}
{.$define LIBDEFLATESTATIC} // Win64 + FPC 3.2 = internal error 200603061
{$define LIBQUICKJSSTATIC}
{$endif CPUX64}
{$endif OSWINDOWS}
{$else}
{$ifdef CPUX86}
{$define LIBQUICKJSSTATIC} // we have our quickjs.obj :)
{$endif CPUX86}
// there is a linking bug with Delphi XE4 on Win64
{$ifdef CPUX64}
{$if CompilerVersion = 25.0} // exactly XE4
// other Win32/Win64 Delphi platforms "should work" (tm) as expected
{$define NOSQLITE3STATIC}
{$ifend}
{$endif} // other Win32/Win64 Delphi platforms should work as expected
{$define LIBQUICKJSSTATIC}
{$endif}
{$endif FPC}

{$ifdef OSWINDOWS}
Expand Down
85 changes: 58 additions & 27 deletions src/script/mormot.script.core.pas
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ TThreadSafeManager = class(TSynPersistent)
fRemoteDebugger: IRemoteDebugger;
fWorkerManager: IWorkerManager;
fOnLog: TSynLogProc;
function ThreadEngineIndex(aThreadID: TThreadID): integer;
function ThreadEngineIndex(aThreadID: TThreadID): PtrInt;
function GetPauseDebuggerOnFirstStep: boolean;
procedure SetPauseDebuggerOnFirstStep(aPauseDebuggerOnFirstStep: boolean);
function GetEngineExpireTimeOutMinutes: cardinal;
Expand All @@ -249,6 +249,13 @@ TThreadSafeManager = class(TSynPersistent)
// - redirect to fEngineClass.From()
function Engine(aContext: TScriptContext): TThreadSafeEngine; overload;
{$ifdef HASINLINE} inline; {$endif}
/// initialize a new engine to be used outside of our engine pool
// - the returned engine won't be owned by this class, so is to be released
// explicitely by the caller
// - this engine won't be registered to the debugger
function NewEngine: TThreadSafeEngine;
/// returns how many times the NewEngine method has been called
function NewEngineCount: integer;
/// setup and create the main engine associated with the pools
// - should be called once at startup from the main thread
// - this engine won't be part of the internal ThreadSafeEngine() pool
Expand Down Expand Up @@ -344,6 +351,7 @@ TThreadSafeManager = class(TSynPersistent)

implementation


{ TThreadSafeManager }

constructor TThreadSafeManager.Create(aEngineClass: TThreadSafeEngineClass;
Expand Down Expand Up @@ -379,11 +387,24 @@ destructor TThreadSafeManager.Destroy;
fEngines.Free;
end;

function TThreadSafeManager.ThreadEngineIndex(aThreadID: TThreadID): PtrInt;
var
e: PPointerArray;
begin
// caller made fEngines.Safe.Lock
e := pointer(fEngines.List);
for result := 0 to fEngines.Count - 1 do
// brute force search is fast enough since fMaxEngines is small
if TThreadSafeEngine(e[result]).fThreadID = aThreadID then
exit;
result := -1;
end;

function TThreadSafeManager.ThreadSafeEngine(ThreadData: pointer;
TagForNewEngine: PtrInt): TThreadSafeEngine;
var
tid: TThreadID;
i: integer;
i: PtrInt;
tobereleased: TThreadSafeEngine;
begin
// retrieve or (re)create the engine associated with this thread
Expand Down Expand Up @@ -447,7 +468,7 @@ function TThreadSafeManager.ThreadSafeEngine(ThreadData: pointer;

function TThreadSafeManager.Engine(aThreadID: TThreadID): TThreadSafeEngine;
var
i: integer;
i: PtrInt;
begin
result := fMainEngine;
if (result <> nil) and
Expand Down Expand Up @@ -486,6 +507,7 @@ function TThreadSafeManager.InitializeMainEngine: TThreadSafeEngine;
fMainEngine := result;
result.fNameForDebug := 'Main';
result.fNeverExpire := true; // not in the pool, anyway
result.AfterCreate;
if Assigned(fRemoteDebugger) and
fDebugMainThread then
fRemoteDebugger.StartDebugCurrentThread(result);
Expand All @@ -498,6 +520,25 @@ function TThreadSafeManager.InitializeMainEngine: TThreadSafeEngine;
raise EScriptException.CreateUtf8('Invalid %.InitializeMainEngine', [self]);
end;

var
NewEngineSequence: integer;

function TThreadSafeManager.NewEngine: TThreadSafeEngine;
begin
result := fEngineClass.Create(nil, nil, 0, 0);
FormatUtf8('NewEngine%', [InterlockedIncrement(NewEngineSequence)],
result.fNameForDebug);
result.fNeverExpire := true; // not in the pool, anyway
result.AfterCreate;
if Assigned(fOnNewEngine) then
result.ThreadSafeCall(fOnNewEngine);
end;

function TThreadSafeManager.NewEngineCount: integer;
begin
result := NewEngineSequence;
end;

function TThreadSafeManager.GetEngineExpireTimeOutMinutes: cardinal;
begin
result := fEngineExpireTimeOutTix div 60000;
Expand All @@ -518,20 +559,6 @@ function TThreadSafeManager.NewWorkerManager: IWorkerManager;
result := nil;
end;

function TThreadSafeManager.ThreadEngineIndex(aThreadID: TThreadID): integer;
var
e: ^TThreadSafeEngine;
begin
// caller made fEngines.Safe.Lock
e := pointer(fEngines.List);
for result := 0 to fEngines.Count - 1 do
if e^.fThreadID = aThreadID then
exit
else
inc(e); // brute force search is fast enough since fMaxEngines is small
result := -1;
end;

function TThreadSafeManager.GetPauseDebuggerOnFirstStep: boolean;
begin
if Assigned(fRemoteDebugger) then
Expand Down Expand Up @@ -593,19 +620,22 @@ constructor TThreadSafeEngine.Create(aManager: TThreadSafeManager;
inherited Create;
fManager := aManager;
fCreateTix := GetTickCount64;
fContentVersion := fManager.ContentVersion;
fThreadID := aThreadId;
fThreadData := aThreadData;
fTag := aTag;
if Assigned(fManager.fOnGetName) then
fNameForDebug := fManager.fOnGetName(self);
if fNameForDebug = '' then
FormatUtf8('% %', [PointerToHexShort(pointer(PtrUInt(fThreadId))),
CurrentThreadName], fNameForDebug);
if Assigned(fManager.fOnGetWebAppRootPath) then
fWebAppRootDir := fManager.fOnGetWebAppRootPath(self)
else
StringToUtf8(Executable.ProgramFilePath, fWebAppRootDir);
if Assigned(fManager) then
begin
fContentVersion := fManager.ContentVersion;
if Assigned(fManager.fOnGetName) then
fNameForDebug := fManager.fOnGetName(self);
if fNameForDebug = '' then
FormatUtf8('% %', [PointerToHexShort(pointer(PtrUInt(fThreadId))),
CurrentThreadName], fNameForDebug);
if Assigned(fManager.fOnGetWebAppRootPath) then
fWebAppRootDir := fManager.fOnGetWebAppRootPath(self)
else
StringToUtf8(Executable.ProgramFilePath, fWebAppRootDir);
end;
// TThreadSafeManager will now call AfterCreate outside of its main lock
end;

Expand Down Expand Up @@ -648,6 +678,7 @@ procedure TThreadSafeEngine.AtomCacheAdd(const Name: RawUtf8; Atom: TScriptAtom)

procedure TThreadSafeEngine.DoBeginRequest;
begin
// paranoid todo: check if we need a Lock here to avoid GPF at expiration?
if fRequestFpuBackup <> [] then
// typical pascal FPU mask is [exDenormalized,exUnderflow,exPrecision]
raise EScriptException.CreateUtf8('Nested %.DoBeginRequest', [self]);
Expand Down
10 changes: 10 additions & 0 deletions test/mormot2tests.dpr
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ uses
mormot.core.interfaces in '..\src\core\mormot.core.interfaces.pas',
mormot.core.mustache in '..\src\core\mormot.core.mustache.pas',
mormot.core.zip in '..\src\core\mormot.core.zip.pas',
mormot.lib.static in '..\src\lib\mormot.lib.static.pas',
mormot.lib.z in '..\src\lib\mormot.lib.z.pas',
mormot.lib.lizard in '..\src\lib\mormot.lib.lizard.pas',
mormot.lib.winhttp in '..\src\lib\mormot.lib.winhttp.pas',
mormot.lib.curl in '..\src\lib\mormot.lib.curl.pas',
mormot.lib.sspi in '..\src\lib\mormot.lib.sspi.pas',
mormot.lib.gssapi in '..\src\lib\mormot.lib.gssapi.pas',
mormot.lib.openssl11 in '..\src\lib\mormot.lib.openssl11.pas',
mormot.lib.quickjs in '..\src\lib\mormot.lib.quickjs.pas',
mormot.net.sock in '..\src\net\mormot.net.sock.pas',
mormot.net.http in '..\src\net\mormot.net.http.pas',
mormot.net.relay in '..\src\net\mormot.net.relay.pas',
Expand Down Expand Up @@ -76,6 +78,8 @@ uses
mormot.rest.http.client in '..\src\rest\mormot.rest.http.client.pas',
mormot.rest.http.server in '..\src\rest\mormot.rest.http.server.pas',
mormot.rest.mvc in '..\src\rest\mormot.rest.mvc.pas',
mormot.script.core in '..\src\script\mormot.script.core.pas',
mormot.script.quickjs in '..\src\script\mormot.script.quickjs.pas',
mormot.db.core in '..\src\db\mormot.db.core.pas',
mormot.db.sql in '..\src\db\mormot.db.sql.pas',
mormot.db.proxy in '..\src\db\mormot.db.proxy.pas',
Expand Down Expand Up @@ -109,6 +113,9 @@ uses
test.core.data in '.\test.core.data.pas',
test.core.crypt in '.\test.core.crypt.pas',
test.core.ecc in '.\test.core.ecc.pas',
{$ifdef LIBQUICKJSSTATIC}
test.core.script in '.\test.core.script.pas',
{$endif LIBQUICKJSSTATIC}
test.net.proto in '.\test.net.proto.pas',
test.orm.core in '.\test.orm.core.pas',
test.orm.sqlite3 in '.\test.orm.sqlite3.pas',
Expand Down Expand Up @@ -151,6 +158,9 @@ end;

procedure TIntegrationTests.SOA;
begin
{$ifdef LIBQUICKJSSTATIC}
AddCase(TTestCoreScript);
{$endif LIBQUICKJSSTATIC}
//exit;
AddCase([
//
Expand Down
Loading

0 comments on commit 5394857

Please sign in to comment.