Fixed <rdar://problem/3674747> Need to implement invokeUndefinedMethodFromWeb...
authorrjw <rjw@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Aug 2004 02:51:46 +0000 (02:51 +0000)
committerrjw <rjw@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Aug 2004 02:51:46 +0000 (02:51 +0000)
The following WebScripting methods are now supported on bound
objects:

- (id)invokeUndefinedMethodFromWebScript:(NSString *)name withArguments:(NSArray *)args;
        - (void)setValue:(id)value forUndefinedKey:(NSString *)key
        - (id)valueForUndefinedKey:(NSString *)key

        Reviewed by Chris.

        * bindings/c/c_class.cpp:
        (CClass::fieldNamed):
        * bindings/c/c_class.h:
        * bindings/jni/jni_class.cpp:
        (JavaClass::fieldNamed):
        * bindings/jni/jni_class.h:
        * bindings/objc/objc_class.h:
        (KJS::Bindings::ObjcClass::isa):
        * bindings/objc/objc_class.mm:
        (ObjcClass::methodsNamed):
        (ObjcClass::fieldNamed):
        (ObjcClass::fallbackObject):
        * bindings/objc/objc_instance.h:
        * bindings/objc/objc_instance.mm:
        (ObjcInstance::invokeMethod):
        (ObjcInstance::setValueOfField):
        (ObjcInstance::setValueOfUndefinedField):
        (ObjcInstance::getValueOfField):
        (ObjcInstance::getValueOfUndefinedField):
        * bindings/objc/objc_runtime.h:
        (KJS::Bindings::ObjcField::~ObjcField):
        (KJS::Bindings::ObjcField::ObjcField):
        (KJS::Bindings::ObjcField::operator=):
        (KJS::Bindings::FallbackObjectImp::classInfo):
        * bindings/objc/objc_runtime.mm:
        (ObjcField::ObjcField):
        (ObjcField::name):
        (ObjcField::type):
        (ObjcField::valueFromInstance):
        (ObjcField::setValueToInstance):
        (FallbackObjectImp::FallbackObjectImp):
        (FallbackObjectImp::get):
        (FallbackObjectImp::put):
        (FallbackObjectImp::canPut):
        (FallbackObjectImp::implementsCall):
        (FallbackObjectImp::call):
        (FallbackObjectImp::hasProperty):
        (FallbackObjectImp::deleteProperty):
        (FallbackObjectImp::defaultValue):
        * bindings/runtime.h:
        (KJS::Bindings::Class::fallbackObject):
        (KJS::Bindings::Instance::getValueOfUndefinedField):
        (KJS::Bindings::Instance::setValueOfUndefinedField):
        * bindings/runtime_object.cpp:
        (RuntimeObjectImp::get):
        (RuntimeObjectImp::put):
        (RuntimeObjectImp::canPut):
        (RuntimeObjectImp::hasProperty):
        * bindings/testbindings.mm:
        (-[MyFirstInterface valueForUndefinedKey:]):
        (-[MyFirstInterface setValue:forUndefinedKey:]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@7227 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/bindings/c/c_class.cpp
JavaScriptCore/bindings/c/c_class.h
JavaScriptCore/bindings/jni/jni_class.cpp
JavaScriptCore/bindings/jni/jni_class.h
JavaScriptCore/bindings/objc/objc_class.h
JavaScriptCore/bindings/objc/objc_class.mm
JavaScriptCore/bindings/objc/objc_instance.h
JavaScriptCore/bindings/objc/objc_instance.mm
JavaScriptCore/bindings/objc/objc_runtime.h
JavaScriptCore/bindings/objc/objc_runtime.mm
JavaScriptCore/bindings/runtime.h
JavaScriptCore/bindings/runtime_object.cpp
JavaScriptCore/bindings/testbindings.mm

index 99bca111e64cc6e1a482c5383aaf992cbf55271d..02a3ae44080ba05489d782fed291d2f60312a099 100644 (file)
@@ -1,3 +1,68 @@
+2004-08-10  Richard Williamson   <rjw@apple.com>
+
+        Fixed <rdar://problem/3674747> Need to implement invokeUndefinedMethodFromWebScript:withArguments:
+
+       The following WebScripting methods are now supported on bound
+       objects:
+
+       - (id)invokeUndefinedMethodFromWebScript:(NSString *)name withArguments:(NSArray *)args;
+        - (void)setValue:(id)value forUndefinedKey:(NSString *)key
+        - (id)valueForUndefinedKey:(NSString *)key
+
+        Reviewed by Chris.
+
+        * bindings/c/c_class.cpp:
+        (CClass::fieldNamed):
+        * bindings/c/c_class.h:
+        * bindings/jni/jni_class.cpp:
+        (JavaClass::fieldNamed):
+        * bindings/jni/jni_class.h:
+        * bindings/objc/objc_class.h:
+        (KJS::Bindings::ObjcClass::isa):
+        * bindings/objc/objc_class.mm:
+        (ObjcClass::methodsNamed):
+        (ObjcClass::fieldNamed):
+        (ObjcClass::fallbackObject):
+        * bindings/objc/objc_instance.h:
+        * bindings/objc/objc_instance.mm:
+        (ObjcInstance::invokeMethod):
+        (ObjcInstance::setValueOfField):
+        (ObjcInstance::setValueOfUndefinedField):
+        (ObjcInstance::getValueOfField):
+        (ObjcInstance::getValueOfUndefinedField):
+        * bindings/objc/objc_runtime.h:
+        (KJS::Bindings::ObjcField::~ObjcField):
+        (KJS::Bindings::ObjcField::ObjcField):
+        (KJS::Bindings::ObjcField::operator=):
+        (KJS::Bindings::FallbackObjectImp::classInfo):
+        * bindings/objc/objc_runtime.mm:
+        (ObjcField::ObjcField):
+        (ObjcField::name):
+        (ObjcField::type):
+        (ObjcField::valueFromInstance):
+        (ObjcField::setValueToInstance):
+        (FallbackObjectImp::FallbackObjectImp):
+        (FallbackObjectImp::get):
+        (FallbackObjectImp::put):
+        (FallbackObjectImp::canPut):
+        (FallbackObjectImp::implementsCall):
+        (FallbackObjectImp::call):
+        (FallbackObjectImp::hasProperty):
+        (FallbackObjectImp::deleteProperty):
+        (FallbackObjectImp::defaultValue):
+        * bindings/runtime.h:
+        (KJS::Bindings::Class::fallbackObject):
+        (KJS::Bindings::Instance::getValueOfUndefinedField):
+        (KJS::Bindings::Instance::setValueOfUndefinedField):
+        * bindings/runtime_object.cpp:
+        (RuntimeObjectImp::get):
+        (RuntimeObjectImp::put):
+        (RuntimeObjectImp::canPut):
+        (RuntimeObjectImp::hasProperty):
+        * bindings/testbindings.mm:
+        (-[MyFirstInterface valueForUndefinedKey:]):
+        (-[MyFirstInterface setValue:forUndefinedKey:]):
+
 2004-08-10  Darin Adler  <darin@apple.com>
 
         Reviewed by Dave.
index 46edc08c94323b4c3220124f648536e758c18aac..59da418d4c4de425956a148cac626dd73ae2ff7a 100644 (file)
@@ -106,7 +106,7 @@ MethodList CClass::methodsNamed(const char *_name) const
 }
 
 
