Differences between Delphi and Smart
Note: The project codenamed “OP4JS” is now officially called Smart Mobile Studio.
The Smart Pascal dialect is more or less identical to Delphi 7, so if you are familiar with either FreePascal or Delphi then learning Smart will be a breeze. This document will outline the differences and features of Smart compared to classical Object Pascal. Unlike other products we have gone to great lengths to keep the source-format as close to Delphi 7 as possible. We believe the wast majority of Delphi developers will feel right at home with Smart and enjoy the opportunities our product delivers.
If you are new to Object Pascal we highly recommend the website Delphi Basics to help you get started.
Character case and syntax
Smart follows the standard Object Pascal text formatting, which is not case sensitive. Your procedure, variable, class and type names can be defined in any case your wish.
Unit variables
This is fully supported, however object instances must be initiated from an existing instance (TApplication being the obvious initiator).
Unit initialization and finalization
Under Delphi and free pascal these two procedures are typically used to initialize unit variables, they are not supported at this time
Generics and RTTI
Some aspects of generics are on the drawing board, but as of writing this is not something we support. But TObject does have a classtype property and you can also use the IS keyword:
if (someobject is TMyObject) then begin //do something end;
Exceptions
Smart has full support for exceptions. JavaScript exceptions will surface as ordinary Object Pascal exceptions and can be handled the exact same way. There is one small difference however, the CreateFmt() constructor is implemented in EW3Exception.
Data-types
Smart supports the following data-types and arrays of these. The range of types is limited by JavaScript.
- TObject
- Boolean
- Integer
- String
- Float
- Variant
Note: JavaScript automatically decides if an integer is 8, 16, 32 or 64 bit in size. Operations like bit shifting only affects the least significant 32 bits. Also The default size of an integer depends on the operative system (32 bits on iOS, 64 bit on a modern iMac).
Interfaces
Smart fully supports interfaces. There is no need for TInterfacedObject, interfaces can be applied directly to TObject since JavaScript does not have a mechanism for observing reference counts.
type IMyInterface = interface procedure myInternalMethod; end; TMyObject = class(TObject, IMyInterface) private { IMyInterface Implementation } procedure myInternalMethod; end;
Constructors and destructors
These are supported as normal (a part of TObject). It must be underlined that when writing components (derived from TW3CustomControl, TW3GraphicControl or TW3Component) you rarely override these directly like under Delphi or Free Pascal, instead you override the protected methods InitializeObject and FinalizeObject. This is to compensate for the lack of BeforeDestruction and AfterConstruction in our object model.
Type casting
Normal typecasting is fully supported. Normal rules apply (you can only cast an object reference to it’s native type or an ancestor).
Var parameters
Smart supports var parameters
Records and composite types
Smart supports records and also arrays of records, both dynamic and static
Variants
Variants are supported as a data-type, but can only be used as containers. The compiler has no operators that deals with them (so you cannot write: A:=1 + myVariant, because there is no way to predict the resulting type without considerable overhead and loss of performance). Under Smart variants are used to transport information between raw JavaScript methods. You can however have variant parameters and function results, but these can only be accessed by methods using ASM sections (see ASM sections below). To force a variant cast explicitly, you can use the following functions from the RTL (W3System.pas):
- function VarAsString(aValue: Variant): String;
- function VarAsInt(aValue: Variant): Integer;
- function VarAsFloat(aValue: Variant): Float;
- function VarAsObj(aValue: Variant): TObject;
- function VarAsBool(aValue: Variant): Boolean;
Dynamic Arrays
Smart supports dynamic arrays in the form of objects (which in reality maps directly to JavaScript arrays). You allocate dynamic arrays using the classical “new” keyword, as such:
type TIntArray = array of Integer; procedure TestArray; var myData: TIntArray; begin myData := new TIntArray[10]; // myData supports: // .length = returns length of array // .push(value) = adds a new item to it // .delete(index,count) = deletes X number of entries // And more. Please see documentation on arrays for more info end;
Events and event handlers
Since everything is an object in the JavaScript world, the “of object” ending for procedure references is not needed (and not supported). So where you under FreePascal or Delphi would write:
type TMyCleverEvent = procedure (Sender: TObject) of object;
Smart demands that you write:
type TMyCleverEvent = procedure (Sender: TObject);
Pointers
JavaScript does not support pointers but deals exclusively with references , as such the pointer data-type serves no function and does not exist in our flavour of Object Pascal. In short we can say that JavaScript deals with references and values in 3 ways:
- references to native objects provided by the browser
- variables holding data structures created by JavaScript (including JavaScript prototype objects)
- references to JavaScript values or functions (functions can return functions in JavaScript)
What we have done to clean up this mess – is to map our rigid Object Pascal terminology onto the unstructured and colorful JavaScript mesh. Our goal has not been to make Object Pascal look like JavaScript, but rather to force JavaScript to abide by our rules. This is much more elegant than trying to mimic or fake a datatype that doesnt exist:
- You handle object references with TObject
- you handle data structures with records or variants
- you handle values using normal variable types
- You handle function references with procedural types (see events and event handlers above)
We have true object inheritance
JavaScript does not support true OOP in the ordinary sense of the word, which makes it exceedingly difficult for classical web-designers to create reusable components or manage large scale projects. But Smart delivers full OOP running under JavaScript (!). Our compiler technology generates a real-life VMT (virtual method table) in JavaScript, which means that you can write objects and inherit from them just like you do in Delphi. This gives you a tremendous advantage over classical web developers that must rely on a wide scope of JavaScript libraries, libraries that rarely co-exist without causing browser instabilities. With Smart this is not the case – here you can enjoy the same freedom of rapid, object oriented software architecture as you would under Delphi or Visual Studio.
ASM blocks
In the world of Delphi or free pascal, it’s quite rare to find people that write assembler side by side with their pascal code anymore. Real assembler is complex, error prone and tricky to master (which is why so few people are good at it). But under Smart assembler is pure fun – because to a browser assembler is JavaScript! Unlike x86 assembler, JavaScript is reasonably easy to work with (although abhorrently difficult to get right beyond 20.000 lines) and if you want to interface more closely with the browser – you have no other option but to use an ASM section. So ASM sections contain normal JavaScript, not machine instructions. Let us have a look at the ground rules:
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 base class for all our controls and components). We then enter the ASM section, use that reference to access and alter the width style property of our control. Of-course you don’t have to do things this way, our RTL is quite rich in functions to deal with such simple tasks. But learning how to benefit from your JavaScript skills is a great bonus. You get the best of both worlds. The same could easily be achieved with a function from our W3Systems.pas RTL unit:
procedure TMyObject.Testing; asm w3_setStyle(tagRef, 'width', w3_intToPxStr(100)); end;
There is one important rule when mixing raw JavaScript with Object Pascal code though, and that is that you must prefix any variables comming from the pascal side with the @ character. The reason is that when our obfuscator (a process that makes the generated sourcecode “unreadable” to human eyes) examines your code, all the names, variables and tokens are altered. As such, the compiler needs something extra to distinguish pascal symbols from raw JavaScript symbols. So all Object Pascal variables (or references) must be prefixed. Due to the nature of JavaScript, in some cases you also have to isolate references inside a ( .. ) block, like this:
procedure TMyObject.Testing; asm (@self.FWidth) = parseInt( (@mRef).style.width ); end;
Of-course, you can use functions in W3System.pas to achieve the exact same thing:
procedure TMyObject.Testing; begin Self.width := w3_getStyleAsInt(tagRef, 'width'); end;
Note: The above is not an example of how to size a control. TW3CustomControl have ample properties and methods for moving and resizing, just like TCustomControl have in Delphi.
Common RTL units
Smart does not try to mimic the Delphi unit names, although the list of functionality and component architecture share many similarities. The Smart RTL is in constant growth but as of writing the following units can be concidered “standard”. There are ofcourse more units in our runtime library, but these units represent the central hub of our system
- W3System.pas – Low level function, procedures and types
- W3Components.pas – Class hierarchy, from TW3TagObj to TW3CustomControl
- W3Storage.pas – Wrappers for the JavaScript storage API
- W3Ctrls.pas – Common controls like TW3Button, TW3Panel, TW3Checkbox and more
- W3Time.pas – Timer and callback objects
- W3Lists.pas – TW3StringList, TW3IntList and TW3ObjectList
- W3Forms.pas – TW3CustomForm and associated information
- W3Application.pas – The central objects that all programs build on
- W3Effects.pas – Webkit animation objects
- W3Graphics.pas – Full implementation of HTML5 canvas objects
- W3Buffers.pas – Memory access and allocation objects
- W3Inet.pas – Ajax and XML http objects
- W3Touch.pas – Touch, multi-touch and gesture object
- W3Fonts.pas – Objects for making fonts management more palatable
- W3Styles.pas – Dynamic creation and management of styles and stylesheets
Can I use a JavaScript library, like jQuery or Qooxdoo?
Sure. Just add it to the “lib” folder and wrap it. You can access native JS through an ASM section, create an instance of it to a variant, and then access it’s methods directly. Take a look at some of the articles under “documentation” and also the developer’s log and you will examples for it 🙂
You can also reference functions through the external keyword:
function someNativeJSFunction:String;external;
.. then you can call it just like any pascal method
There is also a “add script” button in the IDE
> the “of object” ending for procedure references is not needed (and not supported)
What is the rationale behind this (the not supported bit)?
This makes it harder to share core code between native Delphi and Smart without using IFDEFs…?
This feature was added just to annoy you, Hallvard 😉
We knew that some smart guy would drop by one of these days and pick on this.
—
To be honest, I’m not sure. It’s been like this in DWS forever. The scripting engine doesn’t need to differentiate between function pointers, closures and procedure references.
As long as the parameters (and result type) match, the declaration will accept standalone functions, object methods, interface methods, anonymous methods and record methods.
It could, of course, be possible to accept the construct (both “reference to” and “of object”) and just let the compiler ignore it. Perhaps produce a hint during compilation.
—
UPDATE:
I just received an answer from Eric on this, and there is no particular reason.
“At the time I was unsure if introducing ‘of object’ or ‘reference to’ without enforcing them (ie. just having the parser ignore them) would be good or bad.
We can add the support with a ‘pedantic’ hint, if there ain’t any argument against it.”
We added support for that today 🙂 Will be in the first hotfix!
Cool! 🙂
This might make it slightly more realistic to share some core / business logic code between a Smart and a Delphi project.
It would be awesome if someone could make a step by step tutorial (howto) on using external javascript like jQuery or Qooxdoo.
Not everybody is an expert on both js and sms or delphi.
So a step by step would be amazing. Not just general hints.
Unless you need something very, very spesific, you dont really need jquery under smart. But i do understand that other libraries can (and will) be absorbed by smart – so documentation is vital. Working with low-level stuff is something that will be best covered in the upcomming smart book (that can be bought now), but i’ll also write some articles on the subject over the holiday. Having said that, in order to absorb a javascript library – you will need at least some understanding of how javascript works, and also how smart creates controls and keeps track of them via handles (which is more or less identical to how delphi does it with windows handles).