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