logo.png
Synopse mORMot Framework API Reference

SynSM.pas unit

Purpose: Features JavaScript execution using the SpiderMonkey library
- this unit is a part of the freeware Synopse framework, licensed under a MPL/GPL/LGPL tri-license; version 1.18

1.1. Units used in the SynSM unit

Unit NameDescription
SynCommonsCommon functions used by most Synopse projects
SynLogLogging functions used by Synopse projects
SynSMAPISpiderMonkey *.h header port to Delphi
SynTableFilter/database/cache/buffer/security/search/multithread/OS features
SynTestsUnit test functions used by Synopse projects

1.2. SynSM class hierarchy

TSynInvokeableVariantTypeTSMVariantTObjectTSMVariantDataTSMValueTSMObjectTSMEngineManagerTSMEngineESynExceptionESMException
SynSM class hierarchy

1.3. Objects implemented in the SynSM unit

ObjectsDescription
ESMExceptionGeneric parent class of all SpiderMonkey-related Exception types
TSMEngineImplements a ThreadSafe JavaScript engine
TSMEngineManagerMain access point to the SpiderMonkey per-thread scripting engines
TSMEngineMethodEventUsed to store one registered method event
TSMObjectJust a wrapper around JavaScript Object API type, to be used with other values wrappers
TSMValueJust a wrapper around jsval API type, to be used with our object wrappers
TSMVariantA custom variant type used to store a SpiderMonkey object in Delphi code
TSMVariantDataMemory structure used for TSMVariant storage of any JavaScript object as Delphi variant

1.3.1. ESMException

ESMException = class(ESynException)

Generic parent class of all SpiderMonkey-related Exception types


1.3.2. TSMValue

TSMValue = object(TObject)

Just a wrapper around jsval API type, to be used with our object wrappers
- SpiderMonkey jsval type can be directly casted to this type via TSMValue(jsval)
- note that some methods expect an execution context to be supplied as parameter, as soon as it contains a non primitive type (double/integer)


function SetJSON(cx: PJSContext; const aJSON: RawUTF8): boolean;

Set the value from UTF-8 encoded JSON
- returns TRUE if aJSON was valid, FALSE in case of an error


function ToBoolean: boolean;

Read the value as boolean


function ToDateTime(cx: PJSContext): TDateTime;

Return the value as a date/time
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


function ToDouble: double;

Read the value as floating point


function ToInt64: int64;

Read the value as one 64 bit integer
- note that SpiderMonkey is not able to store all Int64 values directly


function ToInteger: integer;

Read the value as one 32 bit integer


function ToJSON(cx: PJSContext): RawUTF8;

Return the value as UTF-8 encoded JSON


function ToNativeFunction(cx: PJSContext): PJSFunction;

Attemps to convert the value into a native function pointer


function ToNativeFunctionName(cx: PJSContext): RawUTF8;

Attemps to convert the value into a native function name


function ToSynUnicode(cx: PJSContext): SynUnicode; overload;

Return the value as an Unicode String
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


function ToUTF8(cx: PJSContext): RawUTF8;

Return the value as an UTF-8 String
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


function ToVariant(cx: PJSContext): Variant; overload;

Return the value as variant (not implemented yet)
- will return any JavaScript string value directly as a RawUTF8
- will return any JavaScript object value as a TDocVariant document
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


function ToWideString(cx: PJSContext): WideString;

Return the value as an Unicode WideString
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


function TransformToSynUnicode(cx: PJSContext): SynUnicode;

Transform a JSValue to its UTF-16 string representation
- JavaScript equivalent is

 variable.toString()

function TransformToUTF8(cx: PJSContext): RawUTF8;

Transform a JSValue to its UTF-8 string representation
- JavaScript equivalent is

 variable.toString()

function ValType(cx: PJSContext): JSType;

Type of the value
- you should better use this before calling other To*() methods


procedure AddJSON(cx: PJSContext; W: TTextWriter);

Add the value as UTF-8 encoded JSON


procedure SetAnsiChar(cx: PJSContext; Text: PAnsiChar; TextLen, CodePage: integer);

Set the value as an Ansi encoded buffer (may be UTF-8 or any code page)
- if CodePage is 0, will use the CurrentAnsiCodePage value
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)
- warning - JSString string is a subject for GC so you must root it or set as property of some object or use SetNativeString() method to pass the value by reference


