I posted a picture of the OP4JS pre-Alpha testbed to my Facebook group, and a member found it interesting that it had an ASM section in the code. How can there be an ASM section in a system that targets JavaScript you wonder?
Well, assembler for OP4JS is basically what assembler would be to a real Delphi application. In this case: raw JavaScript. Eric Grange, my friend and author of our compiler, has done a great job on transposing classical Object Pascal terminology onto the (let’s face it) bewildering typology of JavaScript.
Take this simple procedure for instance:
procedure TMyObject.Testing; var mRef: TObject; begin mRef := tagRef; asm (@mRef).style.width=100 + "px"; end; end;
What we do here is to first get a handle to the our tag (the tagRef is a public property of TW3TagObject, the class TW3CustomControl inherits from). We then enter the ASM section, use that reference to access and alter the width style property. Ofcourse you dont have to deal with this stuff unless you want to, because OP4JS will ship with a rich set of controls ready to be used. And rolling your own controls is so simple it’s almost ridicules.
The squiglies
But why the @ prefix you wonder? Well, when we obfuscate our code (make it unreadable, or less “stealable” for the average hacker), all the names of variables and methods are altered. As such, the compiler needs to be able to quickly figure out if you are using a JS based variable or an Object Pascal variable. So all Object Pascal variables have to be prefixed with the @ character. Due to the nature of Javascript you also need to isolate direct references to object fields and native fields inside ( [ .. ] ) sections, like in this example:
asm (@self.FWidth) = parseInt( (@mRef).style.width ); end;
Where did my pointers go?
JavaScript don’t have pointers, at least not in the proper sense of addresses and memory buffers. What it does have are object references and data references (it also has a weak reference counted garbage collector). So under OP4JS we dont have a type called “pointer” because it would screw up the reality of compiling to JavaScript. Instead, TObject serves the function of a universal object reference, and variant serves as a universal data reference. Variants are special however, because we dont have any operators dealing with it directly (so you cant write: A:=1 + someVariant). The variant type is just used to transport data from one method to another.
But, all the standard types are there: string, boolean, float, integer and so on. You also have records, interfaces and enumerations to play with. It really is a neat system once you get the hang of it.
function getSomeData: Variant; begin asm /* return a data structure */ @result = {first:"hello",second:"hello2"} end; end; procedure handleSomeData(aValue: Variant); begin asm /* access the data structure passed in */ alert(@aValue.first); //will show "hello" end; end;
Super simple custom control
To demonstrate how extremely simple it is to create your own controls under OP4JS, let’s make a scroll-text. As you probably know HTML have a tag called marquee that scrolls text in a variety of ways. It has been called an evil tag because it consumes a lot of CPU speed (and that part is very true), but just for the hell of it – let’s make a super-simple OP4JS version of it:
unit myscroller; interface uses w3system, w3components; type TW3Marquee = Class(TW3CustomControl) private FText: String; procedure SetText(aValue:String); public Property Text:String read FText write SetText; End; implementation procedure TW3Marquee.SetText(aValue:String); Begin //Keep the text Ftext:=aValue; // Cheat and inject the tag into our innerHTML // This works because TCustomControl has already created // a tag for us. So we just inject the content innerHtml:='<marquee style="' + 'font-family: verdana;' + 'width: 100%;' + 'height: 100%;' + '-webkit-marquee-increment:3px;' + '-webkit-marquee-speed:fast;' + '-webkit-marquee-direction:backwards;' + '-webkit-marquee-repetition:infinite;' + '">' + FText + '</marquee>'; end; end.
Naturally this is just a quick example. A fully functional and quality control would have a slightly different layout. There we would isolate the marquee in it’s own tag object. But the approach is perfectly valid, although a bit slap-dash 🙂
OP4JS does not work by injecting text into the DOM, that is to slow. We create tag objects via the Javascript API and access our objects by reference. This is why OP4JS apps are much faster than those produced by other JS compilers. We try to avoid the use of getElementById() because it results in poor performance.
Is capitalization of setText in TWarquee3M required to start with a small letter or is this just chosen to better reflect the usual JavaScript usage?
It’s just like Delphi/Object Pascal. Not case sensitive. So you may call it SetText, settext, SETTEXT or whatever.
In handleSomeData procedure, could you use aValue.First in object pascal code (not asm/Javascript block)? I.e. is there any kind of late-binding for this variant type?
In short, no. But in a “real” example you would just use normal records for this.
Type
TMyRecord = Record
first: string;
second: string;
End;
function getSomeData:TMyRecord;
Begin
result.first := ‘hello’;
result.second:=’hello2′;
end;
You know..normal delphi stuff.
Variant is not a type you use much, like i mentioned it’s more for transportation of JS structures than it is values (but you can use it if you like, but we dont have compiler operators for it).
But one neat trick is this:
type
TMyRecord = Record
first,second:String;
End;
procedure TForm1.Test;
var
mData: TMyRecord;
Begin
asm
(@mData).first = “from js”;
(@mData).second = ” its all good”;
end;
w3_alert(mData.first + mdata.second);
end;
There is no difference between our records, and a typed javascript record. This opens up enourmous potential when dealing with server-side technology and client side dependencies (i.e native JS libs).
This is why I’m interested a lot about op4js for a pure object pascal coding on the AJAX client side using our little Client-Server mORMot framework.
Our objects will be defined as plain classes, so we’ll be able to access from it with strong-typing.
I was just wondering about late-binding possibility with variant in op4js scripts. But it is not a mandatory