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