From kfm-devel Fri Oct 19 14:32:01 2001 From: Peter Kelly Date: Fri, 19 Oct 2001 14:32:01 +0000 To: kfm-devel Subject: KJS API changes X-MARC-Message: https://marc.info/?l=kfm-devel&m=100350140009031 Hi all, I've recently been coding a reworking of the KJS API for KDE 3.0. This has involved a number of changes in structure, and as such the new API will be source incompatible with the 2.x series. The only code that uses KJS that I know of is khtml and kpac, so I do not anticipate this being a major inconvenience to other develoeprs out there (if it is, let me know!). At present the changes are incomplete so are not ready for HEAD, but I have created a new branch for those interested in working on the new API. This will only be short lived (hopefully < 1 week) before it is merged back into HEAD and khtml and kpac are updated. Note that the branch was from KJS as it was ~3 weeks ago - I will merge in other changes since then as soon as the code is in a working state. The changes basically involve cleaning up the API and removing a number of awkward aspects of the previous version, particularly relating to static class variables and the inheritance hierarchy, and exposing less of the internal code which made it hard to add new virtual functions etc. At some stage before 3.0 is out I will write a porting guide for any applications out there that are using the 2.x API, and also some documentation on how to include the API in your apps. Here is an overview of the changes that have been made: - There is now a disctinction between primitive values and objects. The spec defines 6 main types of values: Undefined, Null, Boolean, String, Number and Object. Of these, only the Object type has semantics such as properties, call(), class etc. The wrapper object class structure has been modified accordingly. So we now have the following class hierarchy: Implementation class Wrapper class ValueImp Value UndefinedImp* Undefined NullImp* Null BooleanImp* Boolean StringImp* String NumberImp* Number ObjectImp Object (*= internal, not part of API) So basically, Imp has been split into ValueImp and ObjectImp, while KJSO has become Value and Object. - Internal functions no longer inherit from the InternalFunctionImp (which is now gone). Instead, they inherit directly from Object and use the call() method to provide access to their functionality. For internal functions such as these, there is no need to establish a new execution context, work out the variable/arguments objects etc. This should also give us some degree of performance improvement due to reduced overheads when calling these functions (and also others such as those defined by the ECMA bindings) Having "host" internal functions implement call() in this way also mean that we can expose less of our internal class structures to the outside world. e.g. with the old API, the FunctionImp class was public, making it impossible to add a new virtual method without breaking binary compatibility - There is no longer such thing as the "current" interpreter or execution context. The context that should be used by any given function depends on a parameter passed in which is a KJSExec pointer. KJSExec objects simply provide access to an interpreter and execution context (and possibly other things in the future), so it is clear which is to be used at all times. Instead of changing the current context, when execution enters a declared function a new KJSExec object is created referencing the new execution context and the old one remains as is. This helps us avoid situations like having a host function perform some action which results in a function being executed or objects being created in another interpreter instance (e.g. calling a function in another frame), and then returning with the wrong current interpreter being set. It also means that when an object is created, there is no ambiguity about what interpreter to use to retrieve the prototype. e.g. when creating a new Object previously, it would use the Object.prototype from the "current" global object. But if the object was supposed to be created within the realms of another interpreter instance, but a different interpreter had just run a script (i.e. the new object is being created not within a script execution), then it would use the wrong prototype object. If the prototype object had been modified in the other script instance, unexpected results may occur. - The Global object has also been removed. Each interpreter instance still has it's own global object, but there is nothing special about this, it is just a normal object without any special functionality. The application is required to pass in a global object upon construction of an interpreter - this can just be created as a direct subclass of ObjectImp, or any other type of object (in the case of the khtml bindings, a Window object will be created before the interpreter and passed in as a global object). We could possibly provide another constructor which creates an ObjectImp to use. When the interpreter is created, whatever object was passed in as the global object is given all the standard properties like Object, String, parseInt etc. The builtin object constructors and prototypes are also kept in the interpreter and made available via the API, e.g. you can use interpreter->builtinObjectPrototpe() to retrieve the original value of Object.prototype, even if this property has been replaced by a script. - All internal object classes like Math, Date, etc. and their associated prototype/function classes now require the prototypes to be passed in as parameters. This makes it unambiguous about what prototypes are to be used, as opposed to grabbing them from the "current" global object and hoping they're the right ones. - ConstructorImp is also gone. Instead, objects which are able to be used as constructors override the construct() method of ObjectImp to provide the functionality they need. In this situation, they also override implementsConstruct() to return true. This is also the same with call() and hasInstance(), where the implementsCall() and implementsHasInstance() need to be overridden also. - Imp::propList() has been made public. This is so host objects can override the property discovery functionality used by for x in y statements. This could be useful in the khtml bindings where we want to provide the appearance of an object having certain properties, without needing to explicitly add them to the property list maintained by ObjectImp. The function now also returns a List pointer of references, rather than the old PropList class, which has made the API a bit cleaner. - The "this" value is explicitly passed to call(), instead of call needing to obtain the this value from the execution context. Note that we do not enter a new execution context for internal/host functions anymore - this only happens when a new function declared in js source is called. - The exec paramater has been added to a number of API functions that get called during execution. This is due to the need to pass the exec through to other methods which could potentially result in js code being executed (for example ValueImp::toString()) - The isA() method has been removed (use type() == instead), and the Type num has been reduced to primitive types only - Undefined, Null, Boolean, String, Number, Object, Reference, List and Completion - The typeInfo struct is also gone. Objects now return a String for their class name. - Imp::putArrayElement() has now changed to be ArrayObjectImp's implementation of put(). The spec seems to indicate that this overrides [[Put]], so this is a neater solution that having a separately named function - There is no longer such thing as a "Host" object - application supplied classes should now inherit directly from ObjectImp. What's left to do: - An equivalent to typeInfo is needed so that classes can be identified using a static pointer rather than a string... this will be mostly useful for testing class types in the internal functions in situations where an application's bindings permit objects named with the same class name as internal types, e.g. "Array" - According to the spec, List is a primitive type, so should derive from Value. Also, it will be useful to have the wrapper class/smart pointer facilities we have with Value/ValueImp for use with Lists so that they can be properly reference counted and garbage collected. - Re-integrate the debugger - Make it work! At the moment the code compiles, but there are quite a number of crashes when running scripts, and things that need to be checked. For those who don't already know, mozilla's javascript test suite now runs with KJS, and has proved invaluable for finding a number of errors that have been missed in the passed. I'll make sure this is running more or less as well as it did previously before merging back the changes to HEAD. You can find scripts to run the tests in khtmltests/js/mozilla. - Various code cleanups, documentation and FIXMEs (###) to sort out -- Peter Kelly pmk@post.com