procedure SetBoolean(const Value: boolean);

Set the value as boolean


procedure SetDateTime(cx: PJSContext; const Value: TDateTime);

Set the value as a date/time
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


procedure SetDouble(const Value: double);

Set the value as floating point


procedure SetInt64(const Value: int64);

Set the value as one 64 bit integer
- this is a somewhat dirty hack, since SpiderMonkey don't support int64: but it is possible to transform int64 to double for ant value < (1 shl 51)
- sometimes we need int64 to be passed do SpiderMonkey (e.g. for an ID)


procedure SetInteger(const Value: integer);

Set the value as one 32 bit integer


procedure SetNativeString(cx: PJSContext; const aStr: SynUnicode);

Set the value as Unicode String by reference
- this is the fastest way to add a string to SpiderMonley: String is in fact not copied to the SpiderMonkey engine, just passed by reference
- Only SynUnicode string support by now (SpiderMonkey is internally UTF-16 based)
- WARNING - as a consequence, aStr must be UNCHANGED until SpiderMonkey engine points to it (SpiderMonkey will also consider its strings as immutable, so will never change its content during execution) - for instance, never pass a function result as aStr, nor use a local SynUnicode variable unless you trigger the Garbage Collection before the end of the local method


procedure SetNull;

Set the value as NULL


procedure SetSynUnicode(cx: PJSContext; const aStr: SynUnicode);

Set the value as an Unicode String
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)
- warning - JSString string is a subject for GC so you must root it or set as property of some object or use SetNativeString() method to pass the value by reference


procedure SetTVarRec(cx: PJSContext; const V: TVarRec);

Set the value as TVarRec (i.e. an "array of const" open parameter)
- here any AnsiString parameter is expected to be a RawUTF8 before Delphi 2009, or its correct code page will be retrieved since Delphi 2009
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


procedure SetUTF8(cx: PJSContext; const aStr: RawUTF8);

Set the value as an UTF-8 String
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)
- warning - JSString string is a subject for GC so you must root it or set as property of some object or use SetNativeString() method to pass the value by reference


procedure SetVariant(cx: PJSContext; const Value: Variant);

Set the value as variant (not implemented yet)
- will set any custom variant type (e.g. TDocVariant) as a JavaScript object value computed from the JSON serialization of the variant
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


procedure SetVoid;

Set the value as VOID


procedure SetWideChar(cx: PJSContext; Text: PWideChar; TextLen: integer);

Set the value as an UTF-16 encoded buffer
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)
- warning - JSString string is a subject for GC so you must root it or set as property of some object or use SetNativeString() method to pass the value by reference


procedure SetWideString(cx: PJSContext; const aStr: WideString);

Set the value as an Unicode WideString
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)
- warning - JSString string is a subject for GC so you must root it or set as property of some object or use SetNativeString() method to pass the value by reference


procedure ToSynUnicode(cx: PJSContext; var result: SynUnicode); overload;

Return the value as an Unicode String
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


procedure ToVariant(cx: PJSContext; var result: Variant); overload;

Return the value as variant (not implemented yet)
- will return any JavaScript string value directly as a RawUTF8
- will return any JavaScript object value as a TDocVariant document
- in SpiderMonkey non-simple type instances do exist in a given JSContext, so we need to know the execution context (using a property is not an option)


property AsBoolean: boolean read ToBoolean write SetBoolean;

Access to the value as boolean


property AsDouble: double read ToDouble write SetDouble;

Access to the value as floating point


property AsInt64: int64 read ToInt64 write SetInt64;

Access to the value as one 64 bit integer


property AsInteger: integer read ToInteger write SetInteger;

Access to the value as integer


property AsJSVal: jsval read FValue write FValue;

Direct access to the internal jsval instance


1.3.3. TSMObject

TSMObject = object(TObject)

Just a wrapper around JavaScript Object API type, to be used with other values wrappers
- SpiderMonkey object type can NOT be directly casted to this type via TSMObject(jsobject) - use JSObject wrapper instead - since we expects an execution context to be specified
- to create instance of this structure, use TSMEngine.NewObject() or MakeObject() overloaded methods


function AsSMValue: TSMValue;