-Field *CClass::fieldNamed(const char *name) const
+Field *CClass::fieldNamed(const char *name, Instance *instance) const
 {
     CFStringRef fieldName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII);
     Field *aField = (Field *)CFDictionaryGetValue (_fields, fieldName);
index 5031d20e3bafa42d59e3ae55d42cb87c84260f71..45cc21b4a68a306433c777fbf682e6ab479f3c0f 100644 (file)
@@ -75,7 +75,7 @@ public:
     
     virtual MethodList methodsNamed(const char *name) const;
     
-    virtual Field *fieldNamed(const char *name) const;
+    virtual Field *fieldNamed(const char *name, Instance *instance) const;
     
     virtual Constructor *constructorAt(long i) const {
         return 0;
index 4eef5b72ff9f89b7249124604168d348d2e83b60..d15923f8f34a63c21352de9e9267d179ef15a719 100644 (file)
@@ -170,7 +170,7 @@ MethodList JavaClass::methodsNamed(const char *name) const
 }
 
 
-Field *JavaClass::fieldNamed(const char *name) const
+Field *JavaClass::fieldNamed(const char *name, Instance *instance) const
 {
     CFStringRef fieldName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII);
     Field *aField = (Field *)CFDictionaryGetValue(_fields, fieldName);
index aff4607f13ba608090006cda488efd83e6e06008..63034a800a5f2422de8543cd07cf7061bf22fbd4 100644 (file)
@@ -100,7 +100,7 @@ public:
     
     virtual MethodList methodsNamed(const char *name) const;
     
