WebCore:
[WebKit-https.git] / JavaScriptGlue / JSValueWrapper.cpp
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "JSValueWrapper.h"
31 #include "JSRun.h"
32 #include <JavaScriptCore/PropertyNameArray.h>
33 #include <pthread.h>
34
35 JSValueWrapper::JSValueWrapper(JSValue *inValue)
36     : fValue(inValue)
37 {
38 }
39
40 JSValueWrapper::~JSValueWrapper()
41 {
42 }
43
44 JSValue *JSValueWrapper::GetValue()
45 {
46     return fValue;
47 }
48
49 /*
50  * This is a slight hack. The JSGlue API has no concept of execution state.
51  * However, execution state is an inherent part of JS, and JSCore requires it.
52  * So, we keep a single execution state for the whole thread and supply it
53  * where necessary.
54
55  * The execution state holds two things: (1) exceptions; (2) the global object. 
56  * JSGlue has no API for accessing exceptions, so we just discard them. As for
57  * the global object, JSGlue includes no calls that depend on it. Its property
58  * getters and setters are per-object; they don't walk up the enclosing scope. 
59  * Functions called by JSObjectCallFunction may reference values in the enclosing 
60  * scope, but they do so through an internally stored scope chain, so we don't 
61  * need to supply the global scope.
62  */      
63
64 pthread_key_t globalObjectKey;
65 pthread_once_t globalObjectKeyOnce = PTHREAD_ONCE_INIT;
66
67 static void unprotectGlobalObject(void* data) 
68 {
69     gcUnprotect(static_cast<JSGlobalObject*>(data));
70 }
71
72 static void initializeGlobalObjectKey()
73 {
74     pthread_key_create(&globalObjectKey, unprotectGlobalObject);
75 }
76
77 static ExecState* getThreadGlobalExecState()
78 {
79     pthread_once(&globalObjectKeyOnce, initializeGlobalObjectKey);
80     JSGlobalObject* globalObject = static_cast<JSGlobalObject*>(pthread_getspecific(globalObjectKey));
81     if (!globalObject) {
82         globalObject = new JSGlueGlobalObject;
83         gcProtect(globalObject);
84         pthread_setspecific(globalObjectKey, globalObject);
85     }
86     
87     ExecState* exec = globalObject->globalExec();
88
89     // Discard exceptions -- otherwise an exception would forestall JS 
90     // evaluation throughout the thread
91     exec->clearException();
92     return exec;
93 }
94
95 void JSValueWrapper::GetJSObectCallBacks(JSObjectCallBacks& callBacks)
96 {
97     callBacks.dispose = (JSObjectDisposeProcPtr)JSValueWrapper::JSObjectDispose;
98     callBacks.equal = (JSObjectEqualProcPtr)0;
99     callBacks.copyPropertyNames = (JSObjectCopyPropertyNamesProcPtr)JSValueWrapper::JSObjectCopyPropertyNames;
100     callBacks.copyCFValue = (JSObjectCopyCFValueProcPtr)JSValueWrapper::JSObjectCopyCFValue;
101     callBacks.copyProperty = (JSObjectCopyPropertyProcPtr)JSValueWrapper::JSObjectCopyProperty;
102     callBacks.setProperty = (JSObjectSetPropertyProcPtr)JSValueWrapper::JSObjectSetProperty;
103     callBacks.callFunction = (JSObjectCallFunctionProcPtr)JSValueWrapper::JSObjectCallFunction;
104 }
105
106 void JSValueWrapper::JSObjectDispose(void *data)
107 {
108     JSValueWrapper* ptr = (JSValueWrapper*)data;
109     delete ptr;
110 }
111
112
113 CFArrayRef JSValueWrapper::JSObjectCopyPropertyNames(void *data)
114 {
115     JSLock lock;
116
117     CFMutableArrayRef result = 0;
118     JSValueWrapper* ptr = (JSValueWrapper*)data;
119     if (ptr)
120     {
121         ExecState* exec = getThreadGlobalExecState();
122         JSObject *object = ptr->GetValue()->toObject(exec);
123         PropertyNameArray propNames;
124         object->getPropertyNames(exec, propNames);
125         PropertyNameArray::const_iterator iterator = propNames.begin();
126
127         while (iterator != propNames.end()) {
128             Identifier name = *iterator;
129             CFStringRef nameStr = IdentifierToCFString(name);
130
131             if (!result)
132             {
133                 result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
134             }
135             if (result && nameStr)
136             {
137                 CFArrayAppendValue(result, nameStr);
138             }
139             ReleaseCFType(nameStr);
140             iterator++;
141         }
142
143     }
144     return result;
145 }
146
147
148 JSObjectRef JSValueWrapper::JSObjectCopyProperty(void *data, CFStringRef propertyName)
149 {
150     JSLock lock;
151
152     JSObjectRef result = 0;
153     JSValueWrapper* ptr = (JSValueWrapper*)data;
154     if (ptr)
155     {
156         ExecState* exec = getThreadGlobalExecState();
157         JSValue *propValue = ptr->GetValue()->toObject(exec)->get(exec, CFStringToIdentifier(propertyName));
158         JSValueWrapper* wrapperValue = new JSValueWrapper(propValue);
159
160         JSObjectCallBacks callBacks;
161         GetJSObectCallBacks(callBacks);
162         result = JSObjectCreateInternal(wrapperValue, &callBacks, JSValueWrapper::JSObjectMark, kJSUserObjectDataTypeJSValueWrapper);
163
164         if (!result)
165         {
166             delete wrapperValue;
167         }
168     }
169     return result;
170 }
171
172 void JSValueWrapper::JSObjectSetProperty(void *data, CFStringRef propertyName, JSObjectRef jsValue)
173 {
174     JSLock lock;
175
176     JSValueWrapper* ptr = (JSValueWrapper*)data;
177     if (ptr)
178     {
179         ExecState* exec = getThreadGlobalExecState();
180         JSValue *value = JSObjectKJSValue((JSUserObject*)jsValue);
181         JSObject *objValue = ptr->GetValue()->toObject(exec);
182         objValue->put(exec, CFStringToIdentifier(propertyName), value);
183     }
184 }
185
186 JSObjectRef JSValueWrapper::JSObjectCallFunction(void *data, JSObjectRef thisObj, CFArrayRef args)
187 {
188     JSLock lock;
189
190     JSObjectRef result = 0;
191     JSValueWrapper* ptr = (JSValueWrapper*)data;
192     if (ptr)
193     {
194         ExecState* exec = getThreadGlobalExecState();
195
196         JSValue *value = JSObjectKJSValue((JSUserObject*)thisObj);
197         JSObject *ksjThisObj = value->toObject(exec);
198         JSObject *objValue = ptr->GetValue()->toObject(exec);
199
200         List listArgs;
201         CFIndex argCount = args ? CFArrayGetCount(args) : 0;
202         for (CFIndex i = 0; i < argCount; i++)
203         {
204             JSObjectRef jsArg = (JSObjectRef)CFArrayGetValueAtIndex(args, i);
205             JSValue *kgsArg = JSObjectKJSValue((JSUserObject*)jsArg);
206             listArgs.append(kgsArg);
207         }
208
209         JSValue *resultValue = objValue->call(exec, ksjThisObj, listArgs);
210         JSValueWrapper* wrapperValue = new JSValueWrapper(resultValue);
211         JSObjectCallBacks callBacks;
212         GetJSObectCallBacks(callBacks);
213         result = JSObjectCreate(wrapperValue, &callBacks);
214         if (!result)
215         {
216             delete wrapperValue;
217         }
218     }
219     return result;
220 }
221
222 CFTypeRef JSValueWrapper::JSObjectCopyCFValue(void *data)
223 {
224     JSLock lock;
225
226     CFTypeRef result = 0;
227     JSValueWrapper* ptr = (JSValueWrapper*)data;
228     if (ptr)
229     {
230         result = KJSValueToCFType(ptr->GetValue(), getThreadGlobalExecState());
231     }
232     return result;
233 }
234
235 void JSValueWrapper::JSObjectMark(void *data)
236 {
237     JSValueWrapper* ptr = (JSValueWrapper*)data;
238     if (ptr)
239     {
240         ptr->fValue->mark();
241     }
242 }