ethanblake4 / dart_eval

Extensible Dart interpreter for Dart with full interop
https://pub.dev/packages/dart_eval
BSD 3-Clause "New" or "Revised" License
331 stars 38 forks source link

[Bug] The readme example can't work. #216

Open canxin121 opened 1 month ago

canxin121 commented 1 month ago

The readme example below can't work with dart_eval: ^0.7.9.

import 'package:dart_eval/dart_eval.dart';

void main() {
  final compiler = Compiler();

  final program = compiler.compile({'my_package': {
    'main.dart': '''
      import 'package:my_package/finder.dart';
      void main() {
        final parentheses = findParentheses('Hello (world)');
        if (parentheses.isNotEmpty) print(parentheses); 
      }
    ''',
    'finder.dart': r'''
      List<int> findParentheses(string) {
        final regex = RegExp(r'\((.*?)\)');
        final matches = regex.allMatches(string);
        return matches.map((match) => match.start).toList();
      }
    '''
  }});

  final runtime = Runtime.ofProgram(program);
  print(runtime.executeLib(
    'package:my_package/main.dart', 'main')); // prints '[6]'
}

Below is the error log:

Unhandled exception:
dart_eval runtime exception: type 'int' is not a subtype of type '$Value'
#0      BoxList.run (package:dart_eval/src/eval/runtime/ops/primitives.dart:255:69)
#1      Runtime.execute (package:dart_eval/src/eval/runtime/runtime.dart:867:12)
#2      Runtime.executeLib (package:dart_eval/src/eval/runtime/runtime.dart:854:12)
at main()

RUNTIME STATE
=============
Program offset: 88
Stack sample: [L0: $"Hello (world)", L1: [L0: 6], *L2: null, L3: null, L4: null, L5: null, L6: null, L7: null, L8: null, L9: null]
Args sample: []
Call stack: [0, -1]
TRACE:
82: PushScope (F7:53, 'main()')
83: PushConstant (C0)
84: BoxString (L0)
85: PushArg (L0)
86: Call (@100)
87: PushReturnValue ()
88: BoxList (L1)  <<< EXCEPTION
89: PushObjectProperty (L1.C1)
90: PushReturnValue ()
91: Unbox (L2)

#0      Runtime.execute (package:dart_eval/src/eval/runtime/runtime.dart:876:7)
#1      Runtime.executeLib (package:dart_eval/src/eval/runtime/runtime.dart:854:12)
#2      main (package:hw/hw.dart:26:17)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

Exited (255).
canxin121 commented 1 month ago
import 'package:dart_eval/dart_eval.dart';

void main() {
  final compiler = Compiler();

  final program = compiler.compile({
    'my_package': {
      'main.dart': '''
      import 'package:my_package/finder.dart';
      void main() {
        final parentheses = findParentheses('Hello (world)');
        if (parentheses.isNotEmpty) print(parentheses); 
      }
    ''',
      'finder.dart': r'''
      List<int> findParentheses(string) {
        final regex = RegExp(r'\((.*?)\)');
        final matches = regex.allMatches(string);
        List<int> result = [];
        for (var match in matches) {
          result.add(match.start);
        }
        return result;
      }
    '''
    }
  });

  final runtime = Runtime.ofProgram(program);
  print(runtime.executeLib(
      'package:my_package/main.dart', 'main')); // prints '[6]'
}

This works fine. It seems the error occurs in matches.map((match) => match.start).toList().

canxin121 commented 1 month ago

And the readme example Wrapper interop can't work. It alse mising a import for $String

import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/dart_eval_extensions.dart';
import 'package:dart_eval/stdlib/core.dart';

/// An example class we want to wrap
class Book {
  Book(this.pages);
  final List<String> pages;

  String getPage(int index) => pages[index];
}

/// This is our wrapper class
class $Book implements $Instance {
  /// Create a type specification for the dart_eval compiler
  static final $type = BridgeTypeSpec('package:hello/book.dart', 'Book').ref;

  /// Create a class declaration for the dart_eval compiler
  static final $declaration = BridgeClassDef(BridgeClassType($type),
      constructors: {
        // Define the default constructor with an empty string
        '': BridgeFunctionDef(
                returns: $type.annotate,
                params: ['pages'.param(CoreTypes.string.ref.annotate)])
            .asConstructor
      },
      methods: {
        'getPage': BridgeFunctionDef(
          returns: CoreTypes.string.ref.annotate,
          params: ['index'.param(CoreTypes.int.ref.annotate)],
        ).asMethod,
      },
      wrap: true);