-    virtual Field *fieldNamed(const char *name) const;
+    virtual Field *fieldNamed(const char *name, Instance *instance) const;
     
     virtual Constructor *constructorAt(long i) const {
         return &_constructors[i]; 
index 1ca73e15db458316c454051ddf6da0a5b67082dc..540730daf20a6f9cc126f4adae503da705ae0227 100644 (file)
@@ -75,14 +75,18 @@ public:
     
     virtual MethodList methodsNamed(const char *name) const;
     
-    virtual Field *fieldNamed(const char *name) const;
+    virtual Field *fieldNamed(const char *name, Instance *instance) const;
+
+    virtual Value fallbackObject(ExecState *exec, Instance *instance, const Identifier &propertyName);
     
     virtual Constructor *constructorAt(long i) const {
         return 0;
     };
     
     virtual long numConstructors() const { return 0; };
-        
+    
+    ClassStructPtr isa() { return _isa; }
+    
 private:
     ClassStructPtr _isa;
     CFDictionaryRef _methods;
index 9676b98aead43a13a662be0342b782e00d83b4ab..122139184d2ad00376ce76448707290d00b30f0f 100644 (file)
@@ -25,6 +25,8 @@
 #include <Foundation/Foundation.h>
 
 #include <objc_class.h>
+#include <objc_instance.h>
+#include <objc_runtime.h>
 #include <objc_utility.h>
 #include <WebScriptObject.h>
 
@@ -137,17 +139,6 @@ MethodList ObjcClass::methodsNamed(const char *_name) const
         } 
         thisClass = thisClass->super_class;
     }
-
-    if (methodList.length() == 0) {
-        thisClass = _isa;
-        if ([(id)thisClass instancesRespondToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){
-            // Fallback methods are created for one-shot use.  They are created here 
-            // and deleted in ObjcInstance::invokeMethod().
-            ObjcMethod *fallbackMethod = new ObjcMethod (thisClass, (const char *)@selector(invokeUndefinedMethodFromWebScript:withArguments:));
-            fallbackMethod->setJavaScriptName(methodName);
-            methodList.addMethod ((Method *)fallbackMethod);
-        }
-    }
     
     CFRelease (methodName);
 
@@ -155,7 +146,7 @@ MethodList ObjcClass::methodsNamed(const char *_name) const
 }
 
 
-Field *ObjcClass::fieldNamed(const char *name) const
+Field *ObjcClass::fieldNamed(const char *name, Instance *instance) const
 {
     ClassStructPtr thisClass = _isa;
 
@@ -166,42 +157,82 @@ Field *ObjcClass::fieldNamed(const char *name) const
         return aField;
     }
 