Returns the associated jsobject instance as a jsvalue


function DefineNativeMethod(const methodName: SynUnicode; func: JSNative; nargs: uintN; attrs: TJSPropertyAttrs): PJSFunction; overload;

Add JSNative compatible function into JS object
- here the method name is specified as SynUnicode
- func if reference to function with JSNative signature
- nargs is function argument count
- actually this method creates a JSFunction and assing its value to

 obj[methodName]

- to add a global function, define it into the "global" object - i.e. call

 TSMEngine.GlobalObject.DefineNativeMethod()

- this method will allow to set custom properties attributes of this engine


function DefineNativeMethod(const methodName: SynUnicode; func: JSNative; nargs: uintN): PJSFunction; overload;

Add JSNative compatible function into JS object
- here the method name is specified as SynUnicode
- func if reference to function with JSNative signature
- nargs is function argument count
- actually this method creates a JSFunction and assing its value to

 obj[methodName]

- to add a global function, define it into the "global" object - i.e. call

 TSMEngine.GlobalObject.DefineNativeMethod()

- this method will use the default properties attributes of this engine


function DefineNativeMethod(const methodName: AnsiString; func: JSNative; nargs: uintN; attrs: TJSPropertyAttrs): PJSFunction; overload;

Add JSNative compatible function into JS object
- here the method name is specified as AnsiString
- func if reference to function with JSNative signature
- nargs is function argument count
- this method will allow to set custom properties attributes of this engine


function DefineNativeMethod(const methodName: AnsiString; func: JSNative; nargs: uintN): PJSFunction; overload;

Add JSNative compatible function into JS object
- here the method name is specified as AnsiString
- func if reference to function with JSNative signature
- nargs is function argument count
- this method will use the default properties attributes of this engine


function Engine: TSMEngine;

Returns the associated script engine instance


function GetPrivateData(expectedClass: PJSClass): pointer;

Retrieve the private data associated with an object, if that object is an instance of a specified class
- wrapper to JS_GetInstancePrivate()


function GetPropValue(const propName: SynUnicode): TSMValue;

Get object property value (call geter for native)
- JavaScript equivalent of

 obj[name]

- returns JSVAL_VOID if object does not have such property


function GetPropVariant(const propName: SynUnicode): variant;

Get object property value (call geter for native)
- you can also use the property Properties[]
- JavaScript equivalent of

 obj[name]

- returns null if object does not have such property


function HasOwnProperty(const propName: SynUnicode): Boolean;

Determine whether a property is physically present on a object
- JavaScript equivalent of

 Object.hasOwnProperty(propName)

function HasProperty(const propName: SynUnicode): Boolean;

Check object property does exist (including prototype chain lookup)


function IsArray: boolean;

Return TRUE if the object is an array


function ItemsCount: cardinal;

Return the number of elements in this array


function Parent: TSMObject;

Get the parent object of a given object


function Prototype: TSMObject;

Get the prototype of a given object


function Run(const methodName: AnsiString; const argv: array of variant): variant;

Executes a JavaScript object method using a Delphi array of variants
- returns the function result as a variant
- JavaScript equivalent of

 rval := obj.methodName(argv[0], ....);

procedure Clear;

Set properties obj and cx to nil


procedure DefineProperty(const name: SynUnicode; const value: TSMValue); overload;

Define an object property with a value, specified as jsvalue
- this is not a direct JavaScript equivalent of

 obj[name] = val

since any setter will be called
- to set a property in a global object, call either

 SMEngine.Global.property := ...          // via late-binding
 SMEngine.GlobalObject.DefineProperty()   // direct via TSMObject

equivalent in JavaScript to:

 var name = value

outside a JavaScript function context (i.e. in global scope)
- if property already exists, it will just replace its value with the supplied value
- this method will use the default properties attributes of this engine


procedure DefineProperty(const name: SynUnicode; const value: variant); overload;

Define an object property with a value, specified as variant
- you can also use the property Properties[]
- this is not a direct JavaScript equivalent of

 obj[name] = val

since any setter will be called
- to set a property in a global object, call either

 SMEngine.Global.property := ...          // via late-binding
 SMEngine.GlobalObject.DefineProperty()   // direct via TSMObject

equivalent in JavaScript to:

 var name = value

