| // 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; |
| } |