1 /* 2 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Library General Public 6 License as published by the Free Software Foundation; either 7 version 2 of the License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public License 15 along with this library; see the file COPYING.LIB. If not, write to 16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 Boston, MA 02110-1301, USA. 18 */ 19 20 #ifndef qscriptoriginalglobalobject_p_h 21 #define qscriptoriginalglobalobject_p_h 22 23 #include <JavaScriptCore/JavaScript.h> 24 #include <JavaScriptCore/JSRetainPtr.h> 25 #include <QtCore/qvector.h> 26 27 /*! 28 \internal 29 This class is a workaround for missing JSC C API functionality. This class keeps all important 30 properties of an original (default) global object, so we can use it even if the global object was 31 changed. 32 33 FIXME this class is a container for workarounds :-) it should be replaced by proper JSC C API calls. 34 35 The class have to be created on the QScriptEnginePrivate creation time (before any change got applied to 36 global object). 37 */ 38 class QScriptOriginalGlobalObject { 39 public: 40 inline QScriptOriginalGlobalObject(JSGlobalContextRef context); 41 inline ~QScriptOriginalGlobalObject(); 42 43 inline bool objectHasOwnProperty(JSObjectRef object, JSStringRef property) const; 44 inline QVector<JSStringRef> objectGetOwnPropertyNames(JSObjectRef object) const; 45 46 inline bool isDate(JSValueRef value) const; 47 inline bool isArray(JSValueRef value) const; 48 inline bool isError(JSValueRef value) const; 49 50 inline JSValueRef functionPrototype() const; 51 private: 52 inline bool isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const; 53 inline void initializeMember(JSObjectRef globalObject, JSStringRef prototypeName, const char* type, JSObjectRef& constructor, JSValueRef& prototype); 54 55 // Copy of the global context reference (the same as in QScriptEnginePrivate). 56 JSGlobalContextRef m_context; 57 58 // Copy of constructors and prototypes used in isType functions. 59 JSObjectRef m_arrayConstructor; 60 JSValueRef m_arrayPrototype; 61 JSObjectRef m_errorConstructor; 62 JSValueRef m_errorPrototype; 63 JSObjectRef m_functionConstructor; 64 JSValueRef m_functionPrototype; 65 JSObjectRef m_dateConstructor; 66 JSValueRef m_datePrototype; 67 68 // Reference to standard JS functions that are not exposed by JSC C API. 69 JSObjectRef m_hasOwnPropertyFunction; 70 JSObjectRef m_getOwnPropertyNamesFunction; 71 }; 72 73 QScriptOriginalGlobalObject::QScriptOriginalGlobalObject(JSGlobalContextRef context) 74 : m_context(JSGlobalContextRetain(context)) 75 { 76 JSObjectRef globalObject = JSContextGetGlobalObject(m_context); 77 JSValueRef exception = 0; 78 JSRetainPtr<JSStringRef> propertyName; 79 80 propertyName.adopt(JSStringCreateWithUTF8CString("prototype")); 81 initializeMember(globalObject, propertyName.get(), "Array", m_arrayConstructor, m_arrayPrototype); 82 initializeMember(globalObject, propertyName.get(), "Error", m_errorConstructor, m_errorPrototype); 83 initializeMember(globalObject, propertyName.get(), "Function", m_functionConstructor, m_functionPrototype); 84 initializeMember(globalObject, propertyName.get(), "Date", m_dateConstructor, m_datePrototype); 85 86 propertyName.adopt(JSStringCreateWithUTF8CString("hasOwnProperty")); 87 m_hasOwnPropertyFunction = const_cast<JSObjectRef>(JSObjectGetProperty(m_context, globalObject, propertyName.get(), &exception)); 88 JSValueProtect(m_context, m_hasOwnPropertyFunction); 89 Q_ASSERT(JSValueIsObject(m_context, m_hasOwnPropertyFunction)); 90 Q_ASSERT(JSObjectIsFunction(m_context, m_hasOwnPropertyFunction)); 91 Q_ASSERT(!exception); 92 93 propertyName.adopt(JSStringCreateWithUTF8CString("Object")); 94 JSObjectRef objectConstructor 95 = const_cast<JSObjectRef>(JSObjectGetProperty(m_context, globalObject, propertyName.get(), &exception)); 96 propertyName.adopt(JSStringCreateWithUTF8CString("getOwnPropertyNames")); 97 m_getOwnPropertyNamesFunction 98 = const_cast<JSObjectRef>(JSObjectGetProperty(m_context, objectConstructor, propertyName.get(), &exception)); 99 JSValueProtect(m_context, m_getOwnPropertyNamesFunction); 100 Q_ASSERT(JSValueIsObject(m_context, m_getOwnPropertyNamesFunction)); 101 Q_ASSERT(JSObjectIsFunction(m_context, m_getOwnPropertyNamesFunction)); 102 Q_ASSERT(!exception); 103 } 104 105 inline void QScriptOriginalGlobalObject::initializeMember(JSObjectRef globalObject, JSStringRef prototypeName, const char* type, JSObjectRef& constructor, JSValueRef& prototype) 106 { 107 JSRetainPtr<JSStringRef> typeName(Adopt, JSStringCreateWithUTF8CString(type)); 108 JSValueRef exception = 0; 109 110 // Save references to the Type constructor and prototype. 111 JSValueRef typeConstructor = JSObjectGetProperty(m_context, globalObject, typeName.get(), &exception); 112 Q_ASSERT(JSValueIsObject(m_context, typeConstructor)); 113 constructor = JSValueToObject(m_context, typeConstructor, &exception); 114 JSValueProtect(m_context, constructor); 115 116 // Note that this is not the [[Prototype]] internal property (which we could 117 // get via JSObjectGetPrototype), but the Type.prototype, that will be set 118 // as [[Prototype]] of Type instances. 119 prototype = JSObjectGetProperty(m_context, constructor, prototypeName, &exception); 120 Q_ASSERT(JSValueIsObject(m_context, prototype)); 121 JSValueProtect(m_context, prototype); 122 Q_ASSERT(!exception); 123 } 124 125 QScriptOriginalGlobalObject::~QScriptOriginalGlobalObject() 126 { 127 JSValueUnprotect(m_context, m_arrayConstructor); 128 JSValueUnprotect(m_context, m_arrayPrototype); 129 JSValueUnprotect(m_context, m_errorConstructor); 130 JSValueUnprotect(m_context, m_errorPrototype); 131 JSValueUnprotect(m_context, m_functionConstructor); 132 JSValueUnprotect(m_context, m_functionPrototype); 133 JSValueUnprotect(m_context, m_dateConstructor); 134 JSValueUnprotect(m_context, m_datePrototype); 135 JSValueUnprotect(m_context, m_hasOwnPropertyFunction); 136 JSValueUnprotect(m_context, m_getOwnPropertyNamesFunction); 137 JSGlobalContextRelease(m_context); 138 } 139 140 inline bool QScriptOriginalGlobalObject::objectHasOwnProperty(JSObjectRef object, JSStringRef property) const 141 { 142 // FIXME This function should be replaced by JSC C API. 143 JSValueRef exception = 0; 144 JSValueRef propertyName[] = { JSValueMakeString(m_context, property) }; 145 JSValueRef result = JSObjectCallAsFunction(m_context, m_hasOwnPropertyFunction, object, 1, propertyName, &exception); 146 return exception ? false : JSValueToBoolean(m_context, result); 147 } 148 149 /*! 150 \internal 151 This method gives ownership of all JSStringRefs. 152 */ 153 inline QVector<JSStringRef> QScriptOriginalGlobalObject::objectGetOwnPropertyNames(JSObjectRef object) const 154 { 155 JSValueRef exception = 0; 156 JSObjectRef propertyNames 157 = const_cast<JSObjectRef>(JSObjectCallAsFunction(m_context, 158 m_getOwnPropertyNamesFunction, 159 /* thisObject */ 0, 160 /* argumentCount */ 1, 161 &object, 162 &exception)); 163 Q_ASSERT(JSValueIsObject(m_context, propertyNames)); 164 Q_ASSERT(!exception); 165 JSStringRef lengthName = QScriptConverter::toString("length"); 166 int count = JSValueToNumber(m_context, JSObjectGetProperty(m_context, propertyNames, lengthName, &exception), &exception); 167 168 Q_ASSERT(!exception); 169 QVector<JSStringRef> names; 170 names.reserve(count); 171 for (int i = 0; i < count; ++i) { 172 JSValueRef tmp = JSObjectGetPropertyAtIndex(m_context, propertyNames, i, &exception); 173 names.append(JSValueToStringCopy(m_context, tmp, &exception)); 174 Q_ASSERT(!exception); 175 } 176 return names; 177 } 178 179 inline bool QScriptOriginalGlobalObject::isDate(JSValueRef value) const 180 { 181 return isType(value, m_dateConstructor, m_datePrototype); 182 } 183 184 inline bool QScriptOriginalGlobalObject::isArray(JSValueRef value) const 185 { 186 return isType(value, m_arrayConstructor, m_arrayPrototype); 187 } 188 189 inline bool QScriptOriginalGlobalObject::isError(JSValueRef value) const 190 { 191 return isType(value, m_errorConstructor, m_errorPrototype); 192 } 193 194 inline JSValueRef QScriptOriginalGlobalObject::functionPrototype() const 195 { 196 return m_functionPrototype; 197 } 198 199 inline bool QScriptOriginalGlobalObject::isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const 200 { 201 // JSC API doesn't export the [[Class]] information for the builtins. But we know that a value 202 // is an object of the Type if it was created with the Type constructor or if it is the Type.prototype. 203 JSValueRef exception = 0; 204 bool result = JSValueIsInstanceOfConstructor(m_context, value, constructor, &exception) || JSValueIsStrictEqual(m_context, value, prototype); 205 Q_ASSERT(!exception); 206 return result; 207 } 208 209 #endif // qscriptoriginalglobalobject_p_h 210