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