lucy-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Marvin Humphrey <>
Subject Re: [lucy-dev] Host overriding of all non-final methods
Date Thu, 03 Mar 2011 13:35:37 GMT
On Thu, Mar 03, 2011 at 09:03:39AM +0000, Andrew S. Townley wrote:
> Lazy or not, I think it's better to fail silently now and then try and build
> better checks into the clownfish compiler or some kind of clownlint tool
> later than imposing exception handling overhead on potentially every method
> call (unless I'm misunderstanding the impact of option B).

The host language's OO hierarchy is only walked once per subclass.  Once Lucy
creates a VTable singleton and stores it away, that VTable will not be
modified.  Thus, there is no extra overhead imposed on every method call
within the Lucy core -- but we don't support all the features of dynamic

Generally speaking, the VTable singleton for a host-defined subclass is
created the first time a constructor for that subclass is called[1].  It is at
this moment that we would attempt to detect illegal overriding of "final"
methods.  Here's the code from from VTable_singleton() in
core/Lucy/Object/VTable.c which enables host method overrides, modified to
enforce "final" methods:

     // Allow host methods to override. 
     novel_host_methods = VTable_novel_host_methods(subclass_name);
     num_novel = VA_Get_Size(novel_host_methods);
     if (num_novel) {
         Hash *meths = Hash_new(num_novel);
         uint32_t i;
         CharBuf *scrunched = CB_new(0);
         ZombieCharBuf *callback_name = ZCB_BLANK();
         for (i = 0; i < num_novel; i++) {
             CharBuf *meth = (CharBuf*)VA_fetch(novel_host_methods, i);
             S_scrunch_charbuf(meth, scrunched);
             Hash_Store(meths, (Obj*)scrunched, INCREF(&EMPTY));
         cfish_Callback **callbacks
             = (cfish_Callback**)singleton->callbacks;
         for (i = 0; callbacks[i] != NULL; i++) {
             cfish_Callback *const callback = callbacks[i];
             ZCB_Assign_Str(callback_name, callback->name,
             S_scrunch_charbuf((CharBuf*)callback_name, scrunched);
             if (Hash_Fetch(meths, (Obj*)scrunched)) {
+                if (callback->method_is_final) {
+                    THROW(ERR, 
+                        "Can't override final method '%o' in class '%o'",
+                        meth, class_name);
+                }
                 VTable_Override(singleton, callback->func, 

Once a VTable is created, if you subsequently attempt to modify behavior using
dynamic features such as Ruby's singleton methods, you will get inconsistent
results.  The behavior will be visible from the host language, but it might
not be visible from inside the Lucy core.  You only get one shot to tell Lucy
that it needs to call back into the host.  If a method was not overridden at
the moment the VTable was created, Lucy won't notice if it gets overridden
    class MyQueryParser < Lucy::Search::QueryParser

    query_parser =
    def query_parser.expand_leaf
        puts "This code will not be called from within the Lucy core."

Clownfish's design is a compromise which lets us support a simple class-based
single-inheritance OO model without sacrificing speed.  Method dispatch in
Clownfish uses a vtable-based double dereference mechanism[2] -- typical for
C++ or Java.  It's less flexible than the dispatch techniques used by dynamic
languages like Ruby, Python and Perl, but until the host callback mechanism
gets invoked, it's much, much faster.

Marvin Humphrey

[1] All parent VTables must be created first, so if the VTable for a host
    subclass two generations removed from its Lucy ancestor is needed before
    its parent, the parent VTable will be initialized first as a side effect.


View raw message