[prev in list] [next in list] [prev in thread] [next in thread]
List: kfm-devel
Subject: KJS API changes
From: Peter Kelly <pmk () post ! com>
Date: 2001-10-19 14:32:01
[Download RAW message or body]
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
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic