Diff
Modified: trunk/Tools/ChangeLog (222107 => 222108)
--- trunk/Tools/ChangeLog 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/ChangeLog 2017-09-15 20:36:27 UTC (rev 222108)
@@ -1,3 +1,70 @@
+2017-09-14 Filip Pizlo <[email protected]>
+
+ WSL should support ++, --, +=, and all of those things
+ https://bugs.webkit.org/show_bug.cgi?id=176975
+
+ Reviewed by Myles Maxfield.
+
+ This adds an internal AST construct called LetExpression, that allows us to anonymously capture
+ a value. This change uses LetExpression to implement prefix and suffix ++/-- and all of the
+ +=/-=/etc.
+
+ * WebGPUShadingLanguageRI/All.js:
+ * WebGPUShadingLanguageRI/Checker.js:
+ * WebGPUShadingLanguageRI/EBufferBuilder.js:
+ (EBufferBuilder.prototype.visitVariableDecl):
+ (EBufferBuilder.prototype.visitLetExpression):
+ (EBufferBuilder):
+ * WebGPUShadingLanguageRI/Evaluator.js:
+ (Evaluator.prototype.visitLetExpression):
+ * WebGPUShadingLanguageRI/LetExpression.js: Added.
+ (LetExpression):
+ (LetExpression.prototype.get origin):
+ (LetExpression.prototype.get name):
+ (LetExpression.prototype.toString):
+ * WebGPUShadingLanguageRI/Lexer.js:
+ (Lexer.prototype.next):
+ (Lexer):
+ * WebGPUShadingLanguageRI/NameResolver.js:
+ (NameResolver.prototype.visitProtocolDecl):
+ * WebGPUShadingLanguageRI/Parse.js:
+ (isCallExpression):
+ (finishParsingPostIncrement):
+ (parsePossibleSuffix):
+ (finishParsingPreIncrement):
+ (parsePreIncrement):
+ (parsePossiblePrefix):
+ (parsePossibleAssignment):
+ (parsePostIncrement):
+ (parseEffectfulExpression):
+ * WebGPUShadingLanguageRI/Rewriter.js:
+ (Rewriter.prototype.visitFunctionLikeBlock):
+ (Rewriter.prototype.visitLetExpression):
+ (Rewriter):
+ * WebGPUShadingLanguageRI/StandardLibrary.js:
+ (int.operator):
+ (uint.operator):
+ * WebGPUShadingLanguageRI/Test.html:
+ * WebGPUShadingLanguageRI/Test.js:
+ (TEST_prefixPlusPlus):
+ (TEST_prefixPlusPlusResult):
+ (TEST_postfixPlusPlus):
+ (TEST_postfixPlusPlusResult):
+ (TEST_prefixMinusMinus):
+ (TEST_prefixMinusMinusResult):
+ (TEST_postfixMinusMinus):
+ (TEST_postfixMinusMinusResult):
+ (TEST_plusEquals):
+ (TEST_plusEqualsResult):
+ (TEST_minusEquals):
+ (TEST_minusEqualsResult):
+ (TEST_timesEquals):
+ (TEST_timesEqualsResult):
+ (TEST_divideEquals):
+ (TEST_divideEqualsResult):
+ * WebGPUShadingLanguageRI/Visitor.js:
+ (Visitor.prototype.visitProtocolDecl):
+
2017-09-15 Brent Fulgham <[email protected]>
Provide mechanism to immediately end tests
Modified: trunk/Tools/WebGPUShadingLanguageRI/All.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/All.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/All.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -83,6 +83,7 @@
load("IntLiteral.js");
load("IntLiteralType.js");
load("Intrinsics.js");
+load("LetExpression.js");
load("Lexer.js");
load("LexerToken.js");
load("LiteralTypeChecker.js");
Modified: trunk/Tools/WebGPUShadingLanguageRI/Checker.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Checker.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Checker.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -177,8 +177,18 @@
return field.type;
}
+ visitLetExpression(node)
+ {
+ node.type = node.argument.visit(this);
+ if (!node.type)
+ throw new Error("Did not get type for node: " + node.argument);
+ return node.body.visit(this);
+ }
+
visitVariableRef(node)
{
+ if (!node.variable.type)
+ throw new Error("Variable has no type: " + node.variable);
return node.variable.type;
}
Modified: trunk/Tools/WebGPUShadingLanguageRI/EBufferBuilder.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/EBufferBuilder.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/EBufferBuilder.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -55,6 +55,15 @@
visitVariableDecl(node)
{
this._createEPtrForNode(node);
+ if (node.initializer)
+ node.initializer.visit(this);
}
+
+ visitLetExpression(node)
+ {
+ this._createEPtrForNode(node);
+ node.argument.visit(this);
+ node.body.visit(this);
+ }
}
Modified: trunk/Tools/WebGPUShadingLanguageRI/Evaluator.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Evaluator.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Evaluator.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -212,6 +212,12 @@
throw node;
}
+ visitLetExpression(node)
+ {
+ node.ePtr.copyFrom(node.argument.visit(this), node.type.size);
+ return node.body.visit(this);
+ }
+
visitCallExpression(node)
{
// We evaluate inlined ASTs, so this can only be a native call.
Added: trunk/Tools/WebGPUShadingLanguageRI/LetExpression.js (0 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/LetExpression.js (rev 0)
+++ trunk/Tools/WebGPUShadingLanguageRI/LetExpression.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+"use strict";
+
+let letExpressionCount = 0;
+
+class LetExpression extends Value {
+ constructor(origin)
+ {
+ super();
+ this._origin = origin;
+ this.index = letExpressionCount++;
+ this.argument = null;
+ this.body = null;
+ }
+
+ get origin() { return this._origin; }
+ get name() { return "let<" + this.index + ">"; }
+
+ toString()
+ {
+ return this.name + "(" + this.argument + ", " + this.body + ")";
+ }
+}
+
Modified: trunk/Tools/WebGPUShadingLanguageRI/Lexer.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Lexer.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Lexer.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -123,7 +123,7 @@
if (/^([0-9]*\.[0-9]+)|([0-9]+\.[0-9]*)/.test(relevantText))
return result("doubleLiteral");
- if (/^->|>=|<=|==|!=|\+=|-=|\*=|\/=|%=|^=|\|=|&=|([{}()\[\]?:=+*\/,.%!~^&|<>@;-])/.test(relevantText))
+ if (/^->|>=|<=|==|!=|\+=|-=|\*=|\/=|%=|^=|\|=|&=|\+\+|--|([{}()\[\]?:=+*\/,.%!~^&|<>@;-])/.test(relevantText))
return result("punctuation");
let remaining = relevantText.substring(0, 20).split(/\s/)[0];
Modified: trunk/Tools/WebGPUShadingLanguageRI/NameResolver.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/NameResolver.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/NameResolver.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -202,12 +202,15 @@
{
this._resolveTypeArguments(node.typeArguments);
- let type = this._nameContext.get(Type, node.name);
- if (!type)
- throw new WTypeError(node.origin.originString, "Could not find type named " + node.name);
- if (!this._nameContext.isDefined(type))
- throw new WTypeError(node.origin.originString, "Illegal forward use of type named " + node.name);
- node.type = type;
+ let type = node.type;
+ if (!type) {
+ type = this._nameContext.get(Type, node.name);
+ if (!type)
+ throw new WTypeError(node.origin.originString, "Could not find type named " + node.name);
+ if (!this._nameContext.isDefined(type))
+ throw new WTypeError(node.origin.originString, "Illegal forward use of type named " + node.name);
+ node.type = type;
+ }
if (type.typeParameters.length != node.typeArguments.length)
throw new WTypeError(node.origin.originString, "Wrong number of type arguments (passed " + node.typeArguments.length + ", expected " + type.typeParameters.length + ")");
@@ -241,6 +244,8 @@
visitVariableRef(node)
{
+ if (node.variable)
+ return;
let result = this._nameContext.get(Value, node.name);
if (!result)
throw new WTypeError(node.origin.originString, "Could not find variable named " + node.name);
@@ -263,7 +268,7 @@
else {
let type = this._nameContext.get(Type, node.name);
if (!type)
- throw new WTypeError(node.origin.originString, "Cannot find any function or type named " + node.name);
+ throw new WTypeError(node.origin.originString, "Cannot find any function or type named \"" + node.name + "\"");
node.becomeCast(type);
node.possibleOverloads = this._nameContext.get(Func, "operator cast");
if (!node.possibleOverloads)
Modified: trunk/Tools/WebGPUShadingLanguageRI/Parse.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Parse.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Parse.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -328,16 +328,54 @@
return new CallExpression(name, name.text, typeArguments, argumentList);
}
- function parsePossibleSuffix()
+ function isCallExpression()
{
- // First check if this is a call _expression_.
- let isCallExpression = lexer.testScope(() => {
+ return lexer.testScope(() => {
consumeKind("identifier");
parseTypeArguments();
consume("(");
});
+ }
+
+ function emitIncrement(token, ptr, extraArg)
+ {
+ let args = [new DereferenceExpression(token, VariableRef.wrap(ptr))];
+ if (extraArg)
+ args.push(extraArg);
- if (isCallExpression)
+ let name = "operator" + token.text;
+ if (/=$/.test(name))
+ name = RegExp.leftContext;
+
+ if (name == "operator")
+ throw new Error("Invalid name: " + name);
+
+ return new Assignment(
+ token,
+ new DereferenceExpression(token, VariableRef.wrap(ptr)),
+ new CallExpression(token, name, [], args));
+ }
+
+ function finishParsingPostIncrement(token, left)
+ {
+ let ptr = new LetExpression(token);
+ ptr.argument = new MakePtrExpression(token, left);
+
+ let oldValue = new LetExpression(token);
+ oldValue.argument = new DereferenceExpression(token, VariableRef.wrap(ptr));
+
+ ptr.body = oldValue;
+
+ oldValue.body = new CommaExpression(token, [
+ emitIncrement(token, ptr),
+ VariableRef.wrap(oldValue)
+ ]);
+ return ptr;
+ }
+
+ function parsePossibleSuffix()
+ {
+ if (isCallExpression())
return parseCallExpression();
let left = parseTerm();
@@ -346,7 +384,7 @@
switch (token.text) {
case "++":
case "--":
- left = new SuffixCallAssignment(token, "operator" + token.text, left);
+ left = finishParsingPostIncrement(token, left);
break;
case ".":
case "->":
@@ -369,11 +407,31 @@
return left;
}
+ function finishParsingPreIncrement(token, left, extraArg)
+ {
+ let ptr = new LetExpression(token);
+ ptr.argument = new MakePtrExpression(token, left);
+ ptr.body = new CommaExpression(token, [
+ emitIncrement(token, ptr, extraArg),
+ new DereferenceExpression(token, VariableRef.wrap(ptr))
+ ]);
+ return ptr;
+ }
+
+ function parsePreIncrement()
+ {
+ let token = consume("++", "--");
+ let left = parsePossiblePrefix();
+ return finishParsingPreIncrement(token, left);
+ }
+
function parsePossiblePrefix()
{
let token;
- if (token = tryConsume("++", "--", "+", "-", "~"))
- return new CallAssignment(token, "operator" + token.text, parsePossiblePrefix());
+ if (test("++", "--"))
+ return parsePreIncrement();
+ if (token = tryConsume("+", "-", "~"))
+ return new CallExpression(token, "operator" + token.text, [], [parsePossiblePrefix()]);
if (token = tryConsume("^"))
return new DereferenceExpression(token, parsePossiblePrefix());
if (token = tryConsume("&"))
@@ -471,8 +529,7 @@
}
if (operator.text == "=")
return new Assignment(operator, lhs, parsePossibleAssignment());
- let name = "operator" + operator.text.substring(0, operator.text.length - 1);
- return new CallAssignment(operator, name, lhs, parsePossibleAssignment());
+ return finishParsingPreIncrement(operator, lhs, parsePossibleAssignment());
}
function parseAssignment()
@@ -480,12 +537,24 @@
return parsePossibleAssignment("required");
}
+ function parsePostIncrement()
+ {
+ let left = parseTerm();
+ let token = consume("++", "--");
+ return finishParsingPostIncrement(token, left);
+ }
+
function parseEffectfulExpression()
{
- let assignment = lexer.backtrackingScope(parseAssignment);
- if (assignment)
- return assignment;
- return parseCallExpression();
+ if (isCallExpression())
+ return parseCallExpression();
+ let preIncrement = lexer.backtrackingScope(parsePreIncrement);
+ if (preIncrement)
+ return preIncrement;
+ let postIncrement = lexer.backtrackingScope(parsePostIncrement);
+ if (postIncrement)
+ return postIncrement;
+ return parseAssignment();
}
function genericParseCommaExpression(finalExpressionParser)
Modified: trunk/Tools/WebGPUShadingLanguageRI/Rewriter.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Rewriter.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Rewriter.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -276,6 +276,7 @@
{
return new FunctionLikeBlock(
node.origin,
+ node.returnType ? node.returnType.visit(this) : null,
node.argumentList.map(argument => argument.visit(this)),
node.parameters.map(parameter => parameter.visit(this)),
node.body.visit(this));
@@ -309,5 +310,17 @@
node.increment ? node.increment.visit(this) : undefined,
node.body.visit(this));
}
+
+ visitLetExpression(node)
+ {
+ let result = new LetExpression(node.origin);
+ result.index = node.index;
+ result.type = node.type ? node.type.visit(this) : null;
+ result.argument = node.argument.visit(this);
+ this._mapNode(node, result);
+ result.body = node.body.visit(this);
+ result.ePtr = node.ePtr;
+ return result;
+ }
}
Modified: trunk/Tools/WebGPUShadingLanguageRI/StandardLibrary.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/StandardLibrary.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/StandardLibrary.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -43,6 +43,10 @@
native int operator+(int, int);
native uint operator+(uint, uint);
+int operator++(int value) { return value + 1; }
+uint operator++(uint value) { return value + 1; }
+int operator--(int value) { return value - 1; }
+uint operator--(uint value) { return value - 1; }
native int operator-(int, int);
native uint operator-(uint, uint);
native int operator*(int, int);
Modified: trunk/Tools/WebGPUShadingLanguageRI/Test.html (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Test.html 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Test.html 2017-09-15 20:36:27 UTC (rev 222108)
@@ -60,6 +60,7 @@
<script src=""
<script src=""
<script src=""
+<script src=""
<script src=""
<script src=""
<script src=""
Modified: trunk/Tools/WebGPUShadingLanguageRI/Test.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Test.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Test.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -2091,6 +2091,190 @@
checkInt(program, callFunction(program, "thingy", [], [makeInt(program, 642)]), 642 + 743 + 91 + 39);
}
+function TEST_prefixPlusPlus()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ ++x;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
+}
+
+function TEST_prefixPlusPlusResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return ++x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
+}
+
+function TEST_postfixPlusPlus()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ x++;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 65);
+}
+
+function TEST_postfixPlusPlusResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return x++;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 64);
+}
+
+function TEST_prefixMinusMinus()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ --x;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
+}
+
+function TEST_prefixMinusMinusResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return --x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
+}
+
+function TEST_postfixMinusMinus()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ x--;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 63);
+}
+
+function TEST_postfixMinusMinusResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return x--;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 64)]), 64);
+}
+
+function TEST_plusEquals()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ x += 42;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 + 42);
+}
+
+function TEST_plusEqualsResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return x += 42;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 + 42);
+}
+
+function TEST_minusEquals()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ x -= 42;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 - 42);
+}
+
+function TEST_minusEqualsResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return x -= 42;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 - 42);
+}
+
+function TEST_timesEquals()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ x *= 42;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 * 42);
+}
+
+function TEST_timesEqualsResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return x *= 42;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), 385 * 42);
+}
+
+function TEST_divideEquals()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ x /= 42;
+ return x;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), (385 / 42) | 0);
+}
+
+function TEST_divideEqualsResult()
+{
+ let program = doPrep(`
+ int foo(int x)
+ {
+ return x /= 42;
+ }
+ `);
+ checkInt(program, callFunction(program, "foo", [], [makeInt(program, 385)]), (385 / 42) | 0);
+}
+
function TEST_twoIntLiterals()
{
let program = doPrep(`
Modified: trunk/Tools/WebGPUShadingLanguageRI/Visitor.js (222107 => 222108)
--- trunk/Tools/WebGPUShadingLanguageRI/Visitor.js 2017-09-15 20:09:16 UTC (rev 222107)
+++ trunk/Tools/WebGPUShadingLanguageRI/Visitor.js 2017-09-15 20:36:27 UTC (rev 222108)
@@ -206,8 +206,6 @@
visitVariableRef(node)
{
- if (node.variable)
- node.variable.visit(this);
}
visitIfStatement(node)
@@ -304,6 +302,8 @@
visitFunctionLikeBlock(node)
{
+ if (node.returnType)
+ node.returnType.visit(this);
for (let argument of node.argumentList)
argument.visit(this);
for (let parameter of node.parameters)
@@ -310,5 +310,11 @@
parameter.visit(this);
node.body.visit(this);
}
+
+ visitLetExpression(node)
+ {
+ node.argument.visit(this);
+ node.body.visit(this);
+ }
}