outside a JavaScript function context (i.e. in global scope)
- if property already exists, it will just replace its value with the supplied value
- this method will use the default properties attributes of this engine


procedure DefineProperty(const name: SynUnicode; const value: variant; attrs: TJSPropertyAttrs); overload;

Define an object property with a value, specified as variant
- you can also use the property Properties[]
- this is not a direct JavaScript equivalent of

 obj[name] = val

since any setter will be called
- to set a property in a global object, call either

 SMEngine.Global.property := ...          // via late-binding
 SMEngine.GlobalObject.DefineProperty()   // direct via TSMObject

equivalent in JavaScript to:

 var name = value

outside a JavaScript function context (i.e. in global scope)
- if property already exists, it will just replace its value with the supplied value
- this method will allow to set custom properties attributes of this engine


procedure DefineProperty(const name: SynUnicode; const value: TSMValue; attrs: TJSPropertyAttrs); overload;

Define an object property with a value, specified as jsvalue
- this is not a direct JavaScript equivalent of

 obj[name] = val

since any setter will be called
- to set a property in a global object, call either

 SMEngine.Global.property := ...          // via late-binding
 SMEngine.GlobalObject.DefineProperty()   // direct via TSMObject

equivalent in JavaScript to:

 var name = value

outside a JavaScript function context (i.e. in global scope)
- if property already exists, it will just replace its value with the supplied value
- this method will allow to set custom properties attributes of this engine


procedure DeleteItem(aIndex: integer);

Delete an item of this object as array


procedure Evaluate(const script: SynUnicode; const scriptName: RawUTF8; lineNo: Cardinal; out result: TSMValue);

Evaluate JavaScript script in the current object scope
- if exception raised in script - raise Delphi ESMException
- on success, returns the last executed expression statement processed in the script in low-level result output variable
- JavaScript Equivalent of

 with(obj) eval(script)

- be careful about execution scope - see JS_ExecuteScript() description
- usualy you need to evaluate script only in global object scope, so you should better always call TSMEngine.Evaluate()


procedure Root;

Protect the object from Garbage Collection
- if this object is not set as property value of any other object or passed as parameter to function, you must protect it


procedure RunMethod(const methodName: AnsiString; const argv: SMValArray; out rval: TSMValue); overload;

Executes a JavaScript object method using low-level SMVal arguments
- returns the function result as a TSMValue
- JavaScript equivalent of

 rval := obj.methodName(argv[0], ....);

procedure RunMethod(const methodName: AnsiString; const argv: array of const; out rval: TSMValue); overload;

Executes a JavaScript object method using a Delphi array of const
- returns the function result as a TSMValue
- JavaScript equivalent of

 rval := obj.methodName(argv[0], ....);

- here any AnsiString parameter is expected to be a RawUTF8 before Delphi 2009, or its correct code page will be retrieved since Delphi 2009


procedure UnRoot;

Unprotect a previously "rooted" object
- WARNING!! Object MUST be protected by a previous Root method call, otherwise you get an access violation


property cx: PJSContext read fCx;

Returns the associated execution context


property DefaultPropertyAttrs: TJSPropertyAttrs read FDefaultPropertyAttrs write SetDefaultPropertyAttrs;

Access to the default attributes when accessing any properties


property Items[aIndex: integer]: variant read GetItem write SetItem;

Access to an item of this object as array


property obj: PJSObject read fObj;

Returns the associated jsobject instance


property PrivateData: pointer read GetPrivate write SetPrivate;

Access the private data field of an object
- wrapper to JS_GetPrivate()/JS_SetPrivate()
- only works if the object's JSClass has the JSCLASS_HAS_PRIVATE flag: it is safer to use GetPrivateData() method and providing the JSClass


property Properties[const propName: SynUnicode]: variant read GetPropVariant write SetPropVariant;

Read/write access to the object properties as variant


1.3.4. TSMEngineMethodEvent

TSMEngineMethodEvent = record

Used to store one registered method event


1.3.5. TSMEngine

TSMEngine = class(TObject)

Implements a ThreadSafe JavaScript engine
- use TSMEngineManager.ThreadSafeEngine to retrieve the Engine instance corresponding to the current thread, in multithread application
- contains JSRuntime + JSContext (to be ready for new SpiderMonkey version where context and runtime is the same)
- contains also one "global" JavaScript object. From script it is accessible via "global." (in browser, this is the "window." object)
- set SpiderMonkey error reporter and store last SpiderMonkey error in LastError property


