Version 2.15.0-11.0.dev
Merge commit '5cec85d5ce49d13105e593f8fe6afb576ca5a3c2' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index f7073dd..20cdef0 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
- "generated": "2021-08-05T11:33:04.746536",
+ "generated": "2021-08-10T10:51:47.272341",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@@ -659,7 +659,7 @@
"name": "status_file",
"rootUri": "../pkg/status_file",
"packageUri": "lib/",
- "languageVersion": "2.3"
+ "languageVersion": "2.12"
},
{
"name": "stream_channel",
diff --git a/pkg/dart_internal/pubspec.yaml b/pkg/dart_internal/pubspec.yaml
index 130631e..d871d5c 100644
--- a/pkg/dart_internal/pubspec.yaml
+++ b/pkg/dart_internal/pubspec.yaml
@@ -1,5 +1,5 @@
name: dart_internal
-version: 0.2.1
+version: 0.2.2
repository: https://github.com/dart-lang/sdk/tree/master/pkg/dart_internal
description: >-
This package is not intended for wide use. It provides a temporary API to
@@ -9,4 +9,4 @@
environment:
# Restrict the upper bound so that we can remove support for this in a later
# version of the SDK without it being a breaking change.
- sdk: ">=2.12.0 <2.15.0"
+ sdk: ">=2.12.0 <2.16.0"
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index 669617b..fc56f09 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -138,6 +138,20 @@
/// have been called.
late final bool isAttach;
+ /// A list of evaluateNames for InstanceRef IDs.
+ ///
+ /// When providing variables for fields/getters or items in maps/arrays, we
+ /// need to provide an expression to the client that evaluates to that
+ /// variable so that functionality like "Add to Watch" or "Copy Value" can
+ /// work. For example, if a user expands a list named `myList` then the 1st
+ /// [Variable] returned should have an evaluateName of `myList[0]`. The `foo`
+ /// getter of that object would then have an evaluateName of `myList[0].foo`.
+ ///
+ /// Since those expressions aren't round-tripped as child variables are
+ /// requested we build them up as we send variables out, so we can append to
+ /// them when returning elements/map entries/fields/getters.
+ final _evaluateNamesForInstanceRefIds = <String, String>{};
+
/// A list of all possible project paths that should be considered the users
/// own code.
///
@@ -219,6 +233,26 @@
// sendResponse();
}
+ /// Builds an evaluateName given a parent VM InstanceRef ID and a suffix.
+ ///
+ /// If [parentInstanceRefId] is `null`, or we have no evaluateName for it,
+ /// will return null.
+ String? buildEvaluateName(
+ String suffix, {
+ required String? parentInstanceRefId,
+ }) {
+ final parentEvaluateName =
+ _evaluateNamesForInstanceRefIds[parentInstanceRefId];
+ return combineEvaluateName(parentEvaluateName, suffix);
+ }
+
+ /// Builds an evaluateName given a prefix and a suffix.
+ ///
+ /// If [prefix] is null, will return be null.
+ String? combineEvaluateName(String? prefix, String suffix) {
+ return prefix != null ? '$prefix$suffix' : null;
+ }
+
/// configurationDone is called by the client when it has finished sending
/// any initial configuration (such as breakpoints and exception pause
/// settings).
@@ -489,11 +523,14 @@
result,
allowCallingToString: evaluateToStringInDebugViews,
);
- // TODO(dantup): We may need to store `expression` with this data
- // to allow building nested evaluateNames.
+
final variablesReference =
_converter.isSimpleKind(result.kind) ? 0 : thread.storeData(result);
+ // Store the expression that gets this object as we may need it to
+ // compute evaluateNames for child objects later.
+ storeEvaluateName(result, expression);
+
sendResponse(EvaluateResponseBody(
result: resultString,
variablesReference: variablesReference,
@@ -663,7 +700,7 @@
// For local variables, we can just reuse the frameId as variablesReference
// as variablesRequest handles stored data of type `Frame` directly.
scopes.add(Scope(
- name: 'Variables',
+ name: 'Locals',
presentationHint: 'locals',
variablesReference: args.frameId,
expensive: false,
@@ -943,6 +980,14 @@
sendResponse();
}
+ /// Stores [evaluateName] as the expression that can be evaluated to get
+ /// [instanceRef].
+ void storeEvaluateName(vm.InstanceRef instanceRef, String? evaluateName) {
+ if (evaluateName != null) {
+ _evaluateNamesForInstanceRefIds[instanceRef.id!] = evaluateName;
+ }
+ }
+
/// Overridden by sub-classes to handle when the client sends a
/// `terminateRequest` (a request for a graceful shut down).
Future<void> terminateImpl();
@@ -1032,19 +1077,55 @@
final vars = vmData.vars;
if (vars != null) {
Future<Variable> convert(int index, vm.BoundVariable variable) {
+ // Store the expression that gets this object as we may need it to
+ // compute evaluateNames for child objects later.
+ storeEvaluateName(variable.value, variable.name);
return _converter.convertVmResponseToVariable(
thread,
variable.value,
name: variable.name,
allowCallingToString: evaluateToStringInDebugViews &&
index <= maxToStringsPerEvaluation,
+ evaluateName: variable.name,
);
}
variables.addAll(await Future.wait(vars.mapIndexed(convert)));
+
+ // Sort the variables by name.
+ variables.sortBy((v) => v.name);
}
- } else if (vmData is vm.MapAssociation) {
- // TODO(dantup): Maps
+ } else if (data is vm.MapAssociation) {
+ final key = data.key;
+ final value = data.value;
+ if (key is vm.InstanceRef && value is vm.InstanceRef) {
+ // For a MapAssociation, we create a dummy set of variables for "key" and
+ // "value" so that each may be expanded if they are complex values.
+ variables.addAll([
+ Variable(
+ name: 'key',
+ value: await _converter.convertVmInstanceRefToDisplayString(
+ thread,
+ key,
+ allowCallingToString: evaluateToStringInDebugViews,
+ ),
+ variablesReference:
+ _converter.isSimpleKind(key.kind) ? 0 : thread.storeData(key),
+ ),
+ Variable(
+ name: 'value',
+ value: await _converter.convertVmInstanceRefToDisplayString(
+ thread,
+ value,
+ allowCallingToString: evaluateToStringInDebugViews,
+ ),
+ variablesReference: _converter.isSimpleKind(value.kind)
+ ? 0
+ : thread.storeData(value),
+ evaluateName:
+ buildEvaluateName('', parentInstanceRefId: value.id)),
+ ]);
+ }
} else if (vmData is vm.ObjRef) {
final object =
await _isolateManager.getObject(storedData.thread.isolate, vmData);
@@ -1056,13 +1137,10 @@
variablesReference: 0,
));
} else if (object is vm.Instance) {
- // TODO(dantup): evaluateName
- // should be built taking the parent into account, for ex. if
- // args.variablesReference == thread.exceptionReference then we need to
- // use some sythensized variable name like `frameExceptionExpression`.
variables.addAll(await _converter.convertVmInstanceToVariablesList(
thread,
object,
+ evaluateName: buildEvaluateName('', parentInstanceRefId: vmData.id),
allowCallingToString: evaluateToStringInDebugViews,
startItem: childStart,
numItems: childCount,
@@ -1076,8 +1154,6 @@
}
}
- variables.sortBy((v) => v.name);
-
sendResponse(VariablesResponseBody(variables: variables));
}
@@ -1172,6 +1248,7 @@
// string they logged regardless of the evaluateToStringInDebugViews
// setting.
allowCallingToString: true,
+ allowTruncatedValue: false,
includeQuotesAroundString: false,
);
}
diff --git a/pkg/dds/lib/src/dap/isolate_manager.dart b/pkg/dds/lib/src/dap/isolate_manager.dart
index 4157cb0..d579bde 100644
--- a/pkg/dds/lib/src/dap/isolate_manager.dart
+++ b/pkg/dds/lib/src/dap/isolate_manager.dart
@@ -444,6 +444,7 @@
// can add a variables scope for it so it can be examined.
final exception = event.exception;
if (exception != null) {
+ _adapter.storeEvaluateName(exception, threadExceptionExpression);
thread.exceptionReference = thread.storeData(exception);
}
diff --git a/pkg/dds/lib/src/dap/protocol_converter.dart b/pkg/dds/lib/src/dap/protocol_converter.dart
index c22b2a4..2d2f7240 100644
--- a/pkg/dds/lib/src/dap/protocol_converter.dart
+++ b/pkg/dds/lib/src/dap/protocol_converter.dart
@@ -61,17 +61,22 @@
ThreadInfo thread,
vm.InstanceRef ref, {
required bool allowCallingToString,
+ bool allowTruncatedValue = true,
bool includeQuotesAroundString = true,
}) async {
final isTruncated = ref.valueAsStringIsTruncated ?? false;
if (ref.kind == vm.InstanceKind.kString && isTruncated) {
- // Call toString() if allowed, otherwise (or if it returns null) fall back
- // to the truncated value with "…" suffix.
- var stringValue = allowCallingToString
+ // Call toString() if allowed (and we don't already have a value),
+ // otherwise (or if it returns null) fall back to the truncated value
+ // with "…" suffix.
+ var stringValue = allowCallingToString &&
+ (ref.valueAsString == null || !allowTruncatedValue)
? await _callToString(
thread,
ref,
- includeQuotesAroundString: includeQuotesAroundString,
+ // Quotes are handled below, so they can be wrapped around the
+ // elipsis.
+ includeQuotesAroundString: false,
)
: null;
stringValue ??= '${ref.valueAsString}…';
@@ -116,6 +121,7 @@
Future<List<dap.Variable>> convertVmInstanceToVariablesList(
ThreadInfo thread,
vm.Instance instance, {
+ required String? evaluateName,
required bool allowCallingToString,
int? startItem = 0,
int? numItems,
@@ -130,6 +136,8 @@
await convertVmResponseToVariable(
thread,
instance,
+ name: null,
+ evaluateName: evaluateName,
allowCallingToString: allowCallingToString,
)
];
@@ -143,7 +151,9 @@
(index, response) => convertVmResponseToVariable(
thread,
response,
- name: '${start + index}',
+ name: '[${start + index}]',
+ evaluateName: _adapter.combineEvaluateName(
+ evaluateName, '[${start + index}]'),
allowCallingToString:
allowCallingToString && index <= maxToStringsPerEvaluation,
),
@@ -158,14 +168,26 @@
return Future.wait(associations
.sublist(start, numItems != null ? start + numItems : null)
.mapIndexed((index, mapEntry) async {
+ final key = mapEntry.key;
+ final value = mapEntry.value;
final callToString =
allowCallingToString && index <= maxToStringsPerEvaluation;
- final keyDisplay = await convertVmResponseToDisplayString(
- thread, mapEntry.key,
+
+ final keyDisplay = await convertVmResponseToDisplayString(thread, key,
allowCallingToString: callToString);
final valueDisplay = await convertVmResponseToDisplayString(
- thread, mapEntry.value,
+ thread, value,
allowCallingToString: callToString);
+
+ // We only provide an evaluateName for the value, and only if the
+ // key is a simple value.
+ if (key is vm.InstanceRef &&
+ value is vm.InstanceRef &&
+ evaluateName != null &&
+ isSimpleKind(key.kind)) {
+ _adapter.storeEvaluateName(value, '$evaluateName[$keyDisplay]');
+ }
+
return dap.Variable(
name: '${start + index}',
value: '$keyDisplay -> $valueDisplay',
@@ -175,11 +197,17 @@
} else if (fields != null) {
// Otherwise, show the fields from the instance.
final variables = await Future.wait(fields.mapIndexed(
- (index, field) async => convertVmResponseToVariable(
- thread, field.value,
- name: field.decl?.name ?? '<unnamed field>',
+ (index, field) async {
+ final name = field.decl?.name;
+ return convertVmResponseToVariable(thread, field.value,
+ name: name ?? '<unnamed field>',
+ evaluateName: name != null
+ ? _adapter.combineEvaluateName(evaluateName, '.$name')
+ : null,
allowCallingToString:
- allowCallingToString && index <= maxToStringsPerEvaluation)));
+ allowCallingToString && index <= maxToStringsPerEvaluation);
+ },
+ ));
// Also evaluate the getters if evaluateGettersInDebugViews=true enabled.
final service = _adapter.vmService;
@@ -202,6 +230,8 @@
thread,
response,
name: getterName,
+ evaluateName:
+ _adapter.combineEvaluateName(evaluateName, '.$getterName'),
allowCallingToString:
allowCallingToString && index <= maxToStringsPerEvaluation,
);
@@ -210,6 +240,9 @@
variables.addAll(await Future.wait(getterNames.mapIndexed(evaluate)));
}
+ // Sort the fields/getters by name.
+ variables.sortBy((v) => v.name);
+
return variables;
} else {
// For any other type that we don't produce variables for, return an empty
@@ -254,7 +287,8 @@
Future<dap.Variable> convertVmResponseToVariable(
ThreadInfo thread,
vm.Response response, {
- String? name,
+ required String? name,
+ required String? evaluateName,
required bool allowCallingToString,
}) async {
if (response is vm.InstanceRef) {
@@ -265,6 +299,7 @@
return dap.Variable(
name: name ?? response.kind.toString(),
+ evaluateName: evaluateName,
value: await convertVmResponseToDisplayString(
thread,
response,
diff --git a/pkg/dds/test/dap/integration/debug_eval_test.dart b/pkg/dds/test/dap/integration/debug_eval_test.dart
index ab24f0c..dc5b2a0 100644
--- a/pkg/dds/test/dap/integration/debug_eval_test.dart
+++ b/pkg/dds/test/dap/integration/debug_eval_test.dart
@@ -47,12 +47,12 @@
);
// Check we got a variablesReference that maps on to the fields.
- expect(result.variablesReference, greaterThan(0));
+ expect(result.variablesReference, isPositive);
await client.expectVariables(
result.variablesReference,
'''
- isUtc: false
- ''',
+ isUtc: false, eval: DateTime(2000, 1, 1).isUtc
+ ''',
);
});
@@ -111,7 +111,7 @@
threadExceptionExpression,
'_Exception',
);
- expect(result.variablesReference, greaterThan(0));
+ expect(result.variablesReference, isPositive);
});
test(
diff --git a/pkg/dds/test/dap/integration/debug_test.dart b/pkg/dds/test/dap/integration/debug_test.dart
index 18b544c..3a58c37 100644
--- a/pkg/dds/test/dap/integration/debug_test.dart
+++ b/pkg/dds/test/dap/integration/debug_test.dart
@@ -159,7 +159,7 @@
// SDK sources should have a sourceReference and no path.
expect(topFrame.source!.path, isNull);
- expect(topFrame.source!.sourceReference, greaterThan(0));
+ expect(topFrame.source!.sourceReference, isPositive);
// Source code should contain the implementation/signature of print().
final source = await client.getValidSource(topFrame.source!);
diff --git a/pkg/dds/test/dap/integration/debug_variables_test.dart b/pkg/dds/test/dap/integration/debug_variables_test.dart
index b5e357c..8acecb6 100644
--- a/pkg/dds/test/dap/integration/debug_variables_test.dart
+++ b/pkg/dds/test/dap/integration/debug_variables_test.dart
@@ -40,18 +40,18 @@
// Check top two frames (in `foo` and in `main`).
await client.expectScopeVariables(
stack.stackFrames[0].id, // Top frame: foo
- 'Variables',
+ 'Locals',
'''
- b: 2
- ''',
+ b: 2, eval: b
+ ''',
);
await client.expectScopeVariables(
stack.stackFrames[1].id, // Second frame: main
- 'Variables',
+ 'Locals',
'''
- args: List (0 items)
- myVariable: 1
- ''',
+ args: List (0 items), eval: args
+ myVariable: 1, eval: myVariable
+ ''',
);
});
@@ -76,9 +76,9 @@
await client.expectScopeVariables(
topFrameId,
'Exceptions',
- '''
- String: "my error"
- ''',
+ r'''
+ String: "my error", eval: $_threadException
+ ''',
);
});
@@ -103,12 +103,11 @@
await client.expectScopeVariables(
topFrameId,
'Exceptions',
- // TODO(dantup): evaluateNames
- '''
- invalidValue: null
- message: "Must not be null"
- name: "args"
- ''',
+ r'''
+ invalidValue: null, eval: $_threadException.invalidValue
+ message: "Must not be null", eval: $_threadException.message
+ name: "args", eval: $_threadException.name
+ ''',
);
});
@@ -128,8 +127,8 @@
expectedName: 'myVariable',
expectedDisplayString: 'DateTime',
expectedVariables: '''
- isUtc: false
- ''',
+ isUtc: false, eval: myVariable.isUtc
+ ''',
);
});
@@ -157,19 +156,19 @@
expectedName: 'myVariable',
expectedDisplayString: 'DateTime',
expectedVariables: '''
- day: 1
- hour: 0
- isUtc: false
- microsecond: 0
- millisecond: 0
- minute: 0
- month: 1
- runtimeType: Type (DateTime)
- second: 0
- timeZoneOffset: Duration
- weekday: 6
- year: 2000
- ''',
+ day: 1, eval: myVariable.day
+ hour: 0, eval: myVariable.hour
+ isUtc: false, eval: myVariable.isUtc
+ microsecond: 0, eval: myVariable.microsecond
+ millisecond: 0, eval: myVariable.millisecond
+ minute: 0, eval: myVariable.minute
+ month: 1, eval: myVariable.month
+ runtimeType: Type (DateTime), eval: myVariable.runtimeType
+ second: 0, eval: myVariable.second
+ timeZoneOffset: Duration, eval: myVariable.timeZoneOffset
+ weekday: 6, eval: myVariable.weekday
+ year: 2000, eval: myVariable.year
+ ''',
ignore: {
// Don't check fields that may very based on timezone as it'll make
// these tests fragile, and this isn't really what's being tested.
@@ -195,12 +194,11 @@
stop.threadId!,
expectedName: 'myVariable',
expectedDisplayString: 'List (3 items)',
- // TODO(dantup): evaluateNames
expectedVariables: '''
- 0: "first"
- 1: "second"
- 2: "third"
- ''',
+ [0]: "first", eval: myVariable[0]
+ [1]: "second", eval: myVariable[1]
+ [2]: "third", eval: myVariable[2]
+ ''',
);
});
@@ -219,22 +217,144 @@
stop.threadId!,
expectedName: 'myVariable',
expectedDisplayString: 'List (3 items)',
- // TODO(dantup): evaluateNames
expectedVariables: '''
- 1: "second"
- ''',
+ [1]: "second", eval: myVariable[1]
+ ''',
start: 1,
count: 1,
);
});
- test('renders a simple map', () {
- // TODO(dantup): Implement this (inc evaluateNames)
- }, skip: true);
+ test('renders a simple map with keys/values', () async {
+ final client = dap.client;
+ final testFile = await dap.createTestFile(r'''
+void main(List<String> args) {
+ final myVariable = {
+ 'zero': 0,
+ 'one': 1,
+ 'two': 2
+ };
+ print('Hello!'); // BREAKPOINT
+}
+ ''');
+ final breakpointLine = lineWith(testFile, '// BREAKPOINT');
- test('renders a simple map subset', () {
- // TODO(dantup): Implement this (inc evaluateNames)
- }, skip: true);
+ final stop = await client.hitBreakpoint(testFile, breakpointLine);
+ final variables = await client.expectLocalVariable(
+ stop.threadId!,
+ expectedName: 'myVariable',
+ expectedDisplayString: 'Map (3 items)',
+ // For maps, we render a level of MapAssociates first, which show
+ // their index numbers. Expanding them has a Key and a Value "field"
+ // which correspond to the items.
+ expectedVariables: '''
+ 0: "zero" -> 0
+ 1: "one" -> 1
+ 2: "two" -> 2
+ ''',
+ );
+
+ // Check one of the MapAssociation variables has the correct Key/Value
+ // inside.
+ expect(variables.variables, hasLength(3));
+ final variableOne = variables.variables[1];
+ expect(variableOne.variablesReference, isPositive);
+ await client.expectVariables(
+ variableOne.variablesReference,
+ '''
+ key: "one"
+ value: 1, eval: myVariable["one"]
+ ''',
+ );
+ });
+
+ test('renders a simple map subset', () async {
+ final client = dap.client;
+ final testFile = await dap.createTestFile(r'''
+void main(List<String> args) {
+ final myVariable = {
+ 'zero': 0,
+ 'one': 1,
+ 'two': 2
+ };
+ print('Hello!'); // BREAKPOINT
+}
+ ''');
+ final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+ final stop = await client.hitBreakpoint(testFile, breakpointLine);
+ await client.expectLocalVariable(
+ stop.threadId!,
+ expectedName: 'myVariable',
+ expectedDisplayString: 'Map (3 items)',
+ // For maps, we render a level of MapAssociates first, which show
+ // their index numbers. Expanding them has a Key and a Value "field"
+ // which correspond to the items.
+ expectedVariables: '''
+ 1: "one" -> 1
+ ''',
+ start: 1,
+ count: 1,
+ );
+ });
+
+ test('renders a complex map with keys/values', () async {
+ final client = dap.client;
+ final testFile = await dap.createTestFile(r'''
+void main(List<String> args) {
+ final myVariable = {
+ DateTime(2000, 1, 1): Exception("my error")
+ };
+ print('Hello!'); // BREAKPOINT
+}
+ ''');
+ final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+ final stop = await client.hitBreakpoint(testFile, breakpointLine);
+ final mapVariables = await client.expectLocalVariable(
+ stop.threadId!,
+ expectedName: 'myVariable',
+ expectedDisplayString: 'Map (1 item)',
+ expectedVariables: '''
+ 0: DateTime -> _Exception
+ ''',
+ );
+
+ // Check one of the MapAssociation variables has the correct Key/Value
+ // inside.
+ expect(mapVariables.variables, hasLength(1));
+ final mapVariable = mapVariables.variables[0];
+ expect(mapVariable.variablesReference, isPositive);
+ final variables = await client.expectVariables(
+ mapVariable.variablesReference,
+ // We don't expect an evaluteName because the key is not a simple type.
+ '''
+ key: DateTime
+ value: _Exception
+ ''',
+ );
+
+ // Check the Key can be drilled into.
+ expect(variables.variables, hasLength(2));
+ final keyVariable = variables.variables[0];
+ expect(keyVariable.variablesReference, isPositive);
+ await client.expectVariables(
+ keyVariable.variablesReference,
+ '''
+ isUtc: false
+ ''',
+ );
+
+ // Check the Value can be drilled into.
+ final valueVariable = variables.variables[1];
+ expect(valueVariable.variablesReference, isPositive);
+ await client.expectVariables(
+ valueVariable.variablesReference,
+ '''
+ message: "my error"
+ ''',
+ );
+ });
// These tests can be slow due to starting up the external server process.
}, timeout: Timeout.none);
}
diff --git a/pkg/dds/test/dap/integration/test_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index 52e8892..0099e3f 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -605,7 +605,7 @@
/// A helper that finds a named variable in the Variables scope for the top
/// frame and asserts its child variables (fields/getters/etc) match.
- Future<void> expectLocalVariable(
+ Future<VariablesResponseBody> expectLocalVariable(
int threadId, {
required String expectedName,
required String expectedDisplayString,
@@ -622,7 +622,7 @@
);
final topFrame = stack.stackFrames.first;
- final variablesScope = await getValidScope(topFrame.id, 'Variables');
+ final variablesScope = await getValidScope(topFrame.id, 'Locals');
final variables =
await getValidVariables(variablesScope.variablesReference);
final expectedVariable = variables.variables
@@ -632,7 +632,7 @@
expect(expectedVariable.value, equals(expectedDisplayString));
// Check the child fields.
- await expectVariables(
+ return expectVariables(
expectedVariable.variablesReference,
expectedVariables,
start: start,
@@ -713,7 +713,7 @@
final type = v.type;
final presentationHint = v.presentationHint;
- buffer.write(v.name);
+ buffer.write('${v.name}: $value');
if (evaluateName != null) {
buffer.write(', eval: $evaluateName');
}
@@ -723,12 +723,11 @@
if (namedVariables != null) {
buffer.write(', $namedVariables named items');
}
- buffer.write(': $value');
if (type != null) {
- buffer.write(' ($type)');
+ buffer.write(', $type');
}
if (presentationHint != null) {
- buffer.write(' ($presentationHint)');
+ buffer.write(', $presentationHint');
}
return buffer.toString();
diff --git a/pkg/status_file/bin/lint.dart b/pkg/status_file/bin/lint.dart
index 17b84f47..fd9f6a0a 100644
--- a/pkg/status_file/bin/lint.dart
+++ b/pkg/status_file/bin/lint.dart
@@ -56,10 +56,11 @@
}
void lintStdIn({bool checkForDisjunctions = false}) {
- List<String> strings = <String>[];
- String readString;
+ var strings = <String>[];
try {
- while (null != (readString = stdin.readLineSync())) {
+ while (true) {
+ var readString = stdin.readLineSync();
+ if (readString == null) break;
strings.add(readString);
}
} on StdinException {
@@ -126,7 +127,7 @@
print("");
return true;
}
- if (statusFile.path != null && statusFile.path.isNotEmpty) {
+ if (statusFile.path.isNotEmpty) {
print("${statusFile.path}");
}
var errors = lintingErrors.toList();
diff --git a/pkg/status_file/bin/remove_non_essential_entries.dart b/pkg/status_file/bin/remove_non_essential_entries.dart
index 409fbcd..8f481c0 100644
--- a/pkg/status_file/bin/remove_non_essential_entries.dart
+++ b/pkg/status_file/bin/remove_non_essential_entries.dart
@@ -33,8 +33,9 @@
import 'package:args/args.dart';
import 'package:status_file/canonical_status_file.dart';
import 'package:status_file/expectation.dart';
+import 'package:status_file/src/expression.dart';
-StatusEntry filterExpectations(
+StatusEntry? filterExpectations(
StatusEntry entry, List<Expectation> expectationsToKeep) {
List<Expectation> remaining = entry.expectations
.where(
@@ -45,14 +46,14 @@
: StatusEntry(entry.path, entry.lineNumber, remaining, entry.comment);
}
-Map<String, Map<int, String>> issues;
+late Map<String, Map<int, String>> issues;
String getIssueState(String project, int issue) {
- Map projectIssues = issues[project];
+ var projectIssues = issues[project];
if (projectIssues == null) {
throw "Cannot find project $project, not one of {${issues.keys.join(",")}}";
}
- String state = projectIssues[issue] ?? "";
+ var state = projectIssues[issue] ?? "";
return "\t$state";
}
@@ -61,7 +62,7 @@
// sorted by issue number then timestamp ascending.
//
// The first line is expected to contain the field names and is skipped.
-void parseIssueFile() async {
+Future<void> parseIssueFile() async {
issues = {};
String issuesLog = await File("issues.log").readAsString();
List<String> lines = issuesLog.split("\n");
@@ -91,13 +92,13 @@
];
String getIssueText(String comment, bool resolveState) {
- int issue;
- String prefix;
- String project;
- for (RegExp pattern in co19IssuePatterns) {
- Match match = pattern.firstMatch(comment);
+ int? issue;
+ late String prefix;
+ late String project;
+ for (var pattern in co19IssuePatterns) {
+ var match = pattern.firstMatch(comment);
if (match != null) {
- issue = int.tryParse(match[1]);
+ issue = int.tryParse(match[1]!);
if (issue != null) {
prefix = "https://github.com/dart-lang/co19/issues/";
project = "dart-lang/co19";
@@ -106,10 +107,10 @@
}
}
if (issue == null) {
- for (RegExp pattern in sdkIssuePatterns) {
- Match match = pattern.firstMatch(comment);
+ for (var pattern in sdkIssuePatterns) {
+ var match = pattern.firstMatch(comment);
if (match != null) {
- issue = int.tryParse(match[1]);
+ issue = int.tryParse(match[1]!);
if (issue != null) {
prefix = "https://dartbug.com/";
project = "dart-lang/sdk";
@@ -119,7 +120,7 @@
}
}
if (issue != null) {
- String state = resolveState ? getIssueState(project, issue) : "";
+ var state = resolveState ? getIssueState(project, issue) : "";
return "$prefix$issue$state";
} else {
return "";
@@ -143,7 +144,7 @@
entries.add(entry);
hasStatusEntries = true;
} else if (entry is StatusEntry) {
- StatusEntry newEntry = entry;
+ StatusEntry? newEntry = entry;
if (entry.comment == null) {
newEntry = filterExpectations(entry, expectationsToKeep);
} else if (removeComments) {
@@ -155,8 +156,9 @@
String expectations = entry.expectations.toString();
// Remove '[' and ']'.
expectations = expectations.substring(1, expectations.length - 1);
- String conditionPrefix =
- section.condition != null ? "${section.condition}" : "";
+ String conditionPrefix = section.condition != Expression.always
+ ? "${section.condition}"
+ : "";
String issueText = await getIssueText(comment, resolveIssueState);
String statusLine = "$conditionPrefix\t$testName\t$expectations"
"\t$comment\t$issueText";
@@ -171,16 +173,18 @@
throw "Unknown entry type ${entry.runtimeType}";
}
}
- bool isDefaultSection = section.condition == null;
+
+ var isDefaultSection = section.condition == Expression.always;
if (hasStatusEntries ||
(isDefaultSection && section.sectionHeaderComments.isNotEmpty)) {
- StatusSection newSection =
+ var newSection =
StatusSection(section.condition, -1, section.sectionHeaderComments);
newSection.entries.addAll(entries);
sections.add(newSection);
}
}
- StatusFile newStatusFile = StatusFile(statusFile.path);
+
+ var newStatusFile = StatusFile(statusFile.path);
newStatusFile.sections.addAll(sections);
return newStatusFile;
}
diff --git a/pkg/status_file/lib/canonical_status_file.dart b/pkg/status_file/lib/canonical_status_file.dart
index 6f7f134..c20924f 100644
--- a/pkg/status_file/lib/canonical_status_file.dart
+++ b/pkg/status_file/lib/canonical_status_file.dart
@@ -95,7 +95,7 @@
/// Checks if [currentLine] is a comment and returns the first regular
/// expression match, or null otherwise.
- Match commentEntryMatch(int currentLine) {
+ Match? commentEntryMatch(int currentLine) {
if (currentLine < 1 || currentLine > lines.length) {
return null;
}
@@ -104,7 +104,7 @@
/// Finds a section header on [currentLine] if the line is in range of
/// [lines].
- Match sectionHeaderMatch(int currentLine) {
+ Match? sectionHeaderMatch(int currentLine) {
if (currentLine < 1 || currentLine > lines.length) {
return null;
}
@@ -178,14 +178,14 @@
// The current section whose rules are being parsed. Initialized to an
// implicit section that matches everything.
StatusSection section =
- new StatusSection(null, -1, implicitSectionHeaderComments);
+ new StatusSection(Expression.always, -1, implicitSectionHeaderComments);
section.entries.addAll(entries);
sections.add(section);
for (; _lineCount <= lines.length; _lineCount++) {
var line = lines[_lineCount - 1];
- fail(String message, [List<String> errors]) {
+ fail(String message, [List<String>? errors]) {
throw new SyntaxError(_shortPath, _lineCount, line, message, errors);
}
@@ -199,7 +199,7 @@
var match = _sectionPattern.firstMatch(line);
if (match != null) {
try {
- var condition = Expression.parse(match[1].trim());
+ var condition = Expression.parse(match[1]!.trim());
section =
new StatusSection(condition, _lineCount, sectionHeaderComments);
sections.add(section);
@@ -214,10 +214,10 @@
// If it is in a new entry we should add to the current section.
match = _entryPattern.firstMatch(line);
if (match != null) {
- var path = match[1].trim();
+ var path = match[1]!.trim();
var expectations = <Expectation>[];
// split expectations
- match[2].split(",").forEach((name) {
+ match[2]!.split(",").forEach((name) {
try {
expectations.add(Expectation.find(name.trim()));
} on ArgumentError {
@@ -229,7 +229,7 @@
.add(new StatusEntry(path, _lineCount, expectations, null));
} else {
section.entries.add(new StatusEntry(
- path, _lineCount, expectations, new Comment(match[3])));
+ path, _lineCount, expectations, new Comment(match[3]!)));
}
continue;
}
@@ -264,8 +264,6 @@
/// Throws a [SyntaxError] on the first found error.
void validate(Environment environment) {
for (var section in sections) {
- if (section.condition == null) continue;
-
var errors = <String>[];
section.condition.validate(environment, errors);
@@ -299,8 +297,8 @@
class StatusSection {
/// The expression that determines when this section is applied.
///
- /// May be `null` for paths that appear before any section header in the file.
- /// In that case, the section always applies.
+ /// Will be [Expression.always] for paths that appear before any section
+ /// header in the file. In that case, the section always applies.
final Expression condition;
/// The one-based line number where the section appears in the file.
@@ -311,8 +309,7 @@
final List<Entry> sectionHeaderComments;
/// Returns true if this section should apply in the given [environment].
- bool isEnabled(Environment environment) =>
- condition == null || condition.evaluate(environment);
+ bool isEnabled(Environment environment) => condition.evaluate(environment);
bool isEmpty() => !entries.any((entry) => entry is StatusEntry);
@@ -322,8 +319,8 @@
String toString() {
var buffer = new StringBuffer();
sectionHeaderComments.forEach(buffer.writeln);
- if (condition != null) {
- buffer.writeln("[ ${condition} ]");
+ if (condition != Expression.always) {
+ buffer.writeln("[ $condition ]");
}
entries.forEach(buffer.writeln);
return buffer.toString();
@@ -336,10 +333,10 @@
Comment(this._comment);
/// Returns the issue number embedded in [comment] or `null` if there is none.
- int issueNumber(String comment) {
+ int? issueNumber(String comment) {
var match = _issuePattern.firstMatch(comment);
if (match == null) return null;
- return int.parse(match[1]);
+ return int.parse(match[1]!);
}
@override
@@ -377,7 +374,7 @@
class StatusEntry extends Entry {
final String path;
final List<Expectation> expectations;
- final Comment comment;
+ final Comment? comment;
StatusEntry(this.path, lineNumber, this.expectations, this.comment)
: super(lineNumber);
diff --git a/pkg/status_file/lib/environment.dart b/pkg/status_file/lib/environment.dart
index 89e20ac..602c2a0 100644
--- a/pkg/status_file/lib/environment.dart
+++ b/pkg/status_file/lib/environment.dart
@@ -12,5 +12,5 @@
void validate(String name, String value, List<String> errors);
/// Looks up the value of the variable with [name].
- String lookUp(String name);
+ String? lookUp(String name);
}
diff --git a/pkg/status_file/lib/expectation.dart b/pkg/status_file/lib/expectation.dart
index 93979fb..6a0223c 100644
--- a/pkg/status_file/lib/expectation.dart
+++ b/pkg/status_file/lib/expectation.dart
@@ -206,17 +206,17 @@
}
final String _name;
- final Expectation _group;
+ final Expectation? _group;
/// Whether this expectation is a test outcome. If not, it's a "meta marker".
final bool isOutcome;
- Expectation._(this._name, {Expectation group, bool isMeta: false})
+ Expectation._(this._name, {Expectation? group, bool isMeta: false})
: _group = group,
isOutcome = !isMeta;
bool canBeOutcomeOf(Expectation expectation) {
- var outcome = this;
+ Expectation? outcome = this;
if (outcome == ignore) return true;
while (outcome != null) {
diff --git a/pkg/status_file/lib/src/disjunctive.dart b/pkg/status_file/lib/src/disjunctive.dart
index b94f17d..b768f69 100644
--- a/pkg/status_file/lib/src/disjunctive.dart
+++ b/pkg/status_file/lib/src/disjunctive.dart
@@ -102,14 +102,14 @@
});
var combinedMinSets = _combineMinSets(
clauses.map((e) => [new LogicExpression.and(e)]).toList(), []);
- List<List<Expression>> minCover = _findMinCover(combinedMinSets, []);
+ List<List<LogicExpression>> minCover = _findMinCover(combinedMinSets, []);
var finalOperands = minCover.map((minSet) => _reduceMinSet(minSet)).toList();
return new LogicExpression.or(finalOperands).normalize();
}
/// Computes all assignments of literals that make the [expression] evaluate to
/// true.
-List<Expression> _satisfiableMinTerms(Expression expression) {
+List<Expression>? _satisfiableMinTerms(Expression expression) {
var variables = _getVariables(expression);
bool hasNotSatisfiableAssignment = false;
List<Expression> satisfiableTerms = <Expression>[];
@@ -142,7 +142,7 @@
/// which the [variables] was found.
class TruthTableEnvironment extends Environment {
final List<Expression> variables;
- int configuration;
+ int configuration = -1;
TruthTableEnvironment(this.variables);
@@ -172,10 +172,10 @@
/// Combines [minSets] recursively as long as possible. Prime implicants (those
/// that cannot be reduced further) are kept track of in [primeImplicants]. When
/// finished the function returns all combined min sets.
-List<List<Expression>> _combineMinSets(
- List<List<Expression>> minSets, List<List<Expression>> primeImplicants) {
+List<List<LogicExpression>> _combineMinSets(List<List<LogicExpression>> minSets,
+ List<List<LogicExpression>> primeImplicants) {
List<List<LogicExpression>> combined = <List<LogicExpression>>[];
- var addedInThisIteration = new Set<List<Expression>>();
+ var addedInThisIteration = new Set<List<LogicExpression>>();
for (var i = 0; i < minSets.length; i++) {
var minSet = minSets[i];
var combinedMinSet = false;
@@ -262,8 +262,9 @@
/// minimum set cover is NP-hard, and we are not trying to be really cleaver
/// here. The implicants that cover only a single truth assignment can be
/// directly added to [cover].
-List<List<Expression>> _findMinCover(
- List<List<Expression>> primaryImplicants, List<List<Expression>> cover) {
+List<List<LogicExpression>> _findMinCover(
+ List<List<LogicExpression>> primaryImplicants,
+ List<List<LogicExpression>> cover) {
var minCover = primaryImplicants.toList()..addAll(cover);
if (cover.isEmpty) {
var allImplicants = primaryImplicants.toList();
@@ -332,9 +333,9 @@
/// Finds the first occurrence of [expressionToFind] in [expressions] or
/// returns null.
-Expression _findFirst<Expression>(
+Expression? _findFirst<Expression>(
expressionToFind, List<Expression> expressions) {
- return expressions.firstWhere(
+ return expressions.cast<Expression?>().firstWhere(
(otherExpression) => expressionToFind.compareTo(otherExpression) == 0,
orElse: () => null);
}
diff --git a/pkg/status_file/lib/src/expression.dart b/pkg/status_file/lib/src/expression.dart
index 16a5bbd..9baf6aa 100644
--- a/pkg/status_file/lib/src/expression.dart
+++ b/pkg/status_file/lib/src/expression.dart
@@ -9,6 +9,11 @@
/// A parsed Boolean expression AST.
abstract class Expression implements Comparable<Expression> {
+ /// An expression that always evaluates to true.
+ ///
+ /// Used for the section at the top of a status file with no expression.
+ static const Expression always = _AlwaysExpression();
+
/// Parses Boolean expressions in a .status file for Dart.
///
/// The grammar is:
@@ -26,6 +31,8 @@
static Expression parse(String expression) =>
new _ExpressionParser(expression).parse();
+ const Expression();
+
/// Validates that this expression does not contain any invalid uses of
/// variables.
///
@@ -111,6 +118,29 @@
}
}
+/// An expression that always evaluates to true.
+///
+/// Used for the implicit section at the top of a status file that always
+/// matches.
+class _AlwaysExpression extends Expression {
+ const _AlwaysExpression();
+
+ @override
+ int _compareToMyType(covariant _AlwaysExpression other) => 0;
+
+ @override
+ int get _typeComparison => 0;
+
+ @override
+ bool evaluate(Environment environment) => true;
+
+ @override
+ Expression normalize() => this;
+
+ @override
+ void validate(Environment environment, List<String> errors) {}
+}
+
/// Tests whether a given variable is or is not equal some literal value, as in:
/// ```
/// $variable == someValue
@@ -156,7 +186,7 @@
// Comparisons come before variables so that "$compiler == ..." and
// "$runtime == ..." appear on the left in status expressions.
- int get _typeComparison => 0;
+ int get _typeComparison => 1;
String toString() => "\$${left.name} ${negate ? '!=' : '=='} $right";
}
@@ -203,7 +233,7 @@
return _compareBool(negate, other.negate);
}
- int get _typeComparison => 1;
+ int get _typeComparison => 2;
String toString() => "${negate ? "!" : ""}\$${variable.name}";
}
@@ -237,7 +267,7 @@
}
}
- Expression normalize() {
+ LogicExpression normalize() {
// Normalize the order of the clauses. Since there is no short-circuiting,
// a || b means the same as b || a. Picking a standard order lets us
// identify and collapse identical expressions that only differ by clause
@@ -281,7 +311,7 @@
return 0;
}
- int get _typeComparison => 2;
+ int get _typeComparison => 3;
String toString() {
String parenthesize(Expression operand) {
@@ -365,7 +395,7 @@
"Expected identifier in expression, got ${_scanner.current}");
}
- var left = new Variable(_scanner.current);
+ var left = new Variable(_scanner.current!);
_scanner.advance();
if (!negate &&
@@ -378,7 +408,7 @@
"Expected value in expression, got ${_scanner.current}");
}
- var right = _scanner.advance();
+ var right = _scanner.advance()!;
return new ComparisonExpression(left, right, isNotEquals);
} else {
return new VariableExpression(left, negate: negate);
@@ -401,7 +431,7 @@
/// The token strings being iterated.
final Iterator<String> tokenIterator;
- String current;
+ String? current;
_Scanner(String expression) : tokenIterator = tokenize(expression).iterator {
advance();
@@ -414,7 +444,7 @@
return _tokenPattern
.allMatches(expression)
- .map((match) => match[0])
+ .map((match) => match[0]!)
.toList();
}
@@ -424,7 +454,7 @@
// All non-identifier tokens are one or two characters,
// so a longer token must be an identifier.
bool get isIdentifier =>
- current.length > 2 || _identifierPattern.hasMatch(current);
+ current!.length > 2 || _identifierPattern.hasMatch(current!);
/// If the current token is [token], consumes it and returns `true`.
bool match(String token) {
@@ -435,7 +465,7 @@
}
/// Consumes the current token and returns it.
- String advance() {
+ String? advance() {
var previous = current;
current = tokenIterator.moveNext() ? tokenIterator.current : null;
return previous;
diff --git a/pkg/status_file/lib/status_file.dart b/pkg/status_file/lib/status_file.dart
index debc28f..86a37a5 100644
--- a/pkg/status_file/lib/status_file.dart
+++ b/pkg/status_file/lib/status_file.dart
@@ -45,7 +45,7 @@
class StatusFile {
final String path;
final List<StatusSection> sections = [];
- final List<String> _comments = [];
+ final List<String?> _comments = [];
int _lineCount = 0;
@@ -58,13 +58,10 @@
var lines = new File(path).readAsLinesSync();
_comments.length = lines.length + 1;
- // The current section whose rules are being parsed.
- StatusSection section;
-
for (var line in lines) {
_lineCount++;
- fail(String message, [List<String> errors]) {
+ fail(String message, [List<String>? errors]) {
throw new SyntaxError(_shortPath, _lineCount, line, message, errors);
}
@@ -86,9 +83,8 @@
var match = _sectionPattern.firstMatch(source);
if (match != null) {
try {
- var condition = Expression.parse(match[1].trim());
- section = new StatusSection(condition, _lineCount);
- sections.add(section);
+ var condition = Expression.parse(match[1]!.trim());
+ sections.add(new StatusSection(condition, _lineCount));
} on FormatException {
fail("Status expression syntax error");
}
@@ -98,10 +94,10 @@
// Otherwise, it should be a new entry under the current section.
match = _entryPattern.firstMatch(source);
if (match != null) {
- var path = match[1].trim();
+ var path = match[1]!.trim();
// TODO(whesse): Handle test names ending in a wildcard (*).
var expectations = <Expectation>[];
- for (var name in match[2].split(",")) {
+ for (var name in match[2]!.split(",")) {
name = name.trim();
try {
expectations.add(Expectation.find(name));
@@ -114,12 +110,11 @@
// If we haven't found a section header yet, create an implicit section
// that matches everything.
- if (section == null) {
- section = new StatusSection(null, -1);
- sections.add(section);
+ if (sections.isEmpty) {
+ sections.add(new StatusSection(Expression.always, -1));
}
- section.entries
+ sections.last.entries
.add(new StatusEntry(path, _lineCount, expectations, issue));
continue;
}
@@ -138,8 +133,6 @@
// TODO(rnystrom): It would be more useful if it reported all of the errors
// instead of stopping on the first.
for (var section in sections) {
- if (section.condition == null) continue;
-
var errors = <String>[];
section.condition.validate(environment, errors);
@@ -158,11 +151,11 @@
}
/// Returns the issue number embedded in [comment] or `null` if there is none.
- int _issueNumber(String comment) {
+ int? _issueNumber(String comment) {
var match = _issuePattern.firstMatch(comment);
if (match == null) return null;
- return int.parse(match[1]);
+ return int.parse(match[1]!);
}
String toString() {
@@ -192,7 +185,7 @@
var lastLine = 0;
var needBlankLine = false;
- void writeLine(String text, int line) {
+ void writeLine(String? text, int line) {
var comment = _comments[line];
if (text == null && comment == null) {
// There's no comment on this line, so it's blank.
@@ -216,17 +209,15 @@
}
void writeText(String text, int line) {
- if (line != null) {
- while (++lastLine < line) {
- writeLine(null, lastLine);
- }
+ while (++lastLine < line) {
+ writeLine(null, lastLine);
}
writeLine(text, line);
}
for (var section in sections) {
- if (section.condition != null) {
+ if (section.condition != Expression.always) {
writeText("[ ${section.condition} ]", section.lineNumber);
}
@@ -254,8 +245,8 @@
class StatusSection {
/// The expression that determines when this section is applied.
///
- /// May be `null` for paths that appear before any section header in the file.
- /// In that case, the section always applies.
+ /// Will be [Expression.always] for paths that appear before any section
+ /// header in the file. In that case, the section always applies.
final Expression condition;
/// The one-based line number where the entry appears in the file.
@@ -264,8 +255,7 @@
final List<StatusEntry> entries = [];
/// Returns true if this section should apply in the given [environment].
- bool isEnabled(Environment environment) =>
- condition == null || condition.evaluate(environment);
+ bool isEnabled(Environment environment) => condition.evaluate(environment);
StatusSection(this.condition, this.lineNumber);
}
@@ -278,7 +268,7 @@
final int lineNumber;
final List<Expectation> expectations;
- final int issue;
+ final int? issue;
StatusEntry(this.path, this.lineNumber, this.expectations, this.issue);
}
@@ -289,7 +279,7 @@
final int lineNumber;
final String line;
final String message;
- final List<String> errors;
+ final List<String>? errors;
SyntaxError(this.file, this.lineNumber, this.line, this.message, this.errors);
@@ -298,10 +288,8 @@
buffer.writeln('$message in "$file" line $lineNumber:');
buffer.writeln(line);
- if (errors != null) {
- for (var error in errors) {
- buffer.writeln("- ${error.replaceAll('\n', '\n ')}");
- }
+ for (var error in errors ?? const []) {
+ buffer.writeln("- ${error.replaceAll('\n', '\n ')}");
}
return buffer.toString().trimRight();
diff --git a/pkg/status_file/lib/status_file_linter.dart b/pkg/status_file/lib/status_file_linter.dart
index d1282ab..7f1e29b 100644
--- a/pkg/status_file/lib/status_file_linter.dart
+++ b/pkg/status_file/lib/status_file_linter.dart
@@ -115,10 +115,9 @@
/// Checks that each section expression have been normalized.
Iterable<LintingError> lintNormalizedSection(StatusSection section) {
- if (section.condition == null) return const [];
var nonNormalized = section.condition.toString();
var normalized = section.condition.normalize().toString();
- if (section.condition.toString() != normalized) {
+ if (nonNormalized != normalized) {
return [
new LintingError(
section.lineNumber,
@@ -184,11 +183,11 @@
if (witness != null) {
return [
new LintingError(
- witness.second.lineNumber,
+ witness.second!.lineNumber,
"Section expressions are not correctly ordered in file. "
- "'${witness.first.condition}' on line ${witness.first.lineNumber} "
- "should come before '${witness.second.condition}' at line "
- "${witness.second.lineNumber}.")
+ "'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
+ "should come before '${witness.second!.condition}' at line "
+ "${witness.second!.lineNumber}.")
];
}
return [];
@@ -198,14 +197,12 @@
Iterable<LintingError> lintSectionHeaderDuplicates(
List<StatusSection> sections) {
var errors = <LintingError>[];
- var sorted = sections.where((section) => section.condition != null).toList()
+ var sorted = sections.toList()
..sort((a, b) => a.condition.compareTo(b.condition));
for (var i = 1; i < sorted.length; i++) {
var section = sorted[i];
var previousSection = sorted[i - 1];
- if (section.condition != null &&
- previousSection.condition != null &&
- section.condition.compareTo(previousSection.condition) == 0) {
+ if (section.condition.compareTo(previousSection.condition) == 0) {
errors.add(new LintingError(
section.lineNumber,
"The condition "
@@ -216,7 +213,7 @@
return errors;
}
-ListNotEqualWitness<T> _findNotEqualWitness<T>(List<T> first, List<T> second) {
+ListNotEqualWitness<T>? _findNotEqualWitness<T>(List<T> first, List<T> second) {
if (first.isEmpty && second.isEmpty) {
return null;
}
@@ -233,7 +230,7 @@
}
class ListNotEqualWitness<T> {
- final T first;
- final T second;
+ final T? first;
+ final T? second;
ListNotEqualWitness(this.first, this.second);
}
diff --git a/pkg/status_file/lib/status_file_normalizer.dart b/pkg/status_file/lib/status_file_normalizer.dart
index 71d90e3..73e1727 100644
--- a/pkg/status_file/lib/status_file_normalizer.dart
+++ b/pkg/status_file/lib/status_file_normalizer.dart
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:status_file/src/expression.dart';
+
import 'canonical_status_file.dart';
import 'dart:convert';
@@ -52,10 +54,9 @@
List<StatusSection> newSections = [];
// Copy over all sections and normalize all the expressions.
oldStatusFile.sections.forEach((section) {
- if (section.condition != null && section.isEmpty()) {
- return;
- }
- if (section.condition != null) {
+ if (section.condition != Expression.always) {
+ if (section.isEmpty()) return;
+
newSections.add(new StatusSection(section.condition.normalize(),
section.lineNumber, section.sectionHeaderComments)
..entries.addAll(section.entries));
@@ -63,23 +64,17 @@
newSections.add(section);
}
});
+
// Sort the headers
- newSections.sort((a, b) {
- if (a.condition == null) {
- return -1;
- } else if (b.condition == null) {
- return 1;
- }
- return a.condition.compareTo(b.condition);
- });
+ newSections.sort((a, b) => a.condition.compareTo(b.condition));
+
// See if we can combine section headers by simple comparison.
- StatusFile newStatusFile = new StatusFile(statusFile.path);
+ var newStatusFile = new StatusFile(statusFile.path);
newStatusFile.sections.add(newSections[0]);
for (var i = 1; i < newSections.length; i++) {
var previousSection = newSections[i - 1];
var currentSection = newSections[i];
- if (previousSection.condition != null &&
- previousSection.condition.compareTo(currentSection.condition) == 0) {
+ if (previousSection.condition.compareTo(currentSection.condition) == 0) {
newStatusFile.sections.last.entries.addAll(currentSection.entries);
} else {
newStatusFile.sections.add(currentSection);
diff --git a/pkg/status_file/pubspec.yaml b/pkg/status_file/pubspec.yaml
index ae78d40..1536b63 100644
--- a/pkg/status_file/pubspec.yaml
+++ b/pkg/status_file/pubspec.yaml
@@ -2,7 +2,7 @@
# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
- sdk: "^2.3.0"
+ sdk: "^2.12.0"
dependencies:
path: "^1.4.0"
args: "^1.4.4"
diff --git a/pkg/status_file/test/linter_test.dart b/pkg/status_file/test/linter_test.dart
index dcd6e06..f97b6ca 100644
--- a/pkg/status_file/test/linter_test.dart
+++ b/pkg/status_file/test/linter_test.dart
@@ -100,7 +100,7 @@
vm/tests: Skip # this comment is valid
""",
"Error at line 1: Expression contains '||'. Please split the expression "
- "into multiple separate sections.",
+ "into multiple separate sections.",
disjunctions: true);
}
@@ -118,7 +118,7 @@
a_test: Pass
""",
"Error at line 1: Test paths are not alphabetically ordered in "
- "section. a_test should come before vm/tests.");
+ "section. a_test should come before vm/tests.");
}
void testCheckForAlphabeticalOrderingOfPaths_okOrdering() {
@@ -139,7 +139,7 @@
xyz_test: Skip
""",
"Error at line 1: The status entry 'a_test: Pass' is duplicated on lines "
- "2 and 3.");
+ "2 and 3.");
}
void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeCompiler() {
@@ -148,7 +148,7 @@
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$compiler == dart2js "
- r"&& $runtime == ff' but was '$runtime == ff && $compiler == dart2js'.");
+ r"&& $runtime == ff' but was '$runtime == ff && $compiler == dart2js'.");
}
void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeMode() {
@@ -157,7 +157,7 @@
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$mode == debug && "
- r"$runtime == ff' but was '$runtime == ff && $mode == debug'.");
+ r"$runtime == ff' but was '$runtime == ff && $mode == debug'.");
}
void testCheckForCorrectOrderingInSections_invalidSystemBeforeMode() {
@@ -166,7 +166,7 @@
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$mode == debug && "
- r"$system == win' but was '$system == win && $mode == debug'.");
+ r"$system == win' but was '$system == win && $mode == debug'.");
}
void testCheckForCorrectOrderingInSections_invalidStrongBeforeKernel() {
@@ -175,7 +175,7 @@
a_test: Pass
""",
r"Error at line 1: Condition expression should be '!$kernel && !$strong' "
- r"but was '!$strong && !$kernel'.");
+ r"but was '!$strong && !$kernel'.");
}
void testCheckForCorrectOrderingInSections_invalidOrdering() {
@@ -184,8 +184,8 @@
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$builder_tag == "
- r"strong && $compiler == dart2js && !$browser' but was "
- r"'$compiler == dart2js && $builder_tag == strong && !$browser'.");
+ r"strong && $compiler == dart2js && !$browser' but was "
+ r"'$compiler == dart2js && $builder_tag == strong && !$browser'.");
}
void testCheckForCorrectOrderingInSections_okOrdering() {
@@ -203,8 +203,8 @@
a_test: Pass
""",
r"Error at line 1: Section expressions are not correctly ordered in file."
- r" '$compiler == dart2js' on line 4 should come before '$runtime == ff' "
- r"at line 1.");
+ r" '$compiler == dart2js' on line 4 should come before '$runtime == ff' "
+ r"at line 1.");
}
void checkLintNormalizedSection_invalidAlphabeticalOrderingVariableArguments() {
@@ -216,8 +216,8 @@
a_test: Pass
""",
r"Error at line 1: Section expressions are not correctly ordered in file."
- r" '$runtime == chrome' on line 4 should come before '$runtime == ff' at "
- r"line 1.");
+ r" '$runtime == chrome' on line 4 should come before '$runtime == ff' at "
+ r"line 1.");
}
void checkLintNormalizedSection_invalidOrderingWithNotEqual() {
@@ -233,8 +233,8 @@
a_test: Pass
""",
r"Error at line 4: Section expressions are not correctly ordered in file."
- r" '$runtime == ff' on line 7 should come before '$runtime != ff' at "
- r"line 4.");
+ r" '$runtime == ff' on line 7 should come before '$runtime != ff' at "
+ r"line 4.");
}
void checkLintNormalizedSection_invalidOrderingWithNegation() {
@@ -251,7 +251,7 @@
""",
r"Error at line 4: Section expressions are not correctly ordered in file."
- r" '$checked' on line 7 should come before '!$checked' at line 4.");
+ r" '$checked' on line 7 should come before '!$checked' at line 4.");
}
void checkLintNormalizedSection_correctOrdering() {
@@ -285,5 +285,5 @@
a_test: Pass
""",
r"Error at line 4: The condition '!$browser' is duplicated on lines 1 "
- r"and 4.");
+ r"and 4.");
}
diff --git a/pkg/status_file/test/normalize_test.dart b/pkg/status_file/test/normalize_test.dart
index 92869c3..27c5985 100644
--- a/pkg/status_file/test/normalize_test.dart
+++ b/pkg/status_file/test/normalize_test.dart
@@ -69,8 +69,8 @@
"$entriesInNormalized. Those two numbers are not the same.");
}
for (var section in original.sections) {
- section.entries.where((entry) => entry is StatusEntry).forEach((entry) =>
- findInStatusFile(normalized, entry, section.condition?.normalize(),
+ section.entries.whereType<StatusEntry>().forEach((entry) =>
+ findInStatusFile(normalized, entry, section.condition.normalize(),
warnOnDuplicateHeader: warnOnDuplicateHeader));
}
}
@@ -87,12 +87,7 @@
{bool warnOnDuplicateHeader = false}) {
int foundEntryPosition = -1;
for (var section in statusFile.sections) {
- if (section.condition == null && condition != null ||
- section.condition != null && condition == null) {
- continue;
- }
- if (section.condition != null &&
- section.condition.normalize().compareTo(condition) != 0) {
+ if (section.condition.normalize().compareTo(condition) != 0) {
continue;
}
var matchingEntries = section.entries
diff --git a/pkg/status_file/test/repo_status_files_test.dart b/pkg/status_file/test/repo_status_files_test.dart
index 9f14fe7..fccd043 100644
--- a/pkg/status_file/test/repo_status_files_test.dart
+++ b/pkg/status_file/test/repo_status_files_test.dart
@@ -19,8 +19,8 @@
if (!entry.path.endsWith(".status")) continue;
try {
new StatusFile.read(entry.path);
- } catch (err) {
- Expect.fail("Could not parse '${entry.path}'.\n$err");
+ } catch (err, stack) {
+ Expect.fail("Could not parse '${entry.path}'.\n$err\n$stack");
}
}
}
diff --git a/pkg/status_file/test/status_expression_dnf_test.dart b/pkg/status_file/test/status_expression_dnf_test.dart
index 475b664..546395a 100644
--- a/pkg/status_file/test/status_expression_dnf_test.dart
+++ b/pkg/status_file/test/status_expression_dnf_test.dart
@@ -55,13 +55,13 @@
// https://en.wikipedia.org/wiki/Quine%E2%80%93McCluskey_algorithm
shouldDnfTo(
r"$a && !$b && !$c && !$d || $a && !$b && !$c && $d || "
- r"$a && !$b && $c && !$d || $a && !$b && $c && $d",
+ r"$a && !$b && $c && !$d || $a && !$b && $c && $d",
r"$a && !$b");
shouldDnfTo(
r"!$a && $b && !$c && !$d || $a && !$b && !$c && !$d || "
- r"$a && !$b && $c && !$d || $a && !$b && $c && $d || $a && $b && !$c && !$d ||"
- r" $a && $b && $c && $d || $a && !$b && !$c && $d || $a && $b && $c && !$d",
+ r"$a && !$b && $c && !$d || $a && !$b && $c && $d || $a && $b && !$c && !$d ||"
+ r" $a && $b && $c && $d || $a && !$b && !$c && $d || $a && $b && $c && !$d",
r"$a && !$b || $a && $c || $b && !$c && !$d");
// Test that an expression is converted to dnf and minified correctly.
diff --git a/pkg/status_file/test/status_expression_test.dart b/pkg/status_file/test/status_expression_test.dart
index 7fb224a..b79d57b 100644
--- a/pkg/status_file/test/status_expression_test.dart
+++ b/pkg/status_file/test/status_expression_test.dart
@@ -17,7 +17,7 @@
}
/// Looks up the value of the variable with [name].
- String lookUp(String name) => _values[name];
+ String? lookUp(String name) => _values[name];
operator []=(String key, String value) => _values[key] = value;
}
@@ -33,8 +33,8 @@
}
void testExpression() {
- var expression = Expression
- .parse(r" $mode == debug && ($arch == chromium || $arch == dartc) ");
+ var expression = Expression.parse(
+ r" $mode == debug && ($arch == chromium || $arch == dartc) ");
Expect.equals(r"$mode == debug && ($arch == chromium || $arch == dartc)",
expression.toString());
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index 438cec8..355d3ba 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -131,6 +131,10 @@
sources = [
"elf_loader.cc",
"elf_loader.h",
+ "virtual_memory.h",
+ "virtual_memory_fuchsia.cc",
+ "virtual_memory_posix.cc",
+ "virtual_memory_win.cc",
]
deps = invoker.deps
}
diff --git a/runtime/bin/elf_loader.cc b/runtime/bin/elf_loader.cc
index 43b152d..a5ff8c0 100644
--- a/runtime/bin/elf_loader.cc
+++ b/runtime/bin/elf_loader.cc
@@ -2,13 +2,9 @@
// 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 <bin/elf_loader.h>
-#include <bin/file.h>
-#include <platform/elf.h>
-#include <platform/globals.h>
-#include <vm/cpu.h>
-#include <vm/virtual_memory.h>
+#include "bin/elf_loader.h"
+#include "platform/globals.h"
#if defined(DART_HOST_OS_FUCHSIA)
#include <sys/mman.h>
#endif
@@ -16,6 +12,10 @@
#include <memory>
#include <utility>
+#include "bin/file.h"
+#include "bin/virtual_memory.h"
+#include "platform/elf.h"
+
namespace dart {
namespace bin {
@@ -369,7 +369,6 @@
bool LoadedElf::LoadSegments() {
// Calculate the total amount of virtual memory needed.
uword total_memory = 0;
- uword maximum_alignment = PageSize();
for (uword i = 0; i < header_.num_program_headers; ++i) {
const dart::elf::ProgramHeader header = program_table_[i];
@@ -381,14 +380,12 @@
total_memory);
CHECK_ERROR(Utils::IsPowerOfTwo(header.alignment),
"Alignment must be a power of two.");
- maximum_alignment =
- Utils::Maximum(maximum_alignment, static_cast<uword>(header.alignment));
}
total_memory = Utils::RoundUp(total_memory, PageSize());
- base_.reset(VirtualMemory::AllocateAligned(
- total_memory, /*alignment=*/maximum_alignment,
- /*is_executable=*/false, "dart-compiled-image"));
+ base_.reset(VirtualMemory::Allocate(total_memory,
+ /*is_executable=*/false,
+ "dart-compiled-image"));
CHECK_ERROR(base_ != nullptr, "Could not reserve virtual memory.");
for (uword i = 0; i < header_.num_program_headers; ++i) {
diff --git a/runtime/bin/virtual_memory.h b/runtime/bin/virtual_memory.h
new file mode 100644
index 0000000..3ec25ed
--- /dev/null
+++ b/runtime/bin/virtual_memory.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef RUNTIME_BIN_VIRTUAL_MEMORY_H_
+#define RUNTIME_BIN_VIRTUAL_MEMORY_H_
+
+#include "platform/allocation.h"
+#include "platform/globals.h"
+
+namespace dart {
+namespace bin {
+
+class VirtualMemory {
+ public:
+ enum Protection {
+ kNoAccess,
+ kReadOnly,
+ kReadWrite,
+ kReadExecute,
+ kReadWriteExecute
+ };
+
+ // The reserved memory is unmapped on destruction.
+ ~VirtualMemory();
+
+ void release() {
+ address_ = nullptr;
+ size_ = 0;
+ }
+
+ uword start() const { return reinterpret_cast<uword>(address_); }
+ uword end() const { return reinterpret_cast<uword>(address_) + size_; }
+ void* address() const { return address_; }
+ intptr_t size() const { return size_; }
+
+ // Changes the protection of the virtual memory area.
+ static void Protect(void* address, intptr_t size, Protection mode);
+ void Protect(Protection mode) { return Protect(address(), size(), mode); }
+
+ // Reserves and commits a virtual memory segment with size. If a segment of
+ // the requested size cannot be allocated, NULL is returned.
+ static VirtualMemory* Allocate(intptr_t size,
+ bool is_executable,
+ const char* name);
+
+ static void Init() { page_size_ = CalculatePageSize(); }
+
+ // Returns the cached page size. Use only if Init() has been called.
+ static intptr_t PageSize() {
+ ASSERT(page_size_ != 0);
+ return page_size_;
+ }
+
+ private:
+ static intptr_t CalculatePageSize();
+
+ // These constructors are only used internally when reserving new virtual
+ // spaces. They do not reserve any virtual address space on their own.
+ VirtualMemory(void* address, size_t size) : address_(address), size_(size) {}
+
+ void* address_;
+ size_t size_;
+
+ static uword page_size_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(VirtualMemory);
+};
+
+} // namespace bin
+} // namespace dart
+
+#endif // RUNTIME_BIN_VIRTUAL_MEMORY_H_
diff --git a/runtime/bin/virtual_memory_fuchsia.cc b/runtime/bin/virtual_memory_fuchsia.cc
new file mode 100644
index 0000000..2ba42b5
--- /dev/null
+++ b/runtime/bin/virtual_memory_fuchsia.cc
@@ -0,0 +1,110 @@
+// 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.
+
+#include "platform/globals.h"
+#if defined(DART_HOST_OS_FUCHSIA)
+
+#include "bin/virtual_memory.h"
+
+#include <zircon/process.h>
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
+
+#include "platform/assert.h"
+#include "platform/utils.h"
+
+namespace dart {
+namespace bin {
+
+uword VirtualMemory::page_size_ = 0;
+
+intptr_t VirtualMemory::CalculatePageSize() {
+ const intptr_t page_size = getpagesize();
+ ASSERT(page_size != 0);
+ ASSERT(Utils::IsPowerOfTwo(page_size));
+ return page_size;
+}
+
+VirtualMemory* VirtualMemory::Allocate(intptr_t size,
+ bool is_executable,
+ const char* name) {
+ ASSERT(Utils::IsAligned(size, page_size_));
+ zx_handle_t vmar = zx_vmar_root_self();
+ zx_handle_t vmo = ZX_HANDLE_INVALID;
+ zx_status_t status = zx_vmo_create(size, 0u, &vmo);
+ if (status != ZX_OK) {
+ return nullptr;
+ }
+
+ if (name != nullptr) {
+ zx_object_set_property(vmo, ZX_PROP_NAME, name, strlen(name));
+ }
+
+ if (is_executable) {
+ // Add ZX_RIGHT_EXECUTE permission to VMO, so it can be mapped
+ // into memory as executable (now or later).
+ status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo);
+ if (status != ZX_OK) {
+ zx_handle_close(vmo);
+ return nullptr;
+ }
+ }
+
+ const zx_vm_option_t region_options =
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE |
+ (is_executable ? ZX_VM_PERM_EXECUTE : 0);
+ uword base;
+ status = zx_vmar_map(vmar, region_options, 0, vmo, 0u, size, &base);
+ zx_handle_close(vmo);
+ if (status != ZX_OK) {
+ return nullptr;
+ }
+
+ return new VirtualMemory(reinterpret_cast<void*>(base), size);
+}
+
+VirtualMemory::~VirtualMemory() {
+ if (address_ != nullptr) {
+ zx_status_t status = zx_vmar_unmap(
+ zx_vmar_root_self(), reinterpret_cast<uword>(address_), size_);
+ if (status != ZX_OK) {
+ FATAL("zx_vmar_unmap failed: %s\n", zx_status_get_string(status));
+ }
+ }
+}
+
+void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) {
+ const uword start_address = reinterpret_cast<uword>(address);
+ const uword end_address = start_address + size;
+ const uword page_address = Utils::RoundDown(start_address, PageSize());
+ uint32_t prot = 0;
+ switch (mode) {
+ case kNoAccess:
+ prot = 0;
+ break;
+ case kReadOnly:
+ prot = ZX_VM_PERM_READ;
+ break;
+ case kReadWrite:
+ prot = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
+ break;
+ case kReadExecute:
+ prot = ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE;
+ break;
+ case kReadWriteExecute:
+ prot = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE;
+ break;
+ }
+ zx_status_t status = zx_vmar_protect(zx_vmar_root_self(), prot, page_address,
+ end_address - page_address);
+ if (status != ZX_OK) {
+ FATAL("zx_vmar_protect(0x%lx, 0x%lx) failed: %s\n", page_address,
+ end_address - page_address, zx_status_get_string(status));
+ }
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(DART_HOST_OS_FUCHSIA)
diff --git a/runtime/bin/virtual_memory_posix.cc b/runtime/bin/virtual_memory_posix.cc
new file mode 100644
index 0000000..3618564
--- /dev/null
+++ b/runtime/bin/virtual_memory_posix.cc
@@ -0,0 +1,110 @@
+// 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.
+
+#include "platform/globals.h"
+#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \
+ defined(DART_HOST_OS_MACOS)
+
+#include "bin/virtual_memory.h"
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "platform/assert.h"
+#include "platform/utils.h"
+
+namespace dart {
+namespace bin {
+
+// standard MAP_FAILED causes "error: use of old-style cast" as it
+// defines MAP_FAILED as ((void *) -1)
+#undef MAP_FAILED
+#define MAP_FAILED reinterpret_cast<void*>(-1)
+
+uword VirtualMemory::page_size_ = 0;
+
+intptr_t VirtualMemory::CalculatePageSize() {
+ const intptr_t page_size = getpagesize();
+ ASSERT(page_size != 0);
+ ASSERT(Utils::IsPowerOfTwo(page_size));
+ return page_size;
+}
+
+VirtualMemory* VirtualMemory::Allocate(intptr_t size,
+ bool is_executable,
+ const char* name) {
+ ASSERT(Utils::IsAligned(size, PageSize()));
+
+ const int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
+
+ int map_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#if (defined(DART_HOST_OS_MACOS) && !defined(DART_HOST_OS_IOS))
+ if (is_executable && IsAtLeastOS10_14()) {
+ map_flags |= MAP_JIT;
+ }
+#endif // defined(DART_HOST_OS_MACOS)
+
+ // Some 64-bit microarchitectures store only the low 32-bits of targets as
+ // part of indirect branch prediction, predicting that the target's upper bits
+ // will be same as the call instruction's address. This leads to misprediction
+ // for indirect calls crossing a 4GB boundary. We ask mmap to place our
+ // generated code near the VM binary to avoid this.
+ void* hint = is_executable ? reinterpret_cast<void*>(&Allocate) : nullptr;
+ void* address = mmap(hint, size, prot, map_flags, -1, 0);
+ if (address == MAP_FAILED) {
+ return nullptr;
+ }
+ return new VirtualMemory(address, size);
+}
+
+VirtualMemory::~VirtualMemory() {
+ if (address_ != nullptr) {
+ if (munmap(address_, size_) != 0) {
+ int error = errno;
+ const int kBufferSize = 1024;
+ char error_buf[kBufferSize];
+ FATAL("munmap error: %d (%s)", error,
+ Utils::StrError(error, error_buf, kBufferSize));
+ }
+ }
+}
+
+void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) {
+ uword start_address = reinterpret_cast<uword>(address);
+ uword end_address = start_address + size;
+ uword page_address = Utils::RoundDown(start_address, PageSize());
+ int prot = 0;
+ switch (mode) {
+ case kNoAccess:
+ prot = PROT_NONE;
+ break;
+ case kReadOnly:
+ prot = PROT_READ;
+ break;
+ case kReadWrite:
+ prot = PROT_READ | PROT_WRITE;
+ break;
+ case kReadExecute:
+ prot = PROT_READ | PROT_EXEC;
+ break;
+ case kReadWriteExecute:
+ prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+ break;
+ }
+ if (mprotect(reinterpret_cast<void*>(page_address),
+ end_address - page_address, prot) != 0) {
+ int error = errno;
+ const int kBufferSize = 1024;
+ char error_buf[kBufferSize];
+ FATAL("mprotect error: %d (%s)", error,
+ Utils::StrError(error, error_buf, kBufferSize));
+ }
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \
+ // defined(DART_HOST_OS_MACOS)
diff --git a/runtime/bin/virtual_memory_win.cc b/runtime/bin/virtual_memory_win.cc
new file mode 100644
index 0000000..b6075ed
--- /dev/null
+++ b/runtime/bin/virtual_memory_win.cc
@@ -0,0 +1,79 @@
+// 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.
+
+#include "platform/globals.h"
+#if defined(DART_HOST_OS_WINDOWS)
+
+#include "bin/virtual_memory.h"
+
+#include "platform/assert.h"
+#include "platform/utils.h"
+
+namespace dart {
+namespace bin {
+
+uword VirtualMemory::page_size_ = 0;
+
+intptr_t VirtualMemory::CalculatePageSize() {
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ const intptr_t page_size = info.dwPageSize;
+ ASSERT(page_size != 0);
+ ASSERT(Utils::IsPowerOfTwo(page_size));
+ return page_size;
+}
+
+VirtualMemory* VirtualMemory::Allocate(intptr_t size,
+ bool is_executable,
+ const char* name) {
+ ASSERT(Utils::IsAligned(size, PageSize()));
+ int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
+ void* address = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, prot);
+ if (address == nullptr) {
+ return nullptr;
+ }
+ return new VirtualMemory(address, size);
+}
+
+VirtualMemory::~VirtualMemory() {
+ if (address_ != nullptr) {
+ if (VirtualFree(address_, 0, MEM_RELEASE) == 0) {
+ FATAL("VirtualFree failed: Error code %d\n", GetLastError());
+ }
+ }
+}
+
+void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) {
+ uword start_address = reinterpret_cast<uword>(address);
+ uword end_address = start_address + size;
+ uword page_address = Utils::RoundDown(start_address, PageSize());
+ DWORD prot = 0;
+ switch (mode) {
+ case kNoAccess:
+ prot = PAGE_NOACCESS;
+ break;
+ case kReadOnly:
+ prot = PAGE_READONLY;
+ break;
+ case kReadWrite:
+ prot = PAGE_READWRITE;
+ break;
+ case kReadExecute:
+ prot = PAGE_EXECUTE_READ;
+ break;
+ case kReadWriteExecute:
+ prot = PAGE_EXECUTE_READWRITE;
+ break;
+ }
+ DWORD old_prot = 0;
+ if (VirtualProtect(reinterpret_cast<void*>(page_address),
+ end_address - page_address, prot, &old_prot) == 0) {
+ FATAL("VirtualProtect failed %d\n", GetLastError());
+ }
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(DART_HOST_OS_WINDOWS)
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 43f124f..aa86706 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -21,7 +21,6 @@
#include "vm/message_handler.h"
#include "vm/message_snapshot.h"
#include "vm/object.h"
-#include "vm/object_graph_copy.h"
#include "vm/object_store.h"
#include "vm/port.h"
#include "vm/resolver.h"
@@ -108,28 +107,13 @@
const Dart_Port destination_port_id = port.Id();
const bool can_send_any_object = isolate->origin_id() == port.origin_id();
-
- if (ApiObjectConverter::CanConvert(obj.ptr())) {
- PortMap::PostMessage(
- Message::New(destination_port_id, obj.ptr(), Message::kNormalPriority));
- } else {
- const bool same_group = FLAG_enable_isolate_groups &&
- PortMap::IsReceiverInThisIsolateGroup(
- destination_port_id, isolate->group());
- if (same_group) {
- const auto& copy = Object::Handle(CopyMutableObjectGraph(obj));
- auto handle = isolate->group()->api_state()->AllocatePersistentHandle();
- handle->set_ptr(copy.ptr());
- std::unique_ptr<Message> message(
- new Message(destination_port_id, handle, Message::kNormalPriority));
- PortMap::PostMessage(std::move(message));
- } else {
- // TODO(turnidge): Throw an exception when the return value is false?
- PortMap::PostMessage(WriteMessage(can_send_any_object, obj,
- destination_port_id,
- Message::kNormalPriority));
- }
- }
+ const bool same_group =
+ FLAG_enable_isolate_groups && PortMap::IsReceiverInThisIsolateGroup(
+ destination_port_id, isolate->group());
+ // TODO(turnidge): Throw an exception when the return value is false?
+ PortMap::PostMessage(WriteMessage(can_send_any_object, same_group, obj,
+ destination_port_id,
+ Message::kNormalPriority));
return Object::null();
}
@@ -581,11 +565,17 @@
}
ObjectPtr IsolateSpawnState::BuildArgs(Thread* thread) {
- return DeserializeMessage(thread, serialized_args_.get());
+ const Object& result =
+ Object::Handle(DeserializeMessage(thread, serialized_args_.get()));
+ serialized_args_.reset();
+ return result.ptr();
}
ObjectPtr IsolateSpawnState::BuildMessage(Thread* thread) {
- return DeserializeMessage(thread, serialized_message_.get());
+ const Object& result =
+ Object::Handle(DeserializeMessage(thread, serialized_message_.get()));
+ serialized_message_.reset();
+ return result.ptr();
}
static void ThrowIsolateSpawnException(const String& message) {
@@ -810,7 +800,8 @@
{
// If parent isolate died, we ignore the fact that we cannot notify it.
PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
- message, state_->parent_port(),
+ /* same_group */ false, message,
+ state_->parent_port(),
Message::kNormalPriority));
}
@@ -877,21 +868,20 @@
bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id();
+ const bool in_new_isolate_group = newIsolateGroup.value();
// We first try to serialize the message. In case the message is not
// serializable this will throw an exception.
SerializedObjectBuffer message_buffer;
- {
- message_buffer.set_message(WriteMessage(
- /* can_send_any_object */ true, message, ILLEGAL_PORT,
- Message::kNormalPriority));
- }
+ message_buffer.set_message(WriteMessage(
+ /* can_send_any_object */ true,
+ /* same_group */ FLAG_enable_isolate_groups && !in_new_isolate_group,
+ message, ILLEGAL_PORT, Message::kNormalPriority));
const char* utf8_package_config =
packageConfig.IsNull() ? NULL : String2UTF8(packageConfig);
const char* utf8_debug_name =
debugName.IsNull() ? NULL : String2UTF8(debugName);
- const bool in_new_isolate_group = newIsolateGroup.value();
std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
port.Id(), isolate->origin_id(), String2UTF8(script_uri), func,
@@ -967,13 +957,14 @@
SerializedObjectBuffer arguments_buffer;
SerializedObjectBuffer message_buffer;
{
- arguments_buffer.set_message(WriteMessage(/* can_send_any_object */ false,
- args, ILLEGAL_PORT,
- Message::kNormalPriority));
+ arguments_buffer.set_message(WriteMessage(
+ /* can_send_any_object */ false,
+ /* same_group */ false, args, ILLEGAL_PORT, Message::kNormalPriority));
}
{
message_buffer.set_message(WriteMessage(/* can_send_any_object */ false,
- message, ILLEGAL_PORT,
+ /* same_group */ false, message,
+ ILLEGAL_PORT,
Message::kNormalPriority));
}
@@ -1047,8 +1038,9 @@
// Ensure message writer (and it's resources, e.g. forwarding tables) are
// cleaned up before handling interrupts.
{
- PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false, msg,
- port.Id(), Message::kOOBPriority));
+ PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
+ /* same_group */ false, msg, port.Id(),
+ Message::kOOBPriority));
}
// Drain interrupts before running so any IMMEDIATE operations on the current
diff --git a/runtime/lib/vmservice.cc b/runtime/lib/vmservice.cc
index 51ff5ea..f3dba9a 100644
--- a/runtime/lib/vmservice.cc
+++ b/runtime/lib/vmservice.cc
@@ -62,7 +62,7 @@
// Serialize message.
// TODO(turnidge): Throw an exception when the return value is false?
bool result = PortMap::PostMessage(WriteMessage(
- /* can_send_any_object */ false, message, sp.Id(),
+ /* can_send_any_object */ false, /* same_group */ false, message, sp.Id(),
Message::kOOBPriority));
return Bool::Get(result).ptr();
#else
diff --git a/runtime/vm/benchmark_test.cc b/runtime/vm/benchmark_test.cc
index 2b5683b..2fe4811 100644
--- a/runtime/vm/benchmark_test.cc
+++ b/runtime/vm/benchmark_test.cc
@@ -519,8 +519,8 @@
for (intptr_t i = 0; i < kLoopCount; i++) {
StackZone zone(thread);
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, null_object, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ null_object, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
ReadMessage(thread, message.get());
@@ -541,8 +541,8 @@
for (intptr_t i = 0; i < kLoopCount; i++) {
StackZone zone(thread);
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, smi_object, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ smi_object, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
ReadMessage(thread, message.get());
@@ -565,8 +565,8 @@
for (intptr_t i = 0; i < kLoopCount; i++) {
StackZone zone(thread);
std::unique_ptr<Message> message = WriteMessage(
- /* can_send_any_object */ true, array_object, ILLEGAL_PORT,
- Message::kNormalPriority);
+ /* can_send_any_object */ true, /* same_group */ false, array_object,
+ ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
ReadMessage(thread, message.get());
@@ -598,8 +598,8 @@
for (intptr_t i = 0; i < kLoopCount; i++) {
StackZone zone(thread);
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, map, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ map, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
ReadMessage(thread, message.get());
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index dcfdb13..8acd172 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -2164,8 +2164,8 @@
const Object& object = Object::Handle(Z, Api::UnwrapHandle(handle));
return PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
- object, port_id,
- Message::kNormalPriority));
+ /* same_group */ false, object,
+ port_id, Message::kNormalPriority));
}
DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id) {
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 458cc40..35236ef 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -124,8 +124,8 @@
static std::unique_ptr<Message> SerializeMessage(Dart_Port dest_port,
const Instance& obj) {
- return WriteMessage(/* can_send_any_object */ false, obj, dest_port,
- Message::kNormalPriority);
+ return WriteMessage(/* can_send_any_object */ false, /* same_group */ false,
+ obj, dest_port, Message::kNormalPriority);
}
static std::unique_ptr<Message> SerializeMessage(Zone* zone,
@@ -1007,8 +1007,9 @@
element = Capability::New(capability);
msg.SetAt(2, element);
- PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false, msg,
- main_port(), Message::kOOBPriority));
+ PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
+ /* same_group */ false, msg, main_port(),
+ Message::kOOBPriority));
}
void IsolateGroup::set_object_store(ObjectStore* object_store) {
@@ -1327,38 +1328,7 @@
}
// Parse the message.
- Object& msg_obj = Object::Handle(zone);
- if (message->IsPersistentHandle()) {
- // msg_array = [
- // <message>,
- // <collection-lib-objects-to-rehash>,
- // <core-lib-objects-to-rehash>,
- // ]
- const auto& msg_array = Array::Handle(
- zone, Array::RawCast(message->persistent_handle()->ptr()));
- ASSERT(msg_array.Length() == 3);
- msg_obj = msg_array.At(0);
- if (msg_array.At(1) != Object::null()) {
- const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(1));
- auto& result = Object::Handle(zone);
- result = DartLibraryCalls::RehashObjectsInDartCollection(
- thread, objects_to_rehash);
- if (result.ptr() != Object::null()) {
- msg_obj = result.ptr();
- }
- }
- if (msg_array.At(2) != Object::null()) {
- const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(2));
- auto& result = Object::Handle(zone);
- result =
- DartLibraryCalls::RehashObjectsInDartCore(thread, objects_to_rehash);
- if (result.ptr() != Object::null()) {
- msg_obj = result.ptr();
- }
- }
- } else {
- msg_obj = ReadMessage(thread, message.get());
- }
+ Object& msg_obj = Object::Handle(zone, ReadMessage(thread, message.get()));
if (msg_obj.IsError()) {
// An error occurred while reading the message.
return ProcessUnhandledException(Error::Cast(msg_obj));
@@ -3306,8 +3276,8 @@
element = Smi::New(Isolate::kBeforeNextEventAction);
msg.SetAt(2, element);
std::unique_ptr<Message> message = WriteMessage(
- /* can_send_any_object */ false, msg, main_port(),
- Message::kOOBPriority);
+ /* can_send_any_object */ false, /* same_group */ false, msg,
+ main_port(), Message::kOOBPriority);
bool posted = PortMap::PostMessage(std::move(message));
ASSERT(posted);
}
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index c3ab4ec..e65fc1c 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -19,6 +19,7 @@
#include "vm/heap/weak_table.h"
#include "vm/longjump.h"
#include "vm/object.h"
+#include "vm/object_graph_copy.h"
#include "vm/object_store.h"
#include "vm/symbols.h"
#include "vm/type_testing_stubs.h"
@@ -3601,11 +3602,18 @@
}
std::unique_ptr<Message> WriteMessage(bool can_send_any_object,
+ bool same_group,
const Object& obj,
Dart_Port dest_port,
Message::Priority priority) {
if (ApiObjectConverter::CanConvert(obj.ptr())) {
return Message::New(dest_port, obj.ptr(), priority);
+ } else if (same_group) {
+ const Object& copy = Object::Handle(CopyMutableObjectGraph(obj));
+ auto handle =
+ IsolateGroup::Current()->api_state()->AllocatePersistentHandle();
+ handle->set_ptr(copy.ptr());
+ return std::make_unique<Message>(dest_port, handle, priority);
}
Thread* thread = Thread::Current();
@@ -3652,6 +3660,37 @@
ObjectPtr ReadMessage(Thread* thread, Message* message) {
if (message->IsRaw()) {
return message->raw_obj();
+ } else if (message->IsPersistentHandle()) {
+ // msg_array = [
+ // <message>,
+ // <collection-lib-objects-to-rehash>,
+ // <core-lib-objects-to-rehash>,
+ // ]
+ Zone* zone = thread->zone();
+ Object& msg_obj = Object::Handle(zone);
+ const auto& msg_array = Array::Handle(
+ zone, Array::RawCast(message->persistent_handle()->ptr()));
+ ASSERT(msg_array.Length() == 3);
+ msg_obj = msg_array.At(0);
+ if (msg_array.At(1) != Object::null()) {
+ const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(1));
+ auto& result = Object::Handle(zone);
+ result = DartLibraryCalls::RehashObjectsInDartCollection(
+ thread, objects_to_rehash);
+ if (result.ptr() != Object::null()) {
+ msg_obj = result.ptr();
+ }
+ }
+ if (msg_array.At(2) != Object::null()) {
+ const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(2));
+ auto& result = Object::Handle(zone);
+ result =
+ DartLibraryCalls::RehashObjectsInDartCore(thread, objects_to_rehash);
+ if (result.ptr() != Object::null()) {
+ msg_obj = result.ptr();
+ }
+ }
+ return msg_obj.ptr();
} else {
RELEASE_ASSERT(message->IsSnapshot());
MessageDeserializer deserializer(thread, message);
diff --git a/runtime/vm/message_snapshot.h b/runtime/vm/message_snapshot.h
index d06640e..be7eb8a 100644
--- a/runtime/vm/message_snapshot.h
+++ b/runtime/vm/message_snapshot.h
@@ -14,6 +14,7 @@
namespace dart {
std::unique_ptr<Message> WriteMessage(bool can_send_any_object,
+ bool same_group,
const Object& obj,
Dart_Port dest_port,
Message::Priority priority);
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index ea1edf5..76d65b6 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -562,7 +562,10 @@
Class::NumNativeFieldsOf(class_table_->At(cid)) != 0;
if (has_native_fields) {
exception_msg_ =
- "Illegal argument in isolate message: (object has native fields)";
+ OS::SCreate(zone_,
+ "Illegal argument in isolate message: (object extends "
+ "NativeWrapper - %s)",
+ Class::Handle(class_table_->At(cid)).ToCString());
return false;
}
return true;
@@ -576,17 +579,28 @@
}
switch (cid) {
+ HANDLE_ILLEGAL_CASE(FunctionType)
+ HANDLE_ILLEGAL_CASE(DynamicLibrary)
HANDLE_ILLEGAL_CASE(MirrorReference)
+ HANDLE_ILLEGAL_CASE(Pointer)
HANDLE_ILLEGAL_CASE(ReceivePort)
HANDLE_ILLEGAL_CASE(StackTrace)
HANDLE_ILLEGAL_CASE(UserTag)
- HANDLE_ILLEGAL_CASE(DynamicLibrary)
- HANDLE_ILLEGAL_CASE(Pointer)
+#define CASE(type) case kFfi##type##Cid:
+ CLASS_LIST_FFI(CASE)
+#undef CASE
+ exception_msg_ =
+ "Native objects (from dart:ffi) such as Pointers and "
+ "Structs cannot be passed between isolates.";
+ return false;
case kClosureCid: {
if (!Function::IsImplicitStaticClosureFunction(
Closure::FunctionOf(Closure::RawCast(object)))) {
- exception_msg_ =
- "Illegal argument in isolate message: (object is a closure)";
+ exception_msg_ = OS::SCreate(
+ zone_,
+ "Illegal argument in isolate message: (object is a closure - %s)",
+ Function::Handle(Closure::FunctionOf(Closure::RawCast(object)))
+ .ToCString());
return false;
}
ASSERT(Closure::ContextOf(Closure::RawCast(object)) == Object::null());
diff --git a/runtime/vm/service_isolate.cc b/runtime/vm/service_isolate.cc
index d840e7b..acc453f 100644
--- a/runtime/vm/service_isolate.cc
+++ b/runtime/vm/service_isolate.cc
@@ -92,8 +92,9 @@
sp, VM_SERVICE_SERVER_INFO_MESSAGE_ID, false /* ignored */,
Bool::Handle() /* ignored */));
ASSERT(!message.IsNull());
- PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false, message,
- port_, Message::kNormalPriority));
+ PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
+ /* same_group */ false, message, port_,
+ Message::kNormalPriority));
}
void ServiceIsolate::ControlWebServer(const SendPort& sp,
@@ -102,8 +103,9 @@
const Array& message = Array::Handle(MakeServerControlMessage(
sp, VM_SERVICE_WEB_SERVER_CONTROL_MESSAGE_ID, enable, silenceOutput));
ASSERT(!message.IsNull());
- PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false, message,
- port_, Message::kNormalPriority));
+ PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
+ /* same_group */ false, message, port_,
+ Message::kNormalPriority));
}
void ServiceIsolate::SetServerAddress(const char* address) {
@@ -228,7 +230,8 @@
name.ToCString(), Dart_GetMainPortId());
}
return PortMap::PostMessage(WriteMessage(
- /* can_send_any_object */ false, list, port_, Message::kNormalPriority));
+ /* can_send_any_object */ false, /* same_group */ false, list, port_,
+ Message::kNormalPriority));
}
bool ServiceIsolate::SendIsolateShutdownMessage() {
@@ -253,7 +256,8 @@
name.ToCString(), Dart_GetMainPortId());
}
return PortMap::PostMessage(WriteMessage(
- /* can_send_any_object */ false, list, port_, Message::kNormalPriority));
+ /* can_send_any_object */ false, /* same_group */ false, list, port_,
+ Message::kNormalPriority));
}
void ServiceIsolate::SendServiceExitMessage() {
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index 9c488ee..d42c2da 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -129,8 +129,8 @@
// Write snapshot with object content.
const Object& null_object = Object::Handle();
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, null_object, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ null_object, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
const Object& serialized_object =
@@ -151,8 +151,8 @@
// Write snapshot with object content.
const Smi& smi = Smi::Handle(Smi::New(124));
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, smi, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, smi,
+ ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
const Object& serialized_object =
@@ -174,8 +174,8 @@
// Write snapshot with object content.
const Smi& smi = Smi::Handle(Smi::New(-1));
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, smi, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, smi,
+ ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
const Object& serialized_object =
@@ -194,8 +194,8 @@
Dart_CObject* SerializeAndDeserializeMint(Zone* zone, const Mint& mint) {
// Write snapshot with object content.
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, mint, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, mint,
+ ILLEGAL_PORT, Message::kNormalPriority);
{
// Switch to a regular zone, where VM handle allocation is allowed.
@@ -265,8 +265,8 @@
// Write snapshot with object content.
const Double& dbl = Double::Handle(Double::New(101.29));
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, dbl, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, dbl,
+ ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
const Object& serialized_object =
@@ -288,7 +288,7 @@
// Write snapshot with true object.
const Bool& bl = Bool::True();
std::unique_ptr<Message> message = WriteMessage(
- /* can_send_any_object */ true, bl, ILLEGAL_PORT,
+ /* can_send_any_object */ true, /* same_group */ false, bl, ILLEGAL_PORT,
Message::kNormalPriority);
// Read object back from the snapshot.
@@ -313,7 +313,7 @@
// Write snapshot with false object.
const Bool& bl = Bool::False();
std::unique_ptr<Message> message = WriteMessage(
- /* can_send_any_object */ true, bl, ILLEGAL_PORT,
+ /* can_send_any_object */ true, /* same_group */ false, bl, ILLEGAL_PORT,
Message::kNormalPriority);
// Read object back from the snapshot.
@@ -334,8 +334,8 @@
// Write snapshot with object content.
const Capability& capability = Capability::Handle(Capability::New(12345));
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, capability, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ capability, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
Capability& obj = Capability::Handle();
@@ -357,8 +357,8 @@
{ \
const Object& before = Object::Handle(object); \
std::unique_ptr<Message> message = \
- WriteMessage(/* can_send_any_object */ true, before, ILLEGAL_PORT, \
- Message::kNormalPriority); \
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, \
+ before, ILLEGAL_PORT, Message::kNormalPriority); \
const Object& after = Object::Handle(ReadMessage(thread, message.get())); \
EXPECT(before.ptr() == after.ptr()); \
}
@@ -384,8 +384,8 @@
// Write snapshot with object content.
String& str = String::Handle(String::New(cstr));
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, str, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, str,
+ ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
String& serialized_str = String::Handle();
@@ -425,8 +425,8 @@
array.SetAt(i, smi);
}
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, array, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ array, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
Array& serialized_array = Array::Handle();
@@ -458,8 +458,8 @@
array.SetAt(i, smi);
}
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, array, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ array, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
Array& serialized_array = Array::Handle();
@@ -537,8 +537,8 @@
const int kArrayLength = 0;
Array& array = Array::Handle(Array::New(kArrayLength));
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, array, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ array, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
Array& serialized_array = Array::Handle();
@@ -563,8 +563,8 @@
typed_data.SetUint8(i, i);
}
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, typed_data, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ typed_data, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
TypedData& serialized_typed_data = TypedData::Handle();
@@ -593,8 +593,8 @@
array.Set##darttype((i * scale), i); \
} \
std::unique_ptr<Message> message = \
- WriteMessage(/* can_send_any_object */ true, array, ILLEGAL_PORT, \
- Message::kNormalPriority); \
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, \
+ array, ILLEGAL_PORT, Message::kNormalPriority); \
TypedData& serialized_array = TypedData::Handle(); \
serialized_array ^= ReadMessage(thread, message.get()); \
for (int i = 0; i < kArrayLength; i++) { \
@@ -613,8 +613,8 @@
reinterpret_cast<uint8_t*>(data), length)); \
intptr_t scale = array.ElementSizeInBytes(); \
std::unique_ptr<Message> message = \
- WriteMessage(/* can_send_any_object */ true, array, ILLEGAL_PORT, \
- Message::kNormalPriority); \
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false, \
+ array, ILLEGAL_PORT, Message::kNormalPriority); \
ExternalTypedData& serialized_array = ExternalTypedData::Handle(); \
serialized_array ^= ReadMessage(thread, message.get()); \
for (int i = 0; i < length; i++) { \
@@ -655,8 +655,8 @@
TypedData& typed_data = TypedData::Handle(
TypedData::New(kTypedDataUint8ArrayCid, kTypedDataLength));
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ true, typed_data, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ true, /* same_group */ false,
+ typed_data, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot.
TypedData& serialized_typed_data = TypedData::Handle();
@@ -772,8 +772,8 @@
Object& obj = Object::Handle(Api::UnwrapHandle(result));
// Serialize the object into a message.
- return WriteMessage(/* can_send_any_object */ false, obj, ILLEGAL_PORT,
- Message::kNormalPriority);
+ return WriteMessage(/* can_send_any_object */ false, /* same_group */ false,
+ obj, ILLEGAL_PORT, Message::kNormalPriority);
}
static void CheckString(Dart_Handle dart_string, const char* expected) {
@@ -781,8 +781,8 @@
String& str = String::Handle();
str ^= Api::UnwrapHandle(dart_string);
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ false, str, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ false, /* same_group */ false, str,
+ ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot into a C structure.
ApiNativeScope scope;
@@ -798,8 +798,8 @@
String& str = String::Handle();
str ^= Api::UnwrapHandle(dart_string);
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ false, str, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ false, /* same_group */ false, str,
+ ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot into a C structure.
ApiNativeScope scope;
@@ -902,8 +902,8 @@
Smi& smi = Smi::Handle();
smi ^= Api::UnwrapHandle(smi_result);
std::unique_ptr<Message> message =
- WriteMessage(/* can_send_any_object */ false, smi, ILLEGAL_PORT,
- Message::kNormalPriority);
+ WriteMessage(/* can_send_any_object */ false, /* same_group */ false,
+ smi, ILLEGAL_PORT, Message::kNormalPriority);
// Read object back from the snapshot into a C structure.
ApiNativeScope scope;
diff --git a/runtime/vm/virtual_memory.h b/runtime/vm/virtual_memory.h
index c674d1c..dc9b2d7 100644
--- a/runtime/vm/virtual_memory.h
+++ b/runtime/vm/virtual_memory.h
@@ -76,13 +76,6 @@
static VirtualMemory* ForImagePage(void* pointer, uword size);
- void release() {
- // Make sure no pages would be leaked.
- const uword size_ = size();
- ASSERT(address() == reserved_.pointer() && size_ == reserved_.size());
- reserved_ = MemoryRegion(nullptr, 0);
- }
-
private:
static intptr_t CalculatePageSize();
diff --git a/runtime/vm/virtual_memory_fuchsia.cc b/runtime/vm/virtual_memory_fuchsia.cc
index 7f7347d..3eb51e8 100644
--- a/runtime/vm/virtual_memory_fuchsia.cc
+++ b/runtime/vm/virtual_memory_fuchsia.cc
@@ -7,8 +7,6 @@
#include "vm/virtual_memory.h"
-#include <sys/mman.h>
-#include <unistd.h>
#include <zircon/process.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
@@ -160,6 +158,7 @@
if (status != ZX_OK) {
LOG_ERR("zx_vmo_replace_as_executable() failed: %s\n",
zx_status_get_string(status));
+ zx_handle_close(vmo);
return NULL;
}
}
@@ -173,6 +172,7 @@
if (status != ZX_OK) {
LOG_ERR("zx_vmar_map(%u, 0x%lx, 0x%lx) failed: %s\n", region_options, base,
size, zx_status_get_string(status));
+ zx_handle_close(vmo);
return NULL;
}
void* region_ptr = reinterpret_cast<void*>(base);
diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc
index 14ff79d..2291d5e 100644
--- a/runtime/vm/virtual_memory_posix.cc
+++ b/runtime/vm/virtual_memory_posix.cc
@@ -80,17 +80,16 @@
page_size_ = CalculatePageSize();
#if defined(DART_COMPRESSED_POINTERS)
+ ASSERT(compressed_heap_ == nullptr);
+ compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
if (compressed_heap_ == nullptr) {
- compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
- if (compressed_heap_ == nullptr) {
- int error = errno;
- const int kBufferSize = 1024;
- char error_buf[kBufferSize];
- FATAL("Failed to reserve region for compressed heap: %d (%s)", error,
- Utils::StrError(error, error_buf, kBufferSize));
- }
- VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
+ int error = errno;
+ const int kBufferSize = 1024;
+ char error_buf[kBufferSize];
+ FATAL("Failed to reserve region for compressed heap: %d (%s)", error,
+ Utils::StrError(error, error_buf, kBufferSize));
}
+ VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
#endif // defined(DART_COMPRESSED_POINTERS)
#if defined(DUAL_MAPPING_SUPPORTED)
diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc
index 2e380c0..79eee52 100644
--- a/runtime/vm/virtual_memory_win.cc
+++ b/runtime/vm/virtual_memory_win.cc
@@ -56,14 +56,13 @@
page_size_ = CalculatePageSize();
#if defined(DART_COMPRESSED_POINTERS)
+ ASSERT(compressed_heap_ == nullptr);
+ compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
if (compressed_heap_ == nullptr) {
- compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
- if (compressed_heap_ == nullptr) {
- int error = GetLastError();
- FATAL("Failed to reserve region for compressed heap: %d", error);
- }
- VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
+ int error = GetLastError();
+ FATAL("Failed to reserve region for compressed heap: %d", error);
}
+ VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
#endif // defined(DART_COMPRESSED_POINTERS)
}
diff --git a/tests/lib/isolate/fast_copy_during_initial_message_test.dart b/tests/lib/isolate/fast_copy_during_initial_message_test.dart
new file mode 100644
index 0000000..375de09
--- /dev/null
+++ b/tests/lib/isolate/fast_copy_during_initial_message_test.dart
@@ -0,0 +1,37 @@
+// 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.
+
+// VMOptions=--enable-isolate-groups --enable-fast-object-copy
+
+import "dart:isolate";
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+echo(message) {
+ var string = message[0] as String;
+ var replyPort = message[1] as SendPort;
+ replyPort.send(string);
+}
+
+main() {
+ asyncStart();
+
+ // This string is constructed at runtime, so it is not const and won't be
+ // identical because of canonicalization. It will only be identical if it is
+ // sent by pointer.
+ var sentString = "xyz" * 2;
+
+ var port;
+ port = new RawReceivePort((message) {
+ var receivedString = message as String;
+
+ Expect.identical(sentString, receivedString);
+
+ port.close();
+ asyncEnd();
+ });
+
+ Isolate.spawn(echo, [sentString, port.sendPort]);
+}
diff --git a/tests/lib_2/isolate/fast_copy_during_initial_message_test.dart b/tests/lib_2/isolate/fast_copy_during_initial_message_test.dart
new file mode 100644
index 0000000..15c3978
--- /dev/null
+++ b/tests/lib_2/isolate/fast_copy_during_initial_message_test.dart
@@ -0,0 +1,39 @@
+// 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 = 2.9
+
+// VMOptions=--enable-isolate-groups --enable-fast-object-copy
+
+import "dart:isolate";
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+echo(message) {
+ var string = message[0] as String;
+ var replyPort = message[1] as SendPort;
+ replyPort.send(string);
+}
+
+main() {
+ asyncStart();
+
+ // This string is constructed at runtime, so it is not const and won't be
+ // identical because of canonicalization. It will only be identical if it is
+ // sent by pointer.
+ var sentString = "xyz" * 2;
+
+ var port;
+ port = new RawReceivePort((message) {
+ var receivedString = message as String;
+
+ Expect.identical(sentString, receivedString);
+
+ port.close();
+ asyncEnd();
+ });
+
+ Isolate.spawn(echo, [sentString, port.sendPort]);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 5d291c9..3cfedd8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 10
+PRERELEASE 11
PRERELEASE_PATCH 0
\ No newline at end of file