Skip to content

Commit 0c76728

Browse files
[jnigen] Use .jar metadata to generate generic types (#158)
Closes #132. ASM backend, parses classes, fields and method signatures from the metadata to add generic type information to other-wise type-erased generics.
1 parent f6998e5 commit 0c76728

File tree

22 files changed

+1195
-42
lines changed

22 files changed

+1195
-42
lines changed

pkgs/jnigen/example/in_app_java/jnigen.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ source_path:
1414
classes:
1515
- 'com.example.in_app_java.AndroidUtils' # from source_path
1616
- 'android.os.Build' # from gradle's compile classpath
17+
- 'java.util.HashMap' # from gradle's compile classpath

pkgs/jnigen/example/in_app_java/lib/android_utils.dart

Lines changed: 397 additions & 0 deletions
Large diffs are not rendered by default.

pkgs/jnigen/example/in_app_java/lib/main.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,24 @@ import 'android_utils.dart';
1111

1212
JObject activity = JObject.fromRef(Jni.getCurrentActivity());
1313

14-
/// Display device model number as Toast
14+
final hashmap = HashMap.ctor2(JString.type, JString.type);
15+
16+
extension IntX on int {
17+
JString toJString() {
18+
return toString().toJString();
19+
}
20+
}
21+
22+
/// Display device model number and the number of times this was called
23+
/// as Toast.
1524
void showToast() {
16-
AndroidUtils.showToast(activity, Build.MODEL, 0);
25+
final toastCount =
26+
hashmap.getOrDefault("toastCount".toJString(), 0.toJString());
27+
final newToastCount = (int.parse(toastCount.toDartString()) + 1).toJString();
28+
hashmap.put("toastCount".toJString(), newToastCount);
29+
final message =
30+
'${newToastCount.toDartString()} - ${Build.MODEL.toDartString()}';
31+
AndroidUtils.showToast(activity, message.toJString(), 0);
1732
}
1833

1934
void main() {

pkgs/jnigen/example/in_app_java/src/android_utils/android_utils.c

Lines changed: 469 additions & 0 deletions
Large diffs are not rendered by default.

pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ class PDDocument extends jni.JObject {
484484
///@return a <code>List</code> of <code>PDSignatureField</code>s
485485
///@throws IOException if no document catalog can be found.
486486
jni.JObject getSignatureFields() =>
487-
jni.JObjectType().fromRef(_getSignatureFields(reference).object);
487+
const jni.JObjectType().fromRef(_getSignatureFields(reference).object);
488488

489489
static final _getSignatureDictionaries = jniLookup<
490490
ffi.NativeFunction<
@@ -498,8 +498,8 @@ class PDDocument extends jni.JObject {
498498
/// Retrieve all signature dictionaries from the document.
499499
///@return a <code>List</code> of <code>PDSignatureField</code>s
500500
///@throws IOException if no document catalog can be found.
501-
jni.JObject getSignatureDictionaries() =>
502-
jni.JObjectType().fromRef(_getSignatureDictionaries(reference).object);
501+
jni.JObject getSignatureDictionaries() => const jni.JObjectType()
502+
.fromRef(_getSignatureDictionaries(reference).object);
503503

504504
static final _registerTrueTypeFontForClosing = jniLookup<
505505
ffi.NativeFunction<

pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ class PDDocumentInformation extends jni.JObject {
377377
///@return all metadata key strings.
378378
///@since Apache PDFBox 1.3.0
379379
jni.JObject getMetadataKeys() =>
380-
jni.JObjectType().fromRef(_getMetadataKeys(reference).object);
380+
const jni.JObjectType().fromRef(_getMetadataKeys(reference).object);
381381

382382
static final _getCustomMetadataValue = jniLookup<
383383
ffi.NativeFunction<

pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ class PDFTextStripper extends jni.JObject {
100100
/// text after second article
101101
///
102102
/// Most PDFs won't have any beads, so charactersByArticle will contain a single entry.
103-
jni.JObject get charactersByArticle =>
104-
jni.JObjectType().fromRef(_get_charactersByArticle(reference).object);
103+
jni.JObject get charactersByArticle => const jni.JObjectType()
104+
.fromRef(_get_charactersByArticle(reference).object);
105105
static final _set_charactersByArticle = jniLookup<
106106
ffi.NativeFunction<
107107
jni.JThrowablePtr Function(
@@ -642,8 +642,8 @@ class PDFTextStripper extends jni.JObject {
642642
/// Character strings are grouped by articles. It is quite common that there will only be a single article. This
643643
/// returns a List that contains List objects, the inner lists will contain TextPosition objects.
644644
///@return A double List of TextPositions for all text strings on the page.
645-
jni.JObject getCharactersByArticle() =>
646-
jni.JObjectType().fromRef(_getCharactersByArticle(reference).object);
645+
jni.JObject getCharactersByArticle() => const jni.JObjectType()
646+
.fromRef(_getCharactersByArticle(reference).object);
647647

648648
static final _setSuppressDuplicateOverlappingText = jniLookup<
649649
ffi.NativeFunction<
@@ -1191,7 +1191,7 @@ class PDFTextStripper extends jni.JObject {
11911191
/// This method returns a list of such regular expression Patterns.
11921192
///@return a list of Pattern objects.
11931193
jni.JObject getListItemPatterns() =>
1194-
jni.JObjectType().fromRef(_getListItemPatterns(reference).object);
1194+
const jni.JObjectType().fromRef(_getListItemPatterns(reference).object);
11951195

11961196
static final _matchPattern = jniLookup<
11971197
ffi.NativeFunction<
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
package com.github.dart_lang.jnigen.apisummarizer.disasm;
6+
7+
import com.github.dart_lang.jnigen.apisummarizer.elements.ClassDecl;
8+
import com.github.dart_lang.jnigen.apisummarizer.elements.TypeParam;
9+
import com.github.dart_lang.jnigen.apisummarizer.elements.TypeUsage;
10+
import org.objectweb.asm.signature.SignatureVisitor;
11+
12+
public class AsmClassSignatureVisitor extends SignatureVisitor {
13+
private final ClassDecl decl;
14+
private int interfaceIndex = -1;
15+
16+
public AsmClassSignatureVisitor(ClassDecl decl) {
17+
super(AsmConstants.API);
18+
this.decl = decl;
19+
}
20+
21+
@Override
22+
public void visitFormalTypeParameter(String name) {
23+
var typeParam = new TypeParam();
24+
typeParam.name = name;
25+
decl.typeParams.add(typeParam);
26+
}
27+
28+
@Override
29+
public SignatureVisitor visitClassBound() {
30+
var typeUsage = new TypeUsage();
31+
// ClassDecl initially has no type parameters. In visitFormalTypeParameter we add them
32+
// and sequentially visitClassBound and visitInterfaceBound.
33+
decl.typeParams.get(decl.typeParams.size() - 1).bounds.add(typeUsage);
34+
return new AsmTypeUsageSignatureVisitor(typeUsage);
35+
}
36+
37+
@Override
38+
public SignatureVisitor visitInterfaceBound() {
39+
var typeUsage = new TypeUsage();
40+
decl.typeParams.get(decl.typeParams.size() - 1).bounds.add(typeUsage);
41+
return new AsmTypeUsageSignatureVisitor(typeUsage);
42+
}
43+
44+
@Override
45+
public SignatureVisitor visitSuperclass() {
46+
return new AsmTypeUsageSignatureVisitor(decl.superclass);
47+
}
48+
49+
@Override
50+
public SignatureVisitor visitInterface() {
51+
interfaceIndex++;
52+
return new AsmTypeUsageSignatureVisitor(decl.interfaces.get(interfaceIndex));
53+
}
54+
}

pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil;
1313
import java.util.*;
1414
import org.objectweb.asm.*;
15+
import org.objectweb.asm.signature.SignatureReader;
1516

1617
public class AsmClassVisitor extends ClassVisitor implements AsmAnnotatedElementVisitor {
1718
private static Param param(
@@ -53,6 +54,10 @@ public void visit(
5354
current.superclass = TypeUtils.typeUsage(Type.getObjectType(superName), null);
5455
current.interfaces =
5556
StreamUtil.map(interfaces, i -> TypeUtils.typeUsage(Type.getObjectType(i), null));
57+
if (signature != null) {
58+
var reader = new SignatureReader(signature);
59+
reader.accept(new AsmClassSignatureVisitor(current));
60+
}
5661
super.visit(version, access, name, signature, superName, interfaces);
5762
}
5863

@@ -66,11 +71,16 @@ public FieldVisitor visitField(
6671
if (name.contains("$")) {
6772
return null;
6873
}
74+
6975
var field = new Field();
7076
field.name = name;
7177
field.type = TypeUtils.typeUsage(Type.getType(descriptor), signature);
7278
field.defaultValue = value;
7379
field.modifiers = TypeUtils.access(access);
80+
if (signature != null) {
81+
var reader = new SignatureReader(signature);
82+
reader.accept(new AsmTypeUsageSignatureVisitor(field.type));
83+
}
7484
peekVisiting().fields.add(field);
7585
return new AsmFieldVisitor(field);
7686
}
@@ -101,6 +111,10 @@ public MethodVisitor visitMethod(
101111
method.returnType = TypeUtils.typeUsage(type.getReturnType(), signature);
102112
method.modifiers = TypeUtils.access(access);
103113
method.params = params;
114+
if (signature != null) {
115+
var reader = new SignatureReader(signature);
116+
reader.accept(new AsmMethodSignatureVisitor(method));
117+
}
104118
peekVisiting().methods.add(method);
105119
return new AsmMethodVisitor(method);
106120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
package com.github.dart_lang.jnigen.apisummarizer.disasm;
6+
7+
import com.github.dart_lang.jnigen.apisummarizer.elements.Method;
8+
import com.github.dart_lang.jnigen.apisummarizer.elements.TypeParam;
9+
import com.github.dart_lang.jnigen.apisummarizer.elements.TypeUsage;
10+
import org.objectweb.asm.signature.SignatureVisitor;
11+
12+
public class AsmMethodSignatureVisitor extends SignatureVisitor {
13+
private final Method method;
14+
private int paramIndex = -1;
15+
16+
public AsmMethodSignatureVisitor(Method method) {
17+
super(AsmConstants.API);
18+
this.method = method;
19+
}
20+
21+
@Override
22+
public void visitFormalTypeParameter(String name) {
23+
var typeParam = new TypeParam();
24+
typeParam.name = name;
25+
method.typeParams.add(typeParam);
26+
}
27+
28+
@Override
29+
public SignatureVisitor visitClassBound() {
30+
var typeUsage = new TypeUsage();
31+
// Method initially has no type parameters. In visitFormalTypeParameter we add them
32+
// and sequentially visitClassBound and visitInterfaceBound.
33+
method.typeParams.get(method.typeParams.size() - 1).bounds.add(typeUsage);
34+
return new AsmTypeUsageSignatureVisitor(typeUsage);
35+
}
36+
37+
@Override
38+
public SignatureVisitor visitInterfaceBound() {
39+
var typeUsage = new TypeUsage();
40+
method.typeParams.get(method.typeParams.size() - 1).bounds.add(typeUsage);
41+
return new AsmTypeUsageSignatureVisitor(typeUsage);
42+
}
43+
44+
@Override
45+
public SignatureVisitor visitReturnType() {
46+
return new AsmTypeUsageSignatureVisitor(method.returnType);
47+
}
48+
49+
@Override
50+
public SignatureVisitor visitParameterType() {
51+
paramIndex++;
52+
return new AsmTypeUsageSignatureVisitor(method.params.get(paramIndex).type);
53+
}
54+
55+
@Override
56+
public SignatureVisitor visitExceptionType() {
57+
// Do nothing.
58+
return new AsmTypeUsageSignatureVisitor(new TypeUsage());
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
package com.github.dart_lang.jnigen.apisummarizer.disasm;
6+
7+
import com.github.dart_lang.jnigen.apisummarizer.elements.TypeUsage;
8+
import java.util.ArrayList;
9+
import org.objectweb.asm.signature.SignatureVisitor;
10+
11+
public class AsmTypeUsageSignatureVisitor extends SignatureVisitor {
12+
private final TypeUsage typeUsage;
13+
14+
public AsmTypeUsageSignatureVisitor(TypeUsage typeUsage) {
15+
super(AsmConstants.API);
16+
this.typeUsage = typeUsage;
17+
}
18+
19+
@Override
20+
public void visitBaseType(char descriptor) {
21+
typeUsage.kind = TypeUsage.Kind.PRIMITIVE;
22+
var name = "";
23+
switch (descriptor) {
24+
case 'Z':
25+
name = "boolean";
26+
break;
27+
case 'B':
28+
name = "byte";
29+
break;
30+
case 'C':
31+
name = "char";
32+
break;
33+
case 'D':
34+
name = "double";
35+
break;
36+
case 'F':
37+
name = "float";
38+
break;
39+
case 'I':
40+
name = "int";
41+
break;
42+
case 'J':
43+
name = "long";
44+
break;
45+
case 'L':
46+
name = "object";
47+
break;
48+
case 'S':
49+
name = "short";
50+
break;
51+
case 'V':
52+
name = "void";
53+
break;
54+
}
55+
typeUsage.shorthand = name;
56+
typeUsage.type = new TypeUsage.PrimitiveType(name);
57+
}
58+
59+
@Override
60+
public SignatureVisitor visitArrayType() {
61+
typeUsage.kind = TypeUsage.Kind.ARRAY;
62+
typeUsage.shorthand = "java.lang.Object[]";
63+
var elementType = new TypeUsage();
64+
typeUsage.type = new TypeUsage.Array(elementType);
65+
return new AsmTypeUsageSignatureVisitor(elementType);
66+
}
67+
68+
@Override
69+
public void visitTypeVariable(String name) {
70+
typeUsage.kind = TypeUsage.Kind.TYPE_VARIABLE;
71+
typeUsage.shorthand = name;
72+
typeUsage.type = new TypeUsage.TypeVar(name);
73+
}
74+
75+
@Override
76+
public void visitClassType(String name) {
77+
typeUsage.kind = TypeUsage.Kind.DECLARED;
78+
typeUsage.shorthand = name.substring(0, name.length()).replace('/', '.');
79+
var components = name.split("[/$]");
80+
var simpleName = components[components.length - 1];
81+
typeUsage.type = new TypeUsage.DeclaredType(name, simpleName, new ArrayList<>());
82+
}
83+
84+
@Override
85+
public SignatureVisitor visitTypeArgument(char wildcard) {
86+
// TODO(#141) support wildcards
87+
// TODO(#144) support extend/super clauses
88+
assert (typeUsage.type instanceof TypeUsage.DeclaredType);
89+
var typeArg = new TypeUsage();
90+
((TypeUsage.DeclaredType) typeUsage.type).params.add(typeArg);
91+
return new AsmTypeUsageSignatureVisitor(typeArg);
92+
}
93+
94+
@Override
95+
public void visitInnerClassType(String name) {
96+
super.visitInnerClassType(name);
97+
// TODO(#139) support nested generic classes
98+
}
99+
}

pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ public class ClassDecl {
3232

3333
public String parentName;
3434
public String packageName;
35-
public List<TypeParam> typeParams;
35+
public List<TypeParam> typeParams = new ArrayList<>();
3636
public List<Method> methods = new ArrayList<>();
3737
public List<Field> fields = new ArrayList<>();
3838
public TypeUsage superclass;
39-
public List<TypeUsage> interfaces;
39+
public List<TypeUsage> interfaces = new ArrayList<>();
4040
public boolean hasStaticInit;
4141
public boolean hasInstanceInit;
4242
public JavaDocComment javadoc;

pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/Method.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
public class Method {
1313
public Set<String> modifiers = new HashSet<>();
1414
public String name;
15-
public List<TypeParam> typeParams;
15+
public List<TypeParam> typeParams = new ArrayList<>();
1616
public List<Param> params = new ArrayList<>();
1717
public TypeUsage returnType;
1818

pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/TypeParam.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
package com.github.dart_lang.jnigen.apisummarizer.elements;
66

7+
import java.util.ArrayList;
78
import java.util.List;
89

910
public class TypeParam {
1011
public String name;
11-
public List<TypeUsage> bounds;
12+
public List<TypeUsage> bounds = new ArrayList<>();
1213
}

0 commit comments

Comments
 (0)