blob: d9a70fa9d23a52804546bf734dd7a2413f1f6645 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/ast_binary_flags.dart';
import 'package:analyzer/src/summary2/ast_binary_tag.dart';
import 'package:analyzer/src/summary2/bundle_writer.dart';
import 'package:analyzer/src/summary2/tokens_writer.dart';
/// Serializer of fully resolved ASTs.
class AstBinaryWriter extends ThrowingAstVisitor<void> {
final ResolutionSink _sink;
final StringIndexer _stringIndexer;
AstBinaryWriter({
required ResolutionSink sink,
required StringIndexer stringIndexer,
}) : _sink = sink,
_stringIndexer = stringIndexer;
@override
void visitAdjacentStrings(AdjacentStrings node) {
_writeByte(Tag.AdjacentStrings);
_writeNodeList(node.strings);
_storeExpression(node);
}
@override
void visitAnnotation(Annotation node) {
_writeByte(Tag.Annotation);
_writeNode(node.name);
_writeOptionalNode(node.typeArguments);
_writeOptionalNode(node.constructorName);
var arguments = node.arguments;
if (arguments != null) {
if (!arguments.arguments.every(_isSerializableExpression)) {
arguments = null;
}
}
_writeOptionalNode(arguments);
_sink.writeElement(node.element);
}
@override
void visitArgumentList(ArgumentList node) {
_writeByte(Tag.ArgumentList);
_writeNodeList(node.arguments);
}
@override
void visitAsExpression(AsExpression node) {
_writeByte(Tag.AsExpression);
_writeNode(node.expression);
_writeNode(node.type);
_storeExpression(node);
}
@override
void visitAssertInitializer(AssertInitializer node) {
_writeByte(Tag.AssertInitializer);
_writeNode(node.condition);
_writeOptionalNode(node.message);
}
@override
void visitAssignmentExpression(AssignmentExpression node) {
_writeByte(Tag.AssignmentExpression);
_writeNode(node.leftHandSide);
_writeNode(node.rightHandSide);
var operatorToken = node.operator.type;
var binaryToken = TokensWriter.astToBinaryTokenType(operatorToken);
_writeByte(binaryToken.index);
_sink.writeElement(node.staticElement);
_sink.writeElement(node.readElement);
_sink.writeType(node.readType);
_sink.writeElement(node.writeElement);
_sink.writeType(node.writeType);
_storeExpression(node);
}
@override
void visitAwaitExpression(AwaitExpression node) {
_writeByte(Tag.AwaitExpression);
_writeNode(node.expression);
_storeExpression(node);
}
@override
void visitBinaryExpression(BinaryExpression node) {
_writeByte(Tag.BinaryExpression);
_writeNode(node.leftOperand);
_writeNode(node.rightOperand);
var operatorToken = node.operator.type;
var binaryToken = TokensWriter.astToBinaryTokenType(operatorToken);
_writeByte(binaryToken.index);
_sink.writeElement(node.staticElement);
_sink.writeType(node.staticInvokeType);
_storeExpression(node);
}
@override
void visitBooleanLiteral(BooleanLiteral node) {
_writeByte(Tag.BooleanLiteral);
_writeByte(node.value ? 1 : 0);
_storeExpression(node);
}
@override
void visitCascadeExpression(CascadeExpression node) {
_writeByte(Tag.CascadeExpression);
_writeNode(node.target);
_writeNodeList(node.cascadeSections);
}
@override
void visitConditionalExpression(ConditionalExpression node) {
_writeByte(Tag.ConditionalExpression);
_writeNode(node.condition);
_writeNode(node.thenExpression);
_writeNode(node.elseExpression);
_storeExpression(node);
}
@override
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
_writeByte(Tag.ConstructorFieldInitializer);
_writeByte(
AstBinaryFlags.encode(
hasThis: node.thisKeyword != null,
),
);
_writeNode(node.fieldName);
_writeNode(node.expression);
}
@override
void visitConstructorName(ConstructorName node) {
_writeByte(Tag.ConstructorName);
// When we parse `C() = A.named` we don't know that `A` is a class name.
// We parse it as a `TypeName(PrefixedIdentifier)`.
// But when we resolve, we rewrite it.
// We need to inform the applier about the right shape of the AST.
// _sink.writeByte(node.name != null ? 1 : 0);
_writeNode(node.type);
_writeOptionalNode(node.name);
_sink.writeElement(node.staticElement);
}
@override
void visitConstructorReference(ConstructorReference node) {
_writeByte(Tag.ConstructorReference);
_writeNode(node.constructorName);
_storeExpression(node);
}
@override
void visitDeclaredIdentifier(DeclaredIdentifier node) {
_writeByte(Tag.DeclaredIdentifier);
_writeByte(
AstBinaryFlags.encode(
isConst: node.keyword?.keyword == Keyword.CONST,
isFinal: node.keyword?.keyword == Keyword.FINAL,
isVar: node.keyword?.keyword == Keyword.VAR,
),
);
_writeOptionalNode(node.type);
_writeDeclarationName(node.identifier);
_storeDeclaration(node);
}
@override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
_writeByte(Tag.DefaultFormalParameter);
_writeByte(
AstBinaryFlags.encode(
hasInitializer: node.defaultValue != null,
isPositional: node.isPositional,
isRequired: node.isRequired,
),
);
_writeNode(node.parameter);
var defaultValue = node.defaultValue;
if (!_isSerializableExpression(defaultValue)) {
defaultValue = null;
}
_writeOptionalNode(defaultValue);
}
@override
void visitDottedName(DottedName node) {
_writeByte(Tag.DottedName);
_writeNodeList(node.components);
}
@override
void visitDoubleLiteral(DoubleLiteral node) {
_writeByte(Tag.DoubleLiteral);
_writeDouble(node.value);
_storeExpression(node);
}
@override
void visitExtensionOverride(ExtensionOverride node) {
_writeByte(Tag.ExtensionOverride);
_writeNode(node.extensionName);
_writeOptionalNode(node.typeArguments);
_writeNode(node.argumentList);
_sink.writeType(node.extendedType);
// TODO(scheglov) typeArgumentTypes?
}
@override
void visitFieldFormalParameter(FieldFormalParameter node) {
_writeByte(Tag.FieldFormalParameter);
_withTypeParameters(node.typeParameters, () {
_writeOptionalNode(node.typeParameters);
_writeOptionalNode(node.type);
_writeOptionalNode(node.parameters);
_storeNormalFormalParameter(
node,
node.keyword,
hasQuestion: node.question != null,
);
});
}
@override
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
_writeByte(Tag.ForEachPartsWithDeclaration);
_writeNode(node.loopVariable);
_storeForEachParts(node);
}
@override
void visitForElement(ForElement node) {
_writeNotSerializableExpression();
}
@override
void visitFormalParameterList(FormalParameterList node) {
_writeByte(Tag.FormalParameterList);
var leftDelimiter = node.leftDelimiter?.type;
_writeByte(
AstBinaryFlags.encode(
isDelimiterCurly: leftDelimiter == TokenType.OPEN_CURLY_BRACKET,
isDelimiterSquare: leftDelimiter == TokenType.OPEN_SQUARE_BRACKET,
),
);
_writeNodeList(node.parameters);
}
@override
void visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
_writeByte(Tag.ForPartsWithDeclarations);
_writeNode(node.variables);
_storeForParts(node);
}
@override
void visitForPartsWithExpression(ForPartsWithExpression node) {
_writeByte(Tag.ForPartsWithExpression);
_writeOptionalNode(node.initialization);
_storeForParts(node);
}
@override
void visitFunctionExpression(FunctionExpression node) {
_writeByte(Tag.FunctionExpressionStub);
}
@override
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
_writeByte(Tag.FunctionExpressionInvocation);
_writeNode(node.function);
_storeInvocationExpression(node);
}
@override
void visitFunctionReference(FunctionReference node) {
_writeByte(Tag.FunctionReference);
_writeNode(node.function);
_writeOptionalNode(node.typeArguments);
_sink.writeOptionalTypeList(node.typeArgumentTypes);
_storeExpression(node);
}
@override
void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
_writeByte(Tag.FunctionTypedFormalParameter);
_withTypeParameters(node.typeParameters, () {
_writeOptionalNode(node.typeParameters);
_writeOptionalNode(node.returnType);
_writeNode(node.parameters);
_storeNormalFormalParameter(node, null);
});
}
@override
void visitGenericFunctionType(GenericFunctionType node) {
_writeByte(Tag.GenericFunctionType);
_writeByte(
AstBinaryFlags.encode(
hasQuestion: node.question != null,
),
);
_withTypeParameters(node.typeParameters, () {
_writeOptionalNode(node.typeParameters);
_writeOptionalNode(node.returnType);
_writeNode(node.parameters);
_sink.writeType(node.type);
});
}
@override
void visitIfElement(IfElement node) {
_writeByte(Tag.IfElement);
_writeNode(node.condition);
_writeNode(node.thenElement);
_writeOptionalNode(node.elseElement);
}
@override
void visitImplicitCallReference(ImplicitCallReference node) {
_writeByte(Tag.ImplicitCallReference);
_writeNode(node.expression);
_writeOptionalNode(node.typeArguments);
_sink.writeOptionalTypeList(node.typeArgumentTypes);
_sink.writeElement(node.staticElement);
_storeExpression(node);
}
@override
void visitIndexExpression(IndexExpression node) {
_writeByte(Tag.IndexExpression);
_writeByte(
AstBinaryFlags.encode(
hasPeriod: node.period != null,
hasQuestion: node.question != null,
),
);
_writeOptionalNode(node.target);
_writeNode(node.index);
_sink.writeElement(node.staticElement);
_storeExpression(node);
}
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
_writeByte(Tag.InstanceCreationExpression);
_writeByte(
AstBinaryFlags.encode(
isConst: node.keyword?.type == Keyword.CONST,
isNew: node.keyword?.type == Keyword.NEW,
),
);
_writeNode(node.constructorName);
_writeNode(node.argumentList);
_storeExpression(node);
}
@override
void visitIntegerLiteral(IntegerLiteral node) {
var value = node.value;
if (value == null) {
_writeByte(Tag.IntegerLiteralNull);
_writeStringReference(node.literal.lexeme);
} else {
var isPositive = value >= 0;
if (!isPositive) {
value = -value;
}
if (value & 0xFF == value) {
_writeByte(
isPositive
? Tag.IntegerLiteralPositive1
: Tag.IntegerLiteralNegative1,
);
_writeStringReference(node.literal.lexeme);
_writeByte(value);
} else {
_writeByte(
isPositive ? Tag.IntegerLiteralPositive : Tag.IntegerLiteralNegative,
);
_writeStringReference(node.literal.lexeme);
_writeUInt32(value >> 32);
_writeUInt32(value & 0xFFFFFFFF);
}
}
// TODO(scheglov) Dont write type, AKA separate true `int` and `double`?
_storeExpression(node);
}
@override
void visitInterpolationExpression(InterpolationExpression node) {
_writeByte(Tag.InterpolationExpression);
_writeByte(
AstBinaryFlags.encode(
isStringInterpolationIdentifier:
node.leftBracket.type == TokenType.STRING_INTERPOLATION_IDENTIFIER,
),
);
_writeNode(node.expression);
}
@override
void visitInterpolationString(InterpolationString node) {
_writeByte(Tag.InterpolationString);
_writeStringReference(node.contents.lexeme);
_writeStringReference(node.value);
}
@override
void visitIsExpression(IsExpression node) {
_writeByte(Tag.IsExpression);
_writeByte(
AstBinaryFlags.encode(
hasNot: node.notOperator != null,
),
);
_writeNode(node.expression);
_writeNode(node.type);
_storeExpression(node);
}
@override
void visitListLiteral(ListLiteral node) {
_writeByte(Tag.ListLiteral);
_writeByte(
AstBinaryFlags.encode(
isConst: node.constKeyword != null,
),
);
_writeOptionalNode(node.typeArguments);
_writeNodeList(node.elements);
_storeExpression(node);
}
@override
void visitMapLiteralEntry(MapLiteralEntry node) {
_writeByte(Tag.MapLiteralEntry);
_writeNode(node.key);
_writeNode(node.value);
}
@override
void visitMethodInvocation(MethodInvocation node) {
_writeByte(Tag.MethodInvocation);
var operatorType = node.operator?.type;
_writeByte(
AstBinaryFlags.encode(
hasPeriod: operatorType == TokenType.PERIOD ||
operatorType == TokenType.QUESTION_PERIOD,
hasPeriod2: operatorType == TokenType.PERIOD_PERIOD ||
operatorType == TokenType.QUESTION_PERIOD_PERIOD,
hasQuestion: operatorType == TokenType.QUESTION_PERIOD ||
operatorType == TokenType.QUESTION_PERIOD_PERIOD,
),
);
_writeOptionalNode(node.target);
_writeNode(node.methodName);
_storeInvocationExpression(node);
}
@override
void visitNamedExpression(NamedExpression node) {
_writeByte(Tag.NamedExpression);
var nameNode = node.name.label;
_writeStringReference(nameNode.name);
_writeNode(node.expression);
}
@override
void visitNamedType(NamedType node) {
_writeByte(Tag.NamedType);
_writeByte(
AstBinaryFlags.encode(
hasQuestion: node.question != null,
hasTypeArguments: node.typeArguments != null,
),
);
_writeNode(node.name);
_writeOptionalNode(node.typeArguments);
_sink.writeType(node.type);
}
@override
void visitNullLiteral(NullLiteral node) {
_writeByte(Tag.NullLiteral);
_storeExpression(node);
}
@override
void visitParenthesizedExpression(ParenthesizedExpression node) {
_writeByte(Tag.ParenthesizedExpression);
_writeNode(node.expression);
_storeExpression(node);
}
@override
void visitPostfixExpression(PostfixExpression node) {
_writeByte(Tag.PostfixExpression);
_writeNode(node.operand);
var operatorToken = node.operator.type;
var binaryToken = TokensWriter.astToBinaryTokenType(operatorToken);
_writeByte(binaryToken.index);
_sink.writeElement(node.staticElement);
if (operatorToken.isIncrementOperator) {
_sink.writeElement(node.readElement);
_sink.writeType(node.readType);
_sink.writeElement(node.writeElement);
_sink.writeType(node.writeType);
}
_storeExpression(node);
}
@override
void visitPrefixedIdentifier(PrefixedIdentifier node) {
_writeByte(Tag.PrefixedIdentifier);
_writeNode(node.prefix);
_writeNode(node.identifier);
// TODO(scheglov) In actual prefixed identifier, the type of the identifier.
_storeExpression(node);
}
@override
void visitPrefixExpression(PrefixExpression node) {
_writeByte(Tag.PrefixExpression);
var operatorToken = node.operator.type;
var binaryToken = TokensWriter.astToBinaryTokenType(operatorToken);
_writeByte(binaryToken.index);
_writeNode(node.operand);
_sink.writeElement(node.staticElement);
if (operatorToken.isIncrementOperator) {
_sink.writeElement(node.readElement);
_sink.writeType(node.readType);
_sink.writeElement(node.writeElement);
_sink.writeType(node.writeType);
}
_storeExpression(node);
}
@override
void visitPropertyAccess(PropertyAccess node) {
_writeByte(Tag.PropertyAccess);
var operatorType = node.operator.type;
_writeByte(
AstBinaryFlags.encode(
hasPeriod: operatorType == TokenType.PERIOD ||
operatorType == TokenType.QUESTION_PERIOD,
hasPeriod2: operatorType == TokenType.PERIOD_PERIOD ||
operatorType == TokenType.QUESTION_PERIOD_PERIOD,
hasQuestion: operatorType == TokenType.QUESTION_PERIOD ||
operatorType == TokenType.QUESTION_PERIOD_PERIOD,
),
);
_writeOptionalNode(node.target);
_writeNode(node.propertyName);
// TODO(scheglov) Get from the property?
_storeExpression(node);
}
@override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
_writeByte(Tag.RedirectingConstructorInvocation);
_writeOptionalNode(node.constructorName);
_writeNode(node.argumentList);
_sink.writeElement(node.staticElement);
}
@override
void visitSetOrMapLiteral(SetOrMapLiteral node) {
_writeByte(Tag.SetOrMapLiteral);
_writeByte(
AstBinaryFlags.encode(
isConst: node.constKeyword != null,
),
);
var isMapBit = node.isMap ? (1 << 0) : 0;
var isSetBit = node.isSet ? (1 << 1) : 0;
_sink.writeByte(isMapBit | isSetBit);
_writeOptionalNode(node.typeArguments);
_writeNodeList(node.elements);
_storeExpression(node);
}
@override
void visitSimpleFormalParameter(SimpleFormalParameter node) {
_writeByte(Tag.SimpleFormalParameter);
_writeOptionalNode(node.type);
_storeNormalFormalParameter(node, node.keyword);
var element = node.declaredElement as ParameterElementImpl;
_sink.writeByte(element.inheritsCovariant ? 1 : 0);
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
_writeByte(Tag.SimpleIdentifier);
_writeStringReference(node.name);
_sink.writeElement(node.staticElement);
_sink.writeOptionalTypeList(node.tearOffTypeArgumentTypes);
_storeExpression(node);
}
@override
void visitSimpleStringLiteral(SimpleStringLiteral node) {
_writeByte(Tag.SimpleStringLiteral);
_writeStringReference(node.literal.lexeme);
_writeStringReference(node.value);
_storeExpression(node);
}
@override
void visitSpreadElement(SpreadElement node) {
_writeByte(Tag.SpreadElement);
_writeByte(
AstBinaryFlags.encode(
hasQuestion:
node.spreadOperator.type == TokenType.PERIOD_PERIOD_PERIOD_QUESTION,
),
);
_writeNode(node.expression);
}
@override
void visitStringInterpolation(StringInterpolation node) {
_writeByte(Tag.StringInterpolation);
_writeNodeList(node.elements);
_storeExpression(node);
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
_writeByte(Tag.SuperConstructorInvocation);
_writeOptionalNode(node.constructorName);
_writeNode(node.argumentList);
_sink.writeElement(node.staticElement);
}
@override
void visitSuperExpression(SuperExpression node) {
_writeByte(Tag.SuperExpression);
_storeExpression(node);
}
@override
void visitSymbolLiteral(SymbolLiteral node) {
_writeByte(Tag.SymbolLiteral);
var components = node.components;
_writeUInt30(components.length);
for (var token in components) {
_writeStringReference(token.lexeme);
}
_storeExpression(node);
}
@override
void visitThisExpression(ThisExpression node) {
_writeByte(Tag.ThisExpression);
_storeExpression(node);
}
@override
void visitThrowExpression(ThrowExpression node) {
_writeByte(Tag.ThrowExpression);
_writeNode(node.expression);
_storeExpression(node);
}
@override
void visitTypeArgumentList(TypeArgumentList node) {
_writeByte(Tag.TypeArgumentList);
_writeNodeList(node.arguments);
}
@override
void visitTypeLiteral(TypeLiteral node) {
_writeByte(Tag.TypeLiteral);
_writeNode(node.type);
_storeExpression(node);
}
@override
void visitTypeParameter(TypeParameter node) {
_writeByte(Tag.TypeParameter);
_writeDeclarationName(node.name);
_writeOptionalNode(node.bound);
_storeDeclaration(node);
}
@override
void visitTypeParameterList(TypeParameterList node) {
_writeByte(Tag.TypeParameterList);
_writeNodeList(node.typeParameters);
}
@override
void visitVariableDeclarationList(VariableDeclarationList node) {
_writeByte(Tag.VariableDeclarationList);
_writeByte(
AstBinaryFlags.encode(
isConst: node.isConst,
isFinal: node.isFinal,
isLate: node.lateKeyword != null,
isVar: node.keyword?.keyword == Keyword.VAR,
),
);
_writeOptionalNode(node.type);
_writeNodeList(node.variables);
_storeAnnotatedNode(node);
}
void _storeAnnotatedNode(AnnotatedNode node) {
_writeNodeList(node.metadata);
}
void _storeDeclaration(Declaration node) {
_storeAnnotatedNode(node);
}
void _storeExpression(Expression node) {
_sink.writeType(node.staticType);
}
void _storeForEachParts(ForEachParts node) {
_writeNode(node.iterable);
_storeForLoopParts(node);
}
void _storeForLoopParts(ForLoopParts node) {}
void _storeFormalParameter(FormalParameter node) {
var element = node.declaredElement as ParameterElementImpl;
_writeActualType(_sink, element.type);
}
void _storeForParts(ForParts node) {
_writeOptionalNode(node.condition);
_writeNodeList(node.updaters);
_storeForLoopParts(node);
}
void _storeInvocationExpression(InvocationExpression node) {
_writeOptionalNode(node.typeArguments);
_writeNode(node.argumentList);
_sink.writeType(node.staticInvokeType);
_sink.writeOptionalTypeList(node.typeArgumentTypes);
_storeExpression(node);
}
void _storeNormalFormalParameter(
NormalFormalParameter node,
Token? keyword, {
bool hasQuestion = false,
}) {
_writeByte(
AstBinaryFlags.encode(
hasName: node.identifier != null,
hasQuestion: hasQuestion,
isConst: keyword?.type == Keyword.CONST,
isCovariant: node.covariantKeyword != null,
isFinal: keyword?.type == Keyword.FINAL,
isRequired: node.requiredKeyword != null,
isVar: keyword?.type == Keyword.VAR,
),
);
_writeNodeList(node.metadata);
if (node.identifier != null) {
_writeDeclarationName(node.identifier!);
}
_storeFormalParameter(node);
}
void _withTypeParameters(TypeParameterList? node, void Function() f) {
if (node == null) {
f();
} else {
var elements = node.typeParameters
.map((typeParameter) => typeParameter.declaredElement!)
.toList();
_sink.localElements.withElements(elements, () {
f();
});
}
}
void _writeActualType(ResolutionSink resolutionSink, DartType type) {
resolutionSink.writeType(type);
}
void _writeByte(int byte) {
assert((byte & 0xFF) == byte);
_sink.addByte(byte);
}
void _writeDeclarationName(SimpleIdentifier node) {
_writeStringReference(node.name);
}
_writeDouble(double value) {
_sink.addDouble(value);
}
void _writeNode(AstNode node) {
node.accept(this);
}
void _writeNodeList(List<AstNode> nodeList) {
_writeUInt30(nodeList.length);
for (var i = 0; i < nodeList.length; ++i) {
nodeList[i].accept(this);
}
}
void _writeNotSerializableExpression() {
var node = astFactory.simpleIdentifier(
StringToken(TokenType.STRING, '_notSerializableExpression', -1),
);
node.accept(this);
}
void _writeOptionalNode(AstNode? node) {
if (node == null) {
_writeByte(Tag.Nothing);
} else {
_writeByte(Tag.Something);
_writeNode(node);
}
}
void _writeStringReference(String string) {
var index = _stringIndexer[string];
_writeUInt30(index);
}
@pragma("vm:prefer-inline")
void _writeUInt30(int value) {
_sink.writeUInt30(value);
}
void _writeUInt32(int value) {
_sink.addByte4((value >> 24) & 0xFF, (value >> 16) & 0xFF,
(value >> 8) & 0xFF, value & 0xFF);
}
/// Return `true` if the expression might be successfully serialized.
///
/// This does not mean that the expression is constant, it just means that
/// we know that it might be serialized and deserialized. For example
/// function expressions are problematic, and are not necessary to
/// deserialize, so we choose not to do this.
static bool _isSerializableExpression(Expression? node) {
if (node == null) return false;
var visitor = _IsSerializableExpressionVisitor();
node.accept(visitor);
return visitor.result;
}
}
class _IsSerializableExpressionVisitor extends RecursiveAstVisitor<void> {
bool result = true;
@override
void visitFunctionExpression(FunctionExpression node) {
result = false;
}
}