dart-lang/sdk

Background isolate throws `No top-level getter 'xxx' declared` when app is obfuscated

Opened this issue · 5 comments

absar commented

When app is obfuscated background isolates run by flutter_workmanager throw Dart Unhandled Exception: NoSuchMethodError: No top-level getter 'xxx' declared in Flutter 2.10 and 3. However it works fine without obfuscation in both Flutter 2.10 and 3. It also works with obfuscation in Flutter 2.8.

Steps to reproduce:

  • Add workmanager: ^0.5.0 dependency in pubspec.yaml
  • Create a new flutter App with this code in main.dart
Code
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';

void bgCallback() {
Workmanager().executeTask((task, inputData) async {
  WidgetsFlutterBinding.ensureInitialized();
  debugPrint('bgCallback | $task was executed. inputData = $inputData');
  return Future.value(true);
});
}

void main() {
WidgetsFlutterBinding.ensureInitialized();
Workmanager().initialize(bgCallback, isInDebugMode: true);
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    theme: ThemeData(primarySwatch: Colors.blue),
    home: const MyHomePage(),
  );
}
}

class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
  return Center(
    child: ElevatedButton(
      onPressed: scheduleJob,
      child: const Text('Run BG tasks'),
    ),
  );
}

Future scheduleJob() async {
  await Workmanager().registerOneOffTask(
    'aTestJob',
    'aTestJob',
    existingWorkPolicy: ExistingWorkPolicy.replace,
  );
}
}
  • Build the app with obfuscation using: flutter build apk --obfuscate --split-debug-info=./build/app/outputs/obfuscation_symbols

  • This will generate the APK
    image

  • Install app-release.apk on a physical Android device or a 64bit emulator

  • Run the App and press Run BG tasks, this should schedule a one time Android workmanager job, which should ideally run bgCallback, you will see in the notification that workmanager is trying to run the callback
    image

  • Expected result is to run bgCallback, but you can see in logcat, it will throw Dart Unhandled Exception: NoSuchMethodError: No top-level getter 'xxx' declared.

Complete Logcat
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/shell/common/shell.cc(93)] Dart Unhandled Exception: NoSuchMethodError: No top-level getter 'sKb' declared.
  Receiver: top-level
  Tried calling: sKb, stack trace: Warning: This VM has been configured to produce stack traces that violate the Dart standard.
  *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
  pid: 6711, tid: 6830, name Unknown
  build_id: '9771c8715b0df74d9646fccb467f9b57'
  isolate_dso_base: 78dc80f0b000, vm_dso_base: 78dc80f0b000
  isolate_instructions: 78dc80fa6e70, vm_instructions: 78dc80fa3000
      #00 abs 000078dc80fc7712 virt 00000000000bc712 _kDartIsolateSnapshotInstructions+0x208a2
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/runtime/dart_isolate.cc(668)] Could not resolve main entrypoint function.
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/runtime/dart_isolate.cc(167)] Could not run the run main Dart entrypoint.
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/runtime/runtime_controller.cc(382)] Could not create root isolate.
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/shell/common/shell.cc(600)] Could not launch engine with configuration.

Environment:

  • Dart SDK version: 2.17.1 (stable) (Tue May 17 17:58:21 2022 +0000) on "windows_x64"
  • Flutter Channel stable 3.0.1
  • OS: Windows 10
  • Android device and Android 11 emulator

//cc @aam

Had the same problem. Turn off obfuscation The problem still exists.

absar commented

@a-siva @mraleph can someone please look into it, it's preventing production rollouts for obfuscated apps

This is working as intended and ultimately has nothing to do with with obfuscation. If you try your code on master channel of Flutter you will discover that it does not work either even without obfuscation.

As it stands now to fix things you need to annotate bgCallback with @pragma('vm:entry-point'):

@pragma('vm:entry-point')
void bgCallback() {
  // ...
}

The underlying problem is that PluginUtilities.getCallbackHandle/getCallbackFromHandle assumes that you can lookup arbitrary top-level static functions by name, which is not true and only ever worked by accident rather than design.

We have a problem on our hands - plugins that use PluginUtilities without telling users to annotate static functions with entry-point pragma are essentially broken. We can opt-in to fix this by always retaining top-level functions in lookup dictionaries if they were ever torn-off, but I'd say this is rather suboptimal.

/cc @bkonyi @rmacnak-google @goderbauer @chinmaygarde @Hixie

absar commented

Thank you very much mraleph it works with @pragma('vm:entry-point'). Am happy to close the issue as long as this is documented somewhere for plugin authors.