#1 2016-08-25 16:27:19

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Beta release of SyNode - ES6 JavaScript + NPM modules

I just commit a beta version of SyNode - the server-side JavaScript execution using the SpiderMonkey 45 library with nodeJS modules support.
The main features:
- based on SpiderMonkey45, so almost full support of ES6
- a remote debugger protocol realization, so can be debugged remotely using Firefox - see SyNode\Samples\02 - Bindings
- CommonJS modules realization, so compatible with NPM modules
- the first (as far as I know) server-side JavaScript engine which supports ES6 modules (import keyword). Relative path resolving not finished yet, but remote debugger already use a ES6 modules import/export
- modules can be implemented using Delphi (as a dll) - see SyNode\Samples\01 - Dll Modules
- JavaScript prototype definition based on Delphi RTTI (supported both "new" and old)

Well tested with Delphi XE2 (32bit). Verified in Delphi7.

The future direction:
- documentation and additional samples
- add a binding implementation for a nodeJS core modules to be more compatible with 3d party nodeJS modules - here community can help a lot
- fpc support? linux support? x64 support?

Last edited by mpv (2016-08-25 16:29:53)

Offline

#2 2016-08-25 16:34:12

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Documentation about core modules (fs, assert, path, etc) can be founded here. It generated using jsDoc and our jsDoc template from sources.

Precompiled x32 mozjs-45 library can be downloaded from here.

Last edited by mpv (2016-08-25 16:35:45)

Offline

#3 2016-08-25 16:46:52

hnb
Member
Registered: 2015-06-15
Posts: 291

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Amazing! My inner version of NewPascal uses NodeJS for few purposes, but thanks to your work I think is worth to review my code and support this effort smile

mpv wrote:

- fpc support? linux support? x64 support?

FPC is the most important part. The Delphi licence (after XE2) is evil:

Licensee agrees not to use the Product to develop an application that is directly competitive to the Product or to any other Embarcadero products

Seems like mORMot breaks that point in many fields, IMO very bad licence for mORMot... The comment from Embarcadero employee is for me very strange and nasty:

Jim McKeeth wrote:

There isn't really any official clarification on what that means. The license agreement is the official clarification - anything "official" beyond that would limit the scope of the agreement.

My understanding is similar to what +Roland Kossow said. Anything that someone would do to specifically target or clone our products. I expect that unless what you are doing is pretty blatant there won't be a reaction.

Basically, don't try to bite the hand that feeds you.

Another point for support for FPC: Delphi Linux compiler will be ARC only.

For extended RTTI for FPC we need to push forward NewPascal project.

Last edited by hnb (2016-08-26 08:31:47)


best regards,
Maciej Izak

Offline

#4 2016-08-26 20:27:15

macfly
Member
From: Brasil
Registered: 2016-08-20
Posts: 374

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Hi Friends,

Tried in Delphi 7 but ShLwApi.pas required in SyNode.pas (line 73) are missing.

unit SyNode;
...
uses
  {$ifdef MSWINDOWS} Windows, ShLwApi,{$endif}
  {$ifdef ISDELPHIXE2}System.SysUtils,{$else}SysUtils,{$endif}
  Classes,

Last edited by macfly (2016-08-26 20:27:56)

Offline

#5 2016-08-27 09:27:24

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

It's strange. On my computer the ShLwApi.pas unit is located in Delphi7\Source\RTL\Win\ShLwApi.pas

Offline

#6 2016-08-27 17:36:35

macfly
Member
From: Brasil
Registered: 2016-08-20
Posts: 374

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Realy strange.
This unit dont exists in my default installation of Delphi 7.

In Delphi xe5 exists.

For Delphi 7 i have found and downloaded from Jedi API
http://www.delphi-jedi.org/apilibrary.html


Now hi is calling for mozjs-45.dll

I have searched but dont found this dll to download.
Only mozjs-24.dll

Offline

#7 2016-08-27 22:04:33

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

See Download link in the #2 post in this topic

Offline

#8 2016-08-28 15:08:04

edwinsn
Member
Registered: 2010-07-02
Posts: 1,215

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

@mpv, this is huge! Well done!


Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.

Offline

#9 2016-08-28 17:34:10

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

@hnb Similar reasons prevented embarcadero to publish mORMot via their getit directory...


IMHO this is a really short view.... But it is their decisions.

Offline

#10 2016-10-11 16:15:45

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Dear mpv,

http://synopse.info/forum/viewtopic.php?id=3544
http://synopse.info/forum/viewtopic.php?id=3571

In the links above, you have identified that there should be a conflict between FastMM in FullDebug mode and a nspr memory manager (SpiderMonkey use a memory manager from Netscape Portable Run-time nspr4.dll).

However, FullDebugMode is needed for FastMM to log all memory leaks to a text file, and therefore rather important for finding memory leaks.

In SynSMAPI.pas, there is mentioned "compile mozjs without nspr" under "road map". Does this mean that this incompatibility between FastMM w/ FullDebugMode and SpiderMonkey will be removed in future ?  big_smile

Offline

