Skip to content
Open
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
26 changes: 24 additions & 2 deletions pkgs/jnigen/lib/src/summary/summary.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ class SummaryParseException implements Exception {
String toString() => message;
}

final _unsupportedClassFileVersionRegex = RegExp(
r'Unsupported class file major version\s+(\d+)',
);

String? getActionableSummaryParseMessage(String stderr) {
final match = _unsupportedClassFileVersionRegex.firstMatch(stderr);
if (match == null) {
return null;
}

final majorVersion = match.group(1);
return 'Cannot generate summary: Java class file version $majorVersion is '
'not supported by the summarizer. This usually means your input classes '
'were compiled with a newer JDK target than JNIgen supports. Use a '
'supported JDK version (11 to 17) (see JNIgen README), or recompile '
'your Java inputs with a lower target (for example: javac --release '
'17 <your-java-files>).';
}

/// A command based summary source which calls the ApiSummarizer command.
/// [sourcePaths] and [classPaths] can be provided for the summarizer to find
/// required dependencies. The [classes] argument specifies the fully qualified
Expand Down Expand Up @@ -179,9 +198,12 @@ Future<Classes> getSummary(Config config) async {
log.info('Parsing inputs took ${stopwatch.elapsedMilliseconds} ms');
} on Exception catch (e) {
await process.exitCode;
final stderr = stderrBuffer.toString();
final message = getActionableSummaryParseMessage(stderr) ??
'Cannot generate summary: $e';
throw SummaryParseException.withStderr(
stderrBuffer.toString(),
'Cannot generate summary: $e',
stderr,
message,
);
} finally {
log.writeSectionToFile('summarizer logs', stderrBuffer.toString());
Expand Down
32 changes: 32 additions & 0 deletions pkgs/jnigen/test/summary_error_message_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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/src/summary/summary.dart';
import 'package:test/test.dart';

void main() {
group('getActionableSummaryParseMessage', () {
test('returns actionable message for unsupported classfile version', () {
const stderr = '''
Exception in thread "main" java.lang.RuntimeException
Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 66
''';

final message = getActionableSummaryParseMessage(stderr);

expect(message, isNotNull);
expect(message, contains('class file version 66'));
expect(message, contains('JDK version (11 to 17) (see JNIgen README)'));
expect(message, contains('javac --release 17'));
});

test('returns null for unrelated stderr', () {
const stderr = 'Not found: [com.github.dart_lang.jnigen.DoesNotExist]';

final message = getActionableSummaryParseMessage(stderr);

expect(message, isNull);
});
});
}
46 changes: 46 additions & 0 deletions pkgs/jnigen/test/summary_generation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@Tags(['summarizer_test'])
library;

import 'dart:io';
import 'dart:math';

import 'package:jnigen/src/config/config.dart';
Expand Down Expand Up @@ -141,6 +142,51 @@ void main() async {
testAllCases(classPath: [targetDir.path]);
});

group('Test unsupported class file version errors', () {
final sourceDir = tempDir.createTempSync('unsupported_classfile_source_');
final classesDir = tempDir.createTempSync('unsupported_classfile_classes_');

setUpAll(() async {
final packageDir = Directory(join(sourceDir.path, 'com', 'example'))
..createSync(recursive: true);
final javaFile = File(join(packageDir.path, 'Hello.java'));
javaFile.writeAsStringSync('''
package com.example;
public class Hello {
public int value() { return 42; }
}
''');
await compileJavaFiles(sourceDir, classesDir);

//in class file encoding the version as 74 so unsupported
//and trigggers for test
final classFile = File(
join(classesDir.path, 'com', 'example', 'Hello.class'),
);
final classFileBytes = classFile.readAsBytesSync();
classFileBytes[6] = 0x00;
classFileBytes[7] = 0x4A;
classFile.writeAsBytesSync(classFileBytes, flush: true);
});

test('- should provide actionable guidance for unsupported versions',
() async {
final config = getSummaryGenerationConfig(classPath: [classesDir.path]);
config.classes = ['com.example.Hello'];

try {
await getSummary(config);
} on SummaryParseException catch (e) {
expect(e.message, contains('Java class file version 74'));
expect(e.message, contains('supported JDK version (11 to 17)'));
expect(e.message, contains('javac --release 17'));
expect(e.message, isNot(contains('FormatException')));
return;
}
throw AssertionError('No exception was caught');
});
});

// Test summary generation from combination of a source and class path
group('Test summary generation from combination', () {
final targetDir = tempDir.createTempSync('combination_test_');
Expand Down