  /// Override $value and $reified to return the value
  @override
  final Book $value;

  @override
  get $reified => $value;

  /// Create a constructor that wraps the Book class
  $Book.wrap(this.$value);

  static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
    return $Book.wrap(Book(args[0]!.$value));
  }

  /// Create a wrapper for property and method getters
  @override
  $Value? $getProperty(Runtime runtime, String identifier) {
    if (identifier == 'getPage') {
      return $Function((_, target, args) {
        return $String($value.getPage(args[0]!.$value));
      });
    }
    return $Object(this).$getProperty(runtime, identifier);
  }

  /// Create a wrapper for property setters
  @override
  void $setProperty(Runtime runtime, String identifier, $Value value) {
    return $Object(this).$setProperty(runtime, identifier, value);
  }

  /// Allow runtime type lookup
  @override
  int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);
}

/// Now we can use it in dart_eval!
void main() {
  final compiler = Compiler();
  compiler.defineBridgeClass($Book.$declaration);

  final program = compiler.compile({
    'hello': {
      'main.dart': '''
      import 'book.dart';
      void main() {
        final book = Book(['Hello world!', 'Hello again!']);
        print(book.getPage(1));
      }
    '''
    }
  });

  final runtime = Runtime.ofProgram(program);
  // Register static methods and constructors with the runtime
  runtime.registerBridgeFunc('package:hello/book.dart', 'Book.', $Book.$new);

  runtime.executeLib('package:hello/main.dart', 'main'); // -> 'Hello again!'
}

Error log:

Unhandled exception:
CompileError: Cannot assign argument of type List to parameter of type String at "(['Hello world!', 'H..." (file package:hello/main.dart)
#0      compileArgumentListWithBridge (package:dart_eval/src/eval/compiler/helpers/argument_list.dart:344:9)
#1      compileMethodInvocation (package:dart_eval/src/eval/compiler/expression/method_invocation.dart:130:22)
#2      compileExpression (package:dart_eval/src/eval/compiler/expression/expression.dart:38:12)
#3      compileVariableDeclarationList (package:dart_eval/src/eval/compiler/statement/variable_declaration.dart:34:17)
#4      compileVariableDeclarationStatement (package:dart_eval/src/eval/compiler/statement/variable_declaration.dart:15:3)
#5      compileStatement (package:dart_eval/src/eval/compiler/statement/statement.dart:24:12)
#6      compileBlock (package:dart_eval/src/eval/compiler/statement/block.dart:19:20)
#7      compileFunctionDeclaration (package:dart_eval/src/eval/compiler/declaration/function.dart:84:14)
#8      compileDeclaration (package:dart_eval/src/eval/compiler/declaration/declaration.dart:24:5)
#9      Compiler.compileSources.<anonymous closure>.<anonymous closure> (package:dart_eval/src/eval/compiler/compiler.dart:492:11)
#10     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13)
#11     Compiler.compileSources.<anonymous closure> (package:dart_eval/src/eval/compiler/compiler.dart:481:15)
#12     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13)
#13     Compiler.compileSources (package:dart_eval/src/eval/compiler/compiler.dart:477:32)
#14     Compiler.compile (package:dart_eval/src/eval/compiler/compiler.dart:163:12)
#15     main (package:hw/hw.dart:77:28)
#16     _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#17     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

Exited (255).
ghost commented 1 month ago

And the readme example Wrapper interop can't work. It alse mising a import for $String

import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/dart_eval_extensions.dart';
import 'package:dart_eval/stdlib/core.dart';

/// An example class we want to wrap
class Book {
  Book(this.pages);
  final List<String> pages;

  String getPage(int index) => pages[index];
}

/// This is our wrapper class
class $Book implements $Instance {
  /// Create a type specification for the dart_eval compiler
  static final $type = BridgeTypeSpec('package:hello/book.dart', 'Book').ref;

  /// Create a class declaration for the dart_eval compiler
  static final $declaration = BridgeClassDef(BridgeClassType($type),
      constructors: {
        // Define the default constructor with an empty string
        '': BridgeFunctionDef(
                returns: $type.annotate,
                params: ['pages'.param(CoreTypes.string.ref.annotate)])
            .asConstructor
      },
      methods: {
        'getPage': BridgeFunctionDef(
          returns: CoreTypes.string.ref.annotate,
          params: ['index'.param(CoreTypes.int.ref.annotate)],
        ).asMethod,
      },
      wrap: true);

