Skip to content

[pigeon] simplifies int handling across the codec, verifies ints in collections #7392

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 22.0.0

* [dart] Changes codec to send int64 instead of int32.
* **Breaking Change** [swift] Changes generic `map` to nullable keys of `AnyHashable` to conform to other platforms.
* Adds tests to validate collections of ints.

## 21.2.0

* Removes restriction on number of custom types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
case (byte) 129:
{
Object value = readValue(buffer);
return value == null ? null : Code.values()[(int) value];
return value == null ? null : Code.values()[((Long) value).intValue()];
}
case (byte) 130:
return MessageData.fromList((ArrayList<Object>) readValue(buffer));
Expand Down Expand Up @@ -339,13 +339,10 @@ static void setUp(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number aArg = (Number) args.get(0);
Number bArg = (Number) args.get(1);
Long aArg = (Long) args.get(0);
Long bArg = (Long) args.get(1);
try {
Long output =
api.add(
(aArg == null) ? null : aArg.longValue(),
(bArg == null) ? null : bArg.longValue());
Long output = api.add(aArg, bArg);
wrapped.add(0, output);
} catch (Throwable exception) {
wrapped = wrapError(exception);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private object MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
129.toByte() -> {
return (readValue(buffer) as Int?)?.let { Code.ofRaw(it) }
return (readValue(buffer) as Long?)?.let { Code.ofRaw(it.toInt()) }
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { MessageData.fromList(it) }
Expand Down Expand Up @@ -161,8 +161,8 @@ interface ExampleHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val aArg = args[0].let { num -> if (num is Int) num.toLong() else num as Long }
val bArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long }
val aArg = args[0] as Long
val bArg = args[1] as Long
val wrapped: List<Any?> =
try {
listOf(api.add(aArg, bArg))
Expand Down
5 changes: 4 additions & 1 deletion packages/pigeon/example/app/lib/src/messages.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is Code) {
if (value is int) {
buffer.putUint8(4);
buffer.putInt64(value);
} else if (value is Code) {
buffer.putUint8(129);
writeValue(buffer, value.index);
} else if (value is MessageData) {
Expand Down
16 changes: 2 additions & 14 deletions packages/pigeon/lib/cpp_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -867,9 +867,7 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
// Returns the expression to convert the given EncodableValue to a field
// value.
String getValueExpression(NamedType field, String encodable) {
if (field.type.baseName == 'int') {
return '$encodable.LongValue()';
} else if (field.type.baseName == 'Object') {
if (field.type.baseName == 'Object') {
return encodable;
} else {
final HostDatatype hostDatatype =
Expand Down Expand Up @@ -1636,17 +1634,7 @@ ${prefix}reply(EncodableValue(std::move(wrapped)));''';
if (hostType.isNullable) {
// Nullable arguments are always pointers, with nullptr corresponding to
// null.
if (hostType.datatype == 'int64_t') {
// The EncodableValue will either be an int32_t or an int64_t depending
// on the value, but the generated API requires an int64_t so that it can
// handle any case. Create a local variable for the 64-bit value...
final String valueVarName = '${argName}_value';
indent.writeln(
'const int64_t $valueVarName = $encodableArgName.IsNull() ? 0 : $encodableArgName.LongValue();');
// ... then declare the arg as a reference to that local.
indent.writeln(
'const auto* $argName = $encodableArgName.IsNull() ? nullptr : &$valueVarName;');
} else if (hostType.datatype == 'EncodableValue') {
if (hostType.datatype == 'EncodableValue') {
// Generic objects just pass the EncodableValue through directly.
indent.writeln('const auto* $argName = &$encodableArgName;');
} else if (hostType.isBuiltin) {
Expand Down
9 changes: 7 additions & 2 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ class DartGenerator extends StructuredGenerator<DartOptions> {
required String dartPackageName,
}) {
void writeEncodeLogic(EnumeratedType customType) {
indent.writeScoped('if (value is ${customType.name}) {', '} else ', () {
indent.writeScoped('else if (value is ${customType.name}) {', '}', () {
if (customType.enumeration < maximumCodecFieldKey) {
indent.writeln('buffer.putUint8(${customType.enumeration});');
if (customType.type == CustomTypes.customClass) {
Expand Down Expand Up @@ -343,11 +343,16 @@ class DartGenerator extends StructuredGenerator<DartOptions> {
indent.writeln('@override');
indent.write('void writeValue(WriteBuffer buffer, Object? value) ');
indent.addScoped('{', '}', () {
indent.writeScoped('if (value is int) {', '}', () {
indent.writeln('buffer.putUint8(4);');
indent.writeln('buffer.putInt64(value);');
}, addTrailingNewline: false);

enumerate(enumeratedTypes,
(int index, final EnumeratedType customType) {
writeEncodeLogic(customType);
});
indent.addScoped('{', '}', () {
indent.addScoped(' else {', '}', () {
indent.writeln('super.writeValue(buffer, value);');
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'ast.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '21.2.0';
const String pigeonVersion = '22.0.0';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down
64 changes: 18 additions & 46 deletions packages/pigeon/lib/java_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ const String _codecName = 'PigeonCodec';

const String _overflowClassName = '${classNamePrefix}CodecOverflow';

// Used to create classes with type Int rather than long.
const String _forceInt = '${varNamePrefix}forceInt';

/// Options that control how Java code will be generated.
class JavaOptions {
/// Creates a [JavaOptions] object
Expand Down Expand Up @@ -253,18 +250,11 @@ class JavaGenerator extends StructuredGenerator<JavaOptions> {
}

void _writeClassField(
JavaOptions generatorOptions,
Indent indent,
NamedType field, {
bool isPrimitive = false,
}) {
JavaOptions generatorOptions, Indent indent, NamedType field) {
final HostDatatype hostDatatype = getFieldHostDatatype(
field, (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
final String nullability = isPrimitive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this being removed? Don't we still use primitive types for non-nullable cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the isPrimitive field isn't being used any longer. This may need to be added back in later though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was only using it for the forceInt type, which isn't needed any longer

? ''
: field.type.isNullable
? '@Nullable '
: '@NonNull ';
final String nullability =
field.type.isNullable ? '@Nullable ' : '@NonNull ';
addDocumentationComments(
indent, field.documentationComments, _docCommentSpec);

Expand All @@ -280,7 +270,7 @@ class JavaGenerator extends StructuredGenerator<JavaOptions> {
indent.writeScoped(
'public void ${_makeSetter(field)}($nullability${hostDatatype.datatype} setterArg) {',
'}', () {
if (!field.type.isNullable && !isPrimitive) {
if (!field.type.isNullable) {
indent.writeScoped('if (setterArg == null) {', '}', () {
indent.writeln(
'throw new IllegalStateException("Nonnull field \\"${field.name}\\" is null.");');
Expand All @@ -306,7 +296,6 @@ class JavaGenerator extends StructuredGenerator<JavaOptions> {
generatorOptions,
indent,
field,
isPrimitive: field.type.baseName == _forceInt,
);
indent.newln();
}
Expand Down Expand Up @@ -490,7 +479,7 @@ class JavaGenerator extends StructuredGenerator<JavaOptions> {
indent
.writeln('$_overflowClassName wrap = new $_overflowClassName();');
indent.writeln(
'wrap.setType(${customType.enumeration - maximumCodecFieldKey});');
'wrap.setType(${customType.enumeration - maximumCodecFieldKey}L);');
indent.writeln(
'wrap.setWrapped($nullCheck((${customType.name}) value).$encodeString);');
}
Expand Down Expand Up @@ -580,7 +569,7 @@ class JavaGenerator extends StructuredGenerator<JavaOptions> {
}) {
final NamedType overflowInteration = NamedType(
name: 'type',
type: const TypeDeclaration(baseName: _forceInt, isNullable: false));
type: const TypeDeclaration(baseName: 'int', isNullable: false));
final NamedType overflowObject = NamedType(
name: 'wrapped',
type: const TypeDeclaration(baseName: 'Object', isNullable: true));
Expand All @@ -607,7 +596,7 @@ class JavaGenerator extends StructuredGenerator<JavaOptions> {
indent.format('''
static @Nullable Object fromList(@NonNull ArrayList<Object> ${varNamePrefix}list) {
$_overflowClassName wrapper = new $_overflowClassName();
wrapper.setType((int) ${varNamePrefix}list.get(0));
wrapper.setType((Long) ${varNamePrefix}list.get(0));
wrapper.setWrapped(${varNamePrefix}list.get(1));
return wrapper.unwrap();
}
Expand All @@ -619,7 +608,7 @@ if (wrapped == null) {
return null;
}
''');
indent.writeScoped('switch (type) {', '}', () {
indent.writeScoped('switch (type.intValue()) {', '}', () {
for (int i = totalCustomCodecKeysAllowed; i < types.length; i++) {
indent.writeln('case ${i - totalCustomCodecKeysAllowed}:');
indent.nest(1, () {
Expand All @@ -628,7 +617,7 @@ if (wrapped == null) {
'return ${types[i].name}.fromList((ArrayList<Object>) wrapped);');
} else if (types[i].type == CustomTypes.customEnum) {
indent.writeln(
'return ${types[i].name}.values()[(int) wrapped];');
'return ${_intToEnum('wrapped', types[i].name, false)};');
}
});
}
Expand Down Expand Up @@ -766,13 +755,8 @@ if (wrapped == null) {
const String output = 'output';
final String outputExpression;
indent.writeln('@SuppressWarnings("ConstantConditions")');
if (func.returnType.baseName == 'int') {
outputExpression =
'listReply.get(0) == null ? null : ((Number) listReply.get(0)).longValue();';
} else {
outputExpression =
'${_cast('listReply.get(0)', javaType: returnType)};';
}
outputExpression =
'${_cast('listReply.get(0)', javaType: returnType)};';
indent.writeln('$returnType $output = $outputExpression');
indent.writeln('result.success($output);');
}
Expand Down Expand Up @@ -951,16 +935,9 @@ if (wrapped == null) {
indent.writeln(
'ArrayList<Object> args = (ArrayList<Object>) message;');
enumerate(method.parameters, (int index, NamedType arg) {
// The StandardMessageCodec can give us [Integer, Long] for
// a Dart 'int'. To keep things simple we just use 64bit
// longs in Pigeon with Java.
final bool isInt = arg.type.baseName == 'int';
final String argType =
isInt ? 'Number' : _javaTypeForDartType(arg.type);
final String argType = _javaTypeForDartType(arg.type);
final String argName = _getSafeArgumentName(index, arg);
final String argExpression = isInt
? '($argName == null) ? null : $argName.longValue()'
: argName;
final String argExpression = argName;
String accessor = 'args.get($index)';
if (argType != 'Object') {
accessor = _cast(accessor, javaType: argType);
Expand Down Expand Up @@ -1176,9 +1153,10 @@ protected static ArrayList<Object> wrapError(@NonNull Throwable exception) {

/// Converts an expression that evaluates to an nullable int to an expression
/// that evaluates to a nullable enum.
String _intToEnum(String expression, String enumName, bool nullable) => nullable
? '$expression == null ? null : $enumName.values()[(int) $expression]'
: '$enumName.values()[(int) $expression]';
String _intToEnum(String expression, String enumName, bool nullable) {
final String toEnum = '$enumName.values()[((Long) $expression).intValue()]';
return nullable ? '$expression == null ? null : $toEnum' : toEnum;
}

String _getArgumentName(int count, NamedType argument) =>
argument.name.isEmpty ? 'arg$count' : argument.name;
Expand Down Expand Up @@ -1231,8 +1209,6 @@ String? _javaTypeForBuiltinDartType(TypeDeclaration type) {
'Int64List': 'long[]',
'Float64List': 'double[]',
'Object': 'Object',
// This is used to allow the creation of true `int`s for the codec overflow class.
_forceInt: 'int',
};
if (javaTypeForDartTypeMap.containsKey(type.baseName)) {
return javaTypeForDartTypeMap[type.baseName];
Expand Down Expand Up @@ -1271,11 +1247,7 @@ String _cast(String variable, {required String javaType}) {
String _castObject(NamedType field, String varName) {
final HostDatatype hostDatatype = getFieldHostDatatype(
field, (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
if (field.type.baseName == 'int') {
return '($varName == null) ? null : (($varName instanceof Integer) ? (Integer) $varName : (${hostDatatype.datatype}) $varName)';
} else {
return _cast(varName, javaType: hostDatatype.datatype);
}
return _cast(varName, javaType: hostDatatype.datatype);
}

/// Returns string of Result class type for method based on [TypeDeclaration].
Expand Down
21 changes: 7 additions & 14 deletions packages/pigeon/lib/kotlin_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,9 @@ class KotlinGenerator extends StructuredGenerator<KotlinOptions> {
indent.writeln('${customType.name}.fromList(it)');
});
} else if (customType.type == CustomTypes.customEnum) {
indent.write('return (readValue(buffer) as Int?)?.let ');
indent.write('return (readValue(buffer) as Long?)?.let ');
indent.addScoped('{', '}', () {
indent.writeln('${customType.name}.ofRaw(it)');
indent.writeln('${customType.name}.ofRaw(it.toInt())');
});
}
});
Expand Down Expand Up @@ -418,7 +418,7 @@ class KotlinGenerator extends StructuredGenerator<KotlinOptions> {
}) {
final NamedType overflowInt = NamedType(
name: 'type',
type: const TypeDeclaration(baseName: 'Int', isNullable: false));
type: const TypeDeclaration(baseName: 'int', isNullable: false));
final NamedType overflowObject = NamedType(
name: 'wrapped',
type: const TypeDeclaration(baseName: 'Object', isNullable: true));
Expand All @@ -445,7 +445,7 @@ class KotlinGenerator extends StructuredGenerator<KotlinOptions> {
companion object {
fun fromList(${varNamePrefix}list: List<Any?>): Any? {
val wrapper = ${generatorOptions.fileSpecificClassNameComponent}$_overflowClassName(
type = ${varNamePrefix}list[0] as Int,
type = ${varNamePrefix}list[0] as Long,
wrapped = ${varNamePrefix}list[1],
);
return wrapper.unwrap()
Expand All @@ -459,14 +459,15 @@ if (wrapped == null) {
return null
}
''');
indent.writeScoped('when (type) {', '}', () {
indent.writeScoped('when (type.toInt()) {', '}', () {
for (int i = totalCustomCodecKeysAllowed; i < types.length; i++) {
indent.writeScoped('${i - totalCustomCodecKeysAllowed} ->', '', () {
if (types[i].type == CustomTypes.customClass) {
indent.writeln(
'return ${types[i].name}.fromList(wrapped as List<Any?>)');
} else if (types[i].type == CustomTypes.customEnum) {
indent.writeln('return ${types[i].name}.ofRaw(wrapped as Int)');
indent.writeln(
'return ${types[i].name}.ofRaw((wrapped as Long).toInt())');
}
});
}
Expand Down Expand Up @@ -1009,13 +1010,5 @@ String _cast(Indent indent, String variable, {required TypeDeclaration type}) {
if (type.isNullable && typeString == 'Any') {
return variable;
}
if (typeString == 'Int' || typeString == 'Long') {
return '$variable${_castInt(type.isNullable)}';
}
return '$variable as ${_nullSafeKotlinTypeForDartType(type)}';
}

String _castInt(bool isNullable) {
final String nullability = isNullable ? '?' : '';
return '.let { num -> if (num is Int) num.toLong() else num as Long$nullability }';
}
1 change: 0 additions & 1 deletion packages/pigeon/lib/objc_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1440,7 +1440,6 @@ String? _nsnumberExtractionMethod(
/// Example: ('FOO', ['Foo', 'Bar']) -> 'FOOFoo *, FOOBar *').
String _flattenTypeArguments(String? classPrefix, List<TypeDeclaration> args) {
final String result = args.map<String>((TypeDeclaration e) {
// print(e);
if (e.isEnum) {
return _enumName(e.baseName,
prefix: classPrefix, box: true, suffix: ' *');
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/swift_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ String _swiftTypeForBuiltinGenericDartType(TypeDeclaration type) {
if (type.baseName == 'List') {
return '[Any?]';
} else if (type.baseName == 'Map') {
return '[AnyHashable: Any?]';
return '[AnyHashable?: Any?]';
} else {
return 'Any';
}
Expand Down
Loading