00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "kjsprototype.h"
00023 #include "kjsinterpreter.h"
00024 #include "kjsarguments.h"
00025 #include "kjsprivate.h"
00026
00027 #include "kjs/object.h"
00028 #include "kjs/JSVariableObject.h"
00029 #include "kjs/context.h"
00030 #include "kjs/interpreter.h"
00031
00032 #include <QMap>
00033
00034 using namespace KJS;
00035
00036 class KJSCustomProperty
00037 {
00038 public:
00039 KJSCustomProperty(KJSPrototype::PropertyGetter g,
00040 KJSPrototype::PropertySetter s)
00041 : getter(g), setter(s)
00042 {
00043 }
00044
00045 JSValue* read(ExecState* exec, void* object);
00046 void write(ExecState* exec, void* object, JSValue* value);
00047
00048 private:
00049 KJSPrototype::PropertyGetter getter;
00050 KJSPrototype::PropertySetter setter;
00051 };
00052
00053 class CustomObjectInfo {
00054 public:
00055 CustomObjectInfo(void* v): iv(v) {}
00056 virtual ~CustomObjectInfo() {}
00057 void* internalValue() { return iv; }
00058 protected:
00059 void* iv;
00060 };
00061
00062 template<class Base>
00063 class CustomObject : public Base, public CustomObjectInfo {
00064 public:
00065 CustomObject(JSValue* proto, void* v)
00066 : Base(proto),
00067 CustomObjectInfo(v)
00068 {}
00069
00070 void put(ExecState* exec, const Identifier& id,
00071 JSValue *value, int attr = None);
00072
00073
00074
00075 static const ClassInfo info;
00076 const ClassInfo* classInfo() const { return &info; }
00077 };
00078
00079 template<>
00080 const ClassInfo CustomObject<JSObject>::info = { "CustomObject", 0, 0, 0 };
00081
00082 template<>
00083 const ClassInfo CustomObject<JSGlobalObject>::info = { "CustomGlobalObject", 0, 0, 0 };
00084
00085 class KJSCustomFunction : public JSObject {
00086 public:
00087 KJSCustomFunction(ExecState* exec, KJSPrototype::FunctionCall f)
00088 : callback(f)
00089 {
00090 setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
00091 }
00092
00093 JSValue* callAsFunction(ExecState* exec, JSObject* thisObj,
00094 const List &args);
00095 bool implementsCall() const { return true; }
00096
00097 private:
00098 KJSPrototype::FunctionCall callback;
00099 };
00100
00101 JSValue* KJSCustomFunction::callAsFunction(ExecState* exec, JSObject* thisObj,
00102 const List &args)
00103 {
00104
00105 CustomObjectInfo* inf = dynamic_cast<CustomObjectInfo*>(thisObj);
00106
00107 if (!inf) {
00108 const char* errMsg = "Attempt at calling a function with an invalid receiver";
00109 KJS::JSObject *err = KJS::Error::create(exec, KJS::TypeError, errMsg);
00110 exec->setException(err);
00111 return err;
00112 }
00113
00114 void* thisValue = inf->internalValue();
00115
00116 KJSContext ctx(EXECSTATE_HANDLE(exec));
00117 KJSArguments a(LIST_HANDLE(&args));
00118 KJSObject res = (*callback)(&ctx, thisValue, a);
00119 return JSVALUE(&res);
00120 }
00121
00122 JSValue* KJSCustomProperty::read(ExecState* exec, void* object)
00123 {
00124 assert(getter);
00125
00126 KJSContext ctx(EXECSTATE_HANDLE(exec));
00127 KJSObject res = (*getter)(&ctx, object);
00128 return JSVALUE(&res);
00129 }
00130
00131 void KJSCustomProperty::write(ExecState* exec, void* object, JSValue* value)
00132 {
00133 KJSContext ctx(EXECSTATE_HANDLE(exec));
00134
00135 if (setter) {
00136 KJSObject vo(JSVALUE_HANDLE(value));
00137 (*setter)(&ctx, object, vo);
00138 } else {
00139 JSObject *e = Error::create(exec, GeneralError,
00140 "Property is read-only");
00141 exec->setException(e);
00142 }
00143 }
00144
00145 static JSValue* getPropertyValue(ExecState* exec, JSObject *originalObject,
00146 const Identifier&, const PropertySlot& sl)
00147 {
00148 CustomObjectInfo* inf = dynamic_cast<CustomObjectInfo*>(originalObject);
00149 if (!inf)
00150 return jsUndefined();
00151
00152 KJSCustomProperty* p =
00153 reinterpret_cast<KJSCustomProperty*>(sl.customValue());
00154
00155 return p->read(exec, inf->internalValue());
00156 }
00157
00158
00159
00160 typedef QMap<UString, KJSCustomProperty*> CustomPropertyMap;
00161
00162 class CustomPrototype : public JSObject {
00163 public:
00164 CustomPrototype()
00165 {
00166 }
00167 ~CustomPrototype()
00168 {
00169 qDeleteAll(properties);
00170 }
00171
00172 bool getOwnPropertySlot(ExecState *exec, const Identifier& id,
00173 PropertySlot& sl)
00174 {
00175 CustomPropertyMap::iterator it = properties.find(id.ustring());
00176 if (it == properties.end())
00177 return JSObject::getOwnPropertySlot(exec, id, sl);
00178
00179 sl.setCustomValue(0, *it, getPropertyValue);
00180
00181 return true;
00182 }
00183
00184 void registerProperty(const QString& name,
00185 KJSPrototype::PropertyGetter g,
00186 KJSPrototype::PropertySetter s)
00187 {
00188 properties.insert(toUString(name), new KJSCustomProperty(g, s));
00189 }
00190
00191 void registerFunction(ExecState* exec,
00192 const QString& name, KJSPrototype::FunctionCall f)
00193 {
00194 putDirect(toIdentifier(name), new KJSCustomFunction(exec, f));
00195 }
00196
00197 template<typename Base>
00198 bool setProperty(ExecState* exec, CustomObject<Base>* obj,
00199 const Identifier& id, JSValue* value)
00200 {
00201 CustomPropertyMap::iterator it = properties.find(id.ustring());
00202 if (it == properties.end())
00203 return false;
00204
00205 (*it)->write(exec, obj->internalValue(), value);
00206
00207 return true;
00208 }
00209
00210 private:
00211 CustomPropertyMap properties;
00212 };
00213
00214 template<class Base>
00215 void CustomObject<Base>::put(ExecState* exec, const Identifier& id,
00216 JSValue* value, int attr)
00217 {
00218 CustomPrototype* p = static_cast<CustomPrototype*>(this->prototype());
00219
00220 if (!p->setProperty(exec, this, id, value))
00221 Base::put(exec, id, value, attr);
00222 }
00223
00224 KJSPrototype::KJSPrototype()
00225 {
00226 CustomPrototype* p = new CustomPrototype;
00227 gcProtect(p);
00228
00229 hnd = PROTOTYPE_HANDLE(p);
00230 }
00231
00232 KJSPrototype::~KJSPrototype()
00233 {
00234 gcUnprotect(PROTOTYPE(this));
00235 }
00236
00237 void KJSPrototype::defineConstant(const QString& name, double value)
00238 {
00239 CustomPrototype* p = PROTOTYPE(this);
00240
00241 p->putDirect(toIdentifier(name), jsNumber(value),
00242 DontEnum|DontDelete|ReadOnly);
00243 }
00244
00245 void KJSPrototype::defineConstant(const QString& name, const QString& value)
00246 {
00247 CustomPrototype* p = PROTOTYPE(this);
00248
00249 p->putDirect(toIdentifier(name), jsString(toUString(value)),
00250 DontEnum|DontDelete|ReadOnly);
00251 }
00252
00253 void KJSPrototype::defineConstant(const QString& name, const KJSObject& value)
00254 {
00255 CustomPrototype* p = PROTOTYPE(this);
00256
00257 p->putDirect(toIdentifier(name), JSVALUE(&value),
00258 DontEnum|DontDelete|ReadOnly);
00259 }
00260
00261 KJSObject KJSPrototype::constructObject(KJSContext* ctx, void *internalValue)
00262 {
00263 CustomPrototype* p = PROTOTYPE(this);
00264
00265 if (ctx && !p->prototype()) {
00266 ExecState* exec = EXECSTATE(ctx);
00267 KJS::Interpreter* i = exec->lexicalInterpreter();
00268 JSObject* objectProto = i->builtinObjectPrototype();
00269 p->setPrototype(objectProto);
00270 }
00271
00272 CustomObject<JSObject>* newObj = new CustomObject<JSObject>(p, internalValue);
00273 return KJSObject(JSVALUE_HANDLE(newObj));
00274 }
00275
00276 KJSGlobalObject KJSPrototype::constructGlobalObject(void *internalValue)
00277 {
00278 CustomPrototype* p = PROTOTYPE(this);
00279
00280 CustomObject<JSGlobalObject>* newObj = new CustomObject<JSGlobalObject>(p, internalValue);
00281 return KJSGlobalObject(JSVALUE_HANDLE(newObj));
00282 }
00283
00284 void KJSPrototype::defineProperty(KJSContext* ctx,
00285 const QString& name,
00286 PropertyGetter getter,
00287 PropertySetter setter)
00288 {
00289 assert(getter);
00290
00291 CustomPrototype* p = PROTOTYPE(this);
00292
00293 p->registerProperty(name, getter, setter);
00294 }
00295
00296 void KJSPrototype::defineFunction(KJSContext* ctx,
00297 const QString& name, FunctionCall callback)
00298 {
00299 assert(callback);
00300
00301 CustomPrototype* p = PROTOTYPE(this);
00302 ExecState* exec = EXECSTATE(ctx);
00303
00304 p->registerFunction(exec, name, callback);
00305 }
00306
00307
00308