blob: 8d38da3619d9bc733f1b233ad00ac03da8d2cc18 [file] [log] [blame]
// Copyright (c) 2017, 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 'dart:typed_data';
import 'package:front_end/src/base/api_signature.dart';
import 'package:front_end/src/fasta/parser.dart'
show Listener, Parser, optional;
import 'package:front_end/src/fasta/parser/top_level_parser.dart';
import 'package:front_end/src/fasta/scanner.dart';
import 'package:front_end/src/fasta/scanner/token_constants.dart'
import 'package:front_end/src/fasta/source/directive_listener.dart';
import 'package:front_end/src/incremental/format.dart';
/// Compute the [UnlinkedUnitBuilder] for the [content].
UnlinkedUnitBuilder computeUnlinkedUnit(List<int> salt, List<int> content) {
// Scan the content.
ScannerResult scanResult = _scan(content);
Token token = scanResult.tokens;
// Parse directives.
var listener = new DirectiveListener();
new TopLevelParser(listener).parseUnit(token);
// Parse to record function bodies.
var parser = new _BodySkippingParser();
ApiSignature apiSignature = new ApiSignature();
// Iterate over tokens and skip bodies.
Iterator<_BodyRange> bodyIterator = parser.bodyRanges.iterator;
for (; token.kind != EOF_TOKEN; token = {
// Move to the body range that ends after the token.
while (bodyIterator.current != null &&
bodyIterator.current.last < token.charOffset) {
// If the current body range starts before or at the token, skip it.
if (bodyIterator.current != null &&
bodyIterator.current.first <= token.charOffset) {
// The token is outside of a function body, add it.
if (token is! ErrorToken) {
return new UnlinkedUnitBuilder(
apiSignature: apiSignature.toByteList(),
hasMixinApplication: parser.hasMixin);
/// Exclude all `native 'xyz';` token sequences.
void _excludeNativeClauses(Token token) {
while (token.kind != EOF_TOKEN) {
Token next =;
if (optional('native', next) && == STRING_TOKEN &&
optional(';', {
next =;
token = next;
/// Scan the content of the file.
ScannerResult _scan(List<int> content) {
var zeroTerminatedBytes = new Uint8List(content.length + 1);
zeroTerminatedBytes.setRange(0, content.length, content);
ScannerResult result = scan(zeroTerminatedBytes);
return result;
/// Convert [NamespaceCombinator] into [UnlinkedCombinatorBuilder].
UnlinkedCombinatorBuilder _toUnlinkedCombinator(NamespaceCombinator c) =>
new UnlinkedCombinatorBuilder(isShow: c.isShow, names: c.names.toList());
/// Convert [NamespaceDirective] into [UnlinkedNamespaceDirectiveBuilder].
UnlinkedNamespaceDirectiveBuilder _toUnlinkedNamespaceDirective(
NamespaceDirective directive) =>
new UnlinkedNamespaceDirectiveBuilder(
uri: directive.uri,
/// The char range of a function body.
class _BodyRange {
/// The char offset of the first token in the range.
final int first;
/// The char offset of the last token in the range.
final int last;
_BodyRange(this.first, this.last);
String toString() => '[$first, $last]';
/// The [Parser] that skips function bodies and remembers their token ranges.
class _BodySkippingParser extends Parser {
bool hasMixin = false;
final List<_BodyRange> bodyRanges = [];
_BodySkippingParser() : super(new Listener());
Token parseFunctionBody(
Token token, bool ofFunctionExpression, bool allowAbstract) {
Token next =;
if (identical('{', next.lexeme)) {
Token close = skipBlock(token);
bodyRanges.add(new _BodyRange(next.charOffset, close.charOffset));
return close;
return super.parseFunctionBody(token, ofFunctionExpression, allowAbstract);
Token parseInvalidBlock(Token token) => skipBlock(token);
Token parseMixinApplicationRest(Token token) {
hasMixin = true;
return super.parseMixinApplicationRest(token);