2008-03-07 Stephanie <slewis@apple.com>
[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     JSLock lock;
70     gcUnprotect(static_cast<JSGlobalObject*>(data));
71 }
72
73 static void initializeGlobalObjectKey()
74 {
75     pthread_key_create(&globalObjectKey, unprotectGlobalObject);
76 }
77
78 static ExecState* getThreadGlobalExecState()
79 {
80     pthread_once(&globalObjectKeyOnce, initializeGlobalObjectKey);
81     JSGlobalObject* globalObject = static_cast<JSGlobalObject*>(pthread_getspecific(globalObjectKey));
82     if (!globalObject) {
83         globalObject = new JSGlueGlobalObject;
84         gcProtect(globalObject);
85         pthread_setspecific(globalObjectKey, globalObject);
86     }
87     
88     ExecState* exec = globalObject->globalExec();
89
90     // Discard exceptions -- otherwise an exception would forestall JS 
91     // evaluation throughout the thread
92     exec->clearException();
93     return exec;
94 }
95
96 void JSValueWrapper::GetJSObectCallBacks(JSObjectCallBacks& callBacks)
97 {
98     callBacks.dispose = (JSObjectDisposeProcPtr)JSValueWrapper::JSObjectDispose;
99     callBacks.equal = (JSObjectEqualProcPtr)0;
100     callBacks.copyPropertyNames = (JSObjectCopyPropertyNamesProcPtr)JSValueWrapper::JSObjectCopyPropertyNames;
101     callBacks.copyCFValue = (JSObjectCopyCFValueProcPtr)JSValueWrapper::JSObjectCopyCFValue;
102     callBacks.copyProperty = (JSObjectCopyPropertyProcPtr)JSValueWrapper::JSObjectCopyProperty;
103     callBacks.setProperty = (JSObjectSetPropertyProcPtr)JSValueWrapper::JSObjectSetProperty;
104     callBacks.callFunction = (JSObjectCallFunctionProcPtr)JSValueWrapper::JSObjectCallFunction;
105 }
106
107 void JSValueWrapper::JSObjectDispose(void *data)
108 {
109     JSValueWrapper* ptr = (JSValueWrapper*)data;
110     delete ptr;
111 }
112
113
114 CFArrayRef JSValueWrapper::JSObjectCopyPropertyNames(void *data)
115 {
116     JSLock lock;
117
118     CFMutableArrayRef result = 0;
119     JSValueWrapper* ptr = (JSValueWrapper*)data;
120     if (ptr)
121     {
122         ExecState* exec = getThreadGlobalExecState();
123         JSObject *object = ptr->GetValue()->toObject(exec);
124         PropertyNameArray propNames;
125         object->getPropertyNames(exec, propNames);
126         PropertyNameArray::const_iterator iterator = propNames.begin();
127
128         while (iterator != propNames.end()) {
129             Identifier name = *iterator;
130             CFStringRef nameStr = IdentifierToCFString(name);
131
132             if (!result)
133             {
134                 result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
135             }
136             if (result && nameStr)
137             {
138                 CFArrayAppendValue(result, nameStr);
139             }
140             ReleaseCFType(nameStr);
141             iterator++;
142         }
143
144     }
145     return result;
146 }
147
148
149 JSObjectRef JSValueWrapper::JSObjectCopyProperty(void *data, CFStringRef propertyName)
150 {
151     JSLock lock;
152
153     JSObjectRef result = 0;
154     JSValueWrapper* ptr = (JSValueWrapper*)data;
155     if (ptr)
156     {
157         ExecState* exec = getThreadGlobalExecState();
158         JSValue *propValue = ptr->GetValue()->toObject(exec)->get(exec, CFStringToIdentifier(propertyName));
159         JSValueWrapper* wrapperValue = new JSValueWrapper(propValue);
160
161         JSObjectCallBacks callBacks;
162         GetJSObectCallBacks(callBacks);
163         result = JSObjectCreateInternal(wrapperValue, &callBacks, JSValueWrapper::JSObjectMark, kJSUserObjectDataTypeJSValueWrapper);
164
165         if (!result)
166         {
167             delete wrapperValue;
168         }
169     }
170     return result;
171 }
172
173 void JSValueWrapper::JSObjectSetProperty(void *data, CFStringRef propertyName, JSObjectRef jsValue)
174 {
175     JSLock lock;
176
177     JSValueWrapper* ptr = (JSValueWrapper*)data;
178     if (ptr)
179     {
180         ExecState* exec = getThreadGlobalExecState();
181         JSValue *value = JSObjectKJSValue((JSUserObject*)jsValue);
182         JSObject *objValue = ptr->GetValue()->toObject(exec);
183         objValue->put(exec, CFStringToIdentifier(propertyName), value);
184     }
185 }
186
187 JSObjectRef JSValueWrapper::JSObjectCallFunction(void *data, JSObjectRef thisObj, CFArrayRef args)
188 {
189     JSLock lock;
190
191     JSObjectRef result = 0;
192     JSValueWrapper* ptr = (JSValueWrapper*)data;
193     if (ptr)
194     {
195         ExecState* exec = getThreadGlobalExecState();
196
197         JSValue *value = JSObjectKJSValue((JSUserObject*)thisObj);
198         JSObject *ksjThisObj = value->toObject(exec);
199         JSObject *objValue = ptr->GetValue()->toObject(exec);
200
201         List listArgs;
202         CFIndex argCount = args ? CFArrayGetCount(args) : 0;
203         for (CFIndex i = 0; i < argCount; i++)
204         {
205             JSObjectRef jsArg = (JSObjectRef)CFArrayGetValueAtIndex(args, i);
206             JSValue *kgsArg = JSObjectKJSValue((JSUserObject*)jsArg);
207             listArgs.append(kgsArg);
208         }
209
210         JSValue *resultValue = objValue->call(exec, ksjThisObj, listArgs);
211         JSValueWrapper* wrapperValue = new JSValueWrapper(resultValue);
212         JSObjectCallBacks callBacks;
213         GetJSObectCallBacks(callBacks);
214         result = JSObjectCreate(wrapperValue, &callBacks);
215         if (!result)
216         {
217             delete wrapperValue;
218         }
219     }
220     return result;
221 }
222
223 CFTypeRef JSValueWrapper::JSObjectCopyCFValue(void *data)
224 {
225     JSLock lock;
226
227     CFTypeRef result = 0;
228     JSValueWrapper* ptr = (JSValueWrapper*)data;
229     if (ptr)
230     {
231         result = KJSValueToCFType(ptr->GetValue(), getThreadGlobalExecState());
232     }
233     return result;
234 }
235
236 void JSValueWrapper::JSObjectMark(void *data)
237 {
238     JSValueWrapper* ptr = (JSValueWrapper*)data;
239     if (ptr)
240     {
241         ptr->fValue->mark();
242     }
243 }