Move -_enterFrame and -_leaveFrame from WebCoreScriptDebugger to WebScriptDeb...
[WebKit-https.git] / WebKit / mac / WebView / WebScriptDebugDelegate.mm
index 71ad1effd620a188a02d7bc1da6500ef1020b589..0e48bf5ac8acdfffe7507386b49d768f6e06d9fb 100644 (file)
 
 #import "WebScriptDebugDelegatePrivate.h"
 
+#import "WebCoreScriptDebugger.h"
 #import "WebDataSource.h"
 #import "WebDataSourceInternal.h"
 #import "WebFrameBridge.h"
 #import "WebFrameInternal.h"
 #import "WebScriptDebugServerPrivate.h"
 #import "WebViewInternal.h"
+#import <JavaScriptCore/ExecState.h>
+#import <JavaScriptCore/JSGlobalObject.h>
+#import <JavaScriptCore/function.h>
+#import <JavaScriptCore/interpreter.h>
 #import <WebCore/Frame.h>
-#import <WebCore/WebCoreScriptDebugger.h>
+#import <WebCore/WebScriptObjectPrivate.h>
 
+using namespace KJS;
 using namespace WebCore;
 
 // FIXME: these error strings should be public for future use by WebScriptObject and in WebScriptObject.h
@@ -46,7 +52,8 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
 
 @interface WebScriptCallFrame (WebScriptDebugDelegateInternal)
 
-- (WebScriptCallFrame *)_initWithFrame:(WebCoreScriptCallFrame *)frame;
+- (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebScriptCallFrame *)caller state:(ExecState *)state;
+- (id)_convertValueToObjcValue:(JSValue *)value;
 
 @end
 
@@ -64,6 +71,7 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
 - (void)dealloc
 {
     [_debugger release];
+    [_current release];
     [super dealloc];
 }
 
@@ -72,9 +80,17 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
     return core(_webFrame)->windowScriptObject();
 }
 
-- (id)newWrapperForFrame:(WebCoreScriptCallFrame *)frame
+- (WebScriptCallFrame *)enterFrame:(ExecState*)state;
 {
-    return [[WebScriptCallFrame alloc] _initWithFrame:frame];
+    _current = [[WebScriptCallFrame alloc] _initWithGlobalObject:[self globalObject] caller:_current state:state];
+    return _current;
+}
+
+- (WebScriptCallFrame *)leaveFrame;
+{
+    WebScriptCallFrame *caller = [[_current caller] retain];
+    [_current release];
+    return _current = caller;
 }
 
 - (void)parsedSource:(NSString *)source fromURL:(NSURL *)url sourceId:(int)sid startLine:(int)startLine errorLine:(int)errorLine errorMessage:(NSString *)errorMessage
@@ -96,52 +112,98 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
     }
 }
 
