First of all, this change is for the universal backend. It is more of a temporary kludge to experiment with dynamically loading files generated by the universal backend. Here’s how it works…
At the end of the link file, the Gambit linker generates a table of the Scheme modules required by the program. The table is passed as a parameter to the g_module_registry_init function of the Gambit runtime system. For example, the link command:
gsc -link -l lib/_gambit.js a.js b.js
will generate at the end of the link file “b_.js”:
g_module_registry_init([new G_ModLinkInfo("_gambit",0), new G_ModLinkInfo("a",1), new G_ModLinkInfo("b",2)]);
At then end of a file generated for the compilation of a module there is a call to a runtime system function that registers the module in the module registry. For example, when “a.scm” is compiled with the command “gsc -target js -c a.scm”, the file “a.js” generated ends with:
g_module_register([g_make_interned_symbol("a"),g_bb1__20_a,1,null,false]);
The parameter of g_module_register is a “module descriptor” giving information on the module: name, main function (g_bb1__20_a), preload flag (1), module meta information (null), and a dummy field (false) that is only interesting for the C backend.
So when the files of generated code are loaded (incrementally) by the JS VM, g_module_register accumulates the modules in a table. When all the modules required by the program have beed loaded (in the example, _gambit, a and b) the function g_module_register will call the main function of the first module. This will run the Scheme code at the toplevel of the first module.
The first module contains a loop, in Scheme, that iterates over the module registry and calls the main function of each of the remaining modules in the registry (this information is available in Scheme in the global variable ##program-descr). Specifically, the first module is the Gambit universal library (lib/_univlib.scm), which ends with a call to ##load-vm (a procedure defined in lib/_kernel.scm that runs each module other than the first).
What my most recent change does is extend g_module_register so that any subsequent module that is loaded by the JS VM causes its immediate execution by calling the module's main function. In the C backend the mechanism is slightly different (and more flexible): the ##load-object-file procedure of the Gambit runtime library constructs a vector of module descriptors and this vector is passed to ##register-module-descrs-and-load! (defined in lib/_kernel.scm) which then runs the modules. It is more flexible because the loaded object file can contain more than one module, and also control goes through Scheme before the module is executed.
Marc
On Jan 3, 2016, at 2:54 AM, Adam adam.mlmb@gmail.com wrote:
Marc,
Wait, what's the point?
That is effectively C modules' default behavior isn't it? (even if it's the runtime or linker-generated code that actually does the actual run command.)
So this way you create symmetrical behavior but the actual run command is in the module file itself rather than in runtime/linker -
Is there any disadvantage.. could be, no, like, I need more modules loaded at the same time and with this model you introduced now, the global namespace in JS files is evaluated at load time so could cause a problem?
2016-01-03 14:10 GMT+08:00 Marc Feeley feeley@iro.umontreal.ca: I’ve just pushed a small change to the module registration method. Now, loading the generated .js file will run that file.
Marc
On Jan 2, 2016, at 11:39 PM, Blake McBride blake@mcbride.name wrote:
Yes, and I use jQuery get and html methods to load the html part.
On Sat, Jan 2, 2016 at 8:43 PM, Marc Feeley feeley@iro.umontreal.ca wrote: That’s an interesting app!
As I said, it shouldn’t be hard to do. In your app, how do you load new JS code? Are you using jQuery and the getScript method?
Marc
On Jan 2, 2016, at 8:42 PM, Blake McBride blake@mcbride.name wrote:
Hi Marc,
Thanks for the reply.
I started development of a Web based business application in 2006. The app is in production use. The front-end is about 450 screens written in Flash. The back-end is written in Java (over 9,000 classes) on top of an SQL database with 250 tables accessed through Hibernate. The front-end and back-end communicate through SOAP web services.
Since Flash is now dead, I need to re-write at least parts of the front-end in JS to support tablets and phones at least. I have front-end JS code that communicates with the back-end SOAP Web services so I theoretically wouldn't need to touch the back-end.
I have a preference to utilize scheme if I can, but the back-end is already written and works. It doesn't make sense to mess with it given the investment.
The problems I had with Gambit (as well as many other systems) is as follows:
Once a user logs in, my app has a consistent visual / UI framework. Beyond this framework, there are 450 different screens that appear within the framework (like in one of the div's).
A. I cannot load all 450 screen at system startup. I must lazy load them as they are called up.
B. The initial UI framework code should contain all of the Gambit machinery and libraries so that each of the 450 screens can be small, light-weight, compiled separately yet have full access to the entire Gambit facility.
With respect, my investigation into Gambit as a possible solution in the past led me to the conclusions at that time it was not possible to produce separately compiled modules that leveraged off of a single main module for the Gambit machinery and libraries.
I suppose some time has passed and I wanted to see if the situation has changed. I can only use Gambit if:
I can compile and load each screen separately
I only have to have one copy of the Gambit machinery and library so that the separate screens are small.
Thank you.
Blake
On Sat, Jan 2, 2016 at 7:06 PM, Marc Feeley feeley@iro.umontreal.ca wrote: asmjs is cool and Gambit has already been compiled to it thanks to emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
Afficher les réponses par date
2016-01-03 14:10 GMT+08:00 Marc Feeley feeley@iro.umontreal.ca:
I’ve just pushed a small change to the module registration method. Now, loading the generated .js file will run that file.
Marc
What do you mean, that (load "abc.js") will load and evaluate abc.js?
2016-01-03 21:42 GMT+08:00 Marc Feeley feeley@iro.umontreal.ca:
First of all, this change is for the universal backend. It is more of a temporary kludge to experiment with dynamically loading files generated by the universal backend. Here’s how it works…
At the end of the link file, the Gambit linker generates a table of the Scheme modules required by the program. The table is passed as a parameter to the g_module_registry_init function of the Gambit runtime system. For example, the link command:
gsc -link -l lib/_gambit.js a.js b.js
will generate at the end of the link file “b_.js”:
g_module_registry_init([new G_ModLinkInfo("_gambit",0), new G_ModLinkInfo("a",1), new G_ModLinkInfo("b",2)]);
At then end of a file generated for the compilation of a module there is a call to a runtime system function that registers the module in the module registry. For example, when “a.scm” is compiled with the command “gsc -target js -c a.scm”, the file “a.js” generated ends with:
g_module_register([g_make_interned_symbol("a"),g_bb1__ 20_a,1,null,false]);
The parameter of g_module_register is a “module descriptor” giving information on the module: name, main function (g_bb1__20_a), preload flag (1), module meta information (null), and a dummy field (false) that is only interesting for the C backend.
So when the files of generated code are loaded (incrementally) by the JS VM, g_module_register accumulates the modules in a table. When all the modules required by the program have beed loaded (in the example, _gambit, a and b) the function g_module_register will call the main function of the first module. This will run the Scheme code at the toplevel of the first module.
The first module contains a loop, in Scheme, that iterates over the module registry and calls the main function of each of the remaining modules in the registry (this information is available in Scheme in the global variable ##program-descr). Specifically, the first module is the Gambit universal library (lib/_univlib.scm), which ends with a call to ##load-vm (a procedure defined in lib/_kernel.scm that runs each module other than the first).
What my most recent change does is extend g_module_register so that any subsequent module that is loaded by the JS VM causes its immediate execution by calling the module's main function. In the C backend the mechanism is slightly different (and more flexible): the ##load-object-file procedure of the Gambit runtime library constructs a vector of module descriptors and this vector is passed to ##register-module-descrs-and-load! (defined in lib/_kernel.scm) which then runs the modules. It is more flexible because the loaded object file can contain more than one module, and also control goes through Scheme before the module is executed.
Marc
What was the point here, to give insight into how JS-side module loading and internal registration works?