-    // FIX ME.  See if the instance implement attributeKeys.  Will need to 
-    // change fieldNamed() signature to take a Bindings::Instance.
-    while (thisClass != 0) {
-        struct objc_ivar_list *fieldsInClass = thisClass->ivars;
-        if (fieldsInClass) {
-            int i, numFieldsInClass = fieldsInClass->ivar_count;
-            for (i = 0; i < numFieldsInClass; i++) {
-                Ivar objcIVar = &fieldsInClass->ivar_list[i];
-                NSString *mappedName = 0;
+    id targetObject = (static_cast<ObjcInstance*>(instance))->getObject();
+    id attributes = [targetObject attributeKeys];
+    if (attributes != nil) {
+        // Class overrides attributeKeys, use that array of key names.
+        unsigned i, count = [attributes count];
+        for (i = 0; i < count; i++) {
+            NSString *keyName = [attributes objectAtIndex: i];
+            const char *UTF8KeyName = [keyName UTF8String]; // ObjC actually only supports ASCII names.
+            
+            // See if the class wants to exclude the selector from visibility in JavaScript.
+            if ([(id)thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)]) {
+                if ([(id)thisClass isKeyExcludedFromWebScript:UTF8KeyName]) {
+                    continue;
+                }
+            }
+            
+            // See if the class want to provide a different name for the selector in JavaScript.
+            // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
+            // of the class.
+            NSString *mappedName = 0;
+            if ([(id)thisClass respondsToSelector:@selector(webScriptNameForKey:)]){
+                mappedName = [(id)thisClass webScriptNameForKey:UTF8KeyName];
+            }
 
-                // See if the class wants to exclude the selector from visibility in JavaScript.
-                if ([(id)thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)]) {
-                    if ([(id)thisClass isKeyExcludedFromWebScript:objcIVar->ivar_name]) {
-                        continue;
+            if ((mappedName && [mappedName isEqual:(NSString *)fieldName]) ||
+                [keyName isEqual:(NSString *)fieldName]) {
+                aField = new ObjcField ((CFStringRef)keyName);
+                CFDictionaryAddValue ((CFMutableDictionaryRef)_fields, fieldName, aField);
+                break;
+            }
+        }
+    }
+    else {
+        // Class doesn't override attributeKeys, so fall back on class runtime
+        // introspection.
+    
+        while (thisClass != 0) {
+            struct objc_ivar_list *fieldsInClass = thisClass->ivars;
+            if (fieldsInClass) {
+                int i, numFieldsInClass = fieldsInClass->ivar_count;
+                for (i = 0; i < numFieldsInClass; i++) {
+                    Ivar objcIVar = &fieldsInClass->ivar_list[i];
+                    NSString *mappedName = 0;
+
+                    // See if the class wants to exclude the selector from visibility in JavaScript.
+                    if ([(id)thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)]) {
+                        if ([(id)thisClass isKeyExcludedFromWebScript:objcIVar->ivar_name]) {
+                            continue;
+                        }
+                    }
+                    
+                    // See if the class want to provide a different name for the selector in JavaScript.
+                    // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
+                    // of the class.
+                    if ([(id)thisClass respondsToSelector:@selector(webScriptNameForKey:)]){
+                        mappedName = [(id)thisClass webScriptNameForKey:objcIVar->ivar_name];
                     }
-                }
-                
-                // See if the class want to provide a different name for the selector in JavaScript.
-                // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
-                // of the class.
-                if ([(id)thisClass respondsToSelector:@selector(webScriptNameForKey:)]){
-                    mappedName = [(id)thisClass webScriptNameForKey:objcIVar->ivar_name];
-                }
 
-                if ((mappedName && [mappedName isEqual:(NSString *)fieldName]) ||
-                    strcmp(objcIVar->ivar_name,name) == 0) {
-                    aField = new ObjcField (objcIVar);
-                    CFDictionaryAddValue ((CFMutableDictionaryRef)_fields, fieldName, aField);
-                    break;
+                    if ((mappedName && [mappedName isEqual:(NSString *)fieldName]) ||
+                        strcmp(objcIVar->ivar_name,name) == 0) {
+                        aField = new ObjcField (objcIVar);
+                        CFDictionaryAddValue ((CFMutableDictionaryRef)_fields, fieldName, aField);
+                        break;
+                    }
                 }
             }
+            thisClass = thisClass->super_class;
         }
-        thisClass = thisClass->super_class;
-    }
 
-    CFRelease (fieldName);
+        CFRelease (fieldName);
+    }
 
     return aField;
-};
+}
+
+KJS::Value ObjcClass::fallbackObject (ExecState *exec, Instance *instance, const Identifier &propertyName)
+{
+    return Object (new FallbackObjectImp(static_cast<ObjcInstance*>(instance), propertyName));
+}
index 9ac1f8b98806532cdb31afebf163d017bc01936d..95f46cf5c43f72a311ef9855d86bb4852aeab5df 100644 (file)
@@ -58,6 +58,12 @@ public:
 
     virtual KJS::Value invokeMethod (KJS::ExecState *exec, const MethodList &method, const KJS::List &args);
 
