Version 2.17.1
* Cherry-pick 9fa681712f6184565ab3e6b9aec11ab09be93594 to stable
* Cherry-pick e418026d74b6c666a7fcb30278a2b10ed97fdb29 to stable
* Cherry-pick 4e520ec7a7ceb903da0c4ec04da7eeb635524748 to stable
* Cherry-pick d2e2c95cb6397fdf4e07b351ae50d1fa85e421e1 to stable
* Cherry-pick 9fb27f9842cd7bb3f2a1c4c84c9934f1952bed83 to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fefe254..ce4c26d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,16 @@
-## 2.17.0
+## 2.17.1 - 2022-05-18
+
+This is a patch release that fixes:
+
+- an analyzer plugin crash (issue [#48682][]).
+- Dart FFI support for `late` `Finalizable` variables (issue [#49024]).
+- `dart compile` on macOS 10.15 (issue [#49010][]).
+
+[#48682]: https://github.com/dart-lang/sdk/issues/48682
+[#49024]: https://github.com/dart-lang/sdk/issues/49024
+[#49010]: https://github.com/dart-lang/sdk/issues/49010
+
+## 2.17.0 - 2022-05-11
### Language
@@ -151,6 +163,24 @@
- Add `ref=` and `[]=` methods to the `StructPointer` and `UnionPointer`
extensions. They copy a compound instance into a native memory region.
+- Add `AbiSpecificInteger`s for common C types:
+ - `char`
+ - `unsigned char`
+ - `signed char`
+ - `short`
+ - `unsigned short`
+ - `int`
+ - `unsigned int`
+ - `long`
+ - `unsigned long`
+ - `long long`
+ - `unsigned long long`
+ - `uintptr_t`
+ - `size_t`
+ - `wchar_t`
+- Add `NativeFinalizer` which can potentially detect when objects are
+ "garbage collected". `NativeFinalizer`s run native code where `dart:core`'s
+ `Finalizer`s run Dart code on finalization.
#### `dart:html`
@@ -305,6 +335,11 @@
error with an existing stack trace, instead of creating
a new stack trace.
+#### `dart:ffi`
+
+- Add `Abi` and `AbiSpecificInteger`. These enable specifying integers which
+ have different sizes/signs per ABI (hardware and OS combination).
+
#### `dart:io`
- **Security advisory**
@@ -747,6 +782,10 @@
Code catching the class should move to catching `Error` instead
(or, for integers, check first for whether it's dividing by zero).
+#### `dart:ffi`
+
+- Add `Bool` native type.
+
#### `dart:io`
- **Breaking change** [#46875](https://github.com/dart-lang/sdk/issues/46875):
@@ -1051,8 +1090,9 @@
#### `dart:ffi`
-- Adds the `DynamicLibrary.providesSymbol` function to check whether a symbol is
+- Add the `DynamicLibrary.providesSymbol` function to check whether a symbol is
available in a dynamic library.
+- Add `Union` native type for interacting with unions in native memory.
#### `dart:html`
@@ -1349,6 +1389,11 @@
- Added `serverWebSocketUri` property to `ServiceProtocolInfo`.
+#### `dart:ffi`
+
+- Add `Packed` for interacting with packed structs in native memory.
+- Add `Array` for interacting with structs with inline arrays.
+
### Dart VM
### Tools
@@ -1489,6 +1534,26 @@
- Add `Set.unmodifiable()` constructor, which allows users to create
unmodifiable `Set` instances.
+#### `dart:ffi`
+
+- **Breaking change** [#44621][]: Invocations with a generic `T` of `sizeOf<T>`,
+ `Pointer<T>.elementAt()`, `Pointer<T extends Struct>.ref`, and
+ `Pointer<T extends Struct>[]` are being deprecated in the current stable
+ release (2.12), and are planned to be fully removed in the following stable
+ release (2.13). Consequently, `allocate` in `package:ffi` will no longer be
+ able to invoke `sizeOf<T>` generically, and will be deprecated as well.
+ Instead, the `Allocator` it is introduced to `dart:ffi`, and also requires a
+ constant `T` on invocations. For migration notes see the breaking change
+ request.
+
+- **Breaking change** [#44622][]: Subtypes of `Struct` without any native member
+ are being deprecated in the current stable release (2.12), and are planned to
+ be fully removed in the following stable release (2.13). Migrate opaque types
+ to extend `Opaque` rather than `Struct`.
+
+[#44621]: https://github.com/dart-lang/sdk/issues/44621
+[#44622]: https://github.com/dart-lang/sdk/issues/44622
+
#### `dart:io`
- `HttpRequest` now correctly follows HTTP 308 redirects
@@ -1524,26 +1589,6 @@
[#42312]: https://github.com/dart-lang/sdk/issues/42312
-### Foreign Function Interface (`dart:ffi`)
-
-- **Breaking change** [#44621][]: Invocations with a generic `T` of `sizeOf<T>`,
- `Pointer<T>.elementAt()`, `Pointer<T extends Struct>.ref`, and
- `Pointer<T extends Struct>[]` are being deprecated in the current stable
- release (2.12), and are planned to be fully removed in the following stable
- release (2.13). Consequently, `allocate` in `package:ffi` will no longer be
- able to invoke `sizeOf<T>` generically, and will be deprecated as well.
- Instead, the `Allocator` it is introduced to `dart:ffi`, and also requires a
- constant `T` on invocations. For migration notes see the breaking change
- request.
-
-- **Breaking change** [#44622][]: Subtypes of `Struct` without any native member
- are being deprecated in the current stable release (2.12), and are planned to
- be fully removed in the following stable release (2.13). Migrate opaque types
- to extend `Opaque` rather than `Struct`.
-
-[#44621]: https://github.com/dart-lang/sdk/issues/44621
-[#44622]: https://github.com/dart-lang/sdk/issues/44622
-
### Dart2JS
- Remove `--no-defer-class-types` and `--no-new-deferred-split`.
@@ -2167,6 +2212,19 @@
parameter provided in the constructor. This will be used by tooling to allow
for better filtering of timeline events.
+#### `dart:ffi`
+
+- **Breaking change**: Changed `Pointer.asFunction()` and
+ `DynamicLibrary.lookupFunction()` to extension methods. Invoking them
+ dynamically previously already threw an exception, so the runtime behavior
+ stays the same. However, the extension methods are only visible if `dart:ffi`
+ is imported directly. This breaks code where `dart:ffi` is not directly
+ imported. To fix, add:
+
+ ```dart
+ import 'dart:ffi';
+ ```
+
#### `dart:html`
- **Breaking change** [#39627][]: Changed the return type of several HTML native
@@ -2306,19 +2364,6 @@
- `Dart_IsNonNullableType()`
- `Dart_IsNullableType()`
-### Foreign Function Interface (`dart:ffi`)
-
-- **Breaking change**: Changed `Pointer.asFunction()` and
- `DynamicLibrary.lookupFunction()` to extension methods. Invoking them
- dynamically previously already threw an exception, so the runtime behavior
- stays the same. However, the extension methods are only visible if `dart:ffi`
- is imported directly. This breaks code where `dart:ffi` is not directly
- imported. To fix, add:
-
- ```dart
- import 'dart:ffi';
- ```
-
### Tools
#### Dart Dev Compiler (DDC)
@@ -2700,18 +2745,7 @@
- Added optional `parent` parameter to `TimelineTask` constructor to allow for
linking of asynchronous timeline events in the DevTools timeline view.
-#### `dart:io`
-
-- Added `enableTimelineLogging` property to `HttpClient` which, when enabled,
- will post HTTP connection and request information to the developer timeline
- for all `HttpClient` instances.
-
-### Dart VM
-
-- Added a new tool for AOT compiling Dart programs to native, self-contained
- executables. See https://dart.dev/tools/dart2native for additional details.
-
-### Foreign Function Interface (`dart:ffi`)
+#### `dart:ffi`
- **Breaking change**: The API now makes use of static extension members. Static
extension members enable the `dart:ffi` API to be more precise with types, and
@@ -2731,6 +2765,17 @@
- The dartanalyzer (commandline and IDEs) now reports `dart:ffi` static errors.
- Callbacks are now supported in AOT (ahead-of-time) compiled code.
+#### `dart:io`
+
+- Added `enableTimelineLogging` property to `HttpClient` which, when enabled,
+ will post HTTP connection and request information to the developer timeline
+ for all `HttpClient` instances.
+
+### Dart VM
+
+- Added a new tool for AOT compiling Dart programs to native, self-contained
+ executables. See https://dart.dev/tools/dart2native for additional details.
+
### Dart for the Web
#### Dart Dev Compiler (DDC)
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
index ae25ec1..7dc3e98 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
@@ -632,13 +632,8 @@
.getChildAssumingFolder(file_paths.dotDartTool)
.getChildAssumingFile(file_paths.packageConfigJson);
if (pubCommand != null) {
- var vmPath = Platform.executable;
- var pubPath = path.join(path.dirname(vmPath), 'pub');
- if (Platform.isWindows) {
- // Process.run requires the `.bat` suffix on Windows
- pubPath = '$pubPath.bat';
- }
- var result = Process.runSync(pubPath, <String>[pubCommand],
+ var result = Process.runSync(
+ Platform.executable, <String>['pub', pubCommand],
stderrEncoding: utf8,
stdoutEncoding: utf8,
workingDirectory: pluginFolder.path,
diff --git a/pkg/dart2native/lib/dart2native_macho.dart b/pkg/dart2native/lib/dart2native_macho.dart
index eefaf9d..1c45d13 100644
--- a/pkg/dart2native/lib/dart2native_macho.dart
+++ b/pkg/dart2native/lib/dart2native_macho.dart
@@ -265,6 +265,32 @@
return numWritten;
}
+class _MacOSVersion {
+ final int? _major;
+ final int? _minor;
+
+ static final _regexp = RegExp(r'Version (?<major>\d+).(?<minor>\d+)');
+ static const _parseFailure = 'Could not determine macOS version';
+
+ const _MacOSVersion._internal(this._major, this._minor);
+
+ static const _unknown = _MacOSVersion._internal(null, null);
+
+ factory _MacOSVersion() {
+ if (!Platform.isMacOS) return _unknown;
+ final match =
+ _regexp.matchAsPrefix(Platform.operatingSystemVersion) as RegExpMatch?;
+ if (match == null) return _unknown;
+ final minor = int.tryParse(match.namedGroup('minor')!);
+ final major = int.tryParse(match.namedGroup('major')!);
+ return _MacOSVersion._internal(major, minor);
+ }
+
+ bool get isValid => _major != null;
+ int get major => _major ?? (throw _parseFailure);
+ int get minor => _minor ?? (throw _parseFailure);
+}
+
// Writes an "appended" dart runtime + script snapshot file in a format
// compatible with MachO executables.
Future writeAppendedMachOExecutable(
@@ -312,17 +338,39 @@
await stream.close();
if (machOFile.hasCodeSignature) {
+ if (!Platform.isMacOS) {
+ throw 'Cannot sign MachO binary on non-macOS platform';
+ }
+
// After writing the modified file, we perform ad-hoc signing (no identity)
- // similar to the linker (the linker-signed option flag) to ensure that any
- // LC_CODE_SIGNATURE block has the correct CD hashes. This is necessary for
- // platforms where signature verification is always on (e.g., OS X on M1).
+ // to ensure that any LC_CODE_SIGNATURE block has the correct CD hashes.
+ // This is necessary for platforms where signature verification is always on
+ // (e.g., OS X on M1).
//
// We use the `-f` flag to force signature overwriting as the official
// Dart binaries (including dartaotruntime) are fully signed.
- final signingProcess = await Process.run(
- 'codesign', ['-f', '-o', 'linker-signed', '-s', '-', outputPath]);
+ final args = ['-f', '-s', '-', outputPath];
+
+ // If running on macOS >=11.0, then the linker-signed option flag can be
+ // used to create a signature that does not need to be force overridden.
+ final version = _MacOSVersion();
+ if (version.isValid && version.major >= 11) {
+ final signingProcess =
+ await Process.run('codesign', ['-o', 'linker-signed', ...args]);
+ if (signingProcess.exitCode == 0) {
+ return;
+ }
+ print('Failed to add a linker signed signature, '
+ 'adding a regular signature instead.');
+ }
+
+ // If that fails or we're running on an older or undetermined version of
+ // macOS, we fall back to signing without the linker-signed option flag.
+ // Thus, to sign the binary, the developer must force signature overwriting.
+ final signingProcess = await Process.run('codesign', args);
if (signingProcess.exitCode != 0) {
- print('Subcommand terminated with exit code ${signingProcess.exitCode}.');
+ print('Failed to replace the dartaotruntime signature, ');
+ print('subcommand terminated with exit code ${signingProcess.exitCode}.');
if (signingProcess.stdout.isNotEmpty) {
print('Subcommand stdout:');
print(signingProcess.stdout);
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index e5c15b0..0d497b4 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -187,7 +187,7 @@
expect(result.stdout, contains('2: foo'));
});
- test('Compile and run executable', () async {
+ Future<void> basicCompileTest() async {
final p = project(mainSrc: 'void main() { print("I love executables"); }');
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
final outFile = path.canonicalize(path.join(p.dirPath, 'lib', 'main.exe'));
@@ -213,7 +213,9 @@
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
expect(result.stdout, contains('I love executables'));
- }, skip: isRunningOnIA32);
+ }
+
+ test('Compile and run executable', basicCompileTest, skip: isRunningOnIA32);
test('Compile to executable disabled on IA32', () async {
final p = project(mainSrc: 'void main() { print("I love executables"); }');
@@ -1112,4 +1114,33 @@
expect(result.stderr, contains('Warning:'));
expect(result.exitCode, 0);
});
+
+ if (Platform.isMacOS) {
+ test('Compile and run executable from signed dartaotruntime', () async {
+ // Either the locally built dartaotruntime is already linker signed
+ // (on M1) or it is unsigned (on X64). For this test, sign the
+ // dartaotruntime executable with a non-linker signed adhoc signature,
+ // which won't cause issues with any other tests that use it. This
+ // ensures the code signing path in dart2native is exercised on X64
+ // (macOS <11.0), and also mimics the case for end users that are using
+ // the published Dart SDK (which is fully signed, not linker signed).
+ final Directory binDir = File(Platform.resolvedExecutable).parent;
+ final String originalRuntimePath =
+ path.join(binDir.path, 'dartaotruntime');
+ final codeSigningProcess = await Process.start('codesign', [
+ '-o',
+ 'runtime',
+ '-s',
+ '-',
+ originalRuntimePath,
+ ]);
+
+ final signingResult = await codeSigningProcess.exitCode;
+ expect(signingResult, 0);
+
+ // Now perform the same basic compile and run test with the signed
+ // dartaotruntime.
+ await basicCompileTest();
+ }, skip: isRunningOnIA32);
+ }
}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_late.dart b/pkg/vm/testcases/transformations/ffi/finalizable_late.dart
new file mode 100644
index 0000000..048ae94
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_late.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2022, 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.16
+
+import 'dart:ffi';
+
+class Foo implements Finalizable {}
+
+void main() {
+ late Foo foo;
+ // Generates a reachability fence between the constructor call and assignment.
+ // That reachability fence should not trigger a late initialization error.
+ foo = Foo();
+ print(foo);
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_late.dart.expect b/pkg/vm/testcases/transformations/ffi/finalizable_late.dart.expect
new file mode 100644
index 0000000..45321de
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_late.dart.expect
@@ -0,0 +1,22 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class Foo extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::Foo
+ : super core::Object::•()
+ ;
+}
+static method main() → void {
+ late self::Foo foo;
+ foo = block {
+ final self::Foo :expressionValueWrappedFinalizable = new self::Foo::•();
+ _in::reachabilityFence(foo);
+ } =>:expressionValueWrappedFinalizable;
+ core::print(foo);
+ _in::reachabilityFence(foo);
+}
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 0af255f..7e93579 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -2048,28 +2048,35 @@
return Fragment();
}
-Fragment StreamingFlowGraphBuilder::BuildVariableGet(TokenPosition* position) {
+Fragment StreamingFlowGraphBuilder::BuildVariableGet(
+ TokenPosition* position,
+ bool allow_late_uninitialized) {
const TokenPosition pos = ReadPosition();
if (position != nullptr) *position = pos;
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
ReadUInt(); // read relative variable index.
SkipOptionalDartType(); // read promoted type.
- return BuildVariableGetImpl(variable_kernel_position, pos);
+ return BuildVariableGetImpl(variable_kernel_position, pos,
+ allow_late_uninitialized);
}
-Fragment StreamingFlowGraphBuilder::BuildVariableGet(uint8_t payload,
- TokenPosition* position) {
+Fragment StreamingFlowGraphBuilder::BuildVariableGet(
+ uint8_t payload,
+ TokenPosition* position,
+ bool allow_late_uninitialized) {
const TokenPosition pos = ReadPosition();
if (position != nullptr) *position = pos;
intptr_t variable_kernel_position = ReadUInt(); // read kernel position.
- return BuildVariableGetImpl(variable_kernel_position, pos);
+ return BuildVariableGetImpl(variable_kernel_position, pos,
+ allow_late_uninitialized);
}
Fragment StreamingFlowGraphBuilder::BuildVariableGetImpl(
intptr_t variable_kernel_position,
- TokenPosition position) {
+ TokenPosition position,
+ bool allow_late_uninitialized) {
LocalVariable* variable = LookupVariable(variable_kernel_position);
- if (!variable->is_late()) {
+ if (!variable->is_late() || allow_late_uninitialized) {
return LoadLocal(variable);
}
@@ -3432,6 +3439,8 @@
switch (recognized_kind) {
case MethodRecognizer::kNativeEffect:
return BuildNativeEffect();
+ case MethodRecognizer::kReachabilityFence:
+ return BuildReachabilityFence();
case MethodRecognizer::kFfiAsFunctionInternal:
return BuildFfiAsFunctionInternal();
case MethodRecognizer::kFfiNativeCallbackFunction:
@@ -5549,6 +5558,45 @@
return code;
}
+Fragment StreamingFlowGraphBuilder::BuildReachabilityFence() {
+ const intptr_t argc = ReadUInt(); // Read argument count.
+ ASSERT(argc == 1); // LoadField, can be late.
+ const intptr_t list_length = ReadListLength(); // Read types list length.
+ ASSERT(list_length == 0);
+
+ const intptr_t positional_count = ReadListLength();
+ ASSERT(positional_count == 1);
+
+ // The CFE transform only generates a subset of argument expressions:
+ // either variable get or `this`.
+ uint8_t payload = 0;
+ Tag tag = ReadTag(&payload);
+ TokenPosition* position = nullptr;
+ const bool allow_late_uninitialized = true;
+ Fragment code;
+ switch (tag) {
+ case kVariableGet:
+ code = BuildVariableGet(position, allow_late_uninitialized);
+ break;
+ case kSpecializedVariableGet:
+ code = BuildVariableGet(payload, position, allow_late_uninitialized);
+ break;
+ case kThisExpression:
+ code = BuildThisExpression(position);
+ break;
+ default:
+ // The transformation should not be generating anything else.
+ FATAL1("Unexpected tag %i", tag);
+ }
+
+ const intptr_t named_args_len = ReadListLength();
+ ASSERT(named_args_len == 0);
+
+ code <<= new (Z) ReachabilityFenceInstr(Pop());
+ code += NullConstant(); // Return type is void.
+ return code;
+}
+
static void ReportIfNotNull(const char* error) {
if (error != nullptr) {
const auto& language_error = Error::Handle(
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index cc8a1f9..29adbcd 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -272,10 +272,14 @@
Fragment BuildArgumentsFromActualArguments(Array* argument_names);
Fragment BuildInvalidExpression(TokenPosition* position);
- Fragment BuildVariableGet(TokenPosition* position);
- Fragment BuildVariableGet(uint8_t payload, TokenPosition* position);
+ Fragment BuildVariableGet(TokenPosition* position,
+ bool allow_late_uninitialized = false);
+ Fragment BuildVariableGet(uint8_t payload,
+ TokenPosition* position,
+ bool allow_late_uninitialized = false);
Fragment BuildVariableGetImpl(intptr_t variable_kernel_position,
- TokenPosition position);
+ TokenPosition position,
+ bool allow_late_uninitialized = false);
Fragment BuildVariableSet(TokenPosition* position);
Fragment BuildVariableSet(uint8_t payload, TokenPosition* position);
Fragment BuildVariableSetImpl(TokenPosition position,
@@ -365,6 +369,10 @@
// Build flow graph for '_nativeEffect'.
Fragment BuildNativeEffect();
+ // Build the call-site manually, to avoid doing initialization checks
+ // for late fields.
+ Fragment BuildReachabilityFence();
+
// Build flow graph for '_loadAbiSpecificInt' and
// '_loadAbiSpecificIntAtIndex', '_storeAbiSpecificInt', and
// '_storeAbiSpecificIntAtIndex' call sites.
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 905570f..c0c5be2 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -949,7 +949,6 @@
case MethodRecognizer::kCopyRangeFromUint8ListToOneByteString:
case MethodRecognizer::kImmutableLinkedHashBase_setIndexStoreRelease:
case MethodRecognizer::kFfiAbi:
- case MethodRecognizer::kReachabilityFence:
case MethodRecognizer::kUtf8DecoderScan:
case MethodRecognizer::kHas63BitSmis:
#define CASE(method, slot) case MethodRecognizer::k##method:
@@ -1279,12 +1278,6 @@
body += Utf8Scan();
body += Box(kUnboxedIntPtr);
break;
- case MethodRecognizer::kReachabilityFence:
- ASSERT_EQUAL(function.NumParameters(), 1);
- body += LoadLocal(parsed_function_->RawParameterVariable(0));
- body += ReachabilityFence();
- body += NullConstant();
- break;
case MethodRecognizer::kFfiAbi:
ASSERT_EQUAL(function.NumParameters(), 0);
body += IntConstant(static_cast<int64_t>(compiler::ffi::TargetAbi()));
diff --git a/tests/language/vm/regress_49005_test.dart b/tests/language/vm/regress_49005_test.dart
new file mode 100644
index 0000000..619aa12
--- /dev/null
+++ b/tests/language/vm/regress_49005_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:ffi';
+
+class Foo implements Finalizable {}
+
+void main() {
+ late Foo foo;
+ foo = Foo();
+ print(foo);
+}
diff --git a/tools/VERSION b/tools/VERSION
index f5f3e2f56..c67ab37 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
CHANNEL stable
MAJOR 2
MINOR 17
-PATCH 0
+PATCH 1
PRERELEASE 0
PRERELEASE_PATCH 0
\ No newline at end of file