1 /* 2 * Copyright (C) 2005 Apple Computer, 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 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #import "WebDataSource.h" 30 #import "WebDataSourceInternal.h" 31 #import "WebFrameInternal.h" 32 #import "WebScriptDebugDelegate.h" 33 #import "WebScriptDebugger.h" 34 #import "WebViewInternal.h" 35 #import <WebCore/Frame.h> 36 #import <WebCore/ScriptController.h> 37 #import <WebCore/WebScriptObjectPrivate.h> 38 #import <WebCore/runtime_root.h> 39 #import <debugger/Debugger.h> 40 #import <debugger/DebuggerActivation.h> 41 #import <debugger/DebuggerCallFrame.h> 42 #import <interpreter/CallFrame.h> 43 #import <runtime/Completion.h> 44 #import <runtime/JSFunction.h> 45 #import <runtime/JSGlobalObject.h> 46 #import <runtime/JSLock.h> 47 48 using namespace JSC; 49 using namespace WebCore; 50 51 // FIXME: these error strings should be public for future use by WebScriptObject and in WebScriptObject.h 52 NSString * const WebScriptErrorDomain = @"WebScriptErrorDomain"; 53 NSString * const WebScriptErrorDescriptionKey = @"WebScriptErrorDescription"; 54 NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber"; 55 56 @interface WebScriptCallFrame (WebScriptDebugDelegateInternal) 57 58 - (id)_convertValueToObjcValue:(JSValue)value; 59 60 @end 61 62 @interface WebScriptCallFramePrivate : NSObject { 63 @public 64 WebScriptObject *globalObject; // the global object's proxy (not retained) 65 WebScriptCallFrame *caller; // previous stack frame 66 DebuggerCallFrame* debuggerCallFrame; 67 WebScriptDebugger* debugger; 68 } 69 @end 70 71 @implementation WebScriptCallFramePrivate 72 - (void)dealloc 73 { 74 [caller release]; 75 delete debuggerCallFrame; 76 [super dealloc]; 77 } 78 @end 79 80 // WebScriptCallFrame 81 // 82 // One of these is created to represent each stack frame. Additionally, there is a "global" 83 // frame to represent the outermost scope. This global frame is always the last frame in 84 // the chain of callers. 85 // 86 // The delegate can assign a "wrapper" to each frame object so it can relay calls through its 87 // own exported interface. This class is private to WebCore (and the delegate). 88 89 @implementation WebScriptCallFrame (WebScriptDebugDelegateInternal) 90 91 - (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj debugger:(WebScriptDebugger *)debugger caller:(WebScriptCallFrame *)caller debuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame 92 { 93 if ((self = [super init])) { 94 _private = [[WebScriptCallFramePrivate alloc] init]; 95 _private->globalObject = globalObj; 96 _private->caller = [caller retain]; 97 _private->debugger = debugger; 98 } 99 return self; 100 } 101 102 - (void)_setDebuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame 103 { 104 if (!_private->debuggerCallFrame) 105 _private->debuggerCallFrame = new DebuggerCallFrame(debuggerCallFrame); 106 else 107 *_private->debuggerCallFrame = debuggerCallFrame; 108 } 109 110 - (void)_clearDebuggerCallFrame 111 { 112 delete _private->debuggerCallFrame; 113 _private->debuggerCallFrame = 0; 114 } 115 116 - (id)_convertValueToObjcValue:(JSValue)value 117 { 118 if (!value) 119 return nil; 120 121 WebScriptObject *globalObject = _private->globalObject; 122 if (value == [globalObject _imp]) 123 return globalObject; 124 125 Bindings::RootObject* root1 = [globalObject _originRootObject]; 126 if (!root1) 127 return nil; 128 129 Bindings::RootObject* root2 = [globalObject _rootObject]; 130 if (!root2) 131 return nil; 132 133 return [WebScriptObject _convertValueToObjcValue:value originRootObject:root1 rootObject:root2]; 134 } 135 136 @end 137 138 139 140 @implementation WebScriptCallFrame 141 142 - (void) dealloc 143 { 144 [_userInfo release]; 145 [_private release]; 146 [super dealloc]; 147 } 148 149 - (void)setUserInfo:(id)userInfo 150 { 151 if (userInfo != _userInfo) { 152 [_userInfo release]; 153 _userInfo = [userInfo retain]; 154 } 155 } 156 157 - (id)userInfo 158 { 159 return _userInfo; 160 } 161 162 - (WebScriptCallFrame *)caller 163 { 164 return _private->caller; 165 } 166 167 // Returns an array of scope objects (most local first). 168 // The properties of each scope object are the variables for that scope. 169 // Note that the last entry in the array will _always_ be the global object (windowScriptObject), 170 // whose properties are the global variables. 171 172 - (NSArray *)scopeChain 173 { 174 if (!_private->debuggerCallFrame) 175 return [NSArray array]; 176 177 JSLock lock(SilenceAssertionsOnly); 178 179 ScopeChainNode* scopeChain = _private->debuggerCallFrame->scopeChain(); 180 if (!scopeChain->next) // global frame 181 return [NSArray arrayWithObject:_private->globalObject]; 182 183 NSMutableArray *scopes = [[NSMutableArray alloc] init]; 184 185 ScopeChainIterator end = scopeChain->end(); 186 for (ScopeChainIterator it = scopeChain->begin(); it != end; ++it) { 187 JSObject* object = it->get(); 188 if (object->isActivationObject()) 189 object = new (scopeChain->globalData) DebuggerActivation(*scopeChain->globalData, object); 190 [scopes addObject:[self _convertValueToObjcValue:object]]; 191 } 192 193 NSArray *result = [NSArray arrayWithArray:scopes]; 194 [scopes release]; 195 return result; 196 } 197 198 // Returns the name of the function for this frame, if available. 199 // Returns nil for anonymous functions and for the global frame. 200 201 - (NSString *)functionName 202 { 203 if (!_private->debuggerCallFrame) 204 return nil; 205 206 const UString* functionName = _private->debuggerCallFrame->functionName(); 207 return functionName ? toNSString(*functionName) : nil; 208 } 209 210 // Returns the pending exception for this frame (nil if none). 211 212 - (id)exception 213 { 214 if (!_private->debuggerCallFrame) 215 return nil; 216 217 JSValue exception = _private->debuggerCallFrame->exception(); 218 return exception ? [self _convertValueToObjcValue:exception] : nil; 219 } 220 221 // Evaluate some JavaScript code in the context of this frame. 222 // The code is evaluated as if by "eval", and the result is returned. 223 // If there is an (uncaught) exception, it is returned as though _it_ were the result. 224 // Calling this method on the global frame is not quite the same as calling the WebScriptObject 225 // method of the same name, due to the treatment of exceptions. 226 227 - (id)evaluateWebScript:(NSString *)script 228 { 229 if (!_private->debuggerCallFrame) 230 return nil; 231 232 JSLock lock(SilenceAssertionsOnly); 233 234 // If this is the global call frame and there is no dynamic global object, 235 // Dashcode is attempting to execute JS in the evaluator using a stale 236 // WebScriptCallFrame. Instead, we need to set the dynamic global object 237 // and evaluate the JS in the global object's global call frame. 238 JSGlobalObject* globalObject = _private->debugger->globalObject(); 239 if (self == _private->debugger->globalCallFrame() && !globalObject->globalData().dynamicGlobalObject) { 240 JSGlobalObject* globalObject = _private->debugger->globalObject(); 241 242 DynamicGlobalObjectScope globalObjectScope(globalObject->globalData(), globalObject); 243 244 JSValue exception; 245 JSValue result = evaluateInGlobalCallFrame(stringToUString(script), exception, globalObject); 246 if (exception) 247 return [self _convertValueToObjcValue:exception]; 248 return result ? [self _convertValueToObjcValue:result] : nil; 249 } 250 251 JSValue exception; 252 JSValue result = _private->debuggerCallFrame->evaluate(stringToUString(script), exception); 253 if (exception) 254 return [self _convertValueToObjcValue:exception]; 255 return result ? [self _convertValueToObjcValue:result] : nil; 256 } 257 258 @end 259