Synopse Open Source - Tag - SpiderMonkeymORMot MVC / SOA / ORM and friends2024-02-02T17:08:25+00:00urn:md5:cc547126eb580a9adbec2349d7c65274DotclearSome thoughts about "modern" pascal, generics, code and data structuresurn:md5:acf1cb9a27b2eaeb30dba14090c0c4502014-09-13T10:43:00+02:002014-09-13T10:15:54+02:00AB4327-GANDIPascal ProgrammingAJAXblogDelphiDependencyInjectiondynamic arraygenericsGoodPracticeinterfaceJavaScriptMustacheSmartMobileStudioSpiderMonkey<p>In a comment of a <a href="https://plus.google.com/103128232230617819686/posts/5Foo4B8wvES">Google+
announcement about new C# features</a>, Stephan did react about my naive
enthusiasm about <a href="http://en.m.wikipedia.org/wiki/The_Smart_Pascal_programming_language">SmartPascal</a>.</p>
<p>Apart from the fact that he did miss the numerous ways of creating
<em>Windows</em> executable in this dialect (I quoted at least 5 diverse ways),
he was chocked by the fact that the <em>SmartPascal</em> syntax, in
its actual idiom, does not support generics.</p>
<p><img src="http://businessjournalism.org/wp-content/uploads/2011/07/GenericDrugs.jpg" alt="" /></p>
<p>But are generics mandatory?<br />
I'm not speaking about any drug identified by its chemical name rather than its
brand name (<a href="http://en.wikipedia.org/wiki/Generic_drug">Wikipedia</a>).<br />
I would neither comment on the current Delphi implementation of generics (which
may appear not so polished, nor very widely used, even in the Delphi RTL/FMX,
but for <code>TList<T> TDictionary<></code>).<br />
Just share some thoughts about what is, to my opinion - which may be wrong and
biased! - the most important part of today's programming.</p> <p>Yes, <em>SmartPascal</em> does not support generics yet.<br />
But it is tempered by several features of the language:</p>
<ul>
<li>Its awesome <em>array</em> support, which makes <code>TList<T></code>
appear quite limited;</li>
<li>The classic object pascal factory pattern of "<code>type of class</code>"
and <em>virtual constructors</em>;</li>
<li>Its support of <code>variant</code>, which maps any <em>Javascript</em>
native object, allowing to write snippets of code which are as powerful as
<code>TDoSomeThingWith<T></code> ,or the
powerful <code>TDictionary<string,TValue></code> engine hidden
within any <em>JavaScript</em> object.</li>
</ul>
<p>I'm quite confident <a href="http://www.delphitools.info/">Eric Grange</a>
(the DWS/SMS compiler main maintainer) would be able to add generics support,
when he would like to, and have time to refactor the compiler AST somewhat
deeply.</p>
<p>But are generics really mandatory for a modern language?<br />
Why is Google not willing to introduce them in their - so trendy and modern -
Go language? Isn't it because of its <a href="http://blog.jonathanoliver.com/golang-has-generics/">very good list
support</a>?<br />
Do you really think that <a href="http://stackoverflow.com/a/8915221/458259">the following code</a> is readable,
maintainable, good OOP practice:</p>
<pre>
Fn<Integer, Integer> times3plus2 = <br /> Currying.<Integer, Integer, Integer>compose().ap(plus2).ap(times3);
</pre>
<p>I understand when <em>Lars Fosdal</em>, in a comment of the
<em>Google+</em> post, wrote that "<em>Once you go Generics, there is no way
back</em>".</p>
<p>Personally, I used to spend a lot of time in C# generics and several awesome
C# libraries - pretty close to Stephan's great library, BTW - (ab)used (of)
LINQ queries, had to fight against WCF, RhinoMock, and Unity XML configuration
files, and I still prefer "classic" object pascal code, without generics.<br />
So it was not difficult for me go find my way back.</p>
<p>Today, if I need something involving multiple types, I try to follow
<a href="https://blog.synopse.info?post/post/2011/11/27/SOLID-design-principles">SOLID principles</a>, define
several small classes, and use inheritance to share as much code as
possible.<br />
If a template system is needed for some huge part of the code, I use <a href="https://blog.synopse.info?post/post/2014/04/28/Mustache-Logic-less-templates-for-Delphi-part-3">SynMustache</a>,
and generate code ahead of time.<br />
If I need a factory lazy pattern for classes, I use virtual constructors, and
follow the <em>Liskov</em> substitution principle.<br />
If I need stubs/mocks or factories for interfaces, a
<code>TypeInfo(IAnyInterface)</code>reference does the job, without the
generics overhead.</p>
<p>I can see almost nothing I could do with generics, which I could not do with
regular code.<br />
Yes, I agree I can't make my code look like C#, but I can still follow SOLID
principles, without being lost in some language-level "design patterns", which
sounds more like Lego bricks than true computer science.</p>
<p>It appears that my today's mind is the following.<br />
IMHO as programmers, we should <em>focus on data structures, not on
code</em>.<br />
I'm sure you <a href="http://programmers.stackexchange.com/q/163185">know the
Linus' quote</a>, and is what I learnt by reading Knuth and others, or looking
at genuine pieces of software like the <a href="http://www.sqlite.org/download.html"><em>SQlite3</em> engine</a> itself.</p>
<p>Of course, this is a matter of taste.<br />
I currently spend hours in SMS for a new project, and also on latest versions
of Delphi.<br />
From the syntax point of view, what I can do with SMS syntax is highly
compatible with Delphi code (I can share 95% of my code with Delphi), and when
I need it, the 5% SMS-specific part can unleash the power of
<em>SmartPascal</em> over <em>JavaScript</em>.<br />
Beyond the syntax, what is amazing with <em>Smart</em> is that you can unleash
the "good part of JavaScript" (I'm sure <a href="http://shop.oreilly.com/product/9780596517748.do">you read that book</a>),
when you need it.<br />
Thanks to Object Pascal, I can <em>focus on data structures</em> (like classes,
arrays or records definitions), follow SOLID principles, and still have pretty
good performance - even with JavaScript as intermediate language, due to the
huge amount of work that today's JS engines did accumulate.<br />
Besides the memory hungry behavior of its <em>GarbageCollector</em> (which
may be an issue), performance is very good, as soon as <a href="http://www.html5rocks.com/en/tutorials/speed/v8">you follow some patterns in
your JavaScript style of coding</a>... just like the SMS compiler does
when generating its JavaScript. The first time I looked at the JavaScript code
generated by SMS, I found it pretty weird and not optimized (in terms of code
size), but after a deeper analysis, and some technical readings, I was amazed.
It does generates code which executes pretty fast on any modern JS engine.</p>
<p>In short, what I like today with SMS is that:</p>
<ol>
<li>I can use modern object pascal to focus on my data structures;</li>
<li>I can let the SMS compiler focus on the code generation.</li>
</ol>
<p>So, with SMS, I'm an happy programmer.<br />
Without the need of using generics.<br />
Lucky me!</p>
<p>You are welcome to comment further these ideas <a href="https://plus.google.com/103128232230617819686/posts/5Foo4B8wvES">on
Google+</a>, which are just my today's mood and suggestive reactions, not any
definitive nor </p>JavaScript support in mORMot via SpiderMonkeyurn:md5:67b0bbae1c854c06318ad885014cd92f2014-04-07T19:29:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkAJAXblogDelphiDocumentationDomainDrivenexceptionGarbageCollectorGoodPracticeinterfaceJavaScriptJSONLateBindingmORMotmultithreadOpenSourceORMperformanceRTTISOASourceSpiderMonkey<p>As <a href="https://blog.synopse.info?post/post/2013/01/20/Adding-JavaScript-support-to-mORMot">we already
stated</a>, we finished the first step of integration of the
<em>SpiderMonkey</em> engine to our <em>mORMot</em> framework.<br />
Version 1.8.5 of the library is already integrated, and latest official
revision <a href="http://synopse.info/forum/viewtopic.php?pid=10201#p10201">will be soon merged,
thanks to <em>mpv</em>'s great contribution</a>.<br />
It can be seen as stable, since it is already used on production site <a href="http://synopse.info/forum/viewtopic.php?pid=9989#p9989">to serve more than
1,000,000 requests per day</a>.</p>
<p><img src="http://2.bp.blogspot.com/-0ejeplNjUjE/UyRK-n4OD7I/AAAAAAAAFXc/_MdlqgQb7E8/s1600/mozilla-firefox.png" alt="" /></p>
<p>You can now easily uses JavaScript on both client and server side.<br />
On server side, <em>mORMot</em>'s implementation offers an unique concept, i.e.
<a href="https://blog.synopse.info?post/post/2013/09/10/Thread-safety-of-mORMot">true multi-threading</a>,
which is IMHO a huge enhancement when compared to the <a href="http://nodejs.org/">regular <em>node.js</em> mono-threaded implementation</a>,
and its <a href="http://callbackhell.com/">callback hell</a>.<br />
In fact, <em>node.js</em> official marketing states its non-blocking scheme is
a plus. It allows to define a HTTP server in a few lines, but huge server
applications need <em>JavaScript</em> experts not to sink into a state a
disgrace.</p> <h3>Scripting abilities of <em>mORMot</em></h3>
<p>As a <em>Delphi</em> framework, <em>mORMot</em> premium language support is
for the <em>object pascal</em> language. But it could be convenient to have
some part of your software not fixed within the executable. In fact, once the
application is compiled, execution flow is written in stone: you can't change
it, unless you modify the Delphi source and compile it again. Since
<em>mORMot</em> is <em>Open Source</em>, you can ship the whole source code to
your customers or services with no restriction, and diffuse your own code as
pre-compiled <code>.dcu</code> files, but your end-user will need to have a
Delphi IDE installed (and paid), and know the Delphi language.</p>
<p>This is when scripting does come on the scene.<br />
For instance, scripting may allow to customize an application behavior for an
end-user (i.e. for reporting), or let a domain expert define evolving
appropriate business rules - following <a href="https://blog.synopse.info?post/post/2014/01/04/Domain-Driven-Design%3A-part-1">Domain Driven Design</a>.</p>
<p>If your business model is to publish a core domain expertise (e.g.
accounting, peripheral driving, database model, domain objects, communication,
AJAX clients...) among several clients, you will sooner or later need to adapt
your application to one or several of your customers. There is no "one
<code>exe</code> to rule them all". Maintaining several executables could
become a "branch-hell". Scripting is welcome here: speed and memory critical
functionality (in which <em>mORMot</em> excels) will be hard-coded within the
main executable, then everything else could be defined in script.</p>
<p>There are plenty of script languages available.<br />
We considered <a href="http://code.google.com/p/dwscript">DelphiWebScript</a>
which is well maintained and expressive (it is the code of our beloved
<em>SmartMobileStudio</em>), but is not very commonly used. We still want to
include it in the close future.<br />
Then <a href="http://www.lua.org">LUA</a> defines a light and versatile
general-purpose language, dedicated to be embedded in any application. Sounds
like a viable solution: if you can help with it, your contribution is
welcome!<br />
We did also take into consideration <a href="http://www.python.org">Python</a>
and <a href="http://www.ruby-lang.org">Ruby</a> but both are now far from
light, and are not meant to be embedded, since they are general-purpose
languages, with a huge set of full-featured packages.</p>
<p>Then, there is <em>JavaScript</em>:</p>
<ul>
<li>This is the <em>World Wide Web</em> assembler. Every programmer in one way
or another knows <em>JavaScript</em>.</li>
<li><em>JavaScript</em> can be a very powerful language - see Crockford's book
<a href="http://shop.oreilly.com/product/9780596517748.do">"<em>JavaScript -
The Good Parts</em>"</a>;</li>
<li>There are a huge number of libraries written in <em>JavaScript</em>:
template engines (jade, mustache...), SOAP and LDAP clients, and many others
(including all <code>node.js</code> libraries of course);</li>
<li>It was the base for some strongly-typed syntax extensions, like
<em><a href="http://coffeescript.org/">CoffeScript</a>, <a href="http://www.typescriptlang.org/">TypeScript</a>, <a href="https://www.dartlang.org/">Dart</a></em>;</li>
<li>In case of <em>AJAX</em> / <em>Rich Internet Application</em> we can
directly share part of logic between client and server (validation, template
rendering...) without any middle-ware;</li>
<li>One long time <em>mORMot</em>'s user (Pavel, aka <em>mpv</em>) already
integrated <em>SpiderMonkey</em> to <em>mORMot</em>'s core. His solution is
used on production to serve billion of requests per day, with success. We
officially integrated his units.<br />
Thanks a lot, Pavel!</li>
</ul>
<p>As a consequence, <em>mORMot</em> introduced direct <em>JavaScript</em>
support via <em>SpiderMonkey</em>.<br />
It allows to:</p>
<ul>
<li>Execute <em>Delphi</em> code from <em>JavaScript</em> - including our ORM
or SOA methods, or even reporting;</li>
<li>Consume <em>JavaScript</em> code from <em>Delphi</em> (e.g. to define and
customize any service or rule, or use some existing <code>.js</code>
library);</li>
<li>Expose <em>JavaScript</em> objects and functions via a
<code>TSMVariant</code> custom variant type: it allows to access any
<em>JavaScript</em> object properties or call any of its functions via
late-binding, from your <em>Delphi</em> code, just as if it was written in
native Object-Pascal;</li>
<li>Follow a classic synchronous blocking pattern, rooted on <a href="https://blog.synopse.info?post/post/2013/09/10/Thread-safety-of-mORMot"><em>mORMot</em>'s multi-thread
efficient model</a>, easy to write and maintain;</li>
<li>Handle <em>JavaScript</em> or <em>Delphi</em> objects as UTF-8 JSON, ready
to be published or consumed via <em>mORMot</em>'s <a href="https://blog.synopse.info?post/post/2014/01/10/RESTful-mORMot">RESTful Client-Server remote access</a>.</li>
</ul>
<h3>SpiderMonkey integration</h3>
<h3>A powerful JavaScript engine</h3>
<p><em><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey">SpiderMonkey</a></em>,
the Mozilla <em>JavaScript</em> engine, can be embedded in your <em>mORMot</em>
application. It could be used on client side, within a Delphi application (e.g.
for reporting), but the main interest of it may be on the server side.</p>
<p>The word <em>JavaScript</em> may bring to mind features such as event
handlers (like <code>onclick</code>), <code>DOM</code> objects,
<code>window.open</code>, and <code>XMLHttpRequest</code>.<br />
But all of these features are actually not provided by the
<em>SpiderMonkey</em> engine itself.</p>
<p><em>SpiderMonkey</em> provides a few core <em>JavaScript</em> data
types—numbers, strings, Arrays, Objects, and so on—and a few methods, such as
<code>Array.push</code>. It also makes it easy for each application to expose
some of its own objects and functions to <em>JavaScript</em> code. Browsers
expose DOM objects. Your application will expose objects that are relevant for
the kind of scripts you want to write. It is up to the application developer to
decide what objects and methods are exposed to scripts.</p>
<h3>Direct access to the SpiderMonkey API</h3>
<p>The <code>SynSMAPI.pas</code> unit is a tuned conversion of the
<em>SpiderMonkey</em> API, providing full <em>ECMAScript 5</em> support and
<em>JIT</em>.<br />
You could take a look at <a href="http://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey">the
full description of this low-level API</a>.</p>
<p>But the <code>SynSM.pas</code> unit will encapsulate most of it into higher
level Delphi classes and structures (including a custom <code>variant</code>
type), so you probably won't need to use <code>SynSMAPI.pas</code> directly in
your code:</p>
<table>
<tbody>
<tr>
<td><strong>Type</strong></td>
<td><strong>Description</strong></td>
</tr>
<tr>
<td><code>TSMEngineManager</code></td>
<td>main access point to the <em>SpiderMonkey</em> per-thread scripting
engines</td>
</tr>
<tr>
<td><code>TSMEngine</code></td>
<td>implements a Thread-Safe <em>JavaScript</em> engine instance</td>
</tr>
<tr>
<td><code>TSMObject</code></td>
<td>wrap a <em>JavaScript</em> object and its execution context</td>
</tr>
<tr>
<td><code>TSMValue</code></td>
<td>wrap a <em>JavaScript</em> value, and interfaces it with Delphi types</td>
</tr>
<tr>
<td><code>TSMVariant</code> /<br />
<code>TSMVariantData</code></td>
<td>define a custom <code>variant</code> type, for direct access to any
<em>JavaScript</em> object, with late-binding</td>
</tr>
</tbody>
</table>
<p>We will see know how to work with all those classes.</p>
<h3>Execution scheme</h3>
<p>The <em>SpiderMonkey JavaScript engine</em> compiles and executes scripts
containing <em>JavaScript</em> statements and functions. The engine handles
memory allocation for the objects needed to execute scripts, and it cleans
up—garbage collects—objects it no longer needs.</p>
<p>In order to run any <em>JavaScript</em> code in <em>SpiderMonkey</em>, an
application must have three key elements:</p>
<ol>
<li>A <code>JSRuntime</code>,</li>
<li>A <code>JSContext</code>,</li>
<li>And a <em>global</em> <code>JSObject</code>.</li>
</ol>
<p>A <code>JSRuntime</code>, or runtime, is the space in which the JavaScript
variables, objects, scripts, and contexts used by your application are
allocated. Every <code>JSContext</code> and every object in an application
lives within a <code>JSRuntime</code>. They cannot travel to other runtimes or
be shared across runtimes.</p>
<p>A <code>JSContext</code>, or context, is like a little machine that can do
many things involving <em>JavaScript</em> code and objects. It can compile and
execute scripts, get and set object properties, call <em>JavaScript</em>
functions, convert <em>JavaScript</em> data from one type to another, create
objects, and so on.</p>
<p>Lastly, the <em>global</em> <code>JSObject</code> is a <em>JavaScript</em>
object which contains all the classes, functions, and variables that are
available for <em>JavaScript</em> code to use. Whenever a web browser code does
something like <code>window.open("http://www.mozilla.org/")</code>, it is
accessing a global property, in this case <code>window</code>.
<em>SpiderMonkey</em> applications have full control over what global
properties scripts can see.</p>
<p>Every <em>SpiderMonkey</em> instance starts out every execution context by
creating its <code>JSRunTime</code>, <code>JSContext</code> instances, and a
global <code>JSObject</code>. It populates this global object with the standard
<em>JavaScript</em> classes, like <code>Array</code> and <code>Object</code>.
Then application initialization code will add whatever custom classes,
functions, and variables (like <code>window</code>) the application wants to
provide; it may be, for a <em>mORMot</em> server application, ORM access or SOA
services consumption and/or implementation.</p>
<p>Each time the application runs a <em>JavaScript</em> script (using, for
example, <code>JS_EvaluateScript</code>), it provides the global object for
that script to use. As the script runs, it can create global functions and
variables of its own. All of these functions, classes, and variables are stored
as properties of the global object.</p>
<h3>Creating your execution context</h3>
<p>The main point about those three key elements is that, in the current
implementation pattern of <em>SpiderMonkey</em>, runtime, context or global
objects are <em>not thread-safe</em>.</p>
<p>Therefore, in the <em>mORMot</em>'s use of this library, each thread will
have its own instance of each.</p>
<p>In the <code>SynSM.pas</code> unit, a <code>TSMEngine</code> class has been
defined to give access to all those linked elements:</p>
<pre>
TSMEngine = <strong>class</strong>
...
<em>/// access to the associated global object as a TSMVariant custom variant</em>
<em>// - allows direct property and method executions in Delphi code, via</em>
<em>// late-binding</em>
<strong>property</strong> Global: <strong>variant read</strong> FGlobal;
<em>/// access to the associated global object as a TSMObject wrapper</em>
<em>// - you can use it to register a method</em>
<strong>property</strong> GlobalObject: TSMObject <strong>read</strong> FGlobalObject;
<em>/// access to the associated global object as low-level PJSObject</em>
<strong>property</strong> GlobalObj: PJSObject <strong>read</strong> FGlobalObject.fobj;
<em>/// access to the associated execution context</em>
<strong>property</strong> cx: PJSContext <strong>read</strong> fCx;
<em>/// access to the associated execution runtime</em>
<strong>property</strong> rt: PJSRuntime <strong>read</strong> frt;
...
</pre>
<p>Our implementation will define one Runtime, one Context, and one global
object per thread, i.e. one <code>TSMEngine</code> class instance per
thread.</p>
<p>A <code>JSRuntime</code>, or runtime, is created for each
<code>TSMEngine</code> instance. In practice, you won't need access to this
value, but rely either on a <code>JSContext</code> or directly a
<code>TSMEngine</code>.</p>
<p>A <code>JSContext</code>, or context, will be the main entry point of all
<em>SpiderMonkey</em> API, which expect this context to be supplied as
parameter. In mORMot, you can retrieve the running <code>TSMEngine</code> from
its context by using the <code>function TSMObject.Engine: TSMEngine</code> - in
fact, the engine instance is stored in the <em>private data</em> slot of each
<code>JSContext</code>.</p>
<p>Lastly, the <code>TSMEngine</code>'s <em>global object</em> contains all the
classes, functions, and variables that are available for JavaScript code to
use. For a <em>mORMot</em> server application, ORM access or SOA services
consumption and/or implementation, as stated above.</p>
<p>You can note that there are several ways to access this global object
instance, from high-level to low-level <em>JavaScript</em> object types. The
<code>TSMEngine.Global</code> property above is in fact a <code>variant</code>.
Our <code>SynSM.pas</code> unit defines in fact a custom <code>variant</code>
type, identified as the <code>TSMVariant class</code>, able to access any
JavaScript object via late-binding, for both variables and functions:</p>
<pre>
engine.Global.MyVariable := 1.0594631;
engine.Global.MyFunction(1,'text');
</pre>
<p>Most web applications only need one runtime, since they are running in a
single thread - and (ab)use of callbacks for non-blocking execution. But in
<em>mORMot</em>, you will have one <code>TMSEngine</code> instance per thread,
using the <code>TSMEngineManager.ThreadSafeEngine</code> method. Then all
execution may be blocking, without any noticeable performance issue, since the
whole <em>mORMot</em> threading design was defined to maximize execution
resources.</p>
<h3>Blocking threading model</h3>
<p>This threading model is the big difference with other server-side scripting
implementation schemes, e.g. the well-known <code>node.js</code> solution.</p>
<p>Multi-threading is not evil, when properly used. And thanks to the
<em>mORMot</em>'s design, you won't be afraid of writing <em>blocking</em>
JavaScript code, without any callbacks. In practice, those callbacks are what
makes most <em>JavaScript</em> code difficult to maintain.</p>
<p>On the client side, i.e. in a web browser, the <em>JavaScript</em> engine
only uses one thread per web page, then uses callbacks to defer execution of
long-running methods (like a remote HTTP request).<br />
If fact, this is one well identified performance issue of modern AJAX
applications. For instance, it is not possible to perform some intensive
calculation in <em>JavaScript</em>, without breaking the web application
responsiveness: you have to split your computation task in small tasks, then
let the <em>JavaScript</em> code pause, until a next piece of computation could
be triggered... On server side, <code>node.js</code> allows to define <a href="http://github.com/laverdet/node-fibers"><em>Fibers</em> and
<em>Futures</em></a> - but this is not available on web clients. Some browsers
did only start to uncouple the <em>JavaScript</em> execution thread from the
HTML rendering thread - and even this is hard to fix... we reached here the
limit of a technology rooted in the 80's...</p>
<p>On the server side, <code>node.js</code> did follow this pattern, which did
make sense (it allows to share code with the client side, with some name-space
tricks), but it is also IMHO a big waste of resources. Why should we stick to
an implementation pattern inherited from the 80's computing model, when all
CPUs were mono core, and threads were not available?</p>
<p>The main problem when working with one single thread, is that your code
shall be asynchronous. Soon or later, you will face a syndrome known as
"<em>Callback Hell</em>". In short, you are nesting anonymous functions, and
define callbacks. The main issue, in addition to lower readability and being
potentially sunk into <code>function()</code> nesting, is that you just lost
the <em>JavaScript</em> exception model. In fact, each callback function has to
explicitly check for the error (returned as a parameter in the callback
function), and handle it.</p>
<p>Of course, you can use so-called <em>Promises</em> and some nice libraries -
mainly <code>async.js</code>.<br />
But even those libraries add complexity, and make code more difficult to write.
For instance, consider the following non-blocking/asynchronous code:</p>
<pre>
getTweetsFor("domenic") <em>// promise-returning function</em>
.then(function (tweets) {
<strong>var</strong> shortUrls = parseTweetsForUrls(tweets);
<strong>var</strong> mostRecentShortUrl = shortUrls[0];
<strong>return</strong> expandUrlUsingTwitterApi(mostRecentShortUrl); <em>// promise-returning function</em>
})
.then(httpGet) <em>// promise-returning function</em>
.then(
function (responseBody) {
console.log("Most recent link text:", responseBody);
},
function (error) {
console.error("Error with the twitterverse:", error);
}
);
</pre>
<p>Taken from <a href="http://domenic.me/2012/10/14/youre-missing-the-point-of-promises">this web
site</a>.</p>
<p>This kind of code will be perfectly readable for a <em>JavaScript</em> daily
user, or someone fluent with functional languages.</p>
<p>But the following blocking/synchronous code may sound much more familiar,
safer and less verbose, to most Delphi / Java / C# programmer:</p>
<pre>
<strong>try</strong> {
<strong>var</strong> tweets = getTweetsFor("domenic"); <em>// blocking</em>
<strong>var</strong> shortUrls = parseTweetsForUrls(tweets);
<strong>var</strong> mostRecentShortUrl = shortUrls[0];
<strong>var</strong> responseBody = httpGet(expandUrlUsingTwitterApi(mostRecentShortUrl)); <em>// blocking x 2</em>
console.log("Most recent link text:", responseBody);
} <strong>catch</strong> (error) {
console.error("Error with the twitterverse: ", error);
}
</pre>
<p>Thanks to the blocking pattern, it becomes obvious that code readability and
maintainability is as high as possible, and error detection is handled nicely
via <em>JavaScript</em> exceptions, and a global <code>try .. catch</code>.</p>
<p>Last but not least, debugging blocking code is easy and straightforward,
since the execution will be linear, following the code flow.</p>
<p>Upcoming ECMAScript 6 should go even further thanks to the
<code>yield</code> keyword and some task generators - see <a href="http://taskjs.org">taskjs</a> - so that asynchronous code may become closer to
the synchronous pattern. But even with <code>yield</code>, your code won't be
as clean as with plain blocking style.</p>
<p>In <em>mORMot</em>, we did choose to follow an alternate path, i.e. write
blocking synchronous code. Sample above shows how easier it is to work with. If
you use it to define some huge business logic, or let a domain expert write the
code, blocking syntax is much more straightforward.</p>
<p>Of course, <em>mORMot</em> allows you to use callbacks and functional
programming pattern in your <em>JavaScript</em> code, if needed. But by
default, you are allowed to write KISS blocking code.</p>
<h3>Interaction with existing code</h3>
<p>Within <em>mORMot</em> units, you can mix <em>Delphi</em> and
<em>JavaScript</em> code by two ways:</p>
<ul>
<li>Either define your own functions in <em>Delphi</em> code, and execute them
from <em>JavaScript</em>; </li>
<li>Or define your own functions in <em>JavaScript</em> code (including any
third-party library), and execute them from <em>Delphi</em>.</li>
</ul>
<p>Like for other part of our framework, performance and integration has been
tuned, to follow our KISS way.</p>
<p>You can take a look at "<code>22 - JavaScript HTTPApi web
server\JSHttpApiServer.dpr</code>" sample for reference code.</p>
<h3>Proper engine initialization</h3>
<p>As was previously stated, the main point to interface the
<em>JavaScript</em> engine is to <em>register</em> all methods when the
<code>TSMEngine</code> instance is initialized.</p>
<p>For this, you set the corresponding <code>OnNewEngine</code> callback event
to the main <code>TSMEngineManager</code> instance.<br />
See for instance, in the sample code:</p>
<pre>
<strong>constructor</strong> TTestServer.Create(<strong>const</strong> Path: TFileName);
<strong>begin</strong>
...
fSMManager := TSMEngineManager.Create;
fSMManager.OnNewEngine := DoOnNewEngine;
...
</pre>
<p>In <code>DoOnNewEngine</code>, you will initialize every newly created
<code>TSMEngine</code> instance, to register all needed Delphi methods and
prepare access to <em>JavaScript</em> via the runtime's global
<code>JSObject</code>.</p>
<p>Then each time you want to access the <em>JavaScript</em> engine, you will
write for instance:</p>
<pre>
<strong>function</strong> TTestServer.Process(Ctxt: THttpServerRequest): cardinal;
<strong>var</strong> engine: TSMEngine;
...
engine := fSMManager.ThreadSafeEngine;
... <em>// now you can use engine, e.g. engine.Global.someMethod()</em>
</pre>
<p>Each thread of the HTTP server thread-pool will be initialized on the fly if
needed, or the previously initialized instance will be quickly returned
otherwise.</p>
<p>Once you have the <code>TSMEngine</code> instance corresponding to the
current thread, you can launch actions on its global object, or tune its
execution.<br />
For instance, it could be a good idea to check for the <em>JavaScript</em> VM's
garbage collection:</p>
<pre>
<strong>function</strong> TTestServer.Process(Ctxt: THttpServerRequest): cardinal;
...
engine := fSMManager.ThreadSafeEngine;
engine.MaybeGarbageCollect; <em>// perform garbage collection if needed</em>
...
</pre>
<p>We will now find out how to interact between <em>JavaScript</em> and
<em>Delphi</em> code.</p>
<h3>Calling Delphi code from JavaScript</h3>
<p>In order to call some <em>Delphi</em> method from <em>JavaScript</em>, you
will have to register the method.<br />
As just stated, it is done by setting a callback within
<code>TSMEngineManager.OnNewEngine</code> initialization code. For
instance:</p>
<pre>
<strong>procedure</strong> TTestServer.DoOnNewEngine(<strong>const</strong> Engine: TSMEngine);
...
<em>// add native function to the engine</em>
Engine.RegisterMethod(Engine.GlobalObj,'loadFile',LoadFile,1);
<strong>end</strong>;
</pre>
<p>Here, the local <code>LoadFile()</code> method is implemented as such in
native code:</p>
<pre>
<strong>function</strong> TTestServer.LoadFile(<strong>const</strong> This: <strong>variant</strong>; <strong>const</strong> Args: <strong>array of variant</strong>): <strong>variant</strong>;
<strong>begin</strong>
<strong>if</strong> length(Args)<>1 <strong>then</strong>
<strong>raise</strong> Exception.Create('Invalid number of args for loadFile(): required 1 (file path)');
result := AnyTextFileToSynUnicode(Args[0]);
<strong>end</strong>;
</pre>
<p>As you can see, this is perfectly easy to follow.<br />
Its purpose is to load a file content from <em>JavaScript</em>, by defining a
new global function named <code>loadFile()</code>.<br />
Remember that the <em>SpiderMonkey</em> engine, by itself, does not know
anything about file system, database or even DOM. Only basic objects were
registered, like arrays. We have to explicitly register the functions needed by
the <em>JavaScript</em> code.</p>
<p>In the above code snippet, we used the
<code>TSMEngineMethodEventVariant</code> callback signature, marshaling
<code>variant</code> values as parameters. This is the easiest method, with
only a slight performance impact.</p>
<p>Such methods have the following features:</p>
<ul>
<li>Arguments will be transmitted from <em>JavaScript</em> values as simple
<em>Delphi</em> types (for numbers or text), or as our custom
<code>TSMVariant</code> type for <em>JavaScript</em> objects, which allows
late-binding;</li>
<li>The <code>This: variant</code> first parameter map the "callee"
<em>JavaScript</em> object as a <code>TSMVariant</code> custom instance, so
that you would be able to access the other object's methods or properties
directly via late-binding;</li>
<li>You can benefit of the <em>JavaScript</em> feature of variable number of
arguments when calling a function, since the input arguments is a dynamic array
of <code>variant</code>;</li>
<li>All those registered methods are registered in a list maintained in the
<code>TSMEngine</code> instance, so it could be pretty convenient to work with,
in some cases;</li>
<li>You can still access to the low-level <em>JSObject</em> values of any the
argument, if needed, since they can be trans-typed to a
<code>TSMVariantData</code> instance (see below) - so you do not loose any
information;</li>
<li>The <em>Delphi</em> native method will be protected by the <em>mORMot</em>
wrapper, so that any exception raised within the process will be catch and
transmitted as a <em>JavaScript</em> exception to the runtime;</li>
<li>There is also an hidden set of the FPU exception mask during execution of
native code (more on it later on) - you should not bother on it here.</li>
</ul>
<p>Now consider how you should have written the same <code>loadFile()</code>
function via low-level API calls.</p>
<p>First, we register the callback:</p>
<pre>
<strong>procedure</strong> TTestServer.DoOnNewEngine(<strong>const</strong> Engine: TSMEngine);
...
<em>// add native function to the engine</em>
Engine.GlobalObject.DefineNativeMethod('loadFile', nsm_loadFile, 1);
<strong>end</strong>;
</pre>
<p>Then its implementation:</p>
<pre>
<strong>function</strong> nsm_loadFile(cx: PJSContext; argc: uintN; vp: Pjsval): JSBool; <strong>cdecl</strong>;
<strong>var</strong> in_argv: PjsvalVector;
filePath: TFileName;
<strong>begin</strong>
TSynFPUException.ForDelphiCode;
<strong>try</strong>
<strong>if</strong> argc<>1 <strong>then</strong>
<strong>raise</strong> Exception.Create('Invalid number of args for loadFile(): required 1 (file path)');
in_argv := JS_ARGV(cx,vp);
filePath := JSVAL_TO_STRING(in_argv[0]).ToString(cx);
JS_SET_RVAL(cx, vp, cx^.NewJSString(AnyTextFileToSynUnicode(filePath)).ToJSVal);
Result := JS_TRUE;
<strong>except</strong>
on E: Exception <strong>do begin</strong> <em>// all exceptions MUST be catched on Delphi side</em>
JS_SET_RVAL(cx, vp, JSVAL_VOID);
JSError(cx, E);
Result := JS_FALSE;
<strong>end</strong>;
<strong>end</strong>;
<strong>end</strong>;
</pre>
<p>As you can see, this <code>nsm_loadFile()</code> function is much more
difficult to follow:</p>
<ul>
<li>Your code shall begin with a cryptic
<code>TSynFPUException.ForDelphiCode</code> instruction, to protect the FPU
exception flag during execution of native code (<em>Delphi</em> RTL expects its
own set of FPU exception mask during execution, which does not match the FPU
exception mask expected by <em>SpiderMonkey</em>);</li>
<li>You have to explicitly catch any Delphi exception which may raise, with a
<code>try...finally</code> block, and marshal them back as <em>JavaScript</em>
errors;</li>
<li>You need to do a lot of manual low-level conversions - via
<code>JS_ARGV()</code> then e.g. <code>JSVAL_TO_STRING()</code> macros - to
retrieve the actual values of the arguments;</li>
<li>And the returning function is to be marshaled by hand - see the
<code>JS_SET_RVAL()</code> line.</li>
</ul>
<p>Since the <code>variant</code>-based callback has only a slight performance
impact (nothing measurable, when compared to the <em>SpiderMonkey</em> engine
performance itself), and still have access to all the transmitted information,
we strongly encourage you to use this safer and cleaner pattern, and do not
define any native function via low-level API.</p>
<p>Note that there is an alternate JSON-based callback, which is not to be used
in your end-user code, but will be used when marshaling to JSON is needed, e.g.
when working with <em>mORMot</em>'s ORM or SOA features.</p>
<h3>TSMVariant custom type</h3>
<p>As stated above, the <code>SynSM.pas</code> unit defines a
<code>TSMVariant</code> custom variant type. It will be used by the unit to
marshal any <em>JSObject</em> instance as variant.</p>
<p>Via the magic of late-binding, it will allow access of any
<em>JavaScript</em> object property, or execute any of its functions. Only with
a slightly performance penalty, but with much better code readability than with
low-level access of the <em>SpiderMonkey</em> API.</p>
<p>The <code>TSMVariantData</code> memory structure can be used to map such a
<code>TSMVariant</code> variant instance. In fact, the custom variant type will
store not only the <em>JSObject</em> value, but also its execution context -
i.e. <em>JSContext</em> - so is pretty convenient to work with.</p>
<p>For instance, you may be able to write code as such:</p>
<pre>
<strong>function</strong> TMyClass.MyFunction(<strong>const</strong> This: <strong>variant</strong>; <strong>const</strong> Args: <strong>array of variant</strong>): <strong>variant</strong>;
<strong>var</strong> global: <strong>variant</strong>;
<strong>begin</strong>
TSMVariantData(This).GetGlobal(global);
global.anotherFunction(Args[0],Args[1],'test');
<em>// same as:</em>
global := TSMVariantData(This).SMObject.Engine.Global;
global.anotherFunction(Args[0],Args[1],'test');
<em>// but you may also write directly:</em>
<strong>with</strong> TSMVariantData(This).SMObject.Engine <strong>do</strong>
Global.anotherFunction(Args[0],Args[1],'test');
result := AnyTextFileToSynUnicode(Args[0]);
<strong>end</strong>;
</pre>
<p>Here, the <code>This</code> custom variant instance is trans-typed via
<code>TSMVariantData(This)</code> to access its internal properties.</p>
<h3>Calling JavaScript code from Delphi</h3>
<p>In order to execute some <em>JavaScript</em> code from <em>Delphi</em>, you
should first define the <em>JavaScript</em> functions to be executed.<br />
This shall take place within <code>TSMEngineManager.OnNewEngine</code>
initialization code:</p>
<pre>
<strong>procedure</strong> TTestServer.DoOnNewEngine(<strong>const</strong> Engine: TSMEngine);
<strong>var</strong> showDownRunner: SynUnicode;
<strong>begin</strong>
<em>// add external JavaScript library to engine (port of the Markdown library)</em>
Engine.Evaluate(fShowDownLib, 'showdown.js');
<em>// add the bootstrap function calling loadfile() then showdown's makeHtml()</em>
showDownRunner := AnyTextFileToSynUnicode(ExeVersion.ProgramFilePath+'showDownRunner.js');
Engine.Evaluate(showDownRunner, 'showDownRunner.js');
...
</pre>
<p>This code first <em>evaluates</em> (i.e. "executes") a general-purpose
<em>JavaScript</em> library contained in the <code>showdown.js</code> file,
available in the sample executable folder. This is an open source library able
to convert any <em>Markdown</em> markup into HTML. Plain standard
<em>JavaScript</em> code.</p>
<p>Then we <em>evaluate</em> (i.e. "execute") a small piece of
<em>JavaScript</em> code, to link the <code>makeHtml()</code> function of the
just defined library with our <code>loadFile()</code> native function:</p>
<pre>
function showDownRunner(pathToFile){
<strong>var</strong> src = loadFile(pathToFile); <em>// call Delphi native code</em>
<strong>var</strong> converter = <strong>new</strong> Showdown.converter(); <em>// get the Showdown converted</em>
<strong>return</strong> converter.makeHtml(src); <em>// convert .md content into HTML via showdown.js</em>
}
</pre>
<p>Now we have a new global <code>function showDownRunner(pathToFile)</code> at
hand, ready to be executed by our <em>Delphi</em> code:</p>
<pre>
<strong>function</strong> TTestServer.Process(Ctxt: THttpServerRequest): cardinal;
<strong>var</strong> content: <strong>variant</strong>;
FileName, FileExt: TFileName;
engine: TSMEngine;
...
<strong>if</strong> FileExt='.md' <strong>then begin</strong>
...
engine := fSMManager.ThreadSafeEngine;
...
content := engine.Global.showDownRunner(FileName);
...
</pre>
<p>As you can see, we access the function via late-binding. Above code is
perfectly readable, and we call here a <em>JavaScript</em> function and a whole
library as natural as if it was native code.</p>
<p>Without late-binding, we may have written, accessing not the <code>Global
TSMVariant</code> instance, but the lower level <code>GlobalObject:
TSMObject</code> property:</p>
<pre>
...
content := engine.GlobalObject.Run('showDownRunner',[SynUnicode(FileName)]);
...
</pre>
<p>It is up to you to choose which kind of code you prefer, but late-binding is
worth considering.</p>
<p>Next step on our side is to directly allow access to <em>mORMot</em>'s ORM
and SOA features, including interface-based services.<br />
Feedback is <a href="http://synopse.info/forum/viewtopic.php?pid=10198#p10198">welcome on our
forum</a>, as usual.</p>