  /// Override $value and $reified to return the value
  @override
  final Book $value;

  @override
  get $reified => $value;

  /// Create a constructor that wraps the Book class
  $Book.wrap(this.$value);

  static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
    return $Book.wrap(Book(args[0]!.$value));
  }

  /// Create a wrapper for property and method getters
  @override
  $Value? $getProperty(Runtime runtime, String identifier) {
    if (identifier == 'getPage') {
      return $Function((_, target, args) {
        return $String($value.getPage(args[0]!.$value));
      });
    }
    return $Object(this).$getProperty(runtime, identifier);
  }

  /// Create a wrapper for property setters
  @override
  void $setProperty(Runtime runtime, String identifier, $Value value) {
    return $Object(this).$setProperty(runtime, identifier, value);
  }

  /// Allow runtime type lookup
  @override
  int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);
}

/// Now we can use it in dart_eval!
void main() {
  final compiler = Compiler();
  compiler.defineBridgeClass($Book.$declaration);

  final program = compiler.compile({
    'hello': {
      'main.dart': '''
      import 'book.dart';
      void main() {
        final book = Book(['Hello world!', 'Hello again!']);
        print(book.getPage(1));
      }
    '''
    }
  });

  final runtime = Runtime.ofProgram(program);
  // Register static methods and constructors with the runtime
  runtime.registerBridgeFunc('package:hello/book.dart', 'Book.', $Book.$new);

  runtime.executeLib('package:hello/main.dart', 'main'); // -> 'Hello again!'
}

Error log:

Unhandled exception:
CompileError: Cannot assign argument of type List to parameter of type String at "(['Hello world!', 'H..." (file package:hello/main.dart)
#0      compileArgumentListWithBridge (package:dart_eval/src/eval/compiler/helpers/argument_list.dart:344:9)
#1      compileMethodInvocation (package:dart_eval/src/eval/compiler/expression/method_invocation.dart:130:22)
#2      compileExpression (package:dart_eval/src/eval/compiler/expression/expression.dart:38:12)
#3      compileVariableDeclarationList (package:dart_eval/src/eval/compiler/statement/variable_declaration.dart:34:17)
#4      compileVariableDeclarationStatement (package:dart_eval/src/eval/compiler/statement/variable_declaration.dart:15:3)
#5      compileStatement (package:dart_eval/src/eval/compiler/statement/statement.dart:24:12)
#6      compileBlock (package:dart_eval/src/eval/compiler/statement/block.dart:19:20)
#7      compileFunctionDeclaration (package:dart_eval/src/eval/compiler/declaration/function.dart:84:14)
#8      compileDeclaration (package:dart_eval/src/eval/compiler/declaration/declaration.dart:24:5)
#9      Compiler.compileSources.<anonymous closure>.<anonymous closure> (package:dart_eval/src/eval/compiler/compiler.dart:492:11)
#10     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13)
#11     Compiler.compileSources.<anonymous closure> (package:dart_eval/src/eval/compiler/compiler.dart:481:15)
#12     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13)
#13     Compiler.compileSources (package:dart_eval/src/eval/compiler/compiler.dart:477:32)
#14     Compiler.compile (package:dart_eval/src/eval/compiler/compiler.dart:163:12)
#15     main (package:hw/hw.dart:77:28)
#16     _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#17     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

Exited (255).

=================

I am having the same issue with $String() at compile step

If I remove the $String() and put just the string compilation works but I get this error

Failed to evaluate program: dart_eval runtime exception: TypeError: "Hi!": type 'String' is not a subtype of type '$Instance' dart:sdkinternal 11994:11 throw dart:sdk_internal 22922:15 _failedAsCheck dart:sdk_internal 22908:14 _generalAsCheckImplementation at main()

RUNTIME STATE

Program offset: 83 Stack sample: [L0: 1, L1: "Hi!", *L2: null, L3: null, L4: null, L5: null, L6: null, L7: null, L8: null, L9: null] Args sample: [] Call stack: [0, -1] TRACE: 77: CopyValue (L2 <-- L1) 78: Pop (1) 79: BoxNum (L2) 80: Return (L2) 81: Pop (2) 82: PushScope (F7:4, 'main()') 83: PushObjectProperty (L1.C0) <<< EXCEPTION 84: PushReturnValue () 85: Unbox (L2) 86: NumAdd (L0 + L2)

ethanblake4 commented 3 days ago

Fixed issue with the readme Wrapper interop example.

As far as the initial issue, I can't reproduce, this example is actually a test in dart_eval and it is working fine.