A while ago I asked about the name of TObject ancestor, well, it’s now live in the SVN and its name is ‘Object‘.
Object is thus the new root class, above TObject. Eventually, it’ll be the ancestor for every other type.
JObject
The earliest need for migrating to a rooted type system was to unify TObject and the new JObject, which is is the new root class for all “prototypal inheritence” JavaScript classes in SmartMS (while TObject remains the root for classical Pascal classes).
Subclasses of JObject are either purely external classes (implemented on the JS side, in the DOM, etc.) or Pascal classes compiled to JavaScript, but that follow the JavaScript “object” conventions rather than the Pascal conventions, ie.
- methods are resolved through prototype (they can be resolved either statically or via a VMT for TObject)
- field and method names are not obfuscated
- no smart-linking or de-virtualization can occur
- they are globally scoped in JS
- marking methods as “virtual” is an error (they are dynamic, which is a bit like being implicitly “virtual” and “overridden”)
- visibility isn’t supported (on the JS-side)
- ClassType, ClassName, etc. don’t exist
These classes are intended primarily for consumption by “external” JavaScript libraries.
They’ll look and behave like “pure” JavaScript classes, so will share their downsides (no obfuscation, no whole-program optimizations, no elimination of unused methods by the smart-linker, etc.); Still, they retain some Smart Pascal advantages (strong typing, local optimizations, obfuscation of method implementations, case insensitivity on the Pascal side).
This is the first time I enjoy the deprecation and re-use of the “object” keyword. The “record” type in DWS is now as complete as I need, for data objects, whereas “class” type is for per-reference OOP instances. Nice. 🙂
Having such a new “JObject” and “object” kind of classes is nice.
You may have introduced a new keyword specifier to the basic “class” (like “TJMyClass = javascript class(TJClassParent)”) or some attributes but it does make sense to have its dedicated keyword, since I suspect your DWS implementation is private
Deep change in the DWS internals, I suspect, and another proof of its very good OOP design, from its early ages. I was impressed by the DWS original code: it was the first Delphi project I saw with so much small classes, small methods, lot of inheritance, and very clear OOP design. And your great work on DWS just made it several steps further.
I just bought the “Smart Book”, and was impressed by some features of the compiler. I followed your work day after day, but when you read through all the features, global list is impressive, and syntax choices are well found. I like for instance the fact that you have some new syntax (e.g. for enumerations), and also the “classic” syntax at the same time. I also like very much your work about helpers, much complete than the Delphi implementation, which sounds more like a syntaxic sugar than an usable feature: in DWS you can have several helpers per type at once.
What about generics, attributes, and other planned features?
This was considered (or something similar), to allow specifying if a subclass of an external JS class is purely external or Pascal-implemented, in the end, just using “external” and introducing a “root” proved simpler and more extensible.
(it’s possible f.i. to subclass a JS class in Pascal, then have it further subclassed in the JS-side, and then subclassed again in Pascal, etc.)
Having a root object that isn’t TObject will have its own uses, and the idea is that you could then introduce other root objects for interfacing with other language’s class paradigms in the future, and mesh everything together.
Attributes could have been another option, but they’re quite verbose and weren’t really necessary at the moment. Also, since the syntax is minimally extended with “object” & “JObject”, nothing forbids adding attribute-controlled stuff later, f.i. to specify other forms of “inheritance” (non-prototype based, but f.i. composition based as I’ve see some JS use).
There are some “undecided” aspects at the moment, like what to do with overloads (forbid them? generate a redirection stub automagically for JS’s consumption?), constructors and properties (JS has some getter/setter syntax, but since it doesn’t have visibility, having all properties published withe getter/setter may not be desirable, so there could either be rules based on visibility or attributes)
It involves some codegen magic yes, but the foundations are in DWScript: the language extension mechanism has been extended so you can add new “built-in” types more easily, but there weren’t many changes required – as SVN log can attest 😉
Attributes are trickling in already, you can have them attached to types, and others are auto-generated for properties.
They can be accessed via RTTI, but there it’s currently low-level, higher-level RTTI interrogation classes aren’t there yet.
For Generics, I suppose I’ll go the FreePascal route instead (ie. templates, with generic-like constraints), though they haven’t started yet. The parser has some support for RTTI-like syntax (used for RTTI-based variants f.i.)
Last major item on the todo is transitionning from stack to closures (so the script engine can do all the JS codegen can), though that’s going to be a major change, so I’m waiting for a “release” to have been completed before starting on it.
You’ve mentioned the planned closure-based engine a few times. What is that about, what sort of new things will it be able to do, and why can’t the current engine do them?
Currently the engine is stack-based, ie. all the local variables end up in the stack.
A closure-based engine would allocate space for local variables in separate spaces, you can think of it as having one hidden class per routine that holds the local variables of a routine.
This allows things like variable capture (like in Delphi, where an anonymous method can “capture” a local variable of it’s parent procedure and read/write to it even when the parent procedure has “ended”), automatic parallelization, lambdas, and a variety of uses derived from these.
I would also use it as an opportunity to make the transition from Variant arrays as universal storage. So this would result in lower runtime memory usage, stronger typing in the engine, and hopefully better performance.