constructor Create(aManager: TSMEngineManager); virtual;

Create one threadsafe JavaScript Engine instance
- initialize internal JSRuntime, JSContext, and global objects and standard JavaScript classes
- do not create Engine directly via this constructor, but instead call TSMEngineManager.ThreadSafeEngine


destructor Destroy; override;

Finalize the JavaScript engine instance


function Evaluate(const script: SynUnicode; const scriptName: RawUTF8='script'; lineNo: Cardinal=1): variant;

Evaluate a JavaScript script in the global scope
- a wrapper to GlobalObject.Evaluate(...)
- if exception raised in script - raise Delphi ESMException
- on success returns last executed expression statement processed in the script as a variant
- JavaScript equivalent to

 eval(script)

function NewSMVariant: variant;

Create new ordinary JavaScript object, stored as TSMVariant custom type
- JavaScript equivalent of

 {}

- new object is subject to Garbage Collection, so should be assigned as value for a property to create new object type property, as in JavaScript:

 var obj = {}

function RegisterMethod(obj: PJSObject; const MethodName: SynUnicode; const Event: TSMEngineMethodEventJSON; ArgumentsCount: integer): PJSFunction; overload;

Register a native Delphi JSON-based method for a given object
- the supplied function name is case-sensitive
- the supplied callback will be executed directly by the JavaScript engine, supplying all parameters as JSON array, and returning the function result either as a JSON value or a JSON object
- raise an ESMException if the function could not be registered


function RegisterMethod(obj: PJSObject; const MethodName: SynUnicode; const Event: TSMEngineMethodEventVariant; ArgumentsCount: integer): PJSFunction; overload;

Register a native Delphi variant-based method for a given object
- the supplied function name is case-sensitive
- the supplied callback will be executed directly by the JavaScript engine, supplying all parameters as variant (including TDocVariant for any complex object), and returning the function result as variant
- raise an ESMException if the function could not be registered


procedure CheckJSError(res: JSBool); virtual;

Check if last call to JSAPI compile/eval fucntion was successful
- raise ESMException if any error occurred
- put error description to SynSMLog


procedure ClearLastError;

Clear last JavaScript error
- called before every evaluate() function call


procedure GarbageCollect;

Trigger Garbage Collection
- all unrooted things (JSString, JSObject, VSVal) will be released


procedure InitClass(clasp: PJSClass; ps: PJSPropertySpec; var newobj: TSMObject);

Create new JavaScript object from its class and property specifications


procedure MakeObject(const value: TSMValue; out obj: TSMObject); overload;

Converts a JavaScript value into a JavaScript object


procedure MakeObject(const value: jsval; out obj: TSMObject); overload;

Converts a JavaScript low-level value into a JavaScript object


procedure MakeObject(jsobj: PJSObject; out obj: TSMObject); overload;

Converts a JavaScript low-level object into a JavaScript object


procedure MaybeGarbageCollect;

Offer the JavaScript engine an opportunity to perform garbage collection if needed
- Tries to determine whether garbage collection in would free up enough memory to be worth the amount of time it would take. If so, it performs some garbage collection
- Frequent calls are safe and will not cause the application to spend a lot of time doing redundant garbage collection work


procedure NewObject(const prototype: TSMObject; out newobj: TSMObject); overload;

Create new JavaScript object with prototype
- JavaScript equivalent of

  {}.__proto__ := prototype;

procedure NewObject(out newobj: TSMObject); overload;

Create new ordinary JavaScript object
- JavaScript equivalent of

 {}

- new object is subject to Garbage Collection, so must be rooted or assigned as value for a property to create new object type property, as in JavaScript:

 var obj = {}

procedure NewObjectWithClass(clasp: PJSClass; const prototype: TSMObject; const parent: TSMObject; var newobj: TSMObject); overload;

Create new JavaScript object from its prototype


procedure NewObjectWithClass(clasp: PJSClass; var newobj: TSMObject); overload;

Create new JavaScript object from its class


procedure NewSMVariantRooted(out newobj: variant);