#11 2016-10-11 20:48:27

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

This idea is obsolete. At last, SM45 require nspr. Actually, Im not understand your problem.personnaly I did use fastmm in full debug mode may be several time during last 10 years. Even in non full debug mode fastmm can detect a memory leak very well.

Offline

#12 2016-10-12 01:07:03

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

mpv wrote:

This idea is obsolete. At last, SM45 require nspr. Actually, Im not understand your problem.personnaly I did use fastmm in full debug mode may be several time during last 10 years. Even in non full debug mode fastmm can detect a memory leak very well.

FastMM with FullDebugMode on can give the call stack trace with line numbers leading to the individual memory leak. Could you comment whether you use this feature, and if yes, how to work around ? Many thanks for your efforts to share your experience ! big_smile (It should be mentioned that FastMM with FullDebugMode on is also very useful to track any buffer overflow, again, with the call stack trace with line numbers .)

Last edited by ComingNine (2016-10-12 01:12:27)

Offline

#13 2016-10-12 09:14:26

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

It seems that the memory footer corruption can be avoided with some modification of TSynTempBuffer to avoid the re-allocation in TSynTempBuffer.Init. The reason is unclear. Could ab or you help to correct the root problem ?

  private
    // tmp: array[0..4095] of AnsiChar; <--- memory footer corruption
    // 
    // for 'mORMot\SyNode\Samples\02 - Bindings\SpiderMonkey45Binding.dpr', 
    // where 'mORMot\SyNode\core_modules\DevTools\Debugger.js' will be loaded
    // = Length of 'mORMot\SyNode\core_modules\DevTools\Debugger.js' * 2 + 1 
    tmp: array[0..118038] of AnsiChar; 
    //
    // if only the files 'mORMot\SyNode\core_modules\node_modules\' are to be loaded
    // = Length of NativeModule.wrap('mORMot\SyNode\core_modules\node_modules\util.js') * 2 + 1 
    // tmp: array[0..33584] of AnsiChar; 
  end;

Offline

#14 2016-10-12 12:05:25

hnb
Member
Registered: 2015-06-15
Posts: 291

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

ComingNine wrote:

It seems that the memory footer corruption can be avoided with some modification of TSynTempBuffer to avoid the re-allocation in TSynTempBuffer.Init. The reason is unclear. Could ab or you help to correct the root problem ?

Maybe related to http://synopse.info/forum/viewtopic.php?id=3468 ?


best regards,
Maciej Izak

Offline

#15 2016-10-12 12:45:25

oz
Member
Registered: 2015-09-02
Posts: 95

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

If so, then a quick test using Delphi 7 compiler & FullDebugMode should work without any problems. Does it?

Offline

#16 2016-10-12 20:58:23

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Issue fixed by [d66449a2c4]. Thanks for reporting!
@AB - please, review the RawUnicodeToUtf8 function - may be we need to allocate one more byte there also?

 tmp.Init(WideCharCount*3+1); 

instead of 

 tmp.Init(WideCharCount*3); 

Offline

#17 2016-10-13 07:52:52

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

May be because of this issue we can't use a jemalloc ((Syn)ScaleMM for C). Now we will try once more..

Last edited by mpv (2016-10-13 07:53:25)

Offline

#18 2016-10-13 12:30:33

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Thanks for the input.

I guess that RawUnicodeToUtf8() is safe, since its output is 8-bit content, so trailing #0 is stored as a single 0 byte.

Offline

#19 2016-10-13 15:45:25

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

mpv wrote:

Issue fixed by [d66449a2c4]. Thanks for reporting!
@AB - please, review the RawUnicodeToUtf8 function - may be we need to allocate one more byte there also?

 tmp.Init(WideCharCount*3+1); 

instead of 

 tmp.Init(WideCharCount*3); 

Dear @mpv, thank you very much for your efforts ! The fix looks too fundamental to the framework to believe !... big_smile

ab wrote:

Thanks for the input.

I guess that RawUnicodeToUtf8() is safe, since its output is 8-bit content, so trailing #0 is stored as a single 0 byte.

Dear @ab,

function RawUnicodeToUtf8(Dest: PUTF8Char; DestLen: PtrInt; Source: PWideChar;
  SourceLen: PtrInt; Flags: TCharConversionFlags): PtrInt; overload;
  ......
  result := PtrInt(Dest);
  inc(DestLen,PtrInt(Dest));
  ......
    if (PtrInt(Dest)<DestLen) and (PtrInt(Source)<SourceLen) then
    repeat
      ......
    until false;
    if not (ccfNoTrailingZero in Flags) then
      Dest^ := #0;

as shown in SynCommons, if all UTF8 chars converted need 3 bytes to store, Dest^ := #0; is essentially setting the WideCharCount*3+1 th position instead of the WideCharCount*3 th position. Is this right ? It seems that @mpv has a good point here. yikes

Last edited by ComingNine (2016-10-13 15:46:43)

Offline

#20 2016-10-13 15:53:20

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

???
There is no such thing as an "UTF-8 char", and Dest^ := #0 is setting only a single byte in the latest position, which is already reserved by TSynTempBuffer.Init:

