"Fossies" - the Fresh Open Source Software Archive

Member "google-gadgets-for-linux-0.11.2/ggadget/scriptable_helper.cc" (6 Jan 2010, 27437 Bytes) of package /linux/misc/old/google-gadgets-for-linux-0.11.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2   Copyright 2008 Google Inc.
    3 
    4   Licensed under the Apache License, Version 2.0 (the "License");
    5   you may not use this file except in compliance with the License.
    6   You may obtain a copy of the License at
    7 
    8        http://www.apache.org/licenses/LICENSE-2.0
    9 
   10   Unless required by applicable law or agreed to in writing, software
   11   distributed under the License is distributed on an "AS IS" BASIS,
   12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13   See the License for the specific language governing permissions and
   14   limitations under the License.
   15 */
   16 
   17 #include <cstring>
   18 #include <map>
   19 #include <vector>
   20 #include "scriptable_helper.h"
   21 #include "logger.h"
   22 #include "scriptable_holder.h"
   23 #include "scriptable_interface.h"
   24 #include "signals.h"
   25 #include "slot.h"
   26 #include "string_utils.h"
   27 
   28 namespace ggadget {
   29 
   30 // Define it to get verbose debug info about reference counting, especially
   31 // useful when run with valgrind.
   32 // #define VERBOSE_DEBUG_REF
   33 
   34 namespace internal {
   35 
   36 class ScriptableHelperImpl : public ScriptableHelperImplInterface {
   37  public:
   38   ScriptableHelperImpl(ScriptableHelperCallbackInterface *owner);
   39   virtual ~ScriptableHelperImpl();
   40 
   41   virtual void RegisterProperty(const char *name, Slot *getter, Slot *setter);
   42   virtual void RegisterStringEnumProperty(const char *name,
   43                                           Slot *getter, Slot *setter,
   44                                           const char **names, int count);
   45   virtual void RegisterMethod(const char *name, Slot *slot);
   46   virtual void RegisterSignal(const char *name, Signal *signal);
   47   virtual void RegisterVariantConstant(const char *name, const Variant &value);
   48   virtual void RegisterClassSignal(const char *name,
   49                                    ClassSignal *class_signal);
   50   virtual void SetInheritsFrom(ScriptableInterface *inherits_from);
   51   virtual void SetArrayHandler(Slot *getter, Slot *setter);
   52   virtual void SetDynamicPropertyHandler(Slot *getter, Slot *setter);
   53 
   54   // The following 3 methods declared in ScriptableInterface should never be
   55   // called.
   56   virtual uint64_t GetClassId() const { return 0; }
   57   virtual bool IsInstanceOf(uint64_t class_id) const {
   58     GGL_UNUSED(class_id);
   59     ASSERT(false); return false;
   60   }
   61   virtual bool IsStrict() const { ASSERT(false); return false; }
   62   virtual bool IsEnumeratable() const { ASSERT(false); return false; }
   63 
   64   virtual void Ref() const;
   65   virtual void Unref(bool transient) const;
   66   virtual int GetRefCount() const { return ref_count_; }
   67 
   68   virtual Connection *ConnectOnReferenceChange(Slot2<void, int, int> *slot);
   69   virtual PropertyType GetPropertyInfo(const char *name, Variant *prototype);
   70   virtual ResultVariant GetProperty(const char *name);
   71   virtual bool SetProperty(const char *name, const Variant &value);
   72   virtual ResultVariant GetPropertyByIndex(int index);
   73   virtual bool SetPropertyByIndex(int index, const Variant &value);
   74 
   75   virtual void SetPendingException(ScriptableInterface *exception);
   76   virtual ScriptableInterface *GetPendingException(bool clear);
   77   virtual bool EnumerateProperties(EnumeratePropertiesCallback *callback);
   78   virtual bool EnumerateElements(EnumerateElementsCallback *callback);
   79 
   80   virtual RegisterableInterface *GetRegisterable() { return this; }
   81 
   82  private:
   83   void EnsureRegistered();
   84   class InheritedPropertiesCallback;
   85   class InheritedElementsCallback;
   86   void AddPropertyInfo(const char *name, PropertyType type,
   87                        const Variant &prototype,
   88                        Slot *getter, Slot *setter);
   89 
   90   struct PropertyInfo {
   91     PropertyInfo() : type(PROPERTY_NOT_EXIST) {
   92       memset(&u, 0, sizeof(u));
   93     }
   94 
   95     void OnRefChange(int ref_count, int change) {
   96       GGL_UNUSED(ref_count);
   97       // We have a similar mechanism in ScriptableHolder.
   98       // Please see the comments there.
   99       if (change == 0) {
  100         ASSERT(u.scriptable_info.ref_change_connection &&
  101                u.scriptable_info.scriptable);
  102         u.scriptable_info.ref_change_connection->Disconnect();
  103         u.scriptable_info.ref_change_connection = NULL;
  104         u.scriptable_info.scriptable->Unref(true);
  105         u.scriptable_info.scriptable = NULL;
  106         prototype = Variant(static_cast<ScriptableInterface *>(NULL));
  107       }
  108     }
  109 
  110     PropertyType type;
  111     Variant prototype;
  112     union {
  113       // For normal properties.
  114       struct {
  115         Slot *getter, *setter;
  116       } slots;
  117       // For ScriptableInterface * constants. Not using ScriptableHolder to
  118       // make it possible to use union to save memory.
  119       struct {
  120         // This is a dup of the scriptable pointer stored in prototype, to
  121         // avoid virtual method call during destruction of the scriptable
  122         // object.
  123         ScriptableInterface *scriptable;
  124         Connection *ref_change_connection;
  125       } scriptable_info;
  126     } u;
  127   };
  128 
  129   // Because PropertyInfo is a copy-able struct, we must deallocate the
  130   // resource outside of the struct instead of in ~PropertyInfo().
  131   static void DestroyPropertyInfo(PropertyInfo *info);
  132   const PropertyInfo *GetPropertyInfoInternal(const char *name);
  133 
  134   ScriptableHelperCallbackInterface *owner_;
  135   mutable int ref_count_;
  136   bool registering_class_;
  137 
  138   typedef LightMap<const char *, PropertyInfo,
  139                    GadgetCharPtrComparator> PropertyInfoMap;
  140 
  141   class ClassInfoMap : public LightMap<uint64_t, PropertyInfoMap> {
  142    public:
  143     ~ClassInfoMap() {
  144       ClassInfoMap::iterator it = this->begin();
  145       ClassInfoMap::iterator end = this->end();
  146       for (; it != end; ++it) {
  147         PropertyInfoMap::iterator prop_it = it->second.begin();
  148         PropertyInfoMap::iterator prop_end = it->second.end();
  149         for (; prop_it != prop_end; ++prop_it)
  150           ScriptableHelperImpl::DestroyPropertyInfo(&prop_it->second);
  151       }
  152     }
  153   };
  154 
  155   // Stores information of all properties of this object.
  156   PropertyInfoMap property_info_;
  157   // Stores class-based property information for all classes.
  158   static ClassInfoMap *all_class_info_;
  159   // If a class has no class-based property_info, let class_property_info_
  160   // point to this map to save duplicated blank maps.
  161   static PropertyInfoMap *blank_property_info_;
  162   PropertyInfoMap *class_property_info_;
  163 
  164 #ifdef _DEBUG
  165   struct ClassStatInfo {
  166     ClassStatInfo()
  167         : class_property_count(0), obj_property_count(0), total_created(0) { }
  168     int class_property_count;
  169     int obj_property_count;
  170     int total_created;
  171   };
  172   typedef LightMap<uint64_t, ClassStatInfo> ClassStatMap;
  173   struct ClassStat {
  174     ~ClassStat() {
  175       // Don't use LOG because the logger may be unavailable now.
  176       printf("ScriptableHelper class stat: classes: %zu\n", map.size());
  177       int properties_reg_in_ctor = 0;
  178       int total_properties = 0;
  179       int properties_if_obj_reg = 0;
  180       for (ClassStatMap::iterator it = map.begin(); it != map.end(); ++it) {
  181         printf("%jx: class properties: %d object properties: %d objects: %d\n",
  182                it->first, it->second.class_property_count,
  183                it->second.obj_property_count, it->second.total_created);
  184         if (it->second.total_created == 0)
  185           properties_reg_in_ctor += it->second.obj_property_count;
  186         properties_if_obj_reg += it->second.obj_property_count +
  187             it->second.class_property_count * it->second.total_created;
  188         total_properties += it->second.obj_property_count +
  189                             it->second.class_property_count;
  190       }
  191       printf("properties registered in constructors: %d\n"
  192              "total properties: %d (if all obj reg: %d, saved %f%%)\n",
  193              properties_reg_in_ctor, total_properties, properties_if_obj_reg,
  194              100.0 - 100.0 * total_properties / properties_if_obj_reg);
  195     }
  196     ClassStatMap map;
  197   };
  198   static ClassStat class_stat_;
  199 #endif
  200 
  201   Signal2<void, int, int> on_reference_change_signal_;
  202   ScriptableInterface *inherits_from_;
  203   Slot *array_getter_;
  204   Slot *array_setter_;
  205   Slot *dynamic_property_getter_;
  206   Slot *dynamic_property_setter_;
  207   ScriptableInterface *pending_exception_;
  208 };
  209 
  210 // Class information shall be truely static and shouldn't be destroyed when
  211 // exiting.
  212 ScriptableHelperImpl::ClassInfoMap *ScriptableHelperImpl::all_class_info_ =
  213   new ScriptableHelperImpl::ClassInfoMap;
  214 ScriptableHelperImpl::PropertyInfoMap
  215   *ScriptableHelperImpl::blank_property_info_ =
  216     new ScriptableHelperImpl::PropertyInfoMap;
  217 
  218 #ifdef _DEBUG
  219 ScriptableHelperImpl::ClassStat ScriptableHelperImpl::class_stat_;
  220 #endif
  221 
  222 ScriptableHelperImplInterface *NewScriptableHelperImpl(
  223     ScriptableHelperCallbackInterface *owner) {
  224   return new ScriptableHelperImpl(owner);
  225 }
  226 
  227 ScriptableHelperImpl::ScriptableHelperImpl(
  228     ScriptableHelperCallbackInterface *owner)
  229     : owner_(owner),
  230       ref_count_(0),
  231       registering_class_(false),
  232       class_property_info_(NULL),
  233       inherits_from_(NULL),
  234       array_getter_(NULL),
  235       array_setter_(NULL),
  236       dynamic_property_getter_(NULL),
  237       dynamic_property_setter_(NULL),
  238       pending_exception_(NULL) {
  239 }
  240 
  241 ScriptableHelperImpl::~ScriptableHelperImpl() {
  242   // Emit the ondelete signal, as early as possible.
  243   on_reference_change_signal_(0, 0);
  244   ASSERT(ref_count_ == 0);
  245 
  246   // Free all owned slots.
  247   for (PropertyInfoMap::iterator it = property_info_.begin();
  248        it != property_info_.end(); ++it) {
  249     DestroyPropertyInfo(&it->second);
  250   }
  251 
  252   delete array_getter_;
  253   delete array_setter_;
  254   delete dynamic_property_getter_;
  255   delete dynamic_property_setter_;
  256 }
  257 
  258 void ScriptableHelperImpl::EnsureRegistered() {
  259   if (!class_property_info_) {
  260     uint64_t class_id = owner_->GetScriptable()->GetClassId();
  261     ClassInfoMap::iterator it = all_class_info_->find(class_id);
  262     if (it == all_class_info_->end()) {
  263       registering_class_ = true;
  264       owner_->DoClassRegister();
  265       registering_class_ = false;
  266       it = all_class_info_->find(class_id);
  267       if (it == all_class_info_->end()) {
  268         // This class's DoClassRegister() did nothing.
  269         class_property_info_ = blank_property_info_;
  270       } else {
  271         class_property_info_ = &it->second;
  272       }
  273     } else {
  274       class_property_info_ = &it->second;
  275     }
  276     owner_->DoRegister();
  277 #ifdef _DEBUG
  278     class_stat_.map[class_id].total_created++;
  279 #endif
  280   }
  281 }
  282 
  283 void ScriptableHelperImpl::DestroyPropertyInfo(PropertyInfo *info) {
  284   if (info->prototype.type() == Variant::TYPE_SLOT)
  285     delete VariantValue<Slot *>()(info->prototype);
  286 
  287   if (info->type == PROPERTY_NORMAL) {
  288     delete info->u.slots.getter;
  289     delete info->u.slots.setter;
  290   } else if (info->type == PROPERTY_CONSTANT &&
  291              info->prototype.type() == Variant::TYPE_SCRIPTABLE) {
  292     if (info->u.scriptable_info.scriptable) {
  293       ASSERT(info->u.scriptable_info.ref_change_connection);
  294       info->u.scriptable_info.ref_change_connection->Disconnect();
  295       info->u.scriptable_info.ref_change_connection = NULL;
  296       info->u.scriptable_info.scriptable->Unref();
  297       info->u.scriptable_info.scriptable = NULL;
  298       info->prototype = Variant(static_cast<ScriptableInterface *>(NULL));
  299     }
  300   }
  301 }
  302 
  303 void ScriptableHelperImpl::AddPropertyInfo(const char *name, PropertyType type,
  304                                            const Variant &prototype,
  305                                            Slot *getter, Slot *setter) {
  306   uint64_t class_id = owner_->GetScriptable()->GetClassId();
  307   PropertyInfo *info =
  308       registering_class_ ? &(*all_class_info_)[class_id][name] :
  309       &property_info_[name];
  310   if (info->type != PROPERTY_NOT_EXIST) {
  311     // A previously registered property is overriden.
  312     DestroyPropertyInfo(info);
  313   }
  314   info->type = type;
  315   info->prototype = prototype;
  316 
  317   if (type == PROPERTY_CONSTANT &&
  318       prototype.type() == Variant::TYPE_SCRIPTABLE) {
  319     ScriptableInterface *scriptable =
  320         VariantValue<ScriptableInterface *>()(prototype);
  321     if (scriptable) {
  322       info->u.scriptable_info.ref_change_connection =
  323           scriptable->ConnectOnReferenceChange(
  324               NewSlot(info, &PropertyInfo::OnRefChange));
  325       info->u.scriptable_info.scriptable = scriptable;
  326       scriptable->Ref();
  327     }
  328   } else {
  329     info->u.slots.getter = getter;
  330     info->u.slots.setter = setter;
  331   }
  332 
  333 #ifdef _DEBUG
  334   if (registering_class_)
  335     class_stat_.map[class_id].class_property_count++;
  336   else
  337     class_stat_.map[class_id].obj_property_count++;
  338 #endif
  339 }
  340 
  341 static Variant DummyGetter() {
  342   return Variant();
  343 }
  344 
  345 void ScriptableHelperImpl::RegisterProperty(const char *name,
  346                                             Slot *getter, Slot *setter) {
  347   ASSERT(name);
  348   Variant prototype;
  349   ASSERT(!setter || setter->GetArgCount() == 1);
  350   if (getter) {
  351     ASSERT(getter->GetArgCount() == 0);
  352     prototype = Variant(getter->GetReturnType());
  353     ASSERT(!setter || prototype.type() == setter->GetArgTypes()[0]);
  354   } else {
  355     getter = NewSlot(DummyGetter);
  356     if (setter)
  357       prototype = Variant(setter->GetArgTypes()[0]);
  358 
  359     if (prototype.type() == Variant::TYPE_SLOT) {
  360       DLOG("Warning: property '%s' is of type Slot, please make sure the return"
  361            " type of this Slot parameter is void or Variant, or use"
  362            " RegisterSignal instead.", name);
  363     }
  364   }
  365 
  366   AddPropertyInfo(name, PROPERTY_NORMAL, prototype, getter, setter);
  367 }
  368 
  369 class StringEnumGetter : public Slot0<const char *> {
  370  public:
  371   StringEnumGetter(Slot *slot, const char **names, int count)
  372       : slot_(slot), names_(names), count_(count) { }
  373   virtual ~StringEnumGetter() {
  374     delete slot_;
  375   }
  376   virtual ResultVariant Call(ScriptableInterface *obj,
  377                              int argc, const Variant argv[]) const {
  378     GGL_UNUSED(argc);
  379     GGL_UNUSED(argv);
  380     int index = VariantValue<int>()(slot_->Call(obj, 0, NULL).v());
  381     return ResultVariant(index >= 0 && index < count_ ?
  382                          Variant(names_[index]) : Variant(""));
  383   }
  384   virtual bool operator==(const Slot &another) const {
  385     GGL_UNUSED(another);
  386     return false; // Not used.
  387   }
  388   Slot *slot_;
  389   const char **names_;
  390   int count_;
  391 };
  392 
  393 class StringEnumSetter : public Slot1<void, const char *> {
  394  public:
  395   StringEnumSetter(Slot *slot, const char **names, int count)
  396       : slot_(slot), names_(names), count_(count) { }
  397   virtual ~StringEnumSetter() {
  398     delete slot_;
  399   }
  400   virtual ResultVariant Call(ScriptableInterface *obj,
  401                              int argc, const Variant argv[]) const {
  402     GGL_UNUSED(argc);
  403     GGL_UNUSED(argv);
  404     const char *name = VariantValue<const char *>()(argv[0]);
  405     if (!name)
  406       return ResultVariant();
  407     for (int i = 0; i < count_; i++) {
  408       if (strcmp(name, names_[i]) == 0) {
  409         Variant param(i);
  410         slot_->Call(obj, 1, &param);
  411         return ResultVariant();
  412       }
  413     }
  414     LOG("Invalid enumerated name: %s", name);
  415     return ResultVariant();
  416   }
  417   virtual bool operator==(const Slot &another) const {
  418     GGL_UNUSED(another);
  419     return false; // Not used.
  420   }
  421   Slot *slot_;
  422   const char **names_;
  423   int count_;
  424 };
  425 
  426 void ScriptableHelperImpl::RegisterStringEnumProperty(
  427     const char *name, Slot *getter, Slot *setter,
  428     const char **names, int count) {
  429   ASSERT(getter);
  430   RegisterProperty(name, new StringEnumGetter(getter, names, count),
  431                    setter ? new StringEnumSetter(setter, names, count) : NULL);
  432 }
  433 
  434 void ScriptableHelperImpl::RegisterMethod(const char *name, Slot *slot) {
  435   ASSERT(name);
  436   ASSERT(slot);
  437   AddPropertyInfo(name, PROPERTY_METHOD, Variant(slot), NULL, NULL);
  438 }
  439 
  440 void ScriptableHelperImpl::RegisterSignal(const char *name, Signal *signal) {
  441   ASSERT(!registering_class_);
  442   ASSERT(name);
  443   ASSERT(signal);
  444 
  445   // Create a SignalSlot as the value of the prototype to let others know
  446   // the calling convention.  It is owned by property_info_.
  447   Variant prototype = Variant(new SignalSlot(signal));
  448   Slot *getter = NewSlot(signal, &Signal::GetDefaultSlot);
  449   Slot *setter = NewSlot(signal, &Signal::SetDefaultSlot);
  450   AddPropertyInfo(name, PROPERTY_NORMAL, prototype, getter, setter);
  451 }
  452 
  453 class ClassSignalGetter : public Slot0<Slot *> {
  454  public:
  455   ClassSignalGetter(ClassSignal *class_signal)
  456       : class_signal_(class_signal) {
  457   }
  458   virtual ResultVariant Call(ScriptableInterface *obj,
  459                              int argc, const Variant argv[]) const {
  460     GGL_UNUSED(argc);
  461     GGL_UNUSED(argv);
  462     Slot *slot = class_signal_->GetSignal(obj)->GetDefaultSlot();
  463     return ResultVariant(Variant(slot));
  464   }
  465   virtual bool operator==(const Slot &another) const {
  466     GGL_UNUSED(another);
  467     return false; // Not used.
  468   }
  469  private:
  470   ClassSignal *class_signal_;
  471 };
  472 
  473 class ClassSignalSetter : public Slot1<void, Slot *> {
  474  public:
  475   ClassSignalSetter(ClassSignal *class_signal)
  476       : class_signal_(class_signal) {
  477   }
  478   virtual ~ClassSignalSetter() {
  479     // class_signal_ is shared between ClassSignalGetter and ClassSignalSetter.
  480     // Let this class care for the deletion.
  481     delete class_signal_;
  482   }
  483   virtual ResultVariant Call(ScriptableInterface *obj,
  484                              int argc, const Variant argv[]) const {
  485     GGL_UNUSED(argc);
  486     ASSERT(argc == 1);
  487     Signal *signal = class_signal_->GetSignal(obj);
  488     Slot *slot = VariantValue<Slot *>()(argv[0]);
  489     signal->SetDefaultSlot(slot);
  490     return ResultVariant();
  491   }
  492   virtual bool operator==(const Slot &another) const {
  493     GGL_UNUSED(another);
  494     return false; // Not used.
  495   }
  496  private:
  497   ClassSignal *class_signal_;
  498 };
  499 
  500 void ScriptableHelperImpl::RegisterClassSignal(const char *name,
  501                                                ClassSignal *class_signal) {
  502   ASSERT(registering_class_);
  503   ASSERT(name);
  504   ASSERT(class_signal);
  505   Variant prototype = Variant(class_signal->NewPrototypeSlot());
  506   AddPropertyInfo(name, PROPERTY_NORMAL, prototype,
  507                   new ClassSignalGetter(class_signal),
  508                   new ClassSignalSetter(class_signal));
  509 }
  510 
  511 void ScriptableHelperImpl::RegisterVariantConstant(const char *name,
  512                                                    const Variant &value) {
  513   ASSERT(name);
  514   ASSERT_M(value.type() != Variant::TYPE_SLOT,
  515            ("Don't register Slot constant. Use RegisterMethod instead."));
  516   AddPropertyInfo(name, PROPERTY_CONSTANT, value, NULL, NULL);
  517 }
  518 
  519 void ScriptableHelperImpl::SetInheritsFrom(
  520     ScriptableInterface *inherits_from) {
  521   ASSERT(!registering_class_);
  522   inherits_from_ = inherits_from;
  523 }
  524 
  525 void ScriptableHelperImpl::SetArrayHandler(Slot *getter, Slot *setter) {
  526   ASSERT(!registering_class_);
  527   ASSERT(getter && getter->GetArgCount() == 1 &&
  528          getter->GetArgTypes()[0] == Variant::TYPE_INT64);
  529   ASSERT(!setter || (setter->GetArgCount() == 2 &&
  530                      setter->GetArgTypes()[0] == Variant::TYPE_INT64 &&
  531                      setter->GetReturnType() == Variant::TYPE_BOOL));
  532   delete array_getter_;
  533   delete array_setter_;
  534   array_getter_ = getter;
  535   array_setter_ = setter;
  536 }
  537 
  538 void ScriptableHelperImpl::SetDynamicPropertyHandler(
  539     Slot *getter, Slot *setter) {
  540   ASSERT(!registering_class_);
  541   ASSERT(getter &&
  542          ((getter->GetArgCount() == 1 &&
  543            getter->GetArgTypes()[0] == Variant::TYPE_STRING) ||
  544           (getter->GetArgCount() == 2 &&
  545            getter->GetArgTypes()[0] == Variant::TYPE_STRING &&
  546            getter->GetArgTypes()[1] == Variant::TYPE_BOOL)));
  547   ASSERT(!setter || (setter->GetArgCount() == 2 &&
  548                      setter->GetArgTypes()[0] == Variant::TYPE_STRING &&
  549                      setter->GetReturnType() == Variant::TYPE_BOOL));
  550   delete dynamic_property_getter_;
  551   delete dynamic_property_setter_;
  552   dynamic_property_getter_ = getter;
  553   dynamic_property_setter_ = setter;
  554 }
  555 
  556 void ScriptableHelperImpl::Ref() const {
  557 #ifdef VERBOSE_DEBUG_REF
  558   DLOG("Ref ref_count_ = %d", ref_count_);
  559 #endif
  560   ASSERT(ref_count_ >= 0);
  561   on_reference_change_signal_(ref_count_, 1);
  562   ref_count_++;
  563 }
  564 
  565 void ScriptableHelperImpl::Unref(bool transient) const {
  566   GGL_UNUSED(transient);
  567   // The parameter traisnent is ignored here. Let the ScriptableHelper
  568   // template deal with it.
  569 #ifdef VERBOSE_DEBUG_REF
  570   DLOG("Unref ref_count_ = %d", ref_count_);
  571 #endif
  572   ASSERT(ref_count_ > 0);
  573   on_reference_change_signal_(ref_count_, -1);
  574   ref_count_--;
  575 }
  576 
  577 Connection *ScriptableHelperImpl::ConnectOnReferenceChange(
  578     Slot2<void, int, int> *slot) {
  579   return on_reference_change_signal_.Connect(slot);
  580 }
  581 
  582 const ScriptableHelperImpl::PropertyInfo *
  583 ScriptableHelperImpl::GetPropertyInfoInternal(const char *name) {
  584   EnsureRegistered();
  585   ASSERT(class_property_info_);
  586   PropertyInfoMap::const_iterator it = property_info_.find(name);
  587   bool found = it != property_info_.end();
  588   if (!found) {
  589     it = class_property_info_->find(name);
  590     found = it != class_property_info_->end();
  591   }
  592   return found ? &it->second : NULL;
  593 }
  594 
  595 ScriptableInterface::PropertyType ScriptableHelperImpl::GetPropertyInfo(
  596     const char *name, Variant *prototype) {
  597   const PropertyInfo *info = GetPropertyInfoInternal(name);
  598   if (info) {
  599     if (prototype)
  600       *prototype = info->prototype;
  601     return info->type;
  602   }
  603 
  604   // Try dynamic properties.
  605   if (dynamic_property_getter_) {
  606     // The second parameter means get property's info.
  607     Variant params[] = { Variant(name), Variant(true) };
  608     int argc = dynamic_property_getter_->GetArgCount();
  609     Variant dynamic_value = dynamic_property_getter_->Call(
  610         owner_->GetScriptable(), argc, params).v();
  611     if (dynamic_value.type() != Variant::TYPE_VOID) {
  612       if (prototype) {
  613         // Returns the slot as prototype, in case this dynamic property is
  614         // a signal.
  615         if (dynamic_value.type() == Variant::TYPE_SLOT)
  616           *prototype = dynamic_value;
  617         else
  618           *prototype = Variant(dynamic_value.type());
  619       }
  620       return PROPERTY_DYNAMIC;
  621     }
  622   }
  623 
  624   // Try inherited properties.
  625   if (inherits_from_)
  626     return inherits_from_->GetPropertyInfo(name, prototype);
  627   return PROPERTY_NOT_EXIST;
  628 }
  629 
  630 // NOTE: Must be exception-safe because the handler may throw exceptions.
  631 ResultVariant ScriptableHelperImpl::GetProperty(const char *name) {
  632   const PropertyInfo *info = GetPropertyInfoInternal(name);
  633   if (info) {
  634     switch (info->type) {
  635       case PROPERTY_NORMAL:
  636         ASSERT(info->u.slots.getter);
  637         return info->u.slots.getter->Call(owner_->GetScriptable(), 0, NULL);
  638       case PROPERTY_CONSTANT:
  639       case PROPERTY_METHOD:
  640         return ResultVariant(info->prototype);
  641       default:
  642         ASSERT(false);
  643         break;
  644     }
  645   } else {
  646     if (dynamic_property_getter_) {
  647       // The second parameter means get property's value.
  648       Variant params[] = { Variant(name), Variant(false) };
  649       int argc = dynamic_property_getter_->GetArgCount();
  650       ResultVariant result =
  651           dynamic_property_getter_->Call(owner_->GetScriptable(), argc, params);
  652       if (result.v().type() != Variant::TYPE_VOID)
  653         return result;
  654     }
  655     if (inherits_from_)
  656       return inherits_from_->GetProperty(name);
  657   }
  658   return ResultVariant();
  659 }
  660 
  661 // NOTE: Must be exception-safe because the handler may throw exceptions.
  662 bool ScriptableHelperImpl::SetProperty(const char *name,
  663                                        const Variant &value) {
  664   const PropertyInfo *info = GetPropertyInfoInternal(name);
  665   if (info) {
  666     switch (info->type) {
  667       case PROPERTY_NORMAL:
  668         if (info->u.slots.setter) {
  669           info->u.slots.setter->Call(owner_->GetScriptable(), 1, &value);
  670           return true;
  671         }
  672         return false;
  673       case PROPERTY_CONSTANT:
  674       case PROPERTY_METHOD:
  675         return false;
  676       default:
  677         ASSERT(false);
  678         break;
  679     }
  680   } else {
  681     if (dynamic_property_setter_) {
  682       Variant params[] = { Variant(name), value };
  683       Variant result = dynamic_property_setter_->Call(
  684           owner_->GetScriptable(), 2, params).v();
  685       ASSERT(result.type() == Variant::TYPE_BOOL);
  686       if (VariantValue<bool>()(result))
  687         return true;
  688     }
  689     if (inherits_from_ && inherits_from_->SetProperty(name, value))
  690       return true;
  691   }
  692   return false;
  693 }
  694 
  695 // NOTE: Must be exception-safe because the handler may throw exceptions.
  696 ResultVariant ScriptableHelperImpl::GetPropertyByIndex(int index) {
  697   EnsureRegistered();
  698   if (array_getter_) {
  699     Variant param(index);
  700     return array_getter_->Call(owner_->GetScriptable(), 1, &param);
  701   }
  702   return ResultVariant();
  703 }
  704 
  705 // NOTE: Must be exception-safe because the handler may throw exceptions.
  706 bool ScriptableHelperImpl::SetPropertyByIndex(int index,
  707                                               const Variant &value) {
  708   EnsureRegistered();
  709   if (array_setter_) {
  710     Variant params[] = { Variant(index), value };
  711     Variant result = array_setter_->Call(owner_->GetScriptable(),
  712                                          2, params).v();
  713     ASSERT(result.type() == Variant::TYPE_BOOL);
  714     return VariantValue<bool>()(result);
  715   }
  716   return false;
  717 }
  718 
  719 void ScriptableHelperImpl::SetPendingException(ScriptableInterface *exception) {
  720   ASSERT(pending_exception_ == NULL);
  721   pending_exception_ = exception;
  722 }
  723 
  724 ScriptableInterface *ScriptableHelperImpl::GetPendingException(bool clear) {
  725   ScriptableInterface *result = pending_exception_;
  726   if (clear)
  727     pending_exception_ = NULL;
  728   return result;
  729 }
  730 
  731 class ScriptableHelperImpl::InheritedPropertiesCallback {
  732  public:
  733   InheritedPropertiesCallback(ScriptableHelperImpl *owner,
  734                               EnumeratePropertiesCallback *callback)
  735       : owner_(owner), callback_(callback) {
  736   }
  737 
  738   bool Callback(const char *name, PropertyType type, const Variant &value) {
  739     if (owner_->property_info_.find(name) ==
  740             owner_->property_info_.end() &&
  741         owner_->class_property_info_->find(name) ==
  742             owner_->class_property_info_->end()) {
  743       // Only emunerate inherited properties which are not overriden by this
  744       // scriptable object.
  745       return (*callback_)(name, type, value);
  746     }
  747     return true;
  748   }
  749 
  750   ScriptableHelperImpl *owner_;
  751   EnumeratePropertiesCallback *callback_;
  752 };
  753 
  754 bool ScriptableHelperImpl::EnumerateProperties(
  755     EnumeratePropertiesCallback *callback) {
  756   ASSERT(callback);
  757   EnsureRegistered();
  758 
  759   // The algorithm below is not optimal, but is fairly clear and short.
  760   // This is not a big problem because this method impl is only used in
  761   // unittests.
  762   if (inherits_from_) {
  763     InheritedPropertiesCallback inherited_callback(this, callback);
  764     if (!inherits_from_->EnumerateProperties(
  765         NewSlot(&inherited_callback, &InheritedPropertiesCallback::Callback))) {
  766       delete callback;
  767       return false;
  768     }
  769   }
  770   for (PropertyInfoMap::const_iterator it = class_property_info_->begin();
  771        it != class_property_info_->end(); ++it) {
  772     if (property_info_.find(it->first) == property_info_.end()) {
  773       ResultVariant value = GetProperty(it->first);
  774       if (!(*callback)(it->first, it->second.type, value.v())) {
  775         delete callback;
  776         return false;
  777       }
  778     }
  779   }
  780   for (PropertyInfoMap::const_iterator it = property_info_.begin();
  781        it != property_info_.end(); ++it) {
  782     ResultVariant value = GetProperty(it->first);
  783     if (!(*callback)(it->first, it->second.type, value.v())) {
  784       delete callback;
  785       return false;
  786     }
  787   }
  788   delete callback;
  789   return true;
  790 }
  791 
  792 bool ScriptableHelperImpl::EnumerateElements(
  793     EnumerateElementsCallback *callback) {
  794   // This helper does nothing.
  795   delete callback;
  796   return true;
  797 }
  798 
  799 } // namespace internal
  800 
  801 } // namespace ggadget