From ccfe19994b64aa2400efb7757a73596621d96926 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 6 Mar 2024 20:24:35 +0100 Subject: [PATCH] js: refactor and simplify AstWriter, properly fix case with variable in catch block --- .../javascript/rendering/AstWriter.java | 146 +++++++++--------- .../rendering/DefaultGlobalNameWriter.java | 11 +- .../javascript/rendering/NameEmitter.java | 4 +- .../javascript/rendering/RuntimeRenderer.java | 10 +- .../templating/JavaScriptTemplate.java | 18 +-- .../templating/TemplatingAstWriter.java | 51 ++---- .../javascript/rendering/AstWriterTest.java | 2 +- .../org/teavm/jso/impl/JSBodyAstEmitter.java | 16 +- 8 files changed, 111 insertions(+), 147 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java b/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java index c7734737a..3399e4022 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/AstWriter.java @@ -15,10 +15,9 @@ */ package org.teavm.backend.javascript.rendering; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -97,9 +96,9 @@ public class AstWriter { protected boolean rootScope = true; private Set aliases = new HashSet<>(); private Function globalNameWriter; - public final Map currentScopes = new HashMap<>(); - protected final Set topLevelScopes = new HashSet<>(); + private Set nonTopLevels = new HashSet<>(); private boolean inFunction; + private int scopeLevel; public AstWriter(SourceWriter writer, Function globalNameWriter) { this.writer = writer; @@ -115,13 +114,13 @@ public class AstWriter { return; } if (aliases.add(name)) { - nameMap.put(name, p -> writer.append(name)); + nameMap.put(name, (w, p) -> w.append(name)); return; } for (int i = 0;; ++i) { String alias = name + "_" + i; if (aliases.add(alias)) { - nameMap.put(name, p -> writer.append(alias)); + nameMap.put(name, (w, p) -> w.append(alias)); return; } } @@ -202,7 +201,7 @@ public class AstWriter { break; case Token.THIS: if (nameMap.containsKey("this")) { - nameMap.get("this").emit(precedence); + nameMap.get("this").emit(writer, precedence); } else { writer.append("this"); } @@ -324,7 +323,7 @@ public class AstWriter { } private void print(Scope node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append('{').softNewLine().indent(); for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { if (!print((AstNode) child)) { @@ -332,7 +331,7 @@ public class AstWriter { } } writer.outdent().append('}'); - leaveScope(scope, node); + leaveScope(scope); } private void print(LabeledStatement node) { @@ -374,17 +373,17 @@ public class AstWriter { } private void print(DoLoop node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append("do ").ws(); print(node.getBody()); writer.append("while").ws().append('('); print(node.getCondition()); writer.append(");"); - leaveScope(scope, node); + leaveScope(scope); } private void print(ForInLoop node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append("for"); if (node.isForEach()) { writer.append(" each"); @@ -395,11 +394,11 @@ public class AstWriter { print(node.getIteratedObject()); writer.append(')').ws(); print(node.getBody()); - leaveScope(scope, node); + leaveScope(scope); } private void print(ForLoop node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append("for").ws().append('('); print(node.getInitializer()); writer.append(';'); @@ -408,16 +407,16 @@ public class AstWriter { print(node.getIncrement()); writer.append(')').ws(); print(node.getBody()); - leaveScope(scope, node); + leaveScope(scope); } private void print(WhileLoop node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append("while").ws().append('('); print(node.getCondition()); writer.append(')').ws(); print(node.getBody()); - leaveScope(scope, node); + leaveScope(scope); } private void print(IfStatement node) { @@ -458,8 +457,10 @@ public class AstWriter { private void print(TryStatement node) { writer.append("try "); print(node.getTryBlock()); - for (CatchClause cc : node.getCatchClauses()) { + for (var cc : node.getCatchClauses()) { writer.ws().append("catch").ws().append('('); + var scope = enterScope(false); + includeInScope(scope, cc.getVarName().getIdentifier()); print(cc.getVarName()); if (cc.getCatchCondition() != null) { writer.append(" if "); @@ -467,6 +468,7 @@ public class AstWriter { } writer.append(')'); print(cc.getBody()); + leaveScope(scope); } if (node.getFinallyBlock() != null) { writer.ws().append("finally "); @@ -475,10 +477,9 @@ public class AstWriter { } private boolean print(VariableDeclaration node) { - if (isTopLevel() && node.getVariables().get(0).getTarget() instanceof Name) { + if (isTopLevelOutput() && node.getVariables().get(0).getTarget() instanceof Name) { var name = (Name) node.getVariables().get(0).getTarget(); - var definingScope = scopeOfId(name.getIdentifier()); - if (definingScope == null || topLevelScopes.contains(definingScope)) { + if (isTopLevelIdentifier(name.getIdentifier())) { printTopLevel(node); return true; } @@ -647,7 +648,7 @@ public class AstWriter { } private void print(ArrayComprehension node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append("["); for (ArrayComprehensionLoop loop : node.getLoops()) { writer.append("for").ws().append("("); @@ -663,11 +664,11 @@ public class AstWriter { } print(node.getResult()); writer.append(']'); - leaveScope(scope, node); + leaveScope(scope); } private void print(GeneratorExpression node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append("("); for (GeneratorExpressionLoop loop : node.getLoops()) { writer.append("for").ws().append("("); @@ -683,7 +684,7 @@ public class AstWriter { } print(node.getResult()); writer.append(')'); - leaveScope(scope, node); + leaveScope(scope); } private void print(NumberLiteral node) { @@ -697,17 +698,16 @@ public class AstWriter { } public void print(Name node, int precedence) { - var definingScope = scopeOfId(node.getIdentifier()); - if (rootScope && definingScope == null) { + if (rootScope && isTopLevelIdentifier(node.getIdentifier())) { var alias = nameMap.get(node.getIdentifier()); if (alias == null) { if (globalNameWriter != null) { alias = globalNameWriter.apply(node.getIdentifier()); } else { - alias = prec -> writer.append(node.getIdentifier()); + alias = (w, prec) -> w.append(node.getIdentifier()); } } - alias.emit(precedence); + alias.emit(writer, precedence); } else { writer.append(node.getIdentifier()); } @@ -753,18 +753,16 @@ public class AstWriter { protected boolean print(FunctionNode node) { var isArrow = node.getFunctionType() == FunctionNode.ARROW_FUNCTION; - if (isTopLevel() && !isArrow && node.getFunctionName() != null) { - var definingScope = scopeOfId(node.getFunctionName().getIdentifier()); - if (definingScope == null || topLevelScopes.contains(definingScope)) { - printTopLevel(node); - return true; - } + if (isTopLevelOutput() && !isArrow && node.getFunctionName() != null + && isTopLevelIdentifier(node.getFunctionName().getIdentifier())) { + printTopLevel(node); + return true; } var wasInFunction = inFunction; inFunction = true; - var scope = enterScope(node); + var scope = enterScope(node, true); if (!isArrow) { - currentScopes.put("arguments", node); + includeInScope(scope, "arguments"); } if (!node.isMethod() && !isArrow) { writer.append("function"); @@ -797,7 +795,7 @@ public class AstWriter { print(node.getBody()); } - leaveScope(scope, node); + leaveScope(scope); inFunction = wasInFunction; return false; } @@ -805,25 +803,25 @@ public class AstWriter { private void printTopLevel(FunctionNode node) { var wasInFunction = inFunction; inFunction = true; - var scope = enterScope(node); - currentScopes.put("arguments", node); + var scope = enterScope(node, true); + includeInScope(scope, "arguments"); writer.startFunctionDeclaration().appendFunction(node.getFunctionName().getIdentifier()); writer.append('('); printList(node.getParams()); writer.append(')').ws(); print(node.getBody()); writer.endDeclaration(); - leaveScope(scope, node); + leaveScope(scope); inFunction = wasInFunction; } private void print(LetNode node) { - var scope = enterScope(node); + var scope = enterScope(node, false); writer.append("let").ws().append('('); printList(node.getVariables().getVariables()); writer.append(')'); print(node.getBody()); - leaveScope(scope, node); + leaveScope(scope); } private void print(ParenthesizedExpression node, int precedence) { @@ -1021,47 +1019,45 @@ public class AstWriter { } } - private Map enterScope(Scope scope) { - if (scope.getSymbolTable() == null) { - return Collections.emptyMap(); - } - var map = new LinkedHashMap(); - for (var name : scope.getSymbolTable().keySet()) { - map.put(name, currentScopes.get(name)); - currentScopes.put(name, scope); - } - onEnterScope(scope); - return map; - } - - protected void onEnterScope(Scope scope) { - if (isTopLevel() && !inFunction()) { - topLevelScopes.add(scope); - } - } - - private void leaveScope(Map backup, Scope scope) { - for (var entry : backup.entrySet()) { - if (entry.getValue() == null) { - currentScopes.remove(entry.getKey()); - } else { - currentScopes.put(entry.getKey(), entry.getValue()); + private Set enterScope(Scope scope, boolean nesting) { + var set = enterScope(nesting); + if (scope.getSymbolTable() != null) { + for (var name : scope.getSymbolTable().keySet()) { + includeInScope(set, name); } } - onLeaveScope(scope); + return set; } - protected void onLeaveScope(Scope scope) { - if (isTopLevel() && !inFunction()) { - topLevelScopes.remove(scope); + public Set enterScope(boolean nesting) { + if (scopeLevel > 0 || nesting) { + ++scopeLevel; + } + return new LinkedHashSet<>(); + } + + public final void includeInScope(Set scope, String name) { + if (nonTopLevels.add(name)) { + scope.add(name); } } - protected Scope scopeOfId(String id) { - return currentScopes.get(id); + public void leaveScope(Set backup) { + nonTopLevels.removeAll(backup); + if (scopeLevel > 0) { + --scopeLevel; + } } - protected boolean isTopLevel() { + protected boolean isTopLevelIdentifier(String id) { + return !nonTopLevels.contains(id); + } + + protected boolean isInTopLevelScope() { + return scopeLevel == 0; + } + + protected boolean isTopLevelOutput() { return false; } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java b/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java index fd8478f3a..f954c0f71 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java @@ -16,20 +16,13 @@ package org.teavm.backend.javascript.rendering; import java.util.function.Function; -import org.teavm.backend.javascript.codegen.SourceWriter; public class DefaultGlobalNameWriter implements Function { - private SourceWriter writer; - - public DefaultGlobalNameWriter(SourceWriter writer) { - this.writer = writer; - } - @Override public NameEmitter apply(String s) { if (s.startsWith("$rt_") || s.startsWith("Long_") || s.equals("Long")) { - return prec -> writer.appendFunction(s); + return (w, intprec) -> w.appendFunction(s); } - return prec -> writer.appendGlobal(s); + return (w, prec) -> w.appendGlobal(s); } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/NameEmitter.java b/core/src/main/java/org/teavm/backend/javascript/rendering/NameEmitter.java index 162978cc6..832762998 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/NameEmitter.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/NameEmitter.java @@ -15,6 +15,8 @@ */ package org.teavm.backend.javascript.rendering; +import org.teavm.backend.javascript.codegen.SourceWriter; + public interface NameEmitter { - void emit(int precedence); + void emit(SourceWriter writer, int precedence); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java index 884bfb7e5..8c646ac45 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java @@ -21,7 +21,9 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Context; import org.mozilla.javascript.ast.AstRoot; @@ -42,6 +44,7 @@ public class RuntimeRenderer { private final ClassReaderSource classSource; private final SourceWriter writer; private final ClassInitializerInfo classInitializerInfo; + private final Set topLevelNames = new HashSet<>(); public RuntimeRenderer(ClassReaderSource classSource, SourceWriter writer, ClassInitializerInfo classInitializerInfo) { @@ -83,12 +86,15 @@ public class RuntimeRenderer { ast.visit(new StringConstantElimination()); new TemplatingAstTransformer(classSource).visit(ast); removablePartsFinder.visit(ast); + topLevelNames.addAll(ast.getSymbolTable().keySet()); return ast; } private void renderRuntimePart(AstRoot ast) { - var astWriter = new TemplatingAstWriter(writer, null, null, classInitializerInfo); - astWriter.hoist(ast); + var astWriter = new TemplatingAstWriter(writer, classInitializerInfo, true); + for (var name : topLevelNames) { + astWriter.declareNameEmitter(name, (w, prec) -> w.appendFunction(name)); + } astWriter.print(ast); } diff --git a/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java b/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java index 12f54f708..2be0e6687 100644 --- a/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java +++ b/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java @@ -21,6 +21,7 @@ import java.util.function.IntFunction; import org.mozilla.javascript.ast.AstNode; import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.ast.Name; +import org.teavm.backend.javascript.rendering.NameEmitter; import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.model.ClassReaderSource; @@ -65,27 +66,26 @@ public class JavaScriptTemplate { public SourceFragment build() { var intParameters = parameters; - var nameParameters = new HashMap(); + var nameParameters = new HashMap(); for (var i = 0; i < node.getParams().size(); ++i) { var param = node.getParams().get(i); if (param instanceof Name) { - nameParameters.put(((Name) param).getIdentifier(), intParameters.apply(i + 1)); + var sourceFragment = intParameters.apply(i + 1); + nameParameters.put(((Name) param).getIdentifier(), sourceFragment::write); } } var thisFragment = parameters.apply(0); var body = node.getBody(); return (writer, precedence) -> { - var astWriter = new TemplatingAstWriter(writer, nameParameters, node, null); + var astWriter = new TemplatingAstWriter(writer, null, false); + for (var entry : nameParameters.entrySet()) { + astWriter.declareNameEmitter(entry.getKey(), entry.getValue()); + } for (var entry : fragments.entrySet()) { astWriter.setFragment(entry.getKey(), entry.getValue()); } - if (node.getSymbolTable() != null) { - for (var name : node.getSymbolTable().keySet()) { - astWriter.currentScopes.put(name, node); - } - } if (thisFragment != null) { - astWriter.declareNameEmitter("this", thisPrecedence -> thisFragment.write(writer, thisPrecedence)); + astWriter.declareNameEmitter("this", thisFragment::write); } for (var child = body.getFirstChild(); child != null; child = child.getNext()) { astWriter.print((AstNode) child); diff --git a/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java b/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java index 5963fc841..be2dd7ebe 100644 --- a/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java @@ -19,10 +19,8 @@ import java.util.HashMap; import java.util.Map; import org.mozilla.javascript.ast.ElementGet; import org.mozilla.javascript.ast.FunctionCall; -import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.ast.Name; import org.mozilla.javascript.ast.PropertyGet; -import org.mozilla.javascript.ast.Scope; import org.mozilla.javascript.ast.StringLiteral; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.rendering.AstWriter; @@ -33,25 +31,14 @@ import org.teavm.model.MethodReference; import org.teavm.model.analysis.ClassInitializerInfo; public class TemplatingAstWriter extends AstWriter { - private Map names; - private Scope scope; private Map fragments = new HashMap<>(); private ClassInitializerInfo classInitializerInfo; + private boolean topLevelOutput; - public TemplatingAstWriter(SourceWriter writer, Map names, Scope scope, - ClassInitializerInfo classInitializerInfo) { - super(writer, new DefaultGlobalNameWriter(writer)); + public TemplatingAstWriter(SourceWriter writer, ClassInitializerInfo classInitializerInfo, boolean topLevelOutput) { + super(writer, new DefaultGlobalNameWriter()); this.classInitializerInfo = classInitializerInfo; - this.names = names; - this.scope = scope; - if (names != null) { - for (var name : names.keySet()) { - currentScopes.put(name, scope); - } - } - if (scope instanceof FunctionNode) { - currentScopes.put("arguments", scope); - } + this.topLevelOutput = topLevelOutput; } public void setFragment(String name, SourceFragment fragment) { @@ -62,7 +49,7 @@ public class TemplatingAstWriter extends AstWriter { protected boolean intrinsic(FunctionCall node, int precedence) { if (node.getTarget() instanceof Name) { var name = (Name) node.getTarget(); - if (scopeOfId(name.getIdentifier()) == null) { + if (isTopLevelIdentifier(name.getIdentifier())) { return tryIntrinsicName(node, name.getIdentifier()); } } @@ -164,7 +151,7 @@ public class TemplatingAstWriter extends AstWriter { var call = (FunctionCall) node.getElement(); if (call.getTarget() instanceof Name) { var name = (Name) call.getTarget(); - if (scopeOfId(name.getIdentifier()) == null) { + if (isTopLevelIdentifier(name.getIdentifier())) { switch (name.getIdentifier()) { case "teavm_javaVirtualMethod": if (writeJavaVirtualMethod(node, call)) { @@ -187,8 +174,7 @@ public class TemplatingAstWriter extends AstWriter { public void print(PropertyGet node) { if (node.getTarget() instanceof Name) { var name = (Name) node.getTarget(); - var scope = scopeOfId(name.getIdentifier()); - if (scope == null && name.getIdentifier().equals("teavm_globals")) { + if (isTopLevelIdentifier(name.getIdentifier()) && name.getIdentifier().equals("teavm_globals")) { var oldRootScope = rootScope; rootScope = false; writer.appendGlobal(node.getProperty().getIdentifier()); @@ -227,26 +213,7 @@ public class TemplatingAstWriter extends AstWriter { } @Override - public void print(Name node, int precedence) { - var definingScope = scopeOfId(node.getIdentifier()); - if (rootScope) { - if (names != null && definingScope == scope) { - var fragment = names.get(node.getIdentifier()); - if (fragment != null) { - fragment.write(writer, precedence); - return; - } - } - if (definingScope == null || topLevelScopes.contains(definingScope)) { - writer.appendFunction(node.getIdentifier()); - return; - } - } - super.print(node, precedence); - } - - @Override - protected boolean isTopLevel() { - return names == null; + public boolean isTopLevelOutput() { + return topLevelOutput; } } diff --git a/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java b/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java index 51627d2e7..f163b9b95 100644 --- a/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java +++ b/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java @@ -35,7 +35,7 @@ public class AstWriterTest { builder.setMinified(true); sourceWriter = builder.build(sb); writer = new AstWriter(sourceWriter, null); - writerWithGlobals = new AstWriter(sourceWriter, name -> prec -> sourceWriter.append("globals.").append(name)); + writerWithGlobals = new AstWriter(sourceWriter, name -> (w, prec) -> w.append("globals.").append(name)); } @Test diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java index 2d3c5e704..5b37b3a36 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyAstEmitter.java @@ -44,21 +44,21 @@ class JSBodyAstEmitter implements JSBodyEmitter { @Override public void emit(InjectorContext context) { - var astWriter = new AstWriter(context.getWriter(), new DefaultGlobalNameWriter(context.getWriter())); + var astWriter = new AstWriter(context.getWriter(), new DefaultGlobalNameWriter()); int paramIndex = 0; if (!isStatic) { int index = paramIndex++; - astWriter.declareNameEmitter("this", prec -> context.writeExpr(context.getArgument(index), + astWriter.declareNameEmitter("this", (w, prec) -> context.writeExpr(context.getArgument(index), convert(prec))); } for (int i = 0; i < parameterNames.length; ++i) { int index = paramIndex++; astWriter.declareNameEmitter(parameterNames[i], - prec -> context.writeExpr(context.getArgument(index), convert(prec))); + (w, prec) -> context.writeExpr(context.getArgument(index), convert(prec))); } for (var importInfo : imports) { astWriter.declareNameEmitter(importInfo.alias, - prec -> context.getWriter().appendFunction(context.importModule(importInfo.fromModule))); + (w, prec) -> context.getWriter().appendFunction(context.importModule(importInfo.fromModule))); } astWriter.hoist(rootAst); astWriter.print(ast, convert(context.getPrecedence())); @@ -148,19 +148,19 @@ class JSBodyAstEmitter implements JSBodyEmitter { @Override public void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { - var astWriter = new AstWriter(writer, new DefaultGlobalNameWriter(writer)); + var astWriter = new AstWriter(writer, new DefaultGlobalNameWriter()); int paramIndex = 1; if (!isStatic) { int index = paramIndex++; - astWriter.declareNameEmitter("this", prec -> writer.append(context.getParameterName(index))); + astWriter.declareNameEmitter("this", (w, prec) -> w.append(context.getParameterName(index))); } for (var parameterName : parameterNames) { int index = paramIndex++; - astWriter.declareNameEmitter(parameterName, prec -> writer.append(context.getParameterName(index))); + astWriter.declareNameEmitter(parameterName, (w, prec) -> w.append(context.getParameterName(index))); } for (var importInfo : imports) { astWriter.declareNameEmitter(importInfo.alias, - prec -> writer.appendFunction(context.importModule(importInfo.fromModule))); + (w, prec) -> w.appendFunction(context.importModule(importInfo.fromModule))); } astWriter.hoist(rootAst); if (ast instanceof Block) {