GetMem(buf,len+1); // +1 to include trailing #0

Offline

#21 2016-10-13 16:07:40

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

ab wrote:

???
There is no such thing as an "UTF-8 char", and Dest^ := #0 is setting only a single byte in the latest position, which is already reserved by TSynTempBuffer.Init:

GetMem(buf,len+1); // +1 to include trailing #0

I should be more precise: characters encoded with UTF8.
I see your point. Now I feel lost again why mpv's fix works... yikes

Last edited by ComingNine (2016-10-13 16:09:44)

Offline

#22 2016-10-13 16:21:03

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

It works because when you convert to UTF-16, the trailing #0 is stored in two bytes, so GetMem(buf,len+1) is not enough, and we have to call Init(  +1) to add space for another byte.
+1 byte +1 byte = +2 bytes = +1 UTF-16 codepoint (WideChar)
and
+1 byte = +1 UTF-8 codepoint (AnsiChar)

Offline

#23 2016-10-13 16:47:21

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Thank you for your patience and efforts to explain ! smile Too dumb to realize that the null terminator has to be two bytes in UTF-16 (also shown here http://stackoverflow.com/questions/2685730/) big_smile

https://www.securecoding.cert.org/confl … terminated
The first paragraph of the link above states "...... UTF-16 strings may contain \u0000 in the middle of the string, ......", but still, for UTF-16, the u'\0' is two bytes .

Last edited by ComingNine (2016-10-13 16:51:46)

Offline

#24 2016-10-18 16:06:48

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Could you help to comment why the "JSObject.Is***Array" returns false in the code below ? The array element is apparently numeric. Many thanks !

PS: The function can be appended to SpiderMonkey45Binding: ufrmSM45Demo.

// Test with: 
//   mainForm.JSTestArray1D([1,2,3])
//   mainForm.JSTestArray1D([1.0,2.0,3.0])
//   mainForm.JSTestArray1D([1.1,2.2,3.3])  
function TfrmSM45Demo.JSTestArray1D(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
  jsArr, jsArrElement: jsval;
  Cnt, I: Cardinal;
begin
  jsArr := vp.argv[0];
  mResult.Lines.Add('###########################');
  mResult.Lines.Add(BoolToStr(jsArr.isObject, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.isArray(cx), True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsTypedArrayObject, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsInt8Array, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsUInt8Array, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsInt16Array, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsUInt16Array, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsInt32Array, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsUInt32Array, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsFloat32Array, True));
  mResult.Lines.Add(BoolToStr(jsArr.asObject.IsFloat64Array, True));
  mResult.Lines.Add('---------------------------');
  jsArr.asObject.GetArrayLength(cx, Cnt);
  mResult.Lines.Add(IntToStr(Cnt));
  jsArr.asObject.GetElement(cx, 0, jsArrElement);
  mResult.Lines.Add(BoolToStr(jsArrElement.isInteger, True));
  mResult.Lines.Add(BoolToStr(jsArrElement.isDouble, True));
  mResult.Lines.Add(BoolToStr(jsArrElement.isNumber, True));
  mResult.Lines.Add(BoolToStr(jsArrElement.isSimpleVariant[cx], True));
  mResult.Lines.Add('###########################');

  Result := True;
end;

Last edited by ComingNine (2016-10-18 16:09:07)

Offline

#25 2016-10-19 02:32:52

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Could you help to comment why the "jsval.asDouble" returns NaN for 0.0 and 1.0 and so forth in the code below, and how to work around this inconvenience ? Is it not possible to pass 0.0 to JavaScript function and retrieve afterwards ? Many thanks !
PS: The function can be appended to SpiderMonkey45Binding: ufrmSM45Demo.

// mainForm.JSTestOnlyZeroAfterDecimal(0.0)    <--- NaN
// mainForm.JSTestOnlyZeroAfterDecimal(0.010)
// mainForm.JSTestOnlyZeroAfterDecimal(1.0)    <--- NaN
// mainForm.JSTestOnlyZeroAfterDecimal(1.010)
function TfrmSM45Demo.JSTestOnlyZeroAfterDecimal(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
  dblval: Double;
begin
  dblval := vp.argv[0].asDouble;
  mResult.Lines.Add('########    ' + FloatToStrF(dblval, ffFixed, 20, 8) + '   ########');
  Result := True;
end;

Last edited by ComingNine (2016-10-19 03:45:49)

Offline

#26 2016-10-19 08:07:17

Orel
Member
Registered: 2014-04-14
Posts: 13

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

ComingNine wrote:

why the "JSObject.Is***Array" returns false

functions JSObject.Is***Array uses for JavaScript typed arrays. Array is not JavaScript typed array, so this functions return false.

Also your using jsArr.isObject without rooting is not good idea - garbage collecting can start in any moment, so your jsArr.isObject in any moment can point to invalid address. Also you do not handle exception if it can throw. the right ussage is

function TfrmSM45Demo.JSTestArray1D(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
  jsArrElement: jsval;
  jsArr: PJSRootedObject;
  Cnt, I: Cardinal;
begin
  try
    mResult.Lines.Add('###########################');
    mResult.Lines.Add(BoolToStr(vp.argv[0].isObject, True));
    jsArr := cx.NewRootedObject(vp.argv[0].asObject);
    try
      mResult.Lines.Add(BoolToStr(jsArr.ptr.isArray(cx), True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsTypedArrayObject, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsInt8Array, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsUInt8Array, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsInt16Array, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsUInt16Array, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsInt32Array, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsUInt32Array, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsFloat32Array, True));
      mResult.Lines.Add(BoolToStr(jsArr.ptr.IsFloat64Array, True));
      mResult.Lines.Add('---------------------------');
      jsArr.ptr.GetArrayLength(cx, Cnt);
      mResult.Lines.Add(IntToStr(Cnt));
      jsArr.ptr.GetElement(cx, 0, jsArrElement);
      mResult.Lines.Add(BoolToStr(jsArrElement.isInteger, True));
      mResult.Lines.Add(BoolToStr(jsArrElement.isDouble, True));
      mResult.Lines.Add(BoolToStr(jsArrElement.isNumber, True));
      mResult.Lines.Add(BoolToStr(jsArrElement.isSimpleVariant[cx], True));
      mResult.Lines.Add('###########################');
    finally
      cx.FreeRootedObject(jsArr);
    end;
    Result := True;
  except
    on E: Exception do begin
      Result := False;
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;
end;

Last edited by Orel (2016-10-19 09:17:32)

Offline

#27 2016-10-19 08:17:37

Orel
Member
Registered: 2014-04-14
Posts: 13

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

ComingNine wrote:

why the "jsval.asDouble" returns NaN for 0.0 and 1.0

SpiderMonkey stores number value in 2 ways: signed int32 or double. 0.0 or 1.0 is converts to int32 and you can not use it as double.

So for using number value you can use the next code

function TfrmSM45Demo.JSTestOnlyZeroAfterDecimal(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
  dblval: Double;
begin
  try
    if (argc = 0) or  not vp.argv[0].isNumber then
      raise ESMException.Create('Expectig argument number type ');
    if vp.argv[0].isDouble then
      dblval := vp.argv[0].asDouble
    else if vp.argv[0].isInteger then
      dblval := vp.argv[0].asInteger
    else
      raise ESMException.Create('This can never happen');
    mResult.Lines.Add('########    ' + FloatToStrF(dblval, ffFixed, 20, 8) + '   ########');
    Result := True;
  except
    on E: Exception do begin
      Result := False;
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;
end;

Offline

#28 2016-10-19 09:14:12

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Dear Orel, thank you very much for your knowledgeable posts ! (PJSRootedObject does not have isObject so that mResult.Lines.Add(BoolToStr(jsArr.isObject, True)); line has to be commented out.)
It then seems that we could not get Delphi array directly from jsval but has to call ptr.GetElement(cx, index, jsArrElement); manually.

Offline

#29 2016-10-19 09:36:15

Orel
Member
Registered: 2014-04-14
Posts: 13

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

isObject is property of jsval. Fixed in example.

ComingNine wrote:

It then seems that we could not get Delphi array directly from jsval but has to call ptr.GetElement(cx, index, jsArrElement); manually.

Yes, you are right.
JavaScript allow the next construction

var arr = [];
arr[5] = 5;
arr[770000001] = 100;

in this case arr.length is 770000002, but array has only 2 values. arr[4] or arr[77778] is undefined. SpiderMonkey do not allocate memory for other values. So, you can not get all values to delphi array directly. Array in JS and array in Delphi is not the same.
Dirrectly acces to values you can get if you use JavaScript typed array. But in this case you cannot change array dimension

Last edited by Orel (2016-10-19 09:51:21)

Offline

#30 2016-10-19 10:25:51

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Thank you very much for your helpful comments ! smile

Offline

#31 2016-10-22 03:17:40

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Dear mpv and Orel,

Could you help to comment how to use SyNode (SpiderMonkey) to expose a whole set of Delphi class/object hierarchy to JavaScript ?

As shown in ADOBE ILLUSTRATOR CC 2015.3 SCRIPTING REFERENCE: JAVASCRIPT, Adobe Illustrator has exposed its whole CPP class/object hierarchy to JavaScript. Consequently, we can create arbitrary Illustrator CPP instances within JavaScript, and call methods of these Illustrator CPP instances within JavaScript.

#include "lib/lodash.3.10.1.js"
    var docRef = Application.Documents.Add();  // Create an empty Document with default settings
    ...
    var groupRef = docRef.layers[0].groupItems[0];  // Manipulate the properties of the newly-created Document
    _.each(groupRef.pathItems, function(pathItemRef)
    {
      if(pathItemRef.strokeDashes.length >= 1)
      {
        pathItemRef.strokeWidth = 2.0;
        pathItemRef.strokeDashes = [2, 4];
      }     
    })

Nonetheless, as shown in the DoOnCreateNewEngine callback in mORMot\SyNode\Samples\02 - Bindings, it seems that only existing Delphi instance is exposed to the JavaScript global object as property.

 
  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
  );

Furthermore, as shown in 22.3.2. Calling Delphi code from JavaScript, it seems that only method of existing Delphi instance is exposed to the JavaScript global object as method.

procedure TTestServer.DoOnNewEngine(const Engine: TSMEngine);
...
  // add native function to the engine
  Engine.RegisterMethod(Engine.GlobalObj,'loadFile',LoadFile,1);
end;
function TTestServer.LoadFile(const This: variant; const Args: array of variant): variant;
begin
  if length(Args)<>1 then
    raise Exception.Create('Invalid number of args for loadFile(): required 1 (file path)');
  result := AnyTextFileToSynUnicode(Args[0]);
end;

Lastly, I could not figure out how to do this from https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_Cookbook

I am therefore rather lost how to achieve similar effect with SyNode (SpiderMonkey). For the hierarchy shown below, could you help to comment how to expose not only the instance of the TMainForm class but also the TWorkerForm and TWorker classes (instances may not be created yet), so that TWorderForm can be created  in JavaScript, and methods of TWorker can be called in JavaScript ?
RrgQHb3.png

Many thanks !!

Last edited by ComingNine (2016-10-22 03:52:02)

Offline

#32 2016-10-22 09:54:34

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

You should expose classes TWorkerForm & TWorker to the engine using TSMEngine.defineClass method. In JavaScript you must define a prototype for a class - for Delphi objects you have 2 choice TSMSimpleRTTIProtoObject & TSMNewRTTIProtoObject.

In the sample "02 - Bindings" we define a prototype TStringsProto and register it in the DoOnCreateNewEngine - see the sample sources . After a class is registered you can create it directly from JS using

var myForm = new TWorkerForm()

Offline

#33 2016-10-22 11:57:20

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

mpv wrote:

You should expose classes TWorkerForm & TWorker to the engine using TSMEngine.defineClass method. In JavaScript you must define a prototype for a class - for Delphi objects you have 2 choice TSMSimpleRTTIProtoObject & TSMNewRTTIProtoObject.

In the sample "02 - Bindings" we define a prototype TStringsProto and register it in the DoOnCreateNewEngine - see the sample sources . After a class is registered you can create it directly from JS using

var myForm = new TWorkerForm()

Thank you very much for your instructions !
Nonetheless, even though TSringsProto is defined in the sample "02 - Bindings", there seems to be lack of any exemplary usage of it… Could you suggest me the guideline to implement feature like var viewFrm = mainForm.ViewForms.Add ? I mean, after I use defineClass, how should I implement correctly the typed collection properties (ViewForms) of mainForm, and the Add method of the typed collection ? Many thanks !

Last edited by ComingNine (2016-10-23 08:06:53)

Offline

#34 2016-10-22 14:15:48

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Could you help to comment a few more small problems that I meet when trying mORMot\SyNode\Samples\02 - Bindings:

(1) when running, in the left memo, calling fs.writeFileSync fails complaining writeFileNew is undefined, calling fs.appendFileSync fails complaining appendFileNew is undefined.

(2) when running, in the left memo, calling alert fails complaining alert is undefined...

(3) when running, in the left memo, calling const console = require('console') or const console = require('console').Console fails complaining Console expects a writable stream instance...

(4) when running, in the left memo, adding four lines below will not trigger function TStringsTextRead at all(breakpoint does not stop)... Is TStringsProto used at all in this sample ?

let msg1 = mainForm.results.Text
let msg2 = mainForm.results

var msg3 = mainForm.results.Text
var msg4 = mainForm.results

(5) when running, in the left memo, var x = new TStrings() is evaluated without error, even after the aEngine.defineClass(mSource.lines.ClassType, TStringsXProto, aEngine.GlobalObject); line in the source code is commented out. Why is this possible ? It seems that SyNode does not automatically call defineClass by itself.

(6) when running, in the left memo, after the code below is evaluated, the right memo gives "HelloWorld" without the line break ? More importantly,  neither function TStringsTextRead nor function TStringsWrite is triggered (breakpoint does not stop)...

var x = new TStrings()
x.Text = "Hello\nWorld"
mainForm.toLog(x.Text)

var y = new TMemoStrings()
y.Text = "Hello\nWorld"
mainForm.toLog(y.Text)

[7] when running, in the left memo, the code below exemplifies the usage of TStringsProto to me ! big_smile

mainForm.toLog("line 1");
mainForm.toLog("line 2\r\nline 3");
mainForm.toLog("line 4");

let l_memo = mainForm.results /* results is the published property of TMainForm and thus accessible automatically ? */
let l_memostrings = mainForm.results.lines /* Lines is the published property of TMemos and thus accessible automatically, as implemented in SyNodeSimpleProto.pas:477: ? */
var v_memo = mainForm.results /* Points to the same reference as l_memo */
var v_memostrings = mainForm.results.lines /* Points to the same reference as l_memostrings */

/* TStringsTextRead */
mainForm.toLog("l_memo.lines.text: " + l_memo.lines.text) 
mainForm.toLog("v_memo.lines.text: " + v_memo.lines.text) 

/* TStringsTextWrite */
l_memostrings.text = "line 1\r\nline 2\r\nline 3\r\nline 4"
mainForm.toLog("v_memostrings.text: " + v_memostrings.text) 

Many thanks !! yikes big_smile

Last edited by ComingNine (2016-10-23 09:07:00)

Offline

#35 2016-10-23 15:09:09

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Taking "mORMot\SyNode\Samples\02 - Bindings" for example, if another TForm descendant unit is added

unit Unit1;

interface

uses
  SysUtils, Classes, Forms;

type
{$M+}

  TWorker = class
  private
    fName: string;
  public
    constructor Create;
  published
    property Name: string read fName;
  end;

  TForm1 = class(TForm)
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    fWorker: TWorker;
  public
  published
    property Worker: TWorker read fWorker;
  end;

implementation

{ TWorker }

constructor TWorker.Create;
begin
  fName := 'A new TWorker instance';
end;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  fWorker := TWorker.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  fWorker.Free;
end;

end.

,and the ufrmSM45Demo unit is modified with the addition of

...
published
...
    function NewForm1(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
...
function TfrmSM45Demo.NewForm1(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
  Frm1: TForm1;
begin
  Frm1 := TForm1.Create(Self);
  vp.rval := CreateJSInstanceObjForSimpleRTTI({FEngine.}cx, Frm1, FEngine.GlobalObject);
  Result := True;
end;

,one can run the JavaScript code below

var f = mainForm.NewForm1()
mainForm.toLog(f.worker.name)

.

Please notice that the first letter of the published property has to be typed in lower case, and there is no such restriction regarding the first letter of the published function.
Could you help to comment about the reason ? Is it possible to remove the restriction for published properties ? big_smile

Offline

#36 2016-10-24 13:40:59

edwinsn
Member
Registered: 2010-07-02
Posts: 1,215

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Today I found some spare time connected from FireFox to the binding002 demo program, very cool smile


Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.

Offline

#37 2016-10-24 22:27:20

warleyalex
Member
From: Sete Lagoas-MG, Brasil
Registered: 2013-01-20
Posts: 250

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Would it be possible to use SynNode to compile smart pascal to javascript?

Following ComingNine's example, generate similar code:

var TWorker = {
   $ClassName:"TWorker",$Parent:TObject
   ,$Init:function ($) {
      TObject.$Init($);
      $.fName = "";
   }

   ,Create$61:function(Self) {
      Self.fName = "A new TWorker instance";
      return Self
   }
   ,Destroy:TObject.Destroy
};

Self.fWorker = TWorker.Create$61($New(TWorker));
console.log(Self.fWorker.fName);  // "A new TWorker instance"

Offline

#38 2016-10-26 15:04:43

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Dear mpv,

Could you help to comment about returning Delphi object from JavaScript function, using mORMot\SyNode\Samples\02 - Bindings as an example:
(1) add TForm1 to the project, as shown in #35 post of this thread, and modify the btnEvaluateClick as below

procedure TfrmSM45Demo.btnEvaluateClick(Sender: TObject);
var
  res: jsval;
  Frm1V: Variant;
  Frm1: TForm1;
begin
  // 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);

  res := FEngine.CallObjectFunction(FEngine.GlobalObject, 'NewForm1AsJSFunc', []);
  Frm1V := res.asSimpleVariant[FEngine.cx];  <---- ESMException in function jsval.getSimpleVariant(cx: PJSContext): Variant;
  Frm1 := TVarData(V).VPointer; // Seems feasible to hold object in Variant according to Barry Kelly's comment in this SO post http://stackoverflow.com/questions/366329/why-cant-delphi-variants-hold-objects
  Frm1.Caption := 'hello ! Delphi Object from JavaScript!';
end;

(2) when running, in the left memo, type in the following code and evaluate. Unfortunately, ESMException occurs in function jsval.getSimpleVariant(cx: PJSContext): Variant;

function NewForm1AsJSFunc()
{
    var f = mainForm.NewForm1()
    return f
}

Could you help to finish the TODOs concerning JSTYPE_OBJECT and JSTYPE_FUNCTION in function jsval.getSimpleVariant, so that Delphi code can call JavaScript functions which return customized Delphi object ?

Last edited by ComingNine (2016-10-26 15:11:31)

Offline

#39 2016-10-27 05:34:29

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

You can get a pointer to the native (Delphi) instance using IsInstanceObject. Unfortunately, we lost a variant compatibility for a complex types because of rooting mechanism in SM45

var
  res: jsval;
  instRef: PSMInstanceRecord;
..
res := FEngine.CallObjectFunction(FEngine.GlobalObject, 'NewForm1AsJSFunc', []);
if IsInstanceObject(FEngine.cx, res, instRef) then
  if (instRef.instance is TForm) then
    .. do something with TForm

Last edited by mpv (2016-10-27 05:36:00)

Offline

#40 2016-10-27 05:52:07

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

ComingNine wrote:

Please notice that the first letter of the published property has to be typed in lower case, and there is no such restriction regarding the first letter of the published function.
Could you help to comment about the reason ? Is it possible to remove the restriction for published properties ?

I'm force the conversion of first character of Delphi properties to the lower case to follow JavaScript naming convention. We do not do it for a method's, because method's (function in term of JS) can be a constructor  (as you write in example above mainForm.NewForm1() ), and JavaScript naming convention for constructors is "start it name from a upper case". So this is a developer responsibility to name the methods in a right way (camelCase for usual functions, StartFromUpper for a functions what must be called with new).

Offline

#41 2016-10-27 13:41:13

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

mpv wrote:

You can get a pointer to the native (Delphi) instance using IsInstanceObject. Unfortunately, we lost a variant compatibility for a complex types because of rooting mechanism in SM45

var
  res: jsval;
  instRef: PSMInstanceRecord;
..
res := FEngine.CallObjectFunction(FEngine.GlobalObject, 'NewForm1AsJSFunc', []);
if IsInstanceObject(FEngine.cx, res, instRef) then
  if (instRef.instance is TForm) then
    .. do something with TForm

Thank you very much for your instructions !

mpv wrote:
ComingNine wrote:

Please notice that the first letter of the published property has to be typed in lower case, and there is no such restriction regarding the first letter of the published function.
Could you help to comment about the reason ? Is it possible to remove the restriction for published properties ?

I'm force the conversion of first character of Delphi properties to the lower case to follow JavaScript naming convention. We do not do it for a method's, because method's (function in term of JS) can be a constructor  (as you write in example above mainForm.NewForm1() ), and JavaScript naming convention for constructors is "start it name from a upper case". So this is a developer responsibility to name the methods in a right way (camelCase for usual functions, StartFromUpper for a functions what must be called with new).

Thank you very much for your helpful comments !

PS: Could you also help to comment about my problems concerning undefined JS functions in #34 post of this thread ? More importantly, do you recommend loading into SyNode renowned JS libraries such as Node.js and Lodash and so forth ? I mean, on the official page of Node.js, it is written that Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine...

smile Many thanks !

Last edited by ComingNine (2016-10-27 13:43:22)

Offline

#42 2016-10-27 14:37:39

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

About fs.writeFile* - we are working on `fs` module right now (this part is already implemented inside UnityBase, but we need do remove some internal dependencies to move it to SyNode)

About `lodash` - you can use it right now
1) Install lodash

>cd "D:\Work\synopse\SyNode\Samples\02 - Bindings\" 
>npm install lodash

2) Use it

var _ = require('lodash');
var sortedSQ = _([3, 2, 1]).map( (x) => x*x ).sort().values();
mainForm.toLog(JSON.stringify(sortedSQ));

Offline

#43 2016-10-27 15:07:08

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

mpv wrote:

About fs.writeFile* - we are working on `fs` module right now (this part is already implemented inside UnityBase, but we need do remove some internal dependencies to move it to SyNode)

About `lodash` - you can use it right now
1) Install lodash

>cd "D:\Work\synopse\SyNode\Samples\02 - Bindings\" 
>npm install lodash

2) Use it

var _ = require('lodash');
var sortedSQ = _([3, 2, 1]).map( (x) => x*x ).sort().values();
mainForm.toLog(JSON.stringify(sortedSQ));

Great to know that !

Thank you for your efforts and helpful comments ! smile

Last edited by ComingNine (2016-10-27 15:07:45)

Offline

#44 2016-10-28 16:34:00

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Dear mpv, could you help to comment the following problem of mine ? Many thanks !

I had a Delphi .dpk package (for Win32) which could build and install without any problem. However, after adding JavaScript-related code (thanks to great mORMot and SyNode projects), even if the mozjs-45.dll, nsp4.dll and the rest dll files are within the .bpl directory, or put into C:\Windows\SysWOW64, the package can no longer be installed. The error is

The procedure entry point JS_GetSharedArrayBufferViewType could not be located in the dynamic library test.bpl 

It is really confusing me because mozjs-45.dll is right there in the same directory as the .bpl file. yikes

Could you help to comment about the reason and possibly the workround ? Many thanks !

Last edited by ComingNine (2016-10-28 16:36:37)

Offline

#45 2016-10-29 10:45:22

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

We commit a huge improvement to the SyNode. The most important changes are:
- x64 SpiderMonkey support - compiled x64 binary is here. Tested with XE2 & XE4
- nodeJS Buffer binding & Buffer module
- pure JavaScript implementation of timer loop, so setTimeout/setInterval etc. are now work as expected

@ComingNine - also this commit fix a issue with JS_GetSharedArrayBufferViewType.

Since we now have a Buffer we can implement a fully nodeJS compatible fs module. Current implementation is based on AnyTextFileToRawUTF8 and do not allow chunked file reading/writing

Offline

#46 2016-10-29 11:33:06

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

mpv wrote:

We commit a huge improvement to the SyNode. The most important changes are:
- x64 SpiderMonkey support - compiled x64 binary is here. Tested with XE2 & XE4
- nodeJS Buffer binding & Buffer module
- pure JavaScript implementation of timer loop, so setTimeout/setInterval etc. are now work as expected

@ComingNine - also this commit fix a issue with JS_GetSharedArrayBufferViewType.

Since we now have a Buffer we can implement a fully nodeJS compatible fs module. Current implementation is based on AnyTextFileToRawUTF8 and do not allow chunked file reading/writing

Really great news ! Really fabulous work !! cool

Offline

#47 2016-10-30 14:43:35

erick
Member
Registered: 2015-09-09
Posts: 155

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

x64 bit - incredible news!  Congratulations!!!

Erick

Offline

#48 2016-10-31 08:49:13

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

mpv wrote:

About fs.writeFile* - we are working on `fs` module right now (this part is already implemented inside UnityBase, but we need do remove some internal dependencies to move it to SyNode)

About `lodash` - you can use it right now
1) Install lodash

>cd "D:\Work\synopse\SyNode\Samples\02 - Bindings\" 
>npm install lodash

2) Use it

