Reviewed by Darin.
[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 <JavaScriptCore/PropertyNameArray.h>
32 #include <pthread.h>
33
34 JSValueWrapper::JSValueWrapper(JSValue *inValue)
35     : fValue(inValue)
36 {
37 }
38
39 JSValueWrapper::~JSValueWrapper()
40 {
41 }
42
43 JSValue *JSValueWrapper::GetValue()
44 {
45     return fValue;
46 }
47
48 /*
49  * This is a slight hack. The JSGlue API has no concept of execution state.
50  * However, execution state is an inherent part of JS, and JSCore requires it.
51  * So, we keep a single execution state for the whole thread and supply it
52  * where necessary.
53
54  * The execution state holds two things: (1) exceptions; (2) the global object. 
55  * JSGlue has no API for accessing exceptions, so we just discard them. As for
56  * the global object, JSGlue includes no calls that depend on it. Its property
57  * getters and setters are per-object; they don't walk up the enclosing scope. 
58  * Functions called by JSObjectCallFunction may reference values in the enclosing 
59  * scope, but they do so through an internally stored scope chain, so we don't 
60  * need to supply the global scope.
61  */      
62
63 pthread_key_t interpreterKey;
64 pthread_once_t interpreterKeyOnce = PTHREAD_ONCE_INIT;
65
66 static void derefInterpreter(void* data) 
67 {
68     static_cast<Interpreter*>(data)->deref();
69 }
70
71 static void initializeInterpreterKey()
72 {
73     pthread_key_create(&interpreterKey, derefInterpreter);
74 }
75
76 static ExecState* getThreadGlobalExecState()
77 {
78     pthread_once(&interpreterKeyOnce, initializeInterpreterKey);
79     Interpreter* interpreter = static_cast<Interpreter*>(pthread_getspecific(interpreterKey));
80     if (!interpreter) {
81         interpreter = new Interpreter();
82         interpreter->ref();
83         pthread_setspecific(interpreterKey, interpreter);
84     }
85
86     // Discard exceptions -- otherwise an exception would forestall JS 
87     // evaluation throughout the thread
88     interpreter->globalExec()->clearException();
89
90     return interpreter->globalExec();
91 }
92
93 void JSValueWrapper::GetJSObectCallBacks(JSObjectCallBacks& callBacks)
94 {
95     callBacks.dispose = (JSObjectDisposeProcPtr)JSValueWrapper::JSObjectDispose;
96     callBacks.equal = (JSObjectEqualProcPtr)0;
97     callBacks.copyPropertyNames = (JSObjectCopyPropertyNamesProcPtr)JSValueWrapper::JSObjectCopyPropertyNames;
98     callBacks.copyCFValue = (JSObjectCopyCFValueProcPtr)JSValueWrapper::JSObjectCopyCFValue;
99     callBacks.copyProperty = (JSObjectCopyPropertyProcPtr)JSValueWrapper::JSObjectCopyProperty;
100     callBacks.setProperty = (JSObjectSetPropertyProcPtr)JSValueWrapper::JSObjectSetProperty;
101     callBacks.callFunction = (JSObjectCallFunctionProcPtr)JSValueWrapper::JSObjectCallFunction;
102 }
103
104 void JSValueWrapper::JSObjectDispose(void *data)
105 {
106     JSValueWrapper* ptr = (JSValueWrapper*)data;
107     delete ptr;
108 }
109
110
111 CFArrayRef JSValueWrapper::JSObjectCopyPropertyNames(void *data)
112 {
113     JSLock lock;
114
115     CFMutableArrayRef result = 0;
116     JSValueWrapper* ptr = (JSValueWrapper*)data;
117     if (ptr)
118     {
119         ExecState* exec = getThreadGlobalExecState();
120         JSObject *object = ptr->GetValue()->toObject(exec);
121         PropertyNameArray propNames;
122         object->getPropertyNames(exec, propNames);
123         PropertyNameArrayIterator iterator = propNames.begin();
124
125         while (iterator != propNames.end()) {
126             Identifier name = *iterator;
127             CFStringRef nameStr = IdentifierToCFString(name);
128
129             if (!result)
130             {
131                 result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
132             }
133             if (result && nameStr)
134             {
135                 CFArrayAppendValue(result, nameStr);
136             }
137             ReleaseCFType(nameStr);
138             iterator++;
139         }
140
141     }
142     return result;
143 }
144
145
146 JSObjectRef JSValueWrapper::JSObjectCopyProperty(void *data, CFStringRef propertyName)
147 {
148     JSLock lock;
149
150     JSObjectRef result = 0;
151     JSValueWrapper* ptr = (JSValueWrapper*)data;
152     if (ptr)
153     {
154         ExecState* exec = getThreadGlobalExecState();
155         JSValue *propValue = ptr->GetValue()->toObject(exec)->get(exec, CFStringToIdentifier(propertyName));
156         JSValueWrapper* wrapperValue = new JSValueWrapper(propValue);
157
158         JSObjectCallBacks callBacks;
159         GetJSObectCallBacks(callBacks);
160         result = JSObjectCreateInternal(wrapperValue, &callBacks, JSValueWrapper::JSObjectMark, kJSUserObjectDataTypeJSValueWrapper);
161
162         if (!result)
163         {
164             delete wrapperValue;
165         }
166     }
167     return result;
168 }
169
170 void JSValueWrapper::JSObjectSetProperty(void *data, CFStringRef propertyName, JSObjectRef jsValue)
171 {
172     JSLock lock;
173
174     JSValueWrapper* ptr = (JSValueWrapper*)data;
175     if (ptr)
176     {
177         ExecState* exec = getThreadGlobalExecState();
178         JSValue *value = JSObjectKJSValue((JSUserObject*)jsValue);
179         JSObject *objValue = ptr->GetValue()->toObject(exec);
180         objValue->put(exec, CFStringToIdentifier(propertyName), value);
181     }
182 }
183
184 JSObjectRef JSValueWrapper::JSObjectCallFunction(void *data, JSObjectRef thisObj, CFArrayRef args)
185 {
186     JSLock lock;
187
188     JSObjectRef result = 0;
189     JSValueWrapper* ptr = (JSValueWrapper*)data;
190     if (ptr)
191     {
192         ExecState* exec = getThreadGlobalExecState();
193
194         JSValue *value = JSObjectKJSValue((JSUserObject*)thisObj);
195         JSObject *ksjThisObj = value->toObject(exec);
196         JSObject *objValue = ptr->GetValue()->toObject(exec);
197
198         List listArgs;
199         CFIndex argCount = args ? CFArrayGetCount(args) : 0;
200         for (CFIndex i = 0; i < argCount; i++)
201         {
202             JSObjectRef jsArg = (JSObjectRef)CFArrayGetValueAtIndex(args, i);
203             JSValue *kgsArg = JSObjectKJSValue((JSUserObject*)jsArg);
204             listArgs.append(kgsArg);
205         }
206
207         JSValue *resultValue = objValue->call(exec, ksjThisObj, listArgs);
208         JSValueWrapper* wrapperValue = new JSValueWrapper(resultValue);
209         JSObjectCallBacks callBacks;
210         GetJSObectCallBacks(callBacks);
211         result = JSObjectCreate(wrapperValue, &callBacks);
212         if (!result)
213         {
214             delete wrapperValue;
215         }
216     }
217     return result;
218 }
219
220 CFTypeRef JSValueWrapper::JSObjectCopyCFValue(void *data)
221 {
222     JSLock lock;
223
224     CFTypeRef result = 0;
225     JSValueWrapper* ptr = (JSValueWrapper*)data;
226     if (ptr)
227     {
228         result = KJSValueToCFType(ptr->GetValue(), getThreadGlobalExecState());
229     }
230     return result;
231 }
232
233 void JSValueWrapper::JSObjectMark(void *data)
234 {
235     JSValueWrapper* ptr = (JSValueWrapper*)data;
236     if (ptr)
237     {
238         ptr->fValue->mark();
239     }
240 }