blob: ac7612ef7335da27877ea26981d48dca416a190a [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:core';
import '../render/ast.dart';
import '../types.dart';
import 'index.dart';
enum SourceLang { cpp, rust, go, unknown }
extension on SourceLang {
String toUnqualifiedName() =>
toString().substring(runtimeType.toString().length + 1);
}
class SourceLangContextMixin {
// ignore: use_setters_to_change_properties
void initSourceLangContextMixin(Report report) {
_report = report;
}
Report _report;
SourceLang get lang => _lang(this);
final _lang = Lazy<SourceLang, ProgramContext>(
(ProgramContext self) => _fuzzyDetectLang(self._report));
}
class SourceLangCompileContextMixin {
bool get isRust => _isRust(this);
final _isRust = Lazy<bool, CompileUnitContext>((CompileUnitContext self) =>
self.name.endsWith('.rs') || self.name.startsWith('[crate: '));
bool get isCFamilyFileExtension => _isCFamilyFileExtension(this);
static final _cFamilyFileExtensionRegex = RegExp(r'\.(c|cc|cpp|h|hh|hpp)$');
final _isCFamilyFileExtension = Lazy<bool, CompileUnitContext>(
(CompileUnitContext self) =>
_cFamilyFileExtensionRegex.hasMatch(self.name));
}
class SourceLangQuery extends Query
implements QueryReport, IgnorePageInHeatmapFilter {
static const String description =
'Groups binaries based on the programming language they are '
'predominantly written in.';
@override
String getDescription() => description;
@override
void addReport(Report report) {
_tallies[report.context.lang]
..size += report.fileTotal
..count += 1;
}
@override
void mergeWith(Iterable<Query> others) {
for (final other in others) {
if (other is SourceLangQuery) {
for (final entry in other._tallies.entries) {
_tallies[entry.key] += entry.value;
}
} else {
throw Exception('$other must be $runtimeType');
}
}
}
@override
QueryReport distill() => this;
final _tallies = Map<SourceLang, Tally>.fromEntries(
SourceLang.values.map((s) => MapEntry(s, Tally.zero())));
@override
String toString() => printMapSorted(_tallies);
@override
Iterable<AnyNode> export() {
final sortedBySize = _tallies.keys.toList()
..sort((a, b) => -_tallies[a].size.compareTo(_tallies[b].size));
return sortedBySize.map((k) => Node(
title: StyledString([
AddColor.white(Plain(k.toUnqualifiedName())),
Plain(': ${_tallies[k]}'),
])));
}
}
SourceLang _fuzzyDetectLang(Report report) {
const goKeyLibraries = <String>{
'net/http',
'crypto/tls',
'github.com/golang/protobuf/proto',
'netstack',
'gvisor.dev/gvisor/pkg/tcpip/transport/tcp',
'math/big',
'gvisor.dev/gvisor/pkg/tcpip/stack',
'reflect',
'vendor/golang.org/x/text/unicode/norm',
'fuchsia.googlesource.com/pmd/pkgfs',
'encoding/json',
'gvisor.dev/gvisor/pkg/state',
'syscall/zx/fidl',
'regexp/syntax',
'vendor/golang.org/x/net/dns/dnsmessage',
'crypto/x509',
'netstack/filter',
'syscall/zx/io',
'golang.org/x/net/dns/dnsmessage',
'crypto/ed25519/internal/edwards25519',
'strconv',
'regexp',
'vendor/golang.org/x/net/idna',
'encoding/asn1',
'fidl/fuchsia/posix/socket',
'fidl/fuchsia/io',
'crypto/elliptic',
'compress/flate',
'fmt',
'runtime/pprof',
'syscall/zx/fdio',
'encoding/hex',
'crypto/rand',
'gvisor.dev/gvisor/pkg/tcpip/link/loopback',
'fuchsia.googlesource.com/pmd/blobfs',
'gvisor.dev/gvisor/pkg/tcpip/network/hash',
'fidl/fuchsia/storage/metrics',
'crypto/dsa',
'crypto/rc4',
'container/heap',
'gvisor.dev/gvisor/pkg/tcpip/seqnum',
'fuchsia.googlesource.com/pmd/allowlist',
'amber/urlscope',
'hash/fnv',
'netstack/link',
'internal/testlog',
'runtime/internal/sys',
'vendor/golang.org/x/text/transform',
'fidl/fuchsia/mem',
'syscall/zx/mem',
'internal/oserror',
'sync/atomic',
'unicode/utf16',
'crypto/internal/randutil',
'netstack/util',
'fuchsia.googlesource.com/pmd/iou',
'math/bits',
'runtime/cgo',
'crypto/subtle',
'net/http/httptrace',
'vendor/golang.org/x/crypto/cryptobyte/asn1',
'runtime/internal/atomic',
'gvisor.dev/gvisor/pkg/rand',
'gvisor.dev/gvisor/pkg/tcpip/hash/jenkins',
'gvisor.dev/gvisor/pkg/linewriter',
};
var numCppUnits = 0;
var numRustUnits = 0;
var numGoUnits = 0;
for (var compileUnit in report.compileUnits) {
if (compileUnit.context.isCFamilyFileExtension) {
numCppUnits += 1;
} else if (compileUnit.context.isRust) {
numRustUnits += 1;
} else if (goKeyLibraries.contains(compileUnit.name)) {
numGoUnits += 1;
}
}
// Preferentially select rust in case multiple languages mix.
if (numRustUnits > 0 &&
numRustUnits >= numGoUnits &&
// Account for Rust binaries linking in the C++ libunwind library.
numCppUnits < numRustUnits * 2) {
return SourceLang.rust;
}
if (numGoUnits >= numCppUnits &&
numGoUnits >= numRustUnits &&
numGoUnits > 0) {
return SourceLang.go;
}
if (numCppUnits >= numRustUnits &&
numCppUnits >= numGoUnits &&
numCppUnits > 0) {
return SourceLang.cpp;
}
return SourceLang.unknown;
}