execute method

Future<void> execute({
  1. Duration duration = const Duration(seconds: 5),
})

Execute the DartBlockProgram.

A new Isolate is spawned, on which the execution of the DartBlockProgram will occur.

This ensures that the UI is not frozen during the execution.

Additionally, a timer is applied. If the execution has not terminated after a set Duration, it is automatically interrupted by killing the Isolate and throwing a DartBlockException indicating that the user's program may contain an infinite loop or a faulty recursive function without a proper ending condition.

By default, the Duration is 5s, which is also the minimum duration allowed.

IMPORTANT: as Dart does not support Isolates on web platforms, the execution model is much more rudimentary:

  • The DartBlockProgram is executed on the main thread, meaning the UI will freeze until the execution has finished.
  • No timer is applied, meaning a faulty program whose execution never ends will lead to an indefinite stall of the app.

More info on the web platform and isolates: https://docs.flutter.dev/perf/isolates#web-platforms-and-compute

Implementation

Future<void> execute({Duration duration = const Duration(seconds: 5)}) async {
  if (duration.inSeconds < 5) {
    duration = Duration(seconds: 5);
  }

  // Reset the environment to wipe traces of the previous execution.
  _reset();
  if (kIsWeb) {
    _executeWeb();
    return;
  }
  bool finishedExecution = false;

  try {
    final resultPort = ReceivePort();
    // Spawn a new isolate on which the DartBlockProgram should be executed.
    final isolate = await Isolate.spawn(
      (List<dynamic> args) async {
        SendPort resultPort = args[0];
        DartBlockExecutor executor = args[1];
        try {
          FunctionCallStatement.init("main", []).run(executor);
        } on DartBlockException catch (ex) {
          Isolate.exit(resultPort, [executor, ex]);
        } on Exception catch (ex) {
          Isolate.exit(resultPort, [executor, ex]);
        } on StackOverflowError catch (_) {
          // DartBlock relies on the StackOverflowError thrown by Dart's own execution engine.
          Isolate.exit(resultPort, [
            executor,
            DartBlockException(
              title: "Stack Overflow",
              message:
                  "The program was killed due to a stack overflow error. This can occur if you have a recursive function without an appropriate ending condition.",
            ),
          ]);
        } on Error catch (_) {
          // Any other (unknown) type of error which may occur.
          Isolate.exit(resultPort, [
            executor,
            DartBlockException(
              title: "Critical Error",
              message:
                  "The program was killed due to an unknown error. Ensure your program does not contain an infinite loop or a recursive function without an ending condition!",
            ),
          ]);
        }
        // Leave the isolate after the execution has finished and send back the DartBlockExecutor which contains the environment and execution output.
        Isolate.exit(resultPort, [executor, null]);
      },
      [resultPort.sendPort, this],

      /// Initially paused such that the timer can be started at the same time.
      paused: true,
      onExit: resultPort.sendPort,
    );

    /// Start the preventive timer, which aims to stop the execution (kill the isolate)
    /// after a certain amount of time in case it has not finished execution yet.
    Future.delayed(duration).then((value) {
      if (!finishedExecution) {
        isolate.kill(priority: Isolate.immediate);
      }
    });
    isolate.resume(isolate.pauseCapability!);
    final response = await resultPort.first;
    finishedExecution = true;

    /// This means the isolate was killed early.
    if (response == null) {
      _thrownException = DartBlockException(
        title: "Infinite Loop",
        message:
            "The program was killed due to its execution taking too long. Ensure your program does not contain an infinite loop or a recursive function without an ending condition!",
      );
    } else {
      DartBlockExecutor receivedExecutor = response[0];
      copyFrom(receivedExecutor);
      Exception? exception = response[1];
      if (exception != null) {
        if (exception is DartBlockException) {
          _thrownException = exception;
          printToConsole("Program execution interrupted by exception.");
        } else {
          _thrownException = DartBlockException.fromException(
            exception: exception,
          );
          printToConsole("Program execution interrupted by exception.");
        }
      } else {
        printToConsole("Program execution finished successfully.");
      }
    }
  } on DartBlockException catch (ex) {
    _thrownException = ex;
    printToConsole("Program execution interrupted by exception.");
  } on Exception catch (ex) {
    _thrownException = DartBlockException.fromException(exception: ex);
    printToConsole("Program execution interrupted by exception.");
  } on StackOverflowError catch (_) {
    _thrownException = DartBlockException(
      title: "Stack Overflow",
      message:
          "The program was killed due to a stack overflow error. This can occur if you have a recursive function without an appropriate ending condition.",
    );
    printToConsole("Program execution interrupted by exception.");
  } on Error catch (_) {
    _thrownException = DartBlockException(
      title: "Critical Error",
      message:
          "The program was killed due to an unknown error. Ensure your program does not contain an infinite loop or a recursive function without an ending condition!",
    );
    printToConsole("Program execution interrupted by exception.");
  }
}