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