Create new ordinary JavaScript object, stored as TSMVariant custom type, and rooted to avoid garbage collection
- JavaScript equivalent of

 {}

- new object is subject to Garbage Collection, so is rooted and should be explicitly unrooted, e.g. via:

 obj: variant;
 ...
 FManager.ThreadSafeEngine.NewSMVariantRooted(obj);
 try
   ... work with obj
 finally
   obj._UnRoot; // pseudo-method
 end;

procedure UnRegisterMethod(JSFunction: PJSFunction);

Unregister a native Delphi method for a given object
- raise an ESMException if the function was not previously registered
- you should not call it usually, but it is available in case


property comp: PJSCompartment read fcomp;

Access to the associated execution compartment


property cx: PJSContext read fCx;

Access to the associated execution context


property DefaultPropertyAttrs: TJSPropertyAttrs read FDefaultPropertyAttrs write SetDefaultPropertyAttrs;

Access to the default attributes when accessing any properties


property EngineContentVersion: Cardinal read FEngineContentVersion;

Internal version number of engine scripts
- used in TSMEngine.ThreadSafeEngine to determine if context is up to date, in order to trigger on-the-fly reload of scripts without the need if restarting the application
- caller must change this parameter value e.g. in case of changes in the scripts folder in an HTTP server


property ErrorExist: boolean read FErrorExist;

TRUE if an error was triggered during JavaScript execution


property Global: variant read FGlobal;

Access to the associated global object as a TSMVariant custom variant
- allows direct property and method executions in Delphi code, via late-binding, for instance:

 engine.Global.MyVariable := 1.0594631;
 engine.Global.MyFunction(1,'text');

property GlobalObj: PJSObject read FGlobalObject.fobj;

Access to the associated global object as low-level PJSObject


property GlobalObject: TSMObject read FGlobalObject;

Access to the associated global object as a TSMObject wrapper
- you can use it to register a method


property LastErrorFileName: RawUTF8 read FLastErrorFileName;

Last error file name triggered during JavaScript execution


property LastErrorLine: integer read FLastErrorLine;

Last error source code line number triggered during JavaScript execution


property LastErrorMsg: RawUTF8 read FLastErrorMsg;

Last error message triggered during JavaScript execution


property rt: PJSRuntime read frt;

Access to the associated execution runtime


property TimeOutAborted: boolean read FTimeOutAborted;

Notifies a WatchDog timeout


property TimeOutValue: Double read fTimeoutInterval write SetTimeoutValue;

Define a WatchDog timeout interval
- is set to -1 by default, i.e. meaning no execution timeout


1.3.6. TSMEngineManager

TSMEngineManager = class(TObject)

Main access point to the SpiderMonkey per-thread scripting engines
- allow thread-safe access to an internal per-thread TSMEngine instance list
- contains runtime-level properties shared between thread-safe engines
- you can create several TSMEngineManager instances, if you need several separate scripting instances
- set OnNewEngine callback to initialize each TSMEngine, when a new thread is accessed, and tune per-engine memory allocation via MaxPerEngineMemory and MaxRecursionDepth
- get the current per-thread TSMEngine instance via ThreadSafeEngine method


constructor Create; virtual;

Initialize the SpiderMonkey scripting engine


destructor Destroy; override;

Finalize the SpiderMonkey scripting engine


function ThreadSafeEngine: TSMEngine;

Get or create one Engine associated with current running thread
- in single thread application will return the MainEngine


procedure ReleaseCurrentThreadEngine;

Method to be called when a thread is about to be finished
- you can call this method just before a thread is finished to ensure that the associated scripting Engine will be released
- could be used e.g. in a try...finally block inside a TThread.Execute overriden method


property ContentVersion: Cardinal read FContentVersion write FContentVersion;

Internal version of the script files
- used in TSMEngine.ThreadSafeEngine to determine if context is up to date, in order to trigger on-the-fly reload of scripts without the need if restarting the application


property Lock: TRTLCriticalSection read FEngineCS;

Lock/mutex used for thread-safe access to the TSMEngine list


property MaxPerEngineMemory: Cardinal read FMaxPerEngineMemory write SetMaxPerEngineMemory default 8*1024*1024;

Max amount of memory (in bytes) for a single SpiderMonkey instance
- this parameter will be set only at Engine start, i.e. it must be set BEFORE any call to ThreadSafeEngine
- default is 8 MB