+    virtual void setValueOfField (KJS::ExecState *exec, const Field *aField, const KJS::Value &aValue) const;
+    virtual void setValueOfUndefinedField (KJS::ExecState *exec, const KJS::Identifier &property, const KJS::Value &aValue);
+    
+    virtual Value ObjcInstance::getValueOfField (KJS::ExecState *exec, const Field *aField) const;
+    virtual KJS::Value getValueOfUndefinedField (KJS::ExecState *exec, const KJS::Identifier &property, KJS::Type hint) const;
+
     ObjectStructPtr getObject() const { return _instance; }
     
     KJS::Value stringValue() const;
index 8951ee2b50a68f01c6aba5fa917ebb4490568338..39ed147c2812ae71823ab759cca19b6946b3b44e 100644 (file)
@@ -36,6 +36,8 @@
 }
 #endif
 
+#include <JavaScriptCore/objc_runtime.h>
+
 using namespace KJS::Bindings;
 using namespace KJS;
 
@@ -233,11 +235,6 @@ NS_DURING
         resultValue = convertObjcValueToValue (exec, buffer, objcValueType);
     }
 
-    // Fallback methods are created for one-shot use.  They are created in 
-    // ObjcClass::methodsNamed() and deleted here.
-    if (method->isFallbackMethod())
-        delete method;
-    
 NS_HANDLER
     
     resultValue = Undefined();
@@ -247,6 +244,65 @@ NS_ENDHANDLER
     return resultValue;
 }
 