var _ = require('lodash');
var sortedSQ = _([3, 2, 1]).map( (x) => x*x ).sort().values();
mainForm.toLog(JSON.stringify(sortedSQ));

Dear mpv and Orel,

at the #26, #29 posts of this thread, Orel has mentioned that Delphi array and JS (normal/typed ?) array are different things. Could you help to suggest how to code the JSNative function at Delphi side, to return array to JS side, which can be used with lodash ? Many thanks !

Last edited by ComingNine (2016-10-31 08:52:31)

Offline

#49 2016-10-31 09:44:15

Orel
Member
Registered: 2014-04-14
Posts: 13

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

ComingNine wrote:

Could you help to suggest how to code the JSNative function at Delphi side, to return array to JS side, which can be used with lodash ? Many thanks !

for normal Array

function <funcName>(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var arr: PJSRootedObject;
...
begin
  try
    ....
    arr := cx.NewRootedObject(cx.NewArrayObject(0));
    try
      arr.ptr.SetElement(cx, 0, SimpleVariantToJSval(cx, 'Some string'));
      arr.ptr.SetElement(cx, 1, SimpleVariantToJSval(cx, 25));
      arr.ptr.SetElement(cx, 2, SimpleVariantToJSval(cx, true));
      ...
      vp.rval := arr.ptr.ToJSValue;
    finally
      cx.FreeRootedObject(arr);
    end;
    Result := True;

  except
    on E: Exception do
    begin
      Result := False;
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;
end;

for Typed array(it is like delphi array - all elements has the same type but you can not change size of array after creation)

function <funcName>(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const arrSize = 50;
var arr: PJSRootedObject;
     arrData: Puint32Vector;
     i: integer;
     isShared: Boolean;
...
begin
  try
    ....
    arr := cx.NewRootedObject(cx.NewUint32Array(arrSize));
    try
      arrData := arr.GetUint32ArrayData(isShared, nil);
      for i := 0 to arrSize - 1 do
        arrData [i] := i*i;
      ...
      vp.rval := arr.ptr.ToJSValue;
    finally
      cx.FreeRootedObject(arr);
    end;
    Result := True;

  except
    on E: Exception do
    begin
      Result := False;
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;
end;

Last edited by Orel (2016-10-31 09:53:52)

Offline

#50 2016-10-31 10:33:22

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Beta release of SyNode - ES6 JavaScript + NPM modules

Dear Orel,

  thank you very much for your kind help and instructive sample code ! cool

  PS: It seems that in your above post and also your #26 post, one should pay strict attention to the rooting and the exception handling.

  Could you help to comment whether you would suggest that I revise the code below (also shown in my #35 post) accordingly ? Many thanks !

  From

function TfrmSM45Demo.NewForm1(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
  Frm1: TForm1;
begin
  Frm1 := TForm1.Create(Self);
  vp.rval := CreateJSInstanceObjForSimpleRTTI({FEngine.}cx, Frm1, FEngine.GlobalObject);
  Result := True;
end;

  to

function TfrmSM45Demo.NewForm1(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
  frm1: TForm1;
  rootObj: PJSRootedObject;
begin
  try
    frm1 := TForm1.Create(Self);
    try
      rootObj := cx.NewRootedObject(CreateJSInstanceObjForSimpleRTTI({FEngine.}cx, frm1, FEngine.GlobalObject));
      vp.rval := rootObj.ptr.ToJSValue;
    finally
      cx.FreeRootedObject(rootObj);
    end;
    Result := True;

  except
    on E: Exception do
    begin
      Result := False;
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;

More over, if the above revision is correct, could you help to comment why there is cx.FreeRootedObject(rootObj); ? I mean, the return value of this JSNative function, vp.rval, will be needed afterwards. The vp.rval stores rootObj.ptr.ToJSValue, but rootObj is freed ? The GC Rooting Guide does not help me understand here. It really puzzles me yikes

Last edited by ComingNine (2016-10-31 12:58:48)

Offline

Board footer

Powered by FluxBB