# Running arbitrary strings as Dart code

Source: https://iiro.dev/how-to-eval-in-dart/
Published: 2019-08-15

Dart does not have eval(), but there are limited ways to run arbitrary code strings when you absolutely need to.

---

<small>(**NOTE:** [This only works in JIT mode](https://twitter.com/mraleph/status/1161907757383131136).
It's a viable solution only if you're building a Dart app that runs with Dart VM and which isn't a [AOT compiled](https://dart.dev/tools/dart2aot).
**You can't use this approach with Flutter**.
Or you can, but it stops working in release mode.)</small>

In Javascript, it's possible to evaluate a string as code:


**File:** `why-would-you-do-that.js`

```javascript
const code = 'console.log("hey!")';

// Execute "code" as Javascript
eval(code);
```

Javascript has `eval()`, which is a special function that takes in a string and _evaluates_ it as Javascript code.
The above example prints a friendly _"hey!"_ to the console.

> _"But Liro, why would you not just call `console.log()` directly?"_

First, my name is _Iiro_.
And to answer the question, you're absolutely right.
You would in fact do just that.

Here's the same thing but with less code:


**File:** `much-better.js`

```javascript
console.log("Hey!")
```

If the result of both examples is the same, why would we ever want to `eval()` in the first place?

In this case, we wouldn't.
That code snippet is **just as an example** - not an actual use case.
A real use case for using `eval()` is quite hard to come up with.

## So - when do I need to `eval()`?

Most of the time, [you should not use eval()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval!) at all.
Chances are that you're never going to need it in application development.

**In very rare cases**, you might actually need it.
Most likely when building developer tools - after all, there's no [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) without eval.

I found myself needing `eval()` today when building something in Dart.
And guess what, it was in the context of building a developer tool.


```dart
final functionName = 'applyMagicSauce';
```


The `functionName` here is hacked together during runtime.
It's done by searching for implementers of a specific abstract class in a Dart project.

Those abstract classes are written by me, but the implementations come from third-party libraries.
There might be multiple `functionName`s to call.

I needed to take that `functionName` and do something like this:


```dart
final functionName = 'applyMagicSauce';
final input = 'some arbitrary string';

// Construct a call to "applyMagicSauce()"
// with "some arbitrary string" as input.
final result = eval('$functionName("$input")');
```


Calling `applyMagicSauce(..)` and passing it a string returns a slightly different string.
I needed to find a way to call it.

## Where did you hide your `eval()`, Dart?

Dart **does not** have an `eval()` function.
What a bummer.

If you're _running Dart in a web browser_, you could kinda do it by calling Javascript from Dart like this:


```dart
void main() {
  final result = js.context.callMethod("eval", /* ... */);
}
```


But that **won't work when running Dart on the VM**.

I'm also pretty sure I wouldn't get anything useful back if I tried to send some Dart to a Javascript `eval()` function.

After some more investigative Googling, I stumpled upon [this comment](https://github.com/dart-lang/sdk/issues/36989#issuecomment-493402079) on a GitHub issue:

> You can create a new library with a main function, **encode the source code as a data: 
> URI and spawn it as a new isolate**, but that will not allow you to create new functions 
> in the current isolate. You'll have to communicate with the new code using isolate port communication.

I decided to try just that.
There was a function called [Isolate.spawnUri](https://api.flutter.dev/flutter/dart-isolate/Isolate/spawnUri.html) that seemed to be just what I needed.

It takes in an `Uri` - which among other things, can be constructed from a string by calling [Uri.dataFromString](https://api.flutter.dev/flutter/dart-core/Uri/Uri.dataFromString.html).


```dart
void main() async {
  final uri = Uri.dataFromString(
    '''
    void main() {
      print("Hellooooooo from the other side!");
    }
    ''',
    mimeType: 'application/dart',
  );
  await Isolate.spawnUri(uri, [], null);
}
```


Guess what?
The damn thing worked.

```text
ironman$ dart lib/eval.dart

Hellooooooo from the other side!
```


Yay for executing arbitrary strings as Dart code!
What about sending something back?


```dart
void main() async {
  final name = 'Eval Knievel';
  final uri = Uri.dataFromString(
    '''
    import "dart:isolate";

    void main(_, SendPort port) {
      port.send("Nice to meet you, $name!");
    }
    ''',
    mimeType: 'application/dart',
  );

  final port = ReceivePort();
  await Isolate.spawnUri(uri, [], port.sendPort);

  final String? response = await (port.first as FutureOr<String?>);
  print(response);
}
```


<small>(Isolates in Dart are quite... _isolated_. 
They don't share memory. 
In order to pass data around, we need to use _ports_ - which might seem a little cumbersome.
On the other hand, we don't run into synchronization issues, so there's that.)</small>

That one worked too:

```text
ironman$ dart lib/eval.dart

Nice to meet you, Eval Knievel!
```

Remember `applyMagicSauce()` from earlier?
Let's imagine I have the full `package:` URI for the containing file in a variable called `packageUri`.
Because I actually do.

I also know that the name of the function I want to call is `applyMagicSauce`.
It takes in one argument which is a String.

It's time to stitch some strings together.


```dart
void main() async {
  // I filled out the content of these variables here
  // for clarity. In a real scenario, you'd probably
  // parse these from somewhere.
  final packageUri = 'package:magic/magic_sauce.dart';
  final functionName = 'applyMagicSauce';
  final input = 'Hellooooooo from the other side!';
  final uri = Uri.dataFromString(
    '''
    import "dart:isolate";
    import '$packageUri';

    void main(_, SendPort port) {
      port.send($functionName("$input"));
    }
    ''',
    mimeType: 'application/dart',
  );

  final port = ReceivePort();
  final isolate =
  await Isolate.spawnUri(uri, [], port.sendPort);
  final String? response = await (port.first as FutureOr<String?>);

  port.close();
  isolate.kill();

  print(response);
}
```


After running this, the result of `applyMagicSauce` will be in the `response` variable.

If the magic sauce function was shuffling a string around, we would get something like this printed on the console:

```text
ironman$ dart lib/eval.dart

lor meetei ooo sdel fthooh!oHoor
```

## Final words

In Javascript, the first rule of using `eval` is **to not use it**.
It applies for Dart too.

It's usually hacky and there's going to be a better way.
If you're just building Flutter apps, you're probably never going to need it.
In fact, if you're building a Flutter app, this approach [will not work for you](https://twitter.com/mraleph/status/1161907757383131136).

The second rule is that _eval can be evil_.
If you do run it with Dart VM, you need to seriously think about making sure that you're not accidentally running something malicious on your end user's computer.
