mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 22:01:19 +00:00
[vm/ffi] NNBD use external fields for structs
This enables adding the `external` keyword to struct fields, which enables use in nnbd (weak mode). Closes: https://github.com/dart-lang/sdk/issues/40247 External fields are not implemented in the analyzer yet: https://github.com/dart-lang/sdk/issues/41940 Change-Id: I9d88acbdabf73ca63a6ad3d549930aa3c97cb53f Cq-Include-Trybots: luci.dart.try: analyzer-nnbd-linux-release-try,dart2js-nnbd-linux-x64-chrome-try,ddc-nnbd-linux-release-chrome-try,front-end-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/148242 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
c10b41a4e2
commit
40f7a11d89
|
@ -156,9 +156,9 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isPointerType(Field field) {
|
bool _isPointerType(DartType type) {
|
||||||
return env.isSubtypeOf(
|
return env.isSubtypeOf(
|
||||||
field.type,
|
type,
|
||||||
InterfaceType(pointerClass, Nullability.legacy, [
|
InterfaceType(pointerClass, Nullability.legacy, [
|
||||||
InterfaceType(nativeTypesClasses[NativeType.kNativeType.index],
|
InterfaceType(nativeTypesClasses[NativeType.kNativeType.index],
|
||||||
Nullability.legacy)
|
Nullability.legacy)
|
||||||
|
@ -166,18 +166,52 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
SubtypeCheckMode.ignoringNullabilities);
|
SubtypeCheckMode.ignoringNullabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns members of [node] that correspond to struct fields.
|
||||||
|
///
|
||||||
|
/// Note that getters and setters that originate from an external field have
|
||||||
|
/// the same `fileOffset`, we always returns getters first.
|
||||||
|
List<Member> _structFieldMembers(Class node) {
|
||||||
|
final externalGetterSetters = [...node.procedures]
|
||||||
|
..retainWhere((p) => p.isExternal && (p.isGetter || p.isSetter));
|
||||||
|
final structMembers = [...node.fields, ...externalGetterSetters]
|
||||||
|
..sort((m1, m2) {
|
||||||
|
if (m1.fileOffset == m2.fileOffset) {
|
||||||
|
// Getter and setter have same offset, getter comes first.
|
||||||
|
return (m1 as Procedure).isGetter ? -1 : 1;
|
||||||
|
}
|
||||||
|
return m1.fileOffset - m2.fileOffset;
|
||||||
|
});
|
||||||
|
return structMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
DartType _structFieldMemberType(Member member) {
|
||||||
|
if (member is Field) {
|
||||||
|
return member.type;
|
||||||
|
}
|
||||||
|
final Procedure p = member;
|
||||||
|
if (p.isGetter) {
|
||||||
|
return p.function.returnType;
|
||||||
|
}
|
||||||
|
return p.function.positionalParameters.single.type;
|
||||||
|
}
|
||||||
|
|
||||||
bool _checkFieldAnnotations(Class node) {
|
bool _checkFieldAnnotations(Class node) {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
for (final Field f in node.fields) {
|
final membersWithAnnotations = _structFieldMembers(node)
|
||||||
if (f.initializer is! NullLiteral) {
|
..retainWhere((m) => (m is Field) || (m is Procedure && m.isGetter));
|
||||||
diagnosticReporter.report(
|
for (final Member f in membersWithAnnotations) {
|
||||||
templateFfiFieldInitializer.withArguments(f.name.name),
|
if (f is Field) {
|
||||||
f.fileOffset,
|
if (f.initializer is! NullLiteral) {
|
||||||
f.name.name.length,
|
diagnosticReporter.report(
|
||||||
f.fileUri);
|
templateFfiFieldInitializer.withArguments(f.name.name),
|
||||||
|
f.fileOffset,
|
||||||
|
f.name.name.length,
|
||||||
|
f.fileUri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final nativeTypeAnnos = _getNativeTypeAnnotations(f).toList();
|
final nativeTypeAnnos = _getNativeTypeAnnotations(f).toList();
|
||||||
if (_isPointerType(f)) {
|
final type = _structFieldMemberType(f);
|
||||||
|
if (_isPointerType(type)) {
|
||||||
if (nativeTypeAnnos.length != 0) {
|
if (nativeTypeAnnos.length != 0) {
|
||||||
diagnosticReporter.report(
|
diagnosticReporter.report(
|
||||||
templateFfiFieldNoAnnotation.withArguments(f.name.name),
|
templateFfiFieldNoAnnotation.withArguments(f.name.name),
|
||||||
|
@ -192,7 +226,6 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
f.name.name.length,
|
f.name.name.length,
|
||||||
f.fileUri);
|
f.fileUri);
|
||||||
} else {
|
} else {
|
||||||
final DartType dartType = f.type;
|
|
||||||
final DartType nativeType = InterfaceType(
|
final DartType nativeType = InterfaceType(
|
||||||
nativeTypesClasses[nativeTypeAnnos.first.index],
|
nativeTypesClasses[nativeTypeAnnos.first.index],
|
||||||
Nullability.legacy);
|
Nullability.legacy);
|
||||||
|
@ -200,10 +233,10 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
final DartType shouldBeDartType =
|
final DartType shouldBeDartType =
|
||||||
convertNativeTypeToDartType(nativeType, /*allowStructs=*/ false);
|
convertNativeTypeToDartType(nativeType, /*allowStructs=*/ false);
|
||||||
if (shouldBeDartType == null ||
|
if (shouldBeDartType == null ||
|
||||||
!env.isSubtypeOf(dartType, shouldBeDartType,
|
!env.isSubtypeOf(type, shouldBeDartType,
|
||||||
SubtypeCheckMode.ignoringNullabilities)) {
|
SubtypeCheckMode.ignoringNullabilities)) {
|
||||||
diagnosticReporter.report(
|
diagnosticReporter.report(
|
||||||
templateFfiTypeMismatch.withArguments(dartType, shouldBeDartType,
|
templateFfiTypeMismatch.withArguments(type, shouldBeDartType,
|
||||||
nativeType, node.enclosingLibrary.isNonNullableByDefault),
|
nativeType, node.enclosingLibrary.isNonNullableByDefault),
|
||||||
f.fileOffset,
|
f.fileOffset,
|
||||||
1,
|
1,
|
||||||
|
@ -261,19 +294,40 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
///
|
///
|
||||||
/// Returns the total size of the struct (for all ABIs).
|
/// Returns the total size of the struct (for all ABIs).
|
||||||
Map<Abi, int> _replaceFields(Class node, IndexedClass indexedClass) {
|
Map<Abi, int> _replaceFields(Class node, IndexedClass indexedClass) {
|
||||||
final fields = <Field>[];
|
|
||||||
final types = <NativeType>[];
|
final types = <NativeType>[];
|
||||||
|
final fields = <int, Field>{};
|
||||||
|
final getters = <int, Procedure>{};
|
||||||
|
final setters = <int, Procedure>{};
|
||||||
|
|
||||||
for (final Field f in node.fields) {
|
int i = 0;
|
||||||
if (_isPointerType(f)) {
|
for (final Member m in _structFieldMembers(node)) {
|
||||||
fields.add(f);
|
final dartType = _structFieldMemberType(m);
|
||||||
types.add(NativeType.kPointer);
|
|
||||||
|
NativeType nativeType;
|
||||||
|
if (_isPointerType(dartType)) {
|
||||||
|
nativeType = NativeType.kPointer;
|
||||||
} else {
|
} else {
|
||||||
final nativeTypeAnnos = _getNativeTypeAnnotations(f).toList();
|
final nativeTypeAnnos = _getNativeTypeAnnotations(m).toList();
|
||||||
if (nativeTypeAnnos.length == 1) {
|
if (nativeTypeAnnos.length == 1) {
|
||||||
final NativeType t = nativeTypeAnnos.first;
|
nativeType = nativeTypeAnnos.first;
|
||||||
fields.add(f);
|
}
|
||||||
types.add(t);
|
}
|
||||||
|
|
||||||
|
if ((m is Field || (m is Procedure && m.isGetter)) &&
|
||||||
|
nativeType != null) {
|
||||||
|
types.add(nativeType);
|
||||||
|
if (m is Field) {
|
||||||
|
fields[i] = m;
|
||||||
|
}
|
||||||
|
if (m is Procedure) {
|
||||||
|
getters[i] = m;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (m is Procedure && m.isSetter) {
|
||||||
|
final index = i - 1; // The corresponding getter's index.
|
||||||
|
if (getters.containsKey(index)) {
|
||||||
|
setters[i - 1] = m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,7 +337,7 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
sizeAndOffsets[abi] = _calculateSizeAndOffsets(types, abi);
|
sizeAndOffsets[abi] = _calculateSizeAndOffsets(types, abi);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < fields.length; i++) {
|
for (final i in fields.keys) {
|
||||||
final fieldOffsets = sizeAndOffsets
|
final fieldOffsets = sizeAndOffsets
|
||||||
.map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
|
.map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
|
||||||
final methods = _generateMethodsForField(
|
final methods = _generateMethodsForField(
|
||||||
|
@ -291,10 +345,35 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
methods.forEach((p) => node.addMember(p));
|
methods.forEach((p) => node.addMember(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Field f in fields) {
|
for (final Field f in fields.values) {
|
||||||
f.remove();
|
f.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final i in getters.keys) {
|
||||||
|
final fieldOffsets = sizeAndOffsets
|
||||||
|
.map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
|
||||||
|
Procedure getter = getters[i];
|
||||||
|
getter.function.body = _generateGetterStatement(
|
||||||
|
getter.function.returnType,
|
||||||
|
types[i],
|
||||||
|
getter.fileOffset,
|
||||||
|
fieldOffsets);
|
||||||
|
getter.isExternal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final i in setters.keys) {
|
||||||
|
final fieldOffsets = sizeAndOffsets
|
||||||
|
.map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
|
||||||
|
Procedure setter = setters[i];
|
||||||
|
setter.function.body = _generateSetterStatement(
|
||||||
|
setter.function.positionalParameters.single.type,
|
||||||
|
types[i],
|
||||||
|
setter.fileOffset,
|
||||||
|
fieldOffsets,
|
||||||
|
setter.function.positionalParameters.single);
|
||||||
|
setter.isExternal = false;
|
||||||
|
}
|
||||||
|
|
||||||
return sizeAndOffsets.map((k, v) => MapEntry(k, v.size));
|
return sizeAndOffsets.map((k, v) => MapEntry(k, v.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,14 +394,9 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
listElementAt);
|
listElementAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Procedure> _generateMethodsForField(Field field, NativeType type,
|
Statement _generateGetterStatement(DartType dartType, NativeType type,
|
||||||
Map<Abi, int> offsets, IndexedClass indexedClass) {
|
int fileOffset, Map<Abi, int> offsets) {
|
||||||
final DartType nativeType = type == NativeType.kPointer
|
final bool isPointer = type == NativeType.kPointer;
|
||||||
? field.type
|
|
||||||
: InterfaceType(nativeTypesClasses[type.index], Nullability.legacy);
|
|
||||||
final Class nativeClass = (nativeType as InterfaceType).classNode;
|
|
||||||
final NativeType nt = getType(nativeClass);
|
|
||||||
final bool isPointer = nt == NativeType.kPointer;
|
|
||||||
|
|
||||||
// Sample output:
|
// Sample output:
|
||||||
// int get x => _loadInt8(pointer, offset);
|
// int get x => _loadInt8(pointer, offset);
|
||||||
|
@ -332,31 +406,27 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
// _fromAddress<Int8>(_loadIntPtr(pointer, offset));
|
// _fromAddress<Int8>(_loadIntPtr(pointer, offset));
|
||||||
final loadMethod = isPointer
|
final loadMethod = isPointer
|
||||||
? loadMethods[NativeType.kIntptr]
|
? loadMethods[NativeType.kIntptr]
|
||||||
: optimizedTypes.contains(nt) ? loadMethods[nt] : loadStructMethod;
|
: optimizedTypes.contains(type) ? loadMethods[type] : loadStructMethod;
|
||||||
Expression getterReturnValue = StaticInvocation(
|
Expression getterReturnValue = StaticInvocation(
|
||||||
loadMethod,
|
loadMethod,
|
||||||
Arguments([
|
Arguments([
|
||||||
PropertyGet(ThisExpression(), addressOfField.name, addressOfField)
|
PropertyGet(ThisExpression(), addressOfField.name, addressOfField)
|
||||||
..fileOffset = field.fileOffset,
|
..fileOffset = fileOffset,
|
||||||
_runtimeBranchOnLayout(offsets)
|
_runtimeBranchOnLayout(offsets)
|
||||||
]))
|
]))
|
||||||
..fileOffset = field.fileOffset;
|
..fileOffset = fileOffset;
|
||||||
if (isPointer) {
|
if (isPointer) {
|
||||||
final typeArg = (nativeType as InterfaceType).typeArguments.single;
|
final typeArg = (dartType as InterfaceType).typeArguments.single;
|
||||||
getterReturnValue = StaticInvocation(
|
getterReturnValue = StaticInvocation(
|
||||||
fromAddressInternal, Arguments([getterReturnValue], types: [typeArg]))
|
fromAddressInternal, Arguments([getterReturnValue], types: [typeArg]))
|
||||||
..fileOffset = field.fileOffset;
|
..fileOffset = fileOffset;
|
||||||
}
|
}
|
||||||
final Procedure getter = Procedure(
|
return ReturnStatement(getterReturnValue);
|
||||||
field.name,
|
}
|
||||||
ProcedureKind.Getter,
|
|
||||||
FunctionNode(ReturnStatement(getterReturnValue),
|
Statement _generateSetterStatement(DartType dartType, NativeType type,
|
||||||
returnType: field.type),
|
int fileOffset, Map<Abi, int> offsets, VariableDeclaration argument) {
|
||||||
fileUri: field.fileUri,
|
final bool isPointer = type == NativeType.kPointer;
|
||||||
reference:
|
|
||||||
indexedClass?.lookupProcedureNotSetter(field.name.name)?.reference)
|
|
||||||
..fileOffset = field.fileOffset
|
|
||||||
..isNonNullableByDefault = field.isNonNullableByDefault;
|
|
||||||
|
|
||||||
// Sample output:
|
// Sample output:
|
||||||
// set x(int v) => _storeInt8(pointer, offset, v);
|
// set x(int v) => _storeInt8(pointer, offset, v);
|
||||||
|
@ -365,35 +435,48 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
// set x(Pointer<Int8> v) =>
|
// set x(Pointer<Int8> v) =>
|
||||||
// _storeIntPtr(pointer, offset, (v as Pointer<Int8>).address);
|
// _storeIntPtr(pointer, offset, (v as Pointer<Int8>).address);
|
||||||
final storeMethod =
|
final storeMethod =
|
||||||
isPointer ? storeMethods[NativeType.kIntptr] : storeMethods[nt];
|
isPointer ? storeMethods[NativeType.kIntptr] : storeMethods[type];
|
||||||
|
Expression argumentExpression = VariableGet(argument)
|
||||||
|
..fileOffset = fileOffset;
|
||||||
|
if (isPointer) {
|
||||||
|
argumentExpression = DirectPropertyGet(argumentExpression, addressGetter)
|
||||||
|
..fileOffset = fileOffset;
|
||||||
|
}
|
||||||
|
return ReturnStatement(StaticInvocation(
|
||||||
|
storeMethod,
|
||||||
|
Arguments([
|
||||||
|
PropertyGet(ThisExpression(), addressOfField.name, addressOfField)
|
||||||
|
..fileOffset = fileOffset,
|
||||||
|
_runtimeBranchOnLayout(offsets),
|
||||||
|
argumentExpression
|
||||||
|
]))
|
||||||
|
..fileOffset = fileOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Procedure> _generateMethodsForField(Field field, NativeType type,
|
||||||
|
Map<Abi, int> offsets, IndexedClass indexedClass) {
|
||||||
|
final getterStatement =
|
||||||
|
_generateGetterStatement(field.type, type, field.fileOffset, offsets);
|
||||||
|
final Procedure getter = Procedure(field.name, ProcedureKind.Getter,
|
||||||
|
FunctionNode(getterStatement, returnType: field.type),
|
||||||
|
fileUri: field.fileUri,
|
||||||
|
reference:
|
||||||
|
indexedClass?.lookupProcedureNotSetter(field.name.name)?.reference)
|
||||||
|
..fileOffset = field.fileOffset
|
||||||
|
..isNonNullableByDefault = field.isNonNullableByDefault;
|
||||||
|
|
||||||
Procedure setter = null;
|
Procedure setter = null;
|
||||||
if (!field.isFinal) {
|
if (!field.isFinal) {
|
||||||
final VariableDeclaration argument =
|
final VariableDeclaration argument =
|
||||||
VariableDeclaration('#v', type: field.type)
|
VariableDeclaration('#v', type: field.type)
|
||||||
..fileOffset = field.fileOffset;
|
..fileOffset = field.fileOffset;
|
||||||
Expression argumentExpression = VariableGet(argument)
|
final setterStatement = _generateSetterStatement(
|
||||||
..fileOffset = field.fileOffset;
|
field.type, type, field.fileOffset, offsets, argument);
|
||||||
if (isPointer) {
|
|
||||||
argumentExpression =
|
|
||||||
DirectPropertyGet(argumentExpression, addressGetter)
|
|
||||||
..fileOffset = field.fileOffset;
|
|
||||||
}
|
|
||||||
setter = Procedure(
|
setter = Procedure(
|
||||||
field.name,
|
field.name,
|
||||||
ProcedureKind.Setter,
|
ProcedureKind.Setter,
|
||||||
FunctionNode(
|
FunctionNode(setterStatement,
|
||||||
ReturnStatement(StaticInvocation(
|
returnType: VoidType(), positionalParameters: [argument]),
|
||||||
storeMethod,
|
|
||||||
Arguments([
|
|
||||||
PropertyGet(
|
|
||||||
ThisExpression(), addressOfField.name, addressOfField)
|
|
||||||
..fileOffset = field.fileOffset,
|
|
||||||
_runtimeBranchOnLayout(offsets),
|
|
||||||
argumentExpression
|
|
||||||
]))
|
|
||||||
..fileOffset = field.fileOffset),
|
|
||||||
returnType: VoidType(),
|
|
||||||
positionalParameters: [argument]),
|
|
||||||
fileUri: field.fileUri,
|
fileUri: field.fileUri,
|
||||||
reference:
|
reference:
|
||||||
indexedClass?.lookupProcedureSetter(field.name.name)?.reference)
|
indexedClass?.lookupProcedureSetter(field.name.name)?.reference)
|
||||||
|
@ -485,7 +568,7 @@ class _FfiDefinitionTransformer extends FfiTransformer {
|
||||||
return fieldType;
|
return fieldType;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<NativeType> _getNativeTypeAnnotations(Field node) {
|
Iterable<NativeType> _getNativeTypeAnnotations(Member node) {
|
||||||
return node.annotations
|
return node.annotations
|
||||||
.whereType<ConstantExpression>()
|
.whereType<ConstantExpression>()
|
||||||
.map((expr) => expr.constant)
|
.map((expr) => expr.constant)
|
||||||
|
|
|
@ -10,12 +10,12 @@ import "package:ffi/ffi.dart";
|
||||||
/// Sample struct for dart:ffi library.
|
/// Sample struct for dart:ffi library.
|
||||||
class Coordinate extends Struct {
|
class Coordinate extends Struct {
|
||||||
@Double()
|
@Double()
|
||||||
double x;
|
external double x;
|
||||||
|
|
||||||
@Double()
|
@Double()
|
||||||
double y;
|
external double y;
|
||||||
|
|
||||||
Pointer<Coordinate> next;
|
external Pointer<Coordinate> next;
|
||||||
|
|
||||||
factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
|
factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
|
||||||
return allocate<Coordinate>().ref
|
return allocate<Coordinate>().ref
|
||||||
|
|
|
@ -9,10 +9,10 @@ import 'dart:ffi';
|
||||||
/// Stripped down sample struct for dart:ffi library.
|
/// Stripped down sample struct for dart:ffi library.
|
||||||
class Coordinate extends Struct {
|
class Coordinate extends Struct {
|
||||||
@Double()
|
@Double()
|
||||||
double x;
|
external double x;
|
||||||
|
|
||||||
@Double()
|
@Double()
|
||||||
double y;
|
external double y;
|
||||||
|
|
||||||
Pointer<Coordinate> next;
|
external Pointer<Coordinate> next;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,5 +61,5 @@ testReifiedGeneric() {
|
||||||
|
|
||||||
class Foo extends Struct {
|
class Foo extends Struct {
|
||||||
@Int8()
|
@Int8()
|
||||||
int a;
|
external int a;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,45 +7,45 @@ import 'dart:ffi';
|
||||||
/// Large sample struct for dart:ffi library.
|
/// Large sample struct for dart:ffi library.
|
||||||
class VeryLargeStruct extends Struct {
|
class VeryLargeStruct extends Struct {
|
||||||
@Int8()
|
@Int8()
|
||||||
int a;
|
external int a;
|
||||||
|
|
||||||
@Int16()
|
@Int16()
|
||||||
int b;
|
external int b;
|
||||||
|
|
||||||
@Int32()
|
@Int32()
|
||||||
int c;
|
external int c;
|
||||||
|
|
||||||
@Int64()
|
@Int64()
|
||||||
int d;
|
external int d;
|
||||||
|
|
||||||
@Uint8()
|
@Uint8()
|
||||||
int e;
|
external int e;
|
||||||
|
|
||||||
@Uint16()
|
@Uint16()
|
||||||
int f;
|
external int f;
|
||||||
|
|
||||||
@Uint32()
|
@Uint32()
|
||||||
int g;
|
external int g;
|
||||||
|
|
||||||
@Uint64()
|
@Uint64()
|
||||||
int h;
|
external int h;
|
||||||
|
|
||||||
@IntPtr()
|
@IntPtr()
|
||||||
int i;
|
external int i;
|
||||||
|
|
||||||
@Double()
|
@Double()
|
||||||
double j;
|
external double j;
|
||||||
|
|
||||||
@Float()
|
@Float()
|
||||||
double k;
|
external double k;
|
||||||
|
|
||||||
Pointer<VeryLargeStruct> parent;
|
external Pointer<VeryLargeStruct> parent;
|
||||||
|
|
||||||
@IntPtr()
|
@IntPtr()
|
||||||
int numChildren;
|
external int numChildren;
|
||||||
|
|
||||||
Pointer<VeryLargeStruct> children;
|
external Pointer<VeryLargeStruct> children;
|
||||||
|
|
||||||
@Int8()
|
@Int8()
|
||||||
int smallLastField;
|
external int smallLastField;
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,9 +309,9 @@ void testNativeFunctionSignatureInvalidOptionalPositional() {
|
||||||
// error on missing field annotation
|
// error on missing field annotation
|
||||||
class TestStruct extends Struct {
|
class TestStruct extends Struct {
|
||||||
@Double()
|
@Double()
|
||||||
double x;
|
external double x;
|
||||||
|
|
||||||
double y; //# 50: compile-time error
|
external double y; //# 50: compile-time error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot extend structs.
|
// Cannot extend structs.
|
||||||
|
@ -321,25 +321,25 @@ class TestStruct3 extends TestStruct {} //# 52: compile-time error
|
||||||
class TestStruct4 extends Struct {
|
class TestStruct4 extends Struct {
|
||||||
@Double()
|
@Double()
|
||||||
@Double() //# 53: compile-time error
|
@Double() //# 53: compile-time error
|
||||||
double z;
|
external double z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// error on annotation not matching up
|
// error on annotation not matching up
|
||||||
class TestStruct5 extends Struct {
|
class TestStruct5 extends Struct {
|
||||||
@Int64() //# 54: compile-time error
|
@Int64() //# 54: compile-time error
|
||||||
double z; //# 54: compile-time error
|
external double z; //# 54: compile-time error
|
||||||
}
|
}
|
||||||
|
|
||||||
// error on annotation not matching up
|
// error on annotation not matching up
|
||||||
class TestStruct6 extends Struct {
|
class TestStruct6 extends Struct {
|
||||||
@Void() //# 55: compile-time error
|
@Void() //# 55: compile-time error
|
||||||
double z; //# 55: compile-time error
|
external double z; //# 55: compile-time error
|
||||||
}
|
}
|
||||||
|
|
||||||
// error on annotation not matching up
|
// error on annotation not matching up
|
||||||
class TestStruct7 extends Struct {
|
class TestStruct7 extends Struct {
|
||||||
@NativeType() //# 56: compile-time error
|
@NativeType() //# 56: compile-time error
|
||||||
double z; //# 56: compile-time error
|
external double z; //# 56: compile-time error
|
||||||
}
|
}
|
||||||
|
|
||||||
// error on field initializer on field
|
// error on field initializer on field
|
||||||
|
@ -350,8 +350,8 @@ class TestStruct8 extends Struct {
|
||||||
|
|
||||||
// error on field initializer in constructor
|
// error on field initializer in constructor
|
||||||
class TestStruct9 extends Struct {
|
class TestStruct9 extends Struct {
|
||||||
@Double()
|
@Double() //# 58: compile-time error
|
||||||
double z;
|
double z; //# 58: compile-time error
|
||||||
|
|
||||||
TestStruct9() : z = 0.0 {} //# 58: compile-time error
|
TestStruct9() : z = 0.0 {} //# 58: compile-time error
|
||||||
}
|
}
|
||||||
|
@ -364,7 +364,7 @@ class TestStruct11<T> extends //# 60: compile-time error
|
||||||
// annotation).
|
// annotation).
|
||||||
class TestStruct12 extends Struct {
|
class TestStruct12 extends Struct {
|
||||||
@Pointer //# 61: compile-time error
|
@Pointer //# 61: compile-time error
|
||||||
TestStruct9 struct; //# 61: compile-time error
|
external TestStruct9 struct; //# 61: compile-time error
|
||||||
}
|
}
|
||||||
|
|
||||||
class DummyAnnotation {
|
class DummyAnnotation {
|
||||||
|
@ -375,7 +375,7 @@ class DummyAnnotation {
|
||||||
class TestStruct13 extends Struct {
|
class TestStruct13 extends Struct {
|
||||||
@DummyAnnotation()
|
@DummyAnnotation()
|
||||||
@Double()
|
@Double()
|
||||||
double z;
|
external double z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot extend native types.
|
// Cannot extend native types.
|
||||||
|
|
Loading…
Reference in a new issue