[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