Version 2.14.0-4.0.dev
Merge commit '61fda382c2342e4b042df5cbbfffa5bbb37f9945' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3441a82..1a4558d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,14 @@
daylight saving changes that are not precisely one hour.
(No change on the Web which uses the JavaScript `Date` object.)
+### Dart VM
+
+* **Breaking Change** [#45071][]: `Dart_NewWeakPersistentHandle`'s and
+ `Dart_NewFinalizableHandle`'s `object` parameter no longer accepts
+ `Pointer`s and subtypes of `Struct`. Expandos no longer accept
+ `Pointer`s and subtypes of `Struct`s.
+
+[#45071]: https://github.com/dart-lang/sdk/issues/45071
### Tools
diff --git a/DEPS b/DEPS
index 62dd712..5c44d05 100644
--- a/DEPS
+++ b/DEPS
@@ -54,6 +54,10 @@
# Checkout Android dependencies only on Mac and Linux.
"download_android_deps": 'host_os == "mac" or host_os == "linux"',
+ # Checkout extra javascript engines for testing or benchmarking.
+ # d8, the V8 shell, is always checked out.
+ "checkout_javascript_engines": False,
+
# As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
# should be kept up to date with the revisions pulled by the Flutter engine.
# The list of revisions for these tools comes from Fuchsia, here:
@@ -662,6 +666,7 @@
"--directory",
Var('dart_root') + "/third_party/firefox_jsshell",
],
+ "condition": "checkout_javascript_engines",
},
{
# Pull Debian sysroot for i386 Linux
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 9c7e2b3..63bfa14 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -2,18 +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.
-// @dart = 2.9
-
import 'package:dart_style/dart_style.dart';
-import 'package:meta/meta.dart';
import 'typescript.dart';
import 'typescript_parser.dart';
final formatter = DartFormatter();
Map<String, Interface> _interfaces = {};
+
+/// TODO(dantup): Rename namespaces -> enums since they're always that now.
Map<String, Namespace> _namespaces = {};
-// TODO(dantup): Rename namespaces -> enums since they're always that now.
Map<String, List<String>> _subtypes = {};
Map<String, TypeAlias> _typeAliases = {};
@@ -63,10 +61,11 @@
}
TypeBase resolveTypeAlias(TypeBase type, {resolveEnumClasses = false}) {
- if (type is Type && _typeAliases.containsKey(type.name)) {
+ if (type is Type) {
final alias = _typeAliases[type.name];
// Only follow the type if we're not an enum, or we wanted to follow enums.
- if (!_namespaces.containsKey(alias.name) || resolveEnumClasses) {
+ if (alias != null &&
+ (!_namespaces.containsKey(alias.name) || resolveEnumClasses)) {
return alias.baseType;
}
}
@@ -83,7 +82,7 @@
}
/// Recursively gets all members from superclasses.
-List<Field> _getAllFields(Interface interface) {
+List<Field> _getAllFields(Interface? interface) {
// Handle missing interfaces (such as special cased interfaces that won't
// be included in this model).
if (interface == null) {
@@ -99,8 +98,8 @@
}
/// Returns a copy of the list sorted by name with duplicates (by name+type) removed.
-List<AstNode> _getSortedUnique(List<AstNode> items) {
- final uniqueByName = <String, AstNode>{};
+List<N> _getSortedUnique<N extends AstNode>(List<N> items) {
+ final uniqueByName = <String, N>{};
items.forEach((item) {
// It's fine to have the same name used for different types (eg. namespace +
// type alias) but some types are just duplicated entirely in the spec in
@@ -325,10 +324,11 @@
var comment = node.commentText?.trim();
if (comment != null && comment.isNotEmpty) {
comment = _rewriteCommentReference(comment);
- Iterable<String> lines = comment.split('\n');
+ var originalLines = comment.split('\n');
// Wrap at 80 - 4 ('/// ') - indent characters.
- lines = _wrapLines(lines, (80 - 4 - buffer.totalIndent).clamp(0, 80));
- lines.forEach((l) => buffer.writeIndentedln('/// $l'.trim()));
+ var wrappedLines =
+ _wrapLines(originalLines, (80 - 4 - buffer.totalIndent).clamp(0, 80));
+ wrappedLines.forEach((l) => buffer.writeIndentedln('/// $l'.trim()));
}
// Marking LSP-deprecated fields as deprecated in Dart results in a lot
// of warnings because we still often populate these fields for clients that
@@ -466,7 +466,7 @@
void _writeFromJsonCode(
IndentableStringBuffer buffer, TypeBase type, String valueCode,
- {bool allowsNull, bool requiresBracesInInterpolation = false}) {
+ {required bool allowsNull, bool requiresBracesInInterpolation = false}) {
type = resolveTypeAlias(type);
if (_isSimpleType(type)) {
@@ -506,7 +506,7 @@
void _writeFromJsonCodeForLiteralUnion(
IndentableStringBuffer buffer, LiteralUnionType union, String valueCode,
- {bool allowsNull}) {
+ {required bool allowsNull}) {
final allowedValues = [
if (allowsNull) null,
...union.literalTypes.map((t) => t.literal)
@@ -518,7 +518,7 @@
void _writeFromJsonCodeForUnion(
IndentableStringBuffer buffer, UnionType union, String valueCode,
- {bool allowsNull, @required bool requiresBracesInInterpolation}) {
+ {required bool allowsNull, required bool requiresBracesInInterpolation}) {
// Write a check against each type, eg.:
// x is y ? new Either.tx(x) : (...)
var hasIncompleteCondition = false;
@@ -575,7 +575,7 @@
..indent();
// First check whether any of our subclasses can deserialise this.
for (final subclassName in _subtypes[interface.name] ?? const <String>[]) {
- final subclass = _interfaces[subclassName];
+ final subclass = _interfaces[subclassName]!;
buffer
..writeIndentedln(
'if (${subclass.name}.canParse(json, nullLspJsonReporter)) {')
@@ -792,7 +792,7 @@
}
void _writeTypeCheckCondition(IndentableStringBuffer buffer,
- Interface interface, String valueCode, TypeBase type, String reporter) {
+ Interface? interface, String valueCode, TypeBase type, String reporter) {
type = resolveTypeAlias(type);
final dartType = type.dartType;
@@ -840,7 +840,6 @@
}
buffer.write(')');
} else if (interface != null &&
- interface.typeArgs != null &&
interface.typeArgs.any((typeArg) => typeArg.lexeme == fullDartType)) {
final comment = '/* $fullDartType.canParse($valueCode) */';
print(
diff --git a/pkg/analysis_server/tool/lsp_spec/typescript.dart b/pkg/analysis_server/tool/lsp_spec/typescript.dart
index da58944..268b1f8 100644
--- a/pkg/analysis_server/tool/lsp_spec/typescript.dart
+++ b/pkg/analysis_server/tool/lsp_spec/typescript.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'typescript_parser.dart';
/// Removes types that are in the spec that we don't want in other signatures.
@@ -22,10 +20,6 @@
}
String cleanComment(String comment) {
- if (comment == null) {
- return null;
- }
-
// Remove the start/end comment markers.
if (comment.startsWith('/**') && comment.endsWith('*/')) {
comment = comment.substring(3, comment.length - 2);
@@ -51,7 +45,7 @@
/// Improves comments in generated code to support where types may have been
/// altered (for ex. with [getImprovedType] above).
-String getImprovedComment(String interfaceName, String fieldName) {
+String? getImprovedComment(String interfaceName, String fieldName) {
const _improvedComments = <String, Map<String, String>>{
'ResponseError': {
'data':
@@ -73,7 +67,7 @@
/// - Narrows unions to single types where they're only generated on the server
/// and we know we always use a specific type. This avoids wrapping a lot
/// of code in `EitherX<Y,Z>.tX()` and simplifies the testing of them.
-String getImprovedType(String interfaceName, String fieldName) {
+String? getImprovedType(String interfaceName, String? fieldName) {
const _improvedTypeMappings = <String, Map<String, String>>{
'Diagnostic': {
'severity': 'DiagnosticSeverity',
diff --git a/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart b/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart
index b18b819..9dc61e0 100644
--- a/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart
+++ b/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'dart:math';
import 'package:analysis_server/src/utilities/strings.dart' show capitalize;
@@ -64,14 +62,14 @@
String get typeArgsString => '<${elementType.dartTypeWithTypeArgs}>';
}
-class AstNode {
- final Comment commentNode;
+abstract class AstNode {
+ final Comment? commentNode;
final bool isDeprecated;
AstNode(this.commentNode)
- : isDeprecated = commentNode?.text?.contains('@deprecated') ?? false;
- String get commentText => commentNode?.text;
+ : isDeprecated = commentNode?.text.contains('@deprecated') ?? false;
+ String? get commentText => commentNode?.text;
- String get name => null;
+ String get name;
}
class Comment extends AstNode {
@@ -81,13 +79,16 @@
Comment(this.token)
: text = cleanComment(token.lexeme),
super(null);
+
+ @override
+ String get name => throw UnsupportedError('Comments do not have a name.');
}
class Const extends Member {
Token nameToken;
TypeBase type;
Token valueToken;
- Const(Comment comment, this.nameToken, this.type, this.valueToken)
+ Const(Comment? comment, this.nameToken, this.type, this.valueToken)
: super(comment);
@override
@@ -111,7 +112,7 @@
final bool allowsNull;
final bool allowsUndefined;
Field(
- Comment comment,
+ Comment? comment,
this.nameToken,
this.type,
this.allowsNull,
@@ -125,7 +126,7 @@
class FixedValueField extends Field {
final Token valueToken;
FixedValueField(
- Comment comment,
+ Comment? comment,
Token nameToken,
this.valueToken,
TypeBase type,
@@ -138,7 +139,7 @@
final TypeBase indexType;
final TypeBase valueType;
Indexer(
- Comment comment,
+ Comment? comment,
this.indexType,
this.valueType,
) : super(comment);
@@ -161,7 +162,7 @@
final List<Member> members;
Interface(
- Comment comment,
+ Comment? comment,
this.nameToken,
this.typeArgs,
this.baseTypes,
@@ -221,15 +222,15 @@
'<${indexType.dartTypeWithTypeArgs}, ${valueType.dartTypeWithTypeArgs}>';
}
-class Member extends AstNode {
- Member(Comment comment) : super(comment);
+abstract class Member extends AstNode {
+ Member(Comment? comment) : super(comment);
}
class Namespace extends AstNode {
final Token nameToken;
final List<Member> members;
Namespace(
- Comment comment,
+ Comment? comment,
this.nameToken,
this.members,
) : super(comment);
@@ -241,14 +242,13 @@
class Parser {
final List<Token> _tokens;
int _current = 0;
- List<AstNode> _nodes;
+ final List<AstNode> _nodes = [];
Parser(this._tokens);
bool get _isAtEnd => _peek().type == TokenType.EOF;
List<AstNode> parse() {
- if (_nodes == null) {
- _nodes = <AstNode>[];
+ if (_nodes.isEmpty) {
while (!_isAtEnd) {
_nodes.add(_topLevel());
}
@@ -262,17 +262,17 @@
/// Checks if the next token is [type] without advancing.
bool _check(TokenType type) => !_isAtEnd && _peek().type == type;
- Comment _comment() {
+ Comment? _comment() {
if (_peek().type != TokenType.COMMENT) {
return null;
}
return Comment(_advance());
}
- Const _const(String containerName, Comment leadingComment) {
+ Const _const(String containerName, Comment? leadingComment) {
_eatUnwantedKeywords();
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
- TypeBase type;
+ TypeBase? type;
if (_match([TokenType.COLON])) {
type = _type(containerName, name.lexeme);
}
@@ -283,7 +283,7 @@
}
_consume(TokenType.SEMI_COLON, 'Expected ;');
- return Const(leadingComment, name, type, value);
+ return Const(leadingComment, name, type!, value!);
}
/// Ensures the next token is [type] and moves to the next, throwing [message]
@@ -302,7 +302,7 @@
// but we have a keyword token, then treat it as an identifier.
if (type == TokenType.IDENTIFIER) {
final next = !_isAtEnd ? _peek() : null;
- if (_isKeyword(next?.type)) {
+ if (next != null && _isKeyword(next.type)) {
_advance();
return Token(TokenType.IDENTIFIER, next.lexeme);
}
@@ -316,7 +316,7 @@
_match([TokenType.READONLY_KEYWORD]);
}
- Namespace _enum(Comment leadingComment) {
+ Namespace _enum(Comment? leadingComment) {
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
_consume(TokenType.LEFT_BRACE, 'Expected {');
final consts = <Const>[];
@@ -333,7 +333,7 @@
Const _enumValue(String enumName) {
final leadingComment = _comment();
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
- TypeBase type;
+ TypeBase? type;
if (_match([TokenType.COLON])) {
type = _type(enumName, name.lexeme);
}
@@ -342,16 +342,16 @@
if (type == null && value != null) {
type = typeOfLiteral(value.type);
}
- return Const(leadingComment, name, type, value);
+ return Const(leadingComment, name, type!, value!);
}
- Field _field(String containerName, Comment leadingComment) {
+ Field _field(String containerName, Comment? leadingComment) {
_eatUnwantedKeywords();
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
var canBeUndefined = _match([TokenType.QUESTION]);
_consume(TokenType.COLON, 'Expected :');
TypeBase type;
- Token value;
+ Token? value;
type = _type(containerName, name.lexeme,
includeUndefined: canBeUndefined, improveTypes: true);
@@ -370,7 +370,7 @@
final _linkTypePattern = RegExp(r'See \{@link (\w+)\}\.?');
final linkTypeMatch = _linkTypePattern.firstMatch(commentText);
if (linkTypeMatch != null) {
- type = Type.identifier(linkTypeMatch.group(1));
+ type = Type.identifier(linkTypeMatch.group(1)!);
leadingComment = Comment(Token(TokenType.COMMENT,
'// ' + commentText.replaceAll(_linkTypePattern, '')));
}
@@ -388,14 +388,13 @@
var canBeNull = false;
if (type is UnionType) {
- UnionType union = type;
// Since undefined and null can appear in the union type list but we want to
// handle it specially in the code generation, we promote them to fields on
// the Field.
- canBeUndefined |= union.types.any(isUndefinedType);
- canBeNull = union.types.any((t) => isNullType(t) || isAnyType(t));
+ canBeUndefined |= type.types.any(isUndefinedType);
+ canBeNull = type.types.any((t) => isNullType(t) || isAnyType(t));
// Finally, we need to remove them from the union.
- final remainingTypes = union.types
+ final remainingTypes = type.types
.where((t) => !isNullType(t) && !isUndefinedType(t))
.toList();
@@ -413,7 +412,7 @@
return Field(leadingComment, name, type, canBeNull, canBeUndefined);
}
- Indexer _indexer(String containerName, Comment leadingComment) {
+ Indexer _indexer(String containerName, Comment? leadingComment) {
final indexer = _field(containerName, leadingComment);
_consume(TokenType.RIGHT_BRACKET, 'Expected ]');
_consume(TokenType.COLON, 'Expected :');
@@ -427,7 +426,7 @@
return Indexer(leadingComment, indexer.type, type);
}
- Interface _interface(Comment leadingComment) {
+ Interface _interface(Comment? leadingComment) {
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
final typeArgs = <Token>[];
if (_match([TokenType.LESS])) {
@@ -493,7 +492,7 @@
}
}
- Namespace _namespace(Comment leadingComment) {
+ Namespace _namespace(Comment? leadingComment) {
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
_consume(TokenType.LEFT_BRACE, 'Expected {');
final members = <Member>[];
@@ -560,7 +559,7 @@
TypeBase _type(
String containerName,
- String fieldName, {
+ String? fieldName, {
bool includeUndefined = false,
bool improveTypes = false,
}) {
@@ -585,7 +584,7 @@
// If we have a single member that is an indexer type, we can use a Map.
if (members.length == 1 && members.single is Indexer) {
- Indexer indexer = members.single;
+ var indexer = members.single as Indexer;
type = MapType(indexer.indexType, indexer.valueType);
} else {
// Add a synthetic interface to the parsers list of nodes to represent this type.
@@ -628,7 +627,7 @@
type = ArrayType(tupleType);
} else {
var typeName = _consume(TokenType.IDENTIFIER, 'Expected identifier');
- final typeArgs = <Type>[];
+ final typeArgs = <TypeBase>[];
if (_match([TokenType.LESS])) {
while (true) {
typeArgs.add(_type(containerName, fieldName));
@@ -679,7 +678,7 @@
return type;
}
- TypeAlias _typeAlias(Comment leadingComment) {
+ TypeAlias _typeAlias(Comment? leadingComment) {
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
_consume(TokenType.EQUAL, 'Expected =');
final type = _type(name.lexeme, null);
@@ -732,16 +731,18 @@
}
final string = _source.substring(_startOfToken, _currentPos);
- if (_keywords.containsKey(string)) {
- _addToken(_keywords[string]);
+ var keyword = _keywords[string];
+ if (keyword != null) {
+ _addToken(keyword);
} else {
_addToken(TokenType.IDENTIFIER);
}
}
- bool _isAlpha(String s) => _validIdentifierCharacters.hasMatch(s);
+ bool _isAlpha(String? s) =>
+ s != null && _validIdentifierCharacters.hasMatch(s);
- bool _isDigit(String s) => s != null && (s.codeUnitAt(0) ^ 0x30) <= 9;
+ bool _isDigit(String? s) => s != null && (s.codeUnitAt(0) ^ 0x30) <= 9;
bool _match(String expected) {
if (_isAtEnd || _source[_currentPos] != expected) {
@@ -771,9 +772,9 @@
_addToken(TokenType.NUMBER);
}
- String _peek() => _isAtEnd ? null : _source[_currentPos];
+ String? _peek() => _isAtEnd ? null : _source[_currentPos];
- String _peekNext() => _isNextAtEnd ? null : _source[_currentPos + 1];
+ String? _peekNext() => _isNextAtEnd ? null : _source[_currentPos + 1];
void _scanToken() {
const singleCharTokens = <String, TokenType>{
@@ -795,8 +796,9 @@
};
final c = _advance();
- if (singleCharTokens.containsKey(c)) {
- _addToken(singleCharTokens[c]);
+ var token = singleCharTokens[c];
+ if (token != null) {
+ _addToken(token);
return;
}
switch (c) {
@@ -979,7 +981,7 @@
final Token nameToken;
final TypeBase baseType;
TypeAlias(
- Comment comment,
+ Comment? comment,
this.nameToken,
this.baseType,
) : super(comment);
diff --git a/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart b/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
index 76c675b..d478a69 100644
--- a/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
@@ -141,7 +141,7 @@
int charEndOffset,
Member referenceFrom,
[String nativeMethodName])
- : _constructor = new Constructor(null,
+ : _constructor = new Constructor(new FunctionNode(null),
name: new Name(name, compilationUnit.library),
fileUri: compilationUnit.fileUri,
reference: referenceFrom?.reference)
@@ -161,6 +161,8 @@
@override
Member get invokeTarget => constructor;
+ FunctionNode get function => _constructor.function;
+
@override
Iterable<Member> get exportedMembers => [constructor];
@@ -202,8 +204,7 @@
@override
Constructor build(SourceLibraryBuilder libraryBuilder) {
if (!_hasBeenBuilt) {
- _constructor.function = buildFunction(libraryBuilder);
- _constructor.function.parent = _constructor;
+ buildFunction(libraryBuilder);
_constructor.function.fileOffset = charOpenParenOffset;
_constructor.function.fileEndOffset = _constructor.fileEndOffset;
_constructor.function.typeParameters = const <TypeParameter>[];
@@ -263,10 +264,10 @@
}
@override
- FunctionNode buildFunction(SourceLibraryBuilder library) {
+ void buildFunction(SourceLibraryBuilder library) {
// According to the specification §9.3 the return type of a constructor
// function is its enclosing class.
- FunctionNode functionNode = super.buildFunction(library);
+ super.buildFunction(library);
ClassBuilder enclosingClassBuilder = parent;
Class enclosingClass = enclosingClassBuilder.cls;
List<DartType> typeParameterTypes = <DartType>[];
@@ -276,9 +277,8 @@
new TypeParameterType.withDefaultNullabilityForLibrary(
typeParameter, library.library));
}
- functionNode.returnType = new InterfaceType(
+ function.returnType = new InterfaceType(
enclosingClass, library.nonNullable, typeParameterTypes);
- return functionNode;
}
@override
diff --git a/pkg/front_end/lib/src/fasta/builder/function_builder.dart b/pkg/front_end/lib/src/fasta/builder/function_builder.dart
index 15880c6..7d11b18 100644
--- a/pkg/front_end/lib/src/fasta/builder/function_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/function_builder.dart
@@ -293,9 +293,6 @@
@override
final String nativeMethodName;
- @override
- FunctionNode function;
-
Statement bodyInternal;
@override
@@ -308,18 +305,16 @@
// }
// }
bodyInternal = newBody;
- if (function != null) {
- // A forwarding semi-stub is a method that is abstract in the source code,
- // but which needs to have a forwarding stub body in order to ensure that
- // covariance checks occur. We don't want to replace the forwarding stub
- // body with null.
- TreeNode parent = function.parent;
- if (!(newBody == null &&
- parent is Procedure &&
- parent.isForwardingSemiStub)) {
- function.body = newBody;
- newBody?.parent = function;
- }
+ // A forwarding semi-stub is a method that is abstract in the source code,
+ // but which needs to have a forwarding stub body in order to ensure that
+ // covariance checks occur. We don't want to replace the forwarding stub
+ // body with null.
+ TreeNode parent = function.parent;
+ if (!(newBody == null &&
+ parent is Procedure &&
+ parent.isForwardingSemiStub)) {
+ function.body = newBody;
+ newBody?.parent = function;
}
}
@@ -342,9 +337,10 @@
@override
bool get isNative => nativeMethodName != null;
- FunctionNode buildFunction(SourceLibraryBuilder library) {
- assert(function == null);
- FunctionNode result = new FunctionNode(body, asyncMarker: asyncModifier);
+ void buildFunction(SourceLibraryBuilder library) {
+ function.asyncMarker = asyncModifier;
+ function.body = body;
+ body?.parent = function;
IncludesTypeParametersNonCovariantly needsCheckVisitor;
if (!isConstructor && !isFactory && parent is ClassBuilder) {
ClassBuilder enclosingClassBuilder = parent;
@@ -360,14 +356,14 @@
if (typeVariables != null) {
for (TypeVariableBuilder t in typeVariables) {
TypeParameter parameter = t.parameter;
- result.typeParameters.add(parameter);
+ function.typeParameters.add(parameter);
if (needsCheckVisitor != null) {
if (parameter.bound.accept(needsCheckVisitor)) {
parameter.isGenericCovariantImpl = true;
}
}
}
- setParents(result.typeParameters, result);
+ setParents(function.typeParameters, function);
}
if (formals != null) {
for (FormalParameterBuilder formal in formals) {
@@ -379,13 +375,13 @@
}
}
if (formal.isNamed) {
- result.namedParameters.add(parameter);
+ function.namedParameters.add(parameter);
} else {
- result.positionalParameters.add(parameter);
+ function.positionalParameters.add(parameter);
}
- parameter.parent = result;
+ parameter.parent = function;
if (formal.isRequired) {
- result.requiredParameterCount++;
+ function.requiredParameterCount++;
}
if (library.isNonNullableByDefault) {
@@ -409,14 +405,14 @@
// assumes that parameters are built, even if illegal in number.
VariableDeclaration parameter =
new VariableDeclarationImpl("#synthetic", 0);
- result.positionalParameters.clear();
- result.positionalParameters.add(parameter);
- parameter.parent = result;
- result.namedParameters.clear();
- result.requiredParameterCount = 1;
+ function.positionalParameters.clear();
+ function.positionalParameters.add(parameter);
+ parameter.parent = function;
+ function.namedParameters.clear();
+ function.requiredParameterCount = 1;
}
if (returnType != null) {
- result.returnType = returnType.build(
+ function.returnType = returnType.build(
library, null, !isConstructor && !isDeclarationInstanceMember);
}
if (!isConstructor && !isDeclarationInstanceMember) {
@@ -444,33 +440,32 @@
}
Set<TypeParameter> set = typeParameters.toSet();
- for (VariableDeclaration parameter in result.positionalParameters) {
+ for (VariableDeclaration parameter in function.positionalParameters) {
if (containsTypeVariable(parameter.type, set)) {
parameter.type = removeTypeVariables(parameter.type);
}
}
- for (VariableDeclaration parameter in result.namedParameters) {
+ for (VariableDeclaration parameter in function.namedParameters) {
if (containsTypeVariable(parameter.type, set)) {
parameter.type = removeTypeVariables(parameter.type);
}
}
- if (containsTypeVariable(result.returnType, set)) {
- result.returnType = removeTypeVariables(result.returnType);
+ if (containsTypeVariable(function.returnType, set)) {
+ function.returnType = removeTypeVariables(function.returnType);
}
}
}
if (isExtensionInstanceMember) {
ExtensionBuilder extensionBuilder = parent;
- _extensionThis = result.positionalParameters.first;
+ _extensionThis = function.positionalParameters.first;
if (extensionBuilder.typeParameters != null) {
int count = extensionBuilder.typeParameters.length;
_extensionTypeParameters = new List<TypeParameter>.filled(count, null);
for (int index = 0; index < count; index++) {
- _extensionTypeParameters[index] = result.typeParameters[index];
+ _extensionTypeParameters[index] = function.typeParameters[index];
}
}
}
- return function = result;
}
@override
diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
index 498826b..087e96d 100644
--- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
@@ -107,15 +107,20 @@
this.isExtensionInstanceMember = isInstanceMember && isExtensionMember,
super(metadata, modifiers, returnType, name, typeVariables, formals,
libraryBuilder, charOffset, nativeMethodName) {
- _procedure = new Procedure(procedureNameScheme.getName(kind, name),
- isExtensionInstanceMember ? ProcedureKind.Method : kind, null,
- fileUri: libraryBuilder.fileUri, reference: procedureReference)
+ _procedure = new Procedure(
+ procedureNameScheme.getName(kind, name),
+ isExtensionInstanceMember ? ProcedureKind.Method : kind,
+ new FunctionNode(null),
+ fileUri: libraryBuilder.fileUri,
+ reference: procedureReference)
..startFileOffset = startCharOffset
..fileOffset = charOffset
..fileEndOffset = charEndOffset
..isNonNullableByDefault = libraryBuilder.isNonNullableByDefault;
}
+ FunctionNode get function => _procedure.function;
+
@override
ProcedureBuilder get origin => actualOrigin ?? this;
@@ -136,11 +141,8 @@
@override
void set asyncModifier(AsyncMarker newModifier) {
actualAsyncModifier = newModifier;
- if (function != null) {
- // No parent, it's an enum.
- function.asyncMarker = actualAsyncModifier;
- function.dartAsyncMarker = actualAsyncModifier;
- }
+ function.asyncMarker = actualAsyncModifier;
+ function.dartAsyncMarker = actualAsyncModifier;
}
@override
@@ -266,7 +268,7 @@
_extensionTearOff ??= new Procedure(
procedureNameScheme.getName(ProcedureKind.Getter, name),
ProcedureKind.Method,
- null,
+ new FunctionNode(null),
isStatic: true,
isExtensionMember: true,
reference: _tearOffReference)
@@ -379,8 +381,7 @@
@override
Procedure build(SourceLibraryBuilder libraryBuilder) {
- _procedure.function = buildFunction(libraryBuilder);
- _procedure.function.parent = _procedure;
+ buildFunction(libraryBuilder);
_procedure.function.fileOffset = charOpenParenOffset;
_procedure.function.fileEndOffset = _procedure.fileEndOffset;
_procedure.isAbstract = isAbstract;
@@ -747,8 +748,7 @@
@override
Procedure build(SourceLibraryBuilder libraryBuilder) {
- _procedure.function = buildFunction(libraryBuilder);
- _procedure.function.parent = _procedure;
+ buildFunction(libraryBuilder);
_procedure.function.fileOffset = charOpenParenOffset;
_procedure.function.fileEndOffset = _procedure.fileEndOffset;
_procedure.isAbstract = isAbstract;
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 50db82b..1f26ed0 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -4999,8 +4999,8 @@
}
push(new FunctionDeclarationImpl(
variable,
- // The function node is created later.
- null)
+ // The real function node is created later.
+ dummyFunctionNode)
..fileOffset = beginToken.charOffset);
declareVariable(variable, scope.parent);
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/member_covariance.dart b/pkg/front_end/lib/src/fasta/kernel/member_covariance.dart
index b33dbcd..03c89ce 100644
--- a/pkg/front_end/lib/src/fasta/kernel/member_covariance.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/member_covariance.dart
@@ -95,7 +95,7 @@
/// Computes the covariance for the [setter].
factory Covariance.fromSetter(Procedure setter) {
int covariance =
- covarianceFromParameter(setter.function!.positionalParameters.first);
+ covarianceFromParameter(setter.function.positionalParameters.first);
if (covariance == 0) {
return const Covariance.empty();
}
@@ -104,7 +104,7 @@
/// Computes the covariance for the [procedure].
factory Covariance.fromMethod(Procedure procedure) {
- FunctionNode function = procedure.function!;
+ FunctionNode function = procedure.function;
List<int>? positionalParameters;
if (function.positionalParameters.isNotEmpty) {
for (int index = 0;
@@ -269,7 +269,7 @@
void applyCovariance(Member member) {
if (isEmpty) return;
if (member is Procedure) {
- FunctionNode function = member.function!;
+ FunctionNode function = member.function;
List<int>? positionalParameters = _positionalParameters;
if (positionalParameters != null) {
for (int index = 0; index < positionalParameters.length; index++) {
diff --git a/pkg/front_end/test/flutter_gallery_leak_tester.dart b/pkg/front_end/test/flutter_gallery_leak_tester.dart
index 37b66be..b3f57e9 100644
--- a/pkg/front_end/test/flutter_gallery_leak_tester.dart
+++ b/pkg/front_end/test/flutter_gallery_leak_tester.dart
@@ -216,10 +216,10 @@
},
stderrReceiver: (s) => print("err> $s"));
- await sendAndWait(heapHelper.process, ['compile package:gallery/main.dart']);
Stopwatch stopwatch = new Stopwatch()..start();
- await pauseAndWait(heapHelper);
+ await sendAndWait(heapHelper.process, ['compile package:gallery/main.dart']);
print("First compile took ${stopwatch.elapsedMilliseconds} ms");
+ await pauseAndWait(heapHelper);
await recompileAndWait(heapHelper.process, "package:gallery/main.dart", []);
await accept(heapHelper);
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index b594898..20ee23e 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
type ComponentFile {
UInt32 magic = 0x90ABCDEF;
- UInt32 formatVersion = 60;
+ UInt32 formatVersion = 61;
Byte[10] shortSdkHash;
List<String> problemsAsJson; // Described in problems.md.
Library[] libraries;
@@ -425,8 +425,7 @@
Name name;
List<Expression> annotations;
MemberReference stubTarget; // May be NullReference.
- // Can only be absent if abstract, but tag is there anyway.
- Option<FunctionNode> function;
+ FunctionNode function;
}
type RedirectingFactoryConstructor extends Member {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index a81d28f..9e1ce0f 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -1988,9 +1988,8 @@
int flags = 0;
- // TODO(johnniwinther): Make this non-nullable.
@override
- FunctionNode? function;
+ FunctionNode function;
List<Initializer> initializers;
@@ -2004,8 +2003,10 @@
Uri? fileUri,
Reference? reference})
: this.initializers = initializers ?? <Initializer>[],
+ // ignore: unnecessary_null_comparison
+ assert(function != null),
super(name, fileUri, reference) {
- function?.parent = this;
+ function.parent = this;
setParents(this.initializers, this);
this.isConst = isConst;
this.isExternal = isExternal;
@@ -2082,16 +2083,17 @@
visitList(annotations, v);
name.accept(v);
visitList(initializers, v);
- function?.accept(v);
+ function.accept(v);
}
@override
void transformChildren(Transformer v) {
v.transformList(annotations, this);
v.transformList(initializers, this);
+ // ignore: unnecessary_null_comparison
if (function != null) {
- function = v.transform(function!);
- function?.parent = this;
+ function = v.transform(function);
+ function.parent = this;
}
}
@@ -2099,9 +2101,10 @@
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformExpressionList(annotations, this);
v.transformInitializerList(initializers, this);
+ // ignore: unnecessary_null_comparison
if (function != null) {
- function = v.transformOrRemove(function!, dummyFunctionNode);
- function?.parent = this;
+ function = v.transform(function);
+ function.parent = this;
}
}
@@ -2504,16 +2507,15 @@
final ProcedureKind kind;
int flags = 0;
- // TODO(johnniwinther): Make this non-nullable.
@override
- FunctionNode? function;
+ FunctionNode function;
// The function node's body might be lazily loaded, meaning that this value
// might not be set correctly yet. Make sure the body is loaded before
// returning anything.
@override
int get transformerFlags {
- function?.body;
+ function.body;
return super.transformerFlags;
}
@@ -2522,7 +2524,7 @@
// body now and only set the value afterwards.
@override
void set transformerFlags(int newValue) {
- function?.body;
+ function.body;
super.transformerFlags = newValue;
}
@@ -2536,7 +2538,7 @@
ProcedureStubKind stubKind;
Reference? stubTargetReference;
- Procedure(Name name, ProcedureKind kind, FunctionNode? function,
+ Procedure(Name name, ProcedureKind kind, FunctionNode function,
{bool isAbstract: false,
bool isStatic: false,
bool isExternal: false,
@@ -2576,15 +2578,17 @@
this.stubTargetReference})
// ignore: unnecessary_null_comparison
: assert(kind != null),
+ // ignore: unnecessary_null_comparison
+ assert(function != null),
super(name, fileUri, reference) {
- function?.parent = this;
+ function.parent = this;
this.isAbstract = isAbstract;
this.isStatic = isStatic;
this.isExternal = isExternal;
this.isConst = isConst;
this.isExtensionMember = isExtensionMember;
this.isSynthetic = isSynthetic;
- this.transformerFlags = transformerFlags;
+ setTransformerFlagsWithoutLazyLoading(transformerFlags);
assert(!(isMemberSignature && stubTargetReference == null),
"No member signature origin for member signature $this.");
assert(
@@ -2753,38 +2757,40 @@
void visitChildren(Visitor v) {
visitList(annotations, v);
name.accept(v);
- function?.accept(v);
+ function.accept(v);
}
@override
void transformChildren(Transformer v) {
v.transformList(annotations, this);
+ // ignore: unnecessary_null_comparison
if (function != null) {
- function = v.transform(function!);
- function?.parent = this;
+ function = v.transform(function);
+ function.parent = this;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformExpressionList(annotations, this);
+ // ignore: unnecessary_null_comparison
if (function != null) {
- function = v.transformOrRemove(function!, dummyFunctionNode);
- function?.parent = this;
+ function = v.transform(function);
+ function.parent = this;
}
}
@override
DartType get getterType {
return isGetter
- ? function!.returnType
- : function!.computeFunctionType(enclosingLibrary.nonNullable);
+ ? function.returnType
+ : function.computeFunctionType(enclosingLibrary.nonNullable);
}
@override
DartType get setterType {
return isSetter
- ? function!.positionalParameters[0].type
+ ? function.positionalParameters[0].type
: const NeverType.nonNullable();
}
@@ -6025,9 +6031,9 @@
.getTypeArgumentsAsInstanceOf(context.thisType!, superclass);
DartType returnType = Substitution.fromPairs(
superclass.typeParameters, receiverTypeArguments!)
- .substituteType(interfaceTarget.function!.returnType);
+ .substituteType(interfaceTarget.function.returnType);
return Substitution.fromPairs(
- interfaceTarget.function!.typeParameters, arguments.types)
+ interfaceTarget.function.typeParameters, arguments.types)
.substituteType(returnType);
}
@@ -6114,8 +6120,8 @@
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return Substitution.fromPairs(
- target.function!.typeParameters, arguments.types)
- .substituteType(target.function!.returnType);
+ target.function.typeParameters, arguments.types)
+ .substituteType(target.function.returnType);
}
@override
@@ -8077,8 +8083,7 @@
/// Common super-interface for [FunctionExpression] and [FunctionDeclaration].
abstract class LocalFunction implements TreeNode {
- // TODO(johnniwinther): Make this non-nullable.
- FunctionNode? get function;
+ FunctionNode get function;
}
/// Expression of form `(x,y) => ...` or `(x,y) { ... }`
@@ -10118,11 +10123,13 @@
VariableDeclaration variable; // Is final and has no initializer.
@override
- FunctionNode? function;
+ FunctionNode function;
- FunctionDeclaration(this.variable, this.function) {
+ FunctionDeclaration(this.variable, this.function)
+ // ignore: unnecessary_null_comparison
+ : assert(function != null) {
variable.parent = this;
- function?.parent = this;
+ function.parent = this;
}
@override
@@ -10135,7 +10142,7 @@
@override
void visitChildren(Visitor v) {
variable.accept(v);
- function?.accept(v);
+ function.accept(v);
}
@override
@@ -10145,9 +10152,10 @@
variable = v.transform(variable);
variable.parent = this;
}
+ // ignore: unnecessary_null_comparison
if (function != null) {
- function = v.transform(function!);
- function?.parent = this;
+ function = v.transform(function);
+ function.parent = this;
}
}
@@ -10158,9 +10166,10 @@
variable = v.transform(variable);
variable.parent = this;
}
+ // ignore: unnecessary_null_comparison
if (function != null) {
- function = v.transformOrRemove(function!, dummyFunctionNode);
- function?.parent = this;
+ function = v.transform(function);
+ function.parent = this;
}
}
@@ -10171,9 +10180,10 @@
@override
void toTextInternal(AstPrinter printer) {
+ // ignore: unnecessary_null_comparison
if (function != null) {
- printer.writeFunctionNode(function!, printer.getVariableName(variable));
- if (function!.body is ReturnStatement) {
+ printer.writeFunctionNode(function, printer.getVariableName(variable));
+ if (function.body is ReturnStatement) {
printer.write(';');
}
}
@@ -12624,7 +12634,7 @@
}
FunctionType getType(StaticTypeContext context) {
- return procedure.function!.computeFunctionType(context.nonNullable);
+ return procedure.function.computeFunctionType(context.nonNullable);
}
}
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 7c1de52..0d99e60 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -499,7 +499,7 @@
}
}
- List<Expression> readAnnotationList(TreeNode? parent) {
+ List<Expression> readAnnotationList([TreeNode? parent]) {
int length = readUInt30();
if (length == 0) return const <Expression>[];
return new List<Expression>.generate(
@@ -1493,15 +1493,15 @@
int fileEndOffset = readOffset();
int flags = readByte();
Name name = readName();
- if (node == null) {
- node = new Constructor(null, reference: reference, name: name);
- }
- List<Expression> annotations = readAnnotationList(node);
+ List<Expression> annotations = readAnnotationList();
assert(() {
debugPath.add(name.text);
return true;
}());
FunctionNode function = readFunctionNode();
+ if (node == null) {
+ node = new Constructor(function, reference: reference, name: name);
+ }
pushVariableDeclarations(function.positionalParameters);
pushVariableDeclarations(function.namedParameters);
_fillTreeNodeList(node.initializers, (index) => readInitializer(), node);
@@ -1515,6 +1515,7 @@
node.name = name;
node.fileUri = fileUri;
node.annotations = annotations;
+ setParents(annotations, node);
node.function = function..parent = node;
node.transformerFlags = transformerFlags;
return node;
@@ -1538,12 +1539,7 @@
ProcedureStubKind stubKind = ProcedureStubKind.values[readByte()];
int flags = readUInt30();
Name name = readName();
- if (node == null) {
- node = new Procedure(name, kind, null, reference: reference);
- } else {
- assert(node.kind == kind);
- }
- List<Expression> annotations = readAnnotationList(node);
+ List<Expression> annotations = readAnnotationList();
assert(() {
debugPath.add(name.text);
return true;
@@ -1554,8 +1550,13 @@
(kind == ProcedureKind.Factory && functionNodeSize <= 50) ||
_disableLazyReading;
Reference? stubTargetReference = readNullableMemberReference();
- FunctionNode? function =
- readFunctionNodeOption(!readFunctionNodeNow, endOffset);
+ FunctionNode function = readFunctionNode(
+ lazyLoadBody: !readFunctionNodeNow, outerEndOffset: endOffset);
+ if (node == null) {
+ node = new Procedure(name, kind, function, reference: reference);
+ } else {
+ assert(node.kind == kind);
+ }
int transformerFlags = getAndResetTransformerFlags();
assert(((_) => true)(debugPath.removeLast()));
node.startFileOffset = startFileOffset;
@@ -1565,15 +1566,15 @@
node.name = name;
node.fileUri = fileUri;
node.annotations = annotations;
- node.function = function;
- function?.parent = node;
+ setParents(annotations, node);
+ node.function = function..parent = node;
node.setTransformerFlagsWithoutLazyLoading(transformerFlags);
node.stubKind = stubKind;
node.stubTargetReference = stubTargetReference;
assert((node.stubKind == ProcedureStubKind.ConcreteForwardingStub &&
node.stubTargetReference != null) ||
- !(node.isForwardingStub && node.function!.body != null));
+ !(node.isForwardingStub && node.function.body != null));
assert(!(node.isMemberSignature && node.stubTargetReference == null),
"No member signature origin for member signature $node.");
return node;
@@ -1686,13 +1687,6 @@
return new AssertInitializer(readStatement() as AssertStatement);
}
- FunctionNode? readFunctionNodeOption(bool lazyLoadBody, int outerEndOffset) {
- return readAndCheckOptionTag()
- ? readFunctionNode(
- lazyLoadBody: lazyLoadBody, outerEndOffset: outerEndOffset)
- : null;
- }
-
FunctionNode readFunctionNode(
{bool lazyLoadBody: false, int outerEndOffset: -1}) {
int tag = readByte();
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 5652534..e7271c5 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -488,15 +488,6 @@
}
}
- void writeOptionalFunctionNode(FunctionNode? node) {
- if (node == null) {
- writeByte(Tag.Nothing);
- } else {
- writeByte(Tag.Something);
- writeFunctionNode(node);
- }
- }
-
void writeLinkTable(Component component) {
_binaryOffsetForLinkTable = getBufferOffset();
writeList(_canonicalNameList, writeCanonicalNameEntry);
@@ -1209,12 +1200,12 @@
writeName(node.name);
writeAnnotationList(node.annotations);
- assert(node.function!.typeParameters.isEmpty);
- writeFunctionNode(node.function!);
+ assert(node.function.typeParameters.isEmpty);
+ writeFunctionNode(node.function);
// Parameters are in scope in the initializers.
_variableIndexer ??= new VariableIndexer();
- _variableIndexer!.restoreScope(node.function!.positionalParameters.length +
- node.function!.namedParameters.length);
+ _variableIndexer!.restoreScope(node.function.positionalParameters.length +
+ node.function.namedParameters.length);
writeNodeList(node.initializers);
leaveScope(memberScope: true);
@@ -1273,13 +1264,13 @@
writeName(node.name);
writeAnnotationList(node.annotations);
writeNullAllowedReference(node.stubTargetReference);
- writeOptionalFunctionNode(node.function);
+ writeFunctionNode(node.function);
leaveScope(memberScope: true);
_currentlyInNonimplementation = currentlyInNonimplementationSaved;
assert(
(node.concreteForwardingStubTarget != null) ||
- !(node.isForwardingStub && node.function!.body != null),
+ !(node.isForwardingStub && node.function.body != null),
"Invalid forwarding stub $node.");
}
@@ -2251,7 +2242,7 @@
writeByte(Tag.FunctionDeclaration);
writeOffset(node.fileOffset);
writeVariableDeclaration(node.variable);
- writeFunctionNode(node.function!);
+ writeFunctionNode(node.function);
}
@override
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index fca44c6..523a5a2 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -174,7 +174,7 @@
/// Internal version of kernel binary format.
/// Bump it when making incompatible changes in kernel binaries.
/// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
- static const int BinaryFormatVersion = 60;
+ static const int BinaryFormatVersion = 61;
}
abstract class ConstantTag {
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 3f09922..6c76c2f 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -485,8 +485,8 @@
// Create the declaration before cloning the body to support recursive
// [LocalFunctionInvocation] nodes.
FunctionDeclaration declaration =
- new FunctionDeclaration(newVariable, null);
- FunctionNode functionNode = clone(node.function!);
+ new FunctionDeclaration(newVariable, dummyFunctionNode);
+ FunctionNode functionNode = clone(node.function);
declaration.function = functionNode..parent = declaration;
return declaration;
}
@@ -757,7 +757,7 @@
_activeFileUri = node.fileUri ?? _activeFileUri;
Constructor result = new Constructor(
- super.clone(node.function!),
+ super.clone(node.function),
name: node.name,
isConst: node.isConst,
isExternal: node.isExternal,
@@ -781,7 +781,7 @@
final Uri? activeFileUriSaved = _activeFileUri;
_activeFileUri = node.fileUri ?? _activeFileUri;
Procedure result = new Procedure(
- node.name, node.kind, super.clone(node.function!),
+ node.name, node.kind, super.clone(node.function),
reference: reference,
transformerFlags: node.transformerFlags,
fileUri: _activeFileUri,
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 3565196..27edbc1 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1206,13 +1206,13 @@
case ProcedureStubKind.ConcreteForwardingStub:
case ProcedureStubKind.NoSuchMethodForwarder:
case ProcedureStubKind.ConcreteMixinStub:
- writeFunction(node.function!, name: getMemberName(node));
+ writeFunction(node.function, name: getMemberName(node));
break;
case ProcedureStubKind.MemberSignature:
case ProcedureStubKind.AbstractMixinStub:
- writeFunction(node.function!,
+ writeFunction(node.function,
name: getMemberName(node), terminateLine: false);
- if (node.function!.body is ReturnStatement) {
+ if (node.function.body is ReturnStatement) {
writeSymbol(';');
}
writeSymbol(' -> ');
@@ -1241,7 +1241,7 @@
if (features.isNotEmpty) {
writeWord("/*${features.join(',')}*/");
}
- writeFunction(node.function!,
+ writeFunction(node.function,
name: node.name, initializers: node.initializers);
}
@@ -2289,8 +2289,9 @@
writeAnnotationList(node.variable.annotations);
writeIndentation();
writeWord('function');
+ // ignore: unnecessary_null_comparison
if (node.function != null) {
- writeFunction(node.function!, name: getVariableName(node.variable));
+ writeFunction(node.function, name: getVariableName(node.variable));
} else {
writeWord(getVariableName(node.variable));
endLine('...;');
diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart
index 0899fe8..f544010 100644
--- a/pkg/kernel/lib/text/text_serializer.dart
+++ b/pkg/kernel/lib/text/text_serializer.dart
@@ -1750,7 +1750,7 @@
TextSerializer<FunctionDeclaration> functionDeclarationSerializer =
Wrapped<Tuple2<VariableDeclaration, FunctionNode>, FunctionDeclaration>(
- (w) => Tuple2(w.variable, w.function!),
+ (w) => Tuple2(w.variable, w.function),
(u) => FunctionDeclaration(u.first, u.second),
Rebind(variableDeclarationSerializer, functionNodeSerializer));
@@ -1983,7 +1983,7 @@
TextSerializer<Procedure> methodSerializer =
Wrapped<Tuple3<Name, int, FunctionNode>, Procedure>(
- (w) => Tuple3(w.name, w.flags, w.function!),
+ (w) => Tuple3(w.name, w.flags, w.function),
(u) =>
Procedure(u.first, ProcedureKind.Method, u.third)..flags = u.second,
Tuple3Serializer(
@@ -1991,7 +1991,7 @@
TextSerializer<Procedure> getterSerializer =
Wrapped<Tuple3<Name, int, FunctionNode>, Procedure>(
- (w) => Tuple3(w.name, w.flags, w.function!),
+ (w) => Tuple3(w.name, w.flags, w.function),
(u) =>
Procedure(u.first, ProcedureKind.Getter, u.third)..flags = u.second,
Tuple3Serializer(
@@ -1999,7 +1999,7 @@
TextSerializer<Procedure> setterSerializer =
Wrapped<Tuple3<Name, int, FunctionNode>, Procedure>(
- (w) => Tuple3(w.name, w.flags, w.function!),
+ (w) => Tuple3(w.name, w.flags, w.function),
(u) =>
Procedure(u.first, ProcedureKind.Setter, u.third)..flags = u.second,
Tuple3Serializer(
@@ -2007,7 +2007,7 @@
TextSerializer<Procedure> operatorSerializer =
Wrapped<Tuple3<Name, int, FunctionNode>, Procedure>(
- (w) => Tuple3(w.name, w.flags, w.function!),
+ (w) => Tuple3(w.name, w.flags, w.function),
(u) => Procedure(u.first, ProcedureKind.Operator, u.third)
..flags = u.second,
Tuple3Serializer(
@@ -2015,7 +2015,7 @@
TextSerializer<Procedure> factorySerializer =
Wrapped<Tuple3<Name, int, FunctionNode>, Procedure>(
- (w) => Tuple3(w.name, w.flags, w.function!),
+ (w) => Tuple3(w.name, w.flags, w.function),
(u) => Procedure(u.first, ProcedureKind.Factory, u.third)
..flags = u.second,
Tuple3Serializer(
@@ -2024,7 +2024,7 @@
TextSerializer<Constructor> constructorSerializer = Wrapped<
Tuple3<Name, int, Tuple2<FunctionNode, List<Initializer>?>>,
Constructor>(
- (w) => Tuple3(w.name, w.flags, Tuple2(w.function!, w.initializers)),
+ (w) => Tuple3(w.name, w.flags, Tuple2(w.function, w.initializers)),
(u) =>
Constructor(u.third.first, name: u.first, initializers: u.third.second)
..flags = u.second,
diff --git a/pkg/kernel/lib/transformations/continuation.dart b/pkg/kernel/lib/transformations/continuation.dart
index c477db5..2404a99 100644
--- a/pkg/kernel/lib/transformations/continuation.dart
+++ b/pkg/kernel/lib/transformations/continuation.dart
@@ -189,7 +189,7 @@
assert(const [
Nullability.nonNullable,
Nullability.legacy
- ].contains(coreTypes.iterableGetIterator.function!.returnType.nullability));
+ ].contains(coreTypes.iterableGetIterator.function.returnType.nullability));
final DartType elementType = stmt.getElementType(staticTypeContext);
final iteratorType = InterfaceType(
@@ -1144,7 +1144,7 @@
TreeNode visitFunctionDeclaration(
FunctionDeclaration stmt, TreeNode? removalSentinel) {
- stmt.function = transform(stmt.function!)..parent = stmt;
+ stmt.function = transform(stmt.function)..parent = stmt;
statements.add(stmt);
return removalSentinel ?? EmptyStatement();
}
diff --git a/pkg/kernel/lib/transformations/mixin_full_resolution.dart b/pkg/kernel/lib/transformations/mixin_full_resolution.dart
index b1a72a8..c9b5c39 100644
--- a/pkg/kernel/lib/transformations/mixin_full_resolution.dart
+++ b/pkg/kernel/lib/transformations/mixin_full_resolution.dart
@@ -138,7 +138,7 @@
if (setter != null) {
setters.remove(field.name);
VariableDeclaration parameter =
- setter.function!.positionalParameters.first;
+ setter.function.positionalParameters.first;
clone.isCovariant = parameter.isCovariant;
clone.isGenericCovariantImpl = parameter.isGenericCovariantImpl;
}
@@ -180,8 +180,8 @@
var originalProcedure = class_.procedures[i];
if (originalProcedure.name == procedure.name &&
originalProcedure.kind == procedure.kind) {
- FunctionNode src = originalProcedure.function!;
- FunctionNode dst = procedure.function!;
+ FunctionNode src = originalProcedure.function;
+ FunctionNode dst = procedure.function;
if (src.positionalParameters.length !=
dst.positionalParameters.length ||
@@ -200,8 +200,8 @@
Procedure clone = cloner.cloneProcedure(procedure, reference);
if (originalIndex != null) {
Procedure originalProcedure = class_.procedures[originalIndex];
- FunctionNode src = originalProcedure.function!;
- FunctionNode dst = clone.function!;
+ FunctionNode src = originalProcedure.function;
+ FunctionNode dst = clone.function;
assert(src.typeParameters.length == dst.typeParameters.length);
for (int j = 0; j < src.typeParameters.length; ++j) {
dst.typeParameters[j].flags = src.typeParameters[j].flags;
diff --git a/pkg/kernel/lib/transformations/scanner.dart b/pkg/kernel/lib/transformations/scanner.dart
index ee9b660..c49a934 100644
--- a/pkg/kernel/lib/transformations/scanner.dart
+++ b/pkg/kernel/lib/transformations/scanner.dart
@@ -2,36 +2,36 @@
// 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.
-// @dart = 2.9
-
library kernel.transformations.scanner;
import '../ast.dart';
import '../kernel.dart';
-abstract class Scanner<X extends TreeNode, Y extends TreeNode> {
- final Scanner<Y, TreeNode> next;
+abstract class Scanner<X extends TreeNode?, Y extends TreeNode?> {
+ final Scanner<Y, TreeNode?>? next;
Scanner(this.next);
bool predicate(X x);
ScanResult<X, Y> scan(TreeNode node);
}
-class ScanResult<X extends TreeNode, Y extends TreeNode> {
- Map<X, ScanResult<Y, TreeNode>> targets = new Map();
- Map<X, ScanError> errors;
+class ScanResult<X extends TreeNode?, Y extends TreeNode?> {
+ Map<X, ScanResult<Y, TreeNode?>?> targets = new Map();
+ Map<X, ScanError>? errors;
+
+ String toString() => 'ScanResult<$X,$Y>';
}
class ScanError {}
-abstract class ClassScanner<Y extends TreeNode> implements Scanner<Class, Y> {
- final Scanner<Y, TreeNode> next;
+abstract class ClassScanner<Y extends TreeNode?> implements Scanner<Class, Y> {
+ final Scanner<Y, TreeNode?>? next;
ClassScanner(this.next);
bool predicate(Class node);
ScanResult<Class, Y> scan(TreeNode node) {
- ScanResult<Class, Y> result = new ScanResult();
+ ScanResult<Class, Y> result = new ScanResult<Class, Y>();
if (node is Class) {
if (predicate(node)) {
@@ -64,14 +64,14 @@
}
abstract class FieldScanner<Y extends TreeNode> implements Scanner<Field, Y> {
- final Scanner<Y, TreeNode> next;
+ final Scanner<Y, TreeNode>? next;
FieldScanner(this.next);
bool predicate(Field node);
ScanResult<Field, Y> scan(TreeNode node) {
- ScanResult<Field, Y> result = new ScanResult();
+ ScanResult<Field, Y> result = new ScanResult<Field, Y>();
if (node is Field) {
_scanField(node, result);
@@ -115,14 +115,14 @@
}
abstract class MemberScanner<Y extends TreeNode> implements Scanner<Member, Y> {
- final Scanner<Y, TreeNode> next;
+ final Scanner<Y, TreeNode?>? next;
MemberScanner(this.next);
bool predicate(Member node);
ScanResult<Member, Y> scan(TreeNode node) {
- ScanResult<Member, Y> result = new ScanResult();
+ ScanResult<Member, Y> result = new ScanResult<Member, Y>();
if (node is Member) {
_scanMember(node, result);
@@ -165,16 +165,16 @@
}
}
-abstract class ProcedureScanner<Y extends TreeNode>
+abstract class ProcedureScanner<Y extends TreeNode?>
implements Scanner<Procedure, Y> {
- final Scanner<Y, TreeNode> next;
+ final Scanner<Y, TreeNode>? next;
ProcedureScanner(this.next);
bool predicate(Procedure node);
ScanResult<Procedure, Y> scan(TreeNode node) {
- ScanResult<Procedure, Y> result = new ScanResult();
+ ScanResult<Procedure, Y> result = new ScanResult<Procedure, Y>();
if (node is Procedure) {
_scanProcedure(node, result);
@@ -219,15 +219,16 @@
abstract class ExpressionScanner<Y extends TreeNode>
extends RecursiveResultVisitor<void> implements Scanner<Expression, Y> {
- final Scanner<Y, TreeNode> next;
- ScanResult<Expression, Y> _result;
+ final Scanner<Y, TreeNode>? next;
+ ScanResult<Expression, Y>? _result;
ExpressionScanner(this.next);
bool predicate(Expression node);
ScanResult<Expression, Y> scan(TreeNode node) {
- ScanResult<Expression, Y> result = _result = new ScanResult();
+ ScanResult<Expression, Y> result =
+ _result = new ScanResult<Expression, Y>();
node.accept(this);
_result = null;
return result;
@@ -235,23 +236,24 @@
void visitExpression(Expression node) {
if (predicate(node)) {
- _result.targets[node] = next?.scan(node);
+ _result!.targets[node] = next?.scan(node);
// TODO: Update result.errors.
}
}
}
-abstract class MethodInvocationScanner<Y extends TreeNode>
+abstract class MethodInvocationScanner<Y extends TreeNode?>
extends RecursiveVisitor implements Scanner<MethodInvocation, Y> {
- final Scanner<Y, TreeNode> next;
- ScanResult<MethodInvocation, Y> _result;
+ final Scanner<Y, TreeNode>? next;
+ ScanResult<MethodInvocation, Y>? _result;
MethodInvocationScanner(this.next);
bool predicate(MethodInvocation node);
ScanResult<MethodInvocation, Y> scan(TreeNode node) {
- ScanResult<MethodInvocation, Y> result = _result = new ScanResult();
+ ScanResult<MethodInvocation, Y> result =
+ _result = new ScanResult<MethodInvocation, Y>();
node.accept(this);
_result = null;
return result;
@@ -259,7 +261,7 @@
void visitMethodInvocation(MethodInvocation node) {
if (predicate(node)) {
- _result.targets[node] = next?.scan(node);
+ _result!.targets[node] = next?.scan(node);
// TODO: Update result.errors.
}
}
diff --git a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
index 7e81c8d..6538a7b 100644
--- a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
+++ b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
@@ -208,7 +208,7 @@
return node;
}
- _addLocationArgument(node, target.function!, constructedClass,
+ _addLocationArgument(node, target.function, constructedClass,
isConst: node.isConst);
return node;
}
@@ -234,7 +234,7 @@
return node;
}
- _addLocationArgument(node, constructor.function!, constructedClass,
+ _addLocationArgument(node, constructor.function, constructedClass,
isConst: node.isConst);
return node;
}
@@ -252,7 +252,7 @@
// constant expression.
!isConst) {
final VariableDeclaration? creationLocationParameter = _getNamedParameter(
- _currentFactory!.function!,
+ _currentFactory!.function,
_creationLocationParameterName,
);
if (creationLocationParameter != null) {
@@ -401,7 +401,7 @@
return;
}
assert(!_hasNamedParameter(
- constructor.function!,
+ constructor.function,
_creationLocationParameterName,
));
final VariableDeclaration variable = new VariableDeclaration(
@@ -409,7 +409,7 @@
type: new InterfaceType(
_locationClass, clazz.enclosingLibrary.nullable),
initializer: new NullLiteral());
- if (!_maybeAddNamedParameter(constructor.function!, variable)) {
+ if (!_maybeAddNamedParameter(constructor.function, variable)) {
return;
}
@@ -424,7 +424,7 @@
}
_maybeAddCreationLocationArgument(
initializer.arguments,
- initializer.target.function!,
+ initializer.target.function,
new VariableGet(variable),
_locationClass,
);
@@ -548,7 +548,7 @@
for (Procedure procedure in clazz.procedures) {
if (procedure.isFactory) {
_maybeAddNamedParameter(
- procedure.function!,
+ procedure.function,
new VariableDeclaration(_creationLocationParameterName,
type: new InterfaceType(
_locationClass, clazz.enclosingLibrary.nullable),
@@ -578,12 +578,12 @@
_locationClass, clazz.enclosingLibrary.nullable),
initializer: new NullLiteral());
if (_hasNamedParameter(
- constructor.function!, _creationLocationParameterName)) {
+ constructor.function, _creationLocationParameterName)) {
// Constructor was already rewritten.
// TODO(jacobr): is this case actually hit?
return;
}
- if (!_maybeAddNamedParameter(constructor.function!, variable)) {
+ if (!_maybeAddNamedParameter(constructor.function, variable)) {
return;
}
for (Initializer initializer in constructor.initializers) {
@@ -597,7 +597,7 @@
_maybeAddCreationLocationArgument(
initializer.arguments,
- initializer.target.function!,
+ initializer.target.function,
new VariableGet(variable),
_locationClass,
);
@@ -605,7 +605,7 @@
_isSubclassOfWidget(initializer.target.enclosingClass)) {
_maybeAddCreationLocationArgument(
initializer.arguments,
- initializer.target.function!,
+ initializer.target.function,
new VariableGet(variable),
_locationClass,
);
diff --git a/pkg/kernel/lib/transformations/value_class.dart b/pkg/kernel/lib/transformations/value_class.dart
index 5a5f424..b50c56f 100644
--- a/pkg/kernel/lib/transformations/value_class.dart
+++ b/pkg/kernel/lib/transformations/value_class.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
library kernel.transformations.value_class;
import 'package:kernel/type_environment.dart';
@@ -21,7 +19,7 @@
}
class JenkinsClassScanner extends ClassScanner<Procedure> {
- JenkinsClassScanner(Scanner<Procedure, TreeNode> next) : super(next);
+ JenkinsClassScanner(Scanner<Procedure, TreeNode?> next) : super(next);
bool predicate(Class node) {
return node.name == "JenkinsSmiHash";
@@ -37,7 +35,7 @@
}
class AllMemberScanner extends MemberScanner<MethodInvocation> {
- AllMemberScanner(Scanner<MethodInvocation, TreeNode> next) : super(next);
+ AllMemberScanner(Scanner<MethodInvocation, TreeNode?> next) : super(next);
bool predicate(Member member) => true;
}
@@ -78,7 +76,7 @@
void transformValueClass(Class cls, CoreTypes coreTypes,
ClassHierarchy hierarchy, TypeEnvironment typeEnvironment) {
- Constructor syntheticConstructor = null;
+ Constructor? syntheticConstructor = null;
for (Constructor constructor in cls.constructors) {
if (constructor.isSynthetic) {
syntheticConstructor = constructor;
@@ -87,9 +85,9 @@
List<VariableDeclaration> allVariables = queryAllInstanceVariables(cls);
List<VariableDeclaration> allVariablesList = allVariables.toList();
- allVariablesList.sort((a, b) => a.name.compareTo(b.name));
+ allVariablesList.sort((a, b) => a.name!.compareTo(b.name!));
- addConstructor(cls, coreTypes, syntheticConstructor);
+ addConstructor(cls, coreTypes, syntheticConstructor!);
addEqualsOperator(cls, coreTypes, hierarchy, allVariablesList);
addHashCode(cls, coreTypes, hierarchy, allVariablesList);
addToString(cls, coreTypes, hierarchy, allVariablesList);
@@ -99,13 +97,13 @@
void addConstructor(
Class cls, CoreTypes coreTypes, Constructor syntheticConstructor) {
- Constructor superConstructor = null;
- for (Constructor constructor in cls.superclass.constructors) {
+ Constructor? superConstructor = null;
+ for (Constructor constructor in cls.superclass!.constructors) {
if (constructor.name.text == "") {
superConstructor = constructor;
}
}
- List<VariableDeclaration> superParameters = superConstructor
+ List<VariableDeclaration> superParameters = superConstructor!
.function.namedParameters
.map<VariableDeclaration>((e) => VariableDeclaration(e.name, type: e.type)
..parent = syntheticConstructor.function)
@@ -118,7 +116,7 @@
List<Initializer> initializersConstructor = cls.fields
.map<Initializer>((f) =>
- FieldInitializer(f, VariableGet(ownFields[f.name.text]))
+ FieldInitializer(f, VariableGet(ownFields[f.name.text]!))
..parent = syntheticConstructor)
.toList();
@@ -157,8 +155,8 @@
DartType fieldsType = variable.type;
if (fieldsType is InterfaceType) {
targetEquals =
- hierarchy.getInterfaceMember(fieldsType.classNode, Name("=="));
- target = hierarchy.getInterfaceMember(cls, Name(variable.name));
+ hierarchy.getInterfaceMember(fieldsType.classNode, Name("=="))!;
+ target = hierarchy.getInterfaceMember(cls, Name(variable.name!))!;
}
targetsEquals[variable] = targetEquals;
targets[variable] = target;
@@ -170,17 +168,17 @@
FunctionNode(
ReturnStatement(allVariables
.map((f) => MethodInvocation(
- PropertyGet(ThisExpression(), Name(f.name), targets[f]),
+ PropertyGet(ThisExpression(), Name(f.name!), targets[f]),
Name("=="),
Arguments([
PropertyGet(
- VariableGet(other, myType), Name(f.name), targets[f])
+ VariableGet(other, myType), Name(f.name!), targets[f])
]),
targetsEquals[f]))
.fold(
IsExpression(VariableGet(other), myType),
(previousValue, element) => LogicalExpression(
- previousValue, LogicalExpressionOperator.AND, element))),
+ previousValue!, LogicalExpressionOperator.AND, element))),
returnType: returnType,
positionalParameters: [other]),
fileUri: cls.fileUri)
@@ -200,17 +198,22 @@
}
DartType returnType = coreTypes.intRawType(cls.enclosingLibrary.nonNullable);
- Procedure hashCombine, hashFinish;
+ Procedure? hashCombine, hashFinish;
HashCombineMethodsScanner hashCombineMethodsScanner =
new HashCombineMethodsScanner();
JenkinsClassScanner jenkinsScanner =
new JenkinsClassScanner(hashCombineMethodsScanner);
ScanResult<Class, Procedure> hashMethodsResult =
- jenkinsScanner.scan(cls.enclosingLibrary.enclosingComponent);
+ jenkinsScanner.scan(cls.enclosingLibrary.enclosingComponent!);
for (Class clazz in hashMethodsResult.targets.keys) {
- for (Procedure procedure in hashMethodsResult.targets[clazz].targets.keys) {
- if (procedure.name.text == "combine") hashCombine = procedure;
- if (procedure.name.text == "finish") hashFinish = procedure;
+ for (Procedure procedure
+ in hashMethodsResult.targets[clazz]!.targets.keys) {
+ if (procedure.name.text == "combine") {
+ hashCombine = procedure;
+ }
+ if (procedure.name.text == "finish") {
+ hashFinish = procedure;
+ }
}
}
@@ -222,8 +225,8 @@
DartType fieldsType = variable.type;
if (fieldsType is InterfaceType) {
targetHashcode =
- hierarchy.getInterfaceMember(fieldsType.classNode, Name("hashCode"));
- target = hierarchy.getInterfaceMember(cls, Name(variable.name));
+ hierarchy.getInterfaceMember(fieldsType.classNode, Name("hashCode"))!;
+ target = hierarchy.getInterfaceMember(cls, Name(variable.name!))!;
}
targetsHashcode[variable] = targetHashcode;
targets[variable] = target;
@@ -233,11 +236,12 @@
ProcedureKind.Getter,
FunctionNode(
ReturnStatement(StaticInvocation(
- hashFinish,
+ hashFinish!,
Arguments([
allVariables
.map((f) => (PropertyGet(
- PropertyGet(ThisExpression(), Name(f.name), targets[f]),
+ PropertyGet(
+ ThisExpression(), Name(f.name!), targets[f]),
Name("hashCode"),
targetsHashcode[f])))
.fold(
@@ -249,7 +253,7 @@
hierarchy.getInterfaceMember(
coreTypes.stringClass, Name("hashCode"))),
(previousValue, element) => StaticInvocation(
- hashCombine, Arguments([previousValue, element])))
+ hashCombine!, Arguments([previousValue, element])))
]))),
returnType: returnType),
fileUri: cls.fileUri)
@@ -263,8 +267,8 @@
for (VariableDeclaration variable in allVariablesList) {
wording.add(StringLiteral("${variable.name}: "));
wording.add(MethodInvocation(
- PropertyGet(ThisExpression(), Name(variable.name),
- hierarchy.getInterfaceMember(cls, Name(variable.name))),
+ PropertyGet(ThisExpression(), Name(variable.name!),
+ hierarchy.getInterfaceMember(cls, Name(variable.name!))),
Name("toString"),
Arguments([]),
(variable.type is InterfaceType)
@@ -306,8 +310,8 @@
DartType fieldsType = variable.type;
if (fieldsType is InterfaceType) {
targetEquals =
- hierarchy.getInterfaceMember(fieldsType.classNode, Name("=="));
- target = hierarchy.getInterfaceMember(cls, Name(variable.name));
+ hierarchy.getInterfaceMember(fieldsType.classNode, Name("=="))!;
+ target = hierarchy.getInterfaceMember(cls, Name(variable.name!))!;
}
targetsEquals[variable] = targetEquals;
targets[variable] = target;
@@ -321,7 +325,7 @@
syntheticConstructor,
Arguments([],
named: allVariables
- .map((f) => NamedExpression(f.name, VariableGet(f)))
+ .map((f) => NamedExpression(f.name!, VariableGet(f)))
.toList()))),
namedParameters: allVariables),
fileUri: cls.fileUri)
@@ -329,13 +333,13 @@
}
List<VariableDeclaration> queryAllInstanceVariables(Class cls) {
- Constructor superConstructor = null;
- for (Constructor constructor in cls.superclass.constructors) {
+ Constructor? superConstructor = null;
+ for (Constructor constructor in cls.superclass!.constructors) {
if (constructor.name.text == "") {
superConstructor = constructor;
}
}
- return superConstructor.function.namedParameters
+ return superConstructor!.function.namedParameters
.map<VariableDeclaration>(
(f) => VariableDeclaration(f.name, type: f.type))
.toList()
@@ -344,20 +348,20 @@
}
void removeValueClassAnnotation(Class cls) {
- int valueClassAnnotationIndex;
+ int? valueClassAnnotationIndex;
for (int annotationIndex = 0;
annotationIndex < cls.annotations.length;
annotationIndex++) {
Expression annotation = cls.annotations[annotationIndex];
if (annotation is ConstantExpression &&
annotation.constant is StringConstant) {
- StringConstant constant = annotation.constant;
+ StringConstant constant = annotation.constant as StringConstant;
if (constant.value == 'valueClass') {
valueClassAnnotationIndex = annotationIndex;
}
}
}
- cls.annotations.removeAt(valueClassAnnotationIndex);
+ cls.annotations.removeAt(valueClassAnnotationIndex!);
}
void treatCopyWithCallSites(Component component, CoreTypes coreTypes,
@@ -368,11 +372,12 @@
ScanResult<Member, MethodInvocation> copyWithCallSites =
copyWithScanner.scan(component);
for (Member memberWithCopyWith in copyWithCallSites.targets.keys) {
- if (copyWithCallSites.targets[memberWithCopyWith].targets != null) {
+ Map<MethodInvocation, ScanResult<TreeNode?, TreeNode?>?>? targets =
+ copyWithCallSites.targets[memberWithCopyWith]?.targets;
+ if (targets != null) {
StaticTypeContext staticTypeContext =
StaticTypeContext(memberWithCopyWith, typeEnvironment);
- for (MethodInvocation copyWithCall
- in copyWithCallSites.targets[memberWithCopyWith].targets.keys) {
+ for (MethodInvocation copyWithCall in targets.keys) {
AsExpression receiver = copyWithCall.receiver as AsExpression;
Expression valueClassInstance = receiver.operand;
@@ -396,14 +401,14 @@
for (NamedExpression argument in copyWithCall.arguments.named) {
preTransformationArguments[argument.name] = argument.value;
}
- Constructor syntheticConstructor;
+ Constructor? syntheticConstructor;
for (Constructor constructor in valueClass.constructors) {
if (constructor.isSynthetic) {
syntheticConstructor = constructor;
}
}
List<VariableDeclaration> allArguments =
- syntheticConstructor.function.namedParameters;
+ syntheticConstructor!.function.namedParameters;
VariableDeclaration letVariable =
VariableDeclaration.forValue(copyWithCall.receiver);
@@ -411,11 +416,11 @@
for (VariableDeclaration argument in allArguments) {
if (preTransformationArguments.containsKey(argument.name)) {
postTransformationArguments.named.add(NamedExpression(
- argument.name, preTransformationArguments[argument.name])
+ argument.name!, preTransformationArguments[argument.name]!)
..parent = postTransformationArguments);
} else {
- postTransformationArguments.named.add(NamedExpression(argument.name,
- PropertyGet(VariableGet(letVariable), Name(argument.name)))
+ postTransformationArguments.named.add(NamedExpression(argument.name!,
+ PropertyGet(VariableGet(letVariable), Name(argument.name!)))
..parent = postTransformationArguments);
}
}
@@ -429,7 +434,7 @@
for (Expression annotation in node.annotations) {
if (annotation is ConstantExpression &&
annotation.constant is StringConstant) {
- StringConstant constant = annotation.constant;
+ StringConstant constant = annotation.constant as StringConstant;
if (constant.value == "valueClass") {
return true;
}
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index a263a04..9431e8e 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -363,7 +363,7 @@
"Only forwarding stubs can have a forwarding stub super target "
"$node.");
}
- node.function!.accept(this);
+ node.function.accept(this);
classTypeParametersAreInScope = false;
visitList(node.annotations, this);
exitParent(oldParent);
@@ -377,7 +377,7 @@
// in scope in the initializer list.
TreeNode? oldParent = enterParent(node);
int stackHeight = enterLocalScope();
- visitChildren(node.function!);
+ visitChildren(node.function);
visitList(node.initializers, this);
if (!isOutline) {
checkInitializers(node);
diff --git a/pkg/kernel/test/verify_test.dart b/pkg/kernel/test/verify_test.dart
index 097200d..f8b1718 100644
--- a/pkg/kernel/test/verify_test.dart
+++ b/pkg/kernel/test/verify_test.dart
@@ -258,8 +258,8 @@
"Incorrect parent pointer on FunctionNode:"
" expected 'Procedure', but found: 'Null'.",
(TestHarness test) {
- var procedure =
- new Procedure(new Name('bar'), ProcedureKind.Method, null);
+ var procedure = new Procedure(
+ new Name('bar'), ProcedureKind.Method, dummyFunctionNode);
procedure.function = new FunctionNode(new EmptyStatement());
test.addNode(procedure);
},
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index 52075ce..44d56f1 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -820,27 +820,41 @@
throw _FfiStaticTypeError();
}
+ /// Returns the class that should not be implemented or extended.
+ ///
+ /// If the superclass is not sealed, returns `null`.
Class _extendsOrImplementsSealedClass(Class klass) {
- final Class superClass = klass.superclass;
+ // Classes in dart:ffi themselves can extend FFI classes.
+ if (klass == arrayClass ||
+ klass == arraySizeClass ||
+ klass == compoundClass ||
+ klass == opaqueClass ||
+ klass == structClass ||
+ nativeTypesClasses.contains(klass)) {
+ return null;
+ }
// The Opaque and Struct classes can be extended, but subclasses
// cannot be (nor implemented).
- if (klass != opaqueClass &&
- klass != structClass &&
- (hierarchy.isSubtypeOf(klass, opaqueClass) ||
- hierarchy.isSubtypeOf(klass, compoundClass))) {
- return superClass != opaqueClass && superClass != structClass
- ? superClass
- : null;
- }
-
- if (!nativeTypesClasses.contains(klass) && klass != arrayClass) {
- for (final parent in nativeTypesClasses) {
- if (hierarchy.isSubtypeOf(klass, parent)) {
- return parent;
+ final onlyDirectExtendsClasses = [opaqueClass, structClass];
+ final superClass = klass.superclass;
+ for (final onlyDirectExtendsClass in onlyDirectExtendsClasses) {
+ if (hierarchy.isSubtypeOf(klass, onlyDirectExtendsClass)) {
+ if (superClass == onlyDirectExtendsClass) {
+ // Directly extending is fine.
+ return null;
+ } else {
+ return superClass;
}
}
}
+
+ for (final parent in nativeTypesClasses) {
+ if (hierarchy.isSubtypeOf(klass, parent)) {
+ return parent;
+ }
+ }
+
return null;
}
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index c8a530d..b22fa57 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -373,8 +373,8 @@
}
/// Marker for noSuchMethod() invocation in the map of invocation targets.
- static final Member kNoSuchMethodMarker =
- new Procedure(new Name('noSuchMethod&&'), ProcedureKind.Method, null);
+ static final Member kNoSuchMethodMarker = new Procedure(
+ new Name('noSuchMethod&&'), ProcedureKind.Method, new FunctionNode(null));
_DispatchableInvocation(Selector selector, Args<Type> args)
: super(selector, args) {
diff --git a/pkg/vm_snapshot_analysis/lib/v8_profile.dart b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
index 5ef79af..cbbe717 100644
--- a/pkg/vm_snapshot_analysis/lib/v8_profile.dart
+++ b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
@@ -88,6 +88,25 @@
m['strings'],
edgesStartIndexForNode);
}
+
+ @override
+ String toString() {
+ final buffer = StringBuffer();
+ buffer
+ ..write("Node count: ")
+ ..writeln(nodeCount)
+ ..write("Edge count: ")
+ ..writeln(edgeCount);
+ buffer.write("Nodes:");
+ for (final node in nodes) {
+ buffer
+ ..writeln()
+ ..write(node.index)
+ ..write(': ')
+ ..writeln(node);
+ }
+ return buffer.toString();
+ }
}
/// Meta-information about the serialized snapshot.
@@ -228,12 +247,6 @@
}.toString();
}
- /// Returns the target of an outgoing edge with the given name (if any),
- /// but first checks for a corresponding artificial edge indicating a dropped
- /// object.
- Node possiblyDroppedTarget(String edgeName) =>
- this[':$edgeName'] ?? this[edgeName];
-
/// Returns the target of an outgoing edge with the given name (if any).
Node operator [](String edgeName) => this
.edges
@@ -404,7 +417,7 @@
ProgramInfoNode createInfoNodeFor(Node node) {
switch (node.type) {
case 'Code':
- var owner = node.possiblyDroppedTarget('owner_');
+ var owner = node['owner_'];
if (owner.type != 'Type') {
final ownerNode =
owner.type == 'Null' ? program.stubs : getInfoNodeFor(owner);
@@ -428,7 +441,7 @@
// Artificial nodes may not have a data_ field.
var data = node['data_'];
if (data?.type == 'ClosureData') {
- owner = data.possiblyDroppedTarget('parent_function_');
+ owner = data['parent_function_'];
}
return makeInfoNode(node.index,
name: node.name,
diff --git a/pkg/vm_snapshot_analysis/test/utils.dart b/pkg/vm_snapshot_analysis/test/utils.dart
index bdc14e7..830ca06 100644
--- a/pkg/vm_snapshot_analysis/test/utils.dart
+++ b/pkg/vm_snapshot_analysis/test/utils.dart
@@ -60,17 +60,21 @@
if (flag != null) '$flag=${snapshot.sizesJson}',
];
- // Compile input.dart to native and output instruction sizes.
- final result = await Process.run(dart2native, [
+ final args = [
'-o',
snapshot.outputBinary,
'--packages=$packages',
'--extra-gen-snapshot-options=${extraGenSnapshotOptions.join(',')}',
mainDart,
- ]);
+ ];
+
+ // Compile input.dart to native and output instruction sizes.
+ final result = await Process.run(dart2native, args);
expect(result.exitCode, equals(0), reason: '''
-Compilation completed successfully.
+Compilation completed with exit code ${result.exitCode}.
+
+Command line: $dart2native ${args.join(' ')}
stdout: ${result.stdout}
stderr: ${result.stderr}
@@ -86,13 +90,18 @@
});
}
+const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES';
+
Future withTempDir(Future Function(String dir) f) async {
final tempDir =
Directory.systemTemp.createTempSync('instruction-sizes-test-');
try {
await f(tempDir.path);
} finally {
- tempDir.deleteSync(recursive: true);
+ if (!Platform.environment.containsKey(keepTempKey) ||
+ Platform.environment[keepTempKey].isEmpty) {
+ tempDir.deleteSync(recursive: true);
+ }
}
}
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index b4415cf..3fce5e6 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -475,7 +475,7 @@
*
* Requires there to be a current isolate.
*
- * \param object An object.
+ * \param object An object with identity.
* \param peer A pointer to a native object or NULL. This value is
* provided to callback when it is invoked.
* \param external_allocation_size The number of externally allocated
@@ -531,7 +531,7 @@
*
* Requires there to be a current isolate.
*
- * \param object An object.
+ * \param object An object with identity.
* \param peer A pointer to a native object or NULL. This value is
* provided to callback when it is invoked.
* \param external_allocation_size The number of externally allocated
diff --git a/runtime/vm/canonical_tables.h b/runtime/vm/canonical_tables.h
index 004b07f..b0e25b5 100644
--- a/runtime/vm/canonical_tables.h
+++ b/runtime/vm/canonical_tables.h
@@ -30,12 +30,12 @@
}
return other.Equals(data_, len_);
}
- intptr_t Hash() const { return hash_; }
+ uword Hash() const { return hash_; }
private:
const CharType* data_;
intptr_t len_;
- intptr_t hash_;
+ uword hash_;
};
typedef CharArray<uint8_t> Latin1Array;
typedef CharArray<uint16_t> UTF16Array;
@@ -55,14 +55,14 @@
}
return other.Equals(str_, begin_index_, len_);
}
- intptr_t Hash() const { return hash_; }
+ uword Hash() const { return hash_; }
private:
bool is_all() const { return begin_index_ == 0 && len_ == str_.Length(); }
const String& str_;
intptr_t begin_index_;
intptr_t len_;
- intptr_t hash_;
+ uword hash_;
};
class ConcatString {
@@ -77,12 +77,12 @@
}
return other.EqualsConcat(str1_, str2_);
}
- intptr_t Hash() const { return hash_; }
+ uword Hash() const { return hash_; }
private:
const String& str1_;
const String& str2_;
- intptr_t hash_;
+ uword hash_;
};
class SymbolTraits {
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 12f1e10..8841d37b 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -1045,12 +1045,6 @@
}
};
-// If DROPPED_NAME(name) is used for a v8 snapshot profile edge, then
-// possiblyDroppedTarget() should be used to retrieve the edge target in
-// pkg/vm_snapshot_analysis/v8_profile.dart so the artificial node is found
-// instead of its in-snapshot replacement.
-#define DROPPED_NAME(name) (":" #name)
-
#if !defined(DART_PRECOMPILED_RUNTIME)
class ClosureDataSerializationCluster : public SerializationCluster {
public:
@@ -1097,25 +1091,6 @@
}
}
- // Some closure data objects have their parent functions dropped from the
- // snapshot, which makes it is impossible to recover program structure when
- // analysing snapshot profile. To facilitate analysis of snapshot profiles
- // we include artificial nodes into profile representing such dropped
- // parent functions.
- void WriteDroppedParentFunctionsIntoProfile(Serializer* s) {
- ASSERT(s->profile_writer() != nullptr);
-
- for (auto data : objects_) {
- ObjectPtr parent_function =
- WeakSerializationReference::Unwrap(data->untag()->parent_function());
- if (s->CreateArtificialNodeIfNeeded(parent_function)) {
- AutoTraceObject(data);
- s->AttributePropertyRef(parent_function, DROPPED_NAME(parent_function_),
- /*permit_artificial_ref=*/true);
- }
- }
- }
-
private:
GrowableArray<ClosureDataPtr> objects_;
};
@@ -1958,38 +1933,19 @@
if (kind == Snapshot::kFullAOT && FLAG_use_bare_instructions &&
code->untag()->object_pool_ != ObjectPool::null()) {
ObjectPoolPtr pool = code->untag()->object_pool_;
-
- for (intptr_t i = 0; i < pool->untag()->length_; i++) {
- uint8_t bits = pool->untag()->entry_bits()[i];
- if (ObjectPool::TypeBits::decode(bits) ==
- ObjectPool::EntryType::kTaggedObject) {
- s->AttributeElementRef(pool->untag()->data()[i].raw_obj_, i);
- }
- }
+ // Non-empty per-code object pools should not be reachable in this mode.
+ ASSERT(!s->HasRef(pool) || pool == Object::empty_object_pool().ptr());
+ s->CreateArtificialNodeIfNeeded(pool);
+ s->AttributePropertyRef(pool, "object_pool_");
}
- if (code->untag()->static_calls_target_table_ != Array::null()) {
- array_ = code->untag()->static_calls_target_table_;
- intptr_t index = code->untag()->object_pool_ != ObjectPool::null()
- ? code->untag()->object_pool_->untag()->length_
- : 0;
- for (auto entry : StaticCallsTable(array_)) {
- auto kind = Code::KindField::decode(
- Smi::Value(entry.Get<Code::kSCallTableKindAndOffset>()));
- switch (kind) {
- case Code::kCallViaCode:
- // Code object in the pool.
- continue;
- case Code::kPcRelativeTTSCall:
- // TTS will be reachable through type object which itself is
- // in the pool.
- continue;
- case Code::kPcRelativeCall:
- case Code::kPcRelativeTailCall:
- auto destination = entry.Get<Code::kSCallTableCodeOrTypeTarget>();
- ASSERT(destination->IsHeapObject() && destination->IsCode());
- s->AttributeElementRef(destination, index++);
- }
- }
+ if (kind != Snapshot::kFullJIT &&
+ code->untag()->static_calls_target_table_ != Array::null()) {
+ auto const table = code->untag()->static_calls_target_table_;
+ // Non-empty static call target tables shouldn't be reachable in this
+ // mode.
+ ASSERT(!s->HasRef(table) || table == Object::empty_array().ptr());
+ s->CreateArtificialNodeIfNeeded(table);
+ s->AttributePropertyRef(table, "static_calls_target_table_");
}
}
#endif // defined(DART_PRECOMPILER)
@@ -1999,6 +1955,15 @@
// for the discarded Code objects.
ASSERT(kind == Snapshot::kFullAOT && FLAG_use_bare_instructions &&
FLAG_dwarf_stack_traces_mode && !FLAG_retain_code_objects);
+#if defined(DART_PRECOMPILER)
+ if (FLAG_write_v8_snapshot_profile_to != nullptr) {
+ // Keep the owner as a (possibly artificial) node for snapshot analysis.
+ const auto& owner = code->untag()->owner_;
+ s->CreateArtificialNodeIfNeeded(owner);
+ s->AttributePropertyRef(owner, "owner_");
+ }
+#endif
+
return;
}
@@ -2047,25 +2012,6 @@
GrowableArray<CodePtr>* objects() { return &objects_; }
GrowableArray<CodePtr>* deferred_objects() { return &deferred_objects_; }
- // Some code objects would have their owners dropped from the snapshot,
- // which makes it is impossible to recover program structure when
- // analysing snapshot profile. To facilitate analysis of snapshot profiles
- // we include artificial nodes into profile representing such dropped
- // owners.
- void WriteDroppedOwnersIntoProfile(Serializer* s) {
- ASSERT(s->profile_writer() != nullptr);
-
- for (auto code : objects_) {
- ObjectPtr owner =
- WeakSerializationReference::Unwrap(code->untag()->owner_);
- if (s->CreateArtificialNodeIfNeeded(owner) || Code::IsDiscarded(code)) {
- AutoTraceObject(code);
- s->AttributePropertyRef(owner, DROPPED_NAME(owner_),
- /*permit_artificial_ref=*/true);
- }
- }
- }
-
private:
static const char* MakeDisambiguatedCodeName(Serializer* s, CodePtr c) {
if (s->profile_writer() == nullptr) {
@@ -2417,43 +2363,28 @@
void Trace(Serializer* s, ObjectPtr object) {
ASSERT(s->kind() == Snapshot::kFullAOT);
- WeakSerializationReferencePtr weak =
- WeakSerializationReference::RawCast(object);
- objects_.Add(weak);
+ objects_.Add(WeakSerializationReference::RawCast(object));
}
void RetraceEphemerons(Serializer* s) {
for (intptr_t i = 0; i < objects_.length(); i++) {
WeakSerializationReferencePtr weak = objects_[i];
- if (!s->HasRef(weak->untag()->target())) {
+ if (!s->IsReachable(weak->untag()->target())) {
s->Push(weak->untag()->replacement());
}
}
}
- intptr_t FinalizeWeak(Serializer* s) { return objects_.length(); }
+ intptr_t Count(Serializer* s) { return objects_.length(); }
void WriteAlloc(Serializer* s) {
- s->WriteCid(kWeakSerializationReferenceCid);
+ UNREACHABLE(); // No WSRs are serialized, and so this cluster is not added.
}
- void ForwardWeakRefs(Serializer* s) {
- Heap* heap = s->heap();
- for (intptr_t i = 0; i < objects_.length(); i++) {
- WeakSerializationReferencePtr weak = objects_[i];
-
- intptr_t id = heap->GetObjectId(weak->untag()->target());
- if (id == kUnreachableReference) {
- id = heap->GetObjectId(weak->untag()->replacement());
- ASSERT(id != kUnreachableReference);
- }
- ASSERT(IsAllocatedReference(id));
- heap->SetObjectId(weak, id);
- }
+ void WriteFill(Serializer* s) {
+ UNREACHABLE(); // No WSRs are serialized, and so this cluster is not added.
}
- void WriteFill(Serializer* s) {}
-
private:
GrowableArray<WeakSerializationReferencePtr> objects_;
};
@@ -2748,11 +2679,9 @@
for (intptr_t i = 0; i < count; i++) {
ObjectPtr object = objects_[i];
s->AssignRef(object);
- if (is_string_cluster) {
- s->TraceStartWritingObject(type_, object, String::RawCast(object));
- } else {
- s->TraceStartWritingObject(type_, object, nullptr);
- }
+ const StringPtr name =
+ is_string_cluster ? String::RawCast(object) : nullptr;
+ Serializer::WritingObjectScope scope(s, type_, object, name);
uint32_t offset = s->GetDataOffset(object);
s->TraceDataOffset(offset);
ASSERT(Utils::IsAligned(
@@ -2761,7 +2690,6 @@
s->WriteUnsigned((offset - running_offset) >>
compiler::target::ObjectAlignment::kObjectAlignmentLog2);
running_offset = offset;
- s->TraceEndWritingObject();
}
WriteCanonicalSetLayout(s);
}
@@ -5050,7 +4978,7 @@
void RetraceEphemerons(Serializer* s) {
for (intptr_t i = 0; i < objects_.length(); i++) {
WeakPropertyPtr property = objects_[i];
- if (s->HasRef(property->untag()->key())) {
+ if (s->IsReachable(property->untag()->key())) {
s->Push(property->untag()->value());
}
}
@@ -5650,18 +5578,18 @@
if (!should_write_symbols_ && s->profile_writer() != nullptr) {
// If writing V8 snapshot profile create an artifical node representing
// VM isolate symbol table.
- auto symbols_ref = s->AssignArtificialRef(symbols_.ptr());
- const V8SnapshotProfileWriter::ObjectId symbols_snapshot_id(
- V8SnapshotProfileWriter::kSnapshot, symbols_ref);
+ s->AssignArtificialRef(symbols_.ptr());
+ const auto& symbols_snapshot_id = s->GetProfileId(symbols_.ptr());
s->profile_writer()->AddRoot(symbols_snapshot_id, "vm_symbols");
- s->profile_writer()->SetObjectTypeAndName(symbols_snapshot_id, "Symbols",
- nullptr);
+ s->profile_writer()->SetObjectType(symbols_snapshot_id, "Symbols");
for (intptr_t i = 0; i < symbols_.Length(); i++) {
- const V8SnapshotProfileWriter::ObjectId code_id(
- V8SnapshotProfileWriter::kSnapshot, s->RefId(symbols_.At(i)));
s->profile_writer()->AttributeReferenceTo(
symbols_snapshot_id,
- {code_id, V8SnapshotProfileWriter::Reference::kElement, i});
+ {
+ V8SnapshotProfileWriter::Reference::kElement,
+ {.offset = i},
+ },
+ s->GetProfileId(symbols_.At(i)));
}
}
}
@@ -6156,16 +6084,16 @@
void Serializer::AddBaseObject(ObjectPtr base_object,
const char* type,
const char* name) {
- intptr_t ref = AssignRef(base_object);
+ AssignRef(base_object);
num_base_objects_++;
if ((profile_writer_ != nullptr) && (type != nullptr)) {
if (name == nullptr) {
name = "<base object>";
}
- profile_writer_->SetObjectTypeAndName(
- {V8SnapshotProfileWriter::kSnapshot, ref}, type, name);
- profile_writer_->AddRoot({V8SnapshotProfileWriter::kSnapshot, ref});
+ const auto& profile_id = GetProfileId(base_object);
+ profile_writer_->SetObjectTypeAndName(profile_id, type, name);
+ profile_writer_->AddRoot(profile_id);
}
}
@@ -6184,7 +6112,8 @@
}
intptr_t Serializer::AssignArtificialRef(ObjectPtr object) {
- ASSERT(object.IsHeapObject());
+ ASSERT(!object.IsHeapObject() || !object.IsInstructions());
+ ASSERT(heap_->GetObjectId(object) == kUnreachableReference);
const intptr_t ref = -(next_ref_index_++);
ASSERT(IsArtificialReference(ref));
heap_->SetObjectId(object, ref);
@@ -6192,120 +6121,248 @@
return ref;
}
-void Serializer::FlushBytesWrittenToRoot() {
-#if defined(DART_PRECOMPILER)
- if (profile_writer_ != nullptr) {
- ASSERT(object_currently_writing_.id_ == 0);
- // All bytes between objects are attributed into root node.
- profile_writer_->AttributeBytesTo(
- V8SnapshotProfileWriter::ArtificialRootId(),
- stream_->Position() - object_currently_writing_.stream_start_);
- object_currently_writing_.stream_start_ = stream_->Position();
- }
-#endif
+void Serializer::FlushProfile() {
+ if (profile_writer_ == nullptr) return;
+ const intptr_t bytes =
+ stream_->Position() - object_currently_writing_.last_stream_position_;
+ profile_writer_->AttributeBytesTo(object_currently_writing_.id_, bytes);
+ object_currently_writing_.last_stream_position_ = stream_->Position();
}
-void Serializer::TraceStartWritingObject(const char* type,
- ObjectPtr obj,
- StringPtr name) {
+V8SnapshotProfileWriter::ObjectId Serializer::GetProfileId(
+ ObjectPtr object) const {
+ // Instructions are handled separately.
+ ASSERT(!object->IsHeapObject() || !object->IsInstructions());
+ intptr_t heap_id = UnsafeRefId(object);
+ if (IsArtificialReference(heap_id)) {
+ return {V8SnapshotProfileWriter::kArtificial, -heap_id};
+ }
+ ASSERT(IsAllocatedReference(heap_id));
+ return {V8SnapshotProfileWriter::kSnapshot, heap_id};
+}
+
+void Serializer::AttributeReference(
+ ObjectPtr object,
+ const V8SnapshotProfileWriter::Reference& reference) {
if (profile_writer_ == nullptr) return;
+#if defined(DART_PRECOMPILER)
+ // Make artificial nodes for dropped targets in WSRs.
+ if (object->IsHeapObject() && object->IsWeakSerializationReference()) {
+ const auto& wsr = WeakSerializationReference::RawCast(object);
+ const auto& target = wsr->untag()->target();
+ if (!CreateArtificialNodeIfNeeded(wsr) && HasArtificialRef(target)) {
+ // The target has artificial information used for snapshot analysis and
+ // the replacement is part of the snapshot, so write information for both.
+ const auto& replacement = wsr->untag()->replacement();
+ profile_writer_->AttributeDroppedReferenceTo(
+ object_currently_writing_.id_, reference, GetProfileId(target),
+ GetProfileId(replacement));
+ return;
+ }
+ // Either the target of the WSR is strongly referenced or the WSR itself is
+ // unreachable, in which case it shares an artificial object ID with the
+ // target due to CreateArtificialNodeIfNeeded, so fall through.
+ ASSERT(HasRef(target) || HasArtificialRef(wsr));
+ } else if (object_currently_writing_.id_.first ==
+ V8SnapshotProfileWriter::kArtificial) {
+ // We may need to recur when writing members of artificial nodes in
+ // CreateArtificialNodeIfNeeded.
+ CreateArtificialNodeIfNeeded(object);
+ }
+#endif
+ profile_writer_->AttributeReferenceTo(object_currently_writing_.id_,
+ reference, GetProfileId(object));
+}
+
+Serializer::WritingObjectScope::WritingObjectScope(
+ Serializer* serializer,
+ const V8SnapshotProfileWriter::ObjectId& id,
+ ObjectPtr object)
+ : serializer_(serializer),
+ old_object_(serializer->object_currently_writing_.object_),
+ old_id_(serializer->object_currently_writing_.id_),
+ old_cid_(serializer->object_currently_writing_.cid_) {
+ if (serializer_->profile_writer_ == nullptr) return;
+ // The ID should correspond to one already added appropriately to the
+ // profile writer.
+ ASSERT(serializer_->profile_writer_->HasId(id));
+ serializer_->FlushProfile();
+ serializer_->object_currently_writing_.object_ = object;
+ serializer_->object_currently_writing_.id_ = id;
+ serializer_->object_currently_writing_.cid_ =
+ object == nullptr ? -1 : object->GetClassIdMayBeSmi();
+}
+
+Serializer::WritingObjectScope::~WritingObjectScope() {
+ if (serializer_->profile_writer_ == nullptr) return;
+ serializer_->FlushProfile();
+ serializer_->object_currently_writing_.object_ = old_object_;
+ serializer_->object_currently_writing_.id_ = old_id_;
+ serializer_->object_currently_writing_.cid_ = old_cid_;
+}
+
+V8SnapshotProfileWriter::ObjectId Serializer::WritingObjectScope::ReserveId(
+ Serializer* s,
+ const char* type,
+ ObjectPtr obj,
+ StringPtr name) {
const char* name_str = nullptr;
if (name != nullptr) {
- REUSABLE_STRING_HANDLESCOPE(thread());
+ REUSABLE_STRING_HANDLESCOPE(s->thread());
String& str = reused_string_handle.Handle();
str = name;
name_str = str.ToCString();
}
-
- TraceStartWritingObject(type, obj, name_str);
+ return ReserveId(s, type, obj, name_str);
}
-void Serializer::TraceStartWritingObject(const char* type,
- ObjectPtr obj,
- const char* name) {
- if (profile_writer_ == nullptr) return;
-
- intptr_t id = heap_->GetObjectId(obj);
- intptr_t cid = obj->GetClassIdMayBeSmi();
- if (IsArtificialReference(id)) {
- id = -id;
+V8SnapshotProfileWriter::ObjectId Serializer::WritingObjectScope::ReserveId(
+ Serializer* s,
+ const char* type,
+ ObjectPtr obj,
+ const char* name) {
+ if (s->profile_writer_ == nullptr) {
+ return V8SnapshotProfileWriter::kArtificialRootId;
}
- ASSERT(IsAllocatedReference(id));
-
- FlushBytesWrittenToRoot();
- object_currently_writing_.object_ = obj;
- object_currently_writing_.id_ = id;
- object_currently_writing_.stream_start_ = stream_->Position();
- object_currently_writing_.cid_ = cid;
- profile_writer_->SetObjectTypeAndName(
- {V8SnapshotProfileWriter::kSnapshot, id}, type, name);
-}
-
-void Serializer::TraceEndWritingObject() {
- if (profile_writer_ != nullptr) {
- ASSERT(IsAllocatedReference(object_currently_writing_.id_));
- profile_writer_->AttributeBytesTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
- stream_->Position() - object_currently_writing_.stream_start_);
- object_currently_writing_ = ProfilingObject();
- object_currently_writing_.stream_start_ = stream_->Position();
+ if (name == nullptr) {
+ // Handle some cases where there are obvious names to assign.
+ switch (obj->GetClassIdMayBeSmi()) {
+ case kSmiCid: {
+ name = OS::SCreate(s->zone(), "%" Pd "", Smi::Value(Smi::RawCast(obj)));
+ break;
+ }
+ case kMintCid: {
+ name = OS::SCreate(s->zone(), "%" Pd64 "",
+ Mint::RawCast(obj)->untag()->value_);
+ break;
+ }
+ case kOneByteStringCid:
+ case kTwoByteStringCid: {
+ REUSABLE_STRING_HANDLESCOPE(s->thread());
+ String& str = reused_string_handle.Handle();
+ str = String::RawCast(obj);
+ name = str.ToCString();
+ break;
+ }
+ }
}
+ const auto& obj_id = s->GetProfileId(obj);
+ s->profile_writer_->SetObjectTypeAndName(obj_id, type, name);
+ return obj_id;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
bool Serializer::CreateArtificialNodeIfNeeded(ObjectPtr obj) {
ASSERT(profile_writer() != nullptr);
- if (obj->GetClassId() == kWeakSerializationReferenceCid) {
- auto wsr = static_cast<WeakSerializationReferencePtr>(obj);
- return CreateArtificialNodeIfNeeded(wsr->untag()->target());
- }
-
- intptr_t id = heap_->GetObjectId(obj);
- if (IsAllocatedReference(id)) {
- return false;
- }
+ // UnsafeRefId will do lazy reference allocation for WSRs.
+ intptr_t id = UnsafeRefId(obj);
+ ASSERT(id != kUnallocatedReference);
if (IsArtificialReference(id)) {
return true;
}
+ if (obj->IsHeapObject() && obj->IsWeakSerializationReference()) {
+ // The object ID for the WSR may need lazy resolution.
+ if (id == kUnallocatedReference) {
+ id = UnsafeRefId(obj);
+ }
+ ASSERT(id != kUnallocatedReference);
+ // Create an artificial node for an unreachable target at this point,
+ // whether or not the WSR itself is reachable.
+ const auto& target =
+ WeakSerializationReference::RawCast(obj)->untag()->target();
+ CreateArtificialNodeIfNeeded(target);
+ if (id == kUnreachableReference) {
+ ASSERT(HasArtificialRef(target));
+ // We can safely set the WSR's object ID to the target's artificial one,
+ // as that won't make it look reachable.
+ heap_->SetObjectId(obj, heap_->GetObjectId(target));
+ return true;
+ }
+ // The WSR is reachable, so continue to the IsAllocatedReference behavior.
+ }
+ if (IsAllocatedReference(id)) {
+ return false;
+ }
ASSERT_EQUAL(id, kUnreachableReference);
id = AssignArtificialRef(obj);
+ auto property = [](const char* name) -> V8SnapshotProfileWriter::Reference {
+ return {V8SnapshotProfileWriter::Reference::kProperty, {.name = name}};
+ };
+ auto element = [](intptr_t index) -> V8SnapshotProfileWriter::Reference {
+ return {V8SnapshotProfileWriter::Reference::kElement, {.offset = index}};
+ };
+
const char* type = nullptr;
StringPtr name_string = nullptr;
const char* name = nullptr;
- GrowableArray<std::pair<ObjectPtr, const char*>> links;
- switch (obj->GetClassId()) {
+ GrowableArray<std::pair<ObjectPtr, V8SnapshotProfileWriter::Reference>> links;
+ switch (obj->GetClassIdMayBeSmi()) {
+ // For profiling static call target tables in AOT mode.
+ case kSmiCid: {
+ type = "Smi";
+ break;
+ }
+ // For profiling per-code object pools in bare instructions mode.
+ case kObjectPoolCid: {
+ type = "ObjectPool";
+ auto const pool = ObjectPool::RawCast(obj);
+ for (intptr_t i = 0; i < pool->untag()->length_; i++) {
+ uint8_t bits = pool->untag()->entry_bits()[i];
+ if (ObjectPool::TypeBits::decode(bits) ==
+ ObjectPool::EntryType::kTaggedObject) {
+ auto const elem = pool->untag()->data()[i].raw_obj_;
+ // Elements should be reachable from the global object pool.
+ ASSERT(HasRef(elem));
+ links.Add({elem, element(i)});
+ }
+ }
+ break;
+ }
+ // For profiling static call target tables in AOT mode.
+ case kArrayCid: {
+ type = "Array";
+ auto const array = Array::RawCast(obj);
+ for (intptr_t i = 0, n = Smi::Value(array->untag()->length()); i < n;
+ i++) {
+ ObjectPtr elem = array->untag()->data()[i];
+ links.Add({elem, element(i)});
+ }
+ break;
+ }
case kFunctionCid: {
FunctionPtr func = static_cast<FunctionPtr>(obj);
type = "Function";
name = FunctionSerializationCluster::MakeDisambiguatedFunctionName(this,
func);
- links.Add({func->untag()->owner(), "owner_"});
+ links.Add({func->untag()->owner(), property("owner_")});
ObjectPtr data = func->untag()->data();
if (data->GetClassId() == kClosureDataCid) {
- links.Add({func->untag()->data(), "data_"});
+ links.Add({func->untag()->data(), property("data_")});
}
break;
}
case kClosureDataCid: {
auto data = static_cast<ClosureDataPtr>(obj);
type = "ClosureData";
- links.Add({data->untag()->parent_function(), "parent_function_"});
+ links.Add(
+ {data->untag()->parent_function(), property("parent_function_")});
break;
}
case kClassCid: {
ClassPtr cls = static_cast<ClassPtr>(obj);
type = "Class";
name_string = cls->untag()->name();
- links.Add({cls->untag()->library(), "library_"});
+ links.Add({cls->untag()->library(), property("library_")});
break;
}
case kPatchClassCid: {
PatchClassPtr patch_cls = static_cast<PatchClassPtr>(obj);
type = "PatchClass";
- links.Add({patch_cls->untag()->patched_class(), "patched_class_"});
+ links.Add(
+ {patch_cls->untag()->patched_class(), property("patched_class_")});
break;
}
case kLibraryCid: {
@@ -6325,23 +6382,57 @@
name = str.ToCString();
}
- // CreateArtificialNodeIfNeeded might call TraceStartWritingObject
- // and these calls don't nest, so we need to call this outside
- // of the tracing scope created below.
+ Serializer::WritingObjectScope scope(this, type, obj, name);
for (const auto& link : links) {
- CreateArtificialNodeIfNeeded(link.first);
+ AttributeReference(link.first, link.second);
}
-
- TraceStartWritingObject(type, obj, name);
- for (const auto& link : links) {
- AttributePropertyRef(link.first, link.second,
- /*permit_artificial_ref=*/true);
- }
- TraceEndWritingObject();
return true;
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
+intptr_t Serializer::RefId(ObjectPtr object) const {
+ auto const id = UnsafeRefId(object);
+ if (IsAllocatedReference(id)) {
+ return id;
+ }
+ ASSERT(id == kUnreachableReference || IsArtificialReference(id));
+ REUSABLE_OBJECT_HANDLESCOPE(thread());
+ auto& handle = thread()->ObjectHandle();
+ handle = object;
+ FATAL("Reference to unreachable object %s", handle.ToCString());
+}
+
+intptr_t Serializer::UnsafeRefId(ObjectPtr object) const {
+ // The object id weak table holds image offsets for Instructions instead
+ // of ref indices.
+ ASSERT(!object->IsHeapObject() || !object->IsInstructions());
+ if (!Snapshot::IncludesCode(kind_) &&
+ object->GetClassIdMayBeSmi() == kCodeCid) {
+ return RefId(Object::null());
+ }
+ auto id = heap_->GetObjectId(object);
+ if (id != kUnallocatedReference) {
+ return id;
+ }
+ // This is the only case where we may still see unallocated references after
+ // WriteAlloc is finished.
+ if (object->IsWeakSerializationReference()) {
+ // Lazily set the object ID of the WSR to the object which will replace
+ // it in the snapshot.
+ auto const wsr = static_cast<WeakSerializationReferencePtr>(object);
+ // Either the target or the replacement must be allocated, since the
+ // WSR is reachable.
+ id = HasRef(wsr->untag()->target()) ? RefId(wsr->untag()->target())
+ : RefId(wsr->untag()->replacement());
+ heap_->SetObjectId(wsr, id);
+ return id;
+ }
+ REUSABLE_OBJECT_HANDLESCOPE(thread());
+ auto& handle = thread()->ObjectHandle();
+ handle = object;
+ FATAL("Reference for object %s is unallocated", handle.ToCString());
+}
+
const char* Serializer::ReadOnlyObjectType(intptr_t cid) {
switch (cid) {
case kPcDescriptorsCid:
@@ -6606,15 +6697,17 @@
const intptr_t offset = image_writer_->GetTextOffsetFor(instr, code);
#if defined(DART_PRECOMPILER)
if (profile_writer_ != nullptr) {
- ASSERT(IsAllocatedReference(object_currently_writing_.id_));
+ ASSERT(IsAllocatedReference(object_currently_writing_.id_.second));
const auto offset_space = vm_ ? V8SnapshotProfileWriter::kVmText
: V8SnapshotProfileWriter::kIsolateText;
const V8SnapshotProfileWriter::ObjectId to_object(offset_space, offset);
- const V8SnapshotProfileWriter::ObjectId from_object(
- V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_);
profile_writer_->AttributeReferenceTo(
- from_object, {to_object, V8SnapshotProfileWriter::Reference::kProperty,
- profile_writer_->EnsureString("<instructions>")});
+ object_currently_writing_.id_,
+ {
+ V8SnapshotProfileWriter::Reference::kProperty,
+ {.name = "<instructions>"},
+ },
+ to_object);
}
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
@@ -6646,17 +6739,19 @@
void Serializer::TraceDataOffset(uint32_t offset) {
if (profile_writer_ != nullptr) {
// ROData cannot be roots.
- ASSERT(IsAllocatedReference(object_currently_writing_.id_));
+ ASSERT(IsAllocatedReference(object_currently_writing_.id_.second));
auto offset_space = vm_ ? V8SnapshotProfileWriter::kVmData
: V8SnapshotProfileWriter::kIsolateData;
- V8SnapshotProfileWriter::ObjectId from_object = {
- V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_};
V8SnapshotProfileWriter::ObjectId to_object = {offset_space, offset};
// TODO(sjindel): Give this edge a more appropriate type than element
// (internal, maybe?).
profile_writer_->AttributeReferenceTo(
- from_object,
- {to_object, V8SnapshotProfileWriter::Reference::kElement, 0});
+ object_currently_writing_.id_,
+ {
+ V8SnapshotProfileWriter::Reference::kElement,
+ {.offset = 0},
+ },
+ to_object);
}
}
@@ -6797,13 +6892,37 @@
}
}
+#define CID_CLUSTER(Type) \
+ reinterpret_cast<Type##SerializationCluster*>(clusters_by_cid_[k##Type##Cid])
+
ZoneGrowableArray<Object*>* Serializer::Serialize(SerializationRoots* roots) {
+ // While object_currently_writing_ is initialized to the artificial root, we
+ // set up a scope to ensure proper flushing to the profile.
+ Serializer::WritingObjectScope scope(
+ this, V8SnapshotProfileWriter::kArtificialRootId);
roots->AddBaseObjects(this);
NoSafepointScope no_safepoint;
roots->PushRoots(this);
+ // Resolving WeakSerializationReferences and WeakProperties may cause new
+ // objects to be pushed on the stack, and handling the changes to the stack
+ // may cause the targets of WeakSerializationReferences and keys of
+ // WeakProperties to become reachable, so we do this as a fixed point
+ // computation. Note that reachability is computed monotonically (an object
+ // can change from not reachable to reachable, but never the reverse), which
+ // is technically a conservative approximation for WSRs, but doing a strict
+ // analysis that allows non-motonoic reachability may not halt.
+ //
+ // To see this, take a WSR whose replacement causes the target of another WSR
+ // to become reachable, which then causes the target of the first WSR to
+ // become reachable, but the only way to reach the target is through the
+ // target of the second WSR, which was only reachable via the replacement
+ // the first.
+ //
+ // In practice, this case doesn't come up as replacements tend to be either
+ // null, smis, or singleton objects that do not contain WSRs currently.
while (stack_.length() > 0) {
// Strong references.
while (stack_.length() > 0) {
@@ -6812,19 +6931,23 @@
// Ephemeron references.
#if defined(DART_PRECOMPILER)
- if (auto const cluster =
- reinterpret_cast<WeakSerializationReferenceSerializationCluster*>(
- clusters_by_cid_[kWeakSerializationReferenceCid])) {
+ if (auto const cluster = CID_CLUSTER(WeakSerializationReference)) {
cluster->RetraceEphemerons(this);
}
#endif
- if (auto const cluster =
- reinterpret_cast<WeakPropertySerializationCluster*>(
- clusters_by_cid_[kWeakPropertyCid])) {
+ if (auto const cluster = CID_CLUSTER(WeakProperty)) {
cluster->RetraceEphemerons(this);
}
}
+#if defined(DART_PRECOMPILER)
+ if (auto const cluster = CID_CLUSTER(WeakSerializationReference)) {
+ // Now that we have computed the reachability fixpoint, we remove the
+ // count of now-reachable WSRs as they are not actually serialized.
+ num_written_objects_ -= cluster->Count(this);
+ }
+#endif
+
GrowableArray<SerializationCluster*> canonical_clusters;
// The order that PostLoad runs matters for some classes because of
// assumptions during canonicalization of some classes about what is already
@@ -6857,19 +6980,16 @@
clusters.Add(clusters_by_cid_[kCodeCid]);
}
for (intptr_t cid = 0; cid < num_cids_; cid++) {
- if (clusters_by_cid_[cid] != nullptr && cid != kCodeCid) {
+ // We don't actually have any WSR objects, references to them are replaced
+ // either with the target or replacement.
+ if (cid == kWeakSerializationReferenceCid) continue;
+ // The code serialization cluster is already handled above.
+ if (cid == kCodeCid) continue;
+ if (clusters_by_cid_[cid] != nullptr) {
clusters.Add(clusters_by_cid_[cid]);
}
}
-#if defined(DART_PRECOMPILER)
- if (auto const cluster =
- reinterpret_cast<WeakSerializationReferenceSerializationCluster*>(
- clusters_by_cid_[kWeakSerializationReferenceCid])) {
- num_written_objects_ -= cluster->FinalizeWeak(this);
- }
-#endif
-
instructions_table_len_ = PrepareInstructions();
intptr_t num_objects = num_base_objects_ + num_written_objects_;
@@ -6913,26 +7033,6 @@
// And recorded them all in [objects_].
ASSERT(objects_->length() == num_objects);
-#if defined(DART_PRECOMPILER)
- if (auto cluster =
- reinterpret_cast<WeakSerializationReferenceSerializationCluster*>(
- clusters_by_cid_[kWeakSerializationReferenceCid])) {
- cluster->ForwardWeakRefs(this);
- }
-
- // When writing snapshot profile, we want to retain some of the program
- // structure information (e.g. information about libraries, classes and
- // functions - even if it was dropped when writing snapshot itself).
- if (FLAG_write_v8_snapshot_profile_to != nullptr) {
- static_cast<CodeSerializationCluster*>(clusters_by_cid_[kCodeCid])
- ->WriteDroppedOwnersIntoProfile(this);
- if (auto const cluster = static_cast<ClosureDataSerializationCluster*>(
- clusters_by_cid_[kClosureDataCid])) {
- cluster->WriteDroppedParentFunctionsIntoProfile(this);
- }
- }
-#endif
-
for (SerializationCluster* cluster : canonical_clusters) {
cluster->WriteAndMeasureFill(this);
#if defined(DEBUG)
@@ -6952,9 +7052,6 @@
Write<int32_t>(kSectionMarker);
#endif
- FlushBytesWrittenToRoot();
- object_currently_writing_.stream_start_ = stream_->Position();
-
PrintSnapshotSizes();
heap()->ResetObjectIdTable();
@@ -6989,6 +7086,14 @@
#if defined(DART_PRECOMPILER)
if (kind() != Snapshot::kFullAOT) return;
+ AssignArtificialRef(entries.ptr());
+ const auto& dispatch_table_snapshot_id = GetProfileId(entries.ptr());
+ if (profile_writer_ != nullptr) {
+ profile_writer_->AddRoot(dispatch_table_snapshot_id, "dispatch_table");
+ profile_writer_->SetObjectType(dispatch_table_snapshot_id, "DispatchTable");
+ }
+ WritingObjectScope scope(this, dispatch_table_snapshot_id);
+
const intptr_t bytes_before = bytes_written();
const intptr_t table_length = entries.IsNull() ? 0 : entries.Length();
@@ -6999,8 +7104,7 @@
return;
}
- auto const code_cluster =
- reinterpret_cast<CodeSerializationCluster*>(clusters_by_cid_[kCodeCid]);
+ auto const code_cluster = CID_CLUSTER(Code);
ASSERT(code_cluster != nullptr);
// Reference IDs in a cluster are allocated sequentially, so we can use the
// first code object's reference ID to calculate the cluster index.
@@ -7079,29 +7183,19 @@
}
dispatch_table_size_ = bytes_written() - bytes_before;
- object_currently_writing_.stream_start_ = stream_->Position();
- // If any bytes were written for the dispatch table, add it to the profile.
- if (dispatch_table_size_ > 0 && profile_writer_ != nullptr) {
- // Grab an unused ref index for a unique object id for the dispatch table.
- const auto dispatch_table_id = next_ref_index_++;
- const V8SnapshotProfileWriter::ObjectId dispatch_table_snapshot_id(
- V8SnapshotProfileWriter::kSnapshot, dispatch_table_id);
- profile_writer_->AddRoot(dispatch_table_snapshot_id, "dispatch_table");
- profile_writer_->SetObjectTypeAndName(dispatch_table_snapshot_id,
- "DispatchTable", nullptr);
- profile_writer_->AttributeBytesTo(dispatch_table_snapshot_id,
- dispatch_table_size_);
-
- if (!entries.IsNull()) {
- for (intptr_t i = 0; i < entries.Length(); i++) {
- auto const code = Code::RawCast(entries.At(i));
- if (code == Code::null()) continue;
- const V8SnapshotProfileWriter::ObjectId code_id(
- V8SnapshotProfileWriter::kSnapshot, RefId(code));
- profile_writer_->AttributeReferenceTo(
- dispatch_table_snapshot_id,
- {code_id, V8SnapshotProfileWriter::Reference::kElement, i});
- }
+ // If any bytes were written for the dispatch table, add the elements of
+ // the dispatch table in the profile.
+ if (profile_writer_ != nullptr && !entries.IsNull()) {
+ for (intptr_t i = 0; i < entries.Length(); i++) {
+ auto const code = Code::RawCast(entries.At(i));
+ if (code == Code::null()) continue;
+ profile_writer_->AttributeReferenceTo(
+ dispatch_table_snapshot_id,
+ {
+ V8SnapshotProfileWriter::Reference::kElement,
+ {.offset = i},
+ },
+ GetProfileId(code));
}
}
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index cbb34de..ebaa3a4 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -251,12 +251,52 @@
intptr_t bytes_written() { return stream_->bytes_written(); }
intptr_t bytes_heap_allocated() { return bytes_heap_allocated_; }
- void FlushBytesWrittenToRoot();
- void TraceStartWritingObject(const char* type, ObjectPtr obj, StringPtr name);
- void TraceStartWritingObject(const char* type,
- ObjectPtr obj,
- const char* name);
- void TraceEndWritingObject();
+ class WritingObjectScope : ValueObject {
+ public:
+ WritingObjectScope(Serializer* serializer,
+ const char* type,
+ ObjectPtr object,
+ StringPtr name)
+ : WritingObjectScope(serializer,
+ ReserveId(serializer, type, object, name),
+ object) {}
+
+ WritingObjectScope(Serializer* serializer,
+ const char* type,
+ ObjectPtr object,
+ const char* name)
+ : WritingObjectScope(serializer,
+ ReserveId(serializer, type, object, name),
+ object) {}
+
+ WritingObjectScope(Serializer* serializer,
+ const V8SnapshotProfileWriter::ObjectId& id,
+ ObjectPtr object = nullptr);
+
+ WritingObjectScope(Serializer* serializer, ObjectPtr object)
+ : WritingObjectScope(serializer,
+ serializer->GetProfileId(object),
+ object) {}
+
+ ~WritingObjectScope();
+
+ private:
+ static V8SnapshotProfileWriter::ObjectId ReserveId(Serializer* serializer,
+ const char* type,
+ ObjectPtr object,
+ StringPtr name);
+
+ static V8SnapshotProfileWriter::ObjectId ReserveId(Serializer* serializer,
+ const char* type,
+ ObjectPtr object,
+ const char* name);
+
+ private:
+ Serializer* const serializer_;
+ const ObjectPtr old_object_;
+ const V8SnapshotProfileWriter::ObjectId old_id_;
+ const classid_t old_cid_;
+ };
// Writes raw data to the stream (basic type).
// sizeof(T) must be in {1,2,4,8}.
@@ -276,72 +316,50 @@
}
void Align(intptr_t alignment) { stream_->Align(alignment); }
+ V8SnapshotProfileWriter::ObjectId GetProfileId(ObjectPtr object) const;
+
void WriteRootRef(ObjectPtr object, const char* name = nullptr) {
intptr_t id = RefId(object);
WriteUnsigned(id);
if (profile_writer_ != nullptr) {
- profile_writer_->AddRoot({V8SnapshotProfileWriter::kSnapshot, id}, name);
+ profile_writer_->AddRoot(GetProfileId(object), name);
}
}
+ // Record a reference from the currently written object to the given object
+ // and return reference id for the given object.
+ void AttributeReference(ObjectPtr object,
+ const V8SnapshotProfileWriter::Reference& reference);
+
+ void AttributeElementRef(ObjectPtr object, intptr_t index) {
+ AttributeReference(object, {V8SnapshotProfileWriter::Reference::kElement,
+ {.offset = index}});
+ }
+
void WriteElementRef(ObjectPtr object, intptr_t index) {
- WriteUnsigned(AttributeElementRef(object, index));
+ AttributeElementRef(object, index);
+ WriteUnsigned(RefId(object));
}
- // Record a reference from the currently written object to the given object
- // and return reference id for the given object.
- intptr_t AttributeElementRef(ObjectPtr object,
- intptr_t index,
- bool permit_artificial_ref = false) {
- intptr_t id = RefId(object, permit_artificial_ref);
- if (profile_writer_ != nullptr) {
- profile_writer_->AttributeReferenceTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
- {{V8SnapshotProfileWriter::kSnapshot, id},
- V8SnapshotProfileWriter::Reference::kElement,
- index});
- }
- return id;
+ void AttributePropertyRef(ObjectPtr object, const char* property) {
+ AttributeReference(object, {V8SnapshotProfileWriter::Reference::kProperty,
+ {.name = property}});
}
void WritePropertyRef(ObjectPtr object, const char* property) {
- WriteUnsigned(AttributePropertyRef(object, property));
- }
-
- // Record a reference from the currently written object to the given object
- // and return reference id for the given object.
- intptr_t AttributePropertyRef(ObjectPtr object,
- const char* property,
- bool permit_artificial_ref = false) {
- intptr_t id = RefId(object, permit_artificial_ref);
- if (profile_writer_ != nullptr) {
- profile_writer_->AttributeReferenceTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
- {{V8SnapshotProfileWriter::kSnapshot, id},
- V8SnapshotProfileWriter::Reference::kProperty,
- profile_writer_->EnsureString(property)});
- }
- return id;
+ AttributePropertyRef(object, property);
+ WriteUnsigned(RefId(object));
}
void WriteOffsetRef(ObjectPtr object, intptr_t offset) {
intptr_t id = RefId(object);
WriteUnsigned(id);
if (profile_writer_ != nullptr) {
- const char* property = offsets_table_->FieldNameForOffset(
- object_currently_writing_.cid_, offset);
- if (property != nullptr) {
- profile_writer_->AttributeReferenceTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
- {{V8SnapshotProfileWriter::kSnapshot, id},
- V8SnapshotProfileWriter::Reference::kProperty,
- profile_writer_->EnsureString(property)});
+ if (auto const property = offsets_table_->FieldNameForOffset(
+ object_currently_writing_.cid_, offset)) {
+ AttributePropertyRef(object, property);
} else {
- profile_writer_->AttributeReferenceTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
- {{V8SnapshotProfileWriter::kSnapshot, id},
- V8SnapshotProfileWriter::Reference::kElement,
- offset});
+ AttributeElementRef(object, offset);
}
}
}
@@ -417,26 +435,29 @@
// Returns the reference ID for the object. Fails for objects that have not
// been allocated a reference ID yet, so should be used only after all
// WriteAlloc calls.
- intptr_t RefId(ObjectPtr object, bool permit_artificial_ref = false) {
- // The object id weak table holds image offsets for Instructions instead
- // of ref indices.
- ASSERT(!object->IsHeapObject() || !object->IsInstructions());
- auto const id = heap_->GetObjectId(object);
- if (permit_artificial_ref && IsArtificialReference(id)) {
- return -id;
- }
- ASSERT(!IsArtificialReference(id));
- if (IsAllocatedReference(id)) {
- return id;
- }
- if (object->IsCode() && !Snapshot::IncludesCode(kind_)) {
- return RefId(Object::null());
- }
- FATAL("Missing ref");
- }
+ intptr_t RefId(ObjectPtr object) const;
+ // Same as RefId, but allows artificial and unreachable references. Still
+ // fails for unallocated references.
+ intptr_t UnsafeRefId(ObjectPtr object) const;
+
+ // Whether the object is reachable.
+ bool IsReachable(ObjectPtr object) const {
+ return IsReachableReference(heap_->GetObjectId(object));
+ }
+ // Whether the object has an allocated reference.
bool HasRef(ObjectPtr object) const {
- return heap_->GetObjectId(object) != kUnreachableReference;
+ return IsAllocatedReference(heap_->GetObjectId(object));
+ }
+ // Whether the object only appears in the V8 snapshot profile.
+ bool HasArtificialRef(ObjectPtr object) const {
+ return IsArtificialReference(heap_->GetObjectId(object));
+ }
+ // Whether a node for the object already has been added to the V8 snapshot
+ // profile.
+ bool HasProfileNode(ObjectPtr object) const {
+ ASSERT(profile_writer_ != nullptr);
+ return profile_writer_->HasId(GetProfileId(object));
}
bool IsWritten(ObjectPtr object) const {
return heap_->GetObjectId(object) > num_base_objects_;
@@ -444,6 +465,7 @@
private:
const char* ReadOnlyObjectType(intptr_t cid);
+ void FlushProfile();
Heap* heap_;
Zone* zone_;
@@ -471,8 +493,11 @@
V8SnapshotProfileWriter* profile_writer_ = nullptr;
struct ProfilingObject {
ObjectPtr object_ = nullptr;
- intptr_t id_ = 0;
- intptr_t stream_start_ = 0;
+ // Unless within a WritingObjectScope, any bytes written are attributed to
+ // the artificial root.
+ V8SnapshotProfileWriter::ObjectId id_ =
+ V8SnapshotProfileWriter::kArtificialRootId;
+ intptr_t last_stream_position_ = 0;
intptr_t cid_ = -1;
} object_currently_writing_;
OffsetsTable* offsets_table_ = nullptr;
@@ -494,10 +519,10 @@
};
#define AutoTraceObject(obj) \
- SerializerWritingObjectScope scope_##__COUNTER__(s, name(), obj, nullptr)
+ Serializer::WritingObjectScope scope_##__COUNTER__(s, name(), obj, nullptr)
#define AutoTraceObjectName(obj, str) \
- SerializerWritingObjectScope scope_##__COUNTER__(s, name(), obj, str)
+ Serializer::WritingObjectScope scope_##__COUNTER__(s, name(), obj, str)
#define WriteFieldValue(field, value) s->WritePropertyRef(value, #field);
@@ -509,29 +534,6 @@
#define WriteCompressedField(obj, name) \
s->WritePropertyRef(obj->untag()->name(), #name "_")
-class SerializerWritingObjectScope {
- public:
- SerializerWritingObjectScope(Serializer* serializer,
- const char* type,
- ObjectPtr object,
- StringPtr name)
- : serializer_(serializer) {
- serializer_->TraceStartWritingObject(type, object, name);
- }
-
- SerializerWritingObjectScope(Serializer* serializer,
- const char* type,
- ObjectPtr object,
- const char* name)
- : serializer_(serializer) {
- serializer_->TraceStartWritingObject(type, object, name);
- }
-
- ~SerializerWritingObjectScope() { serializer_->TraceEndWritingObject(); }
-
- private:
- Serializer* serializer_;
-};
// This class can be used to read version and features from a snapshot before
// the VM has been initialized.
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 8dacf84..e638bf2 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -255,7 +255,7 @@
static Value ValueOf(Pair kv) { return kv.value; }
- static inline intptr_t Hashcode(Key key) {
+ static inline uword Hash(Key key) {
if (key->IsFunction()) {
return Function::Cast(*key).Hash();
}
diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h
index 2af72af..315c712 100644
--- a/runtime/vm/compiler/aot/precompiler.h
+++ b/runtime/vm/compiler/aot/precompiler.h
@@ -42,7 +42,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key; }
+ static inline uword Hash(Key key) { return key; }
static inline bool IsKeyEqual(Pair pair, Key key) { return pair == key; }
};
@@ -60,7 +60,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->ptr() == key->ptr();
@@ -92,7 +92,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) {
+ static inline uword Hash(Key key) {
const TokenPosition token_pos = key->token_pos();
if (token_pos.IsReal()) {
return token_pos.Hash();
@@ -118,7 +118,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->token_pos().Hash(); }
+ static inline uword Hash(Key key) { return key->token_pos().Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->ptr() == key->ptr();
@@ -138,7 +138,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->ptr() == key->ptr();
@@ -158,7 +158,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->ptr() == key->ptr();
@@ -178,7 +178,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->ptr() == key->ptr();
@@ -198,7 +198,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->ptr() == key->ptr();
@@ -218,7 +218,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->GetClassId(); }
+ static inline uword Hash(Key key) { return key->GetClassId(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->ptr() == key->ptr();
diff --git a/runtime/vm/compiler/aot/precompiler_tracer.h b/runtime/vm/compiler/aot/precompiler_tracer.h
index 70a9359..324e82c 100644
--- a/runtime/vm/compiler/aot/precompiler_tracer.h
+++ b/runtime/vm/compiler/aot/precompiler_tracer.h
@@ -54,7 +54,7 @@
struct CString {
const char* str;
const intptr_t length;
- intptr_t hash;
+ uword hash;
};
struct StringTableTraits {
diff --git a/runtime/vm/compiler/assembler/assembler_base.cc b/runtime/vm/compiler/assembler/assembler_base.cc
index 7034652..9d50186 100644
--- a/runtime/vm/compiler/assembler/assembler_base.cc
+++ b/runtime/vm/compiler/assembler/assembler_base.cc
@@ -259,7 +259,7 @@
Breakpoint();
}
-intptr_t ObjIndexPair::Hashcode(Key key) {
+uword ObjIndexPair::Hash(Key key) {
if (key.type() != ObjectPoolBuilderEntry::kTaggedObject) {
return key.raw_value_;
}
diff --git a/runtime/vm/compiler/assembler/object_pool_builder.h b/runtime/vm/compiler/assembler/object_pool_builder.h
index 2bc2add..db77eb8 100644
--- a/runtime/vm/compiler/assembler/object_pool_builder.h
+++ b/runtime/vm/compiler/assembler/object_pool_builder.h
@@ -95,7 +95,7 @@
static Value ValueOf(Pair kv) { return kv.value_; }
- static intptr_t Hashcode(Key key);
+ static uword Hash(Key key);
static inline bool IsKeyEqual(Pair kv, Key key) {
if (kv.key_.entry_bits_ != key.entry_bits_) return false;
diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h
index 09e3caf..b028847 100644
--- a/runtime/vm/compiler/backend/flow_graph.h
+++ b/runtime/vm/compiler/backend/flow_graph.h
@@ -58,7 +58,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) {
+ static inline uword Hash(Key key) {
if (key.IsSmi()) {
return Smi::Cast(key).Value();
}
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 449eeaf..74532bb 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -649,14 +649,13 @@
return result;
}
-intptr_t Instruction::Hashcode() const {
- intptr_t result = tag();
+uword Instruction::Hash() const {
+ uword result = tag();
for (intptr_t i = 0; i < InputCount(); ++i) {
Value* value = InputAt(i);
- intptr_t j = value->definition()->ssa_temp_index();
- result = result * 31 + j;
+ result = CombineHashes(result, value->definition()->ssa_temp_index());
}
- return result;
+ return FinalizeHash(result, kBitsPerInt32 - 1);
}
bool Instruction::Equals(Instruction* other) const {
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 3516f74..c4ff302 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -1077,7 +1077,7 @@
virtual bool has_inlining_id() const { return inlining_id_ >= 0; }
// Returns a hash code for use with hash maps.
- virtual intptr_t Hashcode() const;
+ virtual uword Hash() const;
// Compares two instructions. Returns true, iff:
// 1. They have the same tag.
@@ -2486,7 +2486,7 @@
return CompilerState::Current().is_aot() ? kNotSpeculative : kGuardInputs;
}
- virtual intptr_t Hashcode() const {
+ virtual uword Hash() const {
UNREACHABLE();
return 0;
}
@@ -2587,7 +2587,7 @@
virtual bool HasUnknownSideEffects() const { return false; }
- virtual intptr_t Hashcode() const {
+ virtual uword Hash() const {
UNREACHABLE();
return 0;
}
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index fb3d09c..6edc0f3 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -428,9 +428,10 @@
}
}
- intptr_t Hashcode() const {
- return (flags_ * 63 + reinterpret_cast<intptr_t>(instance_)) * 31 +
- FieldHashcode();
+ uword Hash() const {
+ return FinalizeHash(
+ CombineHashes(flags_, reinterpret_cast<uword>(instance_)),
+ kBitsPerInt32 - 1);
}
bool Equals(const Place* other) const {
@@ -460,7 +461,7 @@
: (raw_selector_ == other->raw_selector_);
}
- intptr_t FieldHashcode() const {
+ uword FieldHash() const {
return (kind() == kStaticField)
? String::Handle(Field::Handle(static_field().Original()).name())
.Hash()
diff --git a/runtime/vm/compiler/backend/slot.cc b/runtime/vm/compiler/backend/slot.cc
index 75fccf8..d0c4d9e 100644
--- a/runtime/vm/compiler/backend/slot.cc
+++ b/runtime/vm/compiler/backend/slot.cc
@@ -408,8 +408,8 @@
}
}
-intptr_t Slot::Hashcode() const {
- intptr_t result = (static_cast<int8_t>(kind_) * 63 + offset_in_bytes_) * 31;
+uword Slot::Hash() const {
+ uword result = (static_cast<int8_t>(kind_) * 63 + offset_in_bytes_) * 31;
if (IsDartField()) {
result += String::Handle(DataAs<const Field>()->name()).Hash();
} else if (IsLocalVariable()) {
diff --git a/runtime/vm/compiler/backend/slot.h b/runtime/vm/compiler/backend/slot.h
index 8a776de..c2078b7 100644
--- a/runtime/vm/compiler/backend/slot.h
+++ b/runtime/vm/compiler/backend/slot.h
@@ -265,7 +265,7 @@
}
bool Equals(const Slot* other) const;
- intptr_t Hashcode() const;
+ uword Hash() const;
bool IsIdentical(const Slot& other) const { return this == &other; }
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index ca9ebbf..aff6de7 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -778,9 +778,7 @@
procedure_helper.SetJustRead(ProcedureHelper::kName);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
- if (ReadTag() == kSomething) {
- CalculateFunctionNodeFingerprint();
- }
+ CalculateFunctionNodeFingerprint();
BuildHash(procedure_helper.kind_);
BuildHash(procedure_helper.flags_);
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index c83e6cb..4ede221 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -1116,9 +1116,7 @@
if (++next_read_ == field) return;
FALL_THROUGH;
case kFunction:
- if (helper_->ReadTag() == kSomething) {
- helper_->SkipFunctionNode(); // read function node.
- }
+ helper_->SkipFunctionNode(); // read function node.
if (++next_read_ == field) return;
FALL_THROUGH;
case kEnd:
@@ -2105,10 +2103,6 @@
if (tag == kProcedure) {
ProcedureHelper procedure_helper(this);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
- if (ReadTag() == kNothing) { // read function node tag.
- // Running a procedure without a function node doesn't make sense.
- UNREACHABLE();
- }
// Now at start of FunctionNode.
} else if (tag == kConstructor) {
ConstructorHelper constructor_helper(this);
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index f55386b..e2be4a9 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -512,9 +512,7 @@
void ScopeBuilder::VisitProcedure() {
ProcedureHelper procedure_helper(&helper_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
- if (helper_.ReadTag() == kSomething) {
- VisitFunctionNode();
- }
+ VisitFunctionNode();
}
void ScopeBuilder::VisitField() {
diff --git a/runtime/vm/compiler/relocation.h b/runtime/vm/compiler/relocation.h
index e9b6d44..948dffc 100644
--- a/runtime/vm/compiler/relocation.h
+++ b/runtime/vm/compiler/relocation.h
@@ -117,8 +117,8 @@
static Key KeyOf(Pair kv) { return kv.instructions; }
static ValueType ValueOf(Pair kv) { return kv.value; }
- static inline intptr_t Hashcode(Key key) {
- return static_cast<intptr_t>(key);
+ static inline uword Hash(Key key) {
+ return Utils::WordHash(static_cast<intptr_t>(key));
}
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair.instructions == key;
diff --git a/runtime/vm/compiler/write_barrier_elimination.cc b/runtime/vm/compiler/write_barrier_elimination.cc
index 84b000f..855cffb 100644
--- a/runtime/vm/compiler/write_barrier_elimination.cc
+++ b/runtime/vm/compiler/write_barrier_elimination.cc
@@ -2,8 +2,6 @@
// 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 <functional>
-
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/compiler_pass.h"
#include "vm/compiler/write_barrier_elimination.h"
@@ -31,7 +29,9 @@
static Key KeyOf(Pair kv) { return kv.definition; }
static Value ValueOf(Pair kv) { return kv.index; }
- static inline intptr_t Hashcode(Key key) { return std::hash<Key>()(key); }
+ static inline uword Hash(Key key) {
+ return Utils::WordHash(reinterpret_cast<intptr_t>(key));
+ }
static inline bool IsKeyEqual(Pair kv, Key key) {
return kv.definition == key;
}
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 206aad7..a87928a 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -991,6 +991,23 @@
obj1_ref->set_ptr(obj2_ref);
}
+// TODO(https://dartbug.com/38491): Reject Unions here as well.
+static bool IsFfiStruct(Thread* T, const Object& obj) {
+ if (obj.IsNull()) {
+ return false;
+ }
+
+ // CFE guarantees we can only have direct subclasses of `Struct`
+ // (no implementations or indirect subclasses are allowed).
+ const auto& klass = Class::Handle(Z, obj.clazz());
+ const auto& super_klass = Class::Handle(Z, klass.SuperClass());
+ if (super_klass.Name() != Symbols::Struct().ptr()) {
+ return false;
+ }
+ const auto& library = Library::Handle(Z, super_klass.library());
+ return library.url() == Symbols::DartFfi().ptr();
+}
+
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
Thread* thread,
const Object& ref,
@@ -1000,6 +1017,13 @@
if (!ref.ptr()->IsHeapObject()) {
return NULL;
}
+ if (ref.IsPointer()) {
+ return NULL;
+ }
+ if (IsFfiStruct(thread, ref)) {
+ return NULL;
+ }
+
FinalizablePersistentHandle* finalizable_ref =
FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
callback, external_allocation_size,
@@ -1008,16 +1032,14 @@
}
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
- Thread* thread,
+ Thread* T,
Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
- REUSABLE_OBJECT_HANDLESCOPE(thread);
- Object& ref = thread->ObjectHandle();
- ref = Api::UnwrapHandle(object);
- return AllocateWeakPersistentHandle(thread, ref, peer,
- external_allocation_size, callback);
+ const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object));
+ return AllocateWeakPersistentHandle(T, ref, peer, external_allocation_size,
+ callback);
}
static Dart_FinalizableHandle AllocateFinalizableHandle(
@@ -1029,6 +1051,12 @@
if (!ref.ptr()->IsHeapObject()) {
return NULL;
}
+ if (ref.IsPointer()) {
+ return NULL;
+ }
+ if (IsFfiStruct(thread, ref)) {
+ return NULL;
+ }
FinalizablePersistentHandle* finalizable_ref =
FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
@@ -1038,15 +1066,13 @@
}
static Dart_FinalizableHandle AllocateFinalizableHandle(
- Thread* thread,
+ Thread* T,
Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
- REUSABLE_OBJECT_HANDLESCOPE(thread);
- Object& ref = thread->ObjectHandle();
- ref = Api::UnwrapHandle(object);
- return AllocateFinalizableHandle(thread, ref, peer, external_allocation_size,
+ const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object));
+ return AllocateFinalizableHandle(T, ref, peer, external_allocation_size,
callback);
}
@@ -1055,15 +1081,13 @@
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
- Thread* thread = Thread::Current();
- CHECK_ISOLATE(thread->isolate());
+ DARTSCOPE(Thread::Current());
if (callback == NULL) {
return NULL;
}
- TransitionNativeToVM transition(thread);
- return AllocateWeakPersistentHandle(thread, object, peer,
- external_allocation_size, callback);
+ return AllocateWeakPersistentHandle(T, object, peer, external_allocation_size,
+ callback);
}
DART_EXPORT Dart_FinalizableHandle
@@ -1071,14 +1095,13 @@
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
- Thread* thread = Thread::Current();
- CHECK_ISOLATE(thread->isolate());
+ DARTSCOPE(Thread::Current());
if (callback == nullptr) {
return nullptr;
}
- TransitionNativeToVM transition(thread);
- return AllocateFinalizableHandle(thread, object, peer,
- external_allocation_size, callback);
+
+ return AllocateFinalizableHandle(T, object, peer, external_allocation_size,
+ callback);
}
DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index d77bbcf..6046c6e 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -3368,6 +3368,35 @@
Dart_NewWeakPersistentHandle(obj2, NULL, 0, NopCallback);
EXPECT_EQ(ref2, static_cast<void*>(NULL));
+ // Pointer object.
+ Dart_Handle ffi_lib = Dart_LookupLibrary(NewString("dart:ffi"));
+ Dart_Handle pointer_type =
+ Dart_GetNonNullableType(ffi_lib, NewString("Pointer"), 0, NULL);
+ Dart_Handle obj3 = Dart_Allocate(pointer_type);
+ EXPECT_VALID(obj3);
+ Dart_WeakPersistentHandle ref3 =
+ Dart_NewWeakPersistentHandle(obj3, nullptr, 0, FinalizableHandleCallback);
+ EXPECT_EQ(ref3, static_cast<void*>(nullptr));
+
+ // Subtype of Struct object.
+ const char* kScriptChars = R"(
+ import 'dart:ffi';
+
+ class MyStruct extends Struct {
+ external Pointer notEmpty;
+ }
+ )";
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ Dart_Handle my_struct_type =
+ Dart_GetNonNullableType(lib, NewString("MyStruct"), 0, NULL);
+ Dart_Handle obj4 = Dart_Allocate(my_struct_type);
+ EXPECT_VALID(obj4);
+ Dart_WeakPersistentHandle ref4 =
+ Dart_NewWeakPersistentHandle(obj4, nullptr, 0, FinalizableHandleCallback);
+ EXPECT_EQ(ref4, static_cast<void*>(nullptr));
+
+ // TODO(https://dartbug.com/38491): Reject Unions here as well.
+
Dart_ExitScope();
}
@@ -3388,6 +3417,33 @@
Dart_NewFinalizableHandle(obj2, nullptr, 0, FinalizableHandleCallback);
EXPECT_EQ(ref2, static_cast<void*>(nullptr));
+ // Pointer object.
+ Dart_Handle ffi_lib = Dart_LookupLibrary(NewString("dart:ffi"));
+ Dart_Handle pointer_type =
+ Dart_GetNonNullableType(ffi_lib, NewString("Pointer"), 0, NULL);
+ Dart_Handle obj3 = Dart_Allocate(pointer_type);
+ EXPECT_VALID(obj3);
+ Dart_FinalizableHandle ref3 =
+ Dart_NewFinalizableHandle(obj3, nullptr, 0, FinalizableHandleCallback);
+ EXPECT_EQ(ref3, static_cast<void*>(nullptr));
+
+ // Subtype of Struct object.
+ const char* kScriptChars = R"(
+ import 'dart:ffi';
+
+ class MyStruct extends Struct {
+ external Pointer notEmpty;
+ }
+ )";
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ Dart_Handle my_struct_type =
+ Dart_GetNonNullableType(lib, NewString("MyStruct"), 0, NULL);
+ Dart_Handle obj4 = Dart_Allocate(my_struct_type);
+ EXPECT_VALID(obj4);
+ Dart_FinalizableHandle ref4 =
+ Dart_NewFinalizableHandle(obj4, nullptr, 0, FinalizableHandleCallback);
+ EXPECT_EQ(ref4, static_cast<void*>(nullptr));
+
Dart_ExitScope();
}
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index b9d99de..6dd2515 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -548,7 +548,9 @@
static Key KeyOf(Pair kv) { return kv.key; }
static Value ValueOf(Pair kv) { return kv.value; }
- static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
+ static uword Hash(Key key) {
+ return Utils::WordHash(reinterpret_cast<intptr_t>(key));
+ }
static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
};
diff --git a/runtime/vm/dwarf.h b/runtime/vm/dwarf.h
index ccf9c83..66851c4 100644
--- a/runtime/vm/dwarf.h
+++ b/runtime/vm/dwarf.h
@@ -28,7 +28,7 @@
static Value ValueOf(Pair kv) { return kv.index_; }
- static inline intptr_t Hashcode(Key key) {
+ static inline uword Hash(Key key) {
return String::Handle(key->url()).Hash();
}
@@ -61,7 +61,7 @@
static Value ValueOf(Pair kv) { return kv.index_; }
- static inline intptr_t Hashcode(Key key) { return key->token_pos().Hash(); }
+ static inline uword Hash(Key key) { return key->token_pos().Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair.function_->ptr() == key->ptr();
@@ -110,9 +110,9 @@
static Value ValueOf(Pair kv) { return kv.value; }
- static inline intptr_t Hashcode(Key key) {
+ static inline uword Hash(Key key) {
// Instructions are always allocated in old space, so they don't move.
- return FinalizeHash(key->PayloadStart(), 32);
+ return Utils::WordHash(key->PayloadStart());
}
static inline bool IsKeyEqual(Pair pair, Key key) {
diff --git a/runtime/vm/hash_map.h b/runtime/vm/hash_map.h
index 5af4c15..77a3389 100644
--- a/runtime/vm/hash_map.h
+++ b/runtime/vm/hash_map.h
@@ -155,7 +155,7 @@
const typename KeyValueTrait::Value kNoValue =
KeyValueTrait::ValueOf(typename KeyValueTrait::Pair());
- uword hash = static_cast<uword>(KeyValueTrait::Hashcode(key));
+ uword hash = KeyValueTrait::Hash(key);
uword pos = Bound(hash);
if (KeyValueTrait::ValueOf(array_[pos].kv) != kNoValue) {
if (KeyValueTrait::IsKeyEqual(array_[pos].kv, key)) {
@@ -301,8 +301,7 @@
if (count_ >= array_size_ >> 1) Resize(array_size_ << 1);
ASSERT(count_ < array_size_);
count_++;
- uword pos = Bound(
- static_cast<uword>(KeyValueTrait::Hashcode(KeyValueTrait::KeyOf(kv))));
+ uword pos = Bound(KeyValueTrait::Hash(KeyValueTrait::KeyOf(kv)));
if (KeyValueTrait::ValueOf(array_[pos].kv) == kNoValue) {
array_[pos].kv = kv;
array_[pos].next = kNil;
@@ -341,7 +340,7 @@
const typename KeyValueTrait::Value kNoValue =
KeyValueTrait::ValueOf(typename KeyValueTrait::Pair());
- uword pos = Bound(static_cast<uword>(KeyValueTrait::Hashcode(key)));
+ uword pos = Bound(KeyValueTrait::Hash(key));
// Check to see if the first element in the bucket is the one we want to
// remove.
@@ -465,7 +464,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hashcode(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair kv, Key key) { return kv->Equals(key); }
};
@@ -479,7 +478,7 @@
static intptr_t KeyOf(Pair kv) { return kv.first(); }
static T ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key; }
+ static inline uword Hash(Key key) { return key; }
static inline bool IsKeyEqual(Pair kv, Key key) { return kv.first() == key; }
};
@@ -500,7 +499,7 @@
static Key KeyOf(Pair kv) { return kv.key; }
static Value ValueOf(Pair kv) { return kv.value; }
- static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
+ static uword Hash(Key key) { return reinterpret_cast<intptr_t>(key); }
static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
};
@@ -510,7 +509,7 @@
using Value = PointerKeyValueTrait<const char>::Value;
using Pair = PointerKeyValueTrait<const char>::Pair;
- static intptr_t Hashcode(Key key) {
+ static uword Hash(Key key) {
ASSERT(key != nullptr);
return Utils::StringHash(key, strlen(key));
}
@@ -550,7 +549,7 @@
typedef typename RawPointerKeyValueTrait<const char, V>::Value Value;
typedef typename RawPointerKeyValueTrait<const char, V>::Pair Pair;
- static intptr_t Hashcode(Key key) {
+ static uword Hash(Key key) {
ASSERT(key != nullptr);
return Utils::StringHash(key, strlen(key));
}
@@ -603,7 +602,7 @@
static Key KeyOf(Pair kv) { return kv.key; }
static Value ValueOf(Pair kv) { return kv.value; }
- static intptr_t Hashcode(Key key) { return key; }
+ static uword Hash(Key key) { return key; }
static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
};
@@ -653,8 +652,8 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) {
- return reinterpret_cast<intptr_t>(key);
+ static inline uword Hash(Key key) {
+ return Utils::WordHash(reinterpret_cast<intptr_t>(key));
}
static inline bool IsKeyEqual(Pair pair, Key key) { return pair == key; }
diff --git a/runtime/vm/hash_map_test.cc b/runtime/vm/hash_map_test.cc
index 27385d9..6d02c08 100644
--- a/runtime/vm/hash_map_test.cc
+++ b/runtime/vm/hash_map_test.cc
@@ -11,7 +11,7 @@
class TestValue {
public:
explicit TestValue(intptr_t x) : x_(x) {}
- intptr_t Hashcode() const { return x_ & 1; }
+ uword Hash() const { return x_ & 1; }
bool Equals(TestValue* other) { return x_ == other->x_; }
private:
diff --git a/runtime/vm/heap/weak_table.h b/runtime/vm/heap/weak_table.h
index c72d773..d5ddc2e 100644
--- a/runtime/vm/heap/weak_table.h
+++ b/runtime/vm/heap/weak_table.h
@@ -193,9 +193,7 @@
void Rehash();
- static intptr_t Hash(ObjectPtr key) {
- return static_cast<uintptr_t>(key) * 92821;
- }
+ static uword Hash(ObjectPtr key) { return static_cast<uword>(key) * 92821; }
Mutex mutex_;
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index f20022e..73cc3d6 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -142,7 +142,7 @@
#endif
}
-intptr_t ObjectOffsetTrait::Hashcode(Key key) {
+uword ObjectOffsetTrait::Hash(Key key) {
ObjectPtr obj = key;
ASSERT(!obj->IsSmi());
@@ -493,7 +493,7 @@
if (profile_writer_ != nullptr) {
const intptr_t end_position = stream->Position();
profile_writer_->AttributeBytesTo(
- V8SnapshotProfileWriter::ArtificialRootId(),
+ V8SnapshotProfileWriter::kArtificialRootId,
end_position - start_position);
}
#endif
@@ -683,7 +683,11 @@
const intptr_t element_offset = id.second - parent_id.second;
profile_writer_->AttributeReferenceTo(
parent_id,
- {id, V8SnapshotProfileWriter::Reference::kElement, element_offset});
+ {
+ V8SnapshotProfileWriter::Reference::kElement,
+ {.offset = element_offset},
+ },
+ id);
// Later objects will have the InstructionsSection as a parent if in
// bare instructions mode, otherwise the image.
if (bare_instruction_payloads) {
@@ -747,7 +751,11 @@
const intptr_t element_offset = id.second - parent_id.second;
profile_writer_->AttributeReferenceTo(
parent_id,
- {id, V8SnapshotProfileWriter::Reference::kElement, element_offset});
+ {
+ V8SnapshotProfileWriter::Reference::kElement,
+ {.offset = element_offset},
+ },
+ id);
}
#endif
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index a11356f..c234dd8 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -178,7 +178,7 @@
static Key KeyOf(Pair kv) { return kv.object; }
static Value ValueOf(Pair kv) { return kv.offset; }
- static intptr_t Hashcode(Key key);
+ static uword Hash(Key key);
static inline bool IsKeyEqual(Pair pair, Key key);
};
@@ -472,13 +472,14 @@
stream_(ASSERT_NOTNULL(stream)),
section_offset_(section_offset),
start_offset_(stream_->Position() - section_offset),
- object_type_(writer->ObjectTypeForProfile(object)) {}
+ object_type_(writer->ObjectTypeForProfile(object)),
+ object_name_(object.IsString() ? object.ToCString() : nullptr) {}
~TraceImageObjectScope() {
if (writer_->profile_writer_ == nullptr) return;
ASSERT(writer_->IsROSpace());
writer_->profile_writer_->SetObjectTypeAndName(
- {writer_->offset_space_, start_offset_}, object_type_, nullptr);
+ {writer_->offset_space_, start_offset_}, object_type_, object_name_);
writer_->profile_writer_->AttributeBytesTo(
{writer_->offset_space_, start_offset_},
stream_->Position() - section_offset_ - start_offset_);
@@ -490,6 +491,7 @@
const intptr_t section_offset_;
const intptr_t start_offset_;
const char* const object_type_;
+ const char* const object_name_;
DISALLOW_COPY_AND_ASSIGN(TraceImageObjectScope);
};
diff --git a/runtime/vm/isolate_reload.h b/runtime/vm/isolate_reload.h
index a09f604..69a399c 100644
--- a/runtime/vm/isolate_reload.h
+++ b/runtime/vm/isolate_reload.h
@@ -256,7 +256,7 @@
static Key KeyOf(Pair kv) { return kv->cid(); }
static Value ValueOf(Pair kv) { return kv; }
- static intptr_t Hashcode(Key key) { return key; }
+ static uword Hash(Key key) { return Utils::WordHash(key); }
static bool IsKeyEqual(Pair kv, Key key) { return kv->cid() == key; }
};
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 29eae67..2a78176 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
// Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 60;
-static const uint32_t kMaxSupportedKernelFormatVersion = 60;
+static const uint32_t kMinSupportedKernelFormatVersion = 61;
+static const uint32_t kMaxSupportedKernelFormatVersion = 61;
// Keep in sync with package:kernel/lib/binary/tag.dart
#define KERNEL_TAG_LIST(V) \
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index e5220d1..26329ce 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1367,7 +1367,8 @@
"runtime");
}
if (!Api::IsFfiEnabled() &&
- target_library.url() == Symbols::DartFfi().ptr()) {
+ target_library.url() == Symbols::DartFfi().ptr() &&
+ library->url() != Symbols::DartCore().ptr()) {
H.ReportError(
"import of dart:ffi is not supported in the current Dart runtime");
}
@@ -2032,8 +2033,6 @@
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
- Tag function_node_tag = helper_.ReadTag();
- ASSERT(function_node_tag == kSomething);
FunctionNodeHelper function_node_helper(&helper_);
function_node_helper.ReadUntilIncluding(FunctionNodeHelper::kDartAsyncMarker);
function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index dec4c98..1943dc8 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -168,7 +168,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->uri->Hash(); }
+ static inline uword Hash(Key key) { return key->uri->Hash(); }
static inline bool IsKeyEqual(Pair kv, Key key) {
// Only compare uri.
diff --git a/runtime/vm/malloc_hooks_tcmalloc.cc b/runtime/vm/malloc_hooks_tcmalloc.cc
index ff9bed6..787a429 100644
--- a/runtime/vm/malloc_hooks_tcmalloc.cc
+++ b/runtime/vm/malloc_hooks_tcmalloc.cc
@@ -180,7 +180,9 @@
static Key KeyOf(Pair kv) { return kv.key; }
static Value ValueOf(Pair kv) { return kv.value; }
- static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
+ static uword Hash(Key key) {
+ return Utils::WordHash(reinterpret_cast<intptr_t>(key));
+ }
static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
};
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index ec54a16..2272cf7 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -6012,7 +6012,7 @@
untag()->set_nullability(Smi::New(value));
}
-intptr_t TypeArguments::HashForRange(intptr_t from_index, intptr_t len) const {
+uword TypeArguments::HashForRange(intptr_t from_index, intptr_t len) const {
if (IsNull()) return kAllDynamicHash;
if (IsRaw(from_index, len)) return kAllDynamicHash;
uint32_t result = 0;
@@ -6045,10 +6045,9 @@
return result;
}
-intptr_t TypeArguments::ComputeHash() const {
+uword TypeArguments::ComputeHash() const {
if (IsNull()) return kAllDynamicHash;
- const intptr_t num_types = Length();
- const uint32_t result = HashForRange(0, num_types);
+ const uword result = HashForRange(0, Length());
if (result != 0) {
SetHash(result);
}
@@ -6819,7 +6818,7 @@
untag()->set_library_kernel_data(data.ptr());
}
-intptr_t Function::Hash() const {
+uword Function::Hash() const {
return String::HashRawSymbol(name());
}
@@ -14567,7 +14566,7 @@
return "CodeSourceMap";
}
-intptr_t CompressedStackMaps::Hashcode() const {
+uword CompressedStackMaps::Hash() const {
NoSafepointScope scope;
uint8_t* data = UnsafeMutableNonPointer(&untag()->data()[0]);
uint8_t* end = data + payload_size();
@@ -15111,7 +15110,7 @@
StoreNonPointer(&untag()->can_patch_to_monomorphic_, value);
}
-intptr_t UnlinkedCall::Hashcode() const {
+uword UnlinkedCall::Hash() const {
return String::Handle(target_name()).Hash();
}
@@ -19955,7 +19954,7 @@
return false;
}
-intptr_t AbstractType::Hash() const {
+uword AbstractType::Hash() const {
// AbstractType is an abstract class.
UNREACHABLE();
return 0;
@@ -20662,7 +20661,7 @@
type_args.EnumerateURIs(uris);
}
-intptr_t Type::ComputeHash() const {
+uword Type::ComputeHash() const {
ASSERT(IsFinalized());
uint32_t result = type_class_id();
// A legacy type should have the same hash as its non-nullable version to be
@@ -20695,7 +20694,7 @@
return result;
}
-intptr_t FunctionType::ComputeHash() const {
+uword FunctionType::ComputeHash() const {
ASSERT(IsFinalized());
uint32_t result = packed_fields();
// A legacy type should have the same hash as its non-nullable version to be
@@ -21121,7 +21120,7 @@
// Break cycle by not printing type arguments.
}
-intptr_t TypeRef::Hash() const {
+uword TypeRef::Hash() const {
// Do not use hash of the referenced type because
// - we could be in process of calculating it (as TypeRef is used to
// represent recursive references to types).
@@ -21614,7 +21613,7 @@
}
#endif // DEBUG
-intptr_t TypeParameter::ComputeHash() const {
+uword TypeParameter::ComputeHash() const {
ASSERT(IsFinalized() || IsBeingFinalized()); // Bound may not be finalized.
uint32_t result = parameterized_class_id();
// Hashing the bound reduces collisions, but may also create cycles.
@@ -22308,20 +22307,20 @@
}
}
-intptr_t String::Hash(const String& str, intptr_t begin_index, intptr_t len) {
+uword String::Hash(const String& str, intptr_t begin_index, intptr_t len) {
StringHasher hasher;
hasher.Add(str, begin_index, len);
return hasher.Finalize();
}
-intptr_t String::HashConcat(const String& str1, const String& str2) {
+uword String::HashConcat(const String& str1, const String& str2) {
StringHasher hasher;
hasher.Add(str1, 0, str1.Length());
hasher.Add(str2, 0, str2.Length());
return hasher.Finalize();
}
-intptr_t String::Hash(StringPtr raw) {
+uword String::Hash(StringPtr raw) {
StringHasher hasher;
uword length = Smi::Value(raw->untag()->length());
if (raw->IsOneByteString() || raw->IsExternalOneByteString()) {
@@ -22347,19 +22346,19 @@
}
}
-intptr_t String::Hash(const char* characters, intptr_t len) {
+uword String::Hash(const char* characters, intptr_t len) {
StringHasher hasher;
hasher.Add(reinterpret_cast<const uint8_t*>(characters), len);
return hasher.Finalize();
}
-intptr_t String::Hash(const uint8_t* characters, intptr_t len) {
+uword String::Hash(const uint8_t* characters, intptr_t len) {
StringHasher hasher;
hasher.Add(characters, len);
return hasher.Finalize();
}
-intptr_t String::Hash(const uint16_t* characters, intptr_t len) {
+uword String::Hash(const uint16_t* characters, intptr_t len) {
StringHasher hasher;
hasher.Add(characters, len);
return hasher.Finalize();
@@ -24767,7 +24766,7 @@
return buffer.buffer();
}
-int64_t Closure::ComputeHash() const {
+uword Closure::ComputeHash() const {
Thread* thread = Thread::Current();
DEBUG_ASSERT(thread->TopErrorHandlerIsExitFrame());
Zone* zone = thread->zone();
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 8c000c9..e5c69e7 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1918,7 +1918,7 @@
return RoundedAllocationSize(sizeof(UntaggedUnlinkedCall));
}
- intptr_t Hashcode() const;
+ uword Hash() const;
bool Equals(const UnlinkedCall& other) const;
static UnlinkedCallPtr New();
@@ -2684,7 +2684,7 @@
return OFFSET_OF(UntaggedFunction, unchecked_entry_point_);
}
- virtual intptr_t Hash() const;
+ virtual uword Hash() const;
// Returns true if there is at least one debugger breakpoint
// set in this function.
@@ -5752,7 +5752,7 @@
// Methods to allow use with PointerKeyValueTrait to create sets of CSMs.
bool Equals(const CompressedStackMaps* other) const { return Equals(*other); }
- intptr_t Hashcode() const;
+ uword Hash() const;
static intptr_t HeaderSize() { return sizeof(UntaggedCompressedStackMaps); }
static intptr_t UnroundedSize(CompressedStackMapsPtr maps) {
@@ -7668,8 +7668,8 @@
// Hash() is not stable until finalization is done.
return 0;
}
- intptr_t Hash() const;
- intptr_t HashForRange(intptr_t from_index, intptr_t len) const;
+ uword Hash() const;
+ uword HashForRange(intptr_t from_index, intptr_t len) const;
static TypeArgumentsPtr New(intptr_t len, Heap::Space space = Heap::kOld);
@@ -7677,7 +7677,7 @@
intptr_t ComputeNullability() const;
void set_nullability(intptr_t value) const;
- intptr_t ComputeHash() const;
+ uword ComputeHash() const;
void SetHash(intptr_t value) const;
// Check if the subvector of length 'len' starting at 'from_index' of this
@@ -7843,7 +7843,7 @@
// list and mark ambiguous triplets to be printed.
virtual void EnumerateURIs(URIs* uris) const;
- virtual intptr_t Hash() const;
+ virtual uword Hash() const;
// The name of this type's class, i.e. without the type argument names of this
// type.
@@ -8047,8 +8047,8 @@
#endif // DEBUG
virtual void EnumerateURIs(URIs* uris) const;
- virtual intptr_t Hash() const;
- intptr_t ComputeHash() const;
+ virtual uword Hash() const;
+ uword ComputeHash() const;
static intptr_t InstanceSize() {
return RoundedAllocationSize(sizeof(UntaggedType));
@@ -8191,8 +8191,8 @@
#endif // DEBUG
virtual void EnumerateURIs(URIs* uris) const;
- virtual intptr_t Hash() const;
- intptr_t ComputeHash() const;
+ virtual uword Hash() const;
+ uword ComputeHash() const;
bool IsSubtypeOf(const FunctionType& other, Heap::Space space) const;
@@ -8447,7 +8447,7 @@
#endif // DEBUG
virtual void EnumerateURIs(URIs* uris) const;
- virtual intptr_t Hash() const;
+ virtual uword Hash() const;
static intptr_t InstanceSize() {
return RoundedAllocationSize(sizeof(UntaggedTypeRef));
@@ -8561,7 +8561,7 @@
#endif // DEBUG
virtual void EnumerateURIs(URIs* uris) const { return; }
- virtual intptr_t Hash() const;
+ virtual uword Hash() const;
// Returns type corresponding to [this] type parameter from the
// given [instantiator_type_arguments] and [function_type_arguments].
@@ -8585,7 +8585,7 @@
Nullability nullability);
private:
- intptr_t ComputeHash() const;
+ uword ComputeHash() const;
void SetHash(intptr_t value) const;
void set_parameterized_class(const Class& value) const;
@@ -8918,8 +8918,8 @@
}
static intptr_t length_offset() { return OFFSET_OF(UntaggedString, length_); }
- intptr_t Hash() const {
- intptr_t result = GetCachedHash(ptr());
+ uword Hash() const {
+ uword result = GetCachedHash(ptr());
if (result != 0) {
return result;
}
@@ -8928,7 +8928,7 @@
return result;
}
- static intptr_t Hash(StringPtr raw);
+ static uword Hash(StringPtr raw);
bool HasHash() const {
ASSERT(Smi::New(0) == nullptr);
@@ -8944,19 +8944,19 @@
return OFFSET_OF(UntaggedString, hash_);
#endif
}
- static intptr_t Hash(const String& str, intptr_t begin_index, intptr_t len);
- static intptr_t Hash(const char* characters, intptr_t len);
- static intptr_t Hash(const uint16_t* characters, intptr_t len);
- static intptr_t Hash(const int32_t* characters, intptr_t len);
- static intptr_t HashRawSymbol(const StringPtr symbol) {
+ static uword Hash(const String& str, intptr_t begin_index, intptr_t len);
+ static uword Hash(const char* characters, intptr_t len);
+ static uword Hash(const uint16_t* characters, intptr_t len);
+ static uword Hash(const int32_t* characters, intptr_t len);
+ static uword HashRawSymbol(const StringPtr symbol) {
ASSERT(symbol->untag()->IsCanonical());
- intptr_t result = GetCachedHash(symbol);
+ const uword result = GetCachedHash(symbol);
ASSERT(result != 0);
return result;
}
// Returns the hash of str1 + str2.
- static intptr_t HashConcat(const String& str1, const String& str2);
+ static uword HashConcat(const String& str1, const String& str2);
virtual ObjectPtr HashCode() const { return Integer::New(Hash()); }
@@ -9178,7 +9178,7 @@
// They are protected to avoid mistaking Latin-1 for UTF-8, but used
// by friendly templated code (e.g., Symbols).
bool Equals(const uint8_t* characters, intptr_t len) const;
- static intptr_t Hash(const uint8_t* characters, intptr_t len);
+ static uword Hash(const uint8_t* characters, intptr_t len);
void SetLength(intptr_t value) const {
// This is only safe because we create a new Smi, which does not cause
@@ -10812,7 +10812,7 @@
virtual uint32_t CanonicalizeHash() const {
return Function::Handle(function()).Hash();
}
- int64_t ComputeHash() const;
+ uword ComputeHash() const;
static ClosurePtr New(const TypeArguments& instantiator_type_arguments,
const TypeArguments& function_type_arguments,
@@ -11536,7 +11536,7 @@
return array.At((index * kEntryLength) + kTargetFunctionIndex);
}
-inline intptr_t Type::Hash() const {
+inline uword Type::Hash() const {
ASSERT(IsFinalized());
intptr_t result = Smi::Value(untag()->hash());
if (result != 0) {
@@ -11551,7 +11551,7 @@
untag()->set_hash(Smi::New(value));
}
-inline intptr_t FunctionType::Hash() const {
+inline uword FunctionType::Hash() const {
ASSERT(IsFinalized());
intptr_t result = Smi::Value(untag()->hash());
if (result != 0) {
@@ -11566,7 +11566,7 @@
untag()->set_hash(Smi::New(value));
}
-inline intptr_t TypeParameter::Hash() const {
+inline uword TypeParameter::Hash() const {
ASSERT(IsFinalized() || IsBeingFinalized()); // Bound may not be finalized.
intptr_t result = Smi::Value(untag()->hash());
if (result != 0) {
@@ -11581,7 +11581,7 @@
untag()->set_hash(Smi::New(value));
}
-inline intptr_t TypeArguments::Hash() const {
+inline uword TypeArguments::Hash() const {
if (IsNull()) return kAllDynamicHash;
intptr_t result = Smi::Value(untag()->hash());
if (result != 0) {
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index e741716..519ea48 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -236,7 +236,6 @@
RW(GrowableObjectArray, ffi_callback_functions) \
RW(Class, ffi_pointer_class) \
RW(Class, ffi_native_type_class) \
- RW(Class, ffi_struct_class) \
RW(Object, ffi_as_function_internal) \
// Please remember the last entry must be referred in the 'to' function below.
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 8d94071..5417f6d 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -1365,7 +1365,7 @@
const String& clef = String::Handle(String::FromUTF16(clef_utf16, 2));
int32_t clef_utf32[] = {0x1D11E};
EXPECT(clef.Equals(clef_utf32, 1));
- intptr_t hash32 = String::Hash(String::FromUTF32(clef_utf32, 1));
+ uword hash32 = String::Hash(String::FromUTF32(clef_utf32, 1));
EXPECT_EQ(hash32, clef.Hash());
EXPECT_EQ(hash32, String::HashConcat(
String::Handle(String::FromUTF16(clef_utf16, 1)),
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index 9cf5d12..a61fb94 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -540,7 +540,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair kv, Key key) {
return kv->function()->ptr() == key->ptr();
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index b8e3de1..2e4bb01 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -468,7 +468,7 @@
static const intptr_t kHashBits = 30;
- intptr_t Hashcode() {
+ uword Hash() {
if (hash_ != 0) return hash_;
uint32_t hash = 0;
hash = CombineHashes(hash, spill_slot_bit_count_);
@@ -555,7 +555,7 @@
static Key KeyOf(Pair kv) { return kv.key; }
static Value ValueOf(Pair kv) { return kv.value; }
- static intptr_t Hashcode(Key key) { return key->Hashcode(); }
+ static uword Hash(Key key) { return key->Hash(); }
static bool IsKeyEqual(Pair kv, Key key) { return key->Equals(kv.key); }
};
@@ -739,7 +739,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Length(); }
+ static inline uword Hash(Key key) { return Utils::WordHash(key->Length()); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->Equals(*key);
@@ -786,7 +786,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->CanonicalizeHash(); }
+ static inline uword Hash(Key key) { return key->CanonicalizeHash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->CanonicalizeEquals(*key);
@@ -876,7 +876,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hashcode(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->Equals(*key);
@@ -951,9 +951,9 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) {
+ static inline uword Hash(Key key) {
ASSERT(!key->IsNull());
- return key->Length();
+ return Utils::WordHash(key->Length());
}
static inline bool IsKeyEqual(Pair pair, Key key) {
@@ -1001,9 +1001,9 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) {
+ static inline uword Hash(Key key) {
ASSERT(!key->IsNull());
- return key->Length();
+ return Utils::WordHash(key->Length());
}
static inline bool IsKeyEqual(Pair pair, Key key) {
@@ -1125,7 +1125,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair->Equals(*key);
@@ -1157,7 +1157,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Size(); }
+ static inline uword Hash(Key key) { return Utils::WordHash(key->Size()); }
static inline bool IsKeyEqual(Pair pair, Key key) {
// In AOT, disabled code objects should not be considered for deduplication.
diff --git a/runtime/vm/raw_object_fields.h b/runtime/vm/raw_object_fields.h
index 54a7a27..2b456be8 100644
--- a/runtime/vm/raw_object_fields.h
+++ b/runtime/vm/raw_object_fields.h
@@ -51,7 +51,9 @@
static Value ValueOf(Pair pair) { return pair.value; }
static Key KeyOf(Pair pair) { return pair.key; }
- static size_t Hashcode(Key key) { return key.first ^ key.second; }
+ static uword Hash(Key key) {
+ return Utils::WordHash(key.first ^ key.second);
+ }
static bool IsKeyEqual(Pair x, Key y) {
return x.key.first == y.first && x.key.second == y.second;
}
diff --git a/runtime/vm/source_report.h b/runtime/vm/source_report.h
index 08f0441..a7acf00 100644
--- a/runtime/vm/source_report.h
+++ b/runtime/vm/source_report.h
@@ -105,7 +105,7 @@
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->key->Hash(); }
+ static inline uword Hash(Key key) { return key->key->Hash(); }
static inline bool IsKeyEqual(Pair kv, Key key) {
return kv->script->ptr() == key->script->ptr();
diff --git a/runtime/vm/token_position.cc b/runtime/vm/token_position.cc
index 507a528..14f7058 100644
--- a/runtime/vm/token_position.cc
+++ b/runtime/vm/token_position.cc
@@ -4,14 +4,13 @@
#include "vm/token_position.h"
-#include "vm/hash.h"
#include "vm/object.h"
#include "vm/zone_text_buffer.h"
namespace dart {
-intptr_t TokenPosition::Hash() const {
- return FinalizeHash(value_, 31);
+uword TokenPosition::Hash() const {
+ return Utils::WordHash(value_);
}
TokenPosition TokenPosition::Deserialize(int32_t value) {
diff --git a/runtime/vm/token_position.h b/runtime/vm/token_position.h
index d15553e..8dcf100 100644
--- a/runtime/vm/token_position.h
+++ b/runtime/vm/token_position.h
@@ -61,7 +61,7 @@
// by the profiler.
class TokenPosition {
public:
- intptr_t Hash() const;
+ uword Hash() const;
// Returns whether the token positions are equal. Defined for all token
// positions.
diff --git a/runtime/vm/type_testing_stubs.h b/runtime/vm/type_testing_stubs.h
index 848ae1a..d8307b8 100644
--- a/runtime/vm/type_testing_stubs.h
+++ b/runtime/vm/type_testing_stubs.h
@@ -287,7 +287,7 @@
static Key KeyOf(Pair kv) { return kv; }
static Value ValueOf(Pair kv) { return kv; }
- static inline intptr_t Hashcode(Key key) { return key->Hash(); }
+ static inline uword Hash(Key key) { return key->Hash(); }
};
class TypeSetTrait : public ObjectSetTrait<const AbstractType> {
diff --git a/runtime/vm/v8_snapshot_writer.cc b/runtime/vm/v8_snapshot_writer.cc
index 4f4c1c1..c828b4f 100644
--- a/runtime/vm/v8_snapshot_writer.cc
+++ b/runtime/vm/v8_snapshot_writer.cc
@@ -2,8 +2,6 @@
// 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.
-#if defined(DART_PRECOMPILER)
-
#include "vm/v8_snapshot_writer.h"
#include "vm/dart.h"
@@ -11,18 +9,20 @@
namespace dart {
-const char* ZoneString(Zone* Z, const char* str) {
- const intptr_t len = strlen(str) + 1;
- char* dest = Z->Alloc<char>(len);
- snprintf(dest, len, "%s", str);
- return dest;
+const V8SnapshotProfileWriter::ObjectId
+ V8SnapshotProfileWriter::kArtificialRootId{kArtificial, 0};
+
+#if defined(DART_PRECOMPILER)
+
+static const char* ZoneString(Zone* Z, const char* str) {
+ return OS::SCreate(Z, "%s", str);
}
V8SnapshotProfileWriter::V8SnapshotProfileWriter(Zone* zone)
: zone_(zone),
node_types_(zone_),
edge_types_(zone_),
- strings_(zone),
+ strings_(zone_),
roots_(zone_) {
node_types_.Insert({"Unknown", kUnknown});
node_types_.Insert({"ArtificialRoot", kArtificialRoot});
@@ -35,28 +35,21 @@
strings_.Insert({"<unknown>", kUnknownString});
strings_.Insert({"<artificial root>", kArtificialRootString});
- nodes_.Insert({ArtificialRootId(),
- {
- kArtificialRoot,
- kArtificialRootString,
- ArtificialRootId(),
- 0,
- nullptr,
- 0,
- }});
+ nodes_.Insert(NodeInfo(zone_, kArtificialRoot, kArtificialRootString,
+ kArtificialRootId, 0, 0));
}
void V8SnapshotProfileWriter::SetObjectTypeAndName(ObjectId object_id,
const char* type,
const char* name) {
ASSERT(type != nullptr);
- NodeInfo* info = EnsureId(object_id);
if (!node_types_.HasKey(type)) {
node_types_.Insert({ZoneString(zone_, type), node_types_.Size()});
}
intptr_t type_id = node_types_.LookupValue(type);
+ NodeInfo* info = EnsureId(object_id);
ASSERT(info->type == kUnknown || info->type == type_id);
info->type = type_id;
if (name != nullptr) {
@@ -72,46 +65,66 @@
EnsureId(object_id)->self_size += num_bytes;
}
-void V8SnapshotProfileWriter::AttributeReferenceTo(ObjectId object_id,
- Reference reference) {
- EnsureId(reference.to_object_id);
- NodeInfo* info = EnsureId(object_id);
+V8SnapshotProfileWriter::ConstantEdgeType
+V8SnapshotProfileWriter::ReferenceTypeToEdgeType(Reference::Type type) {
+ switch (type) {
+ case Reference::kElement:
+ return ConstantEdgeType::kElement;
+ case Reference::kProperty:
+ return ConstantEdgeType::kProperty;
+ }
+}
- ASSERT(reference.offset_or_name >= 0);
- info->edges->Add({
- static_cast<intptr_t>(reference.reference_type == Reference::kElement
- ? kElement
- : kProperty),
- reference.offset_or_name,
- reference.to_object_id,
- });
+void V8SnapshotProfileWriter::AttributeReferenceTo(ObjectId from_object_id,
+ Reference reference,
+ ObjectId to_object_id) {
+ const bool is_element = reference.reference_type == Reference::kElement;
+ ASSERT(is_element ? reference.offset >= 0 : reference.name != nullptr);
+
+ EnsureId(to_object_id);
+ const Edge edge(ReferenceTypeToEdgeType(reference.reference_type),
+ is_element ? reference.offset : EnsureString(reference.name));
+ EnsureId(from_object_id)->AddEdge(edge, to_object_id);
++edge_count_;
}
-V8SnapshotProfileWriter::NodeInfo V8SnapshotProfileWriter::DefaultNode(
- ObjectId object_id) {
- return {
- kUnknown,
- kUnknownString,
- object_id,
- 0,
- new (zone_) ZoneGrowableArray<EdgeInfo>(zone_, 0),
- -1,
- };
+void V8SnapshotProfileWriter::AttributeDroppedReferenceTo(
+ ObjectId from_object_id,
+ Reference reference,
+ ObjectId to_object_id,
+ ObjectId replacement_object_id) {
+ ASSERT(to_object_id.first == kArtificial);
+ ASSERT(replacement_object_id.first != kArtificial);
+
+ const bool is_element = reference.reference_type == Reference::kElement;
+ ASSERT(is_element ? reference.offset >= 0 : reference.name != nullptr);
+
+ // The target node is added normally.
+ AttributeReferenceTo(from_object_id, reference, to_object_id);
+
+ // Put the replacement node at an invalid offset or name that can still be
+ // associated with the real one. For offsets, this is the negative offset.
+ // For names, it's the name prefixed with ":replacement_".
+ EnsureId(replacement_object_id);
+ const Edge replacement_edge(
+ ReferenceTypeToEdgeType(reference.reference_type),
+ is_element ? -reference.offset
+ : EnsureString(
+ OS::SCreate(zone_, ":replacement_%s", reference.name)));
+ EnsureId(from_object_id)->AddEdge(replacement_edge, replacement_object_id);
+ ++edge_count_;
}
-const V8SnapshotProfileWriter::NodeInfo&
-V8SnapshotProfileWriter::ArtificialRoot() {
- return nodes_.Lookup(ArtificialRootId())->value;
+bool V8SnapshotProfileWriter::HasId(const ObjectId& object_id) {
+ return nodes_.HasKey(object_id);
}
V8SnapshotProfileWriter::NodeInfo* V8SnapshotProfileWriter::EnsureId(
ObjectId object_id) {
- if (!nodes_.HasKey(object_id)) {
- NodeInfo info = DefaultNode(object_id);
- nodes_.Insert({object_id, info});
+ if (!HasId(object_id)) {
+ nodes_.Insert(NodeInfo(zone_, kUnknown, kUnknownString, object_id, 0, -1));
}
- return &nodes_.Lookup(object_id)->value;
+ return nodes_.Lookup(object_id);
}
intptr_t V8SnapshotProfileWriter::EnsureString(const char* str) {
@@ -122,24 +135,23 @@
return strings_.LookupValue(str);
}
-void V8SnapshotProfileWriter::WriteNodeInfo(JSONWriter* writer,
- const NodeInfo& info) {
+intptr_t V8SnapshotProfileWriter::WriteNodeInfo(JSONWriter* writer,
+ const NodeInfo& info) {
writer->PrintValue(info.type);
writer->PrintValue(info.name);
writer->PrintValue(NodeIdFor(info.id));
writer->PrintValue(info.self_size);
- // The artificial root has 'nullptr' edges, it actually points to all the
- // roots.
- writer->PrintValue64(info.edges != nullptr ? info.edges->length()
- : roots_.Size());
+ writer->PrintValue64(info.edges->Length());
writer->PrintNewline();
+ return kNumNodeFields;
}
void V8SnapshotProfileWriter::WriteEdgeInfo(JSONWriter* writer,
- const EdgeInfo& info) {
- writer->PrintValue64(info.type);
- writer->PrintValue64(info.name_or_index);
- writer->PrintValue64(nodes_.LookupValue(info.to_node).offset);
+ const Edge& info,
+ const ObjectId& target) {
+ writer->PrintValue64(info.first);
+ writer->PrintValue64(info.second);
+ writer->PrintValue64(nodes_.LookupValue(target).offset);
writer->PrintNewline();
}
@@ -151,11 +163,13 @@
// (most likely an oversight).
if (roots_.HasKey(object_id)) return;
- ObjectIdToNodeInfoTraits::Pair pair;
- pair.key = object_id;
- pair.value = NodeInfo{
- 0, name != nullptr ? EnsureString(name) : -1, object_id, 0, nullptr, 0};
- roots_.Insert(pair);
+ auto const info = NodeInfo(
+ zone_, 0, name != nullptr ? EnsureString(name) : -1, object_id, 0, 0);
+ roots_.Insert(info);
+ auto const root = EnsureId(kArtificialRootId);
+ root->AddEdge(info.name != -1 ? Edge(kProperty, info.name)
+ : Edge(kInternal, root->edges->Length()),
+ object_id);
}
void V8SnapshotProfileWriter::WriteStringsTable(
@@ -226,50 +240,37 @@
}
writer->CloseObject();
+ const auto& root = *nodes_.Lookup(kArtificialRootId);
+ auto nodes_it = nodes_.GetIterator();
+
{
writer->OpenArray("nodes");
- // Write the artificial root node.
- WriteNodeInfo(writer, ArtificialRoot());
- intptr_t offset = kNumNodeFields;
- ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
- auto it = nodes_.GetIterator();
- while ((entry = it.Next()) != nullptr) {
- ASSERT(entry->key == entry->value.id);
- if (entry->value.id == ArtificialRootId()) {
- continue; // Written separately above.
- }
- entry->value.offset = offset;
- WriteNodeInfo(writer, entry->value);
- offset += kNumNodeFields;
+ // Always write the information for the artificial root first.
+ intptr_t offset = WriteNodeInfo(writer, root);
+ for (auto entry = nodes_it.Next(); entry != nullptr;
+ entry = nodes_it.Next()) {
+ if (entry->id == kArtificialRootId) continue;
+ entry->offset = offset;
+ offset += WriteNodeInfo(writer, *entry);
}
writer->CloseArray();
+ nodes_it.Reset();
}
{
+ auto write_edges = [&](const NodeInfo& info) {
+ auto edges_it = info.edges->GetIterator();
+ while (auto const pair = edges_it.Next()) {
+ WriteEdgeInfo(writer, pair->edge, pair->target);
+ }
+ };
writer->OpenArray("edges");
-
- // Write references from the artificial root to the actual roots.
- ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
- auto roots_it = roots_.GetIterator();
- for (int i = 0; (entry = roots_it.Next()) != nullptr; ++i) {
- if (entry->value.name != -1) {
- WriteEdgeInfo(writer, {kProperty, entry->value.name, entry->key});
- } else {
- WriteEdgeInfo(writer, {kInternal, i, entry->key});
- }
+ // Always write the information for the artificial root first.
+ write_edges(root);
+ while (auto const entry = nodes_it.Next()) {
+ if (entry->id == kArtificialRootId) continue;
+ write_edges(*entry);
}
-
- auto nodes_it = nodes_.GetIterator();
- while ((entry = nodes_it.Next()) != nullptr) {
- if (entry->value.edges == nullptr) {
- continue; // Artificial root, its edges are written separately above.
- }
-
- for (intptr_t i = 0; i < entry->value.edges->length(); ++i) {
- WriteEdgeInfo(writer, entry->value.edges->At(i));
- }
- }
-
writer->CloseArray();
}
@@ -308,6 +309,6 @@
}
}
-} // namespace dart
-
#endif
+
+} // namespace dart
diff --git a/runtime/vm/v8_snapshot_writer.h b/runtime/vm/v8_snapshot_writer.h
index 34486ff..09ff87c 100644
--- a/runtime/vm/v8_snapshot_writer.h
+++ b/runtime/vm/v8_snapshot_writer.h
@@ -31,7 +31,7 @@
static Key KeyOf(Pair pair) { return pair.key; }
- static size_t Hashcode(Key key) { return String::Hash(key, strlen(key)); }
+ static uword Hash(Key key) { return String::Hash(key, strlen(key)); }
static bool IsKeyEqual(Pair x, Key y) { return strcmp(x.key, y) == 0; }
};
@@ -51,12 +51,14 @@
typedef std::pair<IdSpace, intptr_t> ObjectId;
struct Reference {
- ObjectId to_object_id;
- enum {
+ enum Type {
kElement,
kProperty,
} reference_type;
- intptr_t offset_or_name;
+ union {
+ intptr_t offset; // kElement
+ const char* name; // kProperty
+ };
};
enum ConstantStrings {
@@ -64,21 +66,35 @@
kArtificialRootString = 1,
};
+ static const ObjectId kArtificialRootId;
+
#if !defined(DART_PRECOMPILER)
explicit V8SnapshotProfileWriter(Zone* zone) {}
virtual ~V8SnapshotProfileWriter() {}
+ void SetObjectType(ObjectId object_id, const char* type) {}
void SetObjectTypeAndName(ObjectId object_id,
const char* type,
const char* name) {}
void AttributeBytesTo(ObjectId object_id, size_t num_bytes) {}
- void AttributeReferenceTo(ObjectId object_id, Reference reference) {}
+ void AttributeReferenceTo(ObjectId from_object_id,
+ Reference reference,
+ ObjectId to_object_id) {}
+ void AttributeWeakReferenceTo(
+ ObjectId from_object_id,
+ Reference reference,
+ ObjectId to_object_id,
+ ObjectId replacement_object_id = kArtificialRootId) {}
void AddRoot(ObjectId object_id, const char* name = nullptr) {}
- intptr_t EnsureString(const char* str) { return 0; }
+ bool HasId(const ObjectId& object_id) { return false; }
#else
explicit V8SnapshotProfileWriter(Zone* zone);
virtual ~V8SnapshotProfileWriter() {}
+ void SetObjectType(ObjectId object_id, const char* type) {
+ SetObjectTypeAndName(object_id, type, nullptr);
+ }
+
// Records that the object referenced by 'object_id' has type 'type'. The
// 'type' for all 'Instance's should be 'Instance', not the user-visible type
// and use 'name' for the real type instead.
@@ -92,9 +108,22 @@
void AttributeBytesTo(ObjectId object_id, size_t num_bytes);
// Records that a reference to the object with id 'to_object_id' was written
- // in order to serialize the object with id 'object_id'. This does not affect
- // the number of bytes charged to 'object_id'.
- void AttributeReferenceTo(ObjectId object_id, Reference reference);
+ // in order to serialize the object with id 'from_object_id'. This does not
+ // affect the number of bytes charged to 'from_object_id'.
+ void AttributeReferenceTo(ObjectId from_object_id,
+ Reference reference,
+ ObjectId to_object_id);
+
+ // Records that a weak serialization reference to a dropped object
+ // with id 'to_object_id' was written in order to serialize the object with id
+ // 'from_object_id'. 'to_object_id' must be an artificial node and
+ // 'replacement_object_id' is recorded as the replacement for the
+ // dropped object in the snapshot. This does not affect the number of
+ // bytes charged to 'from_object_id'.
+ void AttributeDroppedReferenceTo(ObjectId from_object_id,
+ Reference reference,
+ ObjectId to_object_id,
+ ObjectId replacement_object_id);
// Marks an object as being a root in the graph. Used for analysis of the
// graph.
@@ -103,26 +132,43 @@
// Write to a file in the V8 Snapshot Profile (JSON/.heapsnapshot) format.
void Write(const char* file);
- intptr_t EnsureString(const char* str);
-
- static ObjectId ArtificialRootId() { return {kArtificial, 0}; }
+ // Whether the given object ID has been added to the profile (via AddRoot,
+ // SetObjectTypeAndName, etc.).
+ bool HasId(const ObjectId& object_id);
private:
static constexpr intptr_t kNumNodeFields = 5;
static constexpr intptr_t kNumEdgeFields = 3;
- struct EdgeInfo {
- intptr_t type;
- intptr_t name_or_index;
- ObjectId to_node;
+ using Edge = std::pair<intptr_t, intptr_t>;
+
+ struct EdgeToObjectIdMapTrait {
+ using Key = Edge;
+ using Value = ObjectId;
+
+ struct Pair {
+ Pair() : edge{kContext, -1}, target(kArtificialRootId) {}
+ Pair(Key key, Value value) : edge(key), target(value) {}
+ Edge edge;
+ ObjectId target;
+ };
+
+ static Key KeyOf(Pair kv) { return kv.edge; }
+ static Value ValueOf(Pair kv) { return kv.target; }
+ static uword Hash(Key key) {
+ return FinalizeHash(CombineHashes(key.first, key.second), 30);
+ }
+ static bool IsKeyEqual(Pair kv, Key key) { return kv.edge == key; }
};
+ using EdgeMap = ZoneDirectChainedHashMap<EdgeToObjectIdMapTrait>;
+
struct NodeInfo {
- intptr_t type;
- intptr_t name;
+ intptr_t type = 0;
+ intptr_t name = 0;
ObjectId id;
- intptr_t self_size;
- ZoneGrowableArray<EdgeInfo>* edges = nullptr;
+ intptr_t self_size = 0;
+ EdgeMap* edges = nullptr;
// Populated during serialization.
intptr_t offset = -1;
// 'trace_node_id' isn't supported.
@@ -132,29 +178,36 @@
bool operator!=(const NodeInfo& other) { return id != other.id; }
bool operator==(const NodeInfo& other) { return !(*this != other); }
- NodeInfo(intptr_t type,
+ void AddEdge(const Edge& edge, const ObjectId& target) {
+ edges->Insert({edge, target});
+ }
+ bool HasEdge(const Edge& edge) { return edges->HasKey(edge); }
+
+ // To allow NodeInfo to be used as the pair in ObjectIdToNodeInfoTraits.
+ NodeInfo() : id{kSnapshot, -1} {}
+
+ NodeInfo(Zone* zone,
+ intptr_t type,
intptr_t name,
- ObjectId id,
+ const ObjectId& id,
intptr_t self_size,
- ZoneGrowableArray<EdgeInfo>* edges,
intptr_t offset)
: type(type),
name(name),
id(id),
self_size(self_size),
- edges(edges),
+ edges(new (zone) EdgeMap(zone)),
offset(offset) {}
};
- NodeInfo DefaultNode(ObjectId object_id);
- const NodeInfo& ArtificialRoot();
-
NodeInfo* EnsureId(ObjectId object_id);
static intptr_t NodeIdFor(ObjectId id) {
return (id.second << kIdSpaceBits) | id.first;
}
- enum ConstantEdgeTypes {
+ intptr_t EnsureString(const char* str);
+
+ enum ConstantEdgeType {
kContext = 0,
kElement = 1,
kProperty = 2,
@@ -165,36 +218,33 @@
kExtra = 7,
};
- enum ConstantNodeTypes {
+ static ConstantEdgeType ReferenceTypeToEdgeType(Reference::Type type);
+
+ enum ConstantNodeType {
kUnknown = 0,
kArtificialRoot = 1,
};
struct ObjectIdToNodeInfoTraits {
+ typedef NodeInfo Pair;
typedef ObjectId Key;
- typedef NodeInfo Value;
+ typedef Pair Value;
- struct Pair {
- Key key;
- Value value;
- Pair()
- : key{kSnapshot, -1}, value{0, 0, {kSnapshot, -1}, 0, nullptr, -1} {};
- Pair(Key k, Value v) : key(k), value(v) {}
- };
+ static Key KeyOf(const Pair& pair) { return pair.id; }
- static Key KeyOf(const Pair& pair) { return pair.key; }
+ static Value ValueOf(const Pair& pair) { return pair; }
- static Value ValueOf(const Pair& pair) { return pair.value; }
+ static uword Hash(Key key) { return Utils::WordHash(NodeIdFor(key)); }
- static size_t Hashcode(Key key) { return NodeIdFor(key); }
-
- static bool IsKeyEqual(const Pair& x, Key y) { return x.key == y; }
+ static bool IsKeyEqual(const Pair& x, Key y) { return x.id == y; }
};
Zone* zone_;
void Write(JSONWriter* writer);
- void WriteNodeInfo(JSONWriter* writer, const NodeInfo& info);
- void WriteEdgeInfo(JSONWriter* writer, const EdgeInfo& info);
+ intptr_t WriteNodeInfo(JSONWriter* writer, const NodeInfo& info);
+ void WriteEdgeInfo(JSONWriter* writer,
+ const Edge& info,
+ const ObjectId& target);
void WriteStringsTable(JSONWriter* writer,
const DirectChainedHashMap<StringToIntMapTraits>& map);
diff --git a/sdk/lib/_internal/vm/lib/core_patch.dart b/sdk/lib/_internal/vm/lib/core_patch.dart
index 6ffdce8..d79e07d 100644
--- a/sdk/lib/_internal/vm/lib/core_patch.dart
+++ b/sdk/lib/_internal/vm/lib/core_patch.dart
@@ -49,6 +49,8 @@
import "dart:convert" show ascii, Encoding, json, latin1, utf8;
+import "dart:ffi" show Pointer, Struct;
+
import "dart:isolate" show Isolate;
import "dart:math" show Random;
diff --git a/sdk/lib/_internal/vm/lib/expando_patch.dart b/sdk/lib/_internal/vm/lib/expando_patch.dart
index 01cbe51..868e6238e 100644
--- a/sdk/lib/_internal/vm/lib/expando_patch.dart
+++ b/sdk/lib/_internal/vm/lib/expando_patch.dart
@@ -140,9 +140,12 @@
if ((object == null) ||
(object is bool) ||
(object is num) ||
- (object is String)) {
+ (object is String) ||
+ (object is Pointer) ||
+ (object is Struct)) {
+ // TODO(https://dartbug.com/38491): Reject Unions here as well.
throw new ArgumentError.value(object,
- "Expandos are not allowed on strings, numbers, booleans or null");
+ "Expandos are not allowed on strings, numbers, booleans, null, Pointers or Structs.");
}
}
diff --git a/sdk/lib/core/expando.dart b/sdk/lib/core/expando.dart
index d623579..c081df8 100644
--- a/sdk/lib/core/expando.dart
+++ b/sdk/lib/core/expando.dart
@@ -6,7 +6,8 @@
/// An [Expando] allows adding new properties to objects.
///
-/// Does not work on numbers, strings, booleans or `null`.
+/// Does not work on numbers, strings, booleans, `null`, `dart:ffi` pointers
+/// or `dart:ffi` structs.
///
/// An `Expando` does not hold on to the added property value after an object
/// becomes inaccessible.
@@ -38,13 +39,15 @@
/// object. If the object hasn't been expanded, the method returns
/// `null`.
///
- /// The object must not be a number, a string or a boolean.
+ /// The object must not be a number, a string, a boolean, `null`, a
+ /// `dart:ffi` pointer, or a `dart:ffi` struct.
external T? operator [](Object object);
/// Sets the value of this [Expando]'s property on the given
/// object. Properties can effectively be removed again by setting
/// their value to `null`.
///
- /// The object must not be a number, a string or a boolean.
+ /// The object must not be a number, a string, a boolean, `null`, a
+ /// `dart:ffi` pointer, or a `dart:ffi` struct.
external void operator []=(Object object, T? value);
}
diff --git a/tests/corelib/expando_test.dart b/tests/corelib/expando_test.dart
index 02810bc..ccc074a 100644
--- a/tests/corelib/expando_test.dart
+++ b/tests/corelib/expando_test.dart
@@ -1,6 +1,9 @@
// Copyright (c) 2012, 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.
+//
+// VMOptions=--enable-ffi=true
+// VMOptions=--enable-ffi=false
import "package:expect/expect.dart";
diff --git a/tests/corelib_2/expando_test.dart b/tests/corelib_2/expando_test.dart
index e2a342b..95ae7db 100644
--- a/tests/corelib_2/expando_test.dart
+++ b/tests/corelib_2/expando_test.dart
@@ -1,6 +1,9 @@
// Copyright (c) 2012, 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.
+//
+// VMOptions=--enable-ffi=true
+// VMOptions=--enable-ffi=false
import "package:expect/expect.dart";
diff --git a/tests/ffi/expando_test.dart b/tests/ffi/expando_test.dart
new file mode 100644
index 0000000..473fc6f
--- /dev/null
+++ b/tests/ffi/expando_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2021, 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.
+//
+// Dart test program for testing dart:ffi primitive data pointers.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+
+void main() {
+ testPointer();
+ testStruct();
+}
+
+Expando<int> expando = Expando('myExpando');
+
+void testPointer() {
+ final pointer = Pointer<Int8>.fromAddress(0xdeadbeef);
+ Expect.throws(() {
+ expando[pointer];
+ });
+ Expect.throws(() {
+ expando[pointer] = 1234;
+ });
+}
+
+class MyStruct extends Struct {
+ external Pointer notEmpty;
+}
+
+void testStruct() {
+ final pointer = Pointer<MyStruct>.fromAddress(0xdeadbeef);
+ final struct = pointer.ref;
+ Expect.throws(() {
+ expando[struct];
+ });
+ Expect.throws(() {
+ expando[struct] = 1234;
+ });
+}
diff --git a/tests/ffi_2/expando_test.dart b/tests/ffi_2/expando_test.dart
new file mode 100644
index 0000000..ac14324
--- /dev/null
+++ b/tests/ffi_2/expando_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2021, 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.
+//
+// Dart test program for testing dart:ffi primitive data pointers.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+
+void main() {
+ testPointer();
+ testStruct();
+}
+
+Expando<int> expando = Expando('myExpando');
+
+void testPointer() {
+ final pointer = Pointer<Int8>.fromAddress(0xdeadbeef);
+ Expect.throws(() {
+ expando[pointer];
+ });
+ Expect.throws(() {
+ expando[pointer] = 1234;
+ });
+}
+
+class MyStruct extends Struct {
+ Pointer notEmpty;
+}
+
+void testStruct() {
+ final pointer = Pointer<MyStruct>.fromAddress(0xdeadbeef);
+ final struct = pointer.ref;
+ Expect.throws(() {
+ expando[struct];
+ });
+ Expect.throws(() {
+ expando[struct] = 1234;
+ });
+}
diff --git a/tools/VERSION b/tools/VERSION
index 9201ef4..e26d75d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 3
+PRERELEASE 4
PRERELEASE_PATCH 0
\ No newline at end of file