unit ufrmSM45Demo; interface uses {$IFNDEF LCL}Windows,{$ELSE}LclIntf, LMessages, LclType, LResources,{$ENDIF} Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, SynCommons, SpiderMonkey, SyNode, SyNodeProto, SyNodeSimpleProto; const WM_DEBUG_INTERRUPT = WM_USER + 1; type { TfrmSM45Demo } TfrmSM45Demo = class(TForm) mSource: TMemo; mResult: TMemo; btnEvaluate: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure btnEvaluateClick(Sender: TObject); private procedure cmd_Itenterupt(var aMessage: TMessage); message WM_DEBUG_INTERRUPT; protected FSMManager: TSMEngineManager; FEngine: TSMEngine; procedure DoOnCreateNewEngine(const aEngine: TSMEngine); function DoOnGetEngineName(const aEngine: TSMEngine): RawUTF8; // a handler, called from a debugger thread to interrupt a current thread // in this example will send a WM_DEBUG_INTERRUPT message to a main window // main application thread catch a message and call FEngine.InterruptCallback procedure doInteruptInOwnThread; /// here we add features to debugger console // type ? in firefox console to get a feature help procedure DoOnJSDebuggerInit(const aEngine: TSMEngine); published property sources: TMemo read mSource; property results: TMemo read mResult; property evaluateButton: TButton read btnEvaluate; function toLog(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; end; var frmSM45Demo: TfrmSM45Demo; implementation {$R *.dfm} {$I Synopse.inc} {$I SynSM.inc} // define SM_DEBUG JS_THREADSAFE CONSIDER_TIME_IN_Z {$I SyNode.inc} // define SM_DEBUG CONSIDER_TIME_IN_Z procedure TfrmSM45Demo.FormCreate(Sender: TObject); begin // create a JavaScript angine manager FSMManager := TSMEngineManager.Create( {$IFDEF CORE_MODULES_IN_RES}''{$ELSE}StringToUTF8(RelToAbs(ExeVersion.ProgramFilePath, '../../core_modules')){$ENDIF}); // optionaly increase a max engine memory FSMManager.MaxPerEngineMemory := 512 * 1024 * 1024; // add a handler called every time new engine is created // inside a handler we can add a binding's to a native functions (implemented in Delphi) // and evaluate some initial JavaScripts FSMManager.OnNewEngine := DoOnCreateNewEngine; FSMManager.OnGetName := DoOnGetEngineName; FSMManager.OnDebuggerInit := DoOnJSDebuggerInit; // start a JavaScript debugger on the localhost:6000 FSMManager.startDebugger('6000'); // debugger can see engines created after startDebugger() call, // so we create a main engine after debugger is started // in this example we need only one engine (we are single-thread) FEngine := FSMManager.ThreadSafeEngine(nil); end; procedure TfrmSM45Demo.FormDestroy(Sender: TObject); begin FSMManager.ReleaseCurrentThreadEngine; FSMManager.Free; end; procedure TfrmSM45Demo.btnEvaluateClick(Sender: TObject); var res: jsval; begin if FEngine = nil then raise Exception.Create('JS engine not initialized'); // evaluate a text from mSource memo if mSource.SelText <> '' then FEngine.Evaluate(mSource.SelText, 'mSourceSelected.js', 1, res) else FEngine.Evaluate(mSource.lines.Text, 'mSource.js', 1, res); end; procedure TfrmSM45Demo.cmd_Itenterupt(var aMessage: TMessage); begin if FEngine = nil then raise Exception.Create('JS engine not initialized'); {$IFDEF SM52} FEngine.cx.RequestInterruptCallback; FEngine.cx.CheckForInterrupt; {$ELSE} FEngine.rt.InterruptCallback(FEngine.cx); {$ENDIF} end; procedure TfrmSM45Demo.doInteruptInOwnThread; begin PostMessage(Self.Handle, WM_DEBUG_INTERRUPT, 0, 0); {$IFNDEF FPC} Application.ProcessMessages; {$ENDIF} end; function TfrmSM45Demo.toLog(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; begin try if (vp.argv[0].isString) then mResult.lines.add(vp.argv[0].asJSString.ToString(cx)) else raise ESMException.Create('toLog accept only String type of arg'); result := true; except on E: Exception do begin Result := False; JSError(cx, E); end; end; end; type TStringsProto = class(TSMSimpleRTTIProtoObject) protected procedure InitObject(aParent: PJSRootedObject); override; end; procedure TfrmSM45Demo.DoOnCreateNewEngine(const aEngine: TSMEngine); begin // for main thread only. Worker threads do not need this if GetCurrentThreadId = MainThreadID then begin aEngine.doInteruptInOwnThread := doInteruptInOwnThread; // in XE actual class of TMemo.lines is TMemoStrings - let's force it to be a TStrings like aEngine.defineClass(mSource.lines.ClassType, TStringsProto, aEngine.GlobalObject); // define a propery mainForm in the JavaScript aEngine.GlobalObject.ptr.DefineProperty(aEngine.cx, 'mainForm', // proeprty value is a wrapper around the Self CreateJSInstanceObjForSimpleRTTI(aEngine.cx, Self, aEngine.GlobalObject), // we can enumerate this property, it read-only and can not be deleted JSPROP_ENUMERATE or JSPROP_READONLY or JSPROP_PERMANENT ); end; end; function TfrmSM45Demo.DoOnGetEngineName(const aEngine: TSMEngine): RawUTF8; begin if GetCurrentThreadId = MainThreadID then result := 'FormEngine'; end; procedure TfrmSM45Demo.DoOnJSDebuggerInit(const aEngine: TSMEngine); begin // aEngine.EvaluateModule( // {$IFDEF MSWINDOWS} // '..\..\..\..\DebuggerInit.js' // {$ELSE} // '../../../../DebuggerInit.js' // {$ENDIF} // ); end; { TStringsProto } function TStringsTextWrite(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl; var this: PJSObject; proto: TSMCustomProtoObject; Instance: PSMInstanceRecord; begin try this := vp.thisObject[cx]; if IsProtoObject(cx, this, proto) then begin vp.rval := JSVAL_NULL; Result := True; exit; end; if not IsInstanceObject(cx, this, Instance) then raise ESMException.Create('No privat data!'); TStrings(Instance.instance).Text := vp.argv[0].asJSString.ToString(cx); Result := True; except on E: Exception do begin Result := False; JSError(cx, E); end; end; end; function TStringsTextRead(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl; var this: PJSObject; proto: TSMCustomProtoObject; Instance: PSMInstanceRecord; begin try this := vp.thisObject[cx]; if IsProtoObject(cx, this, proto) then begin vp.rval := JSVAL_NULL; Result := True; exit; end; if not IsInstanceObject(cx, this, Instance) then raise ESMException.Create('No privat data!'); vp.rval := SimpleVariantToJSval(cx, TStrings(Instance.instance).Text); Result := True; except on E: Exception do begin Result := False; JSError(cx, E); end; end; end; procedure TStringsProto.InitObject(aParent: PJSRootedObject); var idx: Integer; begin inherited; idx := Length(FJSProps); SetLength(FJSProps, idx + 1); FJSProps[idx].flags := JSPROP_ENUMERATE or JSPROP_PERMANENT or JSPROP_SHARED; FJSProps[idx].Name := 'text'; FJSProps[idx].setter.native.info := nil; FJSProps[idx].setter.native.op := TStringsTextWrite; FJSProps[idx].getter.native.info := nil; FJSProps[idx].getter.native.op := TStringsTextRead; end; end.