Version 2.6.0-dev.8.2
* Cherry-pick 8b3d76dff19e8ff67169db867a51d9a9d4d19f7b to dev
* Cherry-pick 5fd6c8a3c19b062887b5830b37bfeb2347eb4d17 to dev
* Cherry-pick b359ac0a1e5b2fa16ec71d6e5788f53b82903057 to dev
* Cherry-pick 2be376385cf06bcf787614fa990c955973eded45 to dev
* Cherry-pick 1a747c6546a1713679266e41828680c14f2305e0 to dev
* Cherry-pick 9e06f24c92b879ee9c6062958e74d07ca6c895c7 to dev
* Cherry-pick 2c1d405c15bcfb2f325d4ac8b89c7452e710a8ba to dev
* Cherry-pick c8b903c2f94f57b666d543bba7890c4940f03994 to dev
* Cherry-pick c49378aadf30cc81f90bf56b2d0dbb969c64e80e to dev
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ee98ea..c77e474 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,12 @@
-## Next release
-(Add new changes here, and they will be copied to the change section for the
- next release)
+## 2.6.0 - 2019-11-05
### Language
-* [Static extension members][]: A new language feature allowing
+* **[IN PREVIEW]** [Static extension members][]: A new language feature allowing
specially declared static functions to be invoked
- like instance members on expressions of appropriate static types.
+ like instance members on expressions of appropriate static types
+ is available in preview.
+
Static extension members are declared using a new `extension`
declaration. Example:
```dart
@@ -85,6 +85,26 @@
* 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`)
+
+* **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 provide convenient access to memory through extension getters and
+ setters. The extension members on `Pointer` provide `.value` and `.value =`
+ for accessing the value in native memory and `[]` and `[]=` for indexed access.
+ The method `asExternalTypedData` has been replaced with `asTypedList` extension
+ methods. And finally, `Structs` do no longer have a type argument and are
+ accessed the extension member `.ref` on `Pointer`.
+ These changes makes the code using `dart:ffi` much more concise.
+* **Breaking change**: The memory management has been removed (`Pointer.allocate`
+ and `Pointer.free`). Instead, memory management is available in
+ [package:ffi](https://pub.dev/packages/ffi).
+* **Breaking change**: `Pointer.offsetBy` was removed, use `cast` and `elementAt`
+ instead.
+* Faster memory load and stores.
+* The dartanalyzer (commandline and IDEs) now reports `dart:ffi` static errors.
+* Callbacks are now supported in AOT (ahead-of-time) compiled code.
+
### Dart for the Web
#### Dart Dev Compiler (DDC)
@@ -94,8 +114,6 @@
### Tools
-#### Pub
-
#### Linter
The Linter was updated to `0.1.101`, which includes:
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 467f542..38a0dbe 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -141,7 +141,7 @@
element.name == 'DynamicLibrary' && element.library.name == 'dart.ffi';
/// Return `true` if the given [element] represents the class `Pointer`.
- bool _isPointer(ClassElement element) =>
+ bool _isPointer(Element element) =>
element.name == 'Pointer' && element.library.name == 'dart.ffi';
/// Return `true` if the [typeName] represents a subtype of `Struct`.
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index d48f9fc..78d3d6a 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -312,7 +312,9 @@
FunctionNode(
ReturnStatement(MethodInvocation(pointer, castMethod.name,
Arguments([], types: [nativeType]), castMethod)),
- returnType: pointerType));
+ returnType: pointerType),
+ fileUri: field.fileUri)
+ ..fileOffset = field.fileOffset;
// Sample output:
// double get x => _xPtr.value;
@@ -328,7 +330,9 @@
PropertyGet(ThisExpression(), pointerName, pointerGetter),
ConstantExpression(IntConstant(0))
], types: typeArguments))),
- returnType: field.type));
+ returnType: field.type),
+ fileUri: field.fileUri)
+ ..fileOffset = field.fileOffset;
// Sample output:
// set x(double v) { _xPtr.value = v; };
@@ -349,7 +353,9 @@
VariableGet(argument)
], types: typeArguments))),
returnType: VoidType(),
- positionalParameters: [argument]));
+ positionalParameters: [argument]),
+ fileUri: field.fileUri)
+ ..fileOffset = field.fileOffset;
}
replacedGetters[field] = getter;
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 784093e..82ac14a 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -120,6 +120,14 @@
return retval;
}
+// Used in regress_39044_test.dart.
+DART_EXPORT int64_t Regress39044(int64_t a, int8_t b) {
+ std::cout << "Regress39044(" << a << ", " << static_cast<int>(b) << ")\n";
+ const int64_t retval = a - b;
+ std::cout << "returning " << retval << "\n";
+ return retval;
+}
+
// Performs some computation on various sized unsigned ints.
// Used for testing value ranges for unsigned ints.
DART_EXPORT int64_t UintComputation(uint8_t a,
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 671ebd3..6990780 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -1106,7 +1106,8 @@
bool ConstantInstr::AttributesEqual(Instruction* other) const {
ConstantInstr* other_constant = other->AsConstant();
ASSERT(other_constant != NULL);
- return (value().raw() == other_constant->value().raw());
+ return (value().raw() == other_constant->value().raw() &&
+ representation() == other_constant->representation());
}
UnboxedConstantInstr::UnboxedConstantInstr(const Object& value,
@@ -5741,12 +5742,10 @@
ASSERT(((1 << CallingConventions::kFirstCalleeSavedCpuReg) &
CallingConventions::kArgumentRegisters) == 0);
-#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_IA32)
- constexpr intptr_t kNumTemps = 2;
-#elif defined(TARGET_ARCH_ARM)
+#if defined(TARGET_ARCH_ARM)
constexpr intptr_t kNumTemps = 3;
#else
- constexpr intptr_t kNumTemps = 1;
+ constexpr intptr_t kNumTemps = 2;
#endif
LocationSummary* summary = new (zone)
@@ -5758,11 +5757,8 @@
CallingConventions::kFirstNonArgumentRegister));
summary->set_temp(0, Location::RegisterLocation(
CallingConventions::kSecondNonArgumentRegister));
-#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM64) || \
- defined(TARGET_ARCH_ARM)
summary->set_temp(1, Location::RegisterLocation(
CallingConventions::kFirstCalleeSavedCpuReg));
-#endif
#if defined(TARGET_ARCH_ARM)
summary->set_temp(2, Location::RegisterLocation(
CallingConventions::kSecondCalleeSavedCpuReg));
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 8a986f7..1d09580 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -997,6 +997,7 @@
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
const Register saved_fp = locs()->temp(0).reg();
+ const Register temp = locs()->temp(1).reg();
const Register branch = locs()->in(TargetAddressIndex()).reg();
// Save frame pointer because we're going to update it when we enter the exit
@@ -1020,8 +1021,8 @@
for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) {
const Location origin = rebase.Rebase(locs()->in(i));
const Location target = arg_locations_[i];
- NoTemporaryAllocator no_temp;
- compiler->EmitMove(target, origin, &no_temp);
+ ConstantTemporaryAllocator temp_alloc(temp);
+ compiler->EmitMove(target, origin, &temp_alloc);
}
// We need to copy the return address up into the dummy stack frame so the
@@ -1037,15 +1038,14 @@
RawPcDescriptors::Kind::kOther, locs());
// Update information in the thread object and enter a safepoint.
- const Register tmp = locs()->temp(1).reg();
if (CanExecuteGeneratedCodeInSafepoint()) {
- __ TransitionGeneratedToNative(branch, FPREG, saved_fp, tmp,
+ __ TransitionGeneratedToNative(branch, FPREG, saved_fp, temp,
/*enter_safepoint=*/true);
__ blx(branch);
// Update information in the thread object and leave the safepoint.
- __ TransitionNativeToGenerated(saved_fp, tmp, /*leave_safepoint=*/true);
+ __ TransitionNativeToGenerated(saved_fp, temp, /*leave_safepoint=*/true);
} else {
// We cannot trust that this code will be executable within a safepoint.
// Therefore we delegate the responsibility of entering/exiting the
@@ -1057,7 +1057,7 @@
call_native_through_safepoint_entry_point_offset()));
// Calls R8 in a safepoint and clobbers NOTFP and R4.
- ASSERT(branch == R8 && tmp == NOTFP && locs()->temp(2).reg() == R4);
+ ASSERT(branch == R8 && temp == NOTFP && locs()->temp(2).reg() == R4);
__ blx(TMP);
}
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 1d934ec..220940e 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -874,9 +874,9 @@
}
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- Register saved_fp = locs()->temp(0).reg();
- Register temp = locs()->temp(1).reg();
- Register branch = locs()->in(TargetAddressIndex()).reg();
+ const Register saved_fp = locs()->temp(0).reg();
+ const Register temp = locs()->temp(1).reg();
+ const Register branch = locs()->in(TargetAddressIndex()).reg();
// Save frame pointer because we're going to update it when we enter the exit
// frame.
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index b6daf52..3e7d0eb 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -924,9 +924,9 @@
}
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- const Register saved_fp = locs()->temp(0).reg(); // volatile
+ const Register saved_fp = locs()->temp(0).reg();
+ const Register temp = locs()->temp(1).reg();
const Register branch = locs()->in(TargetAddressIndex()).reg();
- const Register tmp = locs()->temp(1).reg(); // callee-saved
// Save frame pointer because we're going to update it when we enter the exit
// frame.
@@ -949,8 +949,8 @@
for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) {
const Location origin = rebase.Rebase(locs()->in(i));
const Location target = arg_locations_[i];
- ConstantTemporaryAllocator tmp_alloc(tmp);
- compiler->EmitMove(target, origin, &tmp_alloc);
+ ConstantTemporaryAllocator temp_alloc(temp);
+ compiler->EmitMove(target, origin, &temp_alloc);
}
// We need to copy a dummy return address up into the dummy stack frame so the
@@ -961,27 +961,27 @@
compiler->EmitCallsiteMetadata(TokenPosition::kNoSource, DeoptId::kNone,
RawPcDescriptors::Kind::kOther, locs());
__ Bind(&get_pc);
- __ popl(tmp);
- __ movl(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), tmp);
+ __ popl(temp);
+ __ movl(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), temp);
if (CanExecuteGeneratedCodeInSafepoint()) {
- __ TransitionGeneratedToNative(branch, FPREG, tmp,
+ __ TransitionGeneratedToNative(branch, FPREG, temp,
/*enter_safepoint=*/true);
__ call(branch);
- __ TransitionNativeToGenerated(tmp, /*leave_safepoint=*/true);
+ __ TransitionNativeToGenerated(temp, /*leave_safepoint=*/true);
} else {
// We cannot trust that this code will be executable within a safepoint.
// Therefore we delegate the responsibility of entering/exiting the
// safepoint to a stub which in the VM isolate's heap, which will never lose
// execute permission.
- __ movl(tmp,
+ __ movl(temp,
compiler::Address(
THR, compiler::target::Thread::
call_native_through_safepoint_entry_point_offset()));
// Calls EAX within a safepoint and clobbers EBX.
- ASSERT(tmp == EBX && branch == EAX);
- __ call(tmp);
+ ASSERT(temp == EBX && branch == EAX);
+ __ call(temp);
}
// The x86 calling convention requires floating point values to be returned on
@@ -1000,7 +1000,7 @@
__ LeaveFrame();
// Instead of returning to the "fake" return address, we just pop it.
- __ popl(tmp);
+ __ popl(temp);
}
void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler,
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index dc394bf..e1beb73 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -933,8 +933,9 @@
}
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- Register saved_fp = locs()->temp(0).reg();
- Register target_address = locs()->in(TargetAddressIndex()).reg();
+ const Register saved_fp = locs()->temp(0).reg();
+ const Register temp = locs()->temp(1).reg();
+ const Register target_address = locs()->in(TargetAddressIndex()).reg();
// Save frame pointer because we're going to update it when we enter the exit
// frame.
@@ -960,8 +961,8 @@
for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) {
const Location origin = rebase.Rebase(locs()->in(i));
const Location target = arg_locations_[i];
- NoTemporaryAllocator temp;
- compiler->EmitMove(target, rebase.Rebase(origin), &temp);
+ ConstantTemporaryAllocator temp_alloc(temp);
+ compiler->EmitMove(target, rebase.Rebase(origin), &temp_alloc);
}
// We need to copy a dummy return address up into the dummy stack frame so the
diff --git a/runtime/vm/compiler/backend/locations.cc b/runtime/vm/compiler/backend/locations.cc
index 37b17c7..210cd37 100644
--- a/runtime/vm/compiler/backend/locations.cc
+++ b/runtime/vm/compiler/backend/locations.cc
@@ -306,7 +306,8 @@
intptr_t index = fpu_reg_slots[loc.fpu_reg()];
ASSERT(index >= 0);
switch (def->representation()) {
- case kUnboxedDouble:
+ case kUnboxedDouble: // SlowPathEnvironmentFor sees _one_ register
+ case kUnboxedFloat: // both for doubles and floats.
return Location::DoubleStackSlot(
compiler::target::frame_layout.FrameSlotForVariableIndex(-index),
FPREG);
diff --git a/runtime/vm/heap/freelist.cc b/runtime/vm/heap/freelist.cc
index c98af37..e77f199 100644
--- a/runtime/vm/heap/freelist.cc
+++ b/runtime/vm/heap/freelist.cc
@@ -323,14 +323,24 @@
intptr_t remainder_index = IndexForSize(remainder_size);
EnqueueElement(element, remainder_index);
- // Postcondition: when allocating in a protected page, the remainder
- // element is no longer writable unless it is in the same page as the
- // allocated element. (The allocated element is still writable, and the
- // remainder element will be protected when the allocated one is).
- if (is_protected &&
- !VirtualMemory::InSamePage(remainder_address - 1, remainder_address)) {
- VirtualMemory::Protect(reinterpret_cast<void*>(remainder_address),
- remainder_size, VirtualMemory::kReadExecute);
+ // Postcondition: when allocating in a protected page, the fraction of the
+ // remainder element which does not share a page with the allocated element is
+ // no longer writable. This means that if the remainder's header is not fully
+ // contained in the last page of the allocation, we need to re-protect the
+ // page it ends on.
+ if (is_protected) {
+ const uword remainder_header_size =
+ FreeListElement::HeaderSizeFor(remainder_size);
+ if (!VirtualMemory::InSamePage(
+ remainder_address - 1,
+ remainder_address + remainder_header_size - 1)) {
+ VirtualMemory::Protect(
+ reinterpret_cast<void*>(
+ Utils::RoundUp(remainder_address, VirtualMemory::PageSize())),
+ remainder_address + remainder_header_size -
+ Utils::RoundUp(remainder_address, VirtualMemory::PageSize()),
+ VirtualMemory::kReadExecute);
+ }
}
}
diff --git a/tests/ffi/regress_38993_test.dart b/tests/ffi/regress_38993_test.dart
new file mode 100644
index 0000000..8c9972f
--- /dev/null
+++ b/tests/ffi/regress_38993_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2019, 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.
+//
+// Tests a compile time error that should not crash the analyzer or CFE.
+
+import "dart:ffi";
+
+class C extends Struct {
+ dynamic x; //# 1: compile-time error
+}
+
+main() {}
diff --git a/tests/ffi/regress_39044_test.dart b/tests/ffi/regress_39044_test.dart
new file mode 100644
index 0000000..785500d
--- /dev/null
+++ b/tests/ffi/regress_39044_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2019, 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.
+//
+// Check that the optimizer does not fuse constants with different
+// representations.
+//
+// SharedObjects=ffi_test_functions
+
+import "dart:ffi";
+
+import "package:expect/expect.dart";
+
+import "dylib_utils.dart";
+
+main() {
+ final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+ final intComputation = ffiTestFunctions.lookupFunction<
+ Int64 Function(Int64, Int8), int Function(int, int)>("Regress39044");
+
+ // The arguments are the same Smi constant, however they are different sizes.
+ final result = intComputation(
+ /* dart::kUnboxedInt64 --> int64_t */ 1,
+ /* dart::kUnboxedInt32 --> truncated to int8_t */ 1);
+
+ Expect.equals(0, result);
+}
diff --git a/tests/ffi/regress_39063_test.dart b/tests/ffi/regress_39063_test.dart
new file mode 100644
index 0000000..52dca18
--- /dev/null
+++ b/tests/ffi/regress_39063_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2019, 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.
+//
+// Check that the optimizer does not fuse constants with different
+// representations.
+//
+// SharedObjects=ffi_test_functions
+
+import "dart:ffi";
+
+import "package:expect/expect.dart";
+
+import "dylib_utils.dart";
+
+typedef VigesimalOp = double Function(
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double);
+
+typedef NativeVigesimalOp = Double Function(
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double);
+
+main() {
+ final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+ final sumManyNumbers = ffiTestFunctions
+ .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
+
+ // Should not crash.
+ sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0,
+ 15, 16.0, 17, 18.0, 19, 20.0);
+}
diff --git a/tests/ffi/regress_39068_test.dart b/tests/ffi/regress_39068_test.dart
new file mode 100644
index 0000000..fbfca38
--- /dev/null
+++ b/tests/ffi/regress_39068_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2019, 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.
+//
+// SharedObjects=ffi_test_functions
+
+import "dart:ffi";
+
+import "package:expect/expect.dart";
+
+import "dylib_utils.dart";
+
+typedef VigesimalOp = double Function(
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double,
+ int,
+ double);
+
+typedef NativeVigesimalOp = Double Function(
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double,
+ IntPtr,
+ Float,
+ IntPtr,
+ Double);
+
+main() {
+ final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+ final sumManyNumbers = ffiTestFunctions
+ .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
+
+ try {
+ sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0,
+ 15, 16.0, 17, 18.0, null, 20.0);
+ } on Error {
+ print('Expected exception on passing null for int');
+ }
+}
diff --git a/tools/VERSION b/tools/VERSION
index 3393977..a6ba21a7 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -34,6 +34,6 @@
MINOR 6
PATCH 0
PRERELEASE 8
-PRERELEASE_PATCH 1
+PRERELEASE_PATCH 2
ABI_VERSION 19
OLDEST_SUPPORTED_ABI_VERSION 18