[Cocoa] Improve ARC compatibility of more code in 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 "JSCInlines.h"
30 #import "JSContextInternal.h"
31 #import "JSContextPrivate.h"
32 #import "JSContextRefInternal.h"
33 #import "JSGlobalObject.h"
34 #import "JSValueInternal.h"
35 #import "JSVirtualMachineInternal.h"
36 #import "JSWrapperMap.h"
37 #import "JavaScriptCore.h"
38 #import "ObjcRuntimeExtras.h"
39 #import "StrongInlines.h"
40
41 #if JSC_OBJC_API_ENABLED
42
43 @implementation JSContext {
44     JSVirtualMachine *m_virtualMachine;
45     JSGlobalContextRef m_context;
46     JSC::Strong<JSC::JSObject> m_exception;
47 }
48
49 - (JSGlobalContextRef)JSGlobalContextRef
50 {
51     return m_context;
52 }
53
54 - (void)ensureWrapperMap
55 {
56     if (!toJS([self JSGlobalContextRef])->lexicalGlobalObject()->wrapperMap()) {
57         // The map will be retained by the GlobalObject in initialization.
58         [[[JSWrapperMap alloc] initWithGlobalContextRef:[self JSGlobalContextRef]] release];
59     }
60 }
61
62 - (instancetype)init
63 {
64     return [self initWithVirtualMachine:[[[JSVirtualMachine alloc] init] autorelease]];
65 }
66
67 - (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
68 {
69     self = [super init];
70     if (!self)
71         return nil;
72
73     m_virtualMachine = [virtualMachine retain];
74     m_context = JSGlobalContextCreateInGroup(getGroupFromVirtualMachine(virtualMachine), 0);
75
76     self.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
77         context.exception = exceptionValue;
78     };
79
80     [self ensureWrapperMap];
81     [m_virtualMachine addContext:self forGlobalContextRef:m_context];
82
83     return self;
84 }
85
86 - (void)dealloc
87 {
88     m_exception.clear();
89     JSGlobalContextRelease(m_context);
90     [m_virtualMachine release];
91     [_exceptionHandler release];
92     [super dealloc];
93 }
94
95 - (JSValue *)evaluateScript:(NSString *)script
96 {
97     return [self evaluateScript:script withSourceURL:nil];
98 }
99
100 - (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL *)sourceURL
101 {
102     JSValueRef exceptionValue = nullptr;
103     JSStringRef scriptJS = JSStringCreateWithCFString((__bridge CFStringRef)script);
104     JSStringRef sourceURLJS = sourceURL ? JSStringCreateWithCFString((__bridge CFStringRef)[sourceURL absoluteString]) : nullptr;
105     JSValueRef result = JSEvaluateScript(m_context, scriptJS, nullptr, sourceURLJS, 0, &exceptionValue);
106     if (sourceURLJS)
107         JSStringRelease(sourceURLJS);
108     JSStringRelease(scriptJS);
109
110     if (exceptionValue)
111         return [self valueFromNotifyException:exceptionValue];
112
113     return [JSValue valueWithJSValueRef:result inContext:self];
114 }
115
116 - (void)setException:(JSValue *)value
117 {
118     JSC::ExecState* exec = toJS(m_context);
119     JSC::VM& vm = exec->vm();
120     JSC::JSLockHolder locker(vm);
121     if (value)
122         m_exception.set(vm, toJS(JSValueToObject(m_context, valueInternalValue(value), 0)));
123     else
124         m_exception.clear();
125 }
126
127 - (JSValue *)exception
128 {
129     if (!m_exception)
130         return nil;
131     return [JSValue valueWithJSValueRef:toRef(m_exception.get()) inContext:self];
132 }
133
134 - (JSWrapperMap *)wrapperMap
135 {
136     return toJS(m_context)->lexicalGlobalObject()->wrapperMap();
137 }
138
139 - (JSValue *)globalObject
140 {
141     return [JSValue valueWithJSValueRef:JSContextGetGlobalObject(m_context) inContext:self];
142 }
143
144 + (JSContext *)currentContext
145 {
146     Thread& thread = Thread::current();
147     CallbackData *entry = (CallbackData *)thread.m_apiData;
148     return entry ? entry->context : nil;
149 }
150
151 + (JSValue *)currentThis
152 {
153     Thread& thread = Thread::current();
154     CallbackData *entry = (CallbackData *)thread.m_apiData;
155     if (!entry)
156         return nil;
157     return [JSValue valueWithJSValueRef:entry->thisValue inContext:[JSContext currentContext]];
158 }
159
160 + (JSValue *)currentCallee
161 {
162     Thread& thread = Thread::current();
163     CallbackData *entry = (CallbackData *)thread.m_apiData;
164     if (!entry)
165         return nil;
166     return [JSValue valueWithJSValueRef:entry->calleeValue inContext:[JSContext currentContext]];
167 }
168
169 + (NSArray *)currentArguments
170 {
171     Thread& thread = Thread::current();
172     CallbackData *entry = (CallbackData *)thread.m_apiData;
173
174     if (!entry)
175         return nil;
176
177     if (!entry->currentArguments) {
178         JSContext *context = [JSContext currentContext];
179         size_t count = entry->argumentCount;
180         JSValue * argumentArray[count];
181         for (size_t i =0; i < count; ++i)
182             argumentArray[i] = [JSValue valueWithJSValueRef:entry->arguments[i] inContext:context];
183         entry->currentArguments = [[NSArray alloc] initWithObjects:argumentArray count:count];
184     }
185
186     return entry->currentArguments;
187 }
188
189 - (JSVirtualMachine *)virtualMachine
190 {
191     return m_virtualMachine;
192 }
193
194 - (NSString *)name
195 {
196     JSStringRef name = JSGlobalContextCopyName(m_context);
197     if (!name)
198         return nil;
199
200     return CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, name));
201 }
202
203 - (void)setName:(NSString *)name
204 {
205     JSStringRef nameJS = name ? JSStringCreateWithCFString((__bridge CFStringRef)name) : nullptr;
206     JSGlobalContextSetName(m_context, nameJS);
207     if (nameJS)
208         JSStringRelease(nameJS);
209 }
210
211 - (BOOL)_remoteInspectionEnabled
212 {
213     return JSGlobalContextGetRemoteInspectionEnabled(m_context);
214 }
215
216 - (void)_setRemoteInspectionEnabled:(BOOL)enabled
217 {
218     JSGlobalContextSetRemoteInspectionEnabled(m_context, enabled);
219 }
220
221 - (BOOL)_includesNativeCallStackWhenReportingExceptions
222 {
223     return JSGlobalContextGetIncludesNativeCallStackWhenReportingExceptions(m_context);
224 }
225
226 - (void)_setIncludesNativeCallStackWhenReportingExceptions:(BOOL)includeNativeCallStack
227 {
228     JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(m_context, includeNativeCallStack);
229 }
230
231 - (CFRunLoopRef)_debuggerRunLoop
232 {
233     return JSGlobalContextGetDebuggerRunLoop(m_context);
234 }
235
236 - (void)_setDebuggerRunLoop:(CFRunLoopRef)runLoop
237 {
238     JSGlobalContextSetDebuggerRunLoop(m_context, runLoop);
239 }
240
241 @end
242
243 @implementation JSContext(SubscriptSupport)
244
245 - (JSValue *)objectForKeyedSubscript:(id)key
246 {
247     return [self globalObject][key];
248 }
249
250 - (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
251 {
252     [self globalObject][key] = object;
253 }
254
255 @end
256
257 @implementation JSContext (Internal)
258
259 - (instancetype)initWithGlobalContextRef:(JSGlobalContextRef)context
260 {
261     self = [super init];
262     if (!self)
263         return nil;
264
265     JSC::JSGlobalObject* globalObject = toJS(context)->lexicalGlobalObject();
266     m_virtualMachine = [[JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&globalObject->vm())] retain];
267     ASSERT(m_virtualMachine);
268     m_context = JSGlobalContextRetain(context);
269     [self ensureWrapperMap];
270
271     self.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
272         context.exception = exceptionValue;
273     };
274
275     [m_virtualMachine addContext:self forGlobalContextRef:m_context];
276
277     return self;
278 }
279
280 - (void)notifyException:(JSValueRef)exceptionValue
281 {
282     self.exceptionHandler(self, [JSValue valueWithJSValueRef:exceptionValue inContext:self]);
283 }
284
285 - (JSValue *)valueFromNotifyException:(JSValueRef)exceptionValue
286 {
287     [self notifyException:exceptionValue];
288     return [JSValue valueWithUndefinedInContext:self];
289 }
290
291 - (BOOL)boolFromNotifyException:(JSValueRef)exceptionValue
292 {
293     [self notifyException:exceptionValue];
294     return NO;
295 }
296
297 - (void)beginCallbackWithData:(CallbackData *)callbackData calleeValue:(JSValueRef)calleeValue thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments
298 {
299     Thread& thread = Thread::current();
300     [self retain];
301     CallbackData *prevStack = (CallbackData *)thread.m_apiData;
302     *callbackData = (CallbackData){ prevStack, self, [self.exception retain], calleeValue, thisValue, argumentCount, arguments, nil };
303     thread.m_apiData = callbackData;
304     self.exception = nil;
305 }
306
307 - (void)endCallbackWithData:(CallbackData *)callbackData
308 {
309     Thread& thread = Thread::current();
310     self.exception = callbackData->preservedException;
311     [callbackData->preservedException release];
312     [callbackData->currentArguments release];
313     thread.m_apiData = callbackData->next;
314     [self release];
315 }
316
317 - (JSValue *)wrapperForObjCObject:(id)object
318 {
319     JSC::JSLockHolder locker(toJS(m_context));
320     return [[self wrapperMap] jsWrapperForObject:object inContext:self];
321 }
322
323 - (JSValue *)wrapperForJSObject:(JSValueRef)value
324 {
325     JSC::JSLockHolder locker(toJS(m_context));
326     return [[self wrapperMap] objcWrapperForJSValueRef:value inContext:self];
327 }
328
329 + (JSContext *)contextWithJSGlobalContextRef:(JSGlobalContextRef)globalContext
330 {
331     JSVirtualMachine *virtualMachine = [JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&toJS(globalContext)->vm())];
332     JSContext *context = [virtualMachine contextForGlobalContextRef:globalContext];
333     if (!context)
334         context = [[[JSContext alloc] initWithGlobalContextRef:globalContext] autorelease];
335     return context;
336 }
337
338 @end
339
340 #endif