property MaxRecursionDepth: Cardinal read FMaxRecursionDepth write FMaxRecursionDepth default 32;

Maximum expected recursion depth for JavaScript functions
- to avoid out of memory situation in functions like

 function f(){ f() };

- default is 32, but you can specify some higher value


property OnNewEngine: TEngineEvent read FOnNewEngine write FOnNewEngine;

Event triggered every time a new Engine is created
- here your code can change the initial state of the Engine


1.3.7. TSMVariant

TSMVariant = class(TSynInvokeableVariantType)

A custom variant type used to store a SpiderMonkey object in Delphi code
- via the magic of late binding, it will allow access of any JavaScript object property, or execute any of its methods
- primitive types (i.e. null, string, or numbers) will be stored as simple variant instances, but JavaScript objects (i.e. objects, prototypes or functions) can be stored as an instance of this TSMVariant custom type
- you can use the _Root and _UnRoot pseudo-methods, which will protect the object instance to avoid unexpected Garbage Collection


function DoFunction(var Dest: TVarData; const V: TVarData; const Name: string; const Arguments: TVarDataArray): Boolean; override;

Low-level callback to execute any JavaScript object method
- add the _(Index: integer): variant method to retrieve an item if the object is an array


procedure Cast(var Dest: TVarData; const Source: TVarData); override;

Handle type conversion
- any TSMVariant will be converted to '<<JavaScript TSMVariant>>' text


procedure CastTo(var Dest: TVarData; const Source: TVarData; const AVarType: TVarType); override;

Handle type conversion
- any TSMVariant will be converted to '<<JavaScript TSMVariant>>' text


class procedure New(const aObject: TSMObject; out aValue: variant); overload;

Initialize a variant instance to store a JavaScript object


class procedure New(cx: PJSContext; obj: PJSObject; out aValue: variant); overload;

Initialize a variant instance to store a JavaScript object


class procedure New(engine: TSMEngine; out aValue: variant); overload;

Initialize a variant instance to store a new JavaScript object


procedure ToJSON(W: TTextWriter; const Value: variant; Escape: TTextWriterKind); override;

This implementation will let SpiderMonkey write directly the JSON content


1.3.8. TSMVariantData

TSMVariantData = object(TObject)

Memory structure used for TSMVariant storage of any JavaScript object as Delphi variant
- primitive types (i.e. null, string, or numbers) will be stored as simple variant instances, but JavaScript objects (i.e. objects, prototypes or functions) can be stored as an instance of this TSMVariant custom type
- this variant stores its execution context, so is pretty convenient to work with in plain Delphi code, also thanks to late-binding feature


procedure GetGlobal(out global: variant);

Retrieve the global object of this execution context
- you can use this from a native function, e.g.:

function TMyClass.MyFunction(const This: variant; const Args: array of variant): variant;
var global: variant;
begin
  TSMVariantData(This).GetGlobal(global);
  global.anotherFunction(Args[0],Args[1],'test');
  // same as:
  global := TSMVariantData(This).SMObject.Engine.Global;
  global.anotherFunction(Args[0],Args[1],'test');
  // but you may also write directly:
  with TSMVariantData(This).SMObject.Engine do
    Global.anotherFunction(Args[0],Args[1],'test');
  result := AnyTextFileToSynUnicode(Args[0]);
end;

procedure Init(aCx: PJSContext; aObj: PJSObject); overload;

Initialize a TSMVariant structure to store a specified JavaScript object


procedure Init(const aObject: TSMObject); overload;

Initialize a TSMVariant structure to store a specified JavaScript object


procedure InitNew(engine: TSMEngine);

Initialize a TSMVariant structure to store a new JavaScript object


property cx: PJSContext read VObject.fcx;

Returns the associated execution context


property obj: PJSObject read VObject.fobj;

Returns the associated jsobject instance


property SMObject: TSMObject read VObject;

Returns the associated TSMObject instance


property VarType: word read VType;

Return the custom variant type identifier, i.e. SMVariantType.VarType


1.4. Types implemented in the SynSM unit

1.4.1. PSMObject

PSMObject = ^TSMObject;

Pointer to our wrapper around JavaScript Object


1.4.2. PSMValue

