Node port - well, actually "general port"

44 views
Skip to first unread message

Ingwie Phoenix

unread,
Nov 6, 2015, 1:51:44 PM11/6/15
to objec...@googlegroups.com
Hello there folks!

I know I know, I bugged you guys before. And, I am aware that there has been a development process as well.

What I want to provide here is a concept on how this could be implemented. Because last thing I had read was that there were issues with the "idea on how to implement".

So, I would like to suggest that concept. You can throw it in the bin and ignore it if you want, or you can use parts of it. I just feel really motivated right now and thought I’d let my thoughts go free here. The other Objective-C javaScript dialect, OJ, is getting ES6 support soon, so I hoped ObjJ might too.

Anyway, here it is.


So generally, when working in NodeJS, we are struck with the same kind of context that we would have in a browser; but it is not the same. The main and biggest difference is, that the Document is missing and the Window object doesn’t have all the properties we might want - well, it is called "global" instead too.

The first thing that ObjJ would have to learn is to be as less-dependant on the browser as it could be. That would allow it to run in virtually any environment. This would be one important step to work within NodeJS.

But maybe you have noticed that I have spoken vaguely of the two - browser and node - at the same time. Because what ObjJ, liek OJ, uses, is a runtime. While OJ is actually compiled and sent ot the browser with a separate runtime, last thing that I remember is that ObjJ is not.

So:
1. We need a kind of runtime.

The next thing we may encounter is, that the browser doesnt have it, Node has it, but many "bundlers" do provide it. There is a vast difference between the three. What I usually do when I am transpiling OJ code for JavaScript, I prepend a line of code to load the OJ runtime into current scope, or create one if none exists. Obviously, the fact that a bundler may throw files everywhere and anywhere is a problem.

Consider the following scenario:



//- Derp.j
    @implementation Derp
    +(id)init {
        [super init];
        // run logic...
    }
    @end

//- App.j
    function main() {
        var app = [[Derp alloc] init];
    }

You may noticed, that App.j doesn’t pull in Derp.j - because, how should it? Using regular JS within Node or a standard bundler, you would use something like:

    var Derp = require("./Derp");

But ObjJ currently doesnt allow that. So there is something that ObjJ needs to learn before it can "really" run on node without having to be pre-compiled:

2. Allow modularization.

But, do we really need a runtime? If we transpiled the code into a JS code, which wouldn’t rely on a real runtime, we would actually be able to easily share code between files - since it doesn’t matter when a file gets executed. Here is an example from OJ which actually happened in my real project:


//- Dialog.oj
    @implementation Dialog
    // ... snip ...
    @end
    // Relevant for method 3...
    module.exports = [Dialog class];

//- App.oj
    // Method 1:
    @class Dialog
    // Method 2:
    #include <Frameworks/Dialog.oj>
    // Method 3:
    var Dialog = require("./Frameworks/Dialog");
    function main() {
        [Dialog showMessage:"Hello, world."];
    }


What may have happened in this code is, that App.oj got executed before Dialog.oj, resulting in Method1 only partially solving an issue. A wrong identifier might be used during the transpilation and I would end up with an error.

The second method prepended the file, but made error messages super cryptic.

The third method did work, only because the class-method would expose the actual class object off the runtime.

To allow modularization, while still keeping the Objective-C syntax, one could either…
- introduce an @export macro (or something alike): @export @implementation myClass ….
- transpile each class into an idividual object and use module.exports or anything alike, without relying on the runtime.

But then, there is the problem of objc_msgSend(…) - or, objj_msgSend(…) still being needed.

What a transpile job could do is:
- If transpiling for the browser, use objj_msgSend as a global function
- if transpiling for a NodeJS environment, require the objj runtime into local scope and use that (i.e.: require("objj").objj_msgSend(…); )

Those however are rather deep changes - into the whole runtime and workflow alltogether. Currently, ObjJ is compiled into a file, and served. But really utilizing ObjJ, to me at least, is by providing a more dynamic environment.

I have written a loader for WebPack that allows OJ code to be translated. I would be all up and happy to provide such stuff for ObjJ too! Cappuchino is definitively an awesome framework I would also like to use myself.

I mean, one of the nice things we could do, is share code between server and client.

// Client...
    var data = [[CPDataSerializer alloc] initWithObject:myObject];
    wsConnection.send(data);

// Server...
    wsConnection.on("data", function(data){
        var myObject = [CPDataSerializer loadFromData:data];
        var something = myObject.something;
    });


As said, those are just ideas. I have never gotten into how ASTs work, how to modify or understand them. All that I can really do is write tools, extensions and bridge code (as in, i can link one thing to another and make them cooperate as much as it is possible).

I wish you all a good day and happy coding! :)


Kind regards,
Ingwie Phoenix
Reply all
Reply to author
Forward
0 new messages