Web Inspector: Eliminate the crazy code for evaluateOnCallFrame
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 May 2016 19:16:19 +0000 (19:16 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 May 2016 19:16:19 +0000 (19:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=157510
<rdar://problem/26191332>

Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* debugger/DebuggerCallFrame.cpp:
(JSC::DebuggerCallFrame::evaluateWithScopeExtension):
Set and clear an optional scope extension object.

* inspector/InjectedScriptSource.js:
(InjectedScript.prototype.evaluate):
(InjectedScript.prototype._evaluateOn):
(InjectedScript.prototype.evaluateOnCallFrame):
Unify the code to use the passed in evaluate function and object.
When evaluating on a call frame the evaluate function ends up being
DebuggerCallFrame::evaluateWithScopeExtension. When evaluating globally
this ends up being JSInjectedScriptHost::evaluateWithScopeExtension.
In both cases "object" is the preferred this object to use.

* debugger/DebuggerCallFrame.h:
* inspector/JSJavaScriptCallFrame.cpp:
(Inspector::JSJavaScriptCallFrame::evaluateWithScopeExtension):
(Inspector::JSJavaScriptCallFrame::evaluate): Deleted.
* inspector/JSJavaScriptCallFrame.h:
* inspector/JSJavaScriptCallFramePrototype.cpp:
(Inspector::JSJavaScriptCallFramePrototype::finishCreation):
(Inspector::jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension):
* inspector/JavaScriptCallFrame.h:
(Inspector::JavaScriptCallFrame::evaluateWithScopeExtension):
(Inspector::JavaScriptCallFrame::evaluate): Deleted.
Pass through to DebuggerCallFrame with the proper arguments.

* debugger/Debugger.cpp:
(JSC::Debugger::hasBreakpoint):
* inspector/ScriptDebugServer.cpp:
(Inspector::ScriptDebugServer::evaluateBreakpointAction):
Use the new evaluate on call frame method name and no scope extension object.

LayoutTests:

* inspector/debugger/evaluateOnCallFrame-CommandLineAPI-expected.txt: Added.
* inspector/debugger/evaluateOnCallFrame-CommandLineAPI.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@200634 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/debugger/evaluateOnCallFrame-CommandLineAPI-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/evaluateOnCallFrame-CommandLineAPI.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/debugger/Debugger.cpp
Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
Source/JavaScriptCore/debugger/DebuggerCallFrame.h
Source/JavaScriptCore/inspector/InjectedScriptSource.js
Source/JavaScriptCore/inspector/JSJavaScriptCallFrame.cpp
Source/JavaScriptCore/inspector/JSJavaScriptCallFrame.h
Source/JavaScriptCore/inspector/JSJavaScriptCallFramePrototype.cpp
Source/JavaScriptCore/inspector/JavaScriptCallFrame.h
Source/JavaScriptCore/inspector/ScriptDebugServer.cpp

index 0182de6..ed016a4 100644 (file)
@@ -1,3 +1,14 @@
+2016-05-10  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Eliminate the crazy code for evaluateOnCallFrame
+        https://bugs.webkit.org/show_bug.cgi?id=157510
+        <rdar://problem/26191332>
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/debugger/evaluateOnCallFrame-CommandLineAPI-expected.txt: Added.
+        * inspector/debugger/evaluateOnCallFrame-CommandLineAPI.html: Added.
+
 2016-05-05  Jer Noble  <jer.noble@apple.com>
 
         Return a Promise from HTMLMediaElement.play()
diff --git a/LayoutTests/inspector/debugger/evaluateOnCallFrame-CommandLineAPI-expected.txt b/LayoutTests/inspector/debugger/evaluateOnCallFrame-CommandLineAPI-expected.txt
new file mode 100644 (file)
index 0000000..4d19aa2
--- /dev/null
@@ -0,0 +1,56 @@
+Tests for the Debugger.evaluateOnCallFrame with the Command Line API.
+
+
+== Running test suite: Debugger.evaluateOnCallFrame.CommandLineAPI
+-- Running test case: ValidateCallFrames
+PASS: Strict call frame is `bar`.
+PASS: Non-strict call frame is `foo`.
+PASS: `a` should be 5 in `bar`.
+PASS: `b` should be 123 in `bar`.
+PASS: `c` should be 0 in `bar`.
+PASS: `this` should be undefined in `bar`.
+PASS: `a` should be 1 in `foo`.
+PASS: `b` should be 2 in `foo`.
+PASS: `c` should be 6 in `foo`.
+PASS: `this` should be 'my-this-object' in `foo`.
+
+-- Running test case: AccessCommandLineAPI
+PASS: CommandLineAPI `keys` can be accessed in the `bar` strict call frame.
+PASS: CommandLineAPI `keys` can be accessed in the `foo` non-strict call frame.
+PASS: CommandLineAPI `keys` should work with a simple object.
+
+-- Running test case: AccessGlobalVariable
+PASS: Should be able to access var in global scope in strict call frame.
+PASS: Should be able to access let in global scope in strict call frame.
+PASS: Should be able to access const in global scope in strict call frame.
+PASS: Should be able to access var in global scope in non-strict call frame.
+PASS: Should be able to access let in global scope in non-strict call frame.
+PASS: Should be able to access const in global scope in non-strict call frame.
+
+-- Running test case: NoVariablesCreatedOnCallFrame
+PASS: Should not be able to access local var created in earlier eval on strict call frame.
+PASS: Should not be able to access local let created in earlier eval on strict call frame.
+PASS: Should not be able to access local const created in earlier eval on strict call frame.
+PASS: Should be able to access local var created in earlier eval on non-strict call frame.
+PASS: Should not be able to access local let created in earlier eval on non-strict call frame.
+PASS: Should not be able to access local const created in earlier eval on non-strict call frame.
+
+-- Running test case: NonStrictAndStrictEvaluations
+PASS: Non-strict evaluation. Should be able to access arguments.callee.
+PASS: Strict evaluation. Should not be able to access arguments.callee.
+
+-- Running test case: CommandLineAPIDoesNotShadowLocalVariables
+PASS: Local variable `keys` should not be shadowed by CommandLineAPI `keys` function in call frame for `foo`.
+
+-- Running test case: CommandLineAPIDoesNotShadowGlobalVariables
+PASS: CommandLineAPI `keys` can be accessed in the `bar` strict call frame.
+PASS: Global assignment to `keys` should be okay.
+PASS: Global variable `keys` should not be shadowed by CommandLineAPI `keys` function in call frame for `bar`.
+PASS: CommandLineAPI `keys` can be accessed in the `bar` strict call frame after deleting global variable `keys`.
+
+-- Running test case: CommandLineAPIDoesNotShadowGlobalObjectProperties
+PASS: `values` should be `window.values` and not shadowed by CommandLineAPI `values` function in strict call frame.
+PASS: `values` should be `window.values` and not shadowed by CommandLineAPI `values` function in non-strict call frame.
+
+-- Running test case: Complete
+
diff --git a/LayoutTests/inspector/debugger/evaluateOnCallFrame-CommandLineAPI.html b/LayoutTests/inspector/debugger/evaluateOnCallFrame-CommandLineAPI.html
new file mode 100644 (file)
index 0000000..d8a9acf
--- /dev/null
@@ -0,0 +1,230 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+var varGlobalVariable = 1;
+let letGlobalVariable = 2;
+const constGlobalVariable = 3;
+
+function foo(a, b) {
+    var c = a + b + arguments[2];
+    var keys = 123;
+    bar(c, keys, -129);
+}
+
+function bar(a, b) {
+    "use strict";
+    let c = b + a + arguments[2];
+    debugger;
+    return c;
+}
+
+function triggerPause() {
+    foo.call("my-this-object", 1, 2, 3);
+}
+
+function test()
+{
+    const objectGroup = "test";
+    const includeCommandLineAPI = true;
+    const returnByValue = true;
+
+    function testEvaluateOnCallFrame(callFrame, expression, callback) {
+        let callFrameId = callFrame.id;
+        DebuggerAgent.evaluateOnCallFrame.invoke({callFrameId, expression, objectGroup, includeCommandLineAPI, returnByValue}, (error, resultValue, wasThrown) => {
+            InspectorTest.assert(!error, "Should not be a protocol error.");
+            InspectorTest.assert(!wasThrown, "Should not be a runtime error.");
+            if (callback)
+                callback(resultValue.value);
+        });
+    }
+
+    function testEvaluateOnCallFrameThrows(callFrame, expression, callback) {
+        let callFrameId = callFrame.id;
+        DebuggerAgent.evaluateOnCallFrame.invoke({callFrameId, expression, objectGroup, includeCommandLineAPI, returnByValue}, (error, resultValue, wasThrown) => {
+            InspectorTest.assert(!error, "Should not be a protocol error.");
+            InspectorTest.assert(wasThrown, "Should be a runtime error.");
+            if (callback)
+                callback(wasThrown);
+        });
+    }
+
+    function testEvaluateOnStrictCallFrame(expression, callback) {
+        let callFrame = WebInspector.debuggerManager.activeCallFrame; // bar
+        testEvaluateOnCallFrame(callFrame, expression, callback);
+    }
+
+    function testEvaluateOnStrictCallFrameThrows(expression, callback) {
+        let callFrame = WebInspector.debuggerManager.activeCallFrame; // bar
+        testEvaluateOnCallFrameThrows(callFrame, expression, callback);
+    }
+
+    function testEvaluateOnNonStrictCallFrame(expression, callback) {
+        let callFrame = WebInspector.debuggerManager.callFrames[1]; // foo
+        testEvaluateOnCallFrame(callFrame, expression, callback);
+    }
+
+    function testEvaluateOnNonStrictCallFrameThrows(expression, callback) {
+        let callFrame = WebInspector.debuggerManager.callFrames[1]; // foo
+        testEvaluateOnCallFrameThrows(callFrame, expression, callback);
+    }
+
+    let suite = InspectorTest.createAsyncSuite("Debugger.evaluateOnCallFrame.CommandLineAPI");
+
+    suite.addTestCase({
+        name: "ValidateCallFrames",
+        description: "Test6 evaluate can access CommandLineAPI methods.",
+        test: (resolve, reject) => {
+            InspectorTest.expectThat(WebInspector.debuggerManager.activeCallFrame.functionName === "bar", "Strict call frame is `bar`.");
+            InspectorTest.expectThat(WebInspector.debuggerManager.callFrames[1].functionName === "foo", "Non-strict call frame is `foo`.");
+
+            testEvaluateOnStrictCallFrame("a", (x) => { InspectorTest.expectThat(x === 6, "`a` should be 5 in `bar`."); });
+            testEvaluateOnStrictCallFrame("b", (x) => { InspectorTest.expectThat(x === 123, "`b` should be 123 in `bar`."); });
+            testEvaluateOnStrictCallFrame("c", (x) => { InspectorTest.expectThat(x === 0, "`c` should be 0 in `bar`."); });
+            testEvaluateOnStrictCallFrame("this", (x) => { InspectorTest.expectThat(x === undefined, "`this` should be undefined in `bar`."); });
+
+            testEvaluateOnNonStrictCallFrame("a", (x) => { InspectorTest.expectThat(x === 1, "`a` should be 1 in `foo`."); });
+            testEvaluateOnNonStrictCallFrame("b", (x) => { InspectorTest.expectThat(x === 2, "`b` should be 2 in `foo`."); });
+            testEvaluateOnNonStrictCallFrame("c", (x) => { InspectorTest.expectThat(x === 6, "`c` should be 6 in `foo`."); });
+            testEvaluateOnNonStrictCallFrame("this.toString()", (x) => { InspectorTest.expectThat(x === "my-this-object", "`this` should be 'my-this-object' in `foo`."); resolve(); });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AccessCommandLineAPI",
+        description: "Test evaluate can access CommandLineAPI methods.",
+        test: (resolve, reject) => {
+            testEvaluateOnStrictCallFrame("keys.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue.includes("[Command Line API]"), "CommandLineAPI `keys` can be accessed in the `bar` strict call frame.");
+            });
+            testEvaluateOnNonStrictCallFrame("dir.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue.includes("[Command Line API]"), "CommandLineAPI `keys` can be accessed in the `foo` non-strict call frame.");
+            });
+            testEvaluateOnStrictCallFrame("keys({a:1, b:2})", (resultValue) => {
+                InspectorTest.expectThat(resultValue.length === 2 && resultValue[0] === "a" && resultValue[1] === "b", "CommandLineAPI `keys` should work with a simple object.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AccessGlobalVariable",
+        description: "Test evaluate can access global variables.",
+        test: (resolve, reject) => {
+            testEvaluateOnStrictCallFrame("varGlobalVariable", (x) => { InspectorTest.expectThat(x === 1, "Should be able to access var in global scope in strict call frame."); });
+            testEvaluateOnStrictCallFrame("letGlobalVariable", (x) => { InspectorTest.expectThat(x === 2, "Should be able to access let in global scope in strict call frame."); });
+            testEvaluateOnStrictCallFrame("constGlobalVariable", (x) => { InspectorTest.expectThat(x === 3, "Should be able to access const in global scope in strict call frame."); });
+
+            testEvaluateOnNonStrictCallFrame("varGlobalVariable", (x) => { InspectorTest.expectThat(x === 1, "Should be able to access var in global scope in non-strict call frame."); });
+            testEvaluateOnNonStrictCallFrame("letGlobalVariable", (x) => { InspectorTest.expectThat(x === 2, "Should be able to access let in global scope in non-strict call frame."); });
+            testEvaluateOnNonStrictCallFrame("constGlobalVariable", (x) => { InspectorTest.expectThat(x === 3, "Should be able to access const in global scope in non-strict call frame."); resolve(); });
+        }
+    });
+
+    suite.addTestCase({
+        name: "NoVariablesCreatedOnCallFrame",
+        description: "Test evaluate may not create new local variables.",
+        test: (resolve, reject) => {
+            testEvaluateOnStrictCallFrame(`
+                var createdVarLocalVariable = 1;
+                let createdLetLocalVariable = 2;
+                const createdConstLocalVariable = 3;
+            `);
+            testEvaluateOnStrictCallFrameThrows("createdVarLocalVariable", (wasThrown) => { InspectorTest.expectThat(wasThrown, "Should not be able to access local var created in earlier eval on strict call frame."); });
+            testEvaluateOnStrictCallFrameThrows("createdLetLocalVariable", (wasThrown) => { InspectorTest.expectThat(wasThrown, "Should not be able to access local let created in earlier eval on strict call frame."); });
+            testEvaluateOnStrictCallFrameThrows("createdConstLocalVariable", (wasThrown) => { InspectorTest.expectThat(wasThrown, "Should not be able to access local const created in earlier eval on strict call frame."); });
+
+            testEvaluateOnNonStrictCallFrame(`
+                var createdVarLocalVariable = 1;
+                let createdLetLocalVariable = 2;
+                const createdConstLocalVariable = 3;
+            `);
+            testEvaluateOnNonStrictCallFrame("createdVarLocalVariable", (resultValue) => { InspectorTest.expectThat(resultValue === 1, "Should be able to access local var created in earlier eval on non-strict call frame."); });
+            testEvaluateOnNonStrictCallFrameThrows("createdLetLocalVariable", (wasThrown) => { InspectorTest.expectThat(wasThrown, "Should not be able to access local let created in earlier eval on non-strict call frame."); });
+            testEvaluateOnNonStrictCallFrameThrows("createdConstLocalVariable", (wasThrown) => { InspectorTest.expectThat(wasThrown, "Should not be able to access local const created in earlier eval on non-strict call frame."); resolve(); });
+        }
+    });
+
+    suite.addTestCase({
+        name: "NonStrictAndStrictEvaluations",
+        description: "Test evaluate can run strict and non-strict programs.",
+        test: (resolve, reject) => {
+            // Non strict, this is okay.
+            testEvaluateOnNonStrictCallFrame("arguments.callee.name", (resultValue) => {
+                InspectorTest.expectThat(resultValue === "foo", "Non-strict evaluation. Should be able to access arguments.callee.");
+            });
+            // Strict, this throw an exception.
+            testEvaluateOnStrictCallFrameThrows("arguments.callee.name", (wasThrown) => {
+                InspectorTest.expectThat(wasThrown, "Strict evaluation. Should not be able to access arguments.callee.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CommandLineAPIDoesNotShadowLocalVariables",
+        description: "Test CommandLineAPI does not shadow local variables.",
+        test: (resolve, reject) => {
+            testEvaluateOnNonStrictCallFrame("keys", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 123, "Local variable `keys` should not be shadowed by CommandLineAPI `keys` function in call frame for `foo`.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CommandLineAPIDoesNotShadowGlobalVariables",
+        description: "Test CommandLineAPI does not shadow global variables.",
+        test: (resolve, reject) => {
+            testEvaluateOnStrictCallFrame("keys.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue.includes("[Command Line API]"), "CommandLineAPI `keys` can be accessed in the `bar` strict call frame.");
+            });
+            testEvaluateOnStrictCallFrame("window.keys = 999", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 999, "Global assignment to `keys` should be okay.");
+            });
+            testEvaluateOnStrictCallFrame("keys", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 999, "Global variable `keys` should not be shadowed by CommandLineAPI `keys` function in call frame for `bar`.");
+            });
+            testEvaluateOnStrictCallFrame("delete window.keys");
+            testEvaluateOnStrictCallFrame("keys.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue.includes("[Command Line API]"), "CommandLineAPI `keys` can be accessed in the `bar` strict call frame after deleting global variable `keys`.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CommandLineAPIDoesNotShadowGlobalObjectProperties",
+        description: "Test CommandLineAPI does not shadow global object properties.",
+        test: (resolve, reject) => {
+            testEvaluateOnStrictCallFrame("values.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue === "[object HTMLDivElement]", "`values` should be `window.values` and not shadowed by CommandLineAPI `values` function in strict call frame.");
+            });
+            testEvaluateOnNonStrictCallFrame("values.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue === "[object HTMLDivElement]", "`values` should be `window.values` and not shadowed by CommandLineAPI `values` function in non-strict call frame.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "Complete",
+        test: (resolve, reject) => {
+            WebInspector.debuggerManager.resume();
+            resolve();
+        }
+    })
+
+    InspectorTest.evaluateInPage("triggerPause()");
+    WebInspector.debuggerManager.singleFireEventListener(WebInspector.DebuggerManager.Event.Paused, (event) => {
+        suite.runTestCasesAndFinish();
+    });
+}
+</script>
+</head>
+<body onload="runTest()">
+<div id="values"></div> <!-- This introduces the named property `window.values` on the window global object. -->
+<p>Tests for the Debugger.evaluateOnCallFrame with the Command Line API.</p>
+</body>
+</html>
index f7f11ab..614553a 100644 (file)
@@ -1,3 +1,44 @@
+2016-05-10  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Eliminate the crazy code for evaluateOnCallFrame
+        https://bugs.webkit.org/show_bug.cgi?id=157510
+        <rdar://problem/26191332>
+
+        Reviewed by Timothy Hatcher.
+
+        * debugger/DebuggerCallFrame.cpp:
+        (JSC::DebuggerCallFrame::evaluateWithScopeExtension):
+        Set and clear an optional scope extension object.
+
+        * inspector/InjectedScriptSource.js:
+        (InjectedScript.prototype.evaluate):
+        (InjectedScript.prototype._evaluateOn):
+        (InjectedScript.prototype.evaluateOnCallFrame):
+        Unify the code to use the passed in evaluate function and object.
+        When evaluating on a call frame the evaluate function ends up being
+        DebuggerCallFrame::evaluateWithScopeExtension. When evaluating globally
+        this ends up being JSInjectedScriptHost::evaluateWithScopeExtension.
+        In both cases "object" is the preferred this object to use.
+
+        * debugger/DebuggerCallFrame.h:
+        * inspector/JSJavaScriptCallFrame.cpp:
+        (Inspector::JSJavaScriptCallFrame::evaluateWithScopeExtension):
+        (Inspector::JSJavaScriptCallFrame::evaluate): Deleted.
+        * inspector/JSJavaScriptCallFrame.h:
+        * inspector/JSJavaScriptCallFramePrototype.cpp:
+        (Inspector::JSJavaScriptCallFramePrototype::finishCreation):
+        (Inspector::jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension):
+        * inspector/JavaScriptCallFrame.h:
+        (Inspector::JavaScriptCallFrame::evaluateWithScopeExtension):
+        (Inspector::JavaScriptCallFrame::evaluate): Deleted.
+        Pass through to DebuggerCallFrame with the proper arguments.
+
+        * debugger/Debugger.cpp:
+        (JSC::Debugger::hasBreakpoint):
+        * inspector/ScriptDebugServer.cpp:
+        (Inspector::ScriptDebugServer::evaluateBreakpointAction):
+        Use the new evaluate on call frame method name and no scope extension object.
+
 2016-05-10  Saam barati  <sbarati@apple.com>
 
         Make super-property-access.js test run for less time because it was timing out in debug builds.
index 69246e2..e2f0205 100644 (file)
@@ -453,7 +453,8 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br
 
     NakedPtr<Exception> exception;
     DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
-    JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception);
+    JSObject* scopeExtensionObject = nullptr;
+    JSValue result = debuggerCallFrame->evaluateWithScopeExtension(breakpoint->condition, scopeExtensionObject, exception);
 
     // We can lose the debugger while executing JavaScript.
     if (!m_currentCallFrame)
index 9277b59..4b34bf3 100644 (file)
 #include "DebuggerEvalEnabler.h"
 #include "DebuggerScope.h"
 #include "Interpreter.h"
+#include "JSCInlines.h"
 #include "JSFunction.h"
 #include "JSLexicalEnvironment.h"
-#include "JSCInlines.h"
+#include "JSWithScope.h"
 #include "Parser.h"
 #include "StackVisitor.h"
 #include "StrongInlines.h"
@@ -175,17 +176,17 @@ JSValue DebuggerCallFrame::thisValue() const
 }
 
 // Evaluate some JavaScript code in the scope of this frame.
-JSValue DebuggerCallFrame::evaluate(const String& script, NakedPtr<Exception>& exception)
+JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
 {
     ASSERT(isValid());
     CallFrame* callFrame = m_callFrame;
     if (!callFrame)
-        return jsNull();
+        return jsUndefined();
 
     JSLockHolder lock(callFrame);
 
     if (!callFrame->codeBlock())
-        return JSValue();
+        return jsUndefined();
     
     DebuggerEvalEnabler evalEnabler(callFrame);
     VM& vm = callFrame->vm();
@@ -211,12 +212,22 @@ JSValue DebuggerCallFrame::evaluate(const String& script, NakedPtr<Exception>& e
         return jsUndefined();
     }
 
+    JSGlobalObject* globalObject = callFrame->vmEntryGlobalObject();
+    if (scopeExtensionObject) {
+        JSScope* ignoredPreviousScope = globalObject->globalScope();
+        globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, scopeExtensionObject, ignoredPreviousScope));
+    }
+
     JSValue thisValue = thisValueForCallFrame(callFrame);
     JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, scope()->jsScope());
     if (vm.exception()) {
         exception = vm.exception();
         vm.clearException();
     }
+
+    if (scopeExtensionObject)
+        globalObject->clearGlobalScopeExtension();
+
     ASSERT(result);
     return result;
 }
index aa3cca5..40e141e 100644 (file)
@@ -68,7 +68,7 @@ public:
     JS_EXPORT_PRIVATE String functionName() const;
     JS_EXPORT_PRIVATE Type type() const;
     JS_EXPORT_PRIVATE JSValue thisValue() const;
-    JSValue evaluate(const String&, NakedPtr<Exception>&);
+    JSValue evaluateWithScopeExtension(const String&, JSObject* scopeExtensionObject, NakedPtr<Exception>&);
 
     bool isValid() const { return !!m_callFrame; }
     JS_EXPORT_PRIVATE void invalidate();
index c8153b9..6687883 100644 (file)
@@ -380,7 +380,7 @@ InjectedScript.prototype = {
 
     evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
     {
-        return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
+        return this._evaluateAndWrap(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
     },
 
     callFunctionOn: function(objectId, expression, args, returnByValue, generatePreview)
@@ -479,50 +479,7 @@ InjectedScript.prototype = {
                 commandLineAPI = new BasicCommandLineAPI(isEvalOnCallFrame ? object : null);
         }
 
-        if (isEvalOnCallFrame) {
-            // We can only use this approach if the evaluate function is the true 'eval'. That allows us to use it with
-            // the 'eval' identifier when calling it. Using 'eval' grants access to the local scope of the closure we
-            // create that provides the command line APIs.
-
-            var parameters = [InjectedScriptHost.evaluate, expression];
-            var expressionFunctionBody = "" +
-                "var global = Function('return this')() || (1, eval)('this');" +
-                "var __originalEval = global.eval; global.eval = __eval;" +
-                "try { return eval(__currentExpression); }" +
-                "finally { global.eval = __originalEval; }";
-
-            if (commandLineAPI) {
-                // To avoid using a 'with' statement (which fails in strict mode and requires injecting the API object)
-                // we instead create a closure where we evaluate the expression. The command line APIs are passed as
-                // parameters to the closure so they are in scope but not injected. This allows the code evaluated in
-                // the console to stay in strict mode (if is was already set), or to get strict mode by prefixing
-                // expressions with 'use strict';.
-
-                var parameterNames = Object.getOwnPropertyNames(commandLineAPI);
-                for (var i = 0; i < parameterNames.length; ++i)
-                    parameters.push(commandLineAPI[parameterNames[i]]);
-
-                var expressionFunctionString = "(function(__eval, __currentExpression, " + parameterNames.join(", ") + ") { " + expressionFunctionBody + " })";
-            } else {
-                // Use a closure in this case too to keep the same behavior of 'var' being captured by the closure instead
-                // of leaking out into the calling scope.
-                var expressionFunctionString = "(function(__eval, __currentExpression) { " + expressionFunctionBody + " })";
-            }
-
-            // Bind 'this' to the function expression using another closure instead of Function.prototype.bind. This ensures things will work if the page replaces bind.
-            var boundExpressionFunctionString = "(function(__function, __thisObject) { return function() { return __function.apply(__thisObject, arguments) }; })(" + expressionFunctionString + ", this)";
-            var expressionFunction = evalFunction.call(object, boundExpressionFunctionString);
-            var result = expressionFunction.apply(null, parameters);
-
-            if (saveResult)
-                this._saveResult(result);
-
-            return result;
-        }
-
-        // When not evaluating on a call frame, we evaluate as a program
-        // with the Command Line API as a scope extension object.
-        var result = InjectedScriptHost.evaluateWithScopeExtension(expression, commandLineAPI);
+        var result = evalFunction.call(object, expression, commandLineAPI);        
         if (saveResult)
             this._saveResult(result);
         return result;
@@ -547,7 +504,7 @@ InjectedScript.prototype = {
         var callFrame = this._callFrameForId(topCallFrame, callFrameId);
         if (!callFrame)
             return "Could not find call frame with given id";
-        return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
+        return this._evaluateAndWrap(callFrame.evaluateWithScopeExtension, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
     },
 
     _callFrameForId: function(topCallFrame, callFrameId)
index 0b0fc42..2dd0a9c 100644 (file)
@@ -73,10 +73,15 @@ JSJavaScriptCallFrame::~JSJavaScriptCallFrame()
     releaseImpl();
 }
 
-JSValue JSJavaScriptCallFrame::evaluate(ExecState* exec)
+JSValue JSJavaScriptCallFrame::evaluateWithScopeExtension(ExecState* exec)
 {
+    String script = exec->argument(0).toString(exec)->value(exec);
+    if (exec->hadException())
+        return jsUndefined();
+
     NakedPtr<Exception> exception;
-    JSValue result = impl().evaluate(exec->argument(0).toString(exec)->value(exec), exception);
+    JSObject* scopeExtension = exec->argument(1).getObject();
+    JSValue result = impl().evaluateWithScopeExtension(script, scopeExtension, exception);
     if (exception)
         exec->vm().throwException(exec, exception);
 
index 324627f..351d3a2 100644 (file)
@@ -57,7 +57,7 @@ public:
     void releaseImpl();
 
     // Functions.
-    JSC::JSValue evaluate(JSC::ExecState*);
+    JSC::JSValue evaluateWithScopeExtension(JSC::ExecState*);
     JSC::JSValue scopeType(JSC::ExecState*);
 
     // Attributes.
index 9bb203f..21840c3 100644 (file)
@@ -38,7 +38,7 @@ using namespace JSC;
 namespace Inspector {
 
 // Functions.
-static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionEvaluate(ExecState*);
+static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionScopeType(ExecState*);
 
 // Attributes.
@@ -59,7 +59,7 @@ void JSJavaScriptCallFramePrototype::finishCreation(VM& vm, JSGlobalObject* glob
     ASSERT(inherits(info()));
     vm.prototypeMap.addPrototype(this);
 
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("evaluate", jsJavaScriptCallFramePrototypeFunctionEvaluate, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("evaluateWithScopeExtension", jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("scopeType", jsJavaScriptCallFramePrototypeFunctionScopeType, DontEnum, 1);
 
     JSC_NATIVE_GETTER("caller", jsJavaScriptCallFrameAttributeCaller, DontEnum | Accessor);
@@ -72,15 +72,14 @@ void JSJavaScriptCallFramePrototype::finishCreation(VM& vm, JSGlobalObject* glob
     JSC_NATIVE_GETTER("type", jsJavaScriptCallFrameAttributeType, DontEnum | Accessor);
 }
 
-EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionEvaluate(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionEvaluateWithScopeExtension(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue();
     JSJavaScriptCallFrame* castedThis = jsDynamicCast<JSJavaScriptCallFrame*>(thisValue);
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
-    return JSValue::encode(castedThis->evaluate(exec));
+    return JSValue::encode(castedThis->evaluateWithScopeExtension(exec));
 }
 
 EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionScopeType(ExecState* exec)
@@ -90,7 +89,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFramePrototypeFunctionScopeType(Exe
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->scopeType(exec));
 }
 
@@ -101,7 +99,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeCaller(ExecState* exe
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->caller(exec));
 }
 
@@ -112,7 +109,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeSourceID(ExecState* e
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->sourceID(exec));
 }
 
@@ -123,7 +119,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeLine(ExecState* exec)
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->line(exec));
 }
 
@@ -134,7 +129,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeColumn(ExecState* exe
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->column(exec));
 }
 
@@ -145,7 +139,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeFunctionName(ExecStat
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->functionName(exec));
 }
 
@@ -156,7 +149,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeScopeChain(ExecState*
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->scopeChain(exec));
 }
 
@@ -167,7 +159,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeThisObject(ExecState*
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->thisObject(exec));
 }
 
@@ -178,7 +169,6 @@ EncodedJSValue JSC_HOST_CALL jsJavaScriptCallFrameAttributeType(ExecState* exec)
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSJavaScriptCallFrame::info());
     return JSValue::encode(castedThis->type(exec));
 }
 
index c7d037b..c8abe9c 100644 (file)
@@ -55,7 +55,7 @@ public:
     JSC::JSGlobalObject* vmEntryGlobalObject() const { return m_debuggerCallFrame->vmEntryGlobalObject(); }
 
     JSC::JSValue thisValue() const { return m_debuggerCallFrame->thisValue(); }
-    JSC::JSValue evaluate(const String& script, NakedPtr<JSC::Exception>& exception) const  { return m_debuggerCallFrame->evaluate(script, exception); }
+    JSC::JSValue evaluateWithScopeExtension(const String& script, JSC::JSObject* scopeExtension, NakedPtr<JSC::Exception>& exception) const { return m_debuggerCallFrame->evaluateWithScopeExtension(script, scopeExtension, exception); }
 
 private:
     JavaScriptCallFrame(PassRefPtr<JSC::DebuggerCallFrame>);
index 88ff090..9cc150b 100644 (file)
@@ -94,7 +94,8 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
     }
     case ScriptBreakpointActionTypeEvaluate: {
         NakedPtr<Exception> exception;
-        debuggerCallFrame->evaluate(breakpointAction.data, exception);
+        JSObject* scopeExtensionObject = nullptr;
+        debuggerCallFrame->evaluateWithScopeExtension(breakpointAction.data, scopeExtensionObject, exception);
         if (exception)
             reportException(debuggerCallFrame->exec(), exception);
         break;
@@ -104,7 +105,8 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
         break;
     case ScriptBreakpointActionTypeProbe: {
         NakedPtr<Exception> exception;
-        JSValue result = debuggerCallFrame->evaluate(breakpointAction.data, exception);
+        JSObject* scopeExtensionObject = nullptr;
+        JSValue result = debuggerCallFrame->evaluateWithScopeExtension(breakpointAction.data, scopeExtensionObject, exception);
         if (exception)
             reportException(debuggerCallFrame->exec(), exception);