Version 2.17.0-8.0.dev
Merge commit '0a9fb1723e393a1442c555b5c18249b652a06e33' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b72adbc..0776b8b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,11 @@
- Add `Finalizer` and `WeakReference` which can potentially detect when
objects are "garbage collected".
+#### `dart:ffi`
+
+- Add `ref=` and `[]=` methods to the `StructPointer` and `UnionPointer`
+ extensions. They copy a compound instance into a native memory region.
+
#### `dart:indexed_db`
- `IdbFactory.supportsDatabaseNames` has been deprecated. It will always return
diff --git a/pkg/_fe_analyzer_shared/pubspec.yaml b/pkg/_fe_analyzer_shared/pubspec.yaml
index 9b35add..16c442b 100644
--- a/pkg/_fe_analyzer_shared/pubspec.yaml
+++ b/pkg/_fe_analyzer_shared/pubspec.yaml
@@ -1,5 +1,5 @@
name: _fe_analyzer_shared
-version: 32.0.0
+version: 33.0.0
description: Logic that is shared between the front_end and analyzer packages.
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/_fe_analyzer_shared
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_check.dart b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
index 4196283..3cd03b5 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_check.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
@@ -59,6 +59,7 @@
extension CompletionResponseExtension
on CheckTarget<CompletionResponseForTesting> {
+ @useResult
CheckTarget<bool> get isIncomplete {
return nest(
value.isIncomplete,
@@ -66,6 +67,7 @@
);
}
+ @useResult
CheckTarget<int> get replacementLength {
return nest(
value.replacementLength,
@@ -73,6 +75,7 @@
);
}
+ @useResult
CheckTarget<int> get replacementOffset {
return nest(
value.replacementOffset,
@@ -80,6 +83,7 @@
);
}
+ @useResult
CheckTarget<List<CompletionSuggestionForTesting>> get suggestions {
var suggestions = value.suggestions.map((e) {
return CompletionSuggestionForTesting(
@@ -117,6 +121,7 @@
extension CompletionSuggestionExtension
on CheckTarget<CompletionSuggestionForTesting> {
+ @useResult
CheckTarget<String> get completion {
return nest(
value.suggestion.completion,
@@ -124,6 +129,7 @@
);
}
+ @useResult
CheckTarget<String?> get defaultArgumentListString {
return nest(
value.suggestion.defaultArgumentListString,
@@ -131,6 +137,7 @@
);
}
+ @useResult
CheckTarget<List<int>?> get defaultArgumentListTextRanges {
return nest(
value.suggestion.defaultArgumentListTextRanges,
@@ -138,6 +145,7 @@
);
}
+ @useResult
CheckTarget<String?> get docComplete {
return nest(
value.suggestion.docComplete,
@@ -145,6 +153,7 @@
);
}
+ @useResult
CheckTarget<String?> get docSummary {
return nest(
value.suggestion.docSummary,
@@ -152,6 +161,7 @@
);
}
+ @useResult
CheckTarget<Element?> get element {
return nest(
value.suggestion.element,
@@ -207,6 +217,7 @@
element.isNotNull.kind.isTopLevelVariable;
}
+ @useResult
CheckTarget<CompletionSuggestionKind> get kind {
return nest(
value.suggestion.kind,
@@ -214,6 +225,7 @@
);
}
+ @useResult
CheckTarget<String?> get libraryUriToImport {
return nest(
value.suggestion.isNotImported == true
@@ -223,6 +235,7 @@
);
}
+ @useResult
CheckTarget<String?> get parameterType {
return nest(
value.suggestion.parameterType,
@@ -231,6 +244,7 @@
}
/// Return the effective replacement length.
+ @useResult
CheckTarget<int> get replacementLength {
return nest(
value.replacementLength,
@@ -239,6 +253,7 @@
}
/// Return the effective replacement offset.
+ @useResult
CheckTarget<int> get replacementOffset {
return nest(
value.replacementOffset,
@@ -246,6 +261,7 @@
);
}
+ @useResult
CheckTarget<String?> get returnType {
return nest(
value.suggestion.returnType,
@@ -253,6 +269,7 @@
);
}
+ @useResult
CheckTarget<int> get selectionLength {
return nest(
value.suggestion.selectionLength,
@@ -260,6 +277,7 @@
);
}
+ @useResult
CheckTarget<int> get selectionOffset {
return nest(
value.suggestion.selectionOffset,
@@ -297,27 +315,9 @@
}
}
-extension CompletionSuggestionListExtension
- on CheckTarget<List<CompletionSuggestionForTesting>> {
- CheckTarget<Iterable<String>> get completions {
- return nest(
- value.map((e) => e.suggestion.completion).toList(),
- (selected) => 'has completions ${valueStr(selected)}',
- );
- }
-
- CheckTarget<Iterable<CompletionSuggestionForTesting>> get withElementClass {
- return nest(
- value.where((e) {
- return e.suggestion.element?.kind == ElementKind.CLASS;
- }).toList(),
- (selected) => 'withElementClass ${valueStr(selected)}',
- );
- }
-}
-
extension CompletionSuggestionsExtension
on CheckTarget<Iterable<CompletionSuggestionForTesting>> {
+ @useResult
CheckTarget<List<String>> get completions {
return nest(
value.map((e) => e.suggestion.completion).toList(),
@@ -325,6 +325,7 @@
);
}
+ @useResult
CheckTarget<Iterable<CompletionSuggestionForTesting>> get namedArguments {
var result = value
.where((suggestion) =>
@@ -336,9 +337,20 @@
(selected) => 'named arguments ${valueStr(selected)}',
);
}
+
+ @useResult
+ CheckTarget<Iterable<CompletionSuggestionForTesting>> get withElementClass {
+ return nest(
+ value.where((e) {
+ return e.suggestion.element?.kind == ElementKind.CLASS;
+ }).toList(),
+ (selected) => 'withElementClass ${valueStr(selected)}',
+ );
+ }
}
extension ElementExtension on CheckTarget<Element> {
+ @useResult
CheckTarget<ElementKind> get kind {
return nest(
value.kind,
@@ -346,6 +358,7 @@
);
}
+ @useResult
CheckTarget<String> get name {
return nest(
value.name,
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 1d47825..c0df15a 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,7 +1,8 @@
-## 3.1.0-dev
+## 3.1.0
* New internal API for `package:dart_style`.
* Removed deprecated non-API `MockSdk` class.
* Removed deprecated `AnalysisDriver` constructor.
+* Updated the current language version to `2.17`.
## 3.0.0
* Removed deprecated `DartType.aliasElement/aliasArguments`.
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index e706822..547e31e 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
name: analyzer
-version: 3.1.0-dev
+version: 3.1.0
description: This package provides a library that performs static analysis of Dart code.
homepage: https://github.com/dart-lang/sdk/tree/main/pkg/analyzer
@@ -7,7 +7,7 @@
sdk: '>=2.14.0 <3.0.0'
dependencies:
- _fe_analyzer_shared: ^32.0.0
+ _fe_analyzer_shared: ^33.0.0
cli_util: ^0.3.0
collection: ^1.15.0
convert: ^3.0.0
diff --git a/pkg/dartdev/lib/src/commands/doc.dart b/pkg/dartdev/lib/src/commands/doc.dart
index d039183..8faff48 100644
--- a/pkg/dartdev/lib/src/commands/doc.dart
+++ b/pkg/dartdev/lib/src/commands/doc.dart
@@ -12,20 +12,20 @@
import '../core.dart';
import '../sdk.dart';
-/// A command to create a new project from a set of templates.
+/// A command to generate documentation for a project.
class DocCommand extends DartdevCommand {
static const String cmdName = 'doc';
- DocCommand({bool verbose = false})
- : super(
- cmdName,
- 'Generate HTML API documentation from Dart documentation comments.',
- verbose,
- ) {
+ static const String cmdDescription = '''
+Generate API documentation for Dart projects.
+
+For additional documentation generation options, see the 'dartdoc_options.yaml' file documentation at https://dart.dev/go/dartdoc-options-file.''';
+
+ DocCommand({bool verbose = false}) : super(cmdName, cmdDescription, verbose) {
argParser.addOption(
'output-dir',
abbr: 'o',
- defaultsTo: path.join('.', 'doc', 'api'),
+ defaultsTo: path.join('doc', 'api'),
help: 'Output directory',
);
argParser.addFlag(
@@ -75,8 +75,9 @@
// Call dartdoc.
if (verbose) {
- log.stdout('Calling dartdoc with the following options: $options');
+ log.stdout('Using the following options: $options');
}
+
final packageConfigProvider = PhysicalPackageConfigProvider();
final packageBuilder = PubPackageBuilder(
config, pubPackageMetaProvider, packageConfigProvider);
diff --git a/pkg/vm/lib/transformations/ffi/common.dart b/pkg/vm/lib/transformations/ffi/common.dart
index 1152655..4446159 100644
--- a/pkg/vm/lib/transformations/ffi/common.dart
+++ b/pkg/vm/lib/transformations/ffi/common.dart
@@ -194,10 +194,14 @@
final Procedure offsetByMethod;
final Procedure elementAtMethod;
final Procedure addressGetter;
- final Procedure structPointerRef;
- final Procedure structPointerElemAt;
- final Procedure unionPointerRef;
- final Procedure unionPointerElemAt;
+ final Procedure structPointerGetRef;
+ final Procedure structPointerSetRef;
+ final Procedure structPointerGetElemAt;
+ final Procedure structPointerSetElemAt;
+ final Procedure unionPointerGetRef;
+ final Procedure unionPointerSetRef;
+ final Procedure unionPointerGetElemAt;
+ final Procedure unionPointerSetElemAt;
final Procedure structArrayElemAt;
final Procedure unionArrayElemAt;
final Procedure arrayArrayElemAt;
@@ -371,14 +375,22 @@
arrayConstructor = index.getConstructor('dart:ffi', 'Array', '_'),
fromAddressInternal =
index.getTopLevelProcedure('dart:ffi', '_fromAddress'),
- structPointerRef =
+ structPointerGetRef =
index.getProcedure('dart:ffi', 'StructPointer', 'get:ref'),
- structPointerElemAt =
+ structPointerSetRef =
+ index.getProcedure('dart:ffi', 'StructPointer', 'set:ref'),
+ structPointerGetElemAt =
index.getProcedure('dart:ffi', 'StructPointer', '[]'),
- unionPointerRef =
+ structPointerSetElemAt =
+ index.getProcedure('dart:ffi', 'StructPointer', '[]='),
+ unionPointerGetRef =
index.getProcedure('dart:ffi', 'UnionPointer', 'get:ref'),
- unionPointerElemAt =
+ unionPointerSetRef =
+ index.getProcedure('dart:ffi', 'UnionPointer', 'set:ref'),
+ unionPointerGetElemAt =
index.getProcedure('dart:ffi', 'UnionPointer', '[]'),
+ unionPointerSetElemAt =
+ index.getProcedure('dart:ffi', 'UnionPointer', '[]='),
structArrayElemAt = index.getProcedure('dart:ffi', 'StructArray', '[]'),
unionArrayElemAt = index.getProcedure('dart:ffi', 'UnionArray', '[]'),
arrayArrayElemAt = index.getProcedure('dart:ffi', 'ArrayArray', '[]'),
diff --git a/pkg/vm/lib/transformations/ffi/use_sites.dart b/pkg/vm/lib/transformations/ffi/use_sites.dart
index d0e44333..ba1a0b1 100644
--- a/pkg/vm/lib/transformations/ffi/use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi/use_sites.dart
@@ -169,15 +169,24 @@
fileOffset: node.fileOffset,
);
}
- if (target == structPointerRef ||
- target == structPointerElemAt ||
- target == unionPointerRef ||
- target == unionPointerElemAt) {
+ if (target == structPointerGetRef ||
+ target == structPointerGetElemAt ||
+ target == unionPointerGetRef ||
+ target == unionPointerGetElemAt) {
final DartType nativeType = node.arguments.types[0];
_ensureNativeTypeValid(nativeType, node, allowCompounds: true);
- return _replaceRef(node);
+ return _replaceGetRef(node);
+ } else if (target == structPointerSetRef ||
+ target == structPointerSetElemAt ||
+ target == unionPointerSetRef ||
+ target == unionPointerSetElemAt) {
+ final DartType nativeType = node.arguments.types[0];
+
+ _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
+
+ return _replaceSetRef(node);
} else if (target == structArrayElemAt || target == unionArrayElemAt) {
final DartType nativeType = node.arguments.types[0];
@@ -533,7 +542,7 @@
return StaticGet(field);
}
- Expression _replaceRef(StaticInvocation node) {
+ Expression _replaceGetRef(StaticInvocation node) {
final dartType = node.arguments.types[0];
final clazz = (dartType as InterfaceType).classNode;
final constructor = clazz.constructors
@@ -555,6 +564,38 @@
return ConstructorInvocation(constructor, Arguments([pointer]));
}
+ /// Replaces a `.ref=` or `[]=` on a compound pointer extension with a memcopy
+ /// call.
+ Expression _replaceSetRef(StaticInvocation node) {
+ final target = node.arguments.positional[0]; // Receiver of extension
+
+ final Expression source, targetOffset;
+
+ if (node.arguments.positional.length == 3) {
+ // []= call, args are (receiver, index, source)
+ source = getCompoundTypedDataBaseField(
+ node.arguments.positional[2], node.fileOffset);
+ targetOffset = multiply(node.arguments.positional[1],
+ _inlineSizeOf(node.arguments.types[0] as InterfaceType)!);
+ } else {
+ // .ref= call, args are (receiver, source)
+ source = getCompoundTypedDataBaseField(
+ node.arguments.positional[1], node.fileOffset);
+ targetOffset = ConstantExpression(IntConstant(0));
+ }
+
+ return StaticInvocation(
+ memCopy,
+ Arguments([
+ target,
+ targetOffset,
+ source,
+ ConstantExpression(IntConstant(0)),
+ _inlineSizeOf(node.arguments.types[0] as InterfaceType)!,
+ ]),
+ );
+ }
+
Expression _replaceRefArray(StaticInvocation node) {
final dartType = node.arguments.types[0];
final clazz = (dartType as InterfaceType).classNode;
diff --git a/pkg/vm/testcases/transformations/ffi/compound_copies.dart b/pkg/vm/testcases/transformations/ffi/compound_copies.dart
new file mode 100644
index 0000000..4717b9b
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/compound_copies.dart
@@ -0,0 +1,27 @@
+import 'dart:ffi';
+
+import 'package:ffi/ffi.dart';
+
+class Coordinate extends Struct {
+ @Int64()
+ external int x;
+
+ @Int64()
+ external int y;
+
+ void copyInto(Pointer<Coordinate> ptr) {
+ ptr.ref = this;
+ }
+}
+
+class SomeUnion extends Union {
+ external Coordinate coordinate;
+ @Int64()
+ external int id;
+
+ void copyIntoAtIndex(Pointer<SomeUnion> ptr, int index) {
+ ptr[index] = this;
+ }
+}
+
+void main() {}
diff --git a/pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect b/pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect
new file mode 100644
index 0000000..98ee713
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect
@@ -0,0 +1,87 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+import "package:ffi/ffi.dart";
+
+@#C6
+class Coordinate extends ffi::Struct {
+ synthetic constructor •() → self::Coordinate
+ : super ffi::Struct::•()
+ ;
+ constructor #fromTypedDataBase(core::Object #typedDataBase) → self::Coordinate
+ : super ffi::Struct::_fromTypedDataBase(#typedDataBase)
+ ;
+ @#C7
+ get x() → core::int
+ return ffi::_loadInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+ @#C7
+ set x(core::int #externalFieldValue) → void
+ return ffi::_storeInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
+ @#C7
+ get y() → core::int
+ return ffi::_loadInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+ @#C7
+ set y(core::int #externalFieldValue) → void
+ return ffi::_storeInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
+ method copyInto(ffi::Pointer<self::Coordinate> ptr) → void {
+ ffi::_memCopy(ptr, #C8, this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, self::Coordinate::#sizeOf);
+ }
+ @#C13
+ static get #sizeOf() → core::int*
+ return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+@#C19
+class SomeUnion extends ffi::Union {
+ synthetic constructor •() → self::SomeUnion
+ : super ffi::Union::•()
+ ;
+ constructor #fromTypedDataBase(core::Object #typedDataBase) → self::SomeUnion
+ : super ffi::Union::_fromTypedDataBase(#typedDataBase)
+ ;
+ get coordinate() → self::Coordinate
+ return new self::Coordinate::#fromTypedDataBase( block {
+ core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase}{core::Object};
+ core::int #offset = #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<self::Coordinate>(#typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}{typ::ByteBuffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}{core::int}.{core::num::+}(#offset){(core::num) → core::num}, #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}){([core::int, core::int?]) → typ::Uint8List});
+ set coordinate(self::Coordinate #externalFieldValue) → void
+ return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+ @#C7
+ get id() → core::int
+ return ffi::_loadInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+ @#C7
+ set id(core::int #externalFieldValue) → void
+ return ffi::_storeInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
+ method copyIntoAtIndex(ffi::Pointer<self::SomeUnion> ptr, core::int index) → void {
+ ffi::_memCopy(ptr, index.{core::num::*}(self::SomeUnion::#sizeOf){(core::num) → core::num}, this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, self::SomeUnion::#sizeOf);
+ }
+ @#C13
+ static get #sizeOf() → core::int*
+ return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+static method main() → void {}
+constants {
+ #C1 = "vm:ffi:struct-fields"
+ #C2 = TypeLiteralConstant(ffi::Int64)
+ #C3 = <core::Type>[#C2, #C2]
+ #C4 = null
+ #C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4}
+ #C6 = core::pragma {name:#C1, options:#C5}
+ #C7 = ffi::Int64 {}
+ #C8 = 0
+ #C9 = <core::int*>[#C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8]
+ #C10 = 8
+ #C11 = <core::int*>[#C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10]
+ #C12 = "vm:prefer-inline"
+ #C13 = core::pragma {name:#C12, options:#C4}
+ #C14 = 16
+ #C15 = <core::int*>[#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14]
+ #C16 = TypeLiteralConstant(self::Coordinate)
+ #C17 = <core::Type>[#C16, #C2]
+ #C18 = ffi::_FfiStructLayout {fieldTypes:#C17, packing:#C4}
+ #C19 = core::pragma {name:#C1, options:#C18}
+}
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index cec0bfe..7ecccf0 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -924,8 +924,16 @@
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
+ set ref(T value) =>
+ throw "UNREACHABLE: This case should have been rewritten in the CFE";
+
+ @patch
T operator [](int index) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
+
+ @patch
+ void operator []=(int index, T value) =>
+ throw "UNREACHABLE: This case should have been rewritten in the CFE.";
}
extension UnionPointer<T extends Union> on Pointer<T> {
@@ -934,8 +942,16 @@
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
+ set ref(T value) =>
+ throw "UNREACHABLE: This case should have been rewritten in the CFE";
+
+ @patch
T operator [](int index) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
+
+ @patch
+ void operator []=(int index, T value) =>
+ throw "UNREACHABLE: This case should have been rewritten in the CFE.";
}
extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index a94f104..b1d53a6 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -648,14 +648,20 @@
/// Extension on [Pointer] specialized for the type argument [Struct].
extension StructPointer<T extends Struct> on Pointer<T> {
- /// Creates a reference to access the fields of this struct backed by native
- /// memory at [address].
+ /// A Dart view of the struct referenced by this pointer.
///
+ /// Reading [ref] creates a reference accessing the fields of this struct
+ /// backed by native memory at [address].
/// The [address] must be aligned according to the struct alignment rules of
/// the platform.
///
- /// This extension method must be invoked with a compile-time constant [T].
+ /// Assigning to [ref] copies contents of the struct into the native memory
+ /// starting at [address].
+ ///
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
external T get ref;
+ external set ref(T value);
/// Creates a reference to access the fields of this struct backed by native
/// memory at `address + sizeOf<T>() * index`.
@@ -663,20 +669,34 @@
/// The [address] must be aligned according to the struct alignment rules of
/// the platform.
///
- /// This extension method must be invoked with a compile-time constant [T].
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
external T operator [](int index);
+
+ /// Copies the [value] struct into native memory, starting at
+ /// `address * sizeOf<T>() * index`.
+ ///
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
+ external void operator []=(int index, T value);
}
/// Extension on [Pointer] specialized for the type argument [Union].
extension UnionPointer<T extends Union> on Pointer<T> {
- /// Creates a reference to access the fields of this union backed by native
- /// memory at [address].
+ /// A Dart view of the union referenced by this pointer.
///
+ /// Reading [ref] creates a reference accessing the fields of this union
+ /// backed by native memory at [address].
/// The [address] must be aligned according to the union alignment rules of
/// the platform.
///
- /// This extension method must be invoked with a compile-time constant [T].
+ /// Assigning to [ref] copies contents of the union into the native memory
+ /// starting at [address].
+ ///
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
external T get ref;
+ external set ref(T value);
/// Creates a reference to access the fields of this union backed by native
/// memory at `address + sizeOf<T>() * index`.
@@ -684,8 +704,16 @@
/// The [address] must be aligned according to the union alignment rules of
/// the platform.
///
- /// This extension method must be invoked with a compile-time constant [T].
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
external T operator [](int index);
+
+ /// Copies the [value] union into native memory, starting at
+ /// `address * sizeOf<T>() * index`.
+ ///
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
+ external void operator []=(int index, T value);
}
/// Extension on [Pointer] specialized for the type argument
@@ -713,13 +741,15 @@
/// Bounds checking indexing methods on [Array]s of [Struct].
extension StructArray<T extends Struct> on Array<T> {
- /// This extension method must be invoked with a compile-time constant [T].
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
external T operator [](int index);
}
/// Bounds checking indexing methods on [Array]s of [Union].
extension UnionArray<T extends Union> on Array<T> {
- /// This extension method must be invoked with a compile-time constant [T].
+ /// This extension method must be invoked on a receiver of type `Pointer<T>`
+ /// where `T` is a compile-time constant type.
external T operator [](int index);
}
diff --git a/tests/ffi/extension_methods_test.dart b/tests/ffi/extension_methods_test.dart
index b8f187e..fb71378 100644
--- a/tests/ffi/extension_methods_test.dart
+++ b/tests/ffi/extension_methods_test.dart
@@ -11,6 +11,7 @@
for (int i = 0; i < 100; i++) {
testStoreLoad();
testReifiedGeneric();
+ testCompoundLoadAndStore();
}
}
@@ -50,6 +51,14 @@
foo.a = 1;
Expect.equals(1, foo.a);
calloc.free(p3);
+
+ final p4 = calloc<Foo>(2);
+ Foo src = p4[1];
+ src.a = 2;
+ p4.ref = src;
+ Foo dst = p4.ref;
+ Expect.equals(2, dst.a);
+ calloc.free(p4);
}
testReifiedGeneric() {
@@ -59,7 +68,37 @@
calloc.free(p);
}
+testCompoundLoadAndStore() {
+ final foos = calloc<Foo>(10);
+ final reference = foos.ref..a = 10;
+
+ for (var i = 1; i < 9; i++) {
+ foos[i] = reference;
+ Expect.isTrue(foos[i].a == 10);
+
+ foos.elementAt(i).ref = reference;
+ Expect.isTrue(foos.elementAt(i).ref.a == 10);
+ }
+
+ final bars = calloc<Bar>(10);
+ bars[0].foo = reference;
+
+ for (var i = 1; i < 9; i++) {
+ bars[i] = bars[0];
+ Expect.isTrue(bars.elementAt(i).ref.foo.a == 10);
+ }
+
+ calloc.free(foos);
+ calloc.free(bars);
+}
+
class Foo extends Struct {
@Int8()
external int a;
}
+
+class Bar extends Union {
+ external Foo foo;
+ @Int32()
+ external int baz;
+}
diff --git a/tests/ffi_2/extension_methods_test.dart b/tests/ffi_2/extension_methods_test.dart
index 9d96f41..cb124af 100644
--- a/tests/ffi_2/extension_methods_test.dart
+++ b/tests/ffi_2/extension_methods_test.dart
@@ -13,6 +13,7 @@
for (int i = 0; i < 100; i++) {
testStoreLoad();
testReifiedGeneric();
+ testCompoundLoadAndStore();
}
}
@@ -52,6 +53,14 @@
foo.a = 1;
Expect.equals(1, foo.a);
calloc.free(p3);
+
+ final p4 = calloc<Foo>(2);
+ Foo src = p4[1];
+ src.a = 2;
+ p4.ref = src;
+ Foo dst = p4.ref;
+ Expect.equals(2, dst.a);
+ calloc.free(p4);
}
testReifiedGeneric() {
@@ -61,7 +70,37 @@
calloc.free(p);
}
+testCompoundLoadAndStore() {
+ final foos = calloc<Foo>(10);
+ final reference = foos.ref..a = 10;
+
+ for (var i = 1; i < 9; i++) {
+ foos[i] = reference;
+ Expect.isTrue(foos[i].a == 10);
+
+ foos.elementAt(i).ref = reference;
+ Expect.isTrue(foos.elementAt(i).ref.a == 10);
+ }
+
+ final bars = calloc<Bar>(10);
+ bars[0].foo = reference;
+
+ for (var i = 1; i < 9; i++) {
+ bars[i] = bars[0];
+ Expect.isTrue(bars.elementAt(i).ref.foo.a == 10);
+ }
+
+ calloc.free(foos);
+ calloc.free(bars);
+}
+
class Foo extends Struct {
@Int8()
int a;
}
+
+class Bar extends Union {
+ Foo foo;
+ @Int32()
+ int baz;
+}
diff --git a/tools/VERSION b/tools/VERSION
index 1c51070..eafe4ae 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 7
+PRERELEASE 8
PRERELEASE_PATCH 0
\ No newline at end of file