+void ObjcInstance::setValueOfField (KJS::ExecState *exec, const Field *aField, const KJS::Value &aValue) const
+{
+    aField->setValueToInstance (exec, this, aValue);
+}
+
+void ObjcInstance::setValueOfUndefinedField (KJS::ExecState *exec, const KJS::Identifier &property, const KJS::Value &aValue)
+{
+    id targetObject = getObject();
+    
+    // This check is not really necessary because NSObject implements
+    // setValue:forUndefinedKey:, and unfortnately the default implementation
+    // throws an exception.
+    if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){
+        
+        NS_DURING
+        
+            ObjcValue objcValue = convertValueToObjcValue (exec, aValue, ObjcObjectType);
+            [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:property.ascii()]];
+        
+        NS_HANDLER
+            
+            // Do nothing.  Class did not override valueForUndefinedKey:.
+            
+        NS_ENDHANDLER
+        
+    }
+}
+
+Value ObjcInstance::getValueOfField (KJS::ExecState *exec, const Field *aField) const {  
+    return aField->valueFromInstance (exec, this);
+}
+
+KJS::Value ObjcInstance::getValueOfUndefinedField (KJS::ExecState *exec, const KJS::Identifier &property, KJS::Type hint) const
+{
+    Value result = Undefined();
+    
+    id targetObject = getObject();
+    
+    // This check is not really necessary because NSObject implements
+    // valueForUndefinedKey:, and unfortnately the default implementation
+    // throws an exception.
+    if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){
+        id objcValue;
+        
+        NS_DURING
+        
+            objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:property.ascii()]];
+            result = convertObjcValueToValue (exec, &objcValue, ObjcObjectType);
+        
+        NS_HANDLER
+            
+            // Do nothing.  Class did not override valueForUndefinedKey:.
+            
+        NS_ENDHANDLER
+        
+    }
+    
+    return result;
+}
 
 KJS::Value ObjcInstance::defaultValue (KJS::Type hint) const
 {
index 9ade5a31b38d4ae3162b2a188d8e01cb60fa4bdb..d2af78139749bb4e38a54cfc9a97029fbf2921f8 100644 (file)
@@ -39,15 +39,31 @@ class Value;
 namespace Bindings
 {
 
+class ObjcInstance;
+
 class ObjcField : public Field
 {
 public:
     ObjcField(Ivar ivar);
+
+    ObjcField(CFStringRef name);
     
-    ~ObjcField() {};
+    ~ObjcField() {
+        if (_name)
+            CFRelease (_name);
+    };
 
     ObjcField(const ObjcField &other) : Field() {
         _ivar = other._ivar;
+
+        if (other._name != _name) {
+            if (_name)
+                CFRelease (_name);
+            if (other._name)
+                _name = (CFStringRef)CFRetain (other._name);
+            else 
+                _name = 0;
+        }
     };
     
     ObjcField &operator=(const ObjcField &other) {
@@ -56,6 +72,15 @@ public:
 
         _ivar = other._ivar;
         
+        if (other._name != _name) {
+            if (_name)
+                CFRelease (_name);
+            if (other._name)
+                _name = (CFStringRef)CFRetain (other._name);
+            else 
+                _name = 0;
+        }
+        
         return *this;
     };
         
@@ -64,9 +89,10 @@ public:
     
     virtual const char *name() const;
     virtual RuntimeType type() const;
-    
+        
 private:
     Ivar _ivar;
+    CFStringRef _name;
 };
 
 
@@ -135,6 +161,40 @@ private:
     ObjectStructPtr _array;
 };
 
+class FallbackObjectImp : public KJS::ObjectImp {
+public:
+    FallbackObjectImp(ObjectImp *proto);
+        
+    FallbackObjectImp(ObjcInstance *i, const KJS::Identifier propertyName);
+
+    const ClassInfo *classInfo() const { return &info; }
+
+    virtual Value get(ExecState *exec, const Identifier &propertyName) const;
+
+    virtual void put(ExecState *exec, const Identifier &propertyName,
+                     const Value &value, int attr = None);
+
+    virtual bool canPut(ExecState *exec, const Identifier &propertyName) const;
+
+    virtual bool implementsCall() const;
+    virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+    virtual bool hasProperty(ExecState *exec,
+                            const Identifier &propertyName) const;
+
+
+    virtual bool deleteProperty(ExecState *exec,
+                                const Identifier &propertyName);
+
+    virtual Value defaultValue(ExecState *exec, Type hint) const;
+
+private:
+    static const ClassInfo info;
+
+    ObjcInstance *_instance;
+    KJS::Identifier _item;
+};
+
 } // namespace Bindings
 
 } // namespace KJS
index cdb3103cfc093ac243b0f70453db552096c6331d..0b867f71364d6482a521d78ba2413a306708aa8d 100644 (file)
@@ -77,16 +77,29 @@ void ObjcMethod::setJavaScriptName (CFStringRef n)
 ObjcField::ObjcField(Ivar ivar) 
 {
     _ivar = ivar;    // Assume ObjectiveC runtime will keep this alive forever
+    _name = 0;
+}
+
+ObjcField::ObjcField(CFStringRef name) 
+{
+    _ivar = 0;
+    _name = (CFStringRef)CFRetain(name);
 }
 
 const char *ObjcField::name() const 
 {
-    return _ivar->ivar_name;
+    if (_ivar)
+        return _ivar->ivar_name;
+    return [(NSString *)_name UTF8String];
 }
 
 RuntimeType ObjcField::type() const 
