Version 3.7.0-0.0.dev
Merge 6d2200b7a65f034719335e23809e375b25ee9e6f into dev
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c762e33..65437ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 3.7.0
+
## 3.6.0
### Language
diff --git a/DEPS b/DEPS
index edbdcbc..8c4bdd5 100644
--- a/DEPS
+++ b/DEPS
@@ -120,8 +120,8 @@
# EOL comment after a dependency to disable this and pin it at its current
# revision.
- "args_rev": "e623652744c82533829f2e62b1aba1a6cf06e291",
- "async_rev": "c0d81f8699682d01d657a9bf827107d11904a247",
+ "args_rev": "e623652744c82533829f2e62b1aba1a6cf06e291", #
+ "async_rev": "5f70a996f673d625e3502597084653686c3e754c",
"bazel_worker_rev": "aa3cc9e826350b960e0c5a67e6065bcedba8b0ac",
"benchmark_harness_rev": "44f125ae1d045aa3de09fe88a8dd70cb7352d563",
"boolean_selector_rev": "d6c7c36ae1111f11cc24306d71d3ab2deea8fa68",
@@ -129,9 +129,9 @@
"characters_rev": "7633a16a22c626e19ca750223237396315268a06",
"cli_util_rev": "c36b3941e38092d6d6f87ac27d9e88f153d3ac38",
"clock_rev": "7956d60042f4ea979c4554d43eeb57d087627869",
- "collection_rev": "24b75d85df6a26aac7be13b56ff1ce4360c04a64",
- "convert_rev": "9035cafefc1da4315f26058734d0c2a19d5ab56a",
- "crypto_rev": "eede7d6918c51159c1422b7449f40dbac660ee57",
+ "collection_rev": "887b826b50f48d6a9cd2c0684aa353e8e3a0fad0",
+ "convert_rev": "d361833e117cb2438d2a2a6d0b0acb28ff0910fb",
+ "crypto_rev": "3d26ef4cf22d4b218ba30e616544ad3cf52f64a1",
"csslib_rev": "a3700b05bbcc42782e8a7024790dbf019d89c249",
# Note: Updates to dart_style have to be coordinated with the infrastructure
# team so that the internal formatter `tools/sdks/dart-sdk/bin/dart format`
@@ -153,9 +153,9 @@
"html_rev": "6d3bc86cf2ab530ef3fa5f84b5980dc318a02af4",
"http_rev": "f59cd79e1322c6272481e4f2ccfa9afcb37a6525",
"http_multi_server_rev": "e7515b5896b83d522189802a1e14e103e19426c0",
- "http_parser_rev": "ce528cf82f3d26ac761e29b2494a9e0c270d4939",
+ "http_parser_rev": "23d775898ee90be9daf3297e298a8869bc755d84",
"intl_rev": "5d65e3808ce40e6282e40881492607df4e35669f",
- "json_rpc_2_rev": "b4810dc7bee5828f240586c81f3f34853cacdbce",
+ "json_rpc_2_rev": "c9b616bded8cdb5bfdc836ba7648afa6aba40062",
"leak_tracker_rev": "f5620600a5ce1c44f65ddaa02001e200b096e14c", # manually rolled
"lints_rev": "5016d63c889936b2100520f999591d0492d9afe3",
"logging_rev": "6fa056098ceca03d399bff64592822b2ae5dee6e",
@@ -166,7 +166,7 @@
"native_rev": "659511886501bcce638c3966590df04984909ef0", # dart-native-interop-team@ is rolling breaking changes manually while the assets features are in experimental.
"package_config_rev": "bafff8e90be25e1985f7e3ee40ea1d22571a93e6",
"path_rev": "e969f42ed112dd702a9453beb9df6c12ae2d3805",
- "pool_rev": "924fb04353cec915d927f9f1aed88e2eda92b98a",
+ "pool_rev": "7bfc71b39742753a88688e56e55a828a2f5dc0bf",
"protobuf_rev": "ccf104dbc36929c0f8708285d5f3a8fae206343e",
"pub_rev": "1efd3f5e274e153637d99698b0ee454f6f2550ab", # disable tools/rev_sdk_deps.dart
"pub_semver_rev": "8cce9d00431b6653026cdfcf6cf8548078c56f02",
@@ -184,17 +184,17 @@
"test_descriptor_rev": "a3db1efe3dc725dcae9ee61647d3bfc19b3231ac",
"test_process_rev": "52ee3f5ab70ed965bb7122c1d499081fbccd0bde",
"test_reflective_loader_rev": "598af2f503955020af0eaa82558d574a03934078",
- "tools_rev": "d4995d47b99d5e9564abfed2218f4a23df75983b",
- "typed_data_rev": "2bb9e6ead6394e2d4ec6068c5ece8b2ec0e2b945",
+ "tools_rev": "d4995d47b99d5e9564abfed2218f4a23df75983b", #
+ "typed_data_rev": "6abfafdcf661cd8a814619d7e2a3e99edb3a3862",
"vector_math_rev": "2cfbe2c115a57b368ccbc3c89ebd38a06764d3d1",
"watcher_rev": "3b850778ad0b62db3aa2cfe48832870c2461db30",
"web_rev": "8478cd27d574249eca3d41f9135458dfda2762c8",
- "web_socket_channel_rev": "0e1d6e2eb5a0bfd62e45b772ac7107d796176cf6",
+ "web_socket_channel_rev": "40aa29f1d2167467f5934d755891a8beb62a1239",
"webdev_rev": "5f30c560dc4e3df341356c43ec1a766ee6b74a7c",
"webdriver_rev": "accfed557c005c87df56ffb626458f87da9f2125",
"webkit_inspection_protocol_rev": "b459c427b74bf5e0919a083a97a167fb74d8bff1",
"yaml_rev": "e773005ab84e1b4d24132b0a687be7f9a3bfda15",
- "yaml_edit_rev": "5c54d455f272bbb83c948ac420c677371e69ae77",
+ "yaml_edit_rev": "35f4248c7bbba289b3899fa55486e2f31ef1a8c5",
# Windows deps
"crashpad_rev": "bf327d8ceb6a669607b0dbab5a83a275d03f99ed",
diff --git a/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart b/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart
index 96b4039..644ec4a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart
@@ -6,7 +6,7 @@
//
// Instead modify 'tools/experimental_features.yaml' and run
// 'dart pkg/front_end/tool/fasta.dart generate-experimental-flags' to update.
-const Version defaultLanguageVersion = const Version(3, 6);
+const Version defaultLanguageVersion = const Version(3, 7);
/// Enum for experimental flags shared between the CFE and the analyzer.
enum ExperimentalFlag {
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 3255c82..cb25638 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -140,14 +140,21 @@
/// while visiting the code for type inference.
abstract class FlowAnalysis<Node extends Object, Statement extends Node,
Expression extends Node, Variable extends Object, Type extends Object> {
- factory FlowAnalysis(FlowAnalysisOperations<Variable, Type> operations,
- AssignedVariables<Node, Variable> assignedVariables,
- {required bool respectImplicitlyTypedVarInitializers,
- required bool fieldPromotionEnabled}) {
- return new _FlowAnalysisImpl(operations, assignedVariables,
- respectImplicitlyTypedVarInitializers:
- respectImplicitlyTypedVarInitializers,
- fieldPromotionEnabled: fieldPromotionEnabled);
+ factory FlowAnalysis(
+ FlowAnalysisOperations<Variable, Type> operations,
+ AssignedVariables<Node, Variable> assignedVariables, {
+ required bool respectImplicitlyTypedVarInitializers,
+ required bool fieldPromotionEnabled,
+ required bool inferenceUpdate4Enabled,
+ }) {
+ return new _FlowAnalysisImpl(
+ operations,
+ assignedVariables,
+ respectImplicitlyTypedVarInitializers:
+ respectImplicitlyTypedVarInitializers,
+ fieldPromotionEnabled: fieldPromotionEnabled,
+ inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+ );
}
factory FlowAnalysis.legacy(FlowAnalysisOperations<Variable, Type> operations,
@@ -1162,13 +1169,17 @@
factory FlowAnalysisDebug(FlowAnalysisOperations<Variable, Type> operations,
AssignedVariables<Node, Variable> assignedVariables,
{required bool respectImplicitlyTypedVarInitializers,
- required bool fieldPromotionEnabled}) {
+ required bool fieldPromotionEnabled,
+ required bool inferenceUpdate4Enabled}) {
print('FlowAnalysisDebug()');
return new FlowAnalysisDebug._(new _FlowAnalysisImpl(
- operations, assignedVariables,
- respectImplicitlyTypedVarInitializers:
- respectImplicitlyTypedVarInitializers,
- fieldPromotionEnabled: fieldPromotionEnabled));
+ operations,
+ assignedVariables,
+ respectImplicitlyTypedVarInitializers:
+ respectImplicitlyTypedVarInitializers,
+ fieldPromotionEnabled: fieldPromotionEnabled,
+ inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+ ));
}
factory FlowAnalysisDebug.legacy(
@@ -4299,6 +4310,8 @@
final bool fieldPromotionEnabled;
+ final bool inferenceUpdate4Enabled;
+
@override
final PromotionKeyStore<Variable> promotionKeyStore;
@@ -4315,10 +4328,13 @@
@override
final List<_Reference<Type>> _cascadeTargetStack = [];
- _FlowAnalysisImpl(this.operations, this._assignedVariables,
- {required this.respectImplicitlyTypedVarInitializers,
- required this.fieldPromotionEnabled})
- : promotionKeyStore = _assignedVariables.promotionKeyStore {
+ _FlowAnalysisImpl(
+ this.operations,
+ this._assignedVariables, {
+ required this.respectImplicitlyTypedVarInitializers,
+ required this.fieldPromotionEnabled,
+ required this.inferenceUpdate4Enabled,
+ }) : promotionKeyStore = _assignedVariables.promotionKeyStore {
if (!_assignedVariables.isFinished) {
_assignedVariables.finish();
}
diff --git a/pkg/_fe_analyzer_shared/pubspec.yaml b/pkg/_fe_analyzer_shared/pubspec.yaml
index bedf240..18c71ae 100644
--- a/pkg/_fe_analyzer_shared/pubspec.yaml
+++ b/pkg/_fe_analyzer_shared/pubspec.yaml
@@ -1,5 +1,5 @@
name: _fe_analyzer_shared
-version: 74.0.0
+version: 75.0.0
description: Logic that is shared between the front_end and analyzer packages.
repository: https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index c125b81..4dc13f8 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -608,7 +608,8 @@
FlowAnalysis<Node, Statement, Expression, Var, SharedTypeView<Type>>(
h.typeOperations, AssignedVariables<Node, Var>(),
respectImplicitlyTypedVarInitializers: true,
- fieldPromotionEnabled: true);
+ fieldPromotionEnabled: true,
+ inferenceUpdate4Enabled: true);
flow.ifStatement_conditionBegin();
flow.ifStatement_thenBegin(e, s);
expect(() => flow.finish(), _asserts);
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index c8b4067..657105d 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -1674,6 +1674,8 @@
bool? _inferenceUpdate3Enabled;
+ bool? _inferenceUpdate4Enabled;
+
bool? _patternsEnabled;
Type? _thisType;
@@ -1703,6 +1705,9 @@
bool get inferenceUpdate3Enabled =>
_inferenceUpdate3Enabled ?? !operations.legacy;
+ bool get inferenceUpdate4Enabled =>
+ _inferenceUpdate4Enabled ?? !operations.legacy;
+
MiniIRBuilder get irBuilder => typeAnalyzer._irBuilder;
bool get patternsEnabled => _patternsEnabled ?? !operations.legacy;
@@ -1787,6 +1792,11 @@
_inferenceUpdate3Enabled = false;
}
+ void disableInferenceUpdate4() {
+ assert(!_started);
+ _inferenceUpdate4Enabled = false;
+ }
+
void disablePatterns() {
assert(!_started);
_patternsEnabled = false;
@@ -1879,10 +1889,14 @@
SharedTypeView<Type>>.legacy(
operations, visitor._assignedVariables)
: FlowAnalysis<Node, Statement, Expression, Var,
- SharedTypeView<Type>>(operations, visitor._assignedVariables,
+ SharedTypeView<Type>>(
+ operations,
+ visitor._assignedVariables,
respectImplicitlyTypedVarInitializers:
_respectImplicitlyTypedVarInitializers,
- fieldPromotionEnabled: _fieldPromotionEnabled);
+ fieldPromotionEnabled: _fieldPromotionEnabled,
+ inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+ );
typeAnalyzer.dispatchStatement(b);
typeAnalyzer.finish();
expect(typeAnalyzer.errors._accumulatedErrors, expectedErrors);
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index e91aa35..563e109 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -2321,7 +2321,9 @@
</dd><dt class="field"><b>lineLength: int<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
- The line length to be used by the formatter.
+ The line length to be used by the formatter. This value is ignored if a
+ <tt>formatter.page_width</tt> has been configured in the relevant
+ <tt>analysis_options.yaml</tt> file.
</p>
</dd></dl><h4>returns:</h4><dl><dt class="field"><b>edits: List<<a href="#type_SourceEdit">SourceEdit</a>></b></dt><dd>
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index 1c1456c..0d49ab42 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -5830,7 +5830,9 @@
/// The length of the current selection in the file.
int selectionLength;
- /// The line length to be used by the formatter.
+ /// The line length to be used by the formatter. This value is ignored if a
+ /// formatter.page_width has been configured in the relevant
+ /// analysis_options.yaml file.
int? lineLength;
EditFormatParams(this.file, this.selectionOffset, this.selectionLength,
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
index 4a5ceaf..e13eb98 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
@@ -7,6 +7,7 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:dart_style/src/dart_formatter.dart';
import 'package:dart_style/src/exceptions.dart';
@@ -50,10 +51,17 @@
);
var driver = server.getAnalysisDriver(file);
- var library = await driver?.getResolvedLibrary(file);
+ var unit = await driver?.getResolvedUnit(file);
+ var (pageWidth, effectiveLanguageVersion) = switch (unit) {
+ ResolvedUnitResult() => (
+ unit.analysisOptions.formatterOptions.pageWidth,
+ unit.libraryElement2.effectiveLanguageVersion,
+ ),
+ (_) => (null, null),
+ };
var formatter = DartFormatter(
- pageWidth: params.lineLength,
- languageVersion: library.effectiveLanguageVersion);
+ pageWidth: pageWidth ?? params.lineLength,
+ languageVersion: effectiveLanguageVersion);
SourceCode formattedResult;
try {
formattedResult = formatter.formatSource(code);
diff --git a/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
index 6d006e9..7ebfa5a 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
@@ -10,6 +10,7 @@
import 'package:analysis_server/src/legacy_analysis_server.dart';
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.g.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
@@ -100,6 +101,14 @@
}
}
+extension LibraryElement2Extension on LibraryElement2 {
+ // TODO(dantup): Remove this once we can use `languageVersion.effective`
+ // in element-model-migrated files without triggering the lint.
+ Version get effectiveLanguageVersion {
+ return languageVersion.effective;
+ }
+}
+
extension SomeResolvedLibraryResultExtension on SomeResolvedLibraryResult? {
Version get effectiveLanguageVersion {
var self = this;
diff --git a/pkg/analysis_server/lib/src/lsp/client_configuration.dart b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
index 1482139..17545ea 100644
--- a/pkg/analysis_server/lib/src/lsp/client_configuration.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
@@ -286,7 +286,8 @@
true;
}
- /// The line length used when formatting documents.
+ /// The line length used when formatting documents if not specified in
+ /// `analysis_options.yaml`.
///
/// If null, the formatters default will be used.
int? get lineLength =>
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
index 53bb3f3..7def579 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
@@ -39,7 +39,7 @@
}
var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
- return generateEditsForFormatting(result, lineLength);
+ return generateEditsForFormatting(result, defaultPageWidth: lineLength);
}
@override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
index 48297a9..eb39038 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
@@ -39,7 +39,8 @@
}
var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
- return generateEditsForFormatting(result, lineLength, range: range);
+ return generateEditsForFormatting(result,
+ defaultPageWidth: lineLength, range: range);
}
@override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
index 22e6492..411bb66 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
@@ -39,7 +39,7 @@
}
var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
- return generateEditsForFormatting(result, lineLength);
+ return generateEditsForFormatting(result, defaultPageWidth: lineLength);
}
@override
diff --git a/pkg/analysis_server/lib/src/lsp/source_edits.dart b/pkg/analysis_server/lib/src/lsp/source_edits.dart
index 7055cce..91fcdd0 100644
--- a/pkg/analysis_server/lib/src/lsp/source_edits.dart
+++ b/pkg/analysis_server/lib/src/lsp/source_edits.dart
@@ -78,13 +78,25 @@
return ErrorOr.success((content: newContent, edits: serverEdits));
}
+/// Generates a list of [TextEdit]s to format the code for [result].
+///
+/// [defaultPageWidth] will be used as the default page width if [result] does
+/// not have an analysis_options file that defines a page width.
+///
+/// If [range] is provided, only edits that intersect with this range will be
+/// returned.
ErrorOr<List<TextEdit>?> generateEditsForFormatting(
- ParsedUnitResult result,
- int? lineLength, {
+ ParsedUnitResult result, {
+ int? defaultPageWidth,
Range? range,
}) {
var unformattedSource = result.content;
+ // The analysis options page width always takes priority over the default from
+ // the LSP configuration.
+ var effectivePageWidth =
+ result.analysisOptions.formatterOptions.pageWidth ?? defaultPageWidth;
+
var code = SourceCode(unformattedSource);
SourceCode formattedResult;
try {
@@ -94,9 +106,9 @@
var languageVersion =
result.unit.declaredElement?.library.languageVersion.effective ??
DartFormatter.latestLanguageVersion;
- formattedResult =
- DartFormatter(pageWidth: lineLength, languageVersion: languageVersion)
- .formatSource(code);
+ var formatter = DartFormatter(
+ pageWidth: effectivePageWidth, languageVersion: languageVersion);
+ formattedResult = formatter.formatSource(code);
} on FormatterException {
// If the document fails to parse, just return no edits to avoid the
// use seeing edits on every save with invalid code (if LSP gains the
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
index 62d845a..679f98f 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
@@ -39,6 +39,9 @@
AnalyzerOptions.codeStyle: MapProducer({
AnalyzerOptions.format: BooleanProducer(),
}),
+ AnalyzerOptions.formatter: MapProducer({
+ AnalyzerOptions.pageWidth: EmptyProducer(),
+ }),
// TODO(brianwilkerson): Create a producer to produce `package:` URIs.
AnalyzerOptions.include: EmptyProducer(),
// TODO(brianwilkerson): Create constants for 'linter' and 'rules'.
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 2bd3783..2785d97 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -829,7 +829,7 @@
continue;
}
- var formatResult = generateEditsForFormatting(result, null);
+ var formatResult = generateEditsForFormatting(result);
await formatResult.mapResult((formatResult) async {
var edits = formatResult ?? [];
if (edits.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 1fcdf41..27f02b5 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -151,9 +151,7 @@
status: needsFix
notes: |-
For each exported name, add a fix to hide the name.
-CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE:
- status: hasFix
-CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO:
+CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS:
status: hasFix
CompileTimeErrorCode.AMBIGUOUS_IMPORT:
status: needsFix
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index b5fb4a4..30d44f6 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -747,10 +747,7 @@
};
final _builtInNonLintMultiProducers = {
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO: [
- AddExtensionOverride.new,
- ],
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE: [
+ CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS: [
AddExtensionOverride.new,
],
CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE: [
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 02d0c24..0f1075b 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -2035,6 +2035,8 @@
kind: identifier
|code-style: |
kind: identifier
+ |formatter: |
+ kind: identifier
|include: |
kind: identifier
|linter: |
diff --git a/pkg/analysis_server/test/edit/format_test.dart b/pkg/analysis_server/test/edit/format_test.dart
index b6e66c1..1356ac6 100644
--- a/pkg/analysis_server/test/edit/format_test.dart
+++ b/pkg/analysis_server/test/edit/format_test.dart
@@ -23,7 +23,49 @@
await setRoots(included: [workspaceRootPath], excluded: []);
}
- Future<void> test_format_longLine() async {
+ Future<void> test_format_longLine_analysisOptions() async {
+ writeTestPackageAnalysisOptionsFile(r'''
+formatter:
+ page_width: 100
+''');
+ var content = '''
+fun(firstParam, secondParam, thirdParam, fourthParam) {
+ if (firstParam.noNull && secondParam.noNull && thirdParam.noNull && fourthParam.noNull) {}
+}
+''';
+ addTestFile(content);
+ await waitForTasksFinished();
+ var formatResult = await _formatAt(0, 3);
+
+ expect(formatResult.edits, isNotNull);
+ expect(formatResult.edits, hasLength(0));
+
+ expect(formatResult.selectionOffset, equals(0));
+ expect(formatResult.selectionLength, equals(3));
+ }
+
+ Future<void> test_format_longLine_analysisOptions_overridesParameter() async {
+ writeTestPackageAnalysisOptionsFile(r'''
+formatter:
+ page_width: 100
+''');
+ var content = '''
+fun(firstParam, secondParam, thirdParam, fourthParam) {
+ if (firstParam.noNull && secondParam.noNull && thirdParam.noNull && fourthParam.noNull) {}
+}
+''';
+ addTestFile(content);
+ await waitForTasksFinished();
+ var formatResult = await _formatAt(0, 3, lineLength: 50);
+
+ expect(formatResult.edits, isNotNull);
+ expect(formatResult.edits, hasLength(0));
+
+ expect(formatResult.selectionOffset, equals(0));
+ expect(formatResult.selectionLength, equals(3));
+ }
+
+ Future<void> test_format_longLine_parameter() async {
var content = '''
fun(firstParam, secondParam, thirdParam, fourthParam) {
if (firstParam.noNull && secondParam.noNull && thirdParam.noNull && fourthParam.noNull) {}
diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
index f375d81..450344d 100644
--- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
@@ -1566,7 +1566,9 @@
///
/// lineLength: int (optional)
///
- /// The line length to be used by the formatter.
+ /// The line length to be used by the formatter. This value is ignored if a
+ /// formatter.page_width has been configured in the relevant
+ /// analysis_options.yaml file.
///
/// Returns
///
diff --git a/pkg/analysis_server/test/lsp/format_test.dart b/pkg/analysis_server/test/lsp/format_test.dart
index 823e5e0..d51df2e 100644
--- a/pkg/analysis_server/test/lsp/format_test.dart
+++ b/pkg/analysis_server/test/lsp/format_test.dart
@@ -20,6 +20,11 @@
@reflectiveTest
class FormatTest extends AbstractLspAnalysisServerTest {
+ /// Some sample code that is over 50 characters and will be wrapped if the
+ /// page width is set to 40.
+ static const codeThatWrapsAt40 =
+ "var a = ' 10 20 30 40';";
+
Future<List<TextEdit>> expectFormattedContents(
Uri uri, String original, String expected) async {
var formatEdits = (await formatDocument(uri))!;
@@ -36,6 +41,11 @@
return formatEdits;
}
+ Future<String> formatContents(Uri uri, String original) async {
+ var formatEdits = (await formatDocument(uri))!;
+ return applyTextEdits(original, formatEdits);
+ }
+
Future<void> test_alreadyFormatted() async {
const contents = '''
void f() {
@@ -383,6 +393,83 @@
await expectFormattedContents(mainFileUri, contents, expectedLongLines);
}
+ Future<void> test_lineLength_analysisOptions() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(mainFilePath, codeContent);
+ await initialize();
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), contains('\n'));
+ }
+
+ Future<void> test_lineLength_analysisOptions_nestedEmpty() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+ var nestedAnalysisOptionsPath =
+ join(projectFolderPath, 'lib', 'analysis_options.yaml');
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(
+ nestedAnalysisOptionsPath, '# empty'); // suppress the parent options.
+ newFile(mainFilePath, codeContent);
+ await initialize();
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), isNot(contains('\n')));
+ }
+
+ Future<void> test_lineLength_analysisOptions_nestedIncludes() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+ var nestedAnalysisOptionsPath =
+ join(projectFolderPath, 'lib', 'analysis_options.yaml');
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(nestedAnalysisOptionsPath, 'include: ../analysis_options.yaml');
+ newFile(mainFilePath, codeContent);
+ await initialize();
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), contains('\n'));
+ }
+
+ Future<void> test_lineLength_analysisOptions_overridesConfig() async {
+ const codeContent = codeThatWrapsAt40;
+ const optionsContent = '''
+formatter:
+ page_width: 40
+''';
+
+ newFile(analysisOptionsPath, optionsContent);
+ newFile(mainFilePath, codeContent);
+ await provideConfig(
+ initialize,
+ {
+ // This won't apply because analysis_options wins.
+ 'lineLength': 100,
+ },
+ );
+
+ // Ignore trailing newlines when checking for wrapping.
+ var formatted = await formatContents(mainFileUri, codeContent);
+ expect(formatted.trim(), contains('\n'));
+ }
+
Future<void> test_lineLength_outsideWorkspaceFolders() async {
const contents = '''
void f() {
diff --git a/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart b/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
index 8a05a37..51c80e8 100644
--- a/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
@@ -128,11 +128,20 @@
getCompletions('^');
assertSuggestion('${AnalyzerOptions.analyzer}: ');
assertSuggestion('${AnalyzerOptions.codeStyle}: ');
+ assertSuggestion('${AnalyzerOptions.formatter}: ');
assertSuggestion('${AnalyzerOptions.include}: ');
// TODO(brianwilkerson): Replace this with a constant.
assertSuggestion('linter: ');
}
+ void test_formatter() {
+ getCompletions('''
+formatter:
+ ^
+''');
+ assertSuggestion('${AnalyzerOptions.pageWidth}: ');
+ }
+
void test_linter() {
getCompletions('''
linter:
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart
index dbb2b44..6e3f949 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart
@@ -44,7 +44,7 @@
}
''', expectedNumberOfFixesForKind: 1, errorFilter: (error) {
return error.errorCode ==
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO;
+ CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS;
});
}
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index 05abbef..181649c 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -36,7 +36,7 @@
- `dart.analysisExcludedFolders` (`List<String>?`): An array of paths (absolute or relative to each workspace folder) that should be excluded from analysis.
- `dart.enableSdkFormatter` (`bool?`): When set to `false`, prevents registration (or unregisters) the SDK formatter. When set to `true` or not supplied, will register/reregister the SDK formatter.
-- `dart.lineLength` (`int?`): The number of characters the formatter should wrap code at. If unspecified, code will be wrapped at `80` characters.
+- `dart.lineLength` (`int?`): Sets a default value for the formatter to wrap code at if no value is specified in `formatter.page_width` in `analysis_options.yaml`. If unspecified by both, code will be wrapped at `80` characters.
- `dart.completeFunctionCalls` (`bool?`): When set to true, completes functions/methods with their required parameters.
- `dart.showTodos` (`bool?`): Whether to generate diagnostics for TODO comments. If unspecified, diagnostics will not be generated.
- `dart.renameFilesWithClasses` (`String`): When set to `"always"`, will include edits to rename files when classes are renamed if the filename matches the class name (but in snake_form). When set to `"prompt"`, a prompt will be shown on each class rename asking to confirm the file rename. Otherwise, files will not be renamed. Renames are performed using LSP's ResourceOperation edits - that means the rename is simply included in the resulting `WorkspaceEdit` and must be handled by the client.
diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
index 8193f72..37b47f5 100644
--- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
+++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
@@ -511,7 +511,8 @@
* @param file The file containing the code to be formatted.
* @param selectionOffset The offset of the current selection in the file.
* @param selectionLength The length of the current selection in the file.
- * @param lineLength The line length to be used by the formatter.
+ * @param lineLength The line length to be used by the formatter. This value is ignored if a
+ * formatter.page_width has been configured in the relevant analysis_options.yaml file.
*/
public void edit_format(String file, int selectionOffset, int selectionLength, int lineLength, FormatConsumer consumer);
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index aed13fb..6d11b0c 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -2186,7 +2186,9 @@
<field name="lineLength" optional="true">
<ref>int</ref>
<p>
- The line length to be used by the formatter.
+ The line length to be used by the formatter. This value is ignored if a
+ <tt>formatter.page_width</tt> has been configured in the relevant
+ <tt>analysis_options.yaml</tt> file.
</p>
</field>
</params>
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
index 1970aac..64ebc2f 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
@@ -5253,7 +5253,9 @@
/// The length of the current selection in the file.
int selectionLength;
- /// The line length to be used by the formatter.
+ /// The line length to be used by the formatter. This value is ignored if a
+ /// formatter.page_width has been configured in the relevant
+ /// analysis_options.yaml file.
int? lineLength;
EditFormatParams(this.file, this.selectionOffset, this.selectionLength,
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 80913ab..e68cbe2 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 6.10.0-dev
+## 6.10.0
* Deprecated `LibraryElement.accessibleExtensions`, use
`CompilationUnitElement.accessibleExtensions` instead.
* Deprecated `LibraryElement.exportedLibraries`.
diff --git a/pkg/analyzer/lib/dart/analysis/analysis_options.dart b/pkg/analyzer/lib/dart/analysis/analysis_options.dart
index 84c11ff..93ea731 100644
--- a/pkg/analyzer/lib/dart/analysis/analysis_options.dart
+++ b/pkg/analyzer/lib/dart/analysis/analysis_options.dart
@@ -4,6 +4,7 @@
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:pub_semver/src/version_constraint.dart';
@@ -38,6 +39,9 @@
/// analysis.
List<String> get excludePatterns;
+ /// Return the options used to control the formatter.
+ FormatterOptions get formatterOptions;
+
/// Return `true` if analysis is to generate hint results (e.g. best practices
/// and analysis based on certain annotations).
@Deprecated("Use 'warning' instead")
diff --git a/pkg/analyzer/lib/dart/analysis/formatter_options.dart b/pkg/analyzer/lib/dart/analysis/formatter_options.dart
new file mode 100644
index 0000000..a6837fd9
--- /dev/null
+++ b/pkg/analyzer/lib/dart/analysis/formatter_options.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, 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.
+
+/// A set of options related to the formatter that apply to the code within a
+/// single analysis context.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class FormatterOptions {
+ /// The width configured for where the formatter should wrap code.
+ int? get pageWidth;
+}
diff --git a/pkg/analyzer/lib/dart/element/element2.dart b/pkg/analyzer/lib/dart/element/element2.dart
index 909e6e0..42bafae 100644
--- a/pkg/analyzer/lib/dart/element/element2.dart
+++ b/pkg/analyzer/lib/dart/element/element2.dart
@@ -202,6 +202,9 @@
ConstructorElement2 get baseElement;
@override
+ InterfaceElement2 get enclosingElement2;
+
+ @override
ConstructorFragment? get firstFragment;
/// Whether the constructor is a const constructor.
diff --git a/pkg/analyzer/lib/error/listener.dart b/pkg/analyzer/lib/error/listener.dart
index 7a50e6e..0896322 100644
--- a/pkg/analyzer/lib/error/listener.dart
+++ b/pkg/analyzer/lib/error/listener.dart
@@ -11,10 +11,8 @@
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/source.dart';
-import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart';
-import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:meta/meta.dart';
import 'package:source_span/source_span.dart';
@@ -170,19 +168,7 @@
return;
}
- if (arguments != null) {
- var invalid = arguments
- .whereNotType<String>()
- .whereNotType<DartType>()
- .whereNotType<Element>()
- .whereNotType<int>()
- .whereNotType<Uri>();
- if (invalid.isNotEmpty) {
- throw ArgumentError('Tried to format an error using '
- '${invalid.map((e) => e.runtimeType).join(', ')}');
- }
- }
-
+ _convertElements(arguments);
contextMessages ??= [];
contextMessages.addAll(_convertTypeNames(arguments));
_errorListener.onError(
@@ -348,76 +334,92 @@
);
}
- /// Given an array of [arguments] that may contain [DartType]s and [Element]s,
- /// converts the types and elements into strings by using the display names of
- /// each, unless there are two or more types or elements with the same display
- /// names, in which case the extended display names will be used in order to
- /// clarify the message.
- List<DiagnosticMessage> _convertTypeNames(List<Object?>? arguments) {
+ /// Convert all [Element]s in the [arguments] into their display strings.
+ void _convertElements(List<Object>? arguments) {
if (arguments == null) {
- return const [];
+ return;
}
- var typeGroups = <String, List<_ToConvert>>{};
for (var i = 0; i < arguments.length; i++) {
var argument = arguments[i];
- if (argument is TypeImpl) {
- var displayName = argument.getDisplayString(preferTypeAlias: true);
- var types = typeGroups.putIfAbsent(displayName, () => []);
- types.add(_TypeToConvert(i, argument, displayName));
- } else if (argument is Element) {
- var displayName = argument.getDisplayString();
- var types = typeGroups.putIfAbsent(displayName, () => []);
- types.add(_ElementToConvert(i, argument, displayName));
+ if (argument is Element) {
+ arguments[i] = argument.getDisplayString();
+ } else if (!(argument is String ||
+ argument is DartType ||
+ argument is int ||
+ argument is Uri)) {
+ throw ArgumentError(
+ 'Tried to format an error using ${argument.runtimeType}');
}
}
+ }
+ /// Given an array of [arguments] that is expected to contain two or more
+ /// types, convert the types into strings by using the display names of the
+ /// types, unless there are two or more types with the same names, in which
+ /// case the extended display names of the types will be used in order to
+ /// clarify the message.
+ List<DiagnosticMessage> _convertTypeNames(List<Object?>? arguments) {
var messages = <DiagnosticMessage>[];
- for (var typeGroup in typeGroups.values) {
+ if (arguments == null) {
+ return messages;
+ }
+
+ Map<String, List<_TypeToConvert>> typeGroups = {};
+ for (int i = 0; i < arguments.length; i++) {
+ var argument = arguments[i];
+ if (argument is TypeImpl) {
+ String displayName = argument.getDisplayString(
+ preferTypeAlias: true,
+ );
+ List<_TypeToConvert> types =
+ typeGroups.putIfAbsent(displayName, () => <_TypeToConvert>[]);
+ types.add(_TypeToConvert(i, argument, displayName));
+ }
+ }
+ for (List<_TypeToConvert> typeGroup in typeGroups.values) {
if (typeGroup.length == 1) {
- var typeToConvert = typeGroup[0];
- // If the display name of a type is unambiguous, just replace the type
- // in the arguments list with its display name.
+ _TypeToConvert typeToConvert = typeGroup[0];
arguments[typeToConvert.index] = typeToConvert.displayName;
- continue;
- }
-
- var nameToElementMap = <String, Set<Element>>{};
- for (var typeToConvert in typeGroup) {
- for (var element in typeToConvert.allElements) {
- var elements = nameToElementMap.putIfAbsent(element.name!, () => {});
- elements.add(element);
- }
- }
-
- for (var typeToConvert in typeGroup) {
- // TODO(brianwilkerson): When clients do a better job of displaying
- // context messages, remove the extra text added to the buffer.
- StringBuffer? buffer;
- for (var element in typeToConvert.allElements) {
- var name = element.name!;
- var sourcePath = element.source!.fullName;
- if (nameToElementMap[name]!.length > 1) {
- if (buffer == null) {
- buffer = StringBuffer();
- buffer.write('where ');
- } else {
- buffer.write(', ');
- }
- buffer.write('$name is defined in $sourcePath');
+ } else {
+ Map<String, Set<Element>> nameToElementMap = {};
+ for (_TypeToConvert typeToConvert in typeGroup) {
+ for (Element element in typeToConvert.allElements()) {
+ Set<Element> elements =
+ nameToElementMap.putIfAbsent(element.name!, () => <Element>{});
+ elements.add(element);
}
- messages.add(DiagnosticMessageImpl(
- filePath: element.source!.fullName,
- length: element.nameLength,
- message: '$name is defined in $sourcePath',
- offset: element.nameOffset,
- url: null,
- ));
}
+ for (_TypeToConvert typeToConvert in typeGroup) {
+ // TODO(brianwilkerson): When clients do a better job of displaying
+ // context messages, remove the extra text added to the buffer.
+ StringBuffer? buffer;
+ for (Element element in typeToConvert.allElements()) {
+ String name = element.name!;
+ if (nameToElementMap[name]!.length > 1) {
+ if (buffer == null) {
+ buffer = StringBuffer();
+ buffer.write('where ');
+ } else {
+ buffer.write(', ');
+ }
+ buffer.write('$name is defined in ${element.source!.fullName}');
+ }
+ messages.add(DiagnosticMessageImpl(
+ filePath: element.source!.fullName,
+ length: element.nameLength,
+ message: '$name is defined in ${element.source!.fullName}',
+ offset: element.nameOffset,
+ url: null));
+ }
- arguments[typeToConvert.index] = buffer != null
- ? '${typeToConvert.displayName} ($buffer)'
- : typeToConvert.displayName;
+ if (buffer != null) {
+ arguments[typeToConvert.index] =
+ '${typeToConvert.displayName} ($buffer)';
+ } else {
+ arguments[typeToConvert.index] = typeToConvert.displayName;
+ }
+ }
}
}
return messages;
@@ -451,22 +453,6 @@
}
}
-/// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
-/// that is an [Element], that is being converted to a display string.
-class _ElementToConvert implements _ToConvert {
- @override
- final int index;
-
- @override
- final String displayName;
-
- @override
- final Iterable<Element> allElements;
-
- _ElementToConvert(this.index, Element element, this.displayName)
- : allElements = [element];
-}
-
/// An [AnalysisErrorListener] that ignores error.
class _NullErrorListener implements AnalysisErrorListener {
@override
@@ -475,61 +461,42 @@
}
}
-/// Used by [ErrorReporter._convertTypeNames] to keep track of an argument that
-/// is being converted to a display string.
-abstract class _ToConvert {
- /// A list of all elements involved in the [DartType] or [Element]'s display
- /// string.
- Iterable<Element> get allElements;
-
- /// The argument's display string, to replace the argument in the argument
- /// list.
- String get displayName;
-
- /// The index of the argument in the argument list.
- int get index;
-}
-
-/// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
-/// that is a [DartType], that is being converted to a display string.
-class _TypeToConvert implements _ToConvert {
- @override
+/// Used by `ErrorReporter._convertTypeNames` to keep track of a type that is
+/// being converted.
+class _TypeToConvert {
final int index;
-
- final DartType _type;
-
- @override
+ final DartType type;
final String displayName;
- @override
- late final Iterable<Element> allElements = () {
- var elements = <Element>{};
+ List<Element>? _allElements;
- void addElementsFrom(DartType type) {
- if (type is FunctionType) {
- addElementsFrom(type.returnType);
- for (var parameter in type.parameters) {
- addElementsFrom(parameter.type);
- }
- } else if (type is RecordType) {
- for (var parameter in type.fields) {
- addElementsFrom(parameter.type);
- }
- } else if (type is InterfaceType) {
- if (elements.add(type.element)) {
- for (var typeArgument in type.typeArguments) {
- addElementsFrom(typeArgument);
+ _TypeToConvert(this.index, this.type, this.displayName);
+
+ List<Element> allElements() {
+ if (_allElements == null) {
+ Set<Element> elements = <Element>{};
+
+ void addElementsFrom(DartType type) {
+ if (type is FunctionType) {
+ addElementsFrom(type.returnType);
+ for (ParameterElement parameter in type.parameters) {
+ addElementsFrom(parameter.type);
+ }
+ } else if (type is InterfaceType) {
+ if (elements.add(type.element)) {
+ for (DartType typeArgument in type.typeArguments) {
+ addElementsFrom(typeArgument);
+ }
}
}
}
+
+ addElementsFrom(type);
+ _allElements = elements.where((element) {
+ var name = element.name;
+ return name != null && name.isNotEmpty;
+ }).toList();
}
-
- addElementsFrom(_type);
- return elements.where((element) {
- var name = element.name;
- return name != null && name.isNotEmpty;
- });
- }();
-
- _TypeToConvert(this.index, this._type, this.displayName);
+ return _allElements!;
+ }
}
diff --git a/pkg/analyzer/lib/src/analysis_options/apply_options.dart b/pkg/analyzer/lib/src/analysis_options/apply_options.dart
index e266feb..6afa907 100644
--- a/pkg/analyzer/lib/src/analysis_options/apply_options.dart
+++ b/pkg/analyzer/lib/src/analysis_options/apply_options.dart
@@ -4,9 +4,11 @@
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/src/analysis_options/code_style_options.dart';
+import 'package:analyzer/src/analysis_options/formatter_options.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
@@ -148,6 +150,18 @@
return CodeStyleOptionsImpl(this, useFormatter: useFormatter);
}
+ FormatterOptions buildFormatterOptions(YamlNode? formatter) {
+ int? pageWidth;
+ if (formatter is YamlMap) {
+ var formatNode = formatter.valueAt(AnalyzerOptions.pageWidth);
+ var formatValue = formatNode?.value;
+ if (formatValue is int && formatValue > 0) {
+ pageWidth = formatValue;
+ }
+ }
+ return FormatterOptionsImpl(this, pageWidth: pageWidth);
+ }
+
void _applyLegacyPlugins(YamlNode? plugins) {
var pluginName = plugins.stringValue;
if (pluginName != null) {
@@ -226,6 +240,10 @@
var codeStyle = optionMap.valueAt(AnalyzerOptions.codeStyle);
codeStyleOptions = buildCodeStyleOptions(codeStyle);
+ // Process the 'formatter' option.
+ var formatter = optionMap.valueAt(AnalyzerOptions.formatter);
+ formatterOptions = buildFormatterOptions(formatter);
+
var config = parseConfig(optionMap);
if (config != null) {
var enabledRules = Registry.ruleRegistry.enabled(config);
diff --git a/pkg/analyzer/lib/src/analysis_options/formatter_options.dart b/pkg/analyzer/lib/src/analysis_options/formatter_options.dart
new file mode 100644
index 0000000..e2352eb
--- /dev/null
+++ b/pkg/analyzer/lib/src/analysis_options/formatter_options.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2024, 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/analysis/analysis_options.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
+
+/// The concrete implementation of [FormatterOptions].
+class FormatterOptionsImpl implements FormatterOptions {
+ /// The analysis options that owns this instance.
+ final AnalysisOptions options;
+
+ /// The width configured for where the formatter should wrap code.
+ @override
+ final int? pageWidth;
+
+ FormatterOptionsImpl(this.options, {this.pageWidth});
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
index 233e1e2..8041f93 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
@@ -12,7 +12,7 @@
/// The current version of the Dart language (or, for non-stable releases, the
/// version of the language currently in the process of being developed).
-const _currentVersion = '3.6.0';
+const _currentVersion = '3.7.0';
/// A map containing information about all known experimental flags.
final _knownFeatures = <String, ExperimentalFeature>{
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 98a53a7..dd6f1ed 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1553,8 +1553,8 @@
ConstructorElement2 get baseElement => this;
@override
- Element2? get enclosingElement2 =>
- (firstFragment._enclosingElement3 as InstanceFragment).element;
+ InterfaceElement2 get enclosingElement2 =>
+ (firstFragment._enclosingElement3 as InterfaceFragment).element;
@override
bool get isConst => firstFragment.isConst;
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index 795de17..c1d845d 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -55,6 +55,9 @@
InterfaceElement get enclosingElement => declaration.enclosingElement;
@override
+ InterfaceElement2 get enclosingElement2 => _element2.enclosingElement2;
+
+ @override
InterfaceElement get enclosingElement3 => declaration.enclosingElement3;
@override
diff --git a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
index b12474a..23b3a8e 100644
--- a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
@@ -124,9 +124,10 @@
/// Tries to match [P] as a subtype for [Q].
///
- /// If the match succeeds, the resulting type constraints are recorded for
- /// later use by [computeConstraints]. If the match fails, the set of type
- /// constraints is unchanged.
+ /// If [P] is a subtype of [Q] under some constraints, the constraints making
+ /// the relation possible are recorded to [_constraints], and `true` is
+ /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+ /// and `false` is returned.
bool trySubtypeMatch(DartType P, DartType Q, bool leftSchema,
{required AstNode? nodeForTesting}) {
// If `P` is `_` then the match holds with no constraints.
@@ -193,7 +194,6 @@
nodeForTesting: nodeForTesting)) {
return true;
}
- _constraints.length = rewind;
}
// Or if `P` is `dynamic` or `void` and `Object` is a subtype match
@@ -203,7 +203,6 @@
nodeForTesting: nodeForTesting)) {
return true;
}
- _constraints.length = rewind;
}
// Or if `P` is a subtype match for `Q0` under non-empty
@@ -213,14 +212,12 @@
if (P_matches_Q0 && _constraints.length != rewind) {
return true;
}
- _constraints.length = rewind;
// Or if `P` is a subtype match for `Null` under constraint set `C`.
if (trySubtypeMatch(P, _typeSystem.nullNone, leftSchema,
nodeForTesting: nodeForTesting)) {
return true;
}
- _constraints.length = rewind;
// Or if `P` is a subtype match for `Q0` under empty
// constraint set `C`.
@@ -295,13 +292,11 @@
// If `B` is a subtype match for `Q` with constraint set `C`.
// Note: we have already eliminated the case that `X` is a variable in `L`.
if (P_nullability == NullabilitySuffix.none && P is TypeParameterTypeImpl) {
- var rewind = _constraints.length;
var B = P.promotedBound ?? P.element.bound;
if (B != null &&
trySubtypeMatch(B, Q, leftSchema, nodeForTesting: nodeForTesting)) {
return true;
}
- _constraints.length = rewind;
}
bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
@@ -369,6 +364,12 @@
}
}
+ /// Matches [P] against [Q], where [P] and [Q] are both function types.
+ ///
+ /// If [P] is a subtype of [Q] under some constraints, the constraints making
+ /// the relation possible are recorded to [_constraints], and `true` is
+ /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+ /// and `false` is returned.
bool _functionType(FunctionType P, FunctionType Q, bool leftSchema,
{required AstNode? nodeForTesting}) {
if (P.nullabilitySuffix != NullabilitySuffix.none) {
@@ -451,11 +452,18 @@
return true;
}
- /// A function type `(M0,..., Mn, [M{n+1}, ..., Mm]) -> R0` is a subtype
- /// match for a function type `(N0,..., Nk, [N{k+1}, ..., Nr]) -> R1` with
- /// respect to `L` under constraints `C0 + ... + Cr + C`.
+ /// Matches [P] against [Q], where [P] and [Q] are both non-generic function
+ /// types.
+ ///
+ /// If [P] is a subtype of [Q] under some constraints, the constraints making
+ /// the relation possible are recorded to [_constraints], and `true` is
+ /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+ /// and `false` is returned.
bool _functionType0(FunctionType f, FunctionType g, bool leftSchema,
{required AstNode? nodeForTesting}) {
+ // A function type `(M0,..., Mn, [M{n+1}, ..., Mm]) -> R0` is a subtype
+ // match for a function type `(N0,..., Nk, [N{k+1}, ..., Nr]) -> R1` with
+ // respect to `L` under constraints `C0 + ... + Cr + C`.
bool? result = performSubtypeConstraintGenerationForFunctionTypes(f, g,
leftSchema: leftSchema, astNodeForTesting: nodeForTesting);
if (result != null) {
@@ -464,12 +472,18 @@
return false;
}
- /// If `P` is `(M0, ..., Mk)` and `Q` is `(N0, ..., Nk)`, then the match
- /// holds under constraints `C0 + ... + Ck`:
- /// If `Mi` is a subtype match for `Ni` with respect to L under
- /// constraints `Ci`.
+ /// Matches [P] against [Q], where [P] and [Q] are both record types.
+ ///
+ /// If [P] is a subtype of [Q] under some constraints, the constraints making
+ /// the relation possible are recorded to [_constraints], and `true` is
+ /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+ /// and `false` is returned.
bool _recordType(RecordTypeImpl P, RecordTypeImpl Q, bool leftSchema,
{required AstNode? nodeForTesting}) {
+ // If `P` is `(M0, ..., Mk)` and `Q` is `(N0, ..., Nk)`, then the match
+ // holds under constraints `C0 + ... + Ck`:
+ // If `Mi` is a subtype match for `Ni` with respect to L under
+ // constraints `Ci`.
if (P.nullabilitySuffix != NullabilitySuffix.none) {
return false;
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index 789b233..1f51888 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -117,33 +117,21 @@
}
// The most specific extension is ambiguous.
- if (mostSpecific.length == 2) {
- _errorReporter.atEntity(
- nameEntity,
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO,
- arguments: [
- name.name,
- mostSpecific[0].extension,
- mostSpecific[1].extension,
- ],
- );
- } else {
- _errorReporter.atEntity(
- nameEntity,
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE,
- arguments: [
- name.name,
- mostSpecific.map((e) {
- var name = e.extension.name;
- if (name != null) {
- return "extension '$name'";
- }
- var type = e.extension.extendedType.getDisplayString();
- return "unnamed extension on '$type'";
- }).commaSeparatedWithAnd,
- ],
- );
- }
+ _errorReporter.atEntity(
+ nameEntity,
+ CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS,
+ arguments: [
+ name.name,
+ mostSpecific.map((e) {
+ var name = e.extension.name;
+ if (name != null) {
+ return "extension '$name'";
+ }
+ var type = e.extension.extendedType.getDisplayString();
+ return "unnamed extension on '$type'";
+ }).commaSeparatedWithAnd,
+ ],
+ );
return ResolutionResult.ambiguous;
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 15f28d2..219759e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -87,24 +87,34 @@
final bool fieldPromotionEnabled;
+ final bool inferenceUpdate4Enabled;
+
/// The current flow, when resolving a function body, or `null` otherwise.
FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
SharedTypeView<DartType>>? flow;
FlowAnalysisHelper(bool retainDataForTesting, FeatureSet featureSet,
{required TypeSystemOperations typeSystemOperations})
- : this._(typeSystemOperations,
- retainDataForTesting ? FlowAnalysisDataForTesting() : null,
- isNonNullableByDefault: featureSet.isEnabled(Feature.non_nullable),
- respectImplicitlyTypedVarInitializers:
- featureSet.isEnabled(Feature.constructor_tearoffs),
- fieldPromotionEnabled:
- featureSet.isEnabled(Feature.inference_update_2));
+ : this._(
+ typeSystemOperations,
+ retainDataForTesting ? FlowAnalysisDataForTesting() : null,
+ isNonNullableByDefault: featureSet.isEnabled(Feature.non_nullable),
+ respectImplicitlyTypedVarInitializers:
+ featureSet.isEnabled(Feature.constructor_tearoffs),
+ fieldPromotionEnabled:
+ featureSet.isEnabled(Feature.inference_update_2),
+ inferenceUpdate4Enabled:
+ featureSet.isEnabled(Feature.inference_update_4),
+ );
- FlowAnalysisHelper._(this.typeOperations, this.dataForTesting,
- {required this.isNonNullableByDefault,
- required this.respectImplicitlyTypedVarInitializers,
- required this.fieldPromotionEnabled});
+ FlowAnalysisHelper._(
+ this.typeOperations,
+ this.dataForTesting, {
+ required this.isNonNullableByDefault,
+ required this.respectImplicitlyTypedVarInitializers,
+ required this.fieldPromotionEnabled,
+ required this.inferenceUpdate4Enabled,
+ });
LocalVariableTypeProvider get localVariableTypeProvider {
return _LocalVariableTypeProvider(this);
@@ -261,10 +271,14 @@
}
flow = isNonNullableByDefault
? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
- SharedTypeView<DartType>>(typeOperations, assignedVariables!,
+ SharedTypeView<DartType>>(
+ typeOperations,
+ assignedVariables!,
respectImplicitlyTypedVarInitializers:
respectImplicitlyTypedVarInitializers,
- fieldPromotionEnabled: fieldPromotionEnabled)
+ fieldPromotionEnabled: fieldPromotionEnabled,
+ inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+ )
: FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
SharedTypeView<DartType>>.legacy(
typeOperations, assignedVariables!);
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index ec12582..343e8e5 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -70,31 +70,14 @@
/// Parameters:
/// 0: the name of the member
/// 1: the names of the declaring extensions
- static const CompileTimeErrorCode
- AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE = CompileTimeErrorCode(
+ static const CompileTimeErrorCode AMBIGUOUS_EXTENSION_MEMBER_ACCESS =
+ CompileTimeErrorCode(
'AMBIGUOUS_EXTENSION_MEMBER_ACCESS',
"A member named '{0}' is defined in {1}, and none are more specific.",
correctionMessage:
"Try using an extension override to specify the extension you want to "
"be chosen.",
hasPublishedDocs: true,
- uniqueName: 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE',
- );
-
- /// Parameters:
- /// 0: the name of the member
- /// 1: the name of the first declaring extension
- /// 2: the names of the second declaring extension
- static const CompileTimeErrorCode AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO =
- CompileTimeErrorCode(
- 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS',
- "A member named '{0}' is defined in '{1}' and '{2}', and neither is more "
- "specific.",
- correctionMessage:
- "Try using an extension override to specify the extension you want to "
- "be chosen.",
- hasPublishedDocs: true,
- uniqueName: 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO',
);
/// Parameters:
diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart
index 407af9ca..c207246 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -49,8 +49,7 @@
CompileTimeErrorCode.ABSTRACT_FIELD_INITIALIZER,
CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
CompileTimeErrorCode.AMBIGUOUS_EXPORT,
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE,
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO,
+ CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS,
CompileTimeErrorCode.AMBIGUOUS_IMPORT,
CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH,
CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER,
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index caf41d6..3950a30 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -9,6 +9,7 @@
import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
@@ -16,6 +17,7 @@
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/analysis_options/code_style_options.dart';
+import 'package:analyzer/src/analysis_options/formatter_options.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/source.dart' show SourceFactory;
import 'package:analyzer/src/lint/linter.dart';
@@ -239,6 +241,9 @@
@override
late CodeStyleOptions codeStyleOptions;
+ @override
+ late FormatterOptions formatterOptions;
+
/// The set of "un-ignorable" error names, as parsed in [AnalyzerOptions] from
/// an analysis options file.
Set<String> unignorableNames = {};
@@ -247,12 +252,14 @@
/// values.
AnalysisOptionsImpl({this.file}) {
codeStyleOptions = CodeStyleOptionsImpl(this, useFormatter: false);
+ formatterOptions = FormatterOptionsImpl(this);
}
/// Initialize a newly created set of analysis options to have the same values
/// as those in the given set of analysis [options].
AnalysisOptionsImpl.from(AnalysisOptions options) {
codeStyleOptions = options.codeStyleOptions;
+ formatterOptions = options.formatterOptions;
contextFeatures = options.contextFeatures;
enabledLegacyPluginNames = options.enabledLegacyPluginNames;
errorProcessors = options.errorProcessors;
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index 8b592db..bf87e60 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -239,6 +239,7 @@
static const String enableExperiment = 'enable-experiment';
static const String errors = 'errors';
static const String exclude = 'exclude';
+ static const String formatter = 'formatter';
static const String include = 'include';
static const String language = 'language';
static const String optionalChecks = 'optional-checks';
@@ -270,6 +271,9 @@
/// Ways to say `include`.
static const List<String> includeSynonyms = ['include', 'true'];
+ // Formatter options.
+ static const String pageWidth = 'page_width';
+
static const String propagateLinterExceptions = 'propagate-linter-exceptions';
/// Ways to say `true` or `false`.
@@ -648,6 +652,52 @@
}
}
+/// Validates `formatter` options.
+class FormatterOptionsValidator extends OptionsValidator {
+ @override
+ void validate(ErrorReporter reporter, YamlMap options) {
+ var formatter = options.valueAt(AnalyzerOptions.formatter);
+ if (formatter is YamlMap) {
+ for (var MapEntry(key: keyNode, value: valueNode)
+ in formatter.nodeMap.entries) {
+ if (keyNode.value == AnalyzerOptions.pageWidth) {
+ _validatePageWidth(keyNode, valueNode, reporter);
+ } else {
+ reporter.atSourceSpan(
+ keyNode.span,
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ arguments: [
+ AnalyzerOptions.formatter,
+ keyNode.toString(),
+ ],
+ );
+ }
+ }
+ } else if (formatter != null && formatter.value != null) {
+ reporter.atSourceSpan(
+ formatter.span,
+ AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+ arguments: [AnalyzerOptions.formatter],
+ );
+ }
+ }
+
+ void _validatePageWidth(
+ YamlNode keyNode, YamlNode valueNode, ErrorReporter reporter) {
+ var value = valueNode.value;
+ if (value is! int || value <= 0) {
+ reporter.atSourceSpan(
+ valueNode.span,
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ arguments: [
+ keyNode.toString(),
+ '"page_width" must be a positive integer.',
+ ],
+ );
+ }
+ }
+}
+
/// Validates `analyzer` language configuration options.
class LanguageOptionValidator extends OptionsValidator {
final ErrorBuilder _builder = ErrorBuilder(AnalyzerOptions.languageOptions);
@@ -776,6 +826,7 @@
}) : _validators = [
AnalyzerOptionsValidator(),
CodeStyleOptionsValidator(),
+ FormatterOptionsValidator(),
LinterOptionsValidator(),
LinterRuleOptionsValidator(
provider: provider,
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 1ead2a7..15aeb69d 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -408,16 +408,14 @@
export 'a.dart';
export 'b.dart' hide C;
```
- AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO:
- sharedName: AMBIGUOUS_EXTENSION_MEMBER_ACCESS
- problemMessage: "A member named '{0}' is defined in '{1}' and '{2}', and neither is more specific."
+ AMBIGUOUS_EXTENSION_MEMBER_ACCESS:
+ problemMessage: "A member named '{0}' is defined in {1}, and none are more specific."
correctionMessage: Try using an extension override to specify the extension you want to be chosen.
hasPublishedDocs: true
comment: |-
Parameters:
0: the name of the member
- 1: the name of the first declaring extension
- 2: the names of the second declaring extension
+ 1: the names of the declaring extensions
documentation: |-
#### Description
@@ -473,15 +471,6 @@
print(E2(s).charCount);
}
```
- AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE:
- sharedName: AMBIGUOUS_EXTENSION_MEMBER_ACCESS
- problemMessage: "A member named '{0}' is defined in {1}, and none are more specific."
- correctionMessage: Try using an extension override to specify the extension you want to be chosen.
- hasPublishedDocs: true
- comment: |-
- Parameters:
- 0: the name of the member
- 1: the names of the declaring extensions
AMBIGUOUS_IMPORT:
problemMessage: "The name '{0}' is defined in the libraries {1}."
correctionMessage: "Try using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports."
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index fcde8f7..39a770e 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
name: analyzer
-version: 6.10.0-dev
+version: 6.10.0
description: >-
This package provides a library that performs static analysis of Dart code.
repository: https://github.com/dart-lang/sdk/tree/main/pkg/analyzer
@@ -8,7 +8,7 @@
sdk: '>=3.3.0 <4.0.0'
dependencies:
- _fe_analyzer_shared: ^74.0.0
+ _fe_analyzer_shared: ^75.0.0
collection: ^1.19.0
convert: ^3.0.0
crypto: ^3.0.0
diff --git a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart
index 7995377..c96edd7 100644
--- a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart
@@ -29,7 +29,7 @@
int f(A a) => a();
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 110, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 110, 1),
]);
}
@@ -47,7 +47,7 @@
0.a;
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 98, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 98, 1),
]);
var node = findNode.propertyAccess('0.a');
@@ -111,7 +111,7 @@
0.a;
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 91, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 91, 1),
]);
var node = findNode.propertyAccess('0.a');
@@ -144,7 +144,7 @@
0.a;
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 96, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 96, 1),
]);
var node = findNode.propertyAccess('0.a');
@@ -172,38 +172,8 @@
0.foo();
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3,
- messageContains: [
- "in 'extension E1 on int' and 'extension E2 on int',",
- ]),
- ]);
- }
-
- test_method_conflict_conflict_notSpecific_sameName() async {
- var one = newFile('$testPackageLibPath/one.dart', '''
-extension E on int { void foo() {} }
-''');
- var two = newFile('$testPackageLibPath/two.dart', '''
-extension E on int { void foo() {} }
-''');
- await assertErrorsInCode('''
-// ignore_for_file: unused_import
-import 'one.dart';
-import 'two.dart';
-void f() {
- 0.foo();
-}
-''', [
- error(
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO,
- 87,
- 3,
- messageContains: [
- "'extension E on int (where E is defined in ${one.path})' and "
- "'extension E on int (where E is defined in ${two.path})',",
- ],
- contextMessages: [message(one, 10, 1), message(two, 10, 1)],
- ),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3,
+ messageContains: ["in extension 'E1' and extension 'E2',"]),
]);
}
@@ -227,10 +197,8 @@
0.foo();
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3,
- messageContains: [
- "in 'extension E1 on int' and 'extension E2 on int',",
- ]),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3,
+ messageContains: ["in extension 'E1' and extension 'E2',"]),
]);
}
@@ -259,7 +227,7 @@
0.a();
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 88, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1),
]);
var node = findNode.methodInvocation('0.a()');
@@ -291,10 +259,8 @@
0.foo();
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3,
- messageContains: [
- "in 'extension E1 on int' and 'extension E2 on int',"
- ]),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3,
+ messageContains: ["in extension 'E1' and extension 'E2',"]),
]);
}
@@ -308,10 +274,7 @@
0.foo();
}
''', [
- error(
- CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE,
- 167,
- 3,
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 3,
messageContains: [
"in extension 'E1', extension 'E2', and extension 'E3',"
]),
@@ -350,7 +313,7 @@
t.foo;
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 396, 3),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 396, 3),
]);
}
@@ -368,7 +331,7 @@
A f(A a) => a + a;
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 122, 5),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 122, 5),
]);
}
@@ -388,7 +351,7 @@
a += 0;
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 130, 2),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 130, 2),
]);
}
@@ -406,7 +369,7 @@
int f(A a) => a[0];
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 134, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 134, 1),
]);
}
@@ -424,7 +387,7 @@
0[1] += 2;
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 136, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 136, 1),
]);
}
@@ -442,7 +405,7 @@
int f(A a) => -a;
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 123, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 123, 1),
]);
}
@@ -460,7 +423,7 @@
0.a = 3;
}
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 88, 1),
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1),
]);
assertResolvedNodeText(findNode.assignment('= 3'), r'''
@@ -513,9 +476,10 @@
int g(List<A> x) => x();
int h(List<B> x) => x();
''', [
- error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 167, 1,
+ error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 1,
messageContains: [
- "'extension on List<A>' and 'extension on List<B>',"
+ "in unnamed extension on 'List<A>' and unnamed extension on "
+ "'List<B>',"
]),
]);
}
diff --git a/pkg/analyzer/test/src/task/options_test.dart b/pkg/analyzer/test/src/task/options_test.dart
index 0b0524f..956fac7 100644
--- a/pkg/analyzer/test/src/task/options_test.dart
+++ b/pkg/analyzer/test/src/task/options_test.dart
@@ -332,6 +332,73 @@
''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES]);
}
+ test_formatter_invalid_key() {
+ validate('''
+formatter:
+ wrong: 123
+''', [
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ ]);
+ }
+
+ test_formatter_invalid_keys() {
+ validate('''
+formatter:
+ wrong: 123
+ wrong2: 123
+''', [
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+ ]);
+ }
+
+ test_formatter_pageWidth_invalid_decimal() {
+ validate('''
+formatter:
+ page_width: 123.45
+''', [
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ ]);
+ }
+
+ test_formatter_pageWidth_invalid_negativeInteger() {
+ validate('''
+formatter:
+ page_width: -123
+''', [
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ ]);
+ }
+
+ test_formatter_pageWidth_invalid_string() {
+ validate('''
+formatter:
+ page_width: "123"
+''', [AnalysisOptionsWarningCode.INVALID_OPTION]);
+ }
+
+ test_formatter_pageWidth_invalid_zero() {
+ validate('''
+formatter:
+ page_width: 0
+''', [
+ AnalysisOptionsWarningCode.INVALID_OPTION,
+ ]);
+ }
+
+ test_formatter_pageWidth_valid_integer() {
+ validate('''
+formatter:
+ page_width: 123
+''', []);
+ }
+
+ test_formatter_valid_empty() {
+ validate('''
+formatter:
+''', []);
+ }
+
test_linter_supported_rules() {
Registry.ruleRegistry.register(TestRule());
validate('''
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 9062916..e5611dc 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -430,9 +430,6 @@
### ambiguous_extension_member_access
-_A member named '{0}' is defined in '{1}' and '{2}', and neither is more
-specific._
-
_A member named '{0}' is defined in {1}, and none are more specific._
#### Description
diff --git a/pkg/dart2wasm/tool/compile_benchmark b/pkg/dart2wasm/tool/compile_benchmark
index 02e5d79..eefae07 100755
--- a/pkg/dart2wasm/tool/compile_benchmark
+++ b/pkg/dart2wasm/tool/compile_benchmark
@@ -126,6 +126,12 @@
shift
;;
+ --extra-compiler-option=--no-js-compatibility)
+ PLATFORM_FILENAME="$BIN_DIR/dart2wasm_platform.dill"
+ DART2WASM_ARGS+=("--no-js-compatibility")
+ shift
+ ;;
+
--extra-compiler-option=*)
DART2WASM_ARGS+=(${1#--extra-compiler-option=})
shift
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart
index c8cc3f2..23ad8d6 100644
--- a/pkg/dartdev/lib/src/commands/compile.dart
+++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -4,7 +4,6 @@
import 'dart:async';
import 'dart:io';
-import 'dart:isolate';
import 'package:args/args.dart';
import 'package:dart2native/generate.dart';
@@ -88,48 +87,41 @@
@override
FutureOr<int> run() async {
- if (!Sdk.checkArtifactExists(sdk.dart2jsSnapshot) ||
- !Sdk.checkArtifactExists(sdk.librariesJson)) {
- return 255;
+ if (!Sdk.checkArtifactExists(sdk.librariesJson)) {
+ return genericErrorExitCode;
}
-
final args = argResults!;
-
- // Build arguments.
- final buildArgs = <String>[
+ var snapshot = sdk.dart2jsAotSnapshot;
+ var runtime = sdk.dartAotRuntime;
+ if (!Sdk.checkArtifactExists(snapshot, logError: false)) {
+ // AOT snapshots cannot be generated on IA32, so we need this fallback
+ // branch until support for IA32 is dropped (https://dartbug.com/49969).
+ snapshot = sdk.dart2jsSnapshot;
+ runtime = sdk.dart;
+ if (!Sdk.checkArtifactExists(snapshot)) {
+ return genericErrorExitCode;
+ }
+ }
+ final dart2jsCommand = [
+ runtime,
+ snapshot,
'--libraries-spec=${sdk.librariesJson}',
'--cfe-invocation-modes=compile',
'--invoker=dart_cli',
// Add the remaining arguments.
if (args.rest.isNotEmpty) ...args.rest.sublist(0),
];
-
- var retval = 0;
- final result = Completer<int>();
- final exitPort = ReceivePort()
- ..listen((msg) {
- result.complete(0);
- });
- final errorPort = ReceivePort()
- ..listen((error) {
- log.stderr(error.toString());
- result.complete(255);
- });
try {
- await Isolate.spawnUri(Uri.file(sdk.dart2jsSnapshot), buildArgs, null,
- onExit: exitPort.sendPort, onError: errorPort.sendPort);
- retval = await result.future;
+ final exitCode = await runProcessInheritStdio(dart2jsCommand);
+ return exitCode;
} catch (e, st) {
log.stderr('Error: JS compilation failed');
log.stderr(e.toString());
if (verbose) {
log.stderr(st.toString());
}
- retval = compileErrorExitCode;
+ return compileErrorExitCode;
}
- errorPort.close();
- exitPort.close();
- return retval;
}
}
diff --git a/pkg/dartdev/lib/src/core.dart b/pkg/dartdev/lib/src/core.dart
index c6a8c79..8e89b55 100644
--- a/pkg/dartdev/lib/src/core.dart
+++ b/pkg/dartdev/lib/src/core.dart
@@ -119,8 +119,11 @@
}
log.trace(command.join(' '));
- final process = await Process.start(command.first, command.skip(1).toList(),
- workingDirectory: cwd);
+ final process = await Process.start(
+ command.first,
+ command.skip(1).toList(),
+ workingDirectory: cwd,
+ );
final (_, _, exitCode) = await (
forward(process.stdout, false),
forward(process.stderr, true),
@@ -129,6 +132,22 @@
return exitCode;
}
+Future<int> runProcessInheritStdio(
+ List<String> command, {
+ bool logToTrace = false,
+ void Function(String str)? listener,
+ String? cwd,
+}) async {
+ log.trace(command.join(' '));
+ final process = await Process.start(
+ command.first,
+ command.skip(1).toList(),
+ workingDirectory: cwd,
+ mode: ProcessStartMode.inheritStdio,
+ );
+ return await process.exitCode;
+}
+
Future _streamLineTransform(
Stream<List<int>> stream,
Function(String line) handler,
diff --git a/pkg/dartdev/lib/src/sdk.dart b/pkg/dartdev/lib/src/sdk.dart
index 168294b..5ef7626 100644
--- a/pkg/dartdev/lib/src/sdk.dart
+++ b/pkg/dartdev/lib/src/sdk.dart
@@ -71,6 +71,10 @@
'dart2js.dart.snapshot',
);
+ String get dart2jsAotSnapshot => _snapshotPathFor(
+ 'dart2js_aot.dart.snapshot',
+ );
+
String get dart2wasmSnapshot => _snapshotPathFor(
'dart2wasm_product.snapshot',
);
diff --git a/pkg/dartdev/test/sdk_test.dart b/pkg/dartdev/test/sdk_test.dart
index de39966..9ef7801 100644
--- a/pkg/dartdev/test/sdk_test.dart
+++ b/pkg/dartdev/test/sdk_test.dart
@@ -30,7 +30,7 @@
});
test('dart2js snapshot', () {
- expectFileExists(Sdk().dart2jsSnapshot);
+ expectSnapshotExists(Sdk().dart2jsAotSnapshot, Sdk().dart2jsSnapshot);
});
}
diff --git a/pkg/dev_compiler/lib/ddc.dart b/pkg/dev_compiler/lib/ddc.dart
index fb4364f..3526364 100755
--- a/pkg/dev_compiler/lib/ddc.dart
+++ b/pkg/dev_compiler/lib/ddc.dart
@@ -18,7 +18,6 @@
import 'src/command/arguments.dart';
import 'src/command/command.dart';
import 'src/command/result.dart';
-import 'src/compiler/shared_command.dart';
import 'src/kernel/expression_compiler_worker.dart';
/// The internal entry point for the Dart Dev Compiler.
diff --git a/pkg/dev_compiler/lib/src/command/command.dart b/pkg/dev_compiler/lib/src/command/command.dart
index 76e7c82..b1a3018 100644
--- a/pkg/dev_compiler/lib/src/command/command.dart
+++ b/pkg/dev_compiler/lib/src/command/command.dart
@@ -8,6 +8,8 @@
import 'package:args/args.dart';
import 'package:build_integration/file_system/multi_root.dart';
+import 'package:front_end/src/api_prototype/macros.dart' as macros
+ show isMacroLibraryUri;
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:kernel/binary/ast_to_binary.dart' as kernel show BinaryPrinter;
import 'package:kernel/class_hierarchy.dart';
@@ -21,7 +23,6 @@
import '../compiler/js_names.dart' as js_ast;
import '../compiler/module_builder.dart';
-import '../compiler/shared_command.dart';
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
@@ -930,3 +931,112 @@
return previousValue;
});
}
+
+/// Convert a [source] string to a Uri, where the source may be a
+/// dart/file/package URI or a local win/mac/linux path.
+Uri sourcePathToUri(String source, {bool? windows}) {
+ if (windows == null) {
+ // Running on the web the Platform check will fail, and we can't use
+ // fromEnvironment because internally it's set to true for dart.library.io.
+ // So just catch the exception and if it fails then we're definitely not on
+ // Windows.
+ try {
+ windows = Platform.isWindows;
+ } catch (e) {
+ windows = false;
+ }
+ }
+ if (windows) {
+ source = source.replaceAll('\\', '/');
+ }
+
+ var result = Uri.base.resolve(source);
+ if (windows && result.scheme.length == 1) {
+ // Assume c: or similar --- interpret as file path.
+ return Uri.file(source, windows: true);
+ }
+ return result;
+}
+
+Uri sourcePathToRelativeUri(String source, {bool? windows}) {
+ var uri = sourcePathToUri(source, windows: windows);
+ if (uri.isScheme('file')) {
+ var uriPath = uri.path;
+ var root = Uri.base.path;
+ if (uriPath.startsWith(root)) {
+ return p.toUri(uriPath.substring(root.length));
+ }
+ }
+ return uri;
+}
+
+/// Adjusts the source uris in [sourceMap] to be relative uris, and returns
+/// the new map.
+///
+/// Source uris show up in two forms, absolute `file:` uris and custom
+/// [multiRootScheme] uris (also "absolute" uris, but always relative to some
+/// multi-root).
+///
+/// - `file:` uris are converted to be relative to [sourceMapBase], which
+/// defaults to the dirname of [sourceMapPath] if not provided.
+///
+/// - [multiRootScheme] uris are prefixed by [multiRootOutputPath]. If the
+/// path starts with `/lib`, then we strip that before making it relative
+/// to the [multiRootOutputPath], and assert that [multiRootOutputPath]
+/// starts with `/packages` (more explanation inline).
+///
+// TODO(#40251): Remove this logic from dev_compiler itself, push it to the
+// invokers of dev_compiler which have more knowledge about how they want
+// source paths to look.
+Map<String, Object?> placeSourceMap(Map<String, Object?> sourceMap,
+ String sourceMapPath, String? multiRootScheme,
+ {String? multiRootOutputPath, String? sourceMapBase}) {
+ var map = Map.of(sourceMap);
+ // Convert to a local file path if it's not.
+ sourceMapPath = sourcePathToUri(p.absolute(p.fromUri(sourceMapPath))).path;
+ var sourceMapDir = p.url.dirname(sourceMapPath);
+ sourceMapBase ??= sourceMapDir;
+ var list = (map['sources'] as List).toList();
+
+ String makeRelative(String sourcePath) {
+ var uri = sourcePathToUri(sourcePath);
+ var scheme = uri.scheme;
+ if (scheme == 'dart' || scheme == 'package' || scheme == multiRootScheme) {
+ if (scheme == multiRootScheme) {
+ // TODO(sigmund): extract all source-map normalization outside ddc. This
+ // custom logic is BUILD specific and could be shared with other tools
+ // like dart2js.
+ var shortPath = uri.path.replaceAll('/sdk/', '/dart-sdk/');
+ var multiRootPath = "${multiRootOutputPath ?? ''}$shortPath";
+ multiRootPath = p.url
+ .joinAll(p.split(p.relative(multiRootPath, from: sourceMapDir)));
+ return multiRootPath;
+ }
+ return sourcePath;
+ }
+
+ if (macros.isMacroLibraryUri(uri)) {
+ // TODO: https://github.com/dart-lang/sdk/issues/53913
+ return sourcePath;
+ }
+
+ if (uri.isScheme('http')) return sourcePath;
+
+ // Convert to a local file path if it's not.
+ sourcePath = sourcePathToUri(p.absolute(p.fromUri(uri))).path;
+
+ // Fall back to a relative path against the source map itself.
+ sourcePath = p.url.relative(sourcePath, from: sourceMapBase);
+
+ // Convert from relative local path to relative URI.
+ return p.toUri(sourcePath).path;
+ }
+
+ for (var i = 0; i < list.length; i++) {
+ list[i] = makeRelative(list[i] as String);
+ }
+ map['sources'] = list;
+ map['file'] =
+ map['file'] != null ? makeRelative(map['file'] as String) : null;
+ return map;
+}
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_command.dart b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
deleted file mode 100644
index cc6595d..0000000
--- a/pkg/dev_compiler/lib/src/compiler/shared_command.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2018, 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:io';
-
-import 'package:front_end/src/api_prototype/macros.dart' as macros
- show isMacroLibraryUri;
-import 'package:path/path.dart' as p;
-
-// TODO(nshahan) Merge all of this file the locations where they are used in
-// the kernel (only) version of DDC.
-
-/// Convert a [source] string to a Uri, where the source may be a
-/// dart/file/package URI or a local win/mac/linux path.
-Uri sourcePathToUri(String source, {bool? windows}) {
- if (windows == null) {
- // Running on the web the Platform check will fail, and we can't use
- // fromEnvironment because internally it's set to true for dart.library.io.
- // So just catch the exception and if it fails then we're definitely not on
- // Windows.
- try {
- windows = Platform.isWindows;
- } catch (e) {
- windows = false;
- }
- }
- if (windows) {
- source = source.replaceAll('\\', '/');
- }
-
- var result = Uri.base.resolve(source);
- if (windows && result.scheme.length == 1) {
- // Assume c: or similar --- interpret as file path.
- return Uri.file(source, windows: true);
- }
- return result;
-}
-
-Uri sourcePathToRelativeUri(String source, {bool? windows}) {
- var uri = sourcePathToUri(source, windows: windows);
- if (uri.isScheme('file')) {
- var uriPath = uri.path;
- var root = Uri.base.path;
- if (uriPath.startsWith(root)) {
- return p.toUri(uriPath.substring(root.length));
- }
- }
- return uri;
-}
-
-/// Adjusts the source uris in [sourceMap] to be relative uris, and returns
-/// the new map.
-///
-/// Source uris show up in two forms, absolute `file:` uris and custom
-/// [multiRootScheme] uris (also "absolute" uris, but always relative to some
-/// multi-root).
-///
-/// - `file:` uris are converted to be relative to [sourceMapBase], which
-/// defaults to the dirname of [sourceMapPath] if not provided.
-///
-/// - [multiRootScheme] uris are prefixed by [multiRootOutputPath]. If the
-/// path starts with `/lib`, then we strip that before making it relative
-/// to the [multiRootOutputPath], and assert that [multiRootOutputPath]
-/// starts with `/packages` (more explanation inline).
-///
-// TODO(#40251): Remove this logic from dev_compiler itself, push it to the
-// invokers of dev_compiler which have more knowledge about how they want
-// source paths to look.
-Map<String, Object?> placeSourceMap(Map<String, Object?> sourceMap,
- String sourceMapPath, String? multiRootScheme,
- {String? multiRootOutputPath, String? sourceMapBase}) {
- var map = Map.of(sourceMap);
- // Convert to a local file path if it's not.
- sourceMapPath = sourcePathToUri(p.absolute(p.fromUri(sourceMapPath))).path;
- var sourceMapDir = p.url.dirname(sourceMapPath);
- sourceMapBase ??= sourceMapDir;
- var list = (map['sources'] as List).toList();
-
- String makeRelative(String sourcePath) {
- var uri = sourcePathToUri(sourcePath);
- var scheme = uri.scheme;
- if (scheme == 'dart' || scheme == 'package' || scheme == multiRootScheme) {
- if (scheme == multiRootScheme) {
- // TODO(sigmund): extract all source-map normalization outside ddc. This
- // custom logic is BUILD specific and could be shared with other tools
- // like dart2js.
- var shortPath = uri.path.replaceAll('/sdk/', '/dart-sdk/');
- var multiRootPath = "${multiRootOutputPath ?? ''}$shortPath";
- multiRootPath = p.url
- .joinAll(p.split(p.relative(multiRootPath, from: sourceMapDir)));
- return multiRootPath;
- }
- return sourcePath;
- }
-
- if (macros.isMacroLibraryUri(uri)) {
- // TODO: https://github.com/dart-lang/sdk/issues/53913
- return sourcePath;
- }
-
- if (uri.isScheme('http')) return sourcePath;
-
- // Convert to a local file path if it's not.
- sourcePath = sourcePathToUri(p.absolute(p.fromUri(uri))).path;
-
- // Fall back to a relative path against the source map itself.
- sourcePath = p.url.relative(sourcePath, from: sourceMapBase);
-
- // Convert from relative local path to relative URI.
- return p.toUri(sourcePath).path;
- }
-
- for (var i = 0; i < list.length; i++) {
- list[i] = makeRelative(list[i] as String);
- }
- map['sources'] = list;
- map['file'] =
- map['file'] != null ? makeRelative(map['file'] as String) : null;
- return map;
-}
diff --git a/pkg/dev_compiler/test/memory_compiler.dart b/pkg/dev_compiler/test/memory_compiler.dart
index 5423cb8..ff7a177 100644
--- a/pkg/dev_compiler/test/memory_compiler.dart
+++ b/pkg/dev_compiler/test/memory_compiler.dart
@@ -4,7 +4,6 @@
import 'package:dev_compiler/dev_compiler.dart';
import 'package:dev_compiler/src/command/command.dart';
-import 'package:dev_compiler/src/compiler/shared_command.dart';
import 'package:dev_compiler/src/js_ast/js_ast.dart';
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:kernel/target/targets.dart';
diff --git a/pkg/dev_compiler/test/string_to_uri_test.dart b/pkg/dev_compiler/test/string_to_uri_test.dart
index 9534711..8213e42 100755
--- a/pkg/dev_compiler/test/string_to_uri_test.dart
+++ b/pkg/dev_compiler/test/string_to_uri_test.dart
@@ -4,7 +4,7 @@
import 'dart:io';
-import 'package:dev_compiler/src/compiler/shared_command.dart';
+import 'package:dev_compiler/src/command/command.dart';
import 'package:test/test.dart';
void main(List<String> args) {
diff --git a/pkg/front_end/lib/src/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
index d98fcc4..96e174d 100644
--- a/pkg/front_end/lib/src/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
@@ -91,7 +91,9 @@
respectImplicitlyTypedVarInitializers:
libraryBuilder.libraryFeatures.constructorTearoffs.isEnabled,
fieldPromotionEnabled:
- libraryBuilder.libraryFeatures.inferenceUpdate2.isEnabled);
+ libraryBuilder.libraryFeatures.inferenceUpdate2.isEnabled,
+ inferenceUpdate4Enabled:
+ libraryBuilder.libraryFeatures.inferenceUpdate4.isEnabled);
@override
final AssignedVariables<TreeNode, VariableDeclaration> assignedVariables;
diff --git a/pkg/json/pubspec.yaml b/pkg/json/pubspec.yaml
index 98a28ee..d58a51b 100644
--- a/pkg/json/pubspec.yaml
+++ b/pkg/json/pubspec.yaml
@@ -7,7 +7,7 @@
repository: https://github.com/dart-lang/sdk/tree/main/pkg/json
version: 0.20.2
environment:
- sdk: ^3.6.0-edge
+ sdk: ^3.7.0-edge
dependencies:
macros: ^0.1.0-main.5
dev_dependencies:
diff --git a/pkg/kernel/lib/default_language_version.dart b/pkg/kernel/lib/default_language_version.dart
index c5a8085..5fa54ec 100644
--- a/pkg/kernel/lib/default_language_version.dart
+++ b/pkg/kernel/lib/default_language_version.dart
@@ -9,4 +9,4 @@
import "ast.dart";
-const Version defaultLanguageVersion = const Version(3, 6);
+const Version defaultLanguageVersion = const Version(3, 7);
diff --git a/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart b/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart
index 4dcd451..3f48868 100644
--- a/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart
+++ b/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart
@@ -67,7 +67,7 @@
static bool _isRegExpInstanceCreation(AstNode? node) {
if (node is InstanceCreationExpression) {
var constructorElement = node.constructorName.element;
- return constructorElement?.enclosingElement2?.name == 'RegExp';
+ return constructorElement?.enclosingElement2.name == 'RegExp';
}
return false;
}
diff --git a/pkg/linter/lib/src/rules/valid_regexps.dart b/pkg/linter/lib/src/rules/valid_regexps.dart
index cc216bb..7cd9250 100644
--- a/pkg/linter/lib/src/rules/valid_regexps.dart
+++ b/pkg/linter/lib/src/rules/valid_regexps.dart
@@ -38,7 +38,7 @@
var element = node.constructorName.element?.enclosingElement2;
if (element == null) return;
- if (element.name == 'RegExp' && (element.library2?.isDartCore ?? false)) {
+ if (element.name == 'RegExp' && element.library2.isDartCore) {
var args = node.argumentList.arguments;
if (args.isEmpty) return;
diff --git a/pkg/vm_service/test/wildcard_test.dart b/pkg/vm_service/test/wildcard_test.dart
index 7d1ebe5..3d87728 100644
--- a/pkg/vm_service/test/wildcard_test.dart
+++ b/pkg/vm_service/test/wildcard_test.dart
@@ -5,7 +5,7 @@
// SharedOptions=--enable-experiment=wildcard-variables
// ignore: invalid_language_version_override
-// @dart = 3.6
+// @dart = 3.7
import 'dart:developer';
// Tests wildcard import prefixes.
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index b2582ca..c06e619 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -10,6 +10,7 @@
import("../vm/compiler/compiler_sources.gni")
import("../vm/ffi/ffi_sources.gni")
import("../vm/heap/heap_sources.gni")
+import("../vm/regexp/regexp_sources.gni")
import("../vm/vm_sources.gni")
import("builtin_impl_sources.gni")
import("builtin_sources.gni")
@@ -1055,6 +1056,7 @@
vm_tests = rebase_path(vm_sources_tests, ".", "../vm")
compiler_tests = rebase_path(compiler_sources_tests, ".", "../vm/compiler")
heap_tests = rebase_path(heap_sources_tests, ".", "../vm/heap")
+ regexp_tests = rebase_path(regexp_sources_tests, ".", "../vm/regexp")
sources = [
"builtin.cc",
@@ -1072,7 +1074,7 @@
"vmservice_impl.cc",
"vmservice_impl.h",
] + builtin_impl_tests + vm_tests + compiler_tests + heap_tests +
- io_impl_tests
+ regexp_tests + io_impl_tests
}
executable("run_vm_tests") {
diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc
index 3fd92bf..1852c51 100644
--- a/runtime/lib/regexp.cc
+++ b/runtime/lib/regexp.cc
@@ -9,14 +9,14 @@
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
-#include "vm/regexp_assembler_bytecode.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_assembler_bytecode.h"
+#include "vm/regexp/regexp_parser.h"
#include "vm/reusable_handles.h"
#include "vm/symbols.h"
#include "vm/thread.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
#endif // !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
diff --git a/runtime/tools/profiling/.gitignore b/runtime/tools/profiling/.gitignore
new file mode 100644
index 0000000..3a85790
--- /dev/null
+++ b/runtime/tools/profiling/.gitignore
@@ -0,0 +1,3 @@
+# https://dart.dev/guides/libraries/private-files
+# Created by `dart pub`
+.dart_tool/
diff --git a/runtime/tools/profiling/CHANGELOG.md b/runtime/tools/profiling/CHANGELOG.md
new file mode 100644
index 0000000..a0712a7
--- /dev/null
+++ b/runtime/tools/profiling/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.1.0
+
+- Initial version.
diff --git a/runtime/tools/profiling/README.md b/runtime/tools/profiling/README.md
new file mode 100644
index 0000000..53a31e6
--- /dev/null
+++ b/runtime/tools/profiling/README.md
@@ -0,0 +1,64 @@
+Various tools for low level profing of code running on the Dart VM.
+
+# Uprobe based profiling
+
+[uprobes](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html) is
+a user-space dynamic tracing mechanism. Using this mechanism the kernel can
+be instructed to place a tracepoint at a particular file offset within a
+specific binary. Whenever this tracepoint is hit the kernel will fetch values
+from the execution context based on the uprobe's description and emit an event.
+A developer can subscribe to uprobe events in a few different ways including
+[perf_event_open](https://man7.org/linux/man-pages/man2/perf_event_open.2.html)
+syscall. uprobes have been enabled by default on all newish Linux kernels
+(4.14+), however they are only truly usable on Android/ARM64 starting from
+5.10+. `bin/set_uprobe.dart` is a helper script for placing uprobes inside
+binaries and using this for profiling.
+
+The core workflow looks like this:
+
+```console
+$ sudo $(which dart) runtime/tools/profiling/bin/set_uprobe.dart probeName symbol binary
+```
+
+This will create an uprobe with name `probeName` which triggers whenever
+the given `symbol` inside the given `binary` is called. You can then record
+an event (and collect the call stack) using:
+
+```console
+$ sudo perf record -g -e uprobes:probeName ...
+```
+
+## Allocation profiling with uprobes
+
+AOT compiler can emit a special probe point (`stub AllocationProbePoint`) which
+triggers for each new space allocation from generated code. `set_uprobe` script
+has special support for this probe point: it will configure probe point to
+record additional information (address of allocated object, allocation top and
+cid of the allocated object) allowing to post process collected data into
+an actual allocation profile.
+
+Start by compiling your application with `--generate-probe-points`:
+
+```console
+$ pkg/vm/tool/precompiler2 --generate-probe-points test.dart test.aot
+```
+
+Then install uprobe on `AllocationProbePoint`:
+
+```console
+$ sudo $(which dart) runtime/tools/profiling/bin/set_uprobe.dart alloc AllocationProbePoint test.aot
+```
+
+Record the profile:
+
+```
+$ sudo perf record -g -e uprobes:alloc out/ReleaseX64/dart_precompiled_runtime test.aot
+$ sudo chmod 0755 perf.data
+```
+
+Produce a coalesced allocation profile from the recording:
+
+```
+$ dart runtime/tools/profiling/bin/convert_allocation_profile.dart perf.data
+$ pprof -flame pprof.profile
+```
\ No newline at end of file
diff --git a/runtime/tools/profiling/analysis_options.yaml b/runtime/tools/profiling/analysis_options.yaml
new file mode 100644
index 0000000..dee8927
--- /dev/null
+++ b/runtime/tools/profiling/analysis_options.yaml
@@ -0,0 +1,30 @@
+# This file configures the static analysis results for your project (errors,
+# warnings, and lints).
+#
+# This enables the 'recommended' set of lints from `package:lints`.
+# This set helps identify many issues that may lead to problems when running
+# or consuming Dart code, and enforces writing Dart using a single, idiomatic
+# style and format.
+#
+# If you want a smaller set of lints you can change this to specify
+# 'package:lints/core.yaml'. These are just the most critical lints
+# (the recommended set includes the core lints).
+# The core lints are also what is used by pub.dev for scoring packages.
+
+include: package:lints/recommended.yaml
+
+# Uncomment the following section to specify additional rules.
+
+# linter:
+# rules:
+# - camel_case_types
+
+# analyzer:
+# exclude:
+# - path/to/excluded/files/**
+
+# For more information about the core and recommended set of lints, see
+# https://dart.dev/go/core-lints
+
+# For additional information about configuring this file, see
+# https://dart.dev/guides/language/analysis-options
diff --git a/runtime/tools/profiling/bin/convert_allocation_profile.dart b/runtime/tools/profiling/bin/convert_allocation_profile.dart
new file mode 100644
index 0000000..11ef149
--- /dev/null
+++ b/runtime/tools/profiling/bin/convert_allocation_profile.dart
@@ -0,0 +1,537 @@
+// Copyright (c) 2024, 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:io';
+import 'dart:typed_data';
+import 'dart:ffi';
+
+import 'package:fixnum/fixnum.dart' hide Int32;
+
+import 'package:profiling/src/perf/perf_data.dart';
+import 'package:profiling/src/symbols.dart';
+import 'package:profiling/src/pprof/generated/profile.pb.dart' as pprof;
+
+/// `PERF_RECORD_SAMPLE` with the following optional fields:
+///
+/// `PERF_SAMPLE_IP`, `PERF_SAMPLE_TID`, `PERF_SAMPLE_TIME`, `PERF_SAMPLE_CALLCHAIN`,
+/// `PERF_SAMPLE_CPU`, `PERF_SAMPLE_PERIOD`, `PERF_SAMPLE_RAW`.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L947
+final class SampleEvent extends Struct {
+ external EventHeader header;
+
+ /// Enabled by `PERF_SAMPLE_IP`
+ @Uint64()
+ external int ip;
+
+ /// Enabled by `PERF_SAMPLE_TID`
+ @Uint32()
+ external int pid;
+
+ /// Enabled by `PERF_SAMPLE_TID`
+ @Uint32()
+ external int tid;
+
+ /// Enabled by `PERF_SAMPLE_TIME`
+ @Uint64()
+ external int time;
+
+ /// Enabled by `PERF_SAMPLE_CPU`
+ @Uint32()
+ external int cpu;
+
+ /// Enabled by `PERF_SAMPLE_CPU`
+ @Uint32()
+ external int res;
+
+ /// Enabled by `PERF_SAMPLE_PERIOD`
+ @Uint64()
+ external int period;
+
+ /// Enabled by `PERF_SAMPLE_CALLCHAIN`
+ @Uint64()
+ external int nr;
+
+ /// Enabled by `PERF_SAMPLE_CALLCHAIN`
+ @Array.variable()
+ external Array<Uint64> ips;
+}
+
+/// Data recorded by the probe stored in a `PERF_SAMPLE_RAW`.
+///
+/// The `size` field is part of `PERF_SAMPLE_RAW` encoding the rest are
+/// part of probe data itself. Format for the recorded data can be recovered
+/// by loading tracepoint information from an optional section identified
+/// by `HEADER_TRACING_DATA` ([OptionalSection.tracingData]). However encoding
+/// of that section is extremely bespoke (see `trace-event-read.c` below), so
+/// instead of fiddling with that we simply hardcode expected format of the
+/// probe. This obviously needs to be kept in sync with `set_uprobe.dart`
+/// script.
+///
+/// ```
+/// $ sudo cat /sys/kernel/tracing/events/uprobes/alloc/format
+/// name: alloc
+/// ID: 1976
+/// format:
+/// field:unsigned short common_type; offset:0; size:2; signed:0;
+/// field:unsigned char common_flags; offset:2; size:1; signed:0;
+/// field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+/// field:int common_pid; offset:4; size:4; signed:1;
+///
+/// field:unsigned long __probe_ip; offset:8; size:8; signed:0;
+/// field:s64 addr; offset:16; size:8; signed:1;
+/// field:s64 top; offset:24; size:8; signed:1;
+/// field:u32 cid; offset:32; size:4; signed:0;
+/// print fmt: "(%lx) addr=%Ld top=%Ld cid=%u", REC->__probe_ip, REC->addr, REC->top, REC->cid
+/// ```
+///
+/// [^1]: https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/trace-event-read.c#L375
+@Packed(1)
+final class ProbeData extends Struct {
+ @Uint32()
+ external int size;
+
+ @Uint16()
+ external int commonType;
+
+ @Uint8()
+ external int commonFlags;
+
+ @Uint8()
+ external int commonPreemptCount;
+
+ @Int32()
+ external int commonPid;
+
+ @Uint64()
+ external int probeIp;
+
+ @Uint64()
+ external int addr;
+
+ @Uint64()
+ external int top;
+
+ @Uint32()
+ external int cid;
+
+ @override
+ String toString() =>
+ 'Probe{addr=${addr.formatAsAddress()},top=${top.formatAsAddress()},cid=$cid}';
+}
+
+/// Lazily populated mapping between file offsets in a binary and profile
+/// location ids.
+///
+/// This class handles convertion of the file offset to the corresponding
+/// symbol name and futher into corresponding location id inside the profile.
+final class SymbolsIndex {
+ final ProfileBuilder profileBuilder;
+
+ final Symbols symbols;
+ final List<Int64?> ids;
+
+ SymbolsIndex(this.profileBuilder, this.symbols)
+ : ids = List<Int64?>.filled(symbols.fileOffsets.length, null);
+
+ static final lineRe =
+ RegExp(r"^(?<addr>[0-9a-f]+)\s+(?<typ>\w+)\s+(?<name>.*)$");
+
+ /// Return location id corresponding to the given [fileOffset].
+ ///
+ /// This function will lazily allocate new ids as necessary by
+ /// calling [ProfileBuilder.addSymbol].
+ Int64? symbolId(int fileOffset) {
+ final index = symbols.symbolIndex(fileOffset);
+ if (index != null) {
+ return (ids[index] ??= profileBuilder.addSymbol(symbols.names[index]));
+ }
+ return null;
+ }
+}
+
+final class Mapping {
+ final int baseAddress;
+ final int length;
+ final String path;
+ final int offset;
+
+ Mapping({
+ required this.baseAddress,
+ required this.length,
+ required this.path,
+ required this.offset,
+ });
+}
+
+/// Symbols information for the whole address space.
+final class AddressSpaceSymbols {
+ /// Base addresses for mapping ranges.
+ ///
+ /// To simplify search we also add ranges that don't have any symbols here.
+ /// Consider for example that we have two mappings `[A, A')` and `[B, B')`
+ /// with symbols (`Sym(A)` and `Sym(B)` respectively). In this case:
+ /// * [baseAddresses] will contain `[0, A, A', B, B']` and
+ /// * [symbolsIndexes] will contain `[null, SA, null, SymB, null]`.
+ final Int64List baseAddresses;
+
+ /// Symbol indexes corresponding to mappings in [baseAddresses].
+ final List<SymbolsIndex?> symbolsIndexes;
+
+ /// File offsets corresponding to mappings in [baseAddresses].
+ final Int64List fileOffsets;
+
+ AddressSpaceSymbols._(
+ this.baseAddresses, this.symbolsIndexes, this.fileOffsets);
+
+ Int64? symbolId(int address) {
+ // We use linear search because we assume the number of mappings
+ // is very small (~2).
+ final limit = baseAddresses.length - 1;
+ for (var i = 0; i < limit; i++) {
+ final start = baseAddresses[i];
+ final end = baseAddresses[i + 1];
+ if (start <= address && address < end) {
+ final fileOffset = address - start + fileOffsets[i];
+ return symbolsIndexes[i]?.symbolId(fileOffset);
+ }
+ }
+ return null;
+ }
+
+ /// Construct [AddressSpaceSymbols] from [Mapping] records loaded from
+ /// `perf.data`.
+ static AddressSpaceSymbols fromMappings(
+ List<Mapping> mappings, ProfileBuilder profileBuilder) {
+ // Try loading symbols for each mapping and keep those that
+ // actually have symbols. Sort resulting list by base address.
+ final mappingsWithSymbols = <(Mapping, SymbolsIndex)>[];
+ for (var event in mappings) {
+ final symbolsIndex = profileBuilder.symbolsIndexFor(event.path);
+ if (symbolsIndex != null) {
+ mappingsWithSymbols.add((event, symbolsIndex));
+ }
+ }
+ mappingsWithSymbols
+ .sort((a, b) => a.$1.baseAddress.compareTo(b.$1.baseAddress));
+
+ // Build `AddressSpaceSymbols` from mappings with symbols.
+ //
+ // Note: we need to accomodate for a situation when two mappings are
+ // adjacent. However we assume that number of mappings is rather small
+ // so we don't optimize this code too much.
+ final result = <({int baseAddress, SymbolsIndex? index, int fileOffset})>[];
+ void addEntry({
+ required int baseAddress,
+ required SymbolsIndex? index,
+ required int fileOffset,
+ }) {
+ if (result.isNotEmpty && result.last.baseAddress == baseAddress) {
+ // Collapse end of the previous mapping and the start of the new
+ // mapping.
+ if (result.last.index != null) {
+ throw StateError('Unexpected intersection of address ranges');
+ }
+ result.removeLast();
+ }
+ result.add(
+ (baseAddress: baseAddress, index: index, fileOffset: fileOffset));
+ }
+
+ addEntry(baseAddress: 0, index: null, fileOffset: 0);
+ for (var e in mappingsWithSymbols) {
+ addEntry(
+ baseAddress: e.$1.baseAddress,
+ index: e.$2,
+ fileOffset: e.$1.offset,
+ );
+ addEntry(
+ baseAddress: e.$1.baseAddress + e.$1.length,
+ index: null,
+ fileOffset: 0,
+ );
+ }
+
+ // Split result into individual components.
+ return AddressSpaceSymbols._(
+ Int64List.fromList(
+ result.map((e) => e.baseAddress).toList(growable: false),
+ ),
+ result.map((e) => e.index).toList(growable: false),
+ Int64List.fromList(
+ result.map((e) => e.fileOffset).toList(growable: false),
+ ),
+ );
+ }
+}
+
+/// A trie node representing a callstack frame.
+///
+/// To minimize the size of the produced `pprof.profile` we collapse all
+/// matching callstacks into a single `pprof.Sample` entry in the profile.
+/// This is done by through a simple [trie][1] data structure.
+///
+/// Nodes corresponding to callstacks from original profile will have not-null
+/// non-zero [totalBytes] associated with them. Path to these nodes should
+/// be flushed into [pprof.Profile] as individual [pprof.Sample] entries
+/// at the end of conversion. See [flushTo].
+///
+/// [1]: https://en.wikipedia.org/wiki/Trie
+final class CallStackTrieNode {
+ /// [pprof.Profile] location id corresponding to this frame.
+ final Int64 id;
+
+ /// Total number of bytes allocated by this frame.
+ int totalBytes = 0;
+
+ /// Callees of this frame.
+ late final Map<Int64, CallStackTrieNode> children =
+ <Int64, CallStackTrieNode>{};
+
+ CallStackTrieNode({required this.id});
+
+ CallStackTrieNode operator [](Int64 id) =>
+ children[id] ??= CallStackTrieNode(id: id);
+
+ void flushTo(pprof.Profile profile, List<Int64> path) {
+ if (totalBytes != 0) {
+ profile.sample.add(
+ pprof.Sample(locationId: path.reversed)..value.add(Int64(totalBytes)),
+ );
+ }
+ for (var child in children.values) {
+ path.add(child.id);
+ child.flushTo(profile, path);
+ path.removeLast();
+ }
+ }
+}
+
+/// Helper for building [pprof.Profile].
+///
+/// It takes care of indexing symbols and managing their ids.
+final class ProfileBuilder {
+ final profile = pprof.Profile();
+
+ final symbolTable = <String, Int64>{};
+ final locationIds = <String, Int64>{};
+
+ final callStackTrieRoot = CallStackTrieNode(id: Int64(-1));
+
+ ProfileBuilder() {
+ addString("");
+ profile.sampleType.add(pprof.ValueType(
+ type: addString('space'),
+ unit: addString('bytes'),
+ ));
+ }
+
+ Int64 addString(String str) {
+ var id = symbolTable[str];
+ if (id != null) {
+ return id;
+ }
+ id = symbolTable[str] = Int64(symbolTable.length);
+ profile.stringTable.add(str);
+ return id;
+ }
+
+ Int64 addSymbol(String symbol) {
+ var id = locationIds[symbol];
+ if (id != null) {
+ return id;
+ }
+ id = locationIds[symbol] = Int64(locationIds.length + 1);
+ profile.function.add(pprof.Function_(id: id, name: addString(symbol)));
+ profile.location
+ .add(pprof.Location(id: id, line: [pprof.Line(functionId: id)]));
+ return id;
+ }
+
+ SymbolsIndex? symbolsIndexFor(String path) {
+ final symbols = Symbols.load(path);
+ if (symbols == null) {
+ return null;
+ }
+ return SymbolsIndex(this, symbols);
+ }
+
+ pprof.Profile finishProfile() {
+ callStackTrieRoot.flushTo(profile, []);
+ return profile;
+ }
+}
+
+/// Helper for converting raw callstack into its symbolized form.
+///
+/// We assume that callstack for each new sample usually shares its prefix
+/// (e.g. outermost callers, like `main`) with the previously processed
+/// sample. This allows us to reuse location ids from the previous sample
+/// for the large portion of the stack.
+final class SymbolizedCallStackBuilder {
+ /// Depth of the current stack.
+ int depth = 0;
+
+ /// Raw addresses for each frame in the caller to callee order.
+ ///
+ /// We do not clear this array between samples allowing us to detect
+ /// situations when we can reuse entries. Only entries `0..depth-1`
+ /// correspond to the current stack. Other entries originate from
+ /// previous samples and might be out of sync with the current sample.
+ ///
+ /// (`0` is the outermost caller, `1` is its callee, etc).
+ final pcs = Int64List(200);
+
+ /// Trie nodes corresponding to each frame in the stack.
+ ///
+ /// For entries in the `0..depth-2` range `nodes[i]` is parent of
+ /// `nodes[i+1]` .
+ ///
+ final nodes = List<CallStackTrieNode?>.filled(200, null);
+
+ /// Trie node for the last frame (either `nodes[depth-1]` or root trie node
+ /// if [depth] is `0`).
+ CallStackTrieNode last;
+
+ /// `true` when the callstack which is currently being built matches
+ /// the prefix of the previous callstack.
+ bool prefixMatches = true;
+
+ SymbolizedCallStackBuilder(ProfileBuilder builder)
+ : last = builder.callStackTrieRoot;
+
+ void add(int pc, AddressSpaceSymbols syms) {
+ if (pcs[depth] != pc) {
+ // Mismatch between newly added `pc` and the `pc` we have from the
+ // previous sample. We need to lookup location id for it.
+ final id = syms.symbolId(pc);
+ if (id == null) {
+ // No symbol - drop the frame.
+ return;
+ }
+
+ pcs[depth] = pc;
+ last = last[id];
+
+ // We might still hit the same node in the trie.
+ if (nodes[depth] != last) {
+ nodes[depth] = last;
+ // From here onward we can't reuse `nodes` because the path has
+ // diverged.
+ prefixMatches = false;
+ }
+ } else if (!prefixMatches) {
+ // Address might match - but the path we got here might be different. This
+ // means we can reuse `id` from the node, but not the node itself.
+ final id = nodes[depth]!.id;
+ nodes[depth] = last = last[id];
+ } else {
+ // This pc *and* the all previous nodes match. We can just reuse
+ // the node.
+ last = nodes[depth]!;
+ }
+ depth++;
+ }
+
+ void addTo(ProfileBuilder builder, int allocatedBytes) {
+ last.totalBytes += allocatedBytes;
+ }
+
+ void reset(ProfileBuilder builder) {
+ depth = 0;
+ last = builder.callStackTrieRoot;
+ prefixMatches = true;
+ }
+}
+
+@pragma('vm:never-inline')
+pprof.Profile buildProfileFromPerfData(String path) {
+ final raf = File(path).openSync();
+
+ final profileBuilder = ProfileBuilder();
+ final perfData = PerfData(raf);
+
+ // Check that input file has expected format.
+ final allAttrs = perfData.readAttrs();
+ if (allAttrs.length != 1) {
+ perfData.reportError(
+ 'Expected single perf_event_attrs structure, got ${allAttrs.length}');
+ }
+
+ final attrs = allAttrs.first;
+
+ if (attrs.type != TypeId.tracepoint) {
+ perfData.reportError('Expected to find a file with tracepoint events');
+ }
+
+ const expectedSampleFormat = SampleFormat.ip |
+ SampleFormat.tid |
+ SampleFormat.time |
+ SampleFormat.callchain |
+ SampleFormat.cpu |
+ SampleFormat.period |
+ SampleFormat.raw;
+ if (attrs.sampleType != expectedSampleFormat) {
+ perfData.reportError(
+ 'Expected to sample format ${SampleFormat.format(expectedSampleFormat)}'
+ ' got ${SampleFormat.format(attrs.sampleType)}: difference '
+ '${SampleFormat.format(attrs.sampleType ^ expectedSampleFormat)}');
+ }
+
+ final mappings = <Mapping>[];
+ perfData.readEvents((type, chunk, pos) {
+ if (type == EventType.mmap2) {
+ final event = Struct.create<Mmap2Event>(chunk, pos);
+ mappings.add(Mapping(
+ baseAddress: event.addr,
+ length: event.len,
+ path: event.filename.toStringFromZeroTerminated(),
+ offset: event.pgoffs,
+ ));
+ } else if (type == EventType.sample && mappings.isNotEmpty) {
+ // TODO: we miss one sample here.
+ return false; // Break iteration.
+ }
+ return true;
+ });
+
+ final syms = AddressSpaceSymbols.fromMappings(mappings, profileBuilder);
+ final stack = SymbolizedCallStackBuilder(profileBuilder);
+ perfData.readEvents((type, chunk, pos) {
+ if (type == EventType.sample) {
+ final sample = Struct.create<SampleEvent>(chunk, pos);
+ final probeData = Struct.create<ProbeData>(
+ chunk, pos + sizeOf<SampleEvent>() + sample.nr * 8);
+
+ for (var i = sample.nr - 1; i > 1; i--) {
+ stack.add(sample.ips[i], syms);
+ }
+ if (stack.depth > 0) {
+ // Accumulate [totalBytes] in the last node.
+ stack.last.totalBytes += probeData.top - probeData.addr - 1;
+ }
+
+ // Reset the stack for the next sample.
+ stack.reset(profileBuilder);
+ }
+
+ return true;
+ });
+
+ print('All data loaded - creating profile.');
+ return profileBuilder.finishProfile();
+}
+
+void main(List<String> args) async {
+ final perfDataPath = args[0];
+ print('loading $perfDataPath');
+ final profile = buildProfileFromPerfData(perfDataPath);
+ print('created ${profile.sample.length} samples');
+
+ print('Serializing proto (pprof.profile)');
+ final result = profile.writeToBuffer();
+ print('... ${result.length} bytes');
+ File('pprof.profile').writeAsBytesSync(result);
+ print('Done');
+}
diff --git a/runtime/tools/profiling/bin/set_uprobe.dart b/runtime/tools/profiling/bin/set_uprobe.dart
new file mode 100644
index 0000000..811767e
--- /dev/null
+++ b/runtime/tools/profiling/bin/set_uprobe.dart
@@ -0,0 +1,135 @@
+// Copyright (c) 2024, 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:io';
+
+import 'package:path/path.dart' as p;
+
+import 'package:profiling/src/elf_utils.dart';
+
+// TODO(vegorov): update this to support Android ARM64 both for standalone
+// binaries and Flutter applications. Prototype code for that is available
+// in https://dart-review.googlesource.com/c/sdk/+/239661.
+void main(List<String> args) async {
+ if (args.length != 3) {
+ print(
+ 'Usage: pkg/vm/tool/set_uprobe.dart <probe-name> <symbol> <AOT snapshot SO file>');
+ exit(-1);
+ }
+
+ final [probeName, symbol, sharedObject] = args;
+
+ final uprobeAddress =
+ await _computeProbesVirtualAddress(sharedObject, symbol);
+ final loadingBias = loadingBiasOf(sharedObject);
+
+ final uprobeFileOffset = (uprobeAddress + loadingBias).toRadixString(16);
+
+ final soName = p.basename(sharedObject);
+ final soPath = p.canonicalize(p.absolute(sharedObject));
+
+ // TODO(vegorov) ARM64 support
+ final threadRegister = "r14";
+ final resultRegister = "ax";
+
+ final uprobeFormat = symbol == 'AllocationProbePoint'
+ ? 'addr=%$resultRegister:s64 top=+${await _getThreadTopOffset()}(%$threadRegister):s64 cid=-1(%$resultRegister):b20@12/32'
+ : '';
+
+ final probe = 'p:$probeName $soPath:0x$uprobeFileOffset $uprobeFormat';
+ print(probe);
+
+ File('/sys/kernel/tracing/uprobe_events').writeAsStringSync(probe);
+}
+
+Future<int> _computeProbesVirtualAddress(
+ String sharedObject, String targetSymbol) async {
+ int offset = 0;
+ if (targetSymbol == 'AllocationProbePoint') {
+ offset = await _determineAllocProbeOffset(sharedObject);
+ }
+
+ final targetRe = RegExp('\\b$targetSymbol\\b');
+ final matches = <String, int>{
+ for (final (:addr, :name) in textSymbolsOf(sharedObject))
+ if (targetRe.hasMatch(name)) name: addr,
+ };
+
+ if (matches.isEmpty) {
+ throw 'Symbol $targetSymbol not found in $sharedObject';
+ }
+
+ if (matches.length != 1) {
+ throw 'Multiple symbols match: ${matches.keys}';
+ }
+
+ final entry = matches.entries.single;
+ print('placing uprobe on ${entry.key} at '
+ '0x${entry.value.toRadixString(16)}+$offset');
+ return entry.value + offset;
+}
+
+// `AllocationProbePoint` stub should have a probe placed at a place where
+// stack frame is properly setup so that unwinding succeeds. The stub itself
+// contains a dummy test immediate instruction which encodes the offset at
+// which the probe should be placed.
+Future<int> _determineAllocProbeOffset(String sharedObject) async {
+ // Dump SO file to get the address of the interesting symbol.
+ final disassembly = await _exec('llvm-objdump', [
+ '--disassemble-symbols=stub AllocationProbePoint',
+ '-Mintel',
+ sharedObject,
+ ]);
+
+ // We are looking for `test al, imm` or `tst x0, #imm` where `imm` is a
+ // hexadecimal immediate encoding offset to the probe point within the stub.
+ final pattern = RegExp(
+ r'^\s+[a-f0-9]+:(( [a-f0-9]{2})+| [a-f0-9]{8})\s+(test|tst)\s+(al|x0),\s+#?0x(?<offset>[0-9a-f]+)\s*$',
+ multiLine: true);
+
+ final match = pattern.firstMatch(disassembly);
+ if (match == null) {
+ print(disassembly);
+ throw StateError(
+ 'failed to find test-immediate instruction encoding the probe offset');
+ }
+
+ return int.parse(match.namedGroup('offset')!, radix: 16);
+}
+
+Future<String> _getThreadTopOffset() async {
+ // TODO(vegorov) ARM64 support
+ final sdkSrc = Platform.script.resolve('../../../..').toFilePath();
+ await _exec(
+ 'ninja', ['-C', 'out/ReleaseX64', '-j1000', '-l64', 'offsets_extractor'],
+ workingDirectory: sdkSrc);
+ final offsets =
+ await _exec(p.join(sdkSrc, 'out/ReleaseX64/offsets_extractor'), []);
+ final line = offsets
+ .split('\n')
+ .firstWhere((line) => line.contains('Thread_top_offset'));
+ final offset = RegExp(r' = (?<offset>0x[a-f\d]+);$')
+ .firstMatch(line)!
+ .namedGroup('offset')!;
+
+ return int.parse(offset).toString();
+}
+
+Future<String> _exec(String executable, List<String> args,
+ {String? workingDirectory}) async {
+ final result =
+ await Process.run(executable, args, workingDirectory: workingDirectory);
+ if (result.exitCode != 0) {
+ throw StateError('''
+Failed to run $executable ${args.join(' ')}
+stdout:
+${result.stdout}
+
+stderr:
+
+${result.stderr}
+''');
+ }
+ return result.stdout as String;
+}
diff --git a/runtime/tools/profiling/lib/src/elf_utils.dart b/runtime/tools/profiling/lib/src/elf_utils.dart
new file mode 100644
index 0000000..5ba8573
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/elf_utils.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2024, 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:io';
+
+/// Compute the difference between virtual address and the file offset of the
+/// TEXT section. It can be used to convert virtual addresses into
+/// file offsets.
+int loadingBiasOf(String path) {
+ final data = Process.runSync('llvm-readelf', ['-l', path]).stdout.split("\n");
+ for (var line in data) {
+ line = line.trim();
+ if (line.startsWith("LOAD") && line.contains("R E")) {
+ final components = line.split(RegExp(r"\s+"));
+ final fileOffset = int.parse(components[1]);
+ final virtAddr = int.parse(components[2]);
+ return virtAddr - fileOffset;
+ }
+ }
+ throw StateError('Unable to determine loading bias for $path');
+}
+
+/// Iterate over all symbols in TEXT section of the given binary.
+Iterable<({int addr, String name})> textSymbolsOf(String path) {
+ // Run `nm -C` on a binary to extract demangled (-C) symbols.
+ final output = Process.runSync('/usr/bin/nm', ['-C', path]);
+ final result = (output.stdout as String).split('\n');
+ if (output.exitCode != 0) throw 'failed to run nm';
+
+ // Parse `nm` output looking for `t` (TEXT) symbols. Each line
+ // has the following format:
+ final lineRe = RegExp(r"^(?<addr>[0-9a-f]+)\s+(?<typ>\w+)\s+(?<name>.*)$");
+ // final symbols = <(int, String)>[];
+ return result.map((line) {
+ final m = lineRe.firstMatch(line);
+ if (m != null && m.namedGroup('typ') == 't') {
+ final addr = int.parse(m.namedGroup('addr')!, radix: 16);
+ final name = m.namedGroup('name')!;
+ return (addr: addr, name: name);
+ }
+ return null;
+ }).nonNulls;
+}
diff --git a/runtime/tools/profiling/lib/src/perf/perf_data.dart b/runtime/tools/profiling/lib/src/perf/perf_data.dart
new file mode 100644
index 0000000..c5c0c5c
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/perf/perf_data.dart
@@ -0,0 +1,630 @@
+// Copyright (c) 2024, 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.
+
+/// This library file contains data structures and helper methods for parsing
+/// `perf.data` files produced by `perf` tool on Linux.
+///
+/// Format of this file is documented in:
+///
+/// * https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/Documentation/perf.data-file-format.txt
+/// * https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h
+/// * https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h
+///
+library;
+
+import 'dart:ffi';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// `struct perf_header`: header of the `perf.data` file.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h#L64
+final class Header extends Struct {
+ @Array(8)
+ external Array<Uint8> magic;
+
+ @Uint64()
+ external int size;
+
+ @Uint64()
+ external int attrSize;
+
+ external FileSection attrs;
+ external FileSection data;
+ external FileSection eventTypes;
+
+ @Uint64()
+ external int flags;
+
+ @Array(3)
+ external Array<Uint64> flags1;
+}
+
+/// `struct perf_file_section`: section inside `perf.data` file.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h#L59
+final class FileSection extends Struct {
+ @Uint64()
+ external int offset;
+
+ @Uint64()
+ external int size;
+
+ @override
+ String toString() {
+ return 'PerfFileSection{offset=$offset,size=$size}';
+ }
+}
+
+/// Optional sections inside `perf.data` file.
+///
+/// The section is present iff corresponding bit in [PerfHeader.flags] is set.
+///
+/// `PerfFileSection` descriptors for present sections will follow in sequence
+/// immediately after the data section (i.e. the first `PerfFileSection` will
+/// be located at `header.data.offset + header.data.size` offset).
+///
+/// See [PerfData.readOptionalSectionHeaders].
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h#L15
+enum OptionalSection {
+ reserved,
+ tracingData,
+ buildId,
+ hostname,
+ osRelease,
+ version,
+ arch,
+ nrCpus,
+ cpuDesc,
+ cpuId,
+ totalMem,
+ cmdLine,
+ eventDesc,
+ cpuTopology,
+ numaTopology,
+ branchStack,
+ groupDesc,
+ auxTrace,
+ stat,
+ cache,
+ sampleTime,
+ sampleTopology,
+ clockId,
+ dirFormat,
+ bpfProgInfo,
+ bpfBtf,
+ compressed,
+ cpuPmuCaps,
+ clockData,
+ hybridTopology,
+ pmuCaps
+}
+
+/// `perf_event_header`: common header of all event entries.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L815
+final class EventHeader extends Struct {
+ @Uint32()
+ external int type;
+
+ @Uint16()
+ external int misc;
+
+ @Uint16()
+ external int size;
+
+ @override
+ String toString() => 'PerfEventHeader{type=$type,misc=$misc,size=$size}';
+}
+
+/// `perf_event_attr`: configuration of the event monitored by `perf`.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L389
+final class EventAttr extends Struct {
+ /// Major type: hardware/software/tracepoint/etc.
+ ///
+ /// See [EventType].
+ @Uint32()
+ external int type;
+
+ @Uint32()
+ external int size;
+
+ /// Type specific configuration information.
+ @Uint64()
+ external int config;
+
+ @Uint64()
+ external int samplePeriodOrFreq;
+
+ @Uint64()
+ external int sampleType;
+
+ @Uint64()
+ external int readFormat;
+
+ /// Various bit fields which we currently don't care about.
+ ///
+ /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L414
+ @Uint64()
+ external int flags;
+
+ @Uint32()
+ external int wakeupEventOrWatermark;
+ @Uint32()
+ external int bpType;
+
+ /// Union of `bp_addr`/`kprobe_func`/`uprobe_path`/`config1`
+ @Uint64()
+ external int config1;
+
+ /// Union of `bp_len`/`kprobe_addr`/`probe_offset`/`config2`
+ @Uint64()
+ external int config2;
+
+ /// One of `enum perf_branch_sample_type`
+ @Uint64()
+ external int branchSampleType;
+
+ /// Defines set of user regs to dump on samples.
+ /// See asm/perf_regs.h for details.
+ @Uint64()
+ external int sampleRegsUser;
+
+ /// Defines size of the user stack to dump on samples.
+ @Uint32()
+ external int sampleStackUser;
+
+ @Int32()
+ external int clockid;
+
+ /// Defines set of regs to dump for each sample
+ /// state captured on:
+ /// - precise = 0: PMU interrupt
+ /// - precise > 0: sampled instruction
+ ///
+ /// See asm/perf_regs.h for details.
+ @Uint64()
+ external int sampleRegsIntr;
+
+ /// Wakeup watermark for AUX area
+ @Uint32()
+ external int auxWatermark;
+ @Uint16()
+ external int sampleMaxStack;
+ @Uint16()
+ external int reserved2;
+ @Uint32()
+ external int auxSampleSize;
+ @Uint32()
+ external int reserved3;
+
+ /// User provided data if sigtrap=1, passed back to user via
+ /// siginfo_t::si_perf_data, e.g. to permit user to identify the event.
+ /// Note, siginfo_t::si_perf_data is long-sized, and sig_data will be
+ /// truncated accordingly on 32 bit architectures.
+ @Uint64()
+ external int sigData;
+
+ /// Extension of config2
+ @Uint64()
+ external int config3;
+}
+
+/// `enum perf_event_type`: type of the recorded event.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L838
+extension type const EventType(int _) implements int {
+ /// `PERF_RECORD_MMAP`
+ ///
+ /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L879
+ static const mmap = EventType(1);
+
+ /// `PERF_RECORD_SAMPLE`
+ ///
+ /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L947
+ static const sample = EventType(9);
+
+ /// `PERF_RECORD_MMAP2`
+ ///
+ /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L1035
+ static const mmap2 = EventType(10);
+}
+
+/// `enum perf_type_id`
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L29
+extension type const TypeId(int index) implements int {
+ static const hardware = TypeId(0);
+ static const software = TypeId(1);
+ static const tracepoint = TypeId(2);
+ static const hwCache = TypeId(3);
+ static const raw = TypeId(4);
+ static const breakpoint = TypeId(5);
+}
+
+/// `enum perf_event_sample_format`: additional information recorded for sample.
+///
+/// Bits that can be set in [PerfEventAttr.sampleType] to request information
+/// in the overflow packets.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L139
+extension type const SampleFormat(int bit) implements int {
+ /// `PERF_SAMPLE_IP`
+ static const ip = SampleFormat(1 << 0);
+
+ /// `PERF_SAMPLE_TID`
+ static const tid = SampleFormat(1 << 1);
+
+ /// `PERF_SAMPLE_TIME`
+ static const time = SampleFormat(1 << 2);
+
+ /// `PERF_SAMPLE_ADDR`
+ static const addr = SampleFormat(1 << 3);
+
+ /// `PERF_SAMPLE_READ`
+ static const read = SampleFormat(1 << 4);
+
+ /// `PERF_SAMPLE_CALLCHAIN`
+ static const callchain = SampleFormat(1 << 5);
+
+ /// `PERF_SAMPLE_ID`
+ static const id = SampleFormat(1 << 6);
+
+ /// `PERF_SAMPLE_CPU`
+ static const cpu = SampleFormat(1 << 7);
+
+ /// `PERF_SAMPLE_PERIOD`
+ static const period = SampleFormat(1 << 8);
+
+ /// `PERF_SAMPLE_STREAM_ID`
+ static const streamId = SampleFormat(1 << 9);
+
+ /// `PERF_SAMPLE_RAW`
+ static const raw = SampleFormat(1 << 10);
+
+ /// `PERF_SAMPLE_BRANCH_STACK`
+ static const branchStack = SampleFormat(1 << 11);
+
+ /// `PERF_SAMPLE_REGS_USER`
+ static const regsUser = SampleFormat(1 << 12);
+
+ /// `PERF_SAMPLE_STACK_USER`
+ static const stackUser = SampleFormat(1 << 13);
+
+ /// `PERF_SAMPLE_WEIGHT`
+ static const weight = SampleFormat(1 << 14);
+
+ /// `PERF_SAMPLE_DATA_SRC`
+ static const dataSrc = SampleFormat(1 << 15);
+
+ /// `PERF_SAMPLE_IDENTIFIER`
+ static const identifier = SampleFormat(1 << 16);
+
+ /// `PERF_SAMPLE_TRANSACTION`
+ static const transaction = SampleFormat(1 << 17);
+
+ /// `PERF_SAMPLE_REGS_INTR`
+ static const regsIntr = SampleFormat(1 << 18);
+
+ /// `PERF_SAMPLE_PHYS_ADDR`
+ static const physAddr = SampleFormat(1 << 19);
+
+ /// `PERF_SAMPLE_AUX`
+ static const aux = SampleFormat(1 << 20);
+
+ /// `PERF_SAMPLE_CGROUP`
+ static const cgroup = SampleFormat(1 << 21);
+
+ /// `PERF_SAMPLE_DATA_PAGE_SIZE`
+ static const dataPageSize = SampleFormat(1 << 22);
+
+ /// `PERF_SAMPLE_CODE_PAGE_SIZE`
+ static const codePageSize = SampleFormat(1 << 23);
+
+ /// `PERF_SAMPLE_WEIGHT_STRUCT`
+ static const weightStruct = SampleFormat(1 << 24);
+
+ static const bitNames = {
+ ip: "ip",
+ tid: "tid",
+ time: "time",
+ addr: "addr",
+ read: "read",
+ callchain: "callchain",
+ id: "id",
+ cpu: "cpu",
+ period: "period",
+ streamId: "streamId",
+ raw: "raw",
+ branchStack: "branchStack",
+ regsUser: "regsUser",
+ stackUser: "stackUser",
+ weight: "weight",
+ dataSrc: "dataSrc",
+ identifier: "identifier",
+ transaction: "transaction",
+ regsIntr: "regsIntr",
+ physAddr: "physAddr",
+ aux: "aux",
+ cgroup: "cgroup",
+ dataPageSize: "dataPageSize",
+ codePageSize: "codePageSize",
+ weightStruct: "weightStruct",
+ };
+
+ static String format(int mask) {
+ return SampleFormat.bitNames.entries
+ .where((e) => (mask & e.key) != 0)
+ .map((e) => e.value)
+ .join('|');
+ }
+}
+
+/// `PERF_RECORD_MMAP`
+///
+/// The `MMAP` events record the `PROT_EXEC` mappings so that we can
+/// correlate userspace `IP`s to code.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L879
+final class MmapEvent extends Struct {
+ external EventHeader header;
+
+ @Uint32()
+ external int pid;
+
+ @Uint32()
+ external int tid;
+
+ @Uint64()
+ external int addr;
+
+ @Uint64()
+ external int len;
+
+ @Uint64()
+ external int pgoffs;
+
+ @Array.variable()
+ external Array<Uint8> filename;
+
+ @override
+ String toString() => 'MmapEvent{addr=${addr.formatAsAddress()},'
+ 'len=$len,pgoffs=$pgoffs,'
+ 'filename=${filename.toStringFromZeroTerminated()}}';
+}
+
+final class BuildId extends Struct {
+ @Uint8()
+ external int size;
+
+ @Uint8()
+ external int reserved1;
+
+ @Uint16()
+ external int reserved2;
+
+ @Array(20)
+ external Array<Uint8> buildId;
+}
+
+final class Ino extends Struct {
+ @Uint32()
+ external int maj;
+
+ @Uint32()
+ external int min;
+
+ @Uint64()
+ external int ino;
+
+ @Uint64()
+ external int inoGeneration;
+}
+
+final class BuildIdOrIno extends Union {
+ external BuildId buildId;
+ external Ino ino;
+}
+
+/// `PERF_RECORD_MMAP2`
+///
+/// The `MMAP2` records are an augmented version of `MMAP` (see [MapEvent]),
+/// they add `maj`, `min`, `ino` numbers to be used to uniquely identify each
+/// mapping.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L1035
+final class Mmap2Event extends Struct {
+ external EventHeader header;
+
+ @Uint32()
+ external int pid;
+
+ @Uint32()
+ external int tid;
+
+ @Uint64()
+ external int addr;
+
+ @Uint64()
+ external int len;
+
+ @Uint64()
+ external int pgoffs;
+
+ external BuildIdOrIno buildIdOrIno;
+
+ @Uint32()
+ external int prot;
+
+ @Uint32()
+ external int flags;
+
+ @Array.variable()
+ external Array<Uint8> filename;
+
+ @override
+ String toString() => 'Mmap2Event{addr=${addr.formatAsAddress()},'
+ 'len=$len,pgoffs=$pgoffs,'
+ 'filename=${filename.toStringFromZeroTerminated()}}';
+}
+
+extension ArrayToString on Array<Uint8> {
+ String toStringFromFixedLength(int length) =>
+ String.fromCharCodes([for (var i = 0; i < length; i++) this[i]]);
+
+ String toStringFromZeroTerminated() {
+ final sb = StringBuffer();
+ for (var i = 0; this[i] != 0; i++) {
+ sb.writeCharCode(this[i]);
+ }
+ return sb.toString();
+ }
+}
+
+extension FormatAsAddress on int {
+ String formatAsAddress() => toRadixString(16);
+}
+
+const int kb = 1024;
+const int mb = 1024 * kb;
+
+final class StreamingSectionReader {
+ final RandomAccessFile f;
+ final FileSection section;
+
+ final chunk = Uint8List(256 * mb);
+
+ /// Number of bytes available in the chunk.
+ int chunkBytes = 0;
+
+ /// Offset from the start of the section to the start of the chunk.
+ int chunkOffset = 0;
+
+ /// Position within the chunk.
+ int pos = 0;
+
+ StreamingSectionReader(this.f, this.section) {
+ f.setPositionSync(section.offset);
+ refill();
+ }
+
+ bool ensure(int bytes) {
+ if (chunkBytes < (pos + bytes)) {
+ refill();
+ }
+ return chunkBytes >= (pos + bytes);
+ }
+
+ void refill() {
+ final int leftOverBytes = chunkBytes - pos;
+ for (int i = 0; i < leftOverBytes; i++) {
+ chunk[i] = chunk[i + pos];
+ }
+ chunkOffset += pos;
+ pos = 0;
+
+ // Are there any more bytes left to read?
+ if (chunkOffset >= section.size) {
+ chunkBytes = 0;
+ return;
+ }
+
+ print(
+ "processed $chunkOffset bytes of ${section.size} total (${(chunkOffset / section.size * 100).floor()} %)");
+
+ final bytesAlreadyRead = chunkOffset + leftOverBytes;
+ final bytesToRead =
+ math.min(section.size - bytesAlreadyRead, chunk.length - leftOverBytes);
+ final bytesRead =
+ f.readIntoSync(chunk, leftOverBytes, leftOverBytes + bytesToRead);
+ chunkBytes = bytesRead + leftOverBytes;
+ }
+}
+
+final class PerfData {
+ final RandomAccessFile f;
+
+ final Header header;
+
+ PerfData(this.f)
+ : header = Struct.create<Header>(f.readSync(sizeOf<Header>())) {
+ final magic = header.magic.toStringFromFixedLength(8);
+ if (magic != 'PERFILE2') {
+ reportError('Incorrect magic in ${f.path} - $magic');
+ }
+ }
+
+ List<EventAttr> readAttrs() {
+ f.setPositionSync(header.attrs.offset);
+ final attrs = f.readSync(header.attrs.size);
+
+ final result = <EventAttr>[];
+ int pos = 0;
+ while (pos + sizeOf<EventAttr>() < attrs.length) {
+ final attr = Struct.create<EventAttr>(attrs, pos);
+ result.add(attr);
+ pos += attr.size;
+ }
+ return result;
+ }
+
+ Map<OptionalSection, FileSection> readOptionalSectionHeaders() {
+ f.setPositionSync(header.data.offset + header.data.size);
+ final optionalHeaders =
+ f.readSync(sizeOf<FileSection>() * OptionalSection.values.length);
+
+ int headerIndex = 0;
+ return {
+ for (final flag in OptionalSection.values)
+ if (header.flags & (1 << flag.index) != 0)
+ flag: Struct.create<FileSection>(
+ optionalHeaders, sizeOf<FileSection>() * headerIndex++),
+ };
+ }
+
+ late final _dataReader = StreamingSectionReader(f, header.data);
+
+ @pragma('vm:prefer-inline')
+ void readEvents(bool Function(int type, Uint8List chunk, int pos) callback) {
+ final reader = _dataReader;
+
+ while (true) {
+ if (!reader.ensure(sizeOf<EventHeader>())) {
+ // No more events.
+ return;
+ }
+
+ // Note: `reader.ensure` might refill the chunk and invalidate
+ // created struct so extract values eagerly.
+ final EventHeader(:type, :size) =
+ Struct.create<EventHeader>(reader.chunk, reader.pos);
+ if (!reader.ensure(size)) {
+ return;
+ }
+
+ // At this point we are guaranteed to have the whole event in the chunk
+ // starting at `reader.pos`.
+ if (!callback(type, reader.chunk, reader.pos)) {
+ reader.pos += size;
+ return;
+ }
+ reader.pos += size;
+ }
+ }
+
+ Never reportError(String message) => throw ParseError(f.path, message);
+}
+
+final class ParseError extends Error {
+ final String file;
+ final String message;
+
+ ParseError(this.file, this.message);
+
+ @override
+ String toString() => 'Failed to parse $file: $message';
+}
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pb.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pb.dart
new file mode 100644
index 0000000..71199bfd
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pb.dart
@@ -0,0 +1,1151 @@
+//
+// Generated code. Do not modify.
+// source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:core' as $core;
+
+import 'package:fixnum/fixnum.dart' as $fixnum;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class Profile extends $pb.GeneratedMessage {
+ factory Profile({
+ $core.Iterable<ValueType>? sampleType,
+ $core.Iterable<Sample>? sample,
+ $core.Iterable<Mapping>? mapping,
+ $core.Iterable<Location>? location,
+ $core.Iterable<Function_>? function,
+ $core.Iterable<$core.String>? stringTable,
+ $fixnum.Int64? dropFrames,
+ $fixnum.Int64? keepFrames,
+ $fixnum.Int64? timeNanos,
+ $fixnum.Int64? durationNanos,
+ ValueType? periodType,
+ $fixnum.Int64? period,
+ $core.Iterable<$fixnum.Int64>? comment,
+ $fixnum.Int64? defaultSampleType,
+ }) {
+ final $result = create();
+ if (sampleType != null) {
+ $result.sampleType.addAll(sampleType);
+ }
+ if (sample != null) {
+ $result.sample.addAll(sample);
+ }
+ if (mapping != null) {
+ $result.mapping.addAll(mapping);
+ }
+ if (location != null) {
+ $result.location.addAll(location);
+ }
+ if (function != null) {
+ $result.function.addAll(function);
+ }
+ if (stringTable != null) {
+ $result.stringTable.addAll(stringTable);
+ }
+ if (dropFrames != null) {
+ $result.dropFrames = dropFrames;
+ }
+ if (keepFrames != null) {
+ $result.keepFrames = keepFrames;
+ }
+ if (timeNanos != null) {
+ $result.timeNanos = timeNanos;
+ }
+ if (durationNanos != null) {
+ $result.durationNanos = durationNanos;
+ }
+ if (periodType != null) {
+ $result.periodType = periodType;
+ }
+ if (period != null) {
+ $result.period = period;
+ }
+ if (comment != null) {
+ $result.comment.addAll(comment);
+ }
+ if (defaultSampleType != null) {
+ $result.defaultSampleType = defaultSampleType;
+ }
+ return $result;
+ }
+ Profile._() : super();
+ factory Profile.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory Profile.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'Profile',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..pc<ValueType>(1, _omitFieldNames ? '' : 'sampleType', $pb.PbFieldType.PM,
+ subBuilder: ValueType.create)
+ ..pc<Sample>(2, _omitFieldNames ? '' : 'sample', $pb.PbFieldType.PM,
+ subBuilder: Sample.create)
+ ..pc<Mapping>(3, _omitFieldNames ? '' : 'mapping', $pb.PbFieldType.PM,
+ subBuilder: Mapping.create)
+ ..pc<Location>(4, _omitFieldNames ? '' : 'location', $pb.PbFieldType.PM,
+ subBuilder: Location.create)
+ ..pc<Function_>(5, _omitFieldNames ? '' : 'function', $pb.PbFieldType.PM,
+ subBuilder: Function_.create)
+ ..pPS(6, _omitFieldNames ? '' : 'stringTable')
+ ..aInt64(7, _omitFieldNames ? '' : 'dropFrames')
+ ..aInt64(8, _omitFieldNames ? '' : 'keepFrames')
+ ..aInt64(9, _omitFieldNames ? '' : 'timeNanos')
+ ..aInt64(10, _omitFieldNames ? '' : 'durationNanos')
+ ..aOM<ValueType>(11, _omitFieldNames ? '' : 'periodType',
+ subBuilder: ValueType.create)
+ ..aInt64(12, _omitFieldNames ? '' : 'period')
+ ..p<$fixnum.Int64>(13, _omitFieldNames ? '' : 'comment', $pb.PbFieldType.K6)
+ ..aInt64(14, _omitFieldNames ? '' : 'defaultSampleType')
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ Profile clone() => Profile()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ Profile copyWith(void Function(Profile) updates) =>
+ super.copyWith((message) => updates(message as Profile)) as Profile;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static Profile create() => Profile._();
+ Profile createEmptyInstance() => create();
+ static $pb.PbList<Profile> createRepeated() => $pb.PbList<Profile>();
+ @$core.pragma('dart2js:noInline')
+ static Profile getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Profile>(create);
+ static Profile? _defaultInstance;
+
+ /// A description of the samples associated with each Sample.value.
+ /// For a cpu profile this might be:
+ /// [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]]
+ /// For a heap profile, this might be:
+ /// [["allocations","count"], ["space","bytes"]],
+ /// If one of the values represents the number of events represented
+ /// by the sample, by convention it should be at index 0 and use
+ /// sample_type.unit == "count".
+ @$pb.TagNumber(1)
+ $core.List<ValueType> get sampleType => $_getList(0);
+
+ /// The set of samples recorded in this profile.
+ @$pb.TagNumber(2)
+ $core.List<Sample> get sample => $_getList(1);
+
+ /// Mapping from address ranges to the image/binary/library mapped
+ /// into that address range. mapping[0] will be the main binary.
+ @$pb.TagNumber(3)
+ $core.List<Mapping> get mapping => $_getList(2);
+
+ /// Useful program location
+ @$pb.TagNumber(4)
+ $core.List<Location> get location => $_getList(3);
+
+ /// Functions referenced by locations
+ @$pb.TagNumber(5)
+ $core.List<Function_> get function => $_getList(4);
+
+ /// A common table for strings referenced by various messages.
+ /// string_table[0] must always be "".
+ @$pb.TagNumber(6)
+ $core.List<$core.String> get stringTable => $_getList(5);
+
+ /// frames with Function.function_name fully matching the following
+ /// regexp will be dropped from the samples, along with their successors.
+ /// Index into string table.
+ @$pb.TagNumber(7)
+ $fixnum.Int64 get dropFrames => $_getI64(6);
+ @$pb.TagNumber(7)
+ set dropFrames($fixnum.Int64 v) {
+ $_setInt64(6, v);
+ }
+
+ @$pb.TagNumber(7)
+ $core.bool hasDropFrames() => $_has(6);
+ @$pb.TagNumber(7)
+ void clearDropFrames() => clearField(7);
+
+ /// frames with Function.function_name fully matching the following
+ /// regexp will be kept, even if it matches drop_functions.
+ /// Index into string table.
+ @$pb.TagNumber(8)
+ $fixnum.Int64 get keepFrames => $_getI64(7);
+ @$pb.TagNumber(8)
+ set keepFrames($fixnum.Int64 v) {
+ $_setInt64(7, v);
+ }
+
+ @$pb.TagNumber(8)
+ $core.bool hasKeepFrames() => $_has(7);
+ @$pb.TagNumber(8)
+ void clearKeepFrames() => clearField(8);
+
+ /// Time of collection (UTC) represented as nanoseconds past the epoch.
+ @$pb.TagNumber(9)
+ $fixnum.Int64 get timeNanos => $_getI64(8);
+ @$pb.TagNumber(9)
+ set timeNanos($fixnum.Int64 v) {
+ $_setInt64(8, v);
+ }
+
+ @$pb.TagNumber(9)
+ $core.bool hasTimeNanos() => $_has(8);
+ @$pb.TagNumber(9)
+ void clearTimeNanos() => clearField(9);
+
+ /// Duration of the profile, if a duration makes sense.
+ @$pb.TagNumber(10)
+ $fixnum.Int64 get durationNanos => $_getI64(9);
+ @$pb.TagNumber(10)
+ set durationNanos($fixnum.Int64 v) {
+ $_setInt64(9, v);
+ }
+
+ @$pb.TagNumber(10)
+ $core.bool hasDurationNanos() => $_has(9);
+ @$pb.TagNumber(10)
+ void clearDurationNanos() => clearField(10);
+
+ /// The kind of events between sampled ocurrences.
+ /// e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+ @$pb.TagNumber(11)
+ ValueType get periodType => $_getN(10);
+ @$pb.TagNumber(11)
+ set periodType(ValueType v) {
+ setField(11, v);
+ }
+
+ @$pb.TagNumber(11)
+ $core.bool hasPeriodType() => $_has(10);
+ @$pb.TagNumber(11)
+ void clearPeriodType() => clearField(11);
+ @$pb.TagNumber(11)
+ ValueType ensurePeriodType() => $_ensure(10);
+
+ /// The number of events between sampled occurrences.
+ @$pb.TagNumber(12)
+ $fixnum.Int64 get period => $_getI64(11);
+ @$pb.TagNumber(12)
+ set period($fixnum.Int64 v) {
+ $_setInt64(11, v);
+ }
+
+ @$pb.TagNumber(12)
+ $core.bool hasPeriod() => $_has(11);
+ @$pb.TagNumber(12)
+ void clearPeriod() => clearField(12);
+
+ /// Freeform text associated to the profile.
+ /// Indices into string table.
+ @$pb.TagNumber(13)
+ $core.List<$fixnum.Int64> get comment => $_getList(12);
+
+ /// Index into the string table of the type of the preferred sample
+ /// value. If unset, clients should default to the last sample value.
+ @$pb.TagNumber(14)
+ $fixnum.Int64 get defaultSampleType => $_getI64(13);
+ @$pb.TagNumber(14)
+ set defaultSampleType($fixnum.Int64 v) {
+ $_setInt64(13, v);
+ }
+
+ @$pb.TagNumber(14)
+ $core.bool hasDefaultSampleType() => $_has(13);
+ @$pb.TagNumber(14)
+ void clearDefaultSampleType() => clearField(14);
+}
+
+/// ValueType describes the semantics and measurement units of a value.
+class ValueType extends $pb.GeneratedMessage {
+ factory ValueType({
+ $fixnum.Int64? type,
+ $fixnum.Int64? unit,
+ }) {
+ final $result = create();
+ if (type != null) {
+ $result.type = type;
+ }
+ if (unit != null) {
+ $result.unit = unit;
+ }
+ return $result;
+ }
+ ValueType._() : super();
+ factory ValueType.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory ValueType.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'ValueType',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..aInt64(1, _omitFieldNames ? '' : 'type')
+ ..aInt64(2, _omitFieldNames ? '' : 'unit')
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ ValueType clone() => ValueType()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ ValueType copyWith(void Function(ValueType) updates) =>
+ super.copyWith((message) => updates(message as ValueType)) as ValueType;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static ValueType create() => ValueType._();
+ ValueType createEmptyInstance() => create();
+ static $pb.PbList<ValueType> createRepeated() => $pb.PbList<ValueType>();
+ @$core.pragma('dart2js:noInline')
+ static ValueType getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ValueType>(create);
+ static ValueType? _defaultInstance;
+
+ /// Index into string table.
+ @$pb.TagNumber(1)
+ $fixnum.Int64 get type => $_getI64(0);
+ @$pb.TagNumber(1)
+ set type($fixnum.Int64 v) {
+ $_setInt64(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasType() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearType() => clearField(1);
+
+ /// Index into string table.
+ @$pb.TagNumber(2)
+ $fixnum.Int64 get unit => $_getI64(1);
+ @$pb.TagNumber(2)
+ set unit($fixnum.Int64 v) {
+ $_setInt64(1, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasUnit() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearUnit() => clearField(2);
+}
+
+/// Each Sample records values encountered in some program
+/// context. The program context is typically a stack trace, perhaps
+/// augmented with auxiliary information like the thread-id, some
+/// indicator of a higher level request being handled etc.
+class Sample extends $pb.GeneratedMessage {
+ factory Sample({
+ $core.Iterable<$fixnum.Int64>? locationId,
+ $core.Iterable<$fixnum.Int64>? value,
+ $core.Iterable<Label>? label,
+ }) {
+ final $result = create();
+ if (locationId != null) {
+ $result.locationId.addAll(locationId);
+ }
+ if (value != null) {
+ $result.value.addAll(value);
+ }
+ if (label != null) {
+ $result.label.addAll(label);
+ }
+ return $result;
+ }
+ Sample._() : super();
+ factory Sample.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory Sample.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'Sample',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..p<$fixnum.Int64>(
+ 1, _omitFieldNames ? '' : 'locationId', $pb.PbFieldType.KU6)
+ ..p<$fixnum.Int64>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.K6)
+ ..pc<Label>(3, _omitFieldNames ? '' : 'label', $pb.PbFieldType.PM,
+ subBuilder: Label.create)
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ Sample clone() => Sample()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ Sample copyWith(void Function(Sample) updates) =>
+ super.copyWith((message) => updates(message as Sample)) as Sample;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static Sample create() => Sample._();
+ Sample createEmptyInstance() => create();
+ static $pb.PbList<Sample> createRepeated() => $pb.PbList<Sample>();
+ @$core.pragma('dart2js:noInline')
+ static Sample getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Sample>(create);
+ static Sample? _defaultInstance;
+
+ /// The ids recorded here correspond to a Profile.location.id.
+ /// The leaf is at location_id[0].
+ @$pb.TagNumber(1)
+ $core.List<$fixnum.Int64> get locationId => $_getList(0);
+
+ /// The type and unit of each value is defined by the corresponding
+ /// entry in Profile.sample_type. All samples must have the same
+ /// number of values, the same as the length of Profile.sample_type.
+ /// When aggregating multiple samples into a single sample, the
+ /// result has a list of values that is the elemntwise sum of the
+ /// lists of the originals.
+ @$pb.TagNumber(2)
+ $core.List<$fixnum.Int64> get value => $_getList(1);
+
+ /// label includes additional context for this sample. It can include
+ /// things like a thread id, allocation size, etc
+ @$pb.TagNumber(3)
+ $core.List<Label> get label => $_getList(2);
+}
+
+class Label extends $pb.GeneratedMessage {
+ factory Label({
+ $fixnum.Int64? key,
+ $fixnum.Int64? str,
+ $fixnum.Int64? num,
+ $fixnum.Int64? numUnit,
+ }) {
+ final $result = create();
+ if (key != null) {
+ $result.key = key;
+ }
+ if (str != null) {
+ $result.str = str;
+ }
+ if (num != null) {
+ $result.num = num;
+ }
+ if (numUnit != null) {
+ $result.numUnit = numUnit;
+ }
+ return $result;
+ }
+ Label._() : super();
+ factory Label.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory Label.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'Label',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..aInt64(1, _omitFieldNames ? '' : 'key')
+ ..aInt64(2, _omitFieldNames ? '' : 'str')
+ ..aInt64(3, _omitFieldNames ? '' : 'num')
+ ..aInt64(4, _omitFieldNames ? '' : 'numUnit')
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ Label clone() => Label()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ Label copyWith(void Function(Label) updates) =>
+ super.copyWith((message) => updates(message as Label)) as Label;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static Label create() => Label._();
+ Label createEmptyInstance() => create();
+ static $pb.PbList<Label> createRepeated() => $pb.PbList<Label>();
+ @$core.pragma('dart2js:noInline')
+ static Label getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Label>(create);
+ static Label? _defaultInstance;
+
+ /// Index into string table
+ @$pb.TagNumber(1)
+ $fixnum.Int64 get key => $_getI64(0);
+ @$pb.TagNumber(1)
+ set key($fixnum.Int64 v) {
+ $_setInt64(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasKey() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearKey() => clearField(1);
+
+ /// Index into string table
+ @$pb.TagNumber(2)
+ $fixnum.Int64 get str => $_getI64(1);
+ @$pb.TagNumber(2)
+ set str($fixnum.Int64 v) {
+ $_setInt64(1, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasStr() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearStr() => clearField(2);
+
+ @$pb.TagNumber(3)
+ $fixnum.Int64 get num => $_getI64(2);
+ @$pb.TagNumber(3)
+ set num($fixnum.Int64 v) {
+ $_setInt64(2, v);
+ }
+
+ @$pb.TagNumber(3)
+ $core.bool hasNum() => $_has(2);
+ @$pb.TagNumber(3)
+ void clearNum() => clearField(3);
+
+ /// Index into string table
+ @$pb.TagNumber(4)
+ $fixnum.Int64 get numUnit => $_getI64(3);
+ @$pb.TagNumber(4)
+ set numUnit($fixnum.Int64 v) {
+ $_setInt64(3, v);
+ }
+
+ @$pb.TagNumber(4)
+ $core.bool hasNumUnit() => $_has(3);
+ @$pb.TagNumber(4)
+ void clearNumUnit() => clearField(4);
+}
+
+class Mapping extends $pb.GeneratedMessage {
+ factory Mapping({
+ $fixnum.Int64? id,
+ $fixnum.Int64? memoryStart,
+ $fixnum.Int64? memoryLimit,
+ $fixnum.Int64? fileOffset,
+ $fixnum.Int64? filename,
+ $fixnum.Int64? buildId,
+ $core.bool? hasFunctions,
+ $core.bool? hasFilenames,
+ $core.bool? hasLineNumbers,
+ $core.bool? hasInlineFrames,
+ }) {
+ final $result = create();
+ if (id != null) {
+ $result.id = id;
+ }
+ if (memoryStart != null) {
+ $result.memoryStart = memoryStart;
+ }
+ if (memoryLimit != null) {
+ $result.memoryLimit = memoryLimit;
+ }
+ if (fileOffset != null) {
+ $result.fileOffset = fileOffset;
+ }
+ if (filename != null) {
+ $result.filename = filename;
+ }
+ if (buildId != null) {
+ $result.buildId = buildId;
+ }
+ if (hasFunctions != null) {
+ $result.hasFunctions = hasFunctions;
+ }
+ if (hasFilenames != null) {
+ $result.hasFilenames = hasFilenames;
+ }
+ if (hasLineNumbers != null) {
+ $result.hasLineNumbers = hasLineNumbers;
+ }
+ if (hasInlineFrames != null) {
+ $result.hasInlineFrames = hasInlineFrames;
+ }
+ return $result;
+ }
+ Mapping._() : super();
+ factory Mapping.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory Mapping.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'Mapping',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..a<$fixnum.Int64>(
+ 2, _omitFieldNames ? '' : 'memoryStart', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..a<$fixnum.Int64>(
+ 3, _omitFieldNames ? '' : 'memoryLimit', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..a<$fixnum.Int64>(
+ 4, _omitFieldNames ? '' : 'fileOffset', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..aInt64(5, _omitFieldNames ? '' : 'filename')
+ ..aInt64(6, _omitFieldNames ? '' : 'buildId')
+ ..aOB(7, _omitFieldNames ? '' : 'hasFunctions')
+ ..aOB(8, _omitFieldNames ? '' : 'hasFilenames')
+ ..aOB(9, _omitFieldNames ? '' : 'hasLineNumbers')
+ ..aOB(10, _omitFieldNames ? '' : 'hasInlineFrames')
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ Mapping clone() => Mapping()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ Mapping copyWith(void Function(Mapping) updates) =>
+ super.copyWith((message) => updates(message as Mapping)) as Mapping;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static Mapping create() => Mapping._();
+ Mapping createEmptyInstance() => create();
+ static $pb.PbList<Mapping> createRepeated() => $pb.PbList<Mapping>();
+ @$core.pragma('dart2js:noInline')
+ static Mapping getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Mapping>(create);
+ static Mapping? _defaultInstance;
+
+ /// Unique nonzero id for the mapping.
+ @$pb.TagNumber(1)
+ $fixnum.Int64 get id => $_getI64(0);
+ @$pb.TagNumber(1)
+ set id($fixnum.Int64 v) {
+ $_setInt64(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasId() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearId() => clearField(1);
+
+ /// Address at which the binary (or DLL) is loaded into memory.
+ @$pb.TagNumber(2)
+ $fixnum.Int64 get memoryStart => $_getI64(1);
+ @$pb.TagNumber(2)
+ set memoryStart($fixnum.Int64 v) {
+ $_setInt64(1, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasMemoryStart() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearMemoryStart() => clearField(2);
+
+ /// The limit of the address range occupied by this mapping.
+ @$pb.TagNumber(3)
+ $fixnum.Int64 get memoryLimit => $_getI64(2);
+ @$pb.TagNumber(3)
+ set memoryLimit($fixnum.Int64 v) {
+ $_setInt64(2, v);
+ }
+
+ @$pb.TagNumber(3)
+ $core.bool hasMemoryLimit() => $_has(2);
+ @$pb.TagNumber(3)
+ void clearMemoryLimit() => clearField(3);
+
+ /// Offset in the binary that corresponds to the first mapped address.
+ @$pb.TagNumber(4)
+ $fixnum.Int64 get fileOffset => $_getI64(3);
+ @$pb.TagNumber(4)
+ set fileOffset($fixnum.Int64 v) {
+ $_setInt64(3, v);
+ }
+
+ @$pb.TagNumber(4)
+ $core.bool hasFileOffset() => $_has(3);
+ @$pb.TagNumber(4)
+ void clearFileOffset() => clearField(4);
+
+ /// The object this entry is loaded from. This can be a filename on
+ /// disk for the main binary and shared libraries, or virtual
+ /// abstractions like "[vdso]".
+ /// Index into string table
+ @$pb.TagNumber(5)
+ $fixnum.Int64 get filename => $_getI64(4);
+ @$pb.TagNumber(5)
+ set filename($fixnum.Int64 v) {
+ $_setInt64(4, v);
+ }
+
+ @$pb.TagNumber(5)
+ $core.bool hasFilename() => $_has(4);
+ @$pb.TagNumber(5)
+ void clearFilename() => clearField(5);
+
+ /// A string that uniquely identifies a particular program version
+ /// with high probability. E.g., for binaries generated by GNU tools,
+ /// it could be the contents of the .note.gnu.build-id field.
+ /// Index into string table
+ @$pb.TagNumber(6)
+ $fixnum.Int64 get buildId => $_getI64(5);
+ @$pb.TagNumber(6)
+ set buildId($fixnum.Int64 v) {
+ $_setInt64(5, v);
+ }
+
+ @$pb.TagNumber(6)
+ $core.bool hasBuildId() => $_has(5);
+ @$pb.TagNumber(6)
+ void clearBuildId() => clearField(6);
+
+ /// The following fields indicate the resolution of symbolic info.
+ @$pb.TagNumber(7)
+ $core.bool get hasFunctions => $_getBF(6);
+ @$pb.TagNumber(7)
+ set hasFunctions($core.bool v) {
+ $_setBool(6, v);
+ }
+
+ @$pb.TagNumber(7)
+ $core.bool hasHasFunctions() => $_has(6);
+ @$pb.TagNumber(7)
+ void clearHasFunctions() => clearField(7);
+
+ @$pb.TagNumber(8)
+ $core.bool get hasFilenames => $_getBF(7);
+ @$pb.TagNumber(8)
+ set hasFilenames($core.bool v) {
+ $_setBool(7, v);
+ }
+
+ @$pb.TagNumber(8)
+ $core.bool hasHasFilenames() => $_has(7);
+ @$pb.TagNumber(8)
+ void clearHasFilenames() => clearField(8);
+
+ @$pb.TagNumber(9)
+ $core.bool get hasLineNumbers => $_getBF(8);
+ @$pb.TagNumber(9)
+ set hasLineNumbers($core.bool v) {
+ $_setBool(8, v);
+ }
+
+ @$pb.TagNumber(9)
+ $core.bool hasHasLineNumbers() => $_has(8);
+ @$pb.TagNumber(9)
+ void clearHasLineNumbers() => clearField(9);
+
+ @$pb.TagNumber(10)
+ $core.bool get hasInlineFrames => $_getBF(9);
+ @$pb.TagNumber(10)
+ set hasInlineFrames($core.bool v) {
+ $_setBool(9, v);
+ }
+
+ @$pb.TagNumber(10)
+ $core.bool hasHasInlineFrames() => $_has(9);
+ @$pb.TagNumber(10)
+ void clearHasInlineFrames() => clearField(10);
+}
+
+/// Describes function and line table debug information.
+class Location extends $pb.GeneratedMessage {
+ factory Location({
+ $fixnum.Int64? id,
+ $fixnum.Int64? mappingId,
+ $fixnum.Int64? address,
+ $core.Iterable<Line>? line,
+ $core.bool? isFolded,
+ }) {
+ final $result = create();
+ if (id != null) {
+ $result.id = id;
+ }
+ if (mappingId != null) {
+ $result.mappingId = mappingId;
+ }
+ if (address != null) {
+ $result.address = address;
+ }
+ if (line != null) {
+ $result.line.addAll(line);
+ }
+ if (isFolded != null) {
+ $result.isFolded = isFolded;
+ }
+ return $result;
+ }
+ Location._() : super();
+ factory Location.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory Location.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'Location',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..a<$fixnum.Int64>(
+ 2, _omitFieldNames ? '' : 'mappingId', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..a<$fixnum.Int64>(3, _omitFieldNames ? '' : 'address', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..pc<Line>(4, _omitFieldNames ? '' : 'line', $pb.PbFieldType.PM,
+ subBuilder: Line.create)
+ ..aOB(5, _omitFieldNames ? '' : 'isFolded')
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ Location clone() => Location()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ Location copyWith(void Function(Location) updates) =>
+ super.copyWith((message) => updates(message as Location)) as Location;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static Location create() => Location._();
+ Location createEmptyInstance() => create();
+ static $pb.PbList<Location> createRepeated() => $pb.PbList<Location>();
+ @$core.pragma('dart2js:noInline')
+ static Location getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Location>(create);
+ static Location? _defaultInstance;
+
+ /// Unique nonzero id for the location. A profile could use
+ /// instruction addresses or any integer sequence as ids.
+ @$pb.TagNumber(1)
+ $fixnum.Int64 get id => $_getI64(0);
+ @$pb.TagNumber(1)
+ set id($fixnum.Int64 v) {
+ $_setInt64(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasId() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearId() => clearField(1);
+
+ /// The id of the corresponding profile.Mapping for this location.
+ /// It can be unset if the mapping is unknown or not applicable for
+ /// this profile type.
+ @$pb.TagNumber(2)
+ $fixnum.Int64 get mappingId => $_getI64(1);
+ @$pb.TagNumber(2)
+ set mappingId($fixnum.Int64 v) {
+ $_setInt64(1, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasMappingId() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearMappingId() => clearField(2);
+
+ /// The instruction address for this location, if available. It
+ /// should be within [Mapping.memory_start...Mapping.memory_limit]
+ /// for the corresponding mapping. A non-leaf address may be in the
+ /// middle of a call instruction. It is up to display tools to find
+ /// the beginning of the instruction if necessary.
+ @$pb.TagNumber(3)
+ $fixnum.Int64 get address => $_getI64(2);
+ @$pb.TagNumber(3)
+ set address($fixnum.Int64 v) {
+ $_setInt64(2, v);
+ }
+
+ @$pb.TagNumber(3)
+ $core.bool hasAddress() => $_has(2);
+ @$pb.TagNumber(3)
+ void clearAddress() => clearField(3);
+
+ /// Multiple line indicates this location has inlined functions,
+ /// where the last entry represents the caller into which the
+ /// preceding entries were inlined.
+ ///
+ /// E.g., if memcpy() is inlined into printf:
+ /// line[0].function_name == "memcpy"
+ /// line[1].function_name == "printf"
+ @$pb.TagNumber(4)
+ $core.List<Line> get line => $_getList(3);
+
+ /// Provides an indication that multiple symbols map to this location's
+ /// address, for example due to identical code folding by the linker. In that
+ /// case the line information above represents one of the multiple
+ /// symbols. This field must be recomputed when the symbolization state of the
+ /// profile changes.
+ @$pb.TagNumber(5)
+ $core.bool get isFolded => $_getBF(4);
+ @$pb.TagNumber(5)
+ set isFolded($core.bool v) {
+ $_setBool(4, v);
+ }
+
+ @$pb.TagNumber(5)
+ $core.bool hasIsFolded() => $_has(4);
+ @$pb.TagNumber(5)
+ void clearIsFolded() => clearField(5);
+}
+
+class Line extends $pb.GeneratedMessage {
+ factory Line({
+ $fixnum.Int64? functionId,
+ $fixnum.Int64? line,
+ }) {
+ final $result = create();
+ if (functionId != null) {
+ $result.functionId = functionId;
+ }
+ if (line != null) {
+ $result.line = line;
+ }
+ return $result;
+ }
+ Line._() : super();
+ factory Line.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory Line.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'Line',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..a<$fixnum.Int64>(
+ 1, _omitFieldNames ? '' : 'functionId', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..aInt64(2, _omitFieldNames ? '' : 'line')
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ Line clone() => Line()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ Line copyWith(void Function(Line) updates) =>
+ super.copyWith((message) => updates(message as Line)) as Line;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static Line create() => Line._();
+ Line createEmptyInstance() => create();
+ static $pb.PbList<Line> createRepeated() => $pb.PbList<Line>();
+ @$core.pragma('dart2js:noInline')
+ static Line getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Line>(create);
+ static Line? _defaultInstance;
+
+ /// The id of the corresponding profile.Function for this line.
+ @$pb.TagNumber(1)
+ $fixnum.Int64 get functionId => $_getI64(0);
+ @$pb.TagNumber(1)
+ set functionId($fixnum.Int64 v) {
+ $_setInt64(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasFunctionId() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearFunctionId() => clearField(1);
+
+ /// Line number in source code.
+ @$pb.TagNumber(2)
+ $fixnum.Int64 get line => $_getI64(1);
+ @$pb.TagNumber(2)
+ set line($fixnum.Int64 v) {
+ $_setInt64(1, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasLine() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearLine() => clearField(2);
+}
+
+class Function_ extends $pb.GeneratedMessage {
+ factory Function_({
+ $fixnum.Int64? id,
+ $fixnum.Int64? name,
+ $fixnum.Int64? systemName,
+ $fixnum.Int64? filename,
+ $fixnum.Int64? startLine,
+ }) {
+ final $result = create();
+ if (id != null) {
+ $result.id = id;
+ }
+ if (name != null) {
+ $result.name = name;
+ }
+ if (systemName != null) {
+ $result.systemName = systemName;
+ }
+ if (filename != null) {
+ $result.filename = filename;
+ }
+ if (startLine != null) {
+ $result.startLine = startLine;
+ }
+ return $result;
+ }
+ Function_._() : super();
+ factory Function_.fromBuffer($core.List<$core.int> i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromBuffer(i, r);
+ factory Function_.fromJson($core.String i,
+ [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+ create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+ _omitMessageNames ? '' : 'Function',
+ package: const $pb.PackageName(
+ _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+ createEmptyInstance: create)
+ ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU6,
+ defaultOrMaker: $fixnum.Int64.ZERO)
+ ..aInt64(2, _omitFieldNames ? '' : 'name')
+ ..aInt64(3, _omitFieldNames ? '' : 'systemName')
+ ..aInt64(4, _omitFieldNames ? '' : 'filename')
+ ..aInt64(5, _omitFieldNames ? '' : 'startLine')
+ ..hasRequiredFields = false;
+
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ Function_ clone() => Function_()..mergeFromMessage(this);
+ @$core.Deprecated('Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ Function_ copyWith(void Function(Function_) updates) =>
+ super.copyWith((message) => updates(message as Function_)) as Function_;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static Function_ create() => Function_._();
+ Function_ createEmptyInstance() => create();
+ static $pb.PbList<Function_> createRepeated() => $pb.PbList<Function_>();
+ @$core.pragma('dart2js:noInline')
+ static Function_ getDefault() =>
+ _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Function_>(create);
+ static Function_? _defaultInstance;
+
+ /// Unique nonzero id for the function.
+ @$pb.TagNumber(1)
+ $fixnum.Int64 get id => $_getI64(0);
+ @$pb.TagNumber(1)
+ set id($fixnum.Int64 v) {
+ $_setInt64(0, v);
+ }
+
+ @$pb.TagNumber(1)
+ $core.bool hasId() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearId() => clearField(1);
+
+ /// Name of the function, in human-readable form if available.
+ /// Index into string table
+ @$pb.TagNumber(2)
+ $fixnum.Int64 get name => $_getI64(1);
+ @$pb.TagNumber(2)
+ set name($fixnum.Int64 v) {
+ $_setInt64(1, v);
+ }
+
+ @$pb.TagNumber(2)
+ $core.bool hasName() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearName() => clearField(2);
+
+ /// Name of the function, as identified by the system.
+ /// For instance, it can be a C++ mangled name.
+ /// Index into string table
+ @$pb.TagNumber(3)
+ $fixnum.Int64 get systemName => $_getI64(2);
+ @$pb.TagNumber(3)
+ set systemName($fixnum.Int64 v) {
+ $_setInt64(2, v);
+ }
+
+ @$pb.TagNumber(3)
+ $core.bool hasSystemName() => $_has(2);
+ @$pb.TagNumber(3)
+ void clearSystemName() => clearField(3);
+
+ /// Source file containing the function.
+ /// Index into string table
+ @$pb.TagNumber(4)
+ $fixnum.Int64 get filename => $_getI64(3);
+ @$pb.TagNumber(4)
+ set filename($fixnum.Int64 v) {
+ $_setInt64(3, v);
+ }
+
+ @$pb.TagNumber(4)
+ $core.bool hasFilename() => $_has(3);
+ @$pb.TagNumber(4)
+ void clearFilename() => clearField(4);
+
+ /// Line number in source file.
+ @$pb.TagNumber(5)
+ $fixnum.Int64 get startLine => $_getI64(4);
+ @$pb.TagNumber(5)
+ set startLine($fixnum.Int64 v) {
+ $_setInt64(4, v);
+ }
+
+ @$pb.TagNumber(5)
+ $core.bool hasStartLine() => $_has(4);
+ @$pb.TagNumber(5)
+ void clearStartLine() => clearField(5);
+}
+
+const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
+const _omitMessageNames =
+ $core.bool.fromEnvironment('protobuf.omit_message_names');
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pbenum.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbenum.dart
new file mode 100644
index 0000000..f04e4f1
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbenum.dart
@@ -0,0 +1,10 @@
+//
+// Generated code. Do not modify.
+// source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pbjson.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbjson.dart
new file mode 100644
index 0000000..dbb0cd4
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbjson.dart
@@ -0,0 +1,243 @@
+//
+// Generated code. Do not modify.
+// source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:convert' as $convert;
+import 'dart:core' as $core;
+import 'dart:typed_data' as $typed_data;
+
+@$core.Deprecated('Use profileDescriptor instead')
+const Profile$json = {
+ '1': 'Profile',
+ '2': [
+ {
+ '1': 'sample_type',
+ '3': 1,
+ '4': 3,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.ValueType',
+ '10': 'sampleType'
+ },
+ {
+ '1': 'sample',
+ '3': 2,
+ '4': 3,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.Sample',
+ '10': 'sample'
+ },
+ {
+ '1': 'mapping',
+ '3': 3,
+ '4': 3,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.Mapping',
+ '10': 'mapping'
+ },
+ {
+ '1': 'location',
+ '3': 4,
+ '4': 3,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.Location',
+ '10': 'location'
+ },
+ {
+ '1': 'function',
+ '3': 5,
+ '4': 3,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.Function',
+ '10': 'function'
+ },
+ {'1': 'string_table', '3': 6, '4': 3, '5': 9, '10': 'stringTable'},
+ {'1': 'drop_frames', '3': 7, '4': 1, '5': 3, '10': 'dropFrames'},
+ {'1': 'keep_frames', '3': 8, '4': 1, '5': 3, '10': 'keepFrames'},
+ {'1': 'time_nanos', '3': 9, '4': 1, '5': 3, '10': 'timeNanos'},
+ {'1': 'duration_nanos', '3': 10, '4': 1, '5': 3, '10': 'durationNanos'},
+ {
+ '1': 'period_type',
+ '3': 11,
+ '4': 1,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.ValueType',
+ '10': 'periodType'
+ },
+ {'1': 'period', '3': 12, '4': 1, '5': 3, '10': 'period'},
+ {'1': 'comment', '3': 13, '4': 3, '5': 3, '10': 'comment'},
+ {
+ '1': 'default_sample_type',
+ '3': 14,
+ '4': 1,
+ '5': 3,
+ '10': 'defaultSampleType'
+ },
+ ],
+};
+
+/// Descriptor for `Profile`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List profileDescriptor = $convert.base64Decode(
+ 'CgdQcm9maWxlElMKC3NhbXBsZV90eXBlGAEgAygLMjIucGVyZmV0dG8udGhpcmRfcGFydHkucG'
+ 'VyZnRvb2xzLnByb2ZpbGVzLlZhbHVlVHlwZVIKc2FtcGxlVHlwZRJHCgZzYW1wbGUYAiADKAsy'
+ 'Ly5wZXJmZXR0by50aGlyZF9wYXJ0eS5wZXJmdG9vbHMucHJvZmlsZXMuU2FtcGxlUgZzYW1wbG'
+ 'USSgoHbWFwcGluZxgDIAMoCzIwLnBlcmZldHRvLnRoaXJkX3BhcnR5LnBlcmZ0b29scy5wcm9m'
+ 'aWxlcy5NYXBwaW5nUgdtYXBwaW5nEk0KCGxvY2F0aW9uGAQgAygLMjEucGVyZmV0dG8udGhpcm'
+ 'RfcGFydHkucGVyZnRvb2xzLnByb2ZpbGVzLkxvY2F0aW9uUghsb2NhdGlvbhJNCghmdW5jdGlv'
+ 'bhgFIAMoCzIxLnBlcmZldHRvLnRoaXJkX3BhcnR5LnBlcmZ0b29scy5wcm9maWxlcy5GdW5jdG'
+ 'lvblIIZnVuY3Rpb24SIQoMc3RyaW5nX3RhYmxlGAYgAygJUgtzdHJpbmdUYWJsZRIfCgtkcm9w'
+ 'X2ZyYW1lcxgHIAEoA1IKZHJvcEZyYW1lcxIfCgtrZWVwX2ZyYW1lcxgIIAEoA1IKa2VlcEZyYW'
+ '1lcxIdCgp0aW1lX25hbm9zGAkgASgDUgl0aW1lTmFub3MSJQoOZHVyYXRpb25fbmFub3MYCiAB'
+ 'KANSDWR1cmF0aW9uTmFub3MSUwoLcGVyaW9kX3R5cGUYCyABKAsyMi5wZXJmZXR0by50aGlyZF'
+ '9wYXJ0eS5wZXJmdG9vbHMucHJvZmlsZXMuVmFsdWVUeXBlUgpwZXJpb2RUeXBlEhYKBnBlcmlv'
+ 'ZBgMIAEoA1IGcGVyaW9kEhgKB2NvbW1lbnQYDSADKANSB2NvbW1lbnQSLgoTZGVmYXVsdF9zYW'
+ '1wbGVfdHlwZRgOIAEoA1IRZGVmYXVsdFNhbXBsZVR5cGU=');
+
+@$core.Deprecated('Use valueTypeDescriptor instead')
+const ValueType$json = {
+ '1': 'ValueType',
+ '2': [
+ {'1': 'type', '3': 1, '4': 1, '5': 3, '10': 'type'},
+ {'1': 'unit', '3': 2, '4': 1, '5': 3, '10': 'unit'},
+ ],
+};
+
+/// Descriptor for `ValueType`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List valueTypeDescriptor = $convert.base64Decode(
+ 'CglWYWx1ZVR5cGUSEgoEdHlwZRgBIAEoA1IEdHlwZRISCgR1bml0GAIgASgDUgR1bml0');
+
+@$core.Deprecated('Use sampleDescriptor instead')
+const Sample$json = {
+ '1': 'Sample',
+ '2': [
+ {'1': 'location_id', '3': 1, '4': 3, '5': 4, '10': 'locationId'},
+ {'1': 'value', '3': 2, '4': 3, '5': 3, '10': 'value'},
+ {
+ '1': 'label',
+ '3': 3,
+ '4': 3,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.Label',
+ '10': 'label'
+ },
+ ],
+};
+
+/// Descriptor for `Sample`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List sampleDescriptor = $convert.base64Decode(
+ 'CgZTYW1wbGUSHwoLbG9jYXRpb25faWQYASADKARSCmxvY2F0aW9uSWQSFAoFdmFsdWUYAiADKA'
+ 'NSBXZhbHVlEkQKBWxhYmVsGAMgAygLMi4ucGVyZmV0dG8udGhpcmRfcGFydHkucGVyZnRvb2xz'
+ 'LnByb2ZpbGVzLkxhYmVsUgVsYWJlbA==');
+
+@$core.Deprecated('Use labelDescriptor instead')
+const Label$json = {
+ '1': 'Label',
+ '2': [
+ {'1': 'key', '3': 1, '4': 1, '5': 3, '10': 'key'},
+ {'1': 'str', '3': 2, '4': 1, '5': 3, '10': 'str'},
+ {'1': 'num', '3': 3, '4': 1, '5': 3, '10': 'num'},
+ {'1': 'num_unit', '3': 4, '4': 1, '5': 3, '10': 'numUnit'},
+ ],
+};
+
+/// Descriptor for `Label`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List labelDescriptor = $convert.base64Decode(
+ 'CgVMYWJlbBIQCgNrZXkYASABKANSA2tleRIQCgNzdHIYAiABKANSA3N0chIQCgNudW0YAyABKA'
+ 'NSA251bRIZCghudW1fdW5pdBgEIAEoA1IHbnVtVW5pdA==');
+
+@$core.Deprecated('Use mappingDescriptor instead')
+const Mapping$json = {
+ '1': 'Mapping',
+ '2': [
+ {'1': 'id', '3': 1, '4': 1, '5': 4, '10': 'id'},
+ {'1': 'memory_start', '3': 2, '4': 1, '5': 4, '10': 'memoryStart'},
+ {'1': 'memory_limit', '3': 3, '4': 1, '5': 4, '10': 'memoryLimit'},
+ {'1': 'file_offset', '3': 4, '4': 1, '5': 4, '10': 'fileOffset'},
+ {'1': 'filename', '3': 5, '4': 1, '5': 3, '10': 'filename'},
+ {'1': 'build_id', '3': 6, '4': 1, '5': 3, '10': 'buildId'},
+ {'1': 'has_functions', '3': 7, '4': 1, '5': 8, '10': 'hasFunctions'},
+ {'1': 'has_filenames', '3': 8, '4': 1, '5': 8, '10': 'hasFilenames'},
+ {'1': 'has_line_numbers', '3': 9, '4': 1, '5': 8, '10': 'hasLineNumbers'},
+ {
+ '1': 'has_inline_frames',
+ '3': 10,
+ '4': 1,
+ '5': 8,
+ '10': 'hasInlineFrames'
+ },
+ ],
+};
+
+/// Descriptor for `Mapping`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List mappingDescriptor = $convert.base64Decode(
+ 'CgdNYXBwaW5nEg4KAmlkGAEgASgEUgJpZBIhCgxtZW1vcnlfc3RhcnQYAiABKARSC21lbW9yeV'
+ 'N0YXJ0EiEKDG1lbW9yeV9saW1pdBgDIAEoBFILbWVtb3J5TGltaXQSHwoLZmlsZV9vZmZzZXQY'
+ 'BCABKARSCmZpbGVPZmZzZXQSGgoIZmlsZW5hbWUYBSABKANSCGZpbGVuYW1lEhkKCGJ1aWxkX2'
+ 'lkGAYgASgDUgdidWlsZElkEiMKDWhhc19mdW5jdGlvbnMYByABKAhSDGhhc0Z1bmN0aW9ucxIj'
+ 'Cg1oYXNfZmlsZW5hbWVzGAggASgIUgxoYXNGaWxlbmFtZXMSKAoQaGFzX2xpbmVfbnVtYmVycx'
+ 'gJIAEoCFIOaGFzTGluZU51bWJlcnMSKgoRaGFzX2lubGluZV9mcmFtZXMYCiABKAhSD2hhc0lu'
+ 'bGluZUZyYW1lcw==');
+
+@$core.Deprecated('Use locationDescriptor instead')
+const Location$json = {
+ '1': 'Location',
+ '2': [
+ {'1': 'id', '3': 1, '4': 1, '5': 4, '10': 'id'},
+ {'1': 'mapping_id', '3': 2, '4': 1, '5': 4, '10': 'mappingId'},
+ {'1': 'address', '3': 3, '4': 1, '5': 4, '10': 'address'},
+ {
+ '1': 'line',
+ '3': 4,
+ '4': 3,
+ '5': 11,
+ '6': '.perfetto.third_party.perftools.profiles.Line',
+ '10': 'line'
+ },
+ {'1': 'is_folded', '3': 5, '4': 1, '5': 8, '10': 'isFolded'},
+ ],
+};
+
+/// Descriptor for `Location`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List locationDescriptor = $convert.base64Decode(
+ 'CghMb2NhdGlvbhIOCgJpZBgBIAEoBFICaWQSHQoKbWFwcGluZ19pZBgCIAEoBFIJbWFwcGluZ0'
+ 'lkEhgKB2FkZHJlc3MYAyABKARSB2FkZHJlc3MSQQoEbGluZRgEIAMoCzItLnBlcmZldHRvLnRo'
+ 'aXJkX3BhcnR5LnBlcmZ0b29scy5wcm9maWxlcy5MaW5lUgRsaW5lEhsKCWlzX2ZvbGRlZBgFIA'
+ 'EoCFIIaXNGb2xkZWQ=');
+
+@$core.Deprecated('Use lineDescriptor instead')
+const Line$json = {
+ '1': 'Line',
+ '2': [
+ {'1': 'function_id', '3': 1, '4': 1, '5': 4, '10': 'functionId'},
+ {'1': 'line', '3': 2, '4': 1, '5': 3, '10': 'line'},
+ ],
+};
+
+/// Descriptor for `Line`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List lineDescriptor = $convert.base64Decode(
+ 'CgRMaW5lEh8KC2Z1bmN0aW9uX2lkGAEgASgEUgpmdW5jdGlvbklkEhIKBGxpbmUYAiABKANSBG'
+ 'xpbmU=');
+
+@$core.Deprecated('Use function_Descriptor instead')
+const Function_$json = {
+ '1': 'Function',
+ '2': [
+ {'1': 'id', '3': 1, '4': 1, '5': 4, '10': 'id'},
+ {'1': 'name', '3': 2, '4': 1, '5': 3, '10': 'name'},
+ {'1': 'system_name', '3': 3, '4': 1, '5': 3, '10': 'systemName'},
+ {'1': 'filename', '3': 4, '4': 1, '5': 3, '10': 'filename'},
+ {'1': 'start_line', '3': 5, '4': 1, '5': 3, '10': 'startLine'},
+ ],
+};
+
+/// Descriptor for `Function`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List function_Descriptor = $convert.base64Decode(
+ 'CghGdW5jdGlvbhIOCgJpZBgBIAEoBFICaWQSEgoEbmFtZRgCIAEoA1IEbmFtZRIfCgtzeXN0ZW'
+ '1fbmFtZRgDIAEoA1IKc3lzdGVtTmFtZRIaCghmaWxlbmFtZRgEIAEoA1IIZmlsZW5hbWUSHQoK'
+ 'c3RhcnRfbGluZRgFIAEoA1IJc3RhcnRMaW5l');
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pbserver.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbserver.dart
new file mode 100644
index 0000000..e81bff6
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbserver.dart
@@ -0,0 +1,13 @@
+//
+// Generated code. Do not modify.
+// source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+export 'profile.pb.dart';
diff --git a/runtime/tools/profiling/lib/src/pprof/profile.proto b/runtime/tools/profiling/lib/src/pprof/profile.proto
new file mode 100644
index 0000000..21282c5
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/profile.proto
@@ -0,0 +1,230 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Profile is a common stacktrace profile format.
+//
+// Measurements represented with this format should follow the
+// following conventions:
+//
+// - Consumers should treat unset optional fields as if they had been
+// set with their default value.
+//
+// - When possible, measurements should be stored in "unsampled" form
+// that is most useful to humans. There should be enough
+// information present to determine the original sampled values.
+//
+// - On-disk, the serialized proto must be gzip-compressed.
+//
+// - The profile is represented as a set of samples, where each sample
+// references a sequence of locations, and where each location belongs
+// to a mapping.
+// - There is a N->1 relationship from sample.location_id entries to
+// locations. For every sample.location_id entry there must be a
+// unique Location with that id.
+// - There is an optional N->1 relationship from locations to
+// mappings. For every nonzero Location.mapping_id there must be a
+// unique Mapping with that id.
+
+syntax = "proto3";
+
+// This is in perfetto.third_party to avoid clashing with potential other
+// copies of this proto.
+package perfetto.third_party.perftools.profiles;
+
+option java_package = "com.google.perftools.profiles";
+option java_outer_classname = "ProfileProto";
+
+message Profile {
+ // A description of the samples associated with each Sample.value.
+ // For a cpu profile this might be:
+ // [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]]
+ // For a heap profile, this might be:
+ // [["allocations","count"], ["space","bytes"]],
+ // If one of the values represents the number of events represented
+ // by the sample, by convention it should be at index 0 and use
+ // sample_type.unit == "count".
+ repeated ValueType sample_type = 1;
+ // The set of samples recorded in this profile.
+ repeated Sample sample = 2;
+ // Mapping from address ranges to the image/binary/library mapped
+ // into that address range. mapping[0] will be the main binary.
+ repeated Mapping mapping = 3;
+ // Useful program location
+ repeated Location location = 4;
+ // Functions referenced by locations
+ repeated Function function = 5;
+ // A common table for strings referenced by various messages.
+ // string_table[0] must always be "".
+ repeated string string_table = 6;
+ // frames with Function.function_name fully matching the following
+ // regexp will be dropped from the samples, along with their successors.
+ // Index into string table.
+ int64 drop_frames = 7;
+ // frames with Function.function_name fully matching the following
+ // regexp will be kept, even if it matches drop_functions.
+ // Index into string table.
+ int64 keep_frames = 8;
+
+ // The following fields are informational, do not affect
+ // interpretation of results.
+
+ // Time of collection (UTC) represented as nanoseconds past the epoch.
+ int64 time_nanos = 9;
+ // Duration of the profile, if a duration makes sense.
+ int64 duration_nanos = 10;
+ // The kind of events between sampled ocurrences.
+ // e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+ ValueType period_type = 11;
+ // The number of events between sampled occurrences.
+ int64 period = 12;
+ // Freeform text associated to the profile.
+ // Indices into string table.
+ repeated int64 comment = 13;
+ // Index into the string table of the type of the preferred sample
+ // value. If unset, clients should default to the last sample value.
+ int64 default_sample_type = 14;
+}
+
+// ValueType describes the semantics and measurement units of a value.
+message ValueType {
+ // Index into string table.
+ int64 type = 1;
+ // Index into string table.
+ int64 unit = 2;
+}
+
+// Each Sample records values encountered in some program
+// context. The program context is typically a stack trace, perhaps
+// augmented with auxiliary information like the thread-id, some
+// indicator of a higher level request being handled etc.
+message Sample {
+ // The ids recorded here correspond to a Profile.location.id.
+ // The leaf is at location_id[0].
+ repeated uint64 location_id = 1;
+ // The type and unit of each value is defined by the corresponding
+ // entry in Profile.sample_type. All samples must have the same
+ // number of values, the same as the length of Profile.sample_type.
+ // When aggregating multiple samples into a single sample, the
+ // result has a list of values that is the elemntwise sum of the
+ // lists of the originals.
+ repeated int64 value = 2;
+ // label includes additional context for this sample. It can include
+ // things like a thread id, allocation size, etc
+ repeated Label label = 3;
+}
+
+message Label {
+ // Index into string table
+ int64 key = 1;
+
+ // At most one of the following must be present
+
+ // Index into string table
+ int64 str = 2;
+
+ int64 num = 3;
+
+ // Should only be present when num is present.
+ // Specifies the units of num.
+ // Use arbitrary string (for example, "requests") as a custom count unit.
+ // If no unit is specified, consumer may apply heuristic to deduce the unit.
+ // Consumers may also interpret units like "bytes" and "kilobytes" as memory
+ // units and units like "seconds" and "nanoseconds" as time units,
+ // and apply appropriate unit conversions to these.
+
+ // Index into string table
+ int64 num_unit = 4;
+}
+
+message Mapping {
+ // Unique nonzero id for the mapping.
+ uint64 id = 1;
+ // Address at which the binary (or DLL) is loaded into memory.
+ uint64 memory_start = 2;
+ // The limit of the address range occupied by this mapping.
+ uint64 memory_limit = 3;
+ // Offset in the binary that corresponds to the first mapped address.
+ uint64 file_offset = 4;
+ // The object this entry is loaded from. This can be a filename on
+ // disk for the main binary and shared libraries, or virtual
+ // abstractions like "[vdso]".
+ // Index into string table
+ int64 filename = 5;
+ // A string that uniquely identifies a particular program version
+ // with high probability. E.g., for binaries generated by GNU tools,
+ // it could be the contents of the .note.gnu.build-id field.
+ // Index into string table
+ int64 build_id = 6;
+
+ // The following fields indicate the resolution of symbolic info.
+ bool has_functions = 7;
+ bool has_filenames = 8;
+ bool has_line_numbers = 9;
+ bool has_inline_frames = 10;
+}
+
+// Describes function and line table debug information.
+message Location {
+ // Unique nonzero id for the location. A profile could use
+ // instruction addresses or any integer sequence as ids.
+ uint64 id = 1;
+ // The id of the corresponding profile.Mapping for this location.
+ // It can be unset if the mapping is unknown or not applicable for
+ // this profile type.
+ uint64 mapping_id = 2;
+ // The instruction address for this location, if available. It
+ // should be within [Mapping.memory_start...Mapping.memory_limit]
+ // for the corresponding mapping. A non-leaf address may be in the
+ // middle of a call instruction. It is up to display tools to find
+ // the beginning of the instruction if necessary.
+ uint64 address = 3;
+ // Multiple line indicates this location has inlined functions,
+ // where the last entry represents the caller into which the
+ // preceding entries were inlined.
+ //
+ // E.g., if memcpy() is inlined into printf:
+ // line[0].function_name == "memcpy"
+ // line[1].function_name == "printf"
+ repeated Line line = 4;
+ // Provides an indication that multiple symbols map to this location's
+ // address, for example due to identical code folding by the linker. In that
+ // case the line information above represents one of the multiple
+ // symbols. This field must be recomputed when the symbolization state of the
+ // profile changes.
+ bool is_folded = 5;
+}
+
+message Line {
+ // The id of the corresponding profile.Function for this line.
+ uint64 function_id = 1;
+ // Line number in source code.
+ int64 line = 2;
+}
+
+message Function {
+ // Unique nonzero id for the function.
+ uint64 id = 1;
+ // Name of the function, in human-readable form if available.
+ // Index into string table
+ int64 name = 2;
+ // Name of the function, as identified by the system.
+ // For instance, it can be a C++ mangled name.
+ // Index into string table
+ int64 system_name = 3;
+ // Source file containing the function.
+ // Index into string table
+ int64 filename = 4;
+ // Line number in source file.
+ int64 start_line = 5;
+}
diff --git a/runtime/tools/profiling/lib/src/symbols.dart b/runtime/tools/profiling/lib/src/symbols.dart
new file mode 100644
index 0000000..03dbd8ef
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/symbols.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2024, 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:profiling/src/elf_utils.dart';
+
+/// Symbols of a TEXT section of a binary indexed by their file offset.
+class Symbols {
+ final Uint32List fileOffsets;
+ final List<String> names;
+
+ Symbols._(this.fileOffsets, this.names);
+
+ /// Given the [fileOffset] find a symbol it falls into.
+ ///
+ /// We assume that symbol with index `i` starts at `fileOffsets[i]`
+ /// and ends at `fileOffset[i+1]`.
+ int? symbolIndex(int fileOffset) {
+ int lo = 0;
+ int hi = fileOffsets.length - 1;
+ while (lo <= hi) {
+ int mid = ((hi - lo + 1) >> 1) + lo;
+ if (fileOffset < fileOffsets[mid]) {
+ hi = mid - 1;
+ } else if ((mid != hi) && (fileOffset >= fileOffsets[mid + 1])) {
+ lo = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+ return null;
+ }
+
+ String? lookupName(int fileOffset) {
+ final index = symbolIndex(fileOffset);
+ return index != null ? names[index] : null;
+ }
+
+ /// Try loading symbols from the binary at [path].
+ static Symbols? load(String path) {
+ try {
+ final loadingBias = loadingBiasOf(path);
+ final symbols = textSymbolsOf(path).toList(growable: false);
+ if (symbols.isEmpty) {
+ return null;
+ }
+
+ // Ensure symbols are sorted to be able to use binary search.
+ symbols.sort((a, b) => a.addr.compareTo(b.addr));
+
+ // `nm` prints virtual addresses - convert these to file offsets
+ // using loading bias.
+ final fileOffsets = Uint32List(symbols.length);
+ final names = List.generate(symbols.length, (i) => symbols[i].name);
+ for (var i = 0; i < symbols.length; i++) {
+ if (symbols[i].addr < loadingBias) {
+ throw StateError(
+ 'unexpected: virtual address ${symbols[i].addr} of symbol '
+ '${symbols[i].name} is less than loading bias $loadingBias');
+ }
+ fileOffsets[i] = symbols[i].addr - loadingBias;
+ }
+
+ return Symbols._(fileOffsets, names);
+ } catch (_) {
+ print('failed to load symbols from $path');
+ return null;
+ }
+ }
+}
diff --git a/runtime/tools/profiling/pubspec.yaml b/runtime/tools/profiling/pubspec.yaml
new file mode 100644
index 0000000..fc0ba9c
--- /dev/null
+++ b/runtime/tools/profiling/pubspec.yaml
@@ -0,0 +1,16 @@
+name: profiling
+description: Utilities for low-level profiling of Dart code
+version: 0.1.0
+publish_to: none
+
+environment:
+ sdk: '>=3.6.0 <4.0.0'
+
+dependencies:
+ protobuf: ^3.1.0
+ fixnum: ^1.1.0
+ path: ^1.9.0
+
+dev_dependencies:
+ lints: ^4.0.0
+ test: ^1.24.0
diff --git a/runtime/tools/run_clang_tidy.dart b/runtime/tools/run_clang_tidy.dart
index 0e0cc94..e0c0634 100644
--- a/runtime/tools/run_clang_tidy.dart
+++ b/runtime/tools/run_clang_tidy.dart
@@ -132,7 +132,7 @@
'runtime/vm/os_thread_linux.h',
'runtime/vm/os_thread_macos.h',
'runtime/vm/os_thread_win.h',
- 'runtime/vm/regexp_assembler_bytecode_inl.h',
+ 'runtime/vm/regexp/regexp_assembler_bytecode_inl.h',
'runtime/vm/simulator_arm.h',
'runtime/vm/simulator_arm64.h',
'runtime/vm/simulator_riscv.h',
diff --git a/runtime/vm/BUILD.gn b/runtime/vm/BUILD.gn
index b0b270fc..2825acf 100644
--- a/runtime/vm/BUILD.gn
+++ b/runtime/vm/BUILD.gn
@@ -39,6 +39,7 @@
import("compiler/compiler_sources.gni")
import("ffi/ffi_sources.gni")
import("heap/heap_sources.gni")
+import("regexp/regexp_sources.gni")
import("vm_sources.gni")
config("libdart_vm_config") {
@@ -193,7 +194,8 @@
sources = vm_sources + rebase_path(compiler_api_sources, ".", "./compiler/") +
rebase_path(disassembler_sources, ".", "./compiler/") +
rebase_path(ffi_sources, ".", "./ffi/") +
- rebase_path(heap_sources, ".", "./heap/")
+ rebase_path(heap_sources, ".", "./heap/") +
+ rebase_path(regexp_sources, ".", "./regexp/")
if (is_android) {
# Android specific workaround for a kernel bug. This source file can't
# go into vm_sources list because it will break Windows build which
diff --git a/runtime/vm/canonical_tables.cc b/runtime/vm/canonical_tables.cc
index b73c90f..245f025 100644
--- a/runtime/vm/canonical_tables.cc
+++ b/runtime/vm/canonical_tables.cc
@@ -4,7 +4,7 @@
#include "vm/canonical_tables.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
namespace dart {
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 317ba35..61811ef 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -47,8 +47,8 @@
#include "vm/os.h"
#include "vm/parser.h"
#include "vm/program_visitor.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_parser.h"
#include "vm/resolver.h"
#include "vm/runtime_entry.h"
#include "vm/stack_trace.h"
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 432e63cb..e36dfcd 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -37,7 +37,7 @@
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
#include "vm/resolver.h"
#include "vm/runtime_entry.h"
#include "vm/scopes.h"
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index b1a81b6..903af47 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -9,7 +9,7 @@
#include "vm/bit_vector.h"
#include "vm/compiler/compiler_state.h"
#include "vm/object_store.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
#include "vm/resolver.h"
#include "vm/timeline.h"
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index c08ea0d..a0d07a6 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -37,8 +37,8 @@
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/parser.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_parser.h"
#include "vm/runtime_entry.h"
#include "vm/symbols.h"
#include "vm/tags.h"
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index 7677680..7d28e45 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -3314,6 +3314,12 @@
GenerateSubtypeNTestCacheStub(assembler, 7);
}
+#ifndef DART_TARGET_SUPPORTS_PROBE_POINTS
+void StubCodeCompiler::GenerateAllocationProbePointStub() {
+ __ Stop("allocation probes are not supported on this platform");
+}
+#endif
+
} // namespace compiler
} // namespace dart
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index c607b79..f8ad177 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -28,6 +28,10 @@
#define __ assembler->
namespace dart {
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+DECLARE_FLAG(bool, generate_probe_points);
+#endif
+
namespace compiler {
// Ensures that [R0] is a new object, if not it will be added to the remembered
@@ -1303,6 +1307,46 @@
GenerateNoSuchMethodDispatcherBody(assembler);
}
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+void StubCodeCompiler::GenerateAllocationProbePointStub() {
+ if (!FLAG_generate_probe_points) {
+ __ Stop("unexpected invocation of an allocation probe");
+ return;
+ }
+
+ // Create a frame on the stack so that we could properly unwind.
+ // Our .eh_frame is very simple and specifies
+ // CFA := FP+16; FP := *(FP+0); LR := *(FP+1);
+ // for the whole .text section.
+ __ EnterStubFrame();
+ // Restore native stack pointer (CSP). Dart SP is currently not
+ // the same because we do not follow ABI which requires native SP
+ // to be 16 bytes aligned. Restoring CSP is important for simpleperf
+ // to be able to unwind the stack - as it copies stack range starting
+ // at CSP before unwinding. If CSP is not restored we copy wrong part
+ // of the stack (CSP is bumped almost to the end of the thread stack).
+ __ andi(CSP, SP, Immediate(~15));
+ // Probe will be placed here by `runtime/tools/profiling/bin/set_uprobe.dart`.
+ const intptr_t probe_offset = __ CodeSize();
+ __ SetupCSPFromThread(THR);
+ __ LeaveStubFrame();
+ __ Ret();
+ // This dummy instruction is encoding offset to the probe point. It will be
+ // used by set_uprobe.dart script.
+ __ TestImmediate(R0, probe_offset);
+}
+#endif
+
+static void InvokeAllocationProbePoint(Assembler* assembler) {
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+ if (FLAG_precompiled_mode && FLAG_generate_probe_points) {
+ __ EnterStubFrame();
+ __ Call(StubCode::AllocationProbePoint());
+ __ LeaveStubFrame();
+ }
+#endif
+}
+
// Called for inline allocation of arrays.
// Input registers (preserved):
// LR: return address.
@@ -1442,6 +1486,7 @@
// Done allocating and initializing the array.
// AllocateArrayABI::kResultReg: new object.
// AllocateArrayABI::kLengthReg: array length as Smi (preserved).
+ InvokeAllocationProbePoint(assembler);
__ ret();
// Unable to allocate the array using the fast inline code, just call
@@ -1478,6 +1523,7 @@
Label slow_case;
__ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+ InvokeAllocationProbePoint(assembler);
__ Ret();
__ Bind(&slow_case);
@@ -1496,6 +1542,7 @@
Label slow_case;
__ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+ InvokeAllocationProbePoint(assembler);
__ Ret();
__ Bind(&slow_case);
@@ -1923,6 +1970,7 @@
// Done allocating and initializing the context.
// R0: new object.
+ InvokeAllocationProbePoint(assembler);
__ ret();
__ Bind(&slow_case);
@@ -1999,6 +2047,7 @@
// Done allocating and initializing the context.
// R0: new object.
+ InvokeAllocationProbePoint(assembler);
__ ret();
__ Bind(&slow_case);
@@ -2345,6 +2394,7 @@
__ Bind(¬_parameterized_case);
} // kClsIdReg = R4, kTypeOffsetReg = R5
+ InvokeAllocationProbePoint(assembler);
__ ret();
__ Bind(&slow_case);
@@ -3940,6 +3990,7 @@
__ b(&loop, UNSIGNED_LESS);
__ WriteAllocationCanary(R1); // Fix overshoot.
+ InvokeAllocationProbePoint(assembler);
__ Ret();
__ Bind(&call_runtime);
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 021ccc5..f578848 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -31,6 +31,11 @@
#define __ assembler->
namespace dart {
+
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+DECLARE_FLAG(bool, generate_probe_points);
+#endif
+
namespace compiler {
// Ensures that [RAX] is a new object, if not it will be added to the remembered
@@ -1240,6 +1245,34 @@
GenerateNoSuchMethodDispatcherBody(assembler, /*receiver_reg=*/RDX);
}
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+void StubCodeCompiler::GenerateAllocationProbePointStub() {
+ if (!FLAG_generate_probe_points) {
+ __ Stop("unexpected invocation of an allocation probe");
+ return;
+ }
+
+ __ EnterStubFrame();
+ // Probe will be placed here by `runtime/tools/profiling/bin/set_uprobe.dart`.
+ const intptr_t probe_offset = __ CodeSize();
+ __ LeaveStubFrame();
+ __ Ret();
+ // This dummy instruction is encoding offset to the probe point. It will be
+ // used by set_uprobe.dart script.
+ __ TestImmediate(RAX, Immediate(probe_offset));
+}
+#endif
+
+static void InvokeAllocationProbePoint(Assembler* assembler) {
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+ if (FLAG_precompiled_mode && FLAG_generate_probe_points) {
+ __ EnterStubFrame();
+ __ Call(StubCode::AllocationProbePoint());
+ __ LeaveStubFrame();
+ }
+#endif
+}
+
// Called for inline allocation of arrays.
// Input registers (preserved):
// AllocateArrayABI::kLengthReg: array length as Smi.
@@ -1360,6 +1393,8 @@
__ cmpq(RDI, RCX);
__ j(UNSIGNED_LESS, &loop);
__ WriteAllocationCanary(RCX);
+
+ InvokeAllocationProbePoint(assembler);
__ ret();
// Unable to allocate the array using the fast inline code, just call
@@ -1393,6 +1428,7 @@
Label slow_case;
__ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+ InvokeAllocationProbePoint(assembler);
__ Ret();
__ Bind(&slow_case);
@@ -1411,6 +1447,7 @@
Label slow_case;
__ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+ InvokeAllocationProbePoint(assembler);
__ Ret();
__ Bind(&slow_case);
@@ -1859,6 +1896,7 @@
// Done allocating and initializing the context.
// RAX: new object.
+ InvokeAllocationProbePoint(assembler);
__ ret();
__ Bind(&slow_case);
@@ -1879,7 +1917,6 @@
// RAX: new object
// Restore the frame pointer.
__ LeaveStubFrame();
-
__ ret();
}
@@ -1930,6 +1967,7 @@
// Done allocating and initializing the context.
// RAX: new object.
+ InvokeAllocationProbePoint(assembler);
__ ret();
__ Bind(&slow_case);
@@ -1952,7 +1990,6 @@
// RAX: new object
// Restore the frame pointer.
__ LeaveStubFrame();
-
__ ret();
}
@@ -2266,6 +2303,7 @@
__ Bind(¬_parameterized_case);
} // kTypeOffsetReg = RDI;
+ InvokeAllocationProbePoint(assembler);
__ ret();
__ Bind(&slow_case);
@@ -3869,6 +3907,7 @@
__ j(UNSIGNED_LESS, &loop, Assembler::kNearJump);
__ WriteAllocationCanary(RCX); // Fix overshoot.
+ InvokeAllocationProbePoint(assembler);
__ ret();
__ Bind(&call_runtime);
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 8dc7311..0afda79 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -884,8 +884,6 @@
// If any other |ErrorPtr| is returned, it means that a |BreakpointLocation|
// was not prepared successfully, and the return value will point to an
// |Error| describing why the |BreakpointLocation| could not be prepared.
- // TODO(derekxu16): Continue looking at all usages of functions that return
- // |ErrorPtr|s and account for Object::no_debuggable_code_error().
ErrorPtr SetBreakpoint(const GrowableHandlePtrArray<const Script>& scripts,
TokenPosition token_pos,
TokenPosition last_token_pos,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 5ff31eb..216a77c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -58,7 +58,7 @@
#include "vm/os.h"
#include "vm/parser.h"
#include "vm/profiler.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
#include "vm/resolver.h"
#include "vm/reusable_handles.h"
#include "vm/reverse_pc_lookup_cache.h"
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index e1be656..7a8f9f3 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -28,7 +28,7 @@
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
#include "vm/resolver.h"
#include "vm/scopes.h"
#include "vm/stack_frame.h"
diff --git a/runtime/vm/regexp.cc b/runtime/vm/regexp/regexp.cc
similarity index 99%
rename from runtime/vm/regexp.cc
rename to runtime/vm/regexp/regexp.cc
index a930e5c..501924b 100644
--- a/runtime/vm/regexp.cc
+++ b/runtime/vm/regexp/regexp.cc
@@ -2,7 +2,7 @@
// 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.
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
#include <memory>
@@ -12,15 +12,15 @@
#include "unicode/uniset.h"
#include "vm/dart_entry.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_assembler_bytecode.h"
-#include "vm/regexp_ast.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler_bytecode.h"
+#include "vm/regexp/regexp_ast.h"
+#include "vm/regexp/unibrow-inl.h"
#include "vm/symbols.h"
#include "vm/thread.h"
-#include "vm/unibrow-inl.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#define Z (zone())
diff --git a/runtime/vm/regexp.h b/runtime/vm/regexp/regexp.h
similarity index 99%
rename from runtime/vm/regexp.h
rename to runtime/vm/regexp/regexp.h
index 8657ec7..3675b9e 100644
--- a/runtime/vm/regexp.h
+++ b/runtime/vm/regexp/regexp.h
@@ -2,13 +2,13 @@
// 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.
-#ifndef RUNTIME_VM_REGEXP_H_
-#define RUNTIME_VM_REGEXP_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_H_
+#define RUNTIME_VM_REGEXP_REGEXP_H_
#include "platform/unicode.h"
#include "vm/object.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
#include "vm/splay-tree.h"
namespace dart {
@@ -1527,4 +1527,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_H_
diff --git a/runtime/vm/regexp_assembler.cc b/runtime/vm/regexp/regexp_assembler.cc
similarity index 97%
rename from runtime/vm/regexp_assembler.cc
rename to runtime/vm/regexp/regexp_assembler.cc
index 0f1ad62..0fac2fb 100644
--- a/runtime/vm/regexp_assembler.cc
+++ b/runtime/vm/regexp/regexp_assembler.cc
@@ -2,16 +2,16 @@
// 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.
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
#include "unicode/uchar.h"
#include "platform/unicode.h"
#include "vm/flags.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
+#include "vm/regexp/unibrow-inl.h"
#include "vm/runtime_entry.h"
-#include "vm/unibrow-inl.h"
namespace dart {
diff --git a/runtime/vm/regexp_assembler.h b/runtime/vm/regexp/regexp_assembler.h
similarity index 98%
rename from runtime/vm/regexp_assembler.h
rename to runtime/vm/regexp/regexp_assembler.h
index d2482c2..59cfba3 100644
--- a/runtime/vm/regexp_assembler.h
+++ b/runtime/vm/regexp/regexp_assembler.h
@@ -2,8 +2,8 @@
// 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.
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_H_
#include "vm/object.h"
@@ -266,4 +266,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_ASSEMBLER_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_H_
diff --git a/runtime/vm/regexp_assembler_bytecode.cc b/runtime/vm/regexp/regexp_assembler_bytecode.cc
similarity index 97%
rename from runtime/vm/regexp_assembler_bytecode.cc
rename to runtime/vm/regexp/regexp_assembler_bytecode.cc
index ac37a0a..927bba9 100644
--- a/runtime/vm/regexp_assembler_bytecode.cc
+++ b/runtime/vm/regexp/regexp_assembler_bytecode.cc
@@ -2,16 +2,16 @@
// 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.
-#include "vm/regexp_assembler_bytecode.h"
+#include "vm/regexp/regexp_assembler_bytecode.h"
#include "vm/exceptions.h"
#include "vm/object_store.h"
-#include "vm/regexp.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_assembler_bytecode_inl.h"
-#include "vm/regexp_bytecodes.h"
-#include "vm/regexp_interpreter.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler_bytecode_inl.h"
+#include "vm/regexp/regexp_bytecodes.h"
+#include "vm/regexp/regexp_interpreter.h"
+#include "vm/regexp/regexp_parser.h"
#include "vm/timeline.h"
namespace dart {
diff --git a/runtime/vm/regexp_assembler_bytecode.h b/runtime/vm/regexp/regexp_assembler_bytecode.h
similarity index 96%
rename from runtime/vm/regexp_assembler_bytecode.h
rename to runtime/vm/regexp/regexp_assembler_bytecode.h
index c8c87af..bbe18ebf 100644
--- a/runtime/vm/regexp_assembler_bytecode.h
+++ b/runtime/vm/regexp/regexp_assembler_bytecode.h
@@ -2,11 +2,11 @@
// 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.
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_H_
#include "vm/object.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
namespace dart {
@@ -143,4 +143,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_H_
diff --git a/runtime/vm/regexp_assembler_bytecode_inl.h b/runtime/vm/regexp/regexp_assembler_bytecode_inl.h
similarity index 85%
rename from runtime/vm/regexp_assembler_bytecode_inl.h
rename to runtime/vm/regexp/regexp_assembler_bytecode_inl.h
index 76e1dc16..a05c788 100644
--- a/runtime/vm/regexp_assembler_bytecode_inl.h
+++ b/runtime/vm/regexp/regexp_assembler_bytecode_inl.h
@@ -4,10 +4,10 @@
// A light-weight assembler for the Irregexp byte code.
-#include "vm/regexp_bytecodes.h"
+#include "vm/regexp/regexp_bytecodes.h"
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_INL_H_
namespace dart {
@@ -51,4 +51,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_INL_H_
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp/regexp_assembler_ir.cc
similarity index 99%
rename from runtime/vm/regexp_assembler_ir.cc
rename to runtime/vm/regexp/regexp_assembler_ir.cc
index eee8061..8a65995 100644
--- a/runtime/vm/regexp_assembler_ir.cc
+++ b/runtime/vm/regexp/regexp_assembler_ir.cc
@@ -4,7 +4,7 @@
#if !defined(DART_PRECOMPILED_RUNTIME)
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
#include <utility>
@@ -17,7 +17,7 @@
#include "vm/dart_entry.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
#include "vm/resolver.h"
#include "vm/runtime_entry.h"
#include "vm/stack_frame.h"
diff --git a/runtime/vm/regexp_assembler_ir.h b/runtime/vm/regexp/regexp_assembler_ir.h
similarity index 98%
rename from runtime/vm/regexp_assembler_ir.h
rename to runtime/vm/regexp/regexp_assembler_ir.h
index b65dba2..0f6eee5 100644
--- a/runtime/vm/regexp_assembler_ir.h
+++ b/runtime/vm/regexp/regexp_assembler_ir.h
@@ -2,13 +2,13 @@
// 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.
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_IR_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_IR_H_
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/il.h"
#include "vm/object.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
namespace dart {
@@ -445,4 +445,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_IR_H_
diff --git a/runtime/vm/regexp_ast.cc b/runtime/vm/regexp/regexp_ast.cc
similarity index 99%
rename from runtime/vm/regexp_ast.cc
rename to runtime/vm/regexp/regexp_ast.cc
index ad0c300..3bfd878 100644
--- a/runtime/vm/regexp_ast.cc
+++ b/runtime/vm/regexp/regexp_ast.cc
@@ -2,7 +2,7 @@
// 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.
-#include "vm/regexp_ast.h"
+#include "vm/regexp/regexp_ast.h"
#include "platform/utils.h"
#include "vm/os.h"
diff --git a/runtime/vm/regexp_ast.h b/runtime/vm/regexp/regexp_ast.h
similarity index 98%
rename from runtime/vm/regexp_ast.h
rename to runtime/vm/regexp/regexp_ast.h
index b040d50..2bee2f0 100644
--- a/runtime/vm/regexp_ast.h
+++ b/runtime/vm/regexp/regexp_ast.h
@@ -2,13 +2,13 @@
// 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.
-#ifndef RUNTIME_VM_REGEXP_AST_H_
-#define RUNTIME_VM_REGEXP_AST_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_AST_H_
+#define RUNTIME_VM_REGEXP_REGEXP_AST_H_
#include "platform/globals.h"
#include "platform/utils.h"
#include "vm/allocation.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
namespace dart {
@@ -445,4 +445,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_AST_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_AST_H_
diff --git a/runtime/vm/regexp_bytecodes.h b/runtime/vm/regexp/regexp_bytecodes.h
similarity index 97%
rename from runtime/vm/regexp_bytecodes.h
rename to runtime/vm/regexp/regexp_bytecodes.h
index e85a335..271a859 100644
--- a/runtime/vm/regexp_bytecodes.h
+++ b/runtime/vm/regexp/regexp_bytecodes.h
@@ -2,8 +2,8 @@
// 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.
-#ifndef RUNTIME_VM_REGEXP_BYTECODES_H_
-#define RUNTIME_VM_REGEXP_BYTECODES_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_BYTECODES_H_
+#define RUNTIME_VM_REGEXP_REGEXP_BYTECODES_H_
namespace dart {
@@ -83,4 +83,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_BYTECODES_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_BYTECODES_H_
diff --git a/runtime/vm/regexp_interpreter.cc b/runtime/vm/regexp/regexp_interpreter.cc
similarity index 98%
rename from runtime/vm/regexp_interpreter.cc
rename to runtime/vm/regexp/regexp_interpreter.cc
index dd56fe8..11f6f59 100644
--- a/runtime/vm/regexp_interpreter.cc
+++ b/runtime/vm/regexp/regexp_interpreter.cc
@@ -7,15 +7,15 @@
#include <memory>
#include <utility>
-#include "heap/safepoint.h"
-#include "vm/regexp_interpreter.h"
+#include "vm/heap/safepoint.h"
+#include "vm/regexp/regexp_interpreter.h"
#include "platform/unicode.h"
#include "vm/object.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_bytecodes.h"
-#include "vm/unibrow-inl.h"
-#include "vm/unibrow.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_bytecodes.h"
+#include "vm/regexp/unibrow-inl.h"
+#include "vm/regexp/unibrow.h"
namespace dart {
diff --git a/runtime/vm/regexp_interpreter.h b/runtime/vm/regexp/regexp_interpreter.h
similarity index 84%
rename from runtime/vm/regexp_interpreter.h
rename to runtime/vm/regexp/regexp_interpreter.h
index f81ea43..78c7b56 100644
--- a/runtime/vm/regexp_interpreter.h
+++ b/runtime/vm/regexp/regexp_interpreter.h
@@ -4,8 +4,8 @@
// A simple interpreter for the Irregexp byte code.
-#ifndef RUNTIME_VM_REGEXP_INTERPRETER_H_
-#define RUNTIME_VM_REGEXP_INTERPRETER_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_INTERPRETER_H_
+#define RUNTIME_VM_REGEXP_REGEXP_INTERPRETER_H_
#include "vm/allocation.h"
#include "vm/object.h"
@@ -26,4 +26,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_INTERPRETER_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_INTERPRETER_H_
diff --git a/runtime/vm/regexp_parser.cc b/runtime/vm/regexp/regexp_parser.cc
similarity index 99%
rename from runtime/vm/regexp_parser.cc
rename to runtime/vm/regexp/regexp_parser.cc
index aaf23b0..792a1b3 100644
--- a/runtime/vm/regexp_parser.cc
+++ b/runtime/vm/regexp/regexp_parser.cc
@@ -2,7 +2,7 @@
// 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.
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_parser.h"
#include "unicode/uchar.h"
#include "unicode/uniset.h"
diff --git a/runtime/vm/regexp_parser.h b/runtime/vm/regexp/regexp_parser.h
similarity index 98%
rename from runtime/vm/regexp_parser.h
rename to runtime/vm/regexp/regexp_parser.h
index c070d7e..72c3f2f 100644
--- a/runtime/vm/regexp_parser.h
+++ b/runtime/vm/regexp/regexp_parser.h
@@ -2,12 +2,12 @@
// 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.
-#ifndef RUNTIME_VM_REGEXP_PARSER_H_
-#define RUNTIME_VM_REGEXP_PARSER_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_PARSER_H_
+#define RUNTIME_VM_REGEXP_REGEXP_PARSER_H_
#include "vm/allocation.h"
#include "vm/growable_array.h"
-#include "vm/regexp_ast.h"
+#include "vm/regexp/regexp_ast.h"
namespace dart {
@@ -264,4 +264,4 @@
} // namespace dart
-#endif // RUNTIME_VM_REGEXP_PARSER_H_
+#endif // RUNTIME_VM_REGEXP_REGEXP_PARSER_H_
diff --git a/runtime/vm/regexp/regexp_sources.gni b/runtime/vm/regexp/regexp_sources.gni
new file mode 100644
index 0000000..04e640c
--- /dev/null
+++ b/runtime/vm/regexp/regexp_sources.gni
@@ -0,0 +1,27 @@
+# Copyright (c) 2024, 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.
+
+regexp_sources = [
+ "regexp.cc",
+ "regexp.h",
+ "regexp_assembler.cc",
+ "regexp_assembler.h",
+ "regexp_assembler_bytecode.cc",
+ "regexp_assembler_bytecode.h",
+ "regexp_assembler_bytecode_inl.h",
+ "regexp_assembler_ir.cc",
+ "regexp_assembler_ir.h",
+ "regexp_ast.cc",
+ "regexp_ast.h",
+ "regexp_bytecodes.h",
+ "regexp_interpreter.cc",
+ "regexp_interpreter.h",
+ "regexp_parser.cc",
+ "regexp_parser.h",
+ "unibrow-inl.h",
+ "unibrow.cc",
+ "unibrow.h",
+]
+
+regexp_sources_tests = [ "regexp_test.cc" ]
diff --git a/runtime/vm/regexp_test.cc b/runtime/vm/regexp/regexp_test.cc
similarity index 96%
rename from runtime/vm/regexp_test.cc
rename to runtime/vm/regexp/regexp_test.cc
index 1a252ec..3f92ccd 100644
--- a/runtime/vm/regexp_test.cc
+++ b/runtime/vm/regexp/regexp_test.cc
@@ -6,8 +6,8 @@
#include "vm/isolate.h"
#include "vm/object.h"
-#include "vm/regexp.h"
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp.h"
+#include "vm/regexp/regexp_assembler_ir.h"
#include "vm/unit_test.h"
namespace dart {
diff --git a/runtime/vm/unibrow-inl.h b/runtime/vm/regexp/unibrow-inl.h
similarity index 87%
rename from runtime/vm/unibrow-inl.h
rename to runtime/vm/regexp/unibrow-inl.h
index ab6f5d5..a8f1037 100644
--- a/runtime/vm/unibrow-inl.h
+++ b/runtime/vm/regexp/unibrow-inl.h
@@ -2,10 +2,10 @@
// 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.
-#ifndef RUNTIME_VM_UNIBROW_INL_H_
-#define RUNTIME_VM_UNIBROW_INL_H_
+#ifndef RUNTIME_VM_REGEXP_UNIBROW_INL_H_
+#define RUNTIME_VM_REGEXP_UNIBROW_INL_H_
-#include "vm/unibrow.h"
+#include "vm/regexp/unibrow.h"
#include "platform/assert.h"
@@ -45,4 +45,4 @@
} // namespace unibrow
-#endif // RUNTIME_VM_UNIBROW_INL_H_
+#endif // RUNTIME_VM_REGEXP_UNIBROW_INL_H_
diff --git a/runtime/vm/unibrow.cc b/runtime/vm/regexp/unibrow.cc
similarity index 99%
rename from runtime/vm/unibrow.cc
rename to runtime/vm/regexp/unibrow.cc
index 4f468ee..affa699 100644
--- a/runtime/vm/unibrow.cc
+++ b/runtime/vm/regexp/unibrow.cc
@@ -4,8 +4,8 @@
//
// This file was generated at 2014-10-08 15:25:47.940335 (in v8, copied to dart)
-#include "vm/unibrow.h"
-#include "vm/unibrow-inl.h"
+#include "vm/regexp/unibrow.h"
+#include "vm/regexp/unibrow-inl.h"
#include <stdio.h>
#include <stdlib.h>
diff --git a/runtime/vm/unibrow.h b/runtime/vm/regexp/unibrow.h
similarity index 94%
rename from runtime/vm/unibrow.h
rename to runtime/vm/regexp/unibrow.h
index c01c2aa..3caada9 100644
--- a/runtime/vm/unibrow.h
+++ b/runtime/vm/regexp/unibrow.h
@@ -2,8 +2,8 @@
// 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.
-#ifndef RUNTIME_VM_UNIBROW_H_
-#define RUNTIME_VM_UNIBROW_H_
+#ifndef RUNTIME_VM_REGEXP_UNIBROW_H_
+#define RUNTIME_VM_REGEXP_UNIBROW_H_
#include <sys/types.h>
@@ -69,4 +69,4 @@
} // namespace unibrow
-#endif // RUNTIME_VM_UNIBROW_H_
+#endif // RUNTIME_VM_REGEXP_UNIBROW_H_
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index 7b7216c..e2699a8 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -24,6 +24,13 @@
DECLARE_FLAG(bool, precompiled_mode);
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+DEFINE_FLAG(bool,
+ generate_probe_points,
+ false,
+ "Generate probe points for installation of user space probes");
+#endif
+
StubCode::StubCodeEntry StubCode::entries_[kNumStubEntries] = {
#if defined(DART_PRECOMPILED_RUNTIME)
#define STUB_CODE_DECLARE(name) {nullptr, #name},
diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h
index 2d29e52..7c14e75 100644
--- a/runtime/vm/stub_code_list.h
+++ b/runtime/vm/stub_code_list.h
@@ -18,6 +18,14 @@
V(LazySpecializeTypeTest) \
V(LazySpecializeNullableTypeTest)
+#if (defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID)) && \
+ (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+// Currently we support probe points only Linux and Android (X64 and ARM64).
+#define DART_TARGET_SUPPORTS_PROBE_POINTS 1
+#endif
+
+#define PROBE_POINT_STUBS_LIST(V) V(AllocationProbePoint)
+
// List of stubs created in the VM isolate, these stubs are shared by different
// isolates running in this dart process.
#define VM_STUB_CODE_LIST(V) \
@@ -28,6 +36,7 @@
V(WriteBarrier) \
V(WriteBarrierWrappers) \
V(ArrayWriteBarrier) \
+ PROBE_POINT_STUBS_LIST(V) \
V(AllocateArray) \
V(AllocateMint) \
V(AllocateDouble) \
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index 4a42526..bc62caf 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -252,22 +252,6 @@
"raw_object.h",
"raw_object_fields.cc",
"raw_object_fields.h",
- "regexp.cc",
- "regexp.h",
- "regexp_assembler.cc",
- "regexp_assembler.h",
- "regexp_assembler_bytecode.cc",
- "regexp_assembler_bytecode.h",
- "regexp_assembler_bytecode_inl.h",
- "regexp_assembler_ir.cc",
- "regexp_assembler_ir.h",
- "regexp_ast.cc",
- "regexp_ast.h",
- "regexp_bytecodes.h",
- "regexp_interpreter.cc",
- "regexp_interpreter.h",
- "regexp_parser.cc",
- "regexp_parser.h",
"report.cc",
"report.h",
"resolver.cc",
@@ -361,9 +345,6 @@
"token_position.h",
"type_testing_stubs.cc",
"type_testing_stubs.h",
- "unibrow-inl.h",
- "unibrow.cc",
- "unibrow.h",
"unicode.cc",
"unicode_data.cc",
"unwinding_records.cc",
@@ -454,7 +435,6 @@
"os_test.cc",
"port_test.cc",
"profiler_test.cc",
- "regexp_test.cc",
"ring_buffer_test.cc",
"scopes_test.cc",
"service_test.cc",
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index e03cf3e..93cf022 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -43,7 +43,8 @@
# ......snapshots/
# ........analysis_server.dart.snapshot
# ........dart2bytecode.snapshot (if dart_dynamic_modules)
-# ........dart2js.dart.snapshot
+# ........dart2js_aot.dart.snapshot (AOT snapshot, if not on ia32)
+# ........dart2js.dart.snapshot (JIT snapshot only on ia32)
# ........dart2wasm_product.snapshot (if not on ia32)
# ........dartdev.dart.snapshot (app-jit snapshot or kernel dill file)
# ........dartdevc.dart.snapshot
@@ -153,10 +154,6 @@
_full_sdk_snapshots = _platform_sdk_snapshots + [
[
- "dart2js",
- "../utils/compiler:dart2js",
- ],
- [
"dartdevc",
"../utils/ddc:dartdevc",
],
@@ -165,6 +162,17 @@
"../utils/bazel:kernel_worker",
],
]
+if (dart_target_arch != "ia32" && dart_target_arch != "x86") {
+ _full_sdk_snapshots += [ [
+ "dart2js_aot",
+ "../utils/compiler:dart2js_sdk_aot",
+ ] ]
+} else {
+ _full_sdk_snapshots += [ [
+ "dart2js",
+ "../utils/compiler:dart2js",
+ ] ]
+}
# Libraries that go under lib/
_full_sdk_libraries = [
diff --git a/sdk/lib/core/duration.dart b/sdk/lib/core/duration.dart
index 64a7874..4f62fc8 100644
--- a/sdk/lib/core/duration.dart
+++ b/sdk/lib/core/duration.dart
@@ -34,12 +34,12 @@
/// as "hours" to the constructor, and can be larger than 59.
///
/// ```dart
-/// const fastestMarathon = Duration(hours: 2, minutes: 3, seconds: 2);
+/// const fastestMarathon = Duration(hours: 2, minutes: 0, seconds: 35);
/// print(fastestMarathon.inDays); // 0
/// print(fastestMarathon.inHours); // 2
-/// print(fastestMarathon.inMinutes); // 123
-/// print(fastestMarathon.inSeconds); // 7382
-/// print(fastestMarathon.inMilliseconds); // 7382000
+/// print(fastestMarathon.inMinutes); // 120
+/// print(fastestMarathon.inSeconds); // 7235
+/// print(fastestMarathon.inMilliseconds); // 7235000
/// ```
/// The duration can be negative, in which case
/// all the properties derived from the duration are also non-positive.
diff --git a/tests/web/wasm/multi_module_stress_test.dart b/tests/web/wasm/multi_module_stress_test.dart
index 89cf936..fef42d4 100644
--- a/tests/web/wasm/multi_module_stress_test.dart
+++ b/tests/web/wasm/multi_module_stress_test.dart
@@ -2,7 +2,7 @@
// 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.
-// dart2wasmOptions=--extra-compiler-option=--enable-multi-module-stress-test-mode
+// dart2wasmOptions=--extra-compiler-option=--enable-multi-module-stress-test-mode --extra-compiler-option=--no-js-compatibility
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/util/memory_compiler.dart' as dart2js;
diff --git a/tools/VERSION b/tools/VERSION
index 7caf555..eda9e50 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -25,7 +25,7 @@
#
CHANNEL dev
MAJOR 3
-MINOR 6
+MINOR 7
PATCH 0
-PRERELEASE 334
+PRERELEASE 0
PRERELEASE_PATCH 0
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 38757db..b1f1013 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2865,7 +2865,8 @@
"script": "tools/build.py",
"arguments": [
"runtime",
- "dart2wasm"
+ "dart2wasm",
+ "create_sdk"
]
},
{
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index 8cca7fb..87f550d 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -109,7 +109,7 @@
# default 'language' "category" with code generated for both CFE and Analyzer,
# while other categories can be tailored more specifically.
-current-version: '3.6.0'
+current-version: '3.7.0'
features:
variance:
diff --git a/utils/compiler/BUILD.gn b/utils/compiler/BUILD.gn
index 7a3093b..f76196a 100644
--- a/utils/compiler/BUILD.gn
+++ b/utils/compiler/BUILD.gn
@@ -87,6 +87,22 @@
name = "dart2js_aot"
}
+aot_snapshot("dart2js_sdk_aot") {
+ deps = [ ":dart2js_create_snapshot_entry" ]
+
+ main_dart = "$target_gen_dir/dart2js.dart"
+ name = "dart2js_aot.dart"
+ output = "$root_gen_dir/dart2js_aot.dart.snapshot"
+
+ # dartaotruntime has dart_product_config applied to it,
+ # so it is built in # product mode in both release and
+ # product builds, and is only built in debug mode in debug
+ # builds. The following line ensures that the dartaotruntime
+ # and dartdevc_aot snapshot in an SDK build are
+ # always compatible with each other.
+ force_product_mode = !dart_debug
+}
+
compile_platform("compile_dart2js_platform_unsound") {
single_root_scheme = "org-dartlang-sdk"
single_root_base = rebase_path("$sdk_root/")