/ API / JSContext.mm
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  #import "config.h"
 27  
 28  #import "APICast.h"
 29  #import "Completion.h"
 30  #import "JSBaseInternal.h"
 31  #import "JSCInlines.h"
 32  #import "JSContextInternal.h"
 33  #import "JSContextPrivate.h"
 34  #import "JSContextRefInternal.h"
 35  #import "JSGlobalObject.h"
 36  #import "JSInternalPromise.h"
 37  #import "JSModuleLoader.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  #ifdef DARLING_NONUNIFIED_BUILD
 46  #include "JSScriptInternal.h"
 47  #include "JSAPIGlobalObject.h"
 48  #endif
 49  
 50  #import <wtf/WeakObjCPtr.h>
 51  
 52  #if JSC_OBJC_API_ENABLED
 53  
 54  #if defined(DARLING) && __i386__
 55  @implementation JSContext
 56  #else
 57  @implementation JSContext {
 58      JSVirtualMachine *m_virtualMachine;
 59      JSGlobalContextRef m_context;
 60      JSC::Strong<JSC::JSObject> m_exception;
 61      WeakObjCPtr<id <JSModuleLoaderDelegate>> m_moduleLoaderDelegate;
 62  }
 63  #endif
 64  
 65  - (JSGlobalContextRef)JSGlobalContextRef
 66  {
 67      return m_context;
 68  }
 69  
 70  - (void)ensureWrapperMap
 71  {
 72      if (!toJS([self JSGlobalContextRef])->wrapperMap()) {
 73          // The map will be retained by the GlobalObject in initialization.
 74          [[[JSWrapperMap alloc] initWithGlobalContextRef:[self JSGlobalContextRef]] release];
 75      }
 76  }
 77  
 78  - (instancetype)init
 79  {
 80      return [self initWithVirtualMachine:[[[JSVirtualMachine alloc] init] autorelease]];
 81  }
 82  
 83  - (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
 84  {
 85      self = [super init];
 86      if (!self)
 87          return nil;
 88  
 89      m_virtualMachine = [virtualMachine retain];
 90      m_context = JSGlobalContextCreateInGroup(getGroupFromVirtualMachine(virtualMachine), 0);
 91  
 92      self.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
 93          context.exception = exceptionValue;
 94      };
 95  
 96      [self ensureWrapperMap];
 97      [m_virtualMachine addContext:self forGlobalContextRef:m_context];
 98  
 99      return self;
100  }
101  
102  - (void)dealloc
103  {
104      m_exception.clear();
105      JSGlobalContextRelease(m_context);
106      [m_virtualMachine release];
107      [_exceptionHandler release];
108      [super dealloc];
109  }
110  
111  - (JSValue *)evaluateScript:(NSString *)script
112  {
113      return [self evaluateScript:script withSourceURL:nil];
114  }
115  
116  - (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL *)sourceURL
117  {
118      JSValueRef exceptionValue = nullptr;
119      auto scriptJS = OpaqueJSString::tryCreate(script);
120      auto sourceURLJS = OpaqueJSString::tryCreate([sourceURL absoluteString]);
121      JSValueRef result = JSEvaluateScript(m_context, scriptJS.get(), nullptr, sourceURLJS.get(), 0, &exceptionValue);
122  
123      if (exceptionValue)
124          return [self valueFromNotifyException:exceptionValue];
125      return [JSValue valueWithJSValueRef:result inContext:self];
126  }
127  
128  - (JSValue *)evaluateJSScript:(JSScript *)script
129  {
130      JSC::JSGlobalObject* globalObject = toJS(m_context);
131      JSC::VM& vm = globalObject->vm();
132      JSC::JSLockHolder locker(vm);
133  
134      if (script.type == kJSScriptTypeProgram) {
135          JSValueRef exceptionValue = nullptr;
136          JSC::SourceCode sourceCode = [script sourceCode];
137          JSValueRef result = JSEvaluateScriptInternal(locker, m_context, nullptr, sourceCode, &exceptionValue);
138  
139          if (exceptionValue)
140              return [self valueFromNotifyException:exceptionValue];
141          return [JSValue valueWithJSValueRef:result inContext:self];
142      }
143  
144      auto* apiGlobalObject = JSC::jsDynamicCast<JSC::JSAPIGlobalObject*>(vm, globalObject);
145      if (!apiGlobalObject)
146          return [JSValue valueWithNewPromiseRejectedWithReason:[JSValue valueWithNewErrorFromMessage:@"Context does not support module loading" inContext:self] inContext:self];
147  
148      auto scope = DECLARE_CATCH_SCOPE(vm);
149      JSC::JSValue result = apiGlobalObject->loadAndEvaluateJSScriptModule(locker, script);
150      if (scope.exception()) {
151          JSValueRef exceptionValue = toRef(apiGlobalObject, scope.exception()->value());
152          scope.clearException();
153          // FIXME: We should not clearException if it is TerminatedExecutionError.
154          // https://bugs.webkit.org/show_bug.cgi?id=220821
155          return [JSValue valueWithNewPromiseRejectedWithReason:[JSValue valueWithJSValueRef:exceptionValue inContext:self] inContext:self];
156      }
157      return [JSValue valueWithJSValueRef:toRef(vm, result) inContext:self];
158  }
159  
160  - (JSValue *)dependencyIdentifiersForModuleJSScript:(JSScript *)script
161  {
162      JSC::JSGlobalObject* globalObject = toJS(m_context);
163      JSC::VM& vm = globalObject->vm();
164      JSC::JSLockHolder locker(vm);
165  
166      if (script.type != kJSScriptTypeModule) {
167          self.exceptionHandler(self, [JSValue valueWithNewErrorFromMessage:@"script is not a module" inContext:self]);
168          return [JSValue valueWithUndefinedInContext:self];
169      }
170  
171      auto scope = DECLARE_CATCH_SCOPE(vm);
172      JSC::JSArray* result = globalObject->moduleLoader()->dependencyKeysIfEvaluated(globalObject, JSC::jsString(vm, [[script sourceURL] absoluteString]));
173      if (scope.exception()) {
174          JSValueRef exceptionValue = toRef(globalObject, scope.exception()->value());
175          scope.clearException();
176          return [self valueFromNotifyException:exceptionValue];
177      }
178  
179      if (!result) {
180          self.exceptionHandler(self, [JSValue valueWithNewErrorFromMessage:@"script has not run in context or was not evaluated successfully" inContext:self]);
181          return [JSValue valueWithUndefinedInContext:self];
182      }
183      return [JSValue valueWithJSValueRef:toRef(vm, result) inContext:self];
184  }
185  
186  - (void)_setITMLDebuggableType
187  {
188      JSC::JSGlobalObject* globalObject = toJS(m_context);
189      JSC::VM& vm = globalObject->vm();
190      JSC::JSLockHolder locker(vm);
191  
192      globalObject->setIsITML();
193  }
194  
195  - (void)setException:(JSValue *)value
196  {
197      JSC::JSGlobalObject* globalObject = toJS(m_context);
198      JSC::VM& vm = globalObject->vm();
199      JSC::JSLockHolder locker(vm);
200      if (value)
201          m_exception.set(vm, toJS(JSValueToObject(m_context, valueInternalValue(value), 0)));
202      else
203          m_exception.clear();
204  }
205  
206  - (JSValue *)exception
207  {
208      if (!m_exception)
209          return nil;
210      return [JSValue valueWithJSValueRef:toRef(m_exception.get()) inContext:self];
211  }
212  
213  - (JSValue *)globalObject
214  {
215      return [JSValue valueWithJSValueRef:JSContextGetGlobalObject(m_context) inContext:self];
216  }
217  
218  + (JSContext *)currentContext
219  {
220      Thread& thread = Thread::current();
221      CallbackData *entry = (CallbackData *)thread.m_apiData;
222      return entry ? entry->context : nil;
223  }
224  
225  + (JSValue *)currentThis
226  {
227      Thread& thread = Thread::current();
228      CallbackData *entry = (CallbackData *)thread.m_apiData;
229      if (!entry)
230          return nil;
231      return [JSValue valueWithJSValueRef:entry->thisValue inContext:[JSContext currentContext]];
232  }
233  
234  + (JSValue *)currentCallee
235  {
236      Thread& thread = Thread::current();
237      CallbackData *entry = (CallbackData *)thread.m_apiData;
238      // calleeValue may be null if we are initializing a promise.
239      if (!entry || !entry->calleeValue)
240          return nil;
241      return [JSValue valueWithJSValueRef:entry->calleeValue inContext:[JSContext currentContext]];
242  }
243  
244  + (NSArray *)currentArguments
245  {
246      Thread& thread = Thread::current();
247      CallbackData *entry = (CallbackData *)thread.m_apiData;
248  
249      if (!entry)
250          return nil;
251  
252      if (!entry->currentArguments) {
253          JSContext *context = [JSContext currentContext];
254          size_t count = entry->argumentCount;
255          NSMutableArray *arguments = [[NSMutableArray alloc] initWithCapacity:count];
256          for (size_t i = 0; i < count; ++i)
257              [arguments setObject:[JSValue valueWithJSValueRef:entry->arguments[i] inContext:context] atIndexedSubscript:i];
258          entry->currentArguments = arguments;
259      }
260  
261      return entry->currentArguments;
262  }
263  
264  - (JSVirtualMachine *)virtualMachine
265  {
266      return m_virtualMachine;
267  }
268  
269  - (NSString *)name
270  {
271      JSStringRef name = JSGlobalContextCopyName(m_context);
272      if (!name)
273          return nil;
274  
275      return CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, name));
276  }
277  
278  - (void)setName:(NSString *)name
279  {
280      JSGlobalContextSetName(m_context, OpaqueJSString::tryCreate(name).get());
281  }
282  
283  - (BOOL)_remoteInspectionEnabled
284  {
285      return JSGlobalContextGetRemoteInspectionEnabled(m_context);
286  }
287  
288  - (void)_setRemoteInspectionEnabled:(BOOL)enabled
289  {
290      JSGlobalContextSetRemoteInspectionEnabled(m_context, enabled);
291  }
292  
293  - (BOOL)_includesNativeCallStackWhenReportingExceptions
294  {
295      return JSGlobalContextGetIncludesNativeCallStackWhenReportingExceptions(m_context);
296  }
297  
298  - (void)_setIncludesNativeCallStackWhenReportingExceptions:(BOOL)includeNativeCallStack
299  {
300      JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(m_context, includeNativeCallStack);
301  }
302  
303  - (CFRunLoopRef)_debuggerRunLoop
304  {
305      return JSGlobalContextGetDebuggerRunLoop(m_context);
306  }
307  
308  - (void)_setDebuggerRunLoop:(CFRunLoopRef)runLoop
309  {
310      JSGlobalContextSetDebuggerRunLoop(m_context, runLoop);
311  }
312  
313  - (id<JSModuleLoaderDelegate>)moduleLoaderDelegate
314  {
315      return m_moduleLoaderDelegate.getAutoreleased();
316  }
317  
318  - (void)setModuleLoaderDelegate:(id<JSModuleLoaderDelegate>)moduleLoaderDelegate
319  {
320      m_moduleLoaderDelegate = moduleLoaderDelegate;
321  }
322  
323  @end
324  
325  @implementation JSContext(SubscriptSupport)
326  
327  - (JSValue *)objectForKeyedSubscript:(id)key
328  {
329      return [self globalObject][key];
330  }
331  
332  - (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
333  {
334      [self globalObject][key] = object;
335  }
336  
337  @end
338  
339  @implementation JSContext (Internal)
340  
341  - (instancetype)initWithGlobalContextRef:(JSGlobalContextRef)context
342  {
343      self = [super init];
344      if (!self)
345          return nil;
346  
347      JSC::JSGlobalObject* globalObject = toJS(context);
348      m_virtualMachine = [[JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&globalObject->vm())] retain];
349      ASSERT(m_virtualMachine);
350      m_context = JSGlobalContextRetain(context);
351      [self ensureWrapperMap];
352  
353      self.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
354          context.exception = exceptionValue;
355      };
356  
357      [m_virtualMachine addContext:self forGlobalContextRef:m_context];
358  
359      return self;
360  }
361  
362  - (void)notifyException:(JSValueRef)exceptionValue
363  {
364      self.exceptionHandler(self, [JSValue valueWithJSValueRef:exceptionValue inContext:self]);
365  }
366  
367  - (JSValue *)valueFromNotifyException:(JSValueRef)exceptionValue
368  {
369      [self notifyException:exceptionValue];
370      return [JSValue valueWithUndefinedInContext:self];
371  }
372  
373  - (BOOL)boolFromNotifyException:(JSValueRef)exceptionValue
374  {
375      [self notifyException:exceptionValue];
376      return NO;
377  }
378  
379  - (void)beginCallbackWithData:(CallbackData *)callbackData calleeValue:(JSValueRef)calleeValue thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments
380  {
381      Thread& thread = Thread::current();
382      [self retain];
383      CallbackData *prevStack = (CallbackData *)thread.m_apiData;
384      *callbackData = (CallbackData){ prevStack, self, [self.exception retain], calleeValue, thisValue, argumentCount, arguments, nil };
385      thread.m_apiData = callbackData;
386      self.exception = nil;
387  }
388  
389  - (void)endCallbackWithData:(CallbackData *)callbackData
390  {
391      Thread& thread = Thread::current();
392      self.exception = callbackData->preservedException;
393      [callbackData->preservedException release];
394      [callbackData->currentArguments release];
395      thread.m_apiData = callbackData->next;
396      [self release];
397  }
398  
399  - (JSValue *)wrapperForObjCObject:(id)object
400  {
401      JSC::JSLockHolder locker(toJS(m_context));
402      return [[self wrapperMap] jsWrapperForObject:object inContext:self];
403  }
404  
405  - (JSWrapperMap *)wrapperMap
406  {
407      return toJS(m_context)->wrapperMap();
408  }
409  
410  - (JSValue *)wrapperForJSObject:(JSValueRef)value
411  {
412      JSC::JSLockHolder locker(toJS(m_context));
413      return [[self wrapperMap] objcWrapperForJSValueRef:value inContext:self];
414  }
415  
416  + (JSContext *)contextWithJSGlobalContextRef:(JSGlobalContextRef)globalContext
417  {
418      JSVirtualMachine *virtualMachine = [JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&toJS(globalContext)->vm())];
419      JSContext *context = [virtualMachine contextForGlobalContextRef:globalContext];
420      if (!context)
421          context = [[[JSContext alloc] initWithGlobalContextRef:globalContext] autorelease];
422      return context;
423  }
424  
425  @end
426  
427  #endif