-{
-    return _ivar->ivar_type;
+{ 
+    if (_ivar)
+        return _ivar->ivar_type;
+    
+    // Type is irrelevant if we use KV to set/get the value.
+    return "";
 }
 
 Value ObjcField::valueFromInstance(KJS::ExecState *exec, const Instance *instance) const
@@ -97,7 +110,7 @@ Value ObjcField::valueFromInstance(KJS::ExecState *exec, const Instance *instanc
 
     NS_DURING
     
-        NSString *key = [NSString stringWithCString:_ivar->ivar_name];
+        NSString *key = [NSString stringWithCString:name()];
         objcValue = [targetObject valueForKey:key];
         
     NS_HANDLER
@@ -134,9 +147,9 @@ void ObjcField::setValueToInstance(KJS::ExecState *exec, const Instance *instanc
     
     NS_DURING
     
-        NSString *key = [NSString stringWithCString:_ivar->ivar_name];
+        NSString *key = [NSString stringWithCString:name()];
         [targetObject setValue:value forKey:key];
-        
+
     NS_HANDLER
         
         Value aValue = Error::create(exec, GeneralError, [[localException reason] lossyCString]);
@@ -238,3 +251,86 @@ unsigned int ObjcArray::getLength() const
 {
     return [_array count];
 }
+
+
+const ClassInfo FallbackObjectImp::info = {"FallbackObject", 0, 0, 0};
+
+FallbackObjectImp::FallbackObjectImp(ObjectImp *proto)
+  : ObjectImp(proto)
+{
+    _instance = 0;
+}
+
+FallbackObjectImp::FallbackObjectImp(ObjcInstance *i, const KJS::Identifier propertyName) : ObjectImp ((ObjectImp *)0)
+{
+    _instance = i;
+    _item = propertyName;
+}
+
+Value FallbackObjectImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+    return Undefined();
+}
+
+void FallbackObjectImp::put(ExecState *exec, const Identifier &propertyName,
+                 const Value &value, int attr)
+{
+}
+
+bool FallbackObjectImp::canPut(ExecState *exec, const Identifier &propertyName) const
+{
+    return false;
+}
+
+
+bool FallbackObjectImp::implementsCall() const
+{
+    return true;
+}
+
+Value FallbackObjectImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+    Value result = Undefined();
+    
+    RuntimeObjectImp *imp = static_cast<RuntimeObjectImp*>(thisObj.imp());
+    if (imp) {
+        Instance *instance = imp->getInternalInstance();
+        
+        instance->begin();
+
+        ObjcInstance *objcInstance = static_cast<ObjcInstance*>(instance);
+        id targetObject = objcInstance->getObject();
+        
+        if ([targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)]){
+            MethodList methodList;
+            ObjcClass *objcClass = static_cast<ObjcClass*>(instance->getClass());
+            ObjcMethod *fallbackMethod = new ObjcMethod (objcClass->isa(), (const char *)@selector(invokeUndefinedMethodFromWebScript:withArguments:));
+            fallbackMethod->setJavaScriptName((CFStringRef)[NSString stringWithCString:_item.ascii()]);
+            methodList.addMethod ((Method *)fallbackMethod);
+            result = instance->invokeMethod(exec, methodList, args);
+            delete fallbackMethod;
+        }
+                
+        instance->end();
+    }
+
+    return result;
+}
+
+bool FallbackObjectImp::hasProperty(ExecState *exec,
+                         const Identifier &propertyName) const
+{
+    return false;
+}
+
+bool FallbackObjectImp::deleteProperty(ExecState *exec,
+                            const Identifier &propertyName)
+{
+    return false;
+}
+
+Value FallbackObjectImp::defaultValue(ExecState *exec, Type hint) const
+{
+    return _instance->getValueOfUndefinedField(exec, _item, hint);
+}
+
index 7e34c6e4b3fb73981de2b312aced7cfb9c9c7f4e..d6594148f306e6be6d240d629de159131d8c5702 100644 (file)
@@ -115,8 +115,10 @@ public:
     virtual Constructor *constructorAt(long i) const = 0;
     virtual long numConstructors() const = 0;
     
-    virtual Field *fieldNamed(const char *name) const = 0;
+    virtual Field *fieldNamed(const char *name, Instance *instance) const = 0;
 
+    virtual Value fallbackObject(KJS::ExecState *exec, Bindings::Instance *instance, const KJS::Identifier &propertyName) { return Undefined(); }
+    
     virtual ~Class() {};
 };
 
@@ -147,7 +149,9 @@ public:
     virtual Class *getClass() const = 0;
     
     virtual KJS::Value getValueOfField (KJS::ExecState *exec, const Field *aField) const;
+    virtual KJS::Value getValueOfUndefinedField (KJS::ExecState *exec, const KJS::Identifier &property, KJS::Type hint) const { return Undefined(); };
     virtual void setValueOfField (KJS::ExecState *exec, const Field *aField, const KJS::Value &aValue) const;