-- (void)enteredFrame:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
+- (void)enteredFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
 {
     WebView *webView = [_webFrame webView];
-    [[webView _scriptDebugDelegateForwarder] webView:webView didEnterCallFrame:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+    [[webView _scriptDebugDelegateForwarder] webView:webView didEnterCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
     if ([WebScriptDebugServer listenerCount])
-        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView didEnterCallFrame:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView didEnterCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
 }
 
-- (void)hitStatement:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
+- (void)hitStatement:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
 {
     WebView *webView = [_webFrame webView];
-    [[webView _scriptDebugDelegateForwarder] webView:webView willExecuteStatement:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+    [[webView _scriptDebugDelegateForwarder] webView:webView willExecuteStatement:frame sourceId:sid line:lineno forWebFrame:_webFrame];
     if ([WebScriptDebugServer listenerCount])
-        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView willExecuteStatement:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView willExecuteStatement:frame sourceId:sid line:lineno forWebFrame:_webFrame];
 }
 
-- (void)leavingFrame:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
+- (void)leavingFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
 {
     WebView *webView = [_webFrame webView];
-    [[webView _scriptDebugDelegateForwarder] webView:webView willLeaveCallFrame:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+    [[webView _scriptDebugDelegateForwarder] webView:webView willLeaveCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
     if ([WebScriptDebugServer listenerCount])
-        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView willLeaveCallFrame:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView willLeaveCallFrame:frame sourceId:sid line:lineno forWebFrame:_webFrame];
 }
 
-- (void)exceptionRaised:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
+- (void)exceptionRaised:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno
 {
     WebView *webView = [_webFrame webView];
-    [[webView _scriptDebugDelegateForwarder] webView:webView exceptionWasRaised:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+    [[webView _scriptDebugDelegateForwarder] webView:webView exceptionWasRaised:frame sourceId:sid line:lineno forWebFrame:_webFrame];
     if ([WebScriptDebugServer listenerCount])
-        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView exceptionWasRaised:[frame wrapper] sourceId:sid line:lineno forWebFrame:_webFrame];
+        [[WebScriptDebugServer sharedScriptDebugServer] webView:webView exceptionWasRaised:frame sourceId:sid line:lineno forWebFrame:_webFrame];
 }
 
 @end
 
+@interface WebScriptCallFramePrivate : NSObject {
+@public
+    WebScriptObject        *globalObject;   // the global object's proxy (not retained)
+    WebScriptCallFrame     *caller;         // previous stack frame
+    KJS::ExecState         *state;
+}
+@end
 
+@implementation WebScriptCallFramePrivate
+- (void)dealloc
+{
+    [caller release];
+    [super dealloc];
+}
+@end
+
+// WebScriptCallFrame
+//
+// One of these is created to represent each stack frame.  Additionally, there is a "global"
+// frame to represent the outermost scope.  This global frame is always the last frame in
+// the chain of callers.
+//
+// The delegate can assign a "wrapper" to each frame object so it can relay calls through its
+// own exported interface.  This class is private to WebCore (and the delegate).
 
 @implementation WebScriptCallFrame (WebScriptDebugDelegateInternal)
 
-- (WebScriptCallFrame *)_initWithFrame:(WebCoreScriptCallFrame *)frame
+- (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebScriptCallFrame *)caller state:(ExecState *)state
 {
     if ((self = [super init])) {
-        _private = frame;
+        _private = [[WebScriptCallFramePrivate alloc] init];
+        _private->globalObject = globalObj;
+        _private->caller = caller; // (already retained)
+        _private->state = state;
     }
     return self;
 }
 
+- (id)_convertValueToObjcValue:(JSValue *)value
+{
+    if (!value)
+        return nil;
+
+    WebScriptObject *globalObject = _private->globalObject;
+    if (value == [globalObject _imp])
+        return globalObject;
+
+    Bindings::RootObject* root1 = [globalObject _originRootObject];
+    if (!root1)
+        return nil;
+
+    Bindings::RootObject* root2 = [globalObject _rootObject];
+    if (!root2)
+        return nil;
+
+    return [WebScriptObject _convertValueToObjcValue:value originRootObject:root1 rootObject:root2];
+}
+
 @end
 
 
@@ -151,6 +213,7 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
 - (void) dealloc
 {
     [_userInfo release];
+    [_private release];
     [super dealloc];
 }
 
@@ -169,27 +232,107 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
 
 - (WebScriptCallFrame *)caller
 {
-    return [[_private caller] wrapper];
+    return _private->caller;
 }
 
+// Returns an array of scope objects (most local first).
+// The properties of each scope object are the variables for that scope.
+// Note that the last entry in the array will _always_ be the global object (windowScriptObject),
+// whose properties are the global variables.
+
 - (NSArray *)scopeChain
 {
-    return [_private scopeChain];
+    ExecState* state = _private->state;
+    if (!state->scopeNode())  // global frame
+        return [NSArray arrayWithObject:_private->globalObject];
+
+    ScopeChain      chain  = state->scopeChain();
+    NSMutableArray *scopes = [[NSMutableArray alloc] init];
+
+    while (!chain.isEmpty()) {
+        [scopes addObject:[self _convertValueToObjcValue:chain.top()]];
+        chain.pop();
+    }
+
+    NSArray *result = [NSArray arrayWithArray:scopes];
+    [scopes release];
+    return result;
 }
 
+// Returns the name of the function for this frame, if available.
+// Returns nil for anonymous functions and for the global frame.
+
 - (NSString *)functionName
 {
-    return [_private functionName];
+    ExecState* state = _private->state;
+    if (!state->scopeNode())
+        return nil;
+
+    FunctionImp* func = state->function();
+    if (!func)
+        return nil;
+
+    Identifier fn = func->functionName();
+    return toNSString(fn.ustring());
 }
 
+// Returns the pending exception for this frame (nil if none).
+
 - (id)exception
 {
-    return [_private exception];
+    ExecState* state = _private->state;
+    if (!state->hadException())
+        return nil;
+    return [self _convertValueToObjcValue:state->exception()];
 }
 
+// Evaluate some JavaScript code in the context of this frame.
+// The code is evaluated as if by "eval", and the result is returned.
+// If there is an (uncaught) exception, it is returned as though _it_ were the result.
+// Calling this method on the global frame is not quite the same as calling the WebScriptObject
+// method of the same name, due to the treatment of exceptions.
+
+// FIXME: If "script" contains var declarations, the machinery to handle local variables
+// efficiently in JavaScriptCore will not work properly. This could lead to crashes or
+// incorrect variable values. So this is not appropriate for evaluating arbitrary script.
 - (id)evaluateWebScript:(NSString *)script
 {
-    return [_private evaluateWebScript:script];
+    JSLock lock;
+
+    UString code = String(script);
+
+    ExecState* state = _private->state;
+    JSGlobalObject* globalObject = state->dynamicGlobalObject();
+
+    // find "eval"
+    JSObject* eval = NULL;
+    if (state->scopeNode()) {  // "eval" won't work without context (i.e. at global scope)
+        JSValue* v = globalObject->get(state, "eval");
+        if (v->isObject() && static_cast<JSObject*>(v)->implementsCall())
+            eval = static_cast<JSObject*>(v);
+        else
+            // no "eval" - fallback operates on global exec state
+            state = globalObject->globalExec();
+    }
+
+    JSValue* savedException = state->exception();
+    state->clearException();
+
+    // evaluate
+    JSValue* result;
+    if (eval) {
+        List args;
+        args.append(jsString(code));
+        result = eval->call(state, 0, args);
+    } else
+        // no "eval", or no context (i.e. global scope) - use global fallback
+        result = Interpreter::evaluate(state, UString(), 0, code.data(), code.size(), globalObject).value();
+
+    if (state->hadException())
+        result = state->exception();    // (may be redundant depending on which eval path was used)
+    state->setException(savedException);
+
+    return [self _convertValueToObjcValue:result];
 }
 
 @end