For those who just want to see a before/after summary, here's an example.

BEFORE: we have to handwrite conversion routines for each downcast or upcast we want to use. Furthermore, we have the ugly AbstractDocument/AbstractNode/AbstractEvent/AbstractEventTarget types, in addition to all of the concrete @mut SomeDOMType that are scattered everywhere.

impl AbstractNode<ScriptView> {
     /// Allow consumers to upcast from derived classes.
     pub fn from_document(doc: AbstractDocument) -> AbstractNode<View> {
         unsafe {
             cast::transmute(doc)
         }
     }

pub fn from_eventtarget(target: AbstractEventTarget) -> tractNode<View> {
         assert!(target.is_node());
         unsafe {
             cast::transmute(target)
         }
     }
}

788         let doctarget = AbstractEventTarget::from_document(document);
789         let wintarget = AbstractEventTarget::from_window(window);
790 window.eventtarget.dispatch_event_with_target(wintarget, Some(doctarget), event);

307                     if child.is_element() {
308                         do child.with_imm_element |elem| {
309                             if callback(elem) {
310                                 elements.push(child);
311                             }
312                         }
313                     }


AFTER all casting methods are automatically generated; all that's left is writing the is_foo method and ensuring that we can tell the concrete type of a DOM object based on the root type in an inheritance chain:

impl UIEventDerived for Event {
    fn is_uievent(&self) -> bool {
        self.type_id == UIEventTypeId
    }
}

let doctarget = EventTargetCast::from(document);
let wintarget = EventTargetCast::from(window);
window.eventtarget.dispatch_event_with_target(wintarget, Some(doctarget), event);

if child.is_element() {
    let elem = ElementCast::to(child);
    if callback(elem) {
      elements.push(elem);
    }
}

In my mind, the biggest improvement here is that we can actually have lists of JSManaged<Element>, whereas before we could only store ~[AbstractNode] with the handwave-y guarantee that all of the nodes should also be elements. I also find the new checked casts much nicer to read (and yes, the downcasts assert at runtime if the value passed is not an instance of the desired type).

Cheers,
Josh

On 12/03/2013 03:07 AM, Josh Matthews wrote:
Ms2ger and I have been working on this on and off, and the Event
hierarchy is looking very nice so far:
https://github.com/jdm/servo/commits/jsmanaged . It even builds and
passes tests, so we should be able to continue converting this
piece-by-piece. There is an absolute minimum amount of boilerplate
required now, which is lovely.

Cheers,
Josh

On 11/28/2013 10:54 AM, Josh Matthews wrote:
I've finally got a sketch of my plans to remove all of the @mut
annotations from Servo's DOM. You can see it at
https://gist.github.com/jdm/7693770, but here's the breakdown of the
improvements:

* no more AbstractNode (\o/) - we can actually use JSManaged<Element>
when we want to refer to something derived from Element now.
* actually fulfils the contract that the SpiderMonkey GC owns the sole
reference
* no need to add as_foo methods for downcasting

Breaking it down further, one of the biggest changes is in the
implementation of downcasting and upcasting. Upcasting a JSManaged<Foo>
to a JSManaged<Bar> is a statically-checked operation that should be
free at runtime. This is enforced by a series of empty traits that each
derived class much implement (ie. see EventTargetBase), such that any
value passed to Bar::from() must be a type that implements BarBase. In a
similar fashion, downcasting is also statically-checked (does the cast
even make sense?), before performing a dynamic assertion (is this base
class actually an instance of the derived type?). Therefore, when
calling Foo::to(), the value passed must implement the FooDerived trait
with an appropriate boolean is_foo method implemented.

These casting changes may not look like a big improvement at first, but
the important consideration is the the upcasting traits can be
automatically generated completely from WebIDL definitions, so that
boilerplate should be essentially free. For downcasting, each type that
is on an inheritance chain will need to implement a single trait with a
single boolean method that performs the required dynamic check; all
other boilerplate can also be generated from WebIDL definitions, and is
therefore essentially free.

I welcome feedback about this sketch. For a quick look at how it can be
used by consumers, see main() in jsgc.rs.

Cheers,
Josh


_______________________________________________
dev-servo mailing list
dev-servo@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-servo

Reply via email to