+    virtual void setValueOfUndefinedField (KJS::ExecState *exec, const KJS::Identifier &property, const KJS::Value &aValue) {};
     
     virtual KJS::Value invokeMethod (KJS::ExecState *exec, const MethodList &method, const KJS::List &args) = 0;
     
index 614fb685fa366187271a877bdbc948d5fbd83cbe..8d8c359283ec67bd6d376abd7c07411356e541ec 100644 (file)
@@ -62,29 +62,38 @@ RuntimeObjectImp::RuntimeObjectImp(Bindings::Instance *i, bool oi) : ObjectImp (
 
 Value RuntimeObjectImp::get(ExecState *exec, const Identifier &propertyName) const
 {
+    Value result = Undefined();
+
     instance->begin();
     
     Class *aClass = instance->getClass();
     
     if (aClass) {
+        
         // See if the instance have a field with the specified name.
-        Field *aField = aClass->fieldNamed(propertyName.ascii());
+        Field *aField = aClass->fieldNamed(propertyName.ascii(), instance);
         if (aField) {
-            return instance->getValueOfField (exec, aField); 
+            result = instance->getValueOfField (exec, aField); 
         }
-        
-        // Now check if a method with specified name exists, if so return a function object for
-        // that method.
-        MethodList methodList = aClass->methodsNamed(propertyName.ascii());
-        if (methodList.length() > 0) {
-            instance->end();
-            return Object (new RuntimeMethodImp(exec, propertyName, methodList));
+        else {
+            // Now check if a method with specified name exists, if so return a function object for
+            // that method.
+            MethodList methodList = aClass->methodsNamed(propertyName.ascii());
+            if (methodList.length() > 0) {
+                result = Object (new RuntimeMethodImp(exec, propertyName, methodList));
+            }
         }
-    }
     
+        if (result.type() == UndefinedType) {
+            // Try a fallback object.
+            result = aClass->fallbackObject (exec, instance, propertyName);
+        }
+    }
+        
+        
     instance->end();
-    
-    return Undefined();
+
+    return result;
 }
 
 void RuntimeObjectImp::put(ExecState *exec, const Identifier &propertyName,
@@ -93,10 +102,13 @@ void RuntimeObjectImp::put(ExecState *exec, const Identifier &propertyName,
     instance->begin();
 
     // Set the value of the property.
-    Field *aField = instance->getClass()->fieldNamed(propertyName.ascii());
+    Field *aField = instance->getClass()->fieldNamed(propertyName.ascii(), instance);
     if (aField) {
         getInternalInstance()->setValueOfField(exec, aField, value);
     }
+    else {
+        getInternalInstance()->setValueOfUndefinedField(exec, propertyName, value);
+    }
 
     instance->end();
 }
@@ -105,7 +117,7 @@ bool RuntimeObjectImp::canPut(ExecState *exec, const Identifier &propertyName) c
 {
     instance->begin();
 
-    Field *aField = instance->getClass()->fieldNamed(propertyName.ascii());
+    Field *aField = instance->getClass()->fieldNamed(propertyName.ascii(), instance);
 
     instance->end();
 
@@ -117,7 +129,7 @@ bool RuntimeObjectImp::hasProperty(ExecState *exec,
 {
     instance->begin();
 
-    Field *aField = instance->getClass()->fieldNamed(propertyName.ascii());
+    Field *aField = instance->getClass()->fieldNamed(propertyName.ascii(), instance);
     if (aField) {
         instance->end();
         return true;
index 516410e65e4eeb9f8b37f302a47f09e300b47641..3a950214c0dd8628ef594667e273a085a3119c0a 100644 (file)
        return @"success";
 }
 
+- (id)valueForUndefinedKey:(NSString *)key
+{
+       NSLog (@"%s:  key = %@", __PRETTY_FUNCTION__, key);
+       return @"aValue";
+}
+
+- (void)setValue:(id)value forUndefinedKey:(NSString *)key
+{
+       NSLog (@"%s:  key = %@", __PRETTY_FUNCTION__, key);
+}
+
 - init
 {
     LOG ("\n");