From 0ae79f6f4b628451ff9674ae5446117354585dc8 Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Thu, 13 Oct 2016 10:37:18 -0700 Subject: [PATCH] Patch token stream when replace/add nodes. R=paulberry@google.com, brianwilkerson@google.com BUG= Review URL: https://codereview.chromium.org/2413293002 . --- pkg/analyzer/lib/src/dart/sdk/patch.dart | 33 +++++++- .../test/src/dart/sdk/patch_test.dart | 77 ++++++++++++++----- 2 files changed, 88 insertions(+), 22 deletions(-) diff --git a/pkg/analyzer/lib/src/dart/sdk/patch.dart b/pkg/analyzer/lib/src/dart/sdk/patch.dart index 424e521cd16..576cb96149b 100644 --- a/pkg/analyzer/lib/src/dart/sdk/patch.dart +++ b/pkg/analyzer/lib/src/dart/sdk/patch.dart @@ -132,15 +132,19 @@ class SdkPatcher { baseDeclaration.name.name == name) { if (_hasPatchAnnotation(patchDeclaration.metadata)) { // Remove the "external" keyword. - if (baseDeclaration.externalKeyword != null) { + Token externalKeyword = baseDeclaration.externalKeyword; + if (externalKeyword != null) { baseDeclaration.externalKeyword = null; + _removeToken(externalKeyword); } else { _failExternalKeyword( baseSource, name, baseDeclaration.offset); } // Replace the body. - baseDeclaration.functionExpression.body = - patchDeclaration.functionExpression.body; + FunctionExpression oldExpr = baseDeclaration.functionExpression; + FunctionBody newBody = patchDeclaration.functionExpression.body; + _replaceNodeTokens(oldExpr.body, newBody); + oldExpr.body = newBody; } } } @@ -161,7 +165,13 @@ class SdkPatcher { } } // Append new top-level declarations. - baseUnit.declarations.addAll(declarationsToAppend); + Token lastToken = baseUnit.endToken.previous; + for (CompilationUnitMember newDeclaration in declarationsToAppend) { + newDeclaration.endToken.setNext(lastToken.next); + lastToken.setNext(newDeclaration.beginToken); + baseUnit.declarations.add(newDeclaration); + lastToken = newDeclaration.endToken; + } } /** @@ -196,4 +206,19 @@ class SdkPatcher { name.name == 'patch'; }); } + + /** + * Remove the [token] from the stream. + */ + static void _removeToken(Token token) { + token.previous.setNext(token.next); + } + + /** + * Replace tokens of the [oldNode] with tokens of the [newNode]. + */ + static void _replaceNodeTokens(AstNode oldNode, AstNode newNode) { + oldNode.beginToken.previous.setNext(newNode.beginToken); + newNode.endToken.setNext(oldNode.endToken.next); + } } diff --git a/pkg/analyzer/test/src/dart/sdk/patch_test.dart b/pkg/analyzer/test/src/dart/sdk/patch_test.dart index 6c51322b005..b5ecd52f546 100644 --- a/pkg/analyzer/test/src/dart/sdk/patch_test.dart +++ b/pkg/analyzer/test/src/dart/sdk/patch_test.dart @@ -3,6 +3,7 @@ // 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/file_system/file_system.dart'; import 'package:analyzer/file_system/memory_file_system.dart'; import 'package:analyzer/src/dart/sdk/patch.dart'; @@ -86,22 +87,6 @@ final Map LIBRARIES = const { }, throwsArgumentError); } - test_topLevel_append() { - CompilationUnit unit = _doTopLevelPatching( - r''' -int bar() => 2; -''', - r''' -int _foo1() => 1; -int get _foo2 => 1; -void set _foo3(int val) {} -'''); - _assertUnitCode( - unit, - 'int bar() => 2; int _foo1() => 1; ' - 'int get _foo2 => 1; void set _foo3(int val) {}'); - } - test_topLevel_fail_topLevelVariable() { expect(() { _doTopLevelPatching( @@ -114,6 +99,26 @@ int _bar; }, throwsArgumentError); } + test_topLevel_function_append() { + CompilationUnit unit = _doTopLevelPatching( + r''' +int foo() => 0; +''', + r''' +int _bar1() => 1; +int _bar2() => 2; +'''); + _assertUnitCode( + unit, 'int foo() => 0; int _bar1() => 1; int _bar2() => 2;'); + + FunctionDeclaration foo = unit.declarations[0]; + FunctionDeclaration bar1 = unit.declarations[1]; + FunctionDeclaration bar2 = unit.declarations[2]; + + _assertPrevNextToken(foo.endToken, bar1.beginToken); + _assertPrevNextToken(bar1.endToken, bar2.beginToken); + } + test_topLevel_function_fail_noExternalKeyword() { expect(() { _doTopLevelPatching( @@ -145,9 +150,20 @@ int bar() => 2; int foo() => 0; ''', r''' -typedef int _bar(); +typedef int _bar1(); +typedef int _bar2(); '''); - _assertUnitCode(unit, 'int foo() => 0; typedef int _bar();'); + _assertUnitCode( + unit, 'int foo() => 0; typedef int _bar1(); typedef int _bar2();'); + + FunctionDeclaration foo = unit.declarations[0]; + FunctionTypeAlias bar1 = unit.declarations[1]; + FunctionTypeAlias bar2 = unit.declarations[2]; + + _assertPrevNextToken(foo.endToken, bar1.beginToken); + _assertPrevNextToken(bar1.endToken, bar2.beginToken); + expect(unit.endToken.type, TokenType.EOF); + expect(bar2.endToken.next, same(unit.endToken)); } test_topLevel_functionTypeAlias_fail_hasAnnotation() { @@ -186,6 +202,26 @@ int bar() => 2; int foo() => 1; '''); _assertUnitCode(unit, 'int foo() => 1; int bar() => 2;'); + + // Prepare functions. + FunctionDeclaration foo = unit.declarations[0]; + FunctionDeclaration bar = unit.declarations[1]; + + // The "external" token is removed from the stream. + { + expect(foo.externalKeyword, isNull); + Token token = foo.beginToken; + expect(token.lexeme, 'int'); + expect(token.previous.type, TokenType.EOF); + } + + // The body tokens are included into the patched token stream. + { + FunctionExpression fooExpr = foo.functionExpression; + FunctionBody fooBody = fooExpr.body; + expect(fooBody.beginToken.previous, same(fooExpr.parameters.endToken)); + expect(fooBody.endToken.next, same(bar.beginToken)); + } } test_topLevel_patch_function_blockBody() { @@ -259,4 +295,9 @@ final Map LIBRARIES = const { provider.newFile( _p('/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart'), code); } + + static void _assertPrevNextToken(Token prev, Token next) { + expect(prev.next, same(next)); + expect(next.previous, same(prev)); + } }