You are not logged in.
About rooting in SpiderMonkey:
Everything (strings, objects, integers, result of CreateJSInstanceObjForSimpleRTTI call, etc.) inside SpiderMonkey represented as JSValue - a 64 bit structure. SpiderMonkey engine create a reference counter for each structure, and when reference counter === 0 will free a JSValue (during garbage collection circle) - this is the same as for Strings or dynamic array in Delphi, but in Delphi string will be destroyed just after reference counted became zero, in JavaScript - when garbage collection circle is executed.
jsval := CreateJSInstanceObjForSimpleRTTI(....); // 0) create a new JSValue inside Engine
try
rootObj := cx.NewRooted*(jsval); // 1) When we want to use a JSValue from outside the SpiderMonkey Engine (from Delphi code) we must say Engine to increase a reference counter
// do something with JSValue
vp.rval := rootObj.ptr.ToJSValue; //see notes* below about ptr
finally
cx.FreeRooted*(rootObj); // 2) When we do not need a reference to JSValue from Delphy, we must say Engine to decrease a reference counter by
cx.FreeRooted is not actually a Free operation - just a dec to the internal reference counter.
Notes* about ptr
Between events 1) and 2) SpiderMonkey can in any moment run a internal memory optimization procedure and move the actual in-memory position of the JSValue to another place. This is why we must not memorize a direct pointer to a JSValue (in our case jsval created on the step 0), but use a rootObj.ptr.
Notes about root/unroot order
Important thing about rooting/unrooting order - inside the SM45 rooting is a stack, so we must unroot things in order opposite we root it. In C++ this solved by automatically variable scoping. In Delphi even if we use a Variants as AB did in SM24 there is no guaranty of variant uninit order, so we must manually and carefully root/unroot.
Last edited by mpv (2016-10-31 14:54:23)
Offline
Wow! If all the node.js built-in modules are implemented, it means that we will be able to **make use** of the vast amount of node modules available on the Internet, right?
Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.
Offline
Wow! That's really a great achievement!
Is the list of that 119 node modules public somewhere?
Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.
Offline
AFAIR the Delphi Web Script JavaScript back-end has been released again under Open Source.
We may include it to compile directly from high-level pascal to JavaScript, then execute it in SyNode... and its full JIT performance... may be a perspective of some interest...
Offline
About rooting in SpiderMonkey:
Everything (strings, objects, integers, result of CreateJSInstanceObjForSimpleRTTI call, etc.) inside SpiderMonkey represented as JSValue - a 64 bit structure. SpiderMonkey engine create a reference counter for each structure, and when reference counter === 0 will free a JSValue (during garbage collection circle) - this is the same as for Strings or dynamic array in Delphi, but in Delphi string will be destroyed just after reference counted became zero, in JavaScript - when garbage collection circle is executed.
jsval := CreateJSInstanceObjForSimpleRTTI(....); // 0) create a new JSValue inside Engine try rootObj := cx.NewRooted*(jsval); // 1) When we want to use a JSValue from outside the SpiderMonkey Engine (from Delphi code) we must say Engine to increase a reference counter // do something with JSValue vp.rval := rootObj.ptr.ToJSValue; //see notes* below about ptr finally cx.FreeRooted*(rootObj); // 2) When we do not need a reference to JSValue from Delphy, we must say Engine to decrease a reference counter by
cx.FreeRooted is not actually a Free operation - just a dec to the internal reference counter.
Notes* about ptr
Between events 1) and 2) SpiderMonkey can in any moment run a internal memory optimization procedure and move the actual in-memory position of the JSValue to another place. This is why we must not memorize a direct pointer to a JSValue (in our case jsval created on the step 0), but use a rootObj.ptr.Notes about root/unroot order
Important thing about rooting/unrooting order - inside the SM45 rooting is a stack, so we must unroot things in order opposite we root it. In C++ this solved by automatically variable scoping. In Delphi even if we use a Variants as AB did in SM24 there is no guaranty of variant uninit order, so we must manually and carefully root/unroot.
I appreciate your efforts and helpful comments very much !
AFAIR the Delphi Web Script JavaScript back-end has been released again under Open Source.
We may include it to compile directly from high-level pascal to JavaScript, then execute it in SyNode... and its full JIT performance... may be a perspective of some interest...
Sounds really interesting ! Looking forward to the proto type
Offline
Dear mpv, I am trying to access the "mainForm" exported by the host SpiderMonkey45Binding.exe from the plugin mathModule.dll. The reason behind my attempt is that if this is indeed possible, this looks like a good way of manipulating "central business service" exported by the host .exe from plugin .dll. Complex additional manipulation referencing the "central business service" could be compiled into plugin .dll without changing the host .exe.
Modified files are uploaded onto gist : uMathModule.pas belongs to "01 - Dll Modules\math-module\src\" and the rest six files belong to "02 - Bindings".
However, I have met several problems in trying with the above files:
(1) The plugin methods have to reference the business class. That is to say, ufrmSM45Demo has to be used in uMathModule.pas. But then the type incompatibility issue arises. For example, in function try_work_tform1_1, InstRec.instance is TfrmSM45Demo fails. I wonder whether it is safe or dangerous to hard cast as done in the code ?
(2) Further attempts try to access the ability of the mainForm , which is obtained by hard-cast, to evaluate JavaScript. In other words:
(a) host evaluates JavaScript string, where host exports mainForm
(b) in the JavaScript string, host calls plugin JS method with the mainForm exported
(c) in the plugin native method, the mainForm obtained by hard-cast is asked to evaluate JavaScript
However, the attempts all fail: function try_work_tform1_2 does not completes 100% correct, and function try_work_tform2_2 gives access violation; function try_work_tform1_3 and try_work_tform2_3 both give "A component named Form? already exists."
(3) The above problems are the same no matter FastMM4 or ShareMem is used for host or plugin or both.
Could you help to check the code and comment the reason ?
Furthermore, if I am taking the wrong way, could you help to comment the correct solution to access the "central business service" of host .exe within the plugin dll ?
Last edited by ComingNine (2016-11-05 12:46:12)
Offline
Dear mpv, I am trying to access the "mainForm" exported by the host SpiderMonkey45Binding.exe from the plugin mathModule.dll. The reason behind my attempt is that if this is indeed possible, this looks like a good way of manipulating "central business service" exported by the host .exe from plugin .dll. Complex additional manipulation referencing the "central business service" could be compiled into plugin .dll without changing the host .exe.......
http://www.delphigroups.info/2/f9/304620.html
http://www.delphigroups.info/2/41/523159.html
http://www.delphigroups.info/2/aa/523141.html
According to the links above, the prerequisite is that (1) everything which needs to be referred to in both .exe and .dll should be put into a package, and (2) both .exe and .dll should be built with Runtime-package enabled.
Furethermore, according to the FastMM FAQ, these FullDebugMode, UseRuntimePackages, ShareMM, ShareMMIfLibrary, AttemptToUseSharedMM defines are enabled. Also, according to one post on the FastMM forum, FastMM is put into its own package and referred to in all other packages, .exe, and .dll projects.
However, the problem now is that when testing in Win7 x64 VM (1 core, 2GB mem), memory leaks are reported when main form is shut down, which should be related to the unload order of runtime packages.
Note that there are no memory leaks reported in Win10 x61 OS (4 core, 32GB mem).
The modified files are upload as mORMot.20161108151029.zip, and the steps to reproduce the memory leaks are below:
(*
In Win7 x64 VM (1 core, 2GB mem),
use D:\mORMot\SyNode\Samples\02 - Bindings\SpiderMonkey45Binding_D24.bat to run the main form,
paste the javascript below,
run any of them,
close main form directly without closing Form1 or Form2,
there is memory leak:
const mathModule = require('../../Samples/01 - Dll Modules/math-module');
mathModule.work_tform1_1(mainForm)
mathModule.work_tform1_2(mainForm)
mathModule.work_tform1_3(mainForm)
mathModule.work_tform2_1(mainForm)
mathModule.work_tform2_2(mainForm)
mathModule.work_tform2_3(mainForm)
--------------------------------2016/11/8 15:04:04--------------------------------
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):
13 - 20 bytes: AnsiString x 2
21 - 36 bytes: AnsiString x 1, Unknown x 2
37 - 52 bytes: TSMDebugger x 1
69 - 84 bytes: TCrtSocket x 1
85 - 100 bytes: TSMRemoteDebuggerThread x 1
149 - 164 bytes: TObjectListLocked x 2
245 - 276 bytes: TRawUTF8ListHashedLocked x 2
*)
ShareMem in the uses clause and replacement BorlndMM.dll in the path does not help.
FastMM's NeverUninstall combined with disabling FastMM's reporting memory leaks completely makes debugging memory leaks much more difficult.
Could you help to comment whether it is possible to prevent the memory leaks when using runtime packages, by making FastMM to unload last or in some other ways ?
Offline
Hi,
In SyNode defineClass can't register a Class with more than 255 methods?
How can I register a Class with more than 255 methods?
The Code In SyNode.pas
/// Define a Delphi class as JS class (prototype), so it can be created via new:
// engine.defineClass(TmyObject, TSMNewRTTIProtoObject);
// and in JS
// var myObj = new TmyObject();
// - Class will be exposed in the aParent javaScript object, if aParent is omitted - in the global object
// - AProto parameter can be a TSMCustomProtoObject descendant (TSMSimpleRTTIProtoObject, TSMNewRTTIProtoObject)
// WARNING - possible prototypes count is limited to 255 - JSCLASS_GLOBAL_SLOT_COUNT = 76 for SM45,
// so it is better to use a bindings, add where a native functions and wrap it in class into the JavaScript
// see `fs.js` for sample.
function defineClass(AForClass: TClass; AProto: TSMCustomProtoObjectClass; aParent: PJSRootedObject = nil): TSMCustomProtoObject;
Offline
Current implementation do not support Delphi classes with mote when 255 public members. But for your task (COM object) we have a better solution - see this topic
Offline
@mpv:
Thanks for your help!
I download some lib from http://registry.unitybase.info
@unitybase/stubs and @unitybase/base etc..
but they can't work well with SyNode, is there any thing I need change?
example: when I use http.get("http://www.xxx.com"); it will show _http.writeEnd is not a function,
I have defineClasses THTTPClient but the THTTPClient not have the "writeEnd" method.
Offline
Can we use SpiderNode? https://github.com/mozilla/spidernode
Offline
Packages from http://registry.unitybase.info registry is not for SyNode, but for our project UnityBase which is based on SyNode. So don't try to use it from SyNode..
But about http - this is a issue, for sure. http must be a part of synode, but we miss a THTTPClient binding. I'll extract it from our source tree and move to SyNode asap
About SpiderNode - this is a port of nodejs from V8 to SpiderMonkey. I don't think it is possible to built-in it into Delphi app.
Last edited by mpv (2017-02-02 14:05:25)
Offline
Ok, Great!
Thank you!
Offline
AFAIR the Delphi Web Script JavaScript back-end has been released again under Open Source.
Do you have a reference for that information?
Offline
See https://bitbucket.org/egrange/dwscript/ … ?at=master
The commit is https://bitbucket.org/egrange/dwscript/ … 6ad747811a
Names "JSCodeGen now under GPL v3".
From the 2016-08-05
Offline
Hi mpv,
It seems that the script cannot be run more than once in "SyNode\Samples\02 - Bindings ", the error is "redeclaration of const fs.",
I guess the question is how to clear the script before next calling to FEngine.Evaluate?
Offline
I also want know how to clear the moudles loaded, I just used it Free every times and Create a New TSMEngine...
Offline
There is no way to "clear" the module once it is loaded (like there is no way to "un-uses" unit in Delphi ). And normally you never need such.
Sample 02 execute only selected lines, so to read file twice (for example) in first time select end execute
const fs = require('fs');
const path = require('path');
let content
content = fs.readFileSync(path.join(process.cwd(), 'ExtendDebuggerConsole.js'));
mainForm.toLog(content);
And after this exec as many time as you need
content = fs.readFileSync(path.join(process.cwd(), 'ExtendDebuggerConsole.js'));
mainForm.toLog(content);
If you need to execute something with side effects several time you need to put it into block (in ES6 block create a variable scope):
{
const fs = require('fs');
const path = require('path');
let content = fs.readFileSync(path.join(process.cwd(), 'ExtendDebuggerConsole.js'));
mainForm.toLog(content);
}
Or use a ES5 pattern - selt-executed function
(function(){
const fs = require('fs');
const path = require('path');
let content = fs.readFileSync(path.join(process.cwd(), 'ExtendDebuggerConsole.js'));
mainForm.toLog(content);
})()
In any case module required only once
Offline
I also want know how to clear the moudles loaded, I just used it Free every times and Create a New TSMEngine...
How did you do it?
Initially, I used following code and it threw AVs:
// evaluate a text from mSource memo
FEngine := FSMManager.ThreadSafeEngine(nil);
try
if mSource.SelText <> '' then
FEngine.Evaluate(mSource.SelText, 'mSourceSelected.js', 1, res)
else
FEngine.Evaluate(mSource.lines.Text, 'mSource.js', 1, res);
finally
FreeAndNil(FEngine);
end;
Then I change the way it freed and it seemed OK now:
// evaluate a text from mSource memo
FEngine := FSMManager.ThreadSafeEngine(nil);
try
if mSource.SelText <> '' then
FEngine.Evaluate(mSource.SelText, 'mSourceSelected.js', 1, res)
else
FEngine.Evaluate(mSource.lines.Text, 'mSource.js', 1, res);
finally
//FreeAndNil(FEngine);
FSMManager.ReleaseCurrentThreadEngine();
end;
Offline
There is no way to "clear" the module once it is loaded (like there is no way to "un-uses" unit in Delphi ). And normally you never need such.
Sample 02 execute only selected lines, so to read file twice (for example) in first time select end execute
...
In any case module required only once
The solution you provided is OK in this sample (i.e., interactive), what if the engine is pooled and its script is loaded from script files which are written by the end users (for example, an integration engine runs customized scripts for data processing)? How the end users should program the scripts to avoid "redefine" issue we are talking here?
Offline
@Bo In this case end users should program his scripts as a module - see how modules work
User script myCalculation.js:
const fs = require('fs')
...
function onInit(){ ....}
function onDone(){..}
function doCalculation(){...}
module.exports = doCalculation
// another way is to export a several methods
// module.exports.onInit = onInit; module.exports.onDone = onDone; module.exports.doCalculation = doCalculation;
Your code
FEngine.Evaluate('require("myCalculation")()', 'eval.js', 1, res)
Modules (user code in your case) are wrapped in closure by require function - you can see this in debugger
Offline
@Bo In this case end users should program his scripts as a module - see how modules work
User script myCalculation.js:const fs = require('fs') ... function onInit(){ ....} function onDone(){..} function doCalculation(){...} module.exports = doCalculation // another way is to export a several methods // module.exports.onInit = onInit; module.exports.onDone = onDone; module.exports.doCalculation = doCalculation;
Your code
FEngine.Evaluate('require("myCalculation")()', 'eval.js', 1, res)
Modules (user code in your case) are wrapped in closure by require function - you can see this in debugger
This will be the equivalent of specifying an entry function in using MSScript.ocx as the script engine wrapper.
Offline
Hi mpv,
Re: SyNode Sample 02, I have found that if the code was referring to a non-existed property of the main form (wrong spelling or capitalized etc.), it raised an generic AV which is not helpful at all, for example, this line of code "mainForm.toLog(mainForm.Caption);" will raise an AV in which does not tell you the line no, what type of error etc., but "mainForm.ToLog(mainForm.caption);" does. What can we do to improve catching/avoid this type of error?
Offline
Actual issue is inside mainForm.toLog function. Fixed by [70cc3773fb]
Offline
Actual issue is inside mainForm.toLog function. Fixed by [70cc3773fb]
This fix does stop the AV, it would be nice if it can actual tell the script author that the variable passed in is undefined.
Also, I have noticed that there is another set of TSMEngineManager and TSMEngine etc in units SynSM, and they use Spider Monkey v24 instead of v45 in SyNode,
what are the main differences? What are the different using scenarios?
Offline
Wow, node.js inside delphi!
Great work!
Is there a way to use VSCODE as development tool with their debugger?
I assume that map files (i have typescript sources) will not work?
How fast SyNode in comparison with NodeJS?
Last edited by George (2017-02-19 18:25:03)
Offline
Not sure about VSCODE, but you can use Firefox remote debugger. AFAIK map files should work.
In most cases SyNode is faster compared do NodeJS (at techempower tests - twice faster - see https://github.com/UnityBaseJS/benchmarks). This is because of SpiderMonkey optimizer and a fact what SyNode don't use a event loop (SyNode is synchronous & multi-thread, nodeJS is single thread and asynchronous)
Offline
SyNode evaluates threadID automatically via "GetCurrentThreadId".
I have thread pool, which means that thread ID may be different.
While looking how to pass thru custom ID inside ThreadSafeEngine, i've found comment line: "SM 45 expects the context to be released in the same thread".
So, SyNode should not be used in applications with thread pool, right?
Then what is the best approach, may be private threads for each SM Engine?
Last edited by George (2017-03-10 19:52:03)
Offline
SyNode & SynSM are multithread by design (opposite to nodeJS & V8). See "22 - JavaScript HTTPApi web server". You should call SMManager.ThreadSafeEngine inside your working thread, as in TTestServer.Process from sample 22
Last edited by mpv (2017-03-12 11:52:58)
Offline
Just for experiment purpose, i've created SyNode app with multiple FSMManager's each in separate thread.
For example if application need to work separately with different "coremodules" folders, with different "MaxPerEngineMemory" values, and so on.
That may be useless or not, don't know now)
Last edited by George (2017-03-18 17:20:32)
Offline
Not sure about VSCODE, but you can use Firefox remote debugger. AFAIK map files should work.
I hope that should be way to debug js from vscode...
Found special extension that can successfully connect with spidermonkey debugger.
But breakpoints not work.
As i saw in firefox, spidermonkey app determines as firefox addon.
Extension page says that 3 types of addons are supported: addonSdk, webExtension and legacy.
Regarding of addon type, corresponding config file is required.
Which type of addon SyNode simulate for debugging?
Last edited by George (2017-03-31 22:56:25)
Offline
Interesting finding! Our primary IDE is WebStorm, but it not work directly with FireFox Remote Debugger Protocol, so we even don't investigate IDE integration. I just installed a VSCode and try to understand how it work...
TIPS: the main entry point for debugger is TSMRemoteDebuggerCommunicationThread.HandleMessage.
I move VSCode topic here
Last edited by mpv (2017-04-01 14:29:08)
Offline
SyNode now support latest SpiderMonkey52 engine - see [1adba740c7] ( to use it define a condition variable SM52).
You can build mozjs52 using BUild instruction , or download from URLs noted in SyNode.pas
SpiderMonkey52 not yet officially released by Mozilla, for example we can't build it without Promises and know some minor issues in debugger, but most of things work well.
Our next steps is:
- compile SyNode using NewPascal (Windows target)
- compile SyNode using NewPascal (Linux x64 target)
Last edited by mpv (2017-04-01 12:25:25)
Offline
Dear mpv, could you share us the news of SyNode support with NewPascal under Linux
Offline
Hi @mpv
I download the dlls from x32: https://unitybase.info/downloads/mozjs-45.zip
Then I define SM52 on the global level, and it can run now, but in the COM object it shows an error.
I used the UBComBridge from @unitybase\com-bridge,
var dm = createobject("dm.dmsoft");
Log(dm.Ver());
This code is ok with SM45, but in the SM52 it will show
dm.Ver is not a function .
.
Is there something wrong with SM52?
Offline
Hi, mpv!
What is the best approach to add "console" emulation for JS context?
Should i create a class with all required methods, instantiate it and then call aEngine.GlobalObject.ptr.DefineProperty?
Is there a way to group few methods in one variable (like console.log, console.clear) without class instance on delphi side?
PS: dll extension can provide such behavior, but i'm interested to implement console internally.
Offline
Hi, mpv!
What is the best approach to add "console" emulation for JS context?
Should i create a class with all required methods, instantiate it and then call aEngine.GlobalObject.ptr.DefineProperty?
Is there a way to group few methods in one variable (like console.log, console.clear) without class instance on delphi side?
PS: dll extension can provide such behavior, but i'm interested to implement console internally.
The best idea is group them in *.js file(like nodejs does). You can see source code of node's console.js on GitHub
In this case you must implement some bindings. You can find examples of bindings in Synode code. Also you can find implementation of nodejs binding on GitHub and translate it to Delphy synode SpiderMonkey compatible code
If you will do this then sharing code to community will be a good idea
Offline
Thanks, will try.
Last edited by George (2017-08-30 16:02:30)
Offline
Hi @mpv
I download the dlls from x32: https://unitybase.info/downloads/mozjs-45.zip
Then I define SM52 on the global level, and it can run now, but in the COM object it shows an error.
I used the UBComBridge from @unitybase\com-bridge,var dm = createobject("dm.dmsoft"); Log(dm.Ver());
This code is ok with SM45, but in the SM52 it will show
dm.Ver is not a function .
.
Is there something wrong with SM52?
Just fix this error in @unitybase/com-bridge@1.0.7. Thanks for report!
Here is the patch
Offline
Dear mpv, could you share us the news of SyNode support with NewPascal under Linux
We spend a month (or even more) just to compile a stand alone SpiderMonkey mozjs.so under Ubuntu due to this bug in config. So work move very slowly...
Offline
Hi @mpv,
It's OK now!
I still think it's perfect to register over 255 functions for the defineClasses.
I have a few classes they have more than 255 functions.
how ever it's great now!
Thanks!
Offline
Need little help, i get strange results while debugging Debugger.js.
constructor (actorID, source, parent) {
super(actorID, parent);
parent._sourcesMap.set(source, this)
this._source = source;
this._breakpoints = {};
}
...
get _resp(){
let source = this._source;// || this.generatedSource;
consoleLog(stringify(source)); // -> [object undefined]
consoleLog(source.url); // -> ModuleLoader.js
...
How that possible?
With JSON.stringify i get:
get _resp(){
let source = this._source;// || this.generatedSource;
consoleLog(JSON.stringify(source)); // -> {}
consoleLog(source.url); // -> ModuleLoader.js
...
I use output log to debug Debugger.js, is better way exists?
Last edited by George (2017-09-04 20:53:46)
Offline
Need little help, i get strange results while debugging Debugger.js.
...
I use output log to debug Debugger.js, is better way exists?
consoleLog(JSON.stringify(source)); // -> {}
is usual behavior for internal debugger's objects
We used for debug debugger
consoleLog(stringify(source.<propertyName>));
List of properties you can see on MDN
But I am not sure that information on that page is actual, so I recommend You see also source code
Offline
Inside folder "core_modules" present file "console.js", inside file i see comment:
Modified by UnityBase core team to be compatible with SyNode
Module initialization relies on process.stdout, process.stderr.
Maybe someone already have usage sample (per engine global console implementation linked with callback from delphi side)?
Or information about how that can be done..
I want create simple demo application that will use more features than shown in current two demos.
With vscode debugger support.
Last edited by George (2017-09-12 08:46:16)
Offline
To make a console work, host application (Delphi executable what create a JS engine) must define process.stdout and process.stderr objects with write method. But this strongly relay on host process, so we do not put implementation inside SyNode.
Global object process is already defined by SMEngine - see TSMEngine.Create
So to implement a stdin & stdout in our application we put this code inside a SMManager.OnNewEngine handler
Multiple threads write to a process std* using SynLog writer - with EchoToConsole enabled
Log.Family.EchoToConsole := [sllWarning, sllError, sllLastError, sllException, sllExceptionOS, sllMemory]
Last edited by mpv (2017-09-13 08:40:43)
Offline