<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[210338] branches/safari-603-branch</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/210338">210338</a></dd>
<dt>Author</dt> <dd>matthew_hanson@apple.com</dd>
<dt>Date</dt> <dd>2017-01-05 09:08:50 -0800 (Thu, 05 Jan 2017)</dd>
</dl>
<h3>Log Message</h3>
<pre>Merge <a href="http://trac.webkit.org/projects/webkit/changeset/210033">r210033</a>. rdar://problem/29755339</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#branchessafari603branchLayoutTestsChangeLog">branches/safari-603-branch/LayoutTests/ChangeLog</a></li>
<li><a href="#branchessafari603branchLayoutTestsinspectorcontrollerruntimecontrollerexpectedtxt">branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller-expected.txt</a></li>
<li><a href="#branchessafari603branchLayoutTestsinspectorcontrollerruntimecontrollerhtml">branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller.html</a></li>
<li><a href="#branchessafari603branchSourceWebInspectorUIChangeLog">branches/safari-603-branch/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#branchessafari603branchSourceWebInspectorUIUserInterfaceControllersBreakpointLogMessageLexerjs">branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/BreakpointLogMessageLexer.js</a></li>
<li><a href="#branchessafari603branchSourceWebInspectorUIUserInterfaceControllersRuntimeManagerjs">branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchessafari603branchLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/LayoutTests/ChangeLog (210337 => 210338)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/LayoutTests/ChangeLog        2017-01-05 17:08:47 UTC (rev 210337)
+++ branches/safari-603-branch/LayoutTests/ChangeLog        2017-01-05 17:08:50 UTC (rev 210338)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2017-01-05 Matthew Hanson <matthew_hanson@apple.com>
+
+ Merge r210033. rdar://problem/29755339
+
+ 2016-12-20 Joseph Pecoraro <pecoraro@apple.com>
+
+ Web Inspector: Console could be made useful for very simple await expressions
+ https://bugs.webkit.org/show_bug.cgi?id=165681
+ <rdar://problem/29755339>
+
+ Reviewed by Brian Burg.
+
+ * inspector/controller/runtime-controller-expected.txt:
+ * inspector/controller/runtime-controller.html:
+ Test the "await expression" convenience of RuntimeManager.
+
</ins><span class="cx"> 2017-01-04 Matthew Hanson <matthew_hanson@apple.com>
</span><span class="cx">
</span><span class="cx"> Merge r209990. rdar://problem/29705967
</span></span></pre></div>
<a id="branchessafari603branchLayoutTestsinspectorcontrollerruntimecontrollerexpectedtxt"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller-expected.txt (210337 => 210338)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller-expected.txt        2017-01-05 17:08:47 UTC (rev 210337)
+++ branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller-expected.txt        2017-01-05 17:08:50 UTC (rev 210338)
</span><span class="lines">@@ -1,4 +1,13 @@
</span><del>-Tests for the Runtime.parse command.
</del><ins>+CONSOLE MESSAGE: line 7: %o
+CONSOLE MESSAGE: line 7: %o
+CONSOLE MESSAGE: line 7: %o
+CONSOLE MESSAGE: line 9: Thrown exception
+CONSOLE MESSAGE: line 7: %o
+CONSOLE MESSAGE: line 9: Promise.reject
+CONSOLE MESSAGE: line 9: Rejection
+CONSOLE MESSAGE: line 7: %o
+CONSOLE MESSAGE: line 7: %o
+Tests for RuntimeManager operations.
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> == Running test suite: RuntimeManager
</span><span class="lines">@@ -20,3 +29,32 @@
</span><span class="cx"> Source: ;{ let a = 1; a += 1; a }
</span><span class="cx"> PASS: Evaluation should produce the labeled statement's value.
</span><span class="cx">
</span><ins>+-- Running test case: RuntimeManager.prototype.evaluateInInspectedWindow.AwaitConvenience
+
+Source: await 1
+PASS: Transformed. Should log the value or an exception.
+Source: await 2
+PASS: Transformed. Should log the value or an exception.
+Source: var x = await 3
+PASS: Transformed. Should log the value or an exception.
+Source: await causeExceptionImmediately()
+PASS: Transformed. Should log the value or an exception.
+Source: await Promise.resolve(4)
+PASS: Transformed. Should log the value or an exception.
+Source: await Promise.reject('Promise.reject')
+PASS: Transformed. Should log the value or an exception.
+Source: await rejectedEventually()
+PASS: Transformed. Should log the value or an exception.
+Source: await asyncOperation()
+PASS: Transformed. Should log the value or an exception.
+Source: x = await asyncOperation()
+PASS: Transformed. Should log the value or an exception.
+Source: return 10
+PASS: Exception. Should not get transformed and produce a SyntaxError.
+Source: await 10; 1
+PASS: Exception. Should not get transformed and produce a SyntaxError.
+Source: 1; await 10;
+PASS: Exception. Should not get transformed and produce a SyntaxError.
+Source: x = y = await 10
+PASS: Exception. Should not get transformed and produce a SyntaxError.
+
</ins></span></pre></div>
<a id="branchessafari603branchLayoutTestsinspectorcontrollerruntimecontrollerhtml"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller.html (210337 => 210338)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller.html        2017-01-05 17:08:47 UTC (rev 210337)
+++ branches/safari-603-branch/LayoutTests/inspector/controller/runtime-controller.html        2017-01-05 17:08:50 UTC (rev 210338)
</span><span class="lines">@@ -3,6 +3,27 @@
</span><span class="cx"> <head>
</span><span class="cx"> <script src="../../http/tests/inspector/resources/inspector-test.js"></script>
</span><span class="cx"> <script>
</span><ins>+let resultNumber = 100;
+function asyncOperation() {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve(resultNumber++);
+ }, 50);
+ });
+}
+
+function rejectedEventually() {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ reject("Rejection");
+ }, 50);
+ });
+}
+
+function causeExceptionImmediately() {
+ throw "Thrown exception";
+}
+
</ins><span class="cx"> function test()
</span><span class="cx"> {
</span><span class="cx"> let suite = InspectorTest.createAsyncSuite("RuntimeManager");
</span><span class="lines">@@ -10,7 +31,7 @@
</span><span class="cx"> suite.addTestCase({
</span><span class="cx"> name: "RuntimeManager.prototype.evaluateInInspectedWindow.ObjectLiteralConvenience",
</span><span class="cx"> description: "Test evaluating an object literal string conveniently converts wraps it in parenthesis to avoid misinterpretation as a program with a block and labeled statement.",
</span><del>- test: (resolve, reject) => {
</del><ins>+ test(resolve, reject) {
</ins><span class="cx"> function testSource(expression, callback) {
</span><span class="cx"> WebInspector.runtimeManager.evaluateInInspectedWindow(expression, {objectGroup: "test"}, (result, wasThrown) => {
</span><span class="cx"> InspectorTest.log("Source: " + expression);
</span><span class="lines">@@ -50,11 +71,64 @@
</span><span class="cx"> }
</span><span class="cx"> });
</span><span class="cx">
</span><ins>+ suite.addTestCase({
+ name: "RuntimeManager.prototype.evaluateInInspectedWindow.AwaitConvenience",
+ description: "Test evaluating a simple await expression wraps it into an async function and eventually resolves the value.",
+ test(resolve, reject) {
+ function testSource(expression, callback) {
+ WebInspector.runtimeManager.evaluateInInspectedWindow(expression, {objectGroup: "test"}, (result, wasThrown) => {
+ InspectorTest.log("Source: " + expression);
+ callback(result, wasThrown);
+ });
+ }
+
+ function expectUndefined(result, wasThrown) {
+ InspectorTest.expectThat(result.isUndefined(), "Transformed. Should log the value or an exception.");
+ }
+
+ function expectException(result, wasThrown) {
+ InspectorTest.expectThat(wasThrown, "Exception. Should not get transformed and produce a SyntaxError.");
+ }
+
+ // The convenience will detect these and make an async task.
+ const expected = 6;
+ testSource("await 1", expectUndefined);
+ testSource(" await 2 ", expectUndefined);
+ testSource("var x = await 3", expectUndefined);
+ testSource("await causeExceptionImmediately()", expectUndefined);
+ testSource("await Promise.resolve(4)", expectUndefined);
+ testSource("await Promise.reject('Promise.reject')", expectUndefined);
+ testSource("await rejectedEventually()", expectUndefined);
+ testSource("await asyncOperation()", expectUndefined);
+ testSource("x = await asyncOperation()", expectUndefined);
+
+ InspectorTest.log("");
+
+ // The convenience will not apply to these noexpressions.
+ testSource("return 10", expectException);
+ testSource("await 10; 1", expectException);
+ testSource("1; await 10;", expectException);
+ testSource("x = y = await 10", expectException);
+
+ // We can resolve after receiving the expected number of console.info messages.
+ let seen = 0;
+ let listener = WebInspector.logManager.addEventListener(WebInspector.LogManager.Event.MessageAdded, (event) => {
+ let consoleMessage = event.data.message;
+ if (consoleMessage.level !== WebInspector.ConsoleMessage.MessageLevel.Info)
+ return;
+ if (++seen !== expected)
+ return;
+ WebInspector.logManager.removeEventListener(WebInspector.LogManager.Event.MessageAdded, listener);
+ resolve();
+ });
+ }
+ });
+
</ins><span class="cx"> suite.runTestCasesAndFinish();
</span><span class="cx"> }
</span><span class="cx"> </script>
</span><span class="cx"> </head>
</span><span class="cx"> <body onload="runTest()">
</span><del>-<p>Tests for the Runtime.parse command.</p>
</del><ins>+<p>Tests for RuntimeManager operations.</p>
</ins><span class="cx"> </body>
</span><span class="cx"> </html>
</span></span></pre></div>
<a id="branchessafari603branchSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/WebInspectorUI/ChangeLog (210337 => 210338)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/WebInspectorUI/ChangeLog        2017-01-05 17:08:47 UTC (rev 210337)
+++ branches/safari-603-branch/Source/WebInspectorUI/ChangeLog        2017-01-05 17:08:50 UTC (rev 210338)
</span><span class="lines">@@ -1,5 +1,52 @@
</span><span class="cx"> 2017-01-05 Matthew Hanson <matthew_hanson@apple.com>
</span><span class="cx">
</span><ins>+ Merge r210033. rdar://problem/29755339
+
+ 2016-12-20 Joseph Pecoraro <pecoraro@apple.com>
+
+ Web Inspector: Console could be made useful for very simple await expressions
+ https://bugs.webkit.org/show_bug.cgi?id=165681
+ <rdar://problem/29755339>
+
+ Reviewed by Brian Burg.
+
+ Normally await expressions are only allowed inside of async functions.
+ They make dealing with async operations easy, but can't be used directly
+ in Web Inspector's console without making your own async function wrapper.
+
+ This change allows simple await expressions to be run in the console.
+ The supported syntaxes are (simple expression with optional assignment):
+
+ await <expr>
+ x = await <expr>
+ let x = await <expr>
+
+ Web Inspector's console will automatically wrap this in an async
+ function and report the resulting value or exception. For instance
+ in the last example above:
+
+ let x;
+ (async function() {
+ try {
+ x = await <expr>;
+ console.info("%o", x);
+ } catch (e) {
+ console.error(e);
+ }
+ })();
+ undefined
+
+ This way users can get the convenience of await in the Console.
+ This also gives users a nice way of extracting a value out of
+ a Promise without writing their own handlers.
+
+ * UserInterface/Controllers/RuntimeManager.js:
+ (WebInspector.RuntimeManager.prototype.evaluateInInspectedWindow):
+ (WebInspector.RuntimeManager.prototype._tryApplyAwaitConvenience):
+ Wrap simple await expressions into a function that will log the result.
+
+2017-01-05 Matthew Hanson <matthew_hanson@apple.com>
+
</ins><span class="cx"> Merge r210032. rdar://problem/29744608
</span><span class="cx">
</span><span class="cx"> 2016-12-20 Joseph Pecoraro <pecoraro@apple.com>
</span></span></pre></div>
<a id="branchessafari603branchSourceWebInspectorUIUserInterfaceControllersBreakpointLogMessageLexerjs"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/BreakpointLogMessageLexer.js (210337 => 210338)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/BreakpointLogMessageLexer.js        2017-01-05 17:08:47 UTC (rev 210337)
+++ branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/BreakpointLogMessageLexer.js        2017-01-05 17:08:50 UTC (rev 210338)
</span><span class="lines">@@ -152,7 +152,7 @@
</span><span class="cx"> _possiblePlaceholder()
</span><span class="cx"> {
</span><span class="cx"> let character = this._consume();
</span><del>- console.assert(character === "$")
</del><ins>+ console.assert(character === "$");
</ins><span class="cx"> let nextCharacter = this._peek();
</span><span class="cx">
</span><span class="cx"> console.assert(this._states.lastValue === WebInspector.BreakpointLogMessageLexer.State.PossiblePlaceholder);
</span></span></pre></div>
<a id="branchessafari603branchSourceWebInspectorUIUserInterfaceControllersRuntimeManagerjs"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js (210337 => 210338)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js        2017-01-05 17:08:47 UTC (rev 210337)
+++ branches/safari-603-branch/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js        2017-01-05 17:08:50 UTC (rev 210338)
</span><span class="lines">@@ -74,6 +74,9 @@
</span><span class="cx"> } else if (/^\s*\{/.test(expression) && /\}\s*$/.test(expression)) {
</span><span class="cx"> // Transform {a:1} to ({a:1}) so it is treated like an object literal instead of a block with a label.
</span><span class="cx"> expression = "(" + expression + ")";
</span><ins>+ } else if (/\bawait\b/.test(expression)) {
+ // Transform `await <expr>` into an async function assignment.
+ expression = this._tryApplyAwaitConvenience(expression);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> expression = sourceURLAppender(expression);
</span><span class="lines">@@ -162,6 +165,91 @@
</span><span class="cx"> if (currentContextWasDestroyed)
</span><span class="cx"> this.activeExecutionContext = WebInspector.mainTarget.executionContext;
</span><span class="cx"> }
</span><ins>+
+ _tryApplyAwaitConvenience(originalExpression)
+ {
+ let esprimaSyntaxTree;
+
+ // Do not transform if the original code parses just fine.
+ try {
+ esprima.parse(originalExpression);
+ return originalExpression;
+ } catch (error) { }
+
+ // Do not transform if the async function version does not parse.
+ try {
+ esprimaSyntaxTree = esprima.parse("(async function(){" + originalExpression + "})");
+ } catch (error) {
+ return originalExpression;
+ }
+
+ // Assert expected AST produced by our wrapping code.
+ console.assert(esprimaSyntaxTree.type === "Program");
+ console.assert(esprimaSyntaxTree.body.length === 1);
+ console.assert(esprimaSyntaxTree.body[0].type === "ExpressionStatement");
+ console.assert(esprimaSyntaxTree.body[0].expression.type === "FunctionExpression");
+ console.assert(esprimaSyntaxTree.body[0].expression.async);
+ console.assert(esprimaSyntaxTree.body[0].expression.body.type === "BlockStatement");
+
+ // Do not transform if there is more than one statement.
+ let asyncFunctionBlock = esprimaSyntaxTree.body[0].expression.body;
+ if (asyncFunctionBlock.body.length !== 1)
+ return originalExpression;
+
+ // Extract the variable name for transformation.
+ let variableName;
+ let anonymous = false;
+ let declarationKind = "var";
+ let awaitPortion;
+ let statement = asyncFunctionBlock.body[0];
+ if (statement.type === "ExpressionStatement"
+ && statement.expression.type === "AwaitExpression") {
+ // await <expr>
+ anonymous = true;
+ } else if (statement.type === "ExpressionStatement"
+ && statement.expression.type === "AssignmentExpression"
+ && statement.expression.right.type === "AwaitExpression"
+ && statement.expression.left.type === "Identifier") {
+ // x = await <expr>
+ variableName = statement.expression.left.name;
+ awaitPortion = originalExpression.substring(originalExpression.indexOf("await"));
+ } else if (statement.type === "VariableDeclaration"
+ && statement.declarations.length === 1
+ && statement.declarations[0].init.type === "AwaitExpression"
+ && statement.declarations[0].id.type === "Identifier") {
+ // var x = await <expr>
+ variableName = statement.declarations[0].id.name;
+ declarationKind = statement.kind;
+ awaitPortion = originalExpression.substring(originalExpression.indexOf("await"));
+ } else {
+ // Do not transform if this was not one of the simple supported syntaxes.
+ return originalExpression;
+ }
+
+ if (anonymous) {
+ return `
+(async function() {
+ try {
+ let result = ${originalExpression};
+ console.info("%o", result);
+ } catch (e) {
+ console.error(e);
+ }
+})();
+undefined`;
+ }
+
+ return `${declarationKind} ${variableName};
+(async function() {
+ try {
+ ${variableName} = ${awaitPortion};
+ console.info("%o", ${variableName});
+ } catch (e) {
+ console.error(e);
+ }
+})();
+undefined;`;
+ }
</ins><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> WebInspector.RuntimeManager.ConsoleObjectGroup = "console";
</span></span></pre>
</div>
</div>
</body>
</html>