Version 2.15.0-159.0.dev
Merge commit 'fa428da3477061de50edab069384359ab677332d' into 'dev'
diff --git a/build/config/clang/clang.gni b/build/config/clang/clang.gni
index 50bdb34..c2ba3f7 100644
--- a/build/config/clang/clang.gni
+++ b/build/config/clang/clang.gni
@@ -2,8 +2,15 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+_toolchain_cpu = host_cpu
+if (host_os == "mac") {
+ # TODO(https://fxbug.dev/73385): Use arm64 toolchain on arm64 when it exists.
+ _toolchain_cpu = "x64"
+}
+
default_clang_prefix =
- rebase_path("//buildtools/${host_os}-${host_cpu}/clang/bin", root_build_dir)
+ rebase_path("//buildtools/${host_os}-${_toolchain_cpu}/clang/bin",
+ root_build_dir)
declare_args() {
clang_prefix = default_clang_prefix
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
index 6c9e4d0..34ecd3a 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
@@ -92,8 +92,7 @@
}
}
}
- }
- if (member is MethodDeclaration && !member.isStatic) {
+ } else if (member is MethodDeclaration && !member.isStatic) {
nodesToMove.add(member);
elementsToMove.add(member.declaredElement!);
}
@@ -177,8 +176,9 @@
for (var node in widgetClass.members) {
if (nodesToMove.contains(node)) {
if (replaceOffset == 0) {
- var linesRange = utils.getLinesRange(range.node(node));
- replaceOffset = linesRange.offset;
+ var comments = node.beginToken.precedingComments;
+ var start = comments ?? node;
+ replaceOffset = utils.getLineContentStart(start.offset);
}
if (node == buildMethod) {
hasBuildMethod = true;
@@ -236,6 +236,14 @@
if (writeEmptyLine) {
builder.writeln();
}
+
+ var comments = member.beginToken.precedingComments;
+ if (comments != null) {
+ var offset = utils.getLineContentStart(comments.offset);
+ var length = comments.end - offset;
+ builder.writeln(utils.getText(offset, length));
+ }
+
var text = rewriteWidgetMemberReferences(member);
builder.write(text);
// Write empty lines between members, but not before the first.
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
index 02ce651..dfb00eb 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
@@ -27,6 +27,96 @@
);
}
+ Future<void> test_comment() async {
+ await resolveTestCode(r'''
+import 'package:flutter/material.dart';
+
+class /*caret*/MyWidget extends StatelessWidget {
+ // something for a
+ final bool a = false;
+
+ const MyWidget();
+
+ // another for b
+ final bool b = true;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+''');
+ await assertHasAssist('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatefulWidget {
+
+ const MyWidget();
+
+ @override
+ State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+ // something for a
+ final bool a = false;
+
+ // another for b
+ final bool b = true;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+''');
+ }
+
+ Future<void> test_comment_documentation() async {
+ await resolveTestCode(r'''
+import 'package:flutter/material.dart';
+
+class /*caret*/MyWidget extends StatelessWidget {
+ /// something for a
+ final bool a = false;
+
+ const MyWidget();
+
+ /// another for b
+ final bool b = true;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+''');
+ await assertHasAssist('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatefulWidget {
+
+ const MyWidget();
+
+ @override
+ State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+ /// something for a
+ final bool a = false;
+
+ /// another for b
+ final bool b = true;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+''');
+ }
+
Future<void> test_empty() async {
await resolveTestCode(r'''
import 'package:flutter/material.dart';
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 1255129..474f80d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -80,7 +80,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 181;
+ static const int DATA_VERSION = 182;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart b/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
index a509482..7aea6d5 100644
--- a/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
+++ b/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
@@ -869,7 +869,7 @@
* operator.
*/
static const ParserErrorCode TYPE_PARAMETER_ON_OPERATOR = ParserErrorCode(
- 'TYPE_PARAMETERS_ON_OPERATOR',
+ 'TYPE_PARAMETER_ON_OPERATOR',
"Types parameters aren't allowed when defining an operator.",
correction: "Try removing the type parameters.");
diff --git a/pkg/analyzer_utilities/lib/tools.dart b/pkg/analyzer_utilities/lib/tools.dart
index f28fc4e..abb3375 100644
--- a/pkg/analyzer_utilities/lib/tools.dart
+++ b/pkg/analyzer_utilities/lib/tools.dart
@@ -228,10 +228,14 @@
}
static String formatText(String text) {
- var file = File(join(Directory.systemTemp.path, 'gen.dart'));
+ var tmpDir = Directory.systemTemp.createTempSync('format');
+ var file = File(join(tmpDir.path, 'gen.dart'));
file.writeAsStringSync(text);
formatFile(file);
- return file.readAsStringSync();
+ var result = file.readAsStringSync();
+ file.deleteSync();
+ tmpDir.deleteSync();
+ return result;
}
static void _throwIfExitCode(ProcessResult result) {
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md
index 7c60f28..7a0f2b5 100644
--- a/pkg/native_stack_traces/CHANGELOG.md
+++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## 0.4.4
+
+- Added handling of dynamic tables for testing.
+
## 0.4.3
- Exported some more of the ELF utilities for use in Dart tests.
diff --git a/pkg/native_stack_traces/lib/elf.dart b/pkg/native_stack_traces/lib/elf.dart
index 3b1624b..3316655 100644
--- a/pkg/native_stack_traces/lib/elf.dart
+++ b/pkg/native_stack_traces/lib/elf.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.
-export 'src/elf.dart' show Elf, Section, Symbol;
+export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
export 'src/constants.dart'
show
isolateDataSymbolName,
diff --git a/pkg/native_stack_traces/lib/src/elf.dart b/pkg/native_stack_traces/lib/src/elf.dart
index 548b5ac..268d5d80 100644
--- a/pkg/native_stack_traces/lib/src/elf.dart
+++ b/pkg/native_stack_traces/lib/src/elf.dart
@@ -214,7 +214,10 @@
static const _ELFDATA2MSB = 0x02;
void writeToStringBuffer(StringBuffer buffer) {
- buffer..write('Format is ')..write(wordSize * 8)..write(' bits');
+ buffer
+ ..write('Format is ')
+ ..write(wordSize * 8)
+ ..write(' bits');
switch (endian) {
case Endian.little:
buffer..writeln(' and little-endian');
@@ -342,6 +345,15 @@
int get length => _entries.length;
ProgramHeaderEntry operator [](int index) => _entries[index];
+ ProgramHeaderEntry? loadSegmentFor(int address) {
+ for (final entry in _entries) {
+ if (entry.vaddr <= address && address <= entry.vaddr + entry.memsz) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
static ProgramHeader fromReader(Reader reader, ElfHeader header) {
final programReader = reader.refocusedCopy(
header.programHeaderOffset, header.programHeaderSize);
@@ -352,7 +364,10 @@
void writeToStringBuffer(StringBuffer buffer) {
for (var i = 0; i < length; i++) {
- if (i != 0) buffer..writeln()..writeln();
+ if (i != 0)
+ buffer
+ ..writeln()
+ ..writeln();
buffer
..write('Entry ')
..write(i)
@@ -422,6 +437,17 @@
static const _SHT_NOBITS = 8;
static const _SHT_DYNSYM = 11;
+ // sh_flags constants from ELF specification.
+ static const _SHF_WRITE = 0x1;
+ static const _SHF_ALLOC = 0x2;
+ static const _SHF_EXECINSTR = 0x4;
+
+ bool get isWritable => flags & _SHF_WRITE != 0;
+ bool get isAllocated => flags & _SHF_ALLOC != 0;
+ bool get isExecutable => flags & _SHF_EXECINSTR != 0;
+
+ bool get hasBits => type != _SHT_NOBITS;
+
void setName(StringTable nameTable) {
name = nameTable[nameIndex]!;
}
@@ -495,7 +521,10 @@
void writeToStringBuffer(StringBuffer buffer) {
for (var i = 0; i < entries.length; i++) {
- if (i != 0) buffer..writeln()..writeln();
+ if (i != 0)
+ buffer
+ ..writeln()
+ ..writeln();
buffer
..write('Entry ')
..write(i)
@@ -536,6 +565,8 @@
return SymbolTable.fromReader(reader, entry);
case SectionHeaderEntry._SHT_NOTE:
return Note.fromReader(reader, entry);
+ case SectionHeaderEntry._SHT_DYNAMIC:
+ return DynamicTable.fromReader(reader, entry);
default:
return Section._(entry);
}
@@ -713,7 +744,10 @@
SymbolVisibility get visibility => SymbolVisibility.values[other & 0x03];
void writeToStringBuffer(StringBuffer buffer) {
- buffer..write('"')..write(name)..write('" =>');
+ buffer
+ ..write('"')
+ ..write(name)
+ ..write('" =>');
switch (bind) {
case SymbolBinding.STB_GLOBAL:
buffer..write(' a global');
@@ -794,6 +828,109 @@
}
}
+/// Represents d_tag constants from ELF specification.
+enum DynamicTableTag {
+ DT_NULL,
+ DT_NEEDED,
+ DT_PLTRELSZ,
+ DT_PLTGOT,
+ DT_HASH,
+ DT_STRTAB,
+ DT_SYMTAB,
+ DT_RELA,
+ DT_RELASZ,
+ DT_RELAENT,
+ DT_STRSZ,
+ DT_SYMENT,
+ // Later d_tag values are not currently used in Dart ELF files.
+}
+
+/// The dynamic table, which contains entries pointing to various relocated
+/// addresses.
+class DynamicTable extends Section {
+ // We don't use DynamicTableTag for the key so that we can handle ELF files
+ // that may use unknown (to us) tags.
+ final Map<int, int> _entries;
+ final _wordSize;
+
+ DynamicTable._(SectionHeaderEntry entry, this._entries, this._wordSize)
+ : super._(entry);
+
+ static DynamicTable fromReader(Reader reader, SectionHeaderEntry entry) {
+ final sectionReader = reader.refocusedCopy(entry.offset, entry.size);
+ final entries = <int, int>{};
+ while (true) {
+ // Each entry is a tag and a value, both native word sized.
+ final tag = _readElfNative(sectionReader);
+ final value = _readElfNative(sectionReader);
+ // A DT_NULL entry signfies the end of entries.
+ if (tag == DynamicTableTag.DT_NULL.index) break;
+ entries[tag] = value;
+ }
+ return DynamicTable._(entry, entries, sectionReader.wordSize);
+ }
+
+ int? operator [](DynamicTableTag tag) => _entries[tag.index];
+ bool containsKey(DynamicTableTag tag) => _entries.containsKey(tag.index);
+
+ // To avoid depending on EnumName.name from 2.15.
+ static const _tagStrings = {
+ DynamicTableTag.DT_NULL: 'DT_NULL',
+ DynamicTableTag.DT_NEEDED: 'DT_NEEDED',
+ DynamicTableTag.DT_PLTRELSZ: 'DT_PLTRELSZ',
+ DynamicTableTag.DT_PLTGOT: 'DT_PLTGOT',
+ DynamicTableTag.DT_HASH: 'DT_HASH',
+ DynamicTableTag.DT_STRTAB: 'DT_STRTAB',
+ DynamicTableTag.DT_SYMTAB: 'DT_SYMTAB',
+ DynamicTableTag.DT_RELA: 'DT_RELA',
+ DynamicTableTag.DT_RELASZ: 'DT_RELASZ',
+ DynamicTableTag.DT_STRSZ: 'DT_STRSZ',
+ DynamicTableTag.DT_SYMENT: 'DT_SYMENT',
+ };
+ static final _maxTagStringLength = (_tagStrings.values.toList()
+ ..sort((s1, s2) => s2.length - s1.length))
+ .first
+ .length;
+
+ @override
+ void writeToStringBuffer(StringBuffer buffer) {
+ buffer
+ ..write('Section "')
+ ..write(headerEntry.name)
+ ..writeln('" is a dynamic table:');
+ for (var kv in _entries.entries) {
+ buffer.write(' ');
+ if (kv.key < DynamicTableTag.values.length) {
+ final tag = DynamicTableTag.values[kv.key];
+ buffer
+ ..write(_tagStrings[tag]?.padRight(_maxTagStringLength))
+ ..write(' => ');
+ switch (tag) {
+ // These are relocated addresses.
+ case DynamicTableTag.DT_HASH:
+ case DynamicTableTag.DT_PLTGOT:
+ case DynamicTableTag.DT_SYMTAB:
+ case DynamicTableTag.DT_STRTAB:
+ case DynamicTableTag.DT_RELA:
+ buffer
+ ..write('0x')
+ ..writeln(paddedHex(kv.value, _wordSize));
+ break;
+ // Other entries are just values or offsets.
+ default:
+ buffer.writeln(kv.value);
+ }
+ } else {
+ buffer
+ ..write("Unknown tag ")
+ ..write(kv.key)
+ ..write(' => ')
+ ..writeln(kv.value);
+ }
+ }
+ }
+}
+
/// Information parsed from an Executable and Linking Format (ELF) file.
class Elf {
final ElfHeader _header;
@@ -819,6 +956,21 @@
Iterable<Section> namedSections(String name) =>
_sectionsByName[name] ?? <Section>[];
+ /// Checks that the contents of a given section have valid addresses when the
+ /// file contents for the corresponding segment is loaded into memory.
+ ///
+ /// Returns false for sections that are not allocated or where the address
+ /// does not correspond to file contents (i.e., NOBITS sections).
+ bool sectionHasValidSegmentAddresses(Section section) {
+ final headerEntry = section.headerEntry;
+ if (!headerEntry.isAllocated || !headerEntry.hasBits) return false;
+ final segment = _programHeader.loadSegmentFor(headerEntry.addr);
+ if (segment == null) return false;
+ return (headerEntry.addr < (segment.vaddr + segment.filesz)) &&
+ (headerEntry.addr + headerEntry.size) <=
+ (segment.vaddr + segment.filesz);
+ }
+
/// Lookup of a dynamic symbol by name.
///
/// Returns -1 if there is no dynamic symbol that matches [name].
diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml
index bdf1f94..7dbc3b2 100644
--- a/pkg/native_stack_traces/pubspec.yaml
+++ b/pkg/native_stack_traces/pubspec.yaml
@@ -1,6 +1,6 @@
name: native_stack_traces
description: Utilities for working with non-symbolic stack traces.
-version: 0.4.3
+version: 0.4.4
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces
diff --git a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
index 091087d..2bd55a2 100644
--- a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
+++ b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
@@ -13,6 +13,7 @@
import "dart:typed_data";
import 'package:expect/expect.dart';
+import 'package:native_stack_traces/elf.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
import 'package:path/path.dart' as path;
@@ -63,6 +64,7 @@
'--elf=$scriptWholeSnapshot',
scriptDill,
]);
+ checkElf(scriptWholeSnapshot);
final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
await run(genSnapshot, <String>[
@@ -72,6 +74,7 @@
'--strip',
scriptDill,
]);
+ checkElf(scriptStrippedOnlySnapshot);
final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
@@ -83,6 +86,8 @@
'--save-debugging-info=$scriptDebuggingInfo',
scriptDill,
]);
+ checkElf(scriptStrippedSnapshot);
+ checkElf(scriptDebuggingInfo);
// Run the resulting scripts, saving the stack traces.
final wholeTrace = await runError(aotRuntime, <String>[
@@ -184,3 +189,46 @@
}
}
}
+
+void checkElf(String filename) {
+ print("Checking ELF file $filename:");
+ final elf = Elf.fromFile(filename);
+ Expect.isNotNull(elf);
+ final dynamic = elf!.namedSections(".dynamic").single as DynamicTable;
+
+ // Check the dynamic string table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_STRTAB), "no string table entry");
+ final dynstr = elf.namedSections(".dynstr").single;
+ print(".dynstr address = ${dynamic[DynamicTableTag.DT_STRTAB]}");
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynstr),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRTAB], dynstr.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_STRSZ),
+ "no string table size entry");
+ print(".dynstr size = ${dynamic[DynamicTableTag.DT_STRSZ]}");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRSZ], dynstr.headerEntry.size);
+
+ // Check the dynamic symbol table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_SYMTAB), "no symbol table entry");
+ print(".dynsym address = ${dynamic[DynamicTableTag.DT_SYMTAB]}");
+ final dynsym = elf.namedSections(".dynsym").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynsym),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_SYMTAB], dynsym.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_SYMENT),
+ "no symbol table entry size entry");
+ print(".dynsym entry size = ${dynamic[DynamicTableTag.DT_SYMENT]}");
+ Expect.equals(
+ dynamic[DynamicTableTag.DT_SYMENT], dynsym.headerEntry.entrySize);
+
+ // Check the hash table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_HASH), "no hash table entry");
+ print(".hash address = ${dynamic[DynamicTableTag.DT_HASH]}");
+ final hash = elf.namedSections(".hash").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(hash),
+ "hash table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_HASH], hash.headerEntry.addr);
+}
diff --git a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
index cc8f9b4..db548f4 100644
--- a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
+++ b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
@@ -15,6 +15,7 @@
import "dart:typed_data";
import 'package:expect/expect.dart';
+import 'package:native_stack_traces/elf.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
import 'package:path/path.dart' as path;
@@ -65,6 +66,7 @@
'--elf=$scriptWholeSnapshot',
scriptDill,
]);
+ checkElf(scriptWholeSnapshot);
final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
await run(genSnapshot, <String>[
@@ -74,6 +76,7 @@
'--strip',
scriptDill,
]);
+ checkElf(scriptStrippedOnlySnapshot);
final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
@@ -85,6 +88,8 @@
'--save-debugging-info=$scriptDebuggingInfo',
scriptDill,
]);
+ checkElf(scriptStrippedSnapshot);
+ checkElf(scriptDebuggingInfo);
// Run the resulting scripts, saving the stack traces.
final wholeTrace = await runError(aotRuntime, <String>[
@@ -186,3 +191,46 @@
}
}
}
+
+void checkElf(String filename) {
+ print("Checking ELF file $filename:");
+ final elf = Elf.fromFile(filename);
+ Expect.isNotNull(elf);
+ final dynamic = elf.namedSections(".dynamic").single as DynamicTable;
+
+ // Check the dynamic string table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_STRTAB), "no string table entry");
+ final dynstr = elf.namedSections(".dynstr").single;
+ print(".dynstr address = ${dynamic[DynamicTableTag.DT_STRTAB]}");
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynstr),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRTAB], dynstr.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_STRSZ),
+ "no string table size entry");
+ print(".dynstr size = ${dynamic[DynamicTableTag.DT_STRSZ]}");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRSZ], dynstr.headerEntry.size);
+
+ // Check the dynamic symbol table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_SYMTAB), "no symbol table entry");
+ print(".dynsym address = ${dynamic[DynamicTableTag.DT_SYMTAB]}");
+ final dynsym = elf.namedSections(".dynsym").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynsym),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_SYMTAB], dynsym.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_SYMENT),
+ "no symbol table entry size entry");
+ print(".dynsym entry size = ${dynamic[DynamicTableTag.DT_SYMENT]}");
+ Expect.equals(
+ dynamic[DynamicTableTag.DT_SYMENT], dynsym.headerEntry.entrySize);
+
+ // Check the hash table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_HASH), "no hash table entry");
+ print(".hash address = ${dynamic[DynamicTableTag.DT_HASH]}");
+ final hash = elf.namedSections(".hash").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(hash),
+ "hash table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_HASH], hash.headerEntry.addr);
+}
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 5537222..b3c92e0 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -92,7 +92,7 @@
// No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
// to clear the upper bits as this instructions uses negative indices as part
// of FP-relative loads.
- // TODO(compressed-pointers): Can we guarentee the index is already
+ // TODO(compressed-pointers): Can we guarantee the index is already
// sign-extended if always comes for an args-descriptor load?
__ movsxd(index, index);
#endif
@@ -123,7 +123,7 @@
// No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
// to clear the upper bits as this instructions uses negative indices as part
// of FP-relative stores.
- // TODO(compressed-pointers): Can we guarentee the index is already
+ // TODO(compressed-pointers): Can we guarantee the index is already
// sign-extended if always comes for an args-descriptor load?
__ movsxd(index, index);
#endif
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index d4748c4..2523166 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -1347,6 +1347,7 @@
// changes while compiling. Propagate that 'error' and retry compilation
// later.
ASSERT(CompilerState::Current().is_aot() ||
+ (error.ptr() == Object::out_of_memory_error().ptr()) ||
Compiler::IsBackgroundCompilation() || error.IsUnhandledException());
Thread::Current()->long_jump_base()->Jump(1, error);
UNREACHABLE();
diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc
index a4f550f..28e8224 100644
--- a/runtime/vm/elf.cc
+++ b/runtime/vm/elf.cc
@@ -173,6 +173,8 @@
}
bool IsWritable() const { return (flags & elf::SHF_WRITE) == elf::SHF_WRITE; }
+ bool HasBits() const { return type != elf::SectionHeaderType::SHT_NOBITS; }
+
// Returns whether the size of a section can change.
bool HasBeenFinalized() const {
// Sections can grow or shrink up until Elf::ComputeOffsets has been run,
@@ -1559,8 +1561,27 @@
// We now do several passes over the collected sections to reorder them in
// a way that minimizes segments (and thus padding) in the resulting snapshot.
- // If a build ID was created, we put it after the program table so it can
- // be read with a minimum number of bytes from the ELF file.
+ auto add_sections_matching =
+ [&](const std::function<bool(Section*)>& should_add) {
+ // We order the sections in a segment so all non-NOBITS sections come
+ // before NOBITS sections, since the former sections correspond to the
+ // file contents for the segment.
+ for (auto* const section : sections_) {
+ if (!section->HasBits()) continue;
+ if (should_add(section)) {
+ add_to_reordered_sections(section);
+ }
+ }
+ for (auto* const section : sections_) {
+ if (section->HasBits()) continue;
+ if (should_add(section)) {
+ add_to_reordered_sections(section);
+ }
+ }
+ };
+
+ // If a build ID was created, we put it right after the program table so it
+ // can be read with a minimum number of bytes from the ELF file.
auto* const build_id = Find(Elf::kBuildIdNoteName);
if (build_id != nullptr) {
ASSERT(build_id->type == elf::SectionHeaderType::SHT_NOTE);
@@ -1568,38 +1589,31 @@
}
// Now add the other non-writable, non-executable allocated sections.
- for (auto* const section : sections_) {
- if (section == build_id) continue; // Already added.
- if (section->IsAllocated() && !section->IsWritable() &&
- !section->IsExecutable()) {
- add_to_reordered_sections(section);
- }
- }
+ add_sections_matching([&](Section* section) -> bool {
+ if (section == build_id) return false; // Already added.
+ return section->IsAllocated() && !section->IsWritable() &&
+ !section->IsExecutable();
+ });
// Now add the executable sections in a new segment.
- for (auto* const section : sections_) {
- if (section->IsExecutable()) { // Implies IsAllocated() && !IsWritable()
- add_to_reordered_sections(section);
- }
- }
+ add_sections_matching([](Section* section) -> bool {
+ return section->IsExecutable(); // Implies IsAllocated() && !IsWritable()
+ });
// Now add all the writable sections.
- for (auto* const section : sections_) {
- if (section->IsWritable()) { // Implies IsAllocated() && !IsExecutable()
- add_to_reordered_sections(section);
- }
- }
+ add_sections_matching([](Section* section) -> bool {
+ return section->IsWritable(); // Implies IsAllocated() && !IsExecutable()
+ });
// We put all non-reserved unallocated sections last. Otherwise, they would
// affect the file offset but not the memory offset of any following allocated
// sections. Doing it in this order makes it easier to keep file and memory
// offsets page-aligned with respect to each other, which is required for
// some loaders.
- for (intptr_t i = 1; i < num_sections; i++) {
- auto* const section = sections_[i];
- if (section->IsAllocated()) continue;
- add_to_reordered_sections(section);
- }
+ add_sections_matching([](Section* section) -> bool {
+ // Don't re-add the initial reserved section.
+ return !section->IsReservedSection() && !section->IsAllocated();
+ });
// All sections should have been accounted for in the loops above.
ASSERT_EQUAL(sections_.length(), reordered_sections.length());
diff --git a/runtime/vm/heap/become.cc b/runtime/vm/heap/become.cc
index 4bb646d..2a4e0aa 100644
--- a/runtime/vm/heap/become.cc
+++ b/runtime/vm/heap/become.cc
@@ -217,6 +217,27 @@
};
#endif
+Become::Become() {
+ IsolateGroup* group = Thread::Current()->isolate_group();
+ ASSERT(group->become() == nullptr); // Only one outstanding become at a time.
+ group->set_become(this);
+}
+
+Become::~Become() {
+ Thread::Current()->isolate_group()->set_become(nullptr);
+}
+
+void Become::Add(const Object& before, const Object& after) {
+ pointers_.Add(before.ptr());
+ pointers_.Add(after.ptr());
+}
+
+void Become::VisitObjectPointers(ObjectPointerVisitor* visitor) {
+ if (pointers_.length() != 0) {
+ visitor->VisitPointers(&pointers_[0], pointers_.length());
+ }
+}
+
void Become::MakeDummyObject(const Instance& instance) {
// Make the forward pointer point to itself.
// This is needed to distinguish it from a real forward object.
@@ -228,7 +249,7 @@
return GetForwardedObject(object) == object;
}
-void Become::CrashDump(ObjectPtr before_obj, ObjectPtr after_obj) {
+static void CrashDump(ObjectPtr before_obj, ObjectPtr after_obj) {
OS::PrintErr("DETECTED FATAL ISSUE IN BECOME MAPPINGS\n");
OS::PrintErr("BEFORE ADDRESS: %#" Px "\n", static_cast<uword>(before_obj));
@@ -256,7 +277,7 @@
}
}
-void Become::ElementsForwardIdentity(const Array& before, const Array& after) {
+void Become::Forward() {
Thread* thread = Thread::Current();
auto heap = thread->isolate_group()->heap();
@@ -264,10 +285,9 @@
HeapIterationScope his(thread);
// Setup forwarding pointers.
- ASSERT(before.Length() == after.Length());
- for (intptr_t i = 0; i < before.Length(); i++) {
- ObjectPtr before_obj = before.At(i);
- ObjectPtr after_obj = after.At(i);
+ for (intptr_t i = 0; i < pointers_.length(); i += 2) {
+ ObjectPtr before_obj = pointers_[i];
+ ObjectPtr after_obj = pointers_[i + 1];
if (before_obj == after_obj) {
FATAL("become: Cannot self-forward");
@@ -304,10 +324,11 @@
FollowForwardingPointers(thread);
#if defined(DEBUG)
- for (intptr_t i = 0; i < before.Length(); i++) {
- ASSERT(before.At(i) == after.At(i));
+ for (intptr_t i = 0; i < pointers_.length(); i += 2) {
+ ASSERT(pointers_[i] == pointers_[i + 1]);
}
#endif
+ pointers_.Clear();
}
void Become::FollowForwardingPointers(Thread* thread) {
diff --git a/runtime/vm/heap/become.h b/runtime/vm/heap/become.h
index 9914056..4b945bf 100644
--- a/runtime/vm/heap/become.h
+++ b/runtime/vm/heap/become.h
@@ -6,6 +6,7 @@
#define RUNTIME_VM_HEAP_BECOME_H_
#include "platform/atomic.h"
+#include "platform/growable_array.h"
#include "vm/allocation.h"
#include "vm/raw_object.h"
@@ -68,15 +69,39 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(ForwardingCorpse);
};
-// TODO(johnmccutchan): Refactor this class so that it is not all static and
-// provides utility methods for building the mapping of before and after.
-class Become : public AllStatic {
+// Forward/exchange object identity within pairs of objects.
+//
+// Forward: Redirects all pointers to each 'before' object to the corresponding
+// 'after' object. Every 'before' object is guaranteed to be unreachable after
+// the operation. The identity hash of the 'before' object is retained.
+//
+// This is useful for atomically applying behavior and schema changes, which can
+// be done by allocating fresh objects with the new schema and forwarding the
+// identity of the old objects to the new objects.
+//
+// Exchange: Redirect all pointers to each 'before' object to the corresponding
+// 'after' object and vice versa. Both objects remain reachable after the
+// operation.
+//
+// This is useful for implementing certain types of proxies. For example, an
+// infrequently accessed object may be written to disk and swapped with a
+// so-called "husk", and swapped back when it is later accessed.
+//
+// This operation is named 'become' after its original in Smalltalk:
+// x become: y "exchange identity for one pair"
+// x becomeForward: y "forward identity for one pair"
+// #(x ...) elementsExchangeIdentityWith: #(y ...)
+// #(x ...) elementsForwardIdentityTo: #(y ...)
+class Become {
public:
- // Smalltalk's one-way bulk become (Array>>#elementsForwardIdentityTo:).
- // Redirects all pointers to elements of 'before' to the corresponding element
- // in 'after'. Every element in 'before' is guaranteed to be not reachable.
- // Useful for atomically applying behavior and schema changes.
- static void ElementsForwardIdentity(const Array& before, const Array& after);
+ Become();
+ ~Become();
+
+ void Add(const Object& before, const Object& after);
+ void Forward();
+ void Exchange() { UNIMPLEMENTED(); }
+
+ void VisitObjectPointers(ObjectPointerVisitor* visitor);
// Convert and instance object into a dummy object,
// making the instance independent of its class.
@@ -88,7 +113,8 @@
static void FollowForwardingPointers(Thread* thread);
private:
- static void CrashDump(ObjectPtr before_obj, ObjectPtr after_obj);
+ MallocGrowableArray<ObjectPtr> pointers_;
+ DISALLOW_COPY_AND_ASSIGN(Become);
};
} // namespace dart
diff --git a/runtime/vm/heap/become_test.cc b/runtime/vm/heap/become_test.cc
index 031e497b..76f325a 100644
--- a/runtime/vm/heap/become_test.cc
+++ b/runtime/vm/heap/become_test.cc
@@ -13,24 +13,25 @@
namespace dart {
void TestBecomeForward(Heap::Space before_space, Heap::Space after_space) {
+ // Allocate the container in old space to test the remembered set.
+ const Array& container = Array::Handle(Array::New(1, Heap::kOld));
+
const String& before_obj = String::Handle(String::New("old", before_space));
const String& after_obj = String::Handle(String::New("new", after_space));
-
+ container.SetAt(0, before_obj);
EXPECT(before_obj.ptr() != after_obj.ptr());
- // Allocate the arrays in old space to test the remembered set.
- const Array& before = Array::Handle(Array::New(1, Heap::kOld));
- before.SetAt(0, before_obj);
- const Array& after = Array::Handle(Array::New(1, Heap::kOld));
- after.SetAt(0, after_obj);
-
- Become::ElementsForwardIdentity(before, after);
+ Become become;
+ become.Add(before_obj, after_obj);
+ become.Forward();
EXPECT(before_obj.ptr() == after_obj.ptr());
+ EXPECT(container.At(0) == after_obj.ptr());
GCTestHelper::CollectAllGarbage();
EXPECT(before_obj.ptr() == after_obj.ptr());
+ EXPECT(container.At(0) == after_obj.ptr());
}
ISOLATE_UNIT_TEST_CASE(BecomeFowardOldToOld) {
@@ -62,11 +63,9 @@
EXPECT_EQ(peer, heap->GetPeer(before_obj.ptr()));
EXPECT_EQ(no_peer, heap->GetPeer(after_obj.ptr()));
- const Array& before = Array::Handle(Array::New(1, Heap::kOld));
- before.SetAt(0, before_obj);
- const Array& after = Array::Handle(Array::New(1, Heap::kOld));
- after.SetAt(0, after_obj);
- Become::ElementsForwardIdentity(before, after);
+ Become become;
+ become.Add(before_obj, after_obj);
+ become.Forward();
EXPECT(before_obj.ptr() == after_obj.ptr());
EXPECT_EQ(peer, heap->GetPeer(before_obj.ptr()));
@@ -86,11 +85,9 @@
EXPECT_EQ(id, heap->GetObjectId(before_obj.ptr()));
EXPECT_EQ(no_id, heap->GetObjectId(after_obj.ptr()));
- const Array& before = Array::Handle(Array::New(1, Heap::kOld));
- before.SetAt(0, before_obj);
- const Array& after = Array::Handle(Array::New(1, Heap::kOld));
- after.SetAt(0, after_obj);
- Become::ElementsForwardIdentity(before, after);
+ Become become;
+ become.Add(before_obj, after_obj);
+ become.Forward();
EXPECT(before_obj.ptr() == after_obj.ptr());
EXPECT_EQ(id, heap->GetObjectId(before_obj.ptr()));
@@ -114,11 +111,9 @@
EXPECT_EQ(no_id,
isolate->forward_table_old()->GetValueExclusive(after_obj.ptr()));
- const Array& before = Array::Handle(Array::New(1, Heap::kOld));
- before.SetAt(0, before_obj);
- const Array& after = Array::Handle(Array::New(1, Heap::kOld));
- after.SetAt(0, after_obj);
- Become::ElementsForwardIdentity(before, after);
+ Become become;
+ become.Add(before_obj, after_obj);
+ become.Forward();
EXPECT(before_obj.ptr() == after_obj.ptr());
EXPECT_EQ(id,
@@ -142,12 +137,9 @@
EXPECT(before_obj.ptr() != after_obj.ptr());
- const Array& before = Array::Handle(Array::New(1, Heap::kOld));
- before.SetAt(0, before_obj);
- const Array& after = Array::Handle(Array::New(1, Heap::kOld));
- after.SetAt(0, after_obj);
-
- Become::ElementsForwardIdentity(before, after);
+ Become become;
+ become.Add(before_obj, after_obj);
+ become.Forward();
EXPECT(before_obj.ptr() == after_obj.ptr());
EXPECT(!after_obj.ptr()->untag()->IsRemembered());
@@ -174,11 +166,9 @@
Object::Handle(card_remembered_array.At(0)).ToCString());
}
- const Array& before = Array::Handle(Array::New(1, Heap::kOld));
- before.SetAt(0, old_element);
- const Array& after = Array::Handle(Array::New(1, Heap::kOld));
- after.SetAt(0, new_element);
- Become::ElementsForwardIdentity(before, after);
+ Become become;
+ become.Add(old_element, new_element);
+ become.Forward();
EXPECT(old_element.ptr() == new_element.ptr());
EXPECT(old_element.ptr()->IsNewObject());
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index 46fc02f..74ca4cc 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -187,7 +187,7 @@
// processing here is idempotent, so repeated visits only hurt performance
// but not correctness. Duplicatation is expected to be low.
// By the absence of a special case, we are treating WeakProperties as
- // strong references here. This guarentees a WeakProperty will only be
+ // strong references here. This guarantees a WeakProperty will only be
// added to the delayed_weak_properties_ list of the worker that
// encounters it during ordinary marking. This is in the same spirit as
// the eliminated write barrier, which would have added the newly written
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 5386b13..940b893 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -2899,6 +2899,10 @@
visitor->VisitPointer(
reinterpret_cast<ObjectPtr*>(&(source()->loaded_blobs_)));
}
+
+ if (become() != nullptr) {
+ become()->VisitObjectPointers(visitor);
+ }
}
void IsolateGroup::VisitStackPointers(ObjectPointerVisitor* visitor,
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index d6555b3..21cd81c 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -46,6 +46,7 @@
// Forward declarations.
class ApiState;
class BackgroundCompiler;
+class Become;
class Capability;
class CodeIndexTable;
class Debugger;
@@ -687,6 +688,9 @@
#endif
}
+ Become* become() const { return become_; }
+ void set_become(Become* become) { become_ = become; }
+
uint64_t id() const { return id_; }
static void Init();
@@ -829,6 +833,7 @@
RelaxedAtomic<intptr_t> reload_every_n_stack_overflow_checks_;
ProgramReloadContext* program_reload_context_ = nullptr;
#endif
+ Become* become_ = nullptr;
#define ISOLATE_METRIC_VARIABLE(type, variable, name, unit) \
type metric_##variable##_;
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 71e5d04..9ad8617 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -178,8 +178,7 @@
shared_class_table_(shared_class_table),
mapping_(mapping),
new_fields_offsets_(new_fields_offsets),
- before_(zone, 16),
- after_(zone, 16) {}
+ before_(zone, 16) {}
void InstanceMorpher::AddObject(ObjectPtr object) {
ASSERT(object->GetClassId() == cid_);
@@ -187,65 +186,65 @@
before_.Add(&instance);
}
-InstancePtr InstanceMorpher::Morph(const Instance& instance) const {
- // Code can reference constants / canonical objects either directly in the
- // instruction stream (ia32) or via an object pool.
- //
- // We have the following invariants:
- //
- // a) Those canonical objects don't change state (i.e. are not mutable):
- // our optimizer can e.g. execute loads of such constants at
- // compile-time.
- //
- // => We ensure that const-classes with live constants cannot be
- // reloaded to become non-const classes (see Class::CheckReload).
- //
- // b) Those canonical objects live in old space: e.g. on ia32 the scavenger
- // does not make the RX pages writable and therefore cannot update
- // pointers embedded in the instruction stream.
- //
- // In order to maintain these invariants we ensure to always morph canonical
- // objects to old space.
- const bool is_canonical = instance.IsCanonical();
- const Heap::Space space = is_canonical ? Heap::kOld : Heap::kNew;
- const auto& result = Instance::Handle(
- Z, Instance::NewFromCidAndSize(shared_class_table_, cid_, space));
+void InstanceMorpher::CreateMorphedCopies(Become* become) {
+ Instance& after = Instance::Handle(Z);
+ Object& value = Object::Handle(Z);
+ for (intptr_t i = 0; i < before_.length(); i++) {
+ const Instance& before = *before_.At(i);
- // We preserve the canonical bit of the object, since this object is present
- // in the class's constants.
- if (is_canonical) {
- result.SetCanonical();
- }
+ // Code can reference constants / canonical objects either directly in the
+ // instruction stream (ia32) or via an object pool.
+ //
+ // We have the following invariants:
+ //
+ // a) Those canonical objects don't change state (i.e. are not mutable):
+ // our optimizer can e.g. execute loads of such constants at
+ // compile-time.
+ //
+ // => We ensure that const-classes with live constants cannot be
+ // reloaded to become non-const classes (see Class::CheckReload).
+ //
+ // b) Those canonical objects live in old space: e.g. on ia32 the
+ // scavenger does not make the RX pages writable and therefore cannot
+ // update pointers embedded in the instruction stream.
+ //
+ // In order to maintain these invariants we ensure to always morph canonical
+ // objects to old space.
+ const bool is_canonical = before.IsCanonical();
+ const Heap::Space space = is_canonical ? Heap::kOld : Heap::kNew;
+ after = Instance::NewFromCidAndSize(shared_class_table_, cid_, space);
+
+ // We preserve the canonical bit of the object, since this object is present
+ // in the class's constants.
+ if (is_canonical) {
+ after.SetCanonical();
+ }
#if defined(HASH_IN_OBJECT_HEADER)
- const uint32_t hash = Object::GetCachedHash(instance.ptr());
- Object::SetCachedHashIfNotSet(result.ptr(), hash);
+ const uint32_t hash = Object::GetCachedHash(before.ptr());
+ Object::SetCachedHashIfNotSet(after.ptr(), hash);
#endif
- // Morph the context from instance to result using mapping_.
- Object& value = Object::Handle(Z);
- for (intptr_t i = 0; i < mapping_->length(); i += 2) {
- intptr_t from_offset = mapping_->At(i);
- intptr_t to_offset = mapping_->At(i + 1);
- ASSERT(from_offset > 0);
- ASSERT(to_offset > 0);
- value = instance.RawGetFieldAtOffset(from_offset);
- result.RawSetFieldAtOffset(to_offset, value);
- }
+ // Morph the context from [before] to [after] using mapping_.
+ for (intptr_t i = 0; i < mapping_->length(); i += 2) {
+ intptr_t from_offset = mapping_->At(i);
+ intptr_t to_offset = mapping_->At(i + 1);
+ ASSERT(from_offset > 0);
+ ASSERT(to_offset > 0);
+ value = before.RawGetFieldAtOffset(from_offset);
+ after.RawSetFieldAtOffset(to_offset, value);
+ }
- for (intptr_t i = 0; i < new_fields_offsets_->length(); i++) {
- const intptr_t field_offset = new_fields_offsets_->At(i);
- result.RawSetFieldAtOffset(field_offset, Object::sentinel());
- }
+ for (intptr_t i = 0; i < new_fields_offsets_->length(); i++) {
+ const intptr_t field_offset = new_fields_offsets_->At(i);
+ after.RawSetFieldAtOffset(field_offset, Object::sentinel());
+ }
- // Convert the instance into a filler object.
- Become::MakeDummyObject(instance);
- return result.ptr();
-}
+ // Convert the old instance into a filler object. We will switch to the new
+ // class table before the next heap walk, so there must be no instances of
+ // any class with the old size.
+ Become::MakeDummyObject(before);
-void InstanceMorpher::CreateMorphedCopies() {
- for (intptr_t i = 0; i < before_.length(); i++) {
- const Instance& copy = Instance::Handle(Z, Morph(*before_.At(i)));
- after_.Add(©);
+ become->Add(before, after);
}
}
@@ -372,34 +371,6 @@
static uword Hash(const Object& obj) { return Library::Cast(obj).UrlHash(); }
};
-class BecomeMapTraits {
- public:
- static bool ReportStats() { return false; }
- static const char* Name() { return "BecomeMapTraits"; }
-
- static bool IsMatch(const Object& a, const Object& b) {
- return a.ptr() == b.ptr();
- }
-
- static uword Hash(const Object& obj) {
- if (obj.IsLibrary()) {
- return Library::Cast(obj).UrlHash();
- } else if (obj.IsClass()) {
- return String::HashRawSymbol(Class::Cast(obj).Name());
- } else if (obj.IsField()) {
- return String::HashRawSymbol(Field::Cast(obj).name());
- } else if (obj.IsClosure()) {
- return String::HashRawSymbol(
- Function::Handle(Closure::Cast(obj).function()).name());
- } else if (obj.IsLibraryPrefix()) {
- return String::HashRawSymbol(LibraryPrefix::Cast(obj).name());
- } else {
- FATAL1("Unexpected type in become: %s\n", obj.ToCString());
- }
- return 0;
- }
-};
-
bool ProgramReloadContext::IsSameClass(const Class& a, const Class& b) {
// TODO(turnidge): We need to look at generic type arguments for
// synthetic mixin classes. Their names are not necessarily unique
@@ -461,8 +432,6 @@
removed_class_set_storage_(Array::null()),
old_libraries_set_storage_(Array::null()),
library_map_storage_(Array::null()),
- become_map_storage_(Array::null()),
- become_enum_mappings_(GrowableObjectArray::null()),
saved_root_library_(Library::null()),
saved_libraries_(GrowableObjectArray::null()) {
// NOTE: DO NOT ALLOCATE ANY RAW OBJECTS HERE. The ProgramReloadContext is not
@@ -793,10 +762,7 @@
// active.
ASSERT(HasNoTasks(heap));
- const Array& before = Array::Handle(Z, Array::New(count));
- const Array& after = Array::Handle(Z, Array::New(count));
-
- MorphInstancesPhase1Allocate(&locator, before, after);
+ MorphInstancesPhase1Allocate(&locator, IG->become());
{
// Apply the new class table before "become". Become will replace
// all the instances of the old size with forwarding corpses, then
@@ -811,7 +777,7 @@
IG->program_reload_context()->DiscardSavedClassTable(
/*is_rollback=*/false);
}
- MorphInstancesPhase2Become(before, after);
+ MorphInstancesPhase2Become(IG->become());
discard_class_tables = false;
}
@@ -1044,10 +1010,6 @@
HashTables::New<UnorderedHashSet<LibraryMapTraits> >(4);
library_map_storage_ =
HashTables::New<UnorderedHashMap<LibraryMapTraits> >(4);
- become_map_storage_ = HashTables::New<UnorderedHashMap<BecomeMapTraits> >(4);
- // Keep a separate array for enum mappings to avoid having to invoke
- // hashCode on the instances.
- become_enum_mappings_ = GrowableObjectArray::New(Heap::kOld);
// While reloading everything we do must be reversible so that we can abort
// safely if the reload fails. This function stashes things to the side and
@@ -1639,39 +1601,7 @@
}
void ProgramReloadContext::CommitAfterInstanceMorphing() {
- {
- const GrowableObjectArray& become_enum_mappings =
- GrowableObjectArray::Handle(become_enum_mappings_);
- UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
- intptr_t replacement_count =
- become_map.NumOccupied() + become_enum_mappings.Length() / 2;
- const Array& before =
- Array::Handle(Array::New(replacement_count, Heap::kOld));
- const Array& after =
- Array::Handle(Array::New(replacement_count, Heap::kOld));
- Object& obj = Object::Handle();
- intptr_t replacement_index = 0;
- UnorderedHashMap<BecomeMapTraits>::Iterator it(&become_map);
- while (it.MoveNext()) {
- const intptr_t entry = it.Current();
- obj = become_map.GetKey(entry);
- before.SetAt(replacement_index, obj);
- obj = become_map.GetPayload(entry, 0);
- after.SetAt(replacement_index, obj);
- replacement_index++;
- }
- for (intptr_t i = 0; i < become_enum_mappings.Length(); i += 2) {
- obj = become_enum_mappings.At(i);
- before.SetAt(replacement_index, obj);
- obj = become_enum_mappings.At(i + 1);
- after.SetAt(replacement_index, obj);
- replacement_index++;
- }
- ASSERT(replacement_index == replacement_count);
- become_map.Release();
-
- Become::ElementsForwardIdentity(before, after);
- }
+ become_.Forward();
// Rehash constants map for all classes. Constants are hashed by content, and
// content may have changed from fields being added or removed.
@@ -1747,8 +1677,7 @@
void IsolateGroupReloadContext::MorphInstancesPhase1Allocate(
ObjectLocator* locator,
- const Array& before,
- const Array& after) {
+ Become* become) {
ASSERT(HasInstanceMorphers());
if (FLAG_trace_reload) {
@@ -1764,27 +1693,14 @@
(count > 1) ? "s" : "");
for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
- instance_morphers_.At(i)->CreateMorphedCopies();
+ instance_morphers_.At(i)->CreateMorphedCopies(become);
}
-
- // Create the inputs for Become.
- intptr_t index = 0;
- for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
- InstanceMorpher* morpher = instance_morphers_.At(i);
- for (intptr_t j = 0; j < morpher->before()->length(); j++) {
- before.SetAt(index, *morpher->before()->At(j));
- after.SetAt(index, *morpher->after()->At(j));
- index++;
- }
- }
- ASSERT(index == count);
}
-void IsolateGroupReloadContext::MorphInstancesPhase2Become(const Array& before,
- const Array& after) {
+void IsolateGroupReloadContext::MorphInstancesPhase2Become(Become* become) {
ASSERT(HasInstanceMorphers());
- Become::ElementsForwardIdentity(before, after);
+ become->Forward();
// The heap now contains only instances with the new size. Ordinary GC is safe
// again.
}
@@ -2550,26 +2466,12 @@
const Field& new_field) {
ASSERT(old_field.is_static());
ASSERT(new_field.is_static());
-
AddBecomeMapping(old_field, new_field);
}
void ProgramReloadContext::AddBecomeMapping(const Object& old,
const Object& neu) {
- ASSERT(become_map_storage_ != Array::null());
- UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
- bool update = become_map.UpdateOrInsert(old, neu);
- ASSERT(!update);
- become_map_storage_ = become_map.Release().ptr();
-}
-
-void ProgramReloadContext::AddEnumBecomeMapping(const Object& old,
- const Object& neu) {
- const GrowableObjectArray& become_enum_mappings =
- GrowableObjectArray::Handle(become_enum_mappings_);
- become_enum_mappings.Add(old);
- become_enum_mappings.Add(neu);
- ASSERT((become_enum_mappings.Length() % 2) == 0);
+ become_.Add(old, neu);
}
void ProgramReloadContext::RebuildDirectSubclasses() {
diff --git a/runtime/vm/isolate_reload.h b/runtime/vm/isolate_reload.h
index 69a399c..939f512 100644
--- a/runtime/vm/isolate_reload.h
+++ b/runtime/vm/isolate_reload.h
@@ -13,6 +13,7 @@
#include "vm/globals.h"
#include "vm/growable_array.h"
#include "vm/hash_map.h"
+#include "vm/heap/become.h"
#include "vm/log.h"
#include "vm/object.h"
@@ -68,24 +69,15 @@
ZoneGrowableArray<intptr_t>* new_fields_offsets);
virtual ~InstanceMorpher() {}
- // Called on each instance that needs to be morphed.
- InstancePtr Morph(const Instance& instance) const;
-
// Adds an object to be morphed.
void AddObject(ObjectPtr object);
// Create the morphed objects based on the before() list.
- void CreateMorphedCopies();
+ void CreateMorphedCopies(Become* become);
// Append the morper info to JSON array.
void AppendTo(JSONArray* array);
- // Returns the list of objects that need to be morphed.
- const GrowableArray<const Instance*>* before() const { return &before_; }
-
- // Returns the list of morphed objects (matches order in before()).
- const GrowableArray<const Instance*>* after() const { return &after_; }
-
// Returns the cid associated with the from_ and to_ class.
intptr_t cid() const { return cid_; }
@@ -100,7 +92,6 @@
ZoneGrowableArray<intptr_t>* new_fields_offsets_;
GrowableArray<const Instance*> before_;
- GrowableArray<const Instance*> after_;
};
class ReasonForCancelling : public ZoneAllocated {
@@ -220,10 +211,8 @@
void CheckpointSharedClassTable();
- void MorphInstancesPhase1Allocate(ObjectLocator* locator,
- const Array& before,
- const Array& after);
- void MorphInstancesPhase2Become(const Array& before, const Array& after);
+ void MorphInstancesPhase1Allocate(ObjectLocator* locator, Become* become);
+ void MorphInstancesPhase2Become(Become* become);
void ForEachIsolate(std::function<void(Isolate*)> callback);
@@ -401,9 +390,10 @@
const Library& original);
void AddStaticFieldMapping(const Field& old_field, const Field& new_field);
void AddBecomeMapping(const Object& old, const Object& neu);
- void AddEnumBecomeMapping(const Object& old, const Object& neu);
void RebuildDirectSubclasses();
+ Become become_;
+
ObjectPtr* from() {
return reinterpret_cast<ObjectPtr*>(&old_classes_set_storage_);
}
@@ -412,8 +402,6 @@
ArrayPtr removed_class_set_storage_;
ArrayPtr old_libraries_set_storage_;
ArrayPtr library_map_storage_;
- ArrayPtr become_map_storage_;
- GrowableObjectArrayPtr become_enum_mappings_;
LibraryPtr saved_root_library_;
GrowableObjectArrayPtr saved_libraries_;
ObjectPtr* to() { return reinterpret_cast<ObjectPtr*>(&saved_libraries_); }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e9ed8fb..d793156 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1177,7 +1177,7 @@
LanguageError::New(error_str, Report::kBailout, Heap::kOld);
error_str = String::New("Out of memory", Heap::kOld);
*out_of_memory_error_ =
- LanguageError::New(error_str, Report::kBailout, Heap::kOld);
+ LanguageError::New(error_str, Report::kError, Heap::kOld);
// Allocate the parameter arrays for method extractor types and names.
*extractor_parameter_types_ = Array::New(1, Heap::kOld);
@@ -2639,9 +2639,7 @@
} else if (thread->top_exit_frame_info() != 0) {
// Use the preallocated out of memory exception to avoid calling
// into dart code or allocating any code.
- const Instance& exception = Instance::Handle(
- thread->isolate_group()->object_store()->out_of_memory());
- Exceptions::Throw(thread, exception);
+ Exceptions::ThrowOOM();
UNREACHABLE();
} else {
// Nowhere to propagate an exception to.
@@ -10089,6 +10087,10 @@
const Object& result =
Object::Handle(zone, Compiler::CompileFunction(thread, *this));
if (result.IsError()) {
+ if (result.ptr() == Object::out_of_memory_error().ptr()) {
+ Exceptions::ThrowOOM();
+ UNREACHABLE();
+ }
if (result.IsLanguageError()) {
Exceptions::ThrowCompileTimeError(LanguageError::Cast(result));
UNREACHABLE();
@@ -10097,7 +10099,7 @@
UNREACHABLE();
}
// Compiling in unoptimized mode should never fail if there are no errors.
- ASSERT(HasCode());
+ RELEASE_ASSERT(HasCode());
ASSERT(ForceOptimize() || unoptimized_code() == result.ptr());
return CurrentCode();
}
@@ -10867,8 +10869,9 @@
void Field::set_guarded_list_length_in_object_offset_unsafe(
intptr_t list_length_offset) const {
ASSERT(IsOriginal());
- StoreNonPointer(&untag()->guarded_list_length_in_object_offset_,
- static_cast<int8_t>(list_length_offset - kHeapObjectTag));
+ StoreNonPointer<int8_t, int8_t, std::memory_order_relaxed>(
+ &untag()->guarded_list_length_in_object_offset_,
+ static_cast<int8_t>(list_length_offset - kHeapObjectTag));
ASSERT(guarded_list_length_in_object_offset() == list_length_offset);
}
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index b2037d9..cc089f2 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -397,7 +397,7 @@
enum_ident.ToCString());
bool removed = enum_map.Remove(enum_ident);
ASSERT(removed);
- reload_context->AddEnumBecomeMapping(old_enum_value, enum_value);
+ reload_context->AddBecomeMapping(old_enum_value, enum_value);
}
}
enums_deleted = enum_map.NumOccupied() > 0;
@@ -409,13 +409,13 @@
// Map the old E.values array to the new E.values array.
ASSERT(!old_enum_values.IsNull());
ASSERT(!enum_values.IsNull());
- reload_context->AddEnumBecomeMapping(old_enum_values, enum_values);
+ reload_context->AddBecomeMapping(old_enum_values, enum_values);
// Map the old E._deleted_enum_sentinel to the new E._deleted_enum_sentinel.
ASSERT(!old_deleted_enum_sentinel.IsNull());
ASSERT(!deleted_enum_sentinel.IsNull());
- reload_context->AddEnumBecomeMapping(old_deleted_enum_sentinel,
- deleted_enum_sentinel);
+ reload_context->AddBecomeMapping(old_deleted_enum_sentinel,
+ deleted_enum_sentinel);
if (enums_deleted) {
// Map all deleted enums to the deleted enum sentinel value.
@@ -432,8 +432,7 @@
ASSERT(!enum_ident.IsNull());
old_enum_value ^= enum_map.GetOrNull(enum_ident);
VTIR_Print("Element `%s` was deleted\n", enum_ident.ToCString());
- reload_context->AddEnumBecomeMapping(old_enum_value,
- deleted_enum_sentinel);
+ reload_context->AddBecomeMapping(old_enum_value, deleted_enum_sentinel);
}
enum_map.Release();
}
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index 6ca5b69..8c41eee 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -466,10 +466,7 @@
"\tisolate: %s\n",
isolate()->name());
}
- NoSafepointScope no_safepoint;
- ErrorPtr error = Thread::Current()->StealStickyError();
- ASSERT(error->IsUnwindError());
- return error;
+ return StealStickyError();
}
}
return Error::null();
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 8b57841..5742615 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -181,6 +181,14 @@
if (!code.IsNull()) {
return code.ptr();
}
+ const Error& error = Error::Handle(Thread::Current()->StealStickyError());
+ if (!error.IsNull()) {
+ if (error.ptr() == Object::out_of_memory_error().ptr()) {
+ Exceptions::ThrowOOM();
+ } else {
+ UNREACHABLE();
+ }
+ }
// Fall back to default.
#else
@@ -220,6 +228,9 @@
if (error.ptr() == Object::branch_offset_error().ptr()) {
ASSERT(!use_far_branches);
use_far_branches = true;
+ } else if (error.ptr() == Object::out_of_memory_error().ptr()) {
+ thread->set_sticky_error(error);
+ return Code::null();
} else {
UNREACHABLE();
}
diff --git a/runtime/vm/virtual_memory_compressed.cc b/runtime/vm/virtual_memory_compressed.cc
index a53007a..2f39734 100644
--- a/runtime/vm/virtual_memory_compressed.cc
+++ b/runtime/vm/virtual_memory_compressed.cc
@@ -53,7 +53,7 @@
ASSERT(Utils::IsAligned(base_, kCompressedHeapPageSize));
ASSERT(Utils::IsAligned(size_, kCompressedHeapPageSize));
// base_ is not necessarily 4GB-aligned, because on some systems we can't make
- // a large enough reservation to guarentee it. Instead, we have only the
+ // a large enough reservation to guarantee it. Instead, we have only the
// weaker property that all addresses in [base_, base_ + size_) have the same
// same upper 32 bits, which is what we really need for compressed pointers.
intptr_t mask = ~(kCompressedHeapAlignment - 1);
diff --git a/tests/language/operator/invalid_operators_test.dart b/tests/language/operator/invalid_operators_test.dart
index fba4e06..066f059 100644
--- a/tests/language/operator/invalid_operators_test.dart
+++ b/tests/language/operator/invalid_operators_test.dart
@@ -496,97 +496,97 @@
// ^
// [cfe] Declared type variables of 'Operators7.==' doesn't match those on overridden method 'Object.=='.
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ><T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator <=<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator >=<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator -<T>() => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator -<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator +<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator /<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ~/<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator *<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator %<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator |<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ^<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator &<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator <<<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator >><T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator []=<T>(a, b) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator []<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ~<T, S>() => true;
// ^^^^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
}
diff --git a/tests/language/operator/operator_triple_shift_error_test.dart b/tests/language/operator/operator_triple_shift_error_test.dart
index 5f0ad8a..36416d1 100644
--- a/tests/language/operator/operator_triple_shift_error_test.dart
+++ b/tests/language/operator/operator_triple_shift_error_test.dart
@@ -49,7 +49,7 @@
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
}
// Operators cannot be static.
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index 0c012d1..dbb1e79 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -16,6 +16,9 @@
[ $compiler == dartdevk && !$checked ]
assert/initializer_const_error2_test/*: SkipByDesign # DDC does not support non-checked mode.
+[ ($compiler == dartdevc || $compiler == dartdevk) && ($runtime == ff || $runtime == firefox) ]
+async/return_throw_test: Skip # Flaky but not enough to be detected. Re-enable pending a decision on the correct behavior. https://github.com/dart-lang/sdk/issues/44395
+
[ $compiler == dartdevc || $compiler == dartdevk ]
async_star/throw_in_catch_test: Skip # Times out. Issue 29920
external_abstract_fields/external_fields_test: SkipByDesign # Non-JS-interop external members are not supported
diff --git a/tests/language_2/operator/invalid_operators_test.dart b/tests/language_2/operator/invalid_operators_test.dart
index 977c644..fdea2ee 100644
--- a/tests/language_2/operator/invalid_operators_test.dart
+++ b/tests/language_2/operator/invalid_operators_test.dart
@@ -497,97 +497,97 @@
// ^
// [cfe] Declared type variables of 'Operators7.==' doesn't match those on overridden method 'Object.=='.
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ><T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator <=<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator >=<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator -<T>() => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator -<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator +<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator /<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ~/<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator *<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator %<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator |<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ^<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator &<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator <<<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator >><T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator []=<T>(a, b) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator []<T>(a) => true;
// ^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
operator ~<T, S>() => true;
// ^^^^^^
- // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+ // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
// ^
// [cfe] Types parameters aren't allowed when defining an operator.
}
diff --git a/tests/standalone/check_for_aot_snapshot_jit_test.dart b/tests/standalone/check_for_aot_snapshot_jit_test.dart
index 92f39cd..27e8311 100644
--- a/tests/standalone/check_for_aot_snapshot_jit_test.dart
+++ b/tests/standalone/check_for_aot_snapshot_jit_test.dart
@@ -19,10 +19,7 @@
"Can't locate gen_kernel$_batchSuffix on this platform");
Expect.isTrue(File(genKernel).existsSync(),
"Can't locate gen_kernel$_batchSuffix on this platform");
- // Currently gen_snapshot is only in buildDir/clang_x64 on Mac ARM64.
- final genSnapshot = Platform.isMacOS && buildDir.endsWith('XARM64')
- ? path.join(buildDir, 'clang_x64', 'gen_snapshot$_execSuffix')
- : path.join(buildDir, 'gen_snapshot$_execSuffix');
+ final genSnapshot = path.join(buildDir, 'gen_snapshot$_execSuffix');
Expect.isTrue(File(genSnapshot).existsSync(),
"Can't locate gen_snapshot$_execSuffix on this platform");
diff --git a/tests/standalone_2/check_for_aot_snapshot_jit_test.dart b/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
index d446a7b..521fa86 100644
--- a/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
+++ b/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
@@ -19,10 +19,7 @@
path.join(sdkDir, 'pkg', 'vm', 'tool', 'gen_kernel$_batchSuffix');
Expect.isTrue(File(genKernel).existsSync(),
"Can't locate gen_kernel$_batchSuffix on this platform");
- // Currently gen_snapshot is only in buildDir/clang_x64 on Mac ARM64.
- final genSnapshot = Platform.isMacOS && buildDir.endsWith('XARM64')
- ? path.join(buildDir, 'clang_x64', 'gen_snapshot$_execSuffix')
- : path.join(buildDir, 'gen_snapshot$_execSuffix');
+ final genSnapshot = path.join(buildDir, 'gen_snapshot$_execSuffix');
Expect.isTrue(File(genSnapshot).existsSync(),
"Can't locate gen_snapshot$_execSuffix on this platform");
diff --git a/tools/VERSION b/tools/VERSION
index 2253081..e31f9b6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 158
+PRERELEASE 159
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 50dc881..892e142 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2936,7 +2936,7 @@
"create_sdk"
],
"environment": {
- "DART_GN_ARGS": "mac_use_goma_rbe=true dart_snapshot_kind=\"app-jit\""
+ "DART_GN_ARGS": "mac_use_goma_rbe=true"
}
},
{
diff --git a/tools/gn.py b/tools/gn.py
index 108adca..cecd030 100755
--- a/tools/gn.py
+++ b/tools/gn.py
@@ -5,6 +5,7 @@
import argparse
import os
+import platform
import subprocess
import sys
import time
@@ -66,7 +67,39 @@
return [merge(x, y) for x, y in gn_args.items()]
+# Runs true if the currently executing python interpreter is running under
+# Rosetta. I.e., python3 is an x64 executable and we're on an arm64 Mac.
+def IsRosetta():
+ if platform.system() == 'Darwin':
+ p = subprocess.Popen(['sysctl', '-in', 'sysctl.proc_translated'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output, _ = p.communicate()
+ return output.decode('utf-8').strip() == '1'
+ return False
+
+
def HostCpuForArch(arch):
+ # Check for Rosetta before checking platform.machine(), as the latter
+ # returns 'x86_64' when running under Rosetta.
+ if IsRosetta():
+ if arch in ['x64', 'x64c']:
+ # Without this case, we would try to build with
+ # host_cpu="arm64"
+ # target_cpu="x64"
+ # dart_target_arch="x64"
+ # Which requires the VM to use an x64 simulator in the host
+ # arm64 binaries, and this simulator is unimplemented.
+ return 'x64'
+ else:
+ return 'arm64'
+
+ m = platform.machine()
+ if m == 'aarch64' or m == 'arm64':
+ return 'arm64'
+ if m == 'armv7l' or m == 'armv6l':
+ return 'arm'
+
if arch in ['ia32', 'arm', 'armv6', 'simarm', 'simarmv6', 'simarm_x64']:
return 'x86'
if arch in [