Objective-C API for JavaScriptCore
[WebKit-https.git] / Source / JavaScriptCore / API / JSContext.mm
1 /*
2  * Copyright (C) 2013 Apple 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #import "APICast.h"
29 #import "JSContextInternal.h"
30 #import "JSGlobalObject.h"
31 #import "JSValueInternal.h"
32 #import "JSVirtualMachineInternal.h"
33 #import "JSWrapperMap.h"
34 #import "JavaScriptCore.h"
35 #import "ObjcRuntimeExtras.h"
36 #import <wtf/HashSet.h>
37
38 #if JS_OBJC_API_ENABLED
39
40 typedef HashMap<JSValueRef, size_t> ProtectMap;
41
42 @implementation JSContext {
43     JSVirtualMachine *m_virtualMachine;
44     JSGlobalContextRef m_context;
45     JSWrapperMap *m_wrapperMap;
46     ProtectMap m_protectCounts;
47 }
48
49 @synthesize exception;
50 @synthesize exceptionHandler;
51
52 - (id)init
53 {
54     return [self initWithVirtualMachine:[[[JSVirtualMachine alloc] init] autorelease]];
55 }
56
57 - (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
58 {
59     self = [super init];
60     if (!self)
61         return nil;
62
63     m_virtualMachine = [virtualMachine retain];
64     m_context = JSGlobalContextCreateInGroup(getGroupFromVirtualMachine(virtualMachine), 0);
65     m_wrapperMap = [[JSWrapperMap alloc] initWithContext:self];
66
67     self.exception = nil;
68     self.exceptionHandler = ^(JSContext* context, JSValue *exceptionValue) {
69         context.exception = exceptionValue;
70     };
71
72     toJS(m_context)->lexicalGlobalObject()->m_apiData = self;
73     return self;
74 }
75
76 - (JSValue *)evaluateScript:(NSString *)script
77 {
78     JSValueRef exceptionValue = 0;
79     JSStringRef scriptJS = JSStringCreateWithCFString((CFStringRef)script);
80     JSValueRef result = JSEvaluateScript(m_context, scriptJS, 0, 0, 0, &exceptionValue);
81     JSStringRelease(scriptJS);
82
83     if (exceptionValue)
84         return [self valueFromNotifyException:exceptionValue];
85
86     return [JSValue valueWithValue:result inContext:self];
87 }
88
89 - (JSValue *)globalObject
90 {
91     return [JSValue valueWithValue:JSContextGetGlobalObject(m_context) inContext:self];
92 }
93
94 + (JSContext *)currentContext
95 {
96     WTFThreadData& threadData = wtfThreadData();
97     CallbackData *entry = (CallbackData *)threadData.m_apiData;
98     return entry ? entry->context : nil;
99 }
100
101 + (JSValue *)currentThis
102 {
103     WTFThreadData& threadData = wtfThreadData();
104     CallbackData *entry = (CallbackData *)threadData.m_apiData;
105
106     if (!entry->currentThis)
107         entry->currentThis = [[JSValue alloc] initWithValue:entry->thisValue inContext:[JSContext currentContext]];
108
109     return entry->currentThis;
110 }
111
112 + (NSArray *)currentArguments
113 {
114     WTFThreadData& threadData = wtfThreadData();
115     CallbackData *entry = (CallbackData *)threadData.m_apiData;
116
117     if (!entry->currentArguments) {
118         JSContext *context = [JSContext currentContext];
119         size_t count = entry->argumentCount;
120         JSValue * argumentArray[count];
121         for (size_t i =0; i < count; ++i)
122             argumentArray[i] = [JSValue valueWithValue:entry->arguments[i] inContext:context];
123         entry->currentArguments = [[NSArray alloc] initWithObjects:argumentArray count:count];
124     }
125
126     return entry->currentArguments;
127 }
128
129 - (JSVirtualMachine*)virtualMachine
130 {
131     return m_virtualMachine;
132 }
133
134 @end
135
136 @implementation JSContext(SubscriptSupport)
137
138 - (JSValue *)objectForKeyedSubscript:(id)key
139 {
140     return [self globalObject][key];
141 }
142
143 - (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
144 {
145     [self globalObject][key] = object;
146 }
147
148 @end
149
150 @implementation JSContext(Internal)
151
152 JSGlobalContextRef contextInternalContext(JSContext* context)
153 {
154     return context->m_context;
155 }
156
157 - (void)dealloc
158 {
159     toJS(m_context)->lexicalGlobalObject()->m_apiData = 0;
160
161     ProtectMap::iterator iterator = m_protectCounts.begin();
162     ProtectMap::iterator end = m_protectCounts.end();
163     for (; iterator != end; ++iterator)
164         JSValueUnprotect(m_context, iterator->key);
165
166     [m_wrapperMap release];
167     JSGlobalContextRelease(m_context);
168     [m_virtualMachine release];
169     [super dealloc];
170 }
171
172 - (void)notifyException:(JSValueRef)exceptionValue
173 {
174     self.exceptionHandler(self, [JSValue valueWithValue:exceptionValue inContext:self]);
175 }
176
177 - (JSValue *)valueFromNotifyException:(JSValueRef)exceptionValue
178 {
179     [self notifyException:exceptionValue];
180     return [JSValue valueWithUndefinedInContext:self];
181 }
182
183 - (BOOL)boolFromNotifyException:(JSValueRef)exceptionValue
184 {
185     [self notifyException:exceptionValue];
186     return NO;
187 }
188
189 - (void)beginCallbackWithData:(CallbackData *)callbackData thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments
190 {
191     WTFThreadData& threadData = wtfThreadData();
192     [self retain];
193     CallbackData *prevStack = (CallbackData *)threadData.m_apiData;
194     *callbackData = (CallbackData){ prevStack, self, [self.exception retain], thisValue, nil, argumentCount, arguments, nil };
195     threadData.m_apiData = callbackData;
196     self.exception = nil;
197 }
198
199 - (void)endCallbackWithData:(CallbackData *)callbackData
200 {
201     WTFThreadData& threadData = wtfThreadData();
202     self.exception = callbackData->preservedException;
203     [callbackData->preservedException release];
204     [callbackData->currentThis release];
205     [callbackData->currentArguments release];
206     threadData.m_apiData = callbackData->next;
207     [self release];
208 }
209
210 - (void)protect:(JSValueRef)value
211 {
212     // Lock access to m_protectCounts
213     JSC::JSLockHolder lock(toJS(m_context));
214
215     ProtectMap::AddResult result = m_protectCounts.add(value, 1);
216     if (result.isNewEntry)
217         JSValueProtect(m_context, value);
218     else
219         ++result.iterator->value;
220 }
221
222 - (void)unprotect:(JSValueRef)value
223 {
224     // Lock access to m_protectCounts
225     JSC::JSLockHolder lock(toJS(m_context));
226
227     ProtectMap::iterator iterator = m_protectCounts.find(value);
228     if (iterator->value == 1) {
229         m_protectCounts.remove(value);
230         JSValueUnprotect(m_context, value);
231     } else
232         --iterator->value;
233 }
234
235 - (JSValue *)wrapperForObject:(id)object
236 {
237     // Lock access to m_wrapperMap
238     JSC::JSLockHolder lock(toJS(m_context));
239     return [m_wrapperMap wrapperForObject:object];
240 }
241
242 @end
243
244 WeakContextRef::WeakContextRef(JSContext *context)
245 {
246     objc_initWeak(&m_weakContext, context);
247 }
248
249 WeakContextRef::~WeakContextRef()
250 {
251     objc_destroyWeak(&m_weakContext);
252 }
253
254 JSContext * WeakContextRef::get()
255 {
256     return objc_loadWeak(&m_weakContext);
257 }
258
259 void WeakContextRef::set(JSContext *context)
260 {
261     objc_storeWeak(&m_weakContext, context);
262 }
263
264 #endif