blob: 1da7f185b89ed94f7e7a8b6e159e11dc87570794 [file] [log] [blame] [edit]
// Copyright (c) 2024, 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 'package:jnigen/jnigen.dart';
import 'package:jnigen/src/bindings/linker.dart';
import 'package:jnigen/src/bindings/renamer.dart';
import 'package:test/test.dart';
extension on Iterable<ClassMember> {
List<String> get finalNames => map((c) => c.finalName).toList();
}
Future<void> rename(Classes classes) async {
final config = Config(
outputConfig: OutputConfig(
dartConfig: DartCodeOutputConfig(
path: Uri.file('test.dart'),
structure: OutputStructure.singleFile,
),
),
classes: [],
);
await classes.accept(Linker(config));
classes.accept(Renamer(config));
}
void main() {
test('Overloading elements that end with a number', () async {
final classes = Classes({
'Foo': ClassDecl(
binaryName: 'Foo',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: 'foo', returnType: DeclaredType.object),
Method(name: 'foo', returnType: DeclaredType.object),
Method(name: 'foo', returnType: DeclaredType.object),
Method(name: 'foo1', returnType: DeclaredType.object),
Method(name: 'foo1', returnType: DeclaredType.object),
Method(name: 'foo1', returnType: DeclaredType.object),
],
),
'x.Foo': ClassDecl(
binaryName: 'x.Foo',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: 'foo', returnType: DeclaredType.object),
Method(name: 'foo', returnType: DeclaredType.object),
Method(name: 'foo1', returnType: DeclaredType.object),
Method(name: 'foo1', returnType: DeclaredType.object),
],
fields: [
Field(name: 'foo', type: DeclaredType.object),
Field(name: 'foo1', type: DeclaredType.object),
],
),
'y.Foo': ClassDecl(
binaryName: 'y.Foo',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
),
'Foo1': ClassDecl(
binaryName: 'Foo1',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
),
'x.Foo1': ClassDecl(
binaryName: 'x.Foo1',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
),
'y.Foo1': ClassDecl(
binaryName: 'y.Foo1',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
),
});
await rename(classes);
final renamedClasses = classes.decls.values.finalNames;
expect(renamedClasses, [
'Foo',
r'Foo$1',
r'Foo$2',
'Foo1',
r'Foo1$1',
r'Foo1$2',
]);
final renamedMethods = classes.decls['Foo']!.methods.finalNames;
expect(renamedMethods, [
'foo',
r'foo$1',
r'foo$2',
'foo1',
r'foo1$1',
r'foo1$2',
]);
final renamedFields = classes.decls['x.Foo']!.fields.finalNames;
// Fields are renamed before methods. So they always keep their original
// name (if not renamed for a different reason).
expect(renamedFields, [
'foo',
'foo1',
]);
final xFooMethods = classes.decls['x.Foo']!.methods.finalNames;
expect(xFooMethods, [
r'foo$1',
r'foo$2',
r'foo1$1',
r'foo1$2',
]);
});
test('Field with the same name as inherited method gets renamed', () async {
final classes = Classes({
'Player': ClassDecl(
binaryName: 'Player',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: 'duck', returnType: DeclaredType.object),
],
),
'DuckOwningPlayer': ClassDecl(
binaryName: 'DuckOwningPlayer',
declKind: DeclKind.classKind,
superclass: DeclaredType(binaryName: 'Player'),
fields: [
Field(name: 'duck', type: DeclaredType.object),
],
),
});
await rename(classes);
final renamedMethods = classes.decls['Player']!.methods.finalNames;
expect(renamedMethods, ['duck']);
final renamedFields = classes.decls['DuckOwningPlayer']!.fields.finalNames;
expect(renamedFields, [r'duck$1']);
});
test('Keywords in names', () async {
final classes = Classes({
'Foo': ClassDecl(
binaryName: 'Foo',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: 'yield', returnType: DeclaredType.object),
Method(name: 'const', returnType: DeclaredType.object),
Method(name: 'const', returnType: DeclaredType.object),
],
fields: [
Field(name: 'const', type: DeclaredType.object),
],
),
'Function': ClassDecl(
binaryName: 'Function',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
),
});
await rename(classes);
final renamedMethods = classes.decls['Foo']!.methods.finalNames;
expect(renamedMethods, [r'yield$', r'const$1', r'const$2']);
final renamedFields = classes.decls['Foo']!.fields.finalNames;
expect(renamedFields, [r'const$']);
final renamedClasses = classes.decls.values.finalNames;
expect(renamedClasses, ['Foo', r'Function$']);
});
test('Names with existing dollar signs', () async {
final classes = Classes({
'Foo': ClassDecl(
binaryName: 'Foo',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: 'foo', returnType: DeclaredType.object),
Method(name: 'foo', returnType: DeclaredType.object),
Method(name: r'foo$1', returnType: DeclaredType.object),
Method(
name: r'$$Many$Dollar$$Signs$', returnType: DeclaredType.object),
Method(name: 'alsoAField', returnType: DeclaredType.object),
Method(name: 'alsoAField', returnType: DeclaredType.object),
],
fields: [
Field(name: r'alsoAField', type: DeclaredType.object),
Field(name: r'alsoAField$1', type: DeclaredType.object),
],
),
});
await rename(classes);
final renamedMethods = classes.decls['Foo']!.methods.finalNames;
expect(renamedMethods, [
'foo',
r'foo$1',
r'foo$$1',
r'$$$$Many$$Dollar$$$$Signs$$',
r'alsoAField$1',
r'alsoAField$2',
]);
final renamedFields = classes.decls['Foo']!.fields.finalNames;
expect(renamedFields, [
'alsoAField',
r'alsoAField$$1',
]);
});
test('Names that start with underscores', () async {
final classes = Classes({
'_Foo': ClassDecl(
binaryName: '_Foo',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: '_foo', returnType: DeclaredType.object),
],
fields: [
Field(name: '_bar', type: DeclaredType.object),
],
),
});
await rename(classes);
final renamedMethods = classes.decls['_Foo']!.methods.finalNames;
expect(renamedMethods, [r'$_foo']);
final renamedFields = classes.decls['_Foo']!.fields.finalNames;
expect(renamedFields, [r'$_bar']);
final renamedClasses = classes.decls.values.finalNames;
expect(renamedClasses, [r'$_Foo']);
});
test('Combination of underscore, dollar signs, and overloading', () async {
// TODO(https://github.com/dart-lang/native/issues/1544): Test class names
// containing dollar signs.
final classes = Classes({
r'_Foo$': ClassDecl(
binaryName: r'_Foo$',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: r'_foo$', returnType: DeclaredType.object),
Method(name: r'_foo$', returnType: DeclaredType.object),
],
fields: [
Field(name: r'_foo$', type: DeclaredType.object),
],
),
});
await rename(classes);
final renamedMethods = classes.decls[r'_Foo$']!.methods.finalNames;
expect(renamedMethods, [r'$_foo$$$1', r'$_foo$$$2']);
final renamedFields = classes.decls[r'_Foo$']!.fields.finalNames;
expect(renamedFields, [r'$_foo$$']);
});
test('Interface implementation methods', () async {
final classes = Classes({
'MyInterface': ClassDecl(
binaryName: 'MyInterface',
declKind: DeclKind.interfaceKind,
superclass: DeclaredType.object,
methods: [
Method(name: 'implement', returnType: DeclaredType.object),
Method(name: 'implementIn', returnType: DeclaredType.object),
],
),
'MyClass': ClassDecl(
binaryName: 'MyClass',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [
Method(name: 'implement', returnType: DeclaredType.object),
Method(name: 'implementIn', returnType: DeclaredType.object),
],
),
});
await rename(classes);
final interfaceRenamedMethods =
classes.decls['MyInterface']!.methods.finalNames;
expect(interfaceRenamedMethods, [r'implement$1', r'implementIn$1']);
final classRenamedMethods = classes.decls['MyClass']!.methods.finalNames;
expect(classRenamedMethods, [r'implement', r'implementIn']);
});
test('Inner classes vs classes with dollar signs', () async {
final classes = Classes({
'Outer': ClassDecl(
binaryName: 'Outer',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [],
),
r'Outer$Inner': ClassDecl(
binaryName: r'Outer$Inner',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [],
outerClassBinaryName: 'Outer',
),
r'Outer$Inner$Innermost': ClassDecl(
binaryName: r'Outer$Inner$Innermost',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [],
outerClassBinaryName: r'Outer$Inner',
),
r'Outer$With$Many$Dollarsigns': ClassDecl(
binaryName: r'Outer$With$Many$Dollarsigns',
declKind: DeclKind.classKind,
superclass: DeclaredType.object,
methods: [],
),
});
await rename(classes);
expect(classes.decls['Outer']!.finalName, 'Outer');
expect(classes.decls[r'Outer$Inner']!.finalName, r'Outer$Inner');
expect(classes.decls[r'Outer$Inner$Innermost']!.finalName,
r'Outer$Inner$Innermost');
expect(classes.decls[r'Outer$With$Many$Dollarsigns']!.finalName,
r'Outer$$With$$Many$$Dollarsigns');
});
}