PSMValue = ^TSMValue;

A pointer to a jsval wrapper


1.4.3. PSMValues

PSMValues = ^TSMValues;

A pointer to a jsval wrappers array


1.4.4. PSMVariantData

PSMVariantData = ^TSMVariantData;

Pointer to a TSMVariant storage


1.4.5. SMValArray

SMValArray = array of TSMValue;

A dynamic array of jsval wrappers


1.4.6. TEngineEvent

TEngineEvent = procedure(const Engine: TSMEngine) of object;

Prototype of SpideMonkey notification callback method


1.4.7. TSMEngineMethodEventDynArray

TSMEngineMethodEventDynArray = array of TSMEngineMethodEvent;

Used to store the registered method events


1.4.8. TSMEngineMethodEventJSON

TSMEngineMethodEventJSON = function(const This: TSMObject; const Args: RawUTF8): RawUTF8 of object;

/ JSON-based callback signature used for TSMEngine.RegisterMethod()
- any Delphi exception raised during this execution will be converted into a JavaScript exception by TSMEngine
- similar to TServiceMethod.InternalExecute() as defined in mORMot (for instance, this callback will be used to execute native Delphi interface-based methods from JavaScript code in mORMotSM.pas unit)
- "this" JavaScript calling object is transmitted as low-level TSMObject
- will expect as input a JSON array of parameters from Args, e.g.

 '[1,2,3]'

- if the method only expect one result, shall return one JSON value, e.g.

 '6'

- if the method expect more than one result (i.e. several var/out parameters in addition to the main function result), it shall return a JSON object, with parameter names for all var/out/result values, e.g.

 '{"first":1,"second":2,"result":3}'

- this allows the function result to be consumed by the JavaScript as a regular JS value or object
- corresponds to meJSON kind of callback method


1.4.9. TSMEngineMethodEventKind

TSMEngineMethodEventKind = ( meVariant, meJSON );

Kinds of callback methods available for TSMEngine.RegisterMethod()


1.4.10. TSMEngineMethodEventVariant

TSMEngineMethodEventVariant = function(const This: variant; const Args: array of variant): variant of object;

/ variant-based callback signature used for TSMEngine.RegisterMethod()
- any Delphi exception raised during this execution will be converted into a JavaScript exception by TSMEngine
- "this" JavaScript calling object is transmitted as a TSMVariant custom variant: you can use late-binding over it to access its methods or properties, or transtype it using TSMVariantData(Instance) and access its low-level API content
- input arguments (and function result) are simple variant values, or TDocVariant custom variant instance for any object as complex document
- corresponds to meVariant kind of callback method


1.4.11. TSMValues

TSMValues = array[0..(MaxInt div sizeof(TSMValue))-1] of TSMValue;

A jsval wrappers array


1.5. Constants implemented in the SynSM unit

1.5.1. STACK_CHUNK_SIZE

STACK_CHUNK_SIZE: cardinal = 8192;

Default stack growing size, in bytes


1.6. Functions or procedures implemented in the SynSM unit

Functions or proceduresDescription
JSErrorTo be used to catch Delphi exceptions inside JSNative function implementation
VariantToJSValConvert a variant to a Java Script value

1.6.1. JSError

procedure JSError(cx: PJSContext; aException: Exception; const aContext: RawByteString='');

To be used to catch Delphi exceptions inside JSNative function implementation
- usage example:

 try
   doSomething()
   Result := JS_TRUE;
 except
 on E: Exception do begin
   JS_SET_RVAL(cx, vp, JSVAL_VOID);
   JSError(cx, E);
   Result := JS_FALSE;
 end;

1.6.2. VariantToJSVal

function VariantToJSVal(cx: PJSContext; const Value: Variant): jsval;

Convert a variant to a Java Script value


1.7. Variables implemented in the SynSM unit

1.7.1. SMVariantType

SMVariantType: TSynInvokeableVariantType = nil;

The internal custom variant type used to register TSMVariant


1.7.2. SynSMLog

SynSMLog: TSynLogClass=TSynLog;

Define the TSynLog class used for logging for all our SynSM related units
- you may override it with TSQLLog, if available from mORMot
- since not all exceptions are handled specificaly by this unit, you may better use a common TSynLog class for the whole application or module