| // Copyright (c) 2015, 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/standard_ast_factory.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart' show GeneralizingAstVisitor; |
| import 'package:analyzer/dart/element/type.dart' show DartType; |
| import 'package:analyzer/src/dart/ast/ast.dart' |
| show |
| FunctionBodyImpl, |
| FunctionExpressionInvocationImpl, |
| MethodInvocationImpl; |
| import 'package:analyzer/src/dart/ast/utilities.dart' |
| show AstCloner, NodeReplacer; |
| import 'package:analyzer/src/generated/parser.dart' show ResolutionCopier; |
| import 'package:analyzer/src/task/strong/ast_properties.dart' as ast_properties; |
| |
| import 'ast_builder.dart'; |
| import 'element_helpers.dart' show isInlineJS; |
| |
| // This class implements a pass which modifies (in place) the ast replacing |
| // abstract coercion nodes with their dart implementations. |
| class CoercionReifier extends GeneralizingAstVisitor<void> { |
| final cloner = _TreeCloner(); |
| |
| CoercionReifier._(); |
| |
| /// Transforms the given compilation units, and returns a new AST with |
| /// explicit coercion nodes in appropriate places. |
| static List<CompilationUnit> reify(List<CompilationUnit> units) { |
| var cr = CoercionReifier._(); |
| return units.map(cr.visitCompilationUnit).toList(growable: false); |
| } |
| |
| /// True if the `as` [node] is a required runtime check for soundness. |
| // TODO(sra): Find a better way to recognize reified coercion, since we |
| // can't set the isSynthetic attribute. |
| static bool isImplicit(AsExpression node) => node.asOperator.offset == 0; |
| |
| /// Creates an implicit cast for expression [e] to [toType]. |
| static Expression castExpression(Expression e, DartType toType) { |
| // We use an empty name in the AST, because the JS code generator only cares |
| // about the target type. It does not look at the AST name. |
| var typeName = astFactory.typeName(ast.identifierFromString(''), null); |
| typeName.type = toType; |
| var cast = ast.asExpression(e, typeName); |
| cast.staticType = toType; |
| return cast; |
| } |
| |
| @override |
| CompilationUnit visitCompilationUnit(CompilationUnit node) { |
| if (ast_properties.hasImplicitCasts(node)) { |
| // Clone compilation unit, so we don't modify the originals. |
| node = _clone(node); |
| super.visitCompilationUnit(node); |
| } |
| return node; |
| } |
| |
| @override |
| visitExpression(Expression node) { |
| node.visitChildren(this); |
| |
| var castType = ast_properties.getImplicitCast(node); |
| if (castType != null) { |
| _replaceNode(node.parent, node, castExpression(node, castType)); |
| } |
| } |
| |
| @override |
| visitSpreadElement(SpreadElement node) { |
| // Skip visiting the expression so we can handle all casts during code |
| // generation. |
| node.expression.visitChildren(this); |
| } |
| |
| @override |
| visitMethodInvocation(MethodInvocation node) { |
| if (isInlineJS(node.methodName.staticElement)) { |
| // Don't cast our inline-JS code in SDK. |
| ast_properties.setImplicitCast(node, null); |
| } |
| visitExpression(node); |
| } |
| |
| @override |
| visitParenthesizedExpression(ParenthesizedExpression node) { |
| super.visitParenthesizedExpression(node); |
| node.staticType = node.expression.staticType; |
| } |
| |
| @override |
| void visitForStatement(ForStatement node) { |
| var forLoopParts = node.forLoopParts; |
| if (forLoopParts is ForEachParts) { |
| // Visit other children. |
| forLoopParts.iterable.accept(this); |
| node.body.accept(this); |
| } else { |
| super.visitForStatement(node); |
| } |
| } |
| |
| @override |
| void visitForElement(ForElement node) { |
| var forLoopParts = node.forLoopParts; |
| if (forLoopParts is ForEachParts) { |
| // Visit other children. |
| forLoopParts.iterable.accept(this); |
| node.body.accept(this); |
| } else { |
| super.visitForElement(node); |
| } |
| } |
| |
| void _replaceNode(AstNode parent, AstNode oldNode, AstNode newNode) { |
| if (!identical(oldNode, newNode)) { |
| var replaced = parent.accept(NodeReplacer(oldNode, newNode)); |
| // It looks like NodeReplacer will always return true. |
| // It does throw IllegalArgumentException though, if child is not found. |
| assert(replaced); |
| } |
| } |
| |
| T _clone<T extends AstNode>(T node) { |
| var copy = node.accept(cloner) as T; |
| ResolutionCopier.copyResolutionData(node, copy); |
| return copy; |
| } |
| } |
| |
| class _TreeCloner extends AstCloner { |
| void _cloneProperties(AstNode clone, AstNode node) { |
| if (clone is Expression && node is Expression) { |
| ast_properties.setImplicitCast( |
| clone, ast_properties.getImplicitCast(node)); |
| ast_properties.setImplicitOperationCast( |
| clone, ast_properties.getImplicitOperationCast(node)); |
| ast_properties.setImplicitSpreadCast( |
| clone, ast_properties.getImplicitSpreadCast(node)); |
| ast_properties.setImplicitSpreadKeyCast( |
| clone, ast_properties.getImplicitSpreadKeyCast(node)); |
| ast_properties.setImplicitSpreadValueCast( |
| clone, ast_properties.getImplicitSpreadValueCast(node)); |
| ast_properties.setIsDynamicInvoke( |
| clone, ast_properties.isDynamicInvoke(node)); |
| } |
| if (clone is Declaration && node is Declaration) { |
| ast_properties.setClassCovariantParameters( |
| clone, ast_properties.getClassCovariantParameters(node)); |
| ast_properties.setSuperclassCovariantParameters( |
| clone, ast_properties.getSuperclassCovariantParameters(node)); |
| } |
| } |
| |
| @override |
| E cloneNode<E extends AstNode>(E node) { |
| var clone = super.cloneNode(node); |
| _cloneProperties(clone, node); |
| return clone; |
| } |
| |
| @override |
| List<E> cloneNodeList<E extends AstNode>(List<E> list) { |
| var clone = super.cloneNodeList(list); |
| for (int i = 0, len = list.length; i < len; i++) { |
| _cloneProperties(clone[i], list[i]); |
| } |
| return clone; |
| } |
| |
| // TODO(jmesserly): ResolutionCopier is not copying this yet. |
| @override |
| BlockFunctionBody visitBlockFunctionBody(BlockFunctionBody node) { |
| var clone = super.visitBlockFunctionBody(node); |
| (clone as FunctionBodyImpl).localVariableInfo = |
| (node as FunctionBodyImpl).localVariableInfo; |
| return clone; |
| } |
| |
| @override |
| ExpressionFunctionBody visitExpressionFunctionBody( |
| ExpressionFunctionBody node) { |
| var clone = super.visitExpressionFunctionBody(node); |
| (clone as FunctionBodyImpl).localVariableInfo = |
| (node as FunctionBodyImpl).localVariableInfo; |
| return clone; |
| } |
| |
| @override |
| FunctionExpressionInvocation visitFunctionExpressionInvocation( |
| FunctionExpressionInvocation node) { |
| var clone = super.visitFunctionExpressionInvocation(node); |
| (clone as FunctionExpressionInvocationImpl).typeArgumentTypes = |
| node.typeArgumentTypes; |
| return clone; |
| } |
| |
| @override |
| MethodInvocation visitMethodInvocation(MethodInvocation node) { |
| var clone = super.visitMethodInvocation(node); |
| (clone as MethodInvocationImpl).typeArgumentTypes = node.typeArgumentTypes; |
| return clone; |
| } |
| |
| // TODO(jmesserly): workaround for |
| // https://github.com/dart-lang/sdk/issues/26368 |
| @override |
| TypeName visitTypeName(TypeName node) { |
| var clone = super.visitTypeName(node); |
| clone.type = node.type; |
| return clone; |
| } |
| } |