├── analysis_options.yaml ├── lib ├── dart_eval_extensions.dart ├── stdlib │ ├── math.dart │ ├── typed_data.dart │ ├── collection.dart │ ├── async.dart │ ├── convert.dart │ ├── io.dart │ └── core.dart ├── src │ └── eval │ │ ├── bridge │ │ ├── extensions │ │ │ ├── extensions.dart │ │ │ ├── spec.dart │ │ │ ├── function.dart │ │ │ ├── type.dart │ │ │ └── parameter.dart │ │ ├── registry.dart │ │ ├── declaration │ │ │ ├── enum.dart │ │ │ ├── enum.g.dart │ │ │ ├── function.g.dart │ │ │ ├── type.g.dart │ │ │ └── function.dart │ │ ├── serializer.dart │ │ └── runtime_bridge.dart │ │ ├── bindgen │ │ ├── errors.dart │ │ ├── enum.dart │ │ ├── context.dart │ │ ├── permission.dart │ │ ├── methods.dart │ │ └── operator.dart │ │ ├── shared │ │ ├── stdlib │ │ │ ├── core │ │ │ │ ├── collection.dart │ │ │ │ ├── record.dart │ │ │ │ ├── enum.dart │ │ │ │ ├── print.dart │ │ │ │ ├── identical.dart │ │ │ │ ├── symbol.dart │ │ │ │ ├── iterator.dart │ │ │ │ ├── comparable.dart │ │ │ │ ├── type.dart │ │ │ │ └── sink.dart │ │ │ ├── typed_data.dart │ │ │ ├── collection.dart │ │ │ ├── async.dart │ │ │ ├── io │ │ │ │ ├── io_sink.dart │ │ │ │ └── http_status.dart │ │ │ ├── async │ │ │ │ ├── zone.dart │ │ │ │ └── future.dart │ │ │ ├── io.dart │ │ │ ├── convert.dart │ │ │ └── convert │ │ │ │ └── converter.dart │ │ └── dynamic_map.dart │ │ ├── compiler │ │ ├── source.dart │ │ ├── model │ │ │ ├── override_spec.dart │ │ │ ├── diagnostic_mode.dart │ │ │ ├── label.dart │ │ │ ├── library.dart │ │ │ ├── compilation_unit.dart │ │ │ └── function_type.dart │ │ ├── expression │ │ │ ├── parenthesized.dart │ │ │ ├── rethrow.dart │ │ │ ├── throw.dart │ │ │ ├── cascade.dart │ │ │ ├── index.dart │ │ │ ├── pattern_assignment.dart │ │ │ ├── symbol.dart │ │ │ ├── funcexpr_invocation.dart │ │ │ ├── is.dart │ │ │ ├── adjacent_strings.dart │ │ │ ├── keywords.dart │ │ │ ├── string_interpolation.dart │ │ │ ├── await.dart │ │ │ ├── assignment.dart │ │ │ ├── postfix.dart │ │ │ ├── conditional.dart │ │ │ ├── as.dart │ │ │ ├── property_access.dart │ │ │ ├── literal.dart │ │ │ ├── prefix.dart │ │ │ └── instance_creation.dart │ │ ├── dispatch.dart │ │ ├── macros │ │ │ ├── macro.dart │ │ │ └── branch.dart │ │ ├── constant_pool.dart │ │ ├── statement │ │ │ ├── do.dart │ │ │ ├── while.dart │ │ │ ├── assert.dart │ │ │ ├── if.dart │ │ │ ├── return.dart │ │ │ ├── pattern_variable_declaration.dart │ │ │ ├── block.dart │ │ │ ├── break.dart │ │ │ └── variable_declaration.dart │ │ ├── helpers │ │ │ ├── assert.dart │ │ │ ├── equality.dart │ │ │ ├── return.dart │ │ │ └── fpl.dart │ │ ├── util │ │ │ ├── tree_shake.dart │ │ │ ├── custom_crawler.dart │ │ │ └── library_graph.dart │ │ ├── source_node_wrapper.dart │ │ ├── scope.dart │ │ ├── collection │ │ │ └── if.dart │ │ ├── util.dart │ │ ├── errors.dart │ │ ├── declaration │ │ │ ├── declaration.dart │ │ │ ├── variable.dart │ │ │ ├── class.dart │ │ │ └── field.dart │ │ ├── optimizer │ │ │ └── prescan.dart │ │ └── offset_tracker.dart │ │ ├── runtime │ │ ├── continuation.dart │ │ ├── security │ │ │ ├── permission.dart │ │ │ └── permissions │ │ │ │ ├── process.dart │ │ │ │ └── network.dart │ │ ├── record.dart │ │ ├── exception.dart │ │ ├── override.dart │ │ ├── type.dart │ │ └── declaration.dart │ │ ├── cli │ │ ├── run.dart │ │ └── utils.dart │ │ ├── plugin.dart │ │ ├── utils │ │ └── wrap_helper.dart │ │ └── eval.dart ├── source_node_wrapper.dart ├── dart_eval_security.dart ├── dart_eval.dart └── dart_eval_bridge.dart ├── .vscode └── launch.json ├── test ├── variable_test.dart ├── diagnostic_mode_test.dart ├── prefixed_import_test.dart ├── util │ └── current_dir_overrides.dart ├── postfix_test.dart ├── prefix_test.dart ├── not_equal_test.dart ├── tearoff_test.dart ├── records_test.dart └── operator_test.dart ├── pubspec.yaml ├── .github └── workflows │ └── dart.yml ├── LICENSE └── .gitignore /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml -------------------------------------------------------------------------------- /lib/dart_eval_extensions.dart: -------------------------------------------------------------------------------- 1 | export 'src/eval/bridge/extensions/extensions.dart'; 2 | -------------------------------------------------------------------------------- /lib/stdlib/math.dart: -------------------------------------------------------------------------------- 1 | /// Provides dart:math bridge classes and wrappers 2 | library; 3 | 4 | export '../src/eval/shared/stdlib/math/point.dart'; 5 | -------------------------------------------------------------------------------- /lib/stdlib/typed_data.dart: -------------------------------------------------------------------------------- 1 | /// Provides dart:typed_data bridge classes and wrappers 2 | library; 3 | 4 | export '../src/eval/shared/stdlib/typed_data/typed_data.dart'; 5 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/extensions/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | 3 | part 'parameter.dart'; 4 | part 'type.dart'; 5 | part 'spec.dart'; 6 | part 'function.dart'; 7 | -------------------------------------------------------------------------------- /lib/stdlib/collection.dart: -------------------------------------------------------------------------------- 1 | /// Provides dart:collection bridge classes and wrappers 2 | library; 3 | 4 | export '../src/eval/shared/stdlib/collection/linked_hash_map.dart'; 5 | export '../src/eval/shared/stdlib/collection/list_queue.dart'; 6 | -------------------------------------------------------------------------------- /lib/stdlib/async.dart: -------------------------------------------------------------------------------- 1 | /// Provides dart:async bridge classes and wrappers 2 | library; 3 | 4 | export '../src/eval/shared/stdlib/async/future.dart'; 5 | export '../src/eval/shared/stdlib/async/stream_controller.dart'; 6 | export '../src/eval/shared/stdlib/async/stream.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/eval/bindgen/errors.dart: -------------------------------------------------------------------------------- 1 | class BindingGenerationError implements Exception { 2 | final String message; 3 | const BindingGenerationError(this.message); 4 | 5 | @override 6 | String toString() { 7 | return 'BindingGenerationError: $message'; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/collection.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:dart_eval/dart_eval_bridge.dart'; 4 | import 'package:dart_eval/stdlib/core.dart'; 5 | 6 | part 'iterable.dart'; 7 | part 'list.dart'; 8 | part 'map.dart'; 9 | part 'set.dart'; 10 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/record.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | 3 | const $recordCls = BridgeClassDef( 4 | BridgeClassType(BridgeTypeRef(CoreTypes.record), isAbstract: false), 5 | constructors: {}, 6 | methods: {}, 7 | wrap: true); 8 | -------------------------------------------------------------------------------- /lib/source_node_wrapper.dart: -------------------------------------------------------------------------------- 1 | /*library dart_eval.source_node_wrapper; 2 | 3 | export 'package:analyzer/dart/ast/ast.dart' 4 | show AstNode, ConstructorDeclaration; 5 | export 'src/eval/compiler/source_node_wrapper.dart'; 6 | export 'src/eval/compiler/builtins.dart' show BuiltinValue;*/ 7 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/source.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/bridge/declaration.dart'; 2 | 3 | class DeclarationOrPrefix { 4 | DeclarationOrPrefix({this.declaration, this.children}); 5 | 6 | DeclarationOrBridge? declaration; 7 | Map? children; 8 | } 9 | -------------------------------------------------------------------------------- /lib/dart_eval_security.dart: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | export 'src/eval/runtime/security/permission.dart'; 4 | export 'src/eval/runtime/security/permissions/filesystem.dart'; 5 | export 'src/eval/runtime/security/permissions/network.dart'; 6 | export 'src/eval/runtime/security/permissions/process.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/eval/shared/dynamic_map.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/stdlib/core.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | 4 | typedef _MappingFunc = $Value Function(dynamic o); 5 | 6 | final _runtimeTypeMapping = {String: (o) => $String(o)}; 7 | 8 | $Value mapDynamic(dynamic o) => _runtimeTypeMapping[o.runtimeType]!(o); 9 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/enum.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | 3 | const $enumDeclaration = BridgeClassDef( 4 | BridgeClassType(BridgeTypeRef(CoreTypes.enumType), isAbstract: true), 5 | constructors: {}, 6 | methods: { 7 | // Other bool methods defined in builtins.dart 8 | }, 9 | wrap: true); 10 | -------------------------------------------------------------------------------- /lib/stdlib/convert.dart: -------------------------------------------------------------------------------- 1 | /// Provides dart:convert bridge classes and wrappers 2 | library; 3 | 4 | export '../src/eval/shared/stdlib/convert/codec.dart'; 5 | export '../src/eval/shared/stdlib/convert/converter.dart'; 6 | export '../src/eval/shared/stdlib/convert/encoding.dart'; 7 | export '../src/eval/shared/stdlib/convert/json.dart'; 8 | export '../src/eval/shared/stdlib/convert/utf.dart'; 9 | -------------------------------------------------------------------------------- /lib/dart_eval.dart: -------------------------------------------------------------------------------- 1 | /// A library providing a Dart bytecode compiler and interpreter. 2 | library; 3 | 4 | export 'src/eval/eval.dart'; 5 | export 'src/eval/runtime/runtime.dart' show Runtime; 6 | export 'src/eval/compiler/compiler.dart'; 7 | export 'src/eval/compiler/program.dart'; 8 | export 'src/eval/runtime/override.dart' hide runtimeOverride; 9 | export 'src/eval/compiler/model/diagnostic_mode.dart'; 10 | -------------------------------------------------------------------------------- /lib/src/eval/bindgen/enum.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element2.dart'; 2 | import 'package:dart_eval/src/eval/bindgen/context.dart'; 3 | 4 | String $enumValues(BindgenContext ctx, EnumElement2 element) { 5 | return ''' 6 | static final _\$values = { 7 | ${element.constants2.map((e) => "'${e.name3}': \$${element.name3}.wrap(${element.name3}.${e.name3})").join(', ')} 8 | }; 9 | '''; 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/model/override_spec.dart: -------------------------------------------------------------------------------- 1 | /// Describes a runtime override stored in the program. 2 | class OverrideSpec { 3 | /// Creates a new [OverrideSpec]. 4 | const OverrideSpec(this.offset, this.versionConstraint); 5 | 6 | /// The bytecode offset of the override. 7 | final int offset; 8 | 9 | /// The pub_semver version constraint of the override. 10 | final String? versionConstraint; 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/extensions/spec.dart: -------------------------------------------------------------------------------- 1 | part of 'extensions.dart'; 2 | 3 | extension BridgeTypeSpecExt on BridgeTypeSpec { 4 | /// Extension to create a type ref from a type spec 5 | BridgeTypeRef get ref { 6 | return BridgeTypeRef(this); 7 | } 8 | 9 | /// Extension to create a type ref from a spec, with type args 10 | BridgeTypeRef refWith(List typeArgs) { 11 | return BridgeTypeRef(this, typeArgs); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/parenthesized.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 4 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 5 | 6 | Variable compileParenthesizedExpression( 7 | ParenthesizedExpression e, CompilerContext ctx) { 8 | return compileExpression(e.expression, ctx); 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/model/diagnostic_mode.dart: -------------------------------------------------------------------------------- 1 | /// Defines the diagnostic modes for the dart_eval compiler. 2 | /// These modes control how the compiler handles parsing diagnostics, 3 | /// such as errors, warnings, and informational messages. 4 | enum DiagnosticMode { 5 | throwIfError, 6 | throwIfErrorOrWarning, 7 | throwErrorPrintWarnings, 8 | throwErrorPrintAll, 9 | printErrors, 10 | printErrorsAndWarnings, 11 | printAll, 12 | ignore 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/continuation.dart: -------------------------------------------------------------------------------- 1 | /// A continuation represents a state of the VM that can be saved and resumed during an async suspension 2 | class Continuation { 3 | const Continuation( 4 | {required this.programOffset, 5 | required this.frameOffset, 6 | required this.frame, 7 | required this.args}); 8 | 9 | final int programOffset; 10 | final int frameOffset; 11 | final List frame; 12 | final List args; 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/dispatch.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/compiler/offset_tracker.dart'; 2 | import 'package:dart_eval/src/eval/compiler/type.dart'; 3 | 4 | /// Compile-time only data describing how to perform a static-dispatch function call (e.g. when the exact function 5 | /// to be called is known at compile time) 6 | class StaticDispatch { 7 | const StaticDispatch(this.offset, this.returnType); 8 | 9 | final DeferredOrOffset offset; 10 | final ReturnType returnType; 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/eval/cli/run.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dart_eval/dart_eval_bridge.dart'; 4 | 5 | void cliRun(String path, String library, String function) { 6 | final evc = File(path).readAsBytesSync(); 7 | final runtime = Runtime(evc.buffer.asByteData()); 8 | var result = runtime.executeLib(library, function); 9 | 10 | if (result != null) { 11 | if (result is $Value) { 12 | result = result.$reified; 13 | } 14 | print('\nProgram exited with result: $result'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/stdlib/io.dart: -------------------------------------------------------------------------------- 1 | /// Provides dart:io bridge classes and wrappers 2 | library; 3 | 4 | export '../src/eval/shared/stdlib/io/directory.dart'; 5 | export '../src/eval/shared/stdlib/io/file_system_entity.dart'; 6 | export '../src/eval/shared/stdlib/io/file.dart'; 7 | export '../src/eval/shared/stdlib/io/http.dart'; 8 | export '../src/eval/shared/stdlib/io/io_sink.dart'; 9 | export '../src/eval/shared/stdlib/io/string_sink.dart'; 10 | export '../src/eval/shared/stdlib/io/socket.dart'; 11 | export '../src/eval/shared/stdlib/io/http_status.dart'; 12 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/macros/macro.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/compiler/context.dart'; 2 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 3 | import 'package:dart_eval/src/eval/compiler/type.dart'; 4 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 5 | 6 | typedef MacroClosure = Function(CompilerContext ctx); 7 | typedef MacroVariableClosure = Variable Function(CompilerContext ctx); 8 | typedef MacroStatementClosure = StatementInfo Function( 9 | CompilerContext ctx, AlwaysReturnType? expectedReturnType); 10 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/constant_pool.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | 3 | class ConstantPool { 4 | ConstantPool(); 5 | 6 | final List pool = []; 7 | final Map _map = {}; 8 | 9 | int addOrGet(T p) { 10 | var hash = const DeepCollectionEquality().hash(p) + p.runtimeType.hashCode; 11 | if (p is List) { 12 | hash ^= p.length; 13 | } 14 | final existing = _map[hash]; 15 | if (existing != null) return existing; 16 | pool.add(p); 17 | return _map[hash] = pool.length - 1; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Test CLI compile", 6 | "type": "dart", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/bin/dart_eval.dart", 9 | "args": [ 10 | "compile" 11 | ], 12 | "cwd": "D:\\Projects\\eval_test" 13 | }, 14 | { 15 | "name": "Dart: Run all Tests", 16 | "type": "dart", 17 | "request": "launch", 18 | "program": "./test/" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/rethrow.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 5 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | 8 | Variable compileRethrowExpression(CompilerContext ctx, RethrowExpression e) { 9 | ctx.pushOp(Throw.make(ctx.caughtExceptions.last.scopeFrameOffset), Throw.LEN); 10 | return Variable(-1, CoreTypes.never.ref(ctx)); 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/eval/bindgen/context.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | 3 | class BindgenContext { 4 | final String filename; 5 | final String uri; 6 | final Set imports = {}; 7 | final Set knownTypes = {}; 8 | final Set unknownTypes = {}; 9 | final bool all; 10 | final Map libOverrides = {}; 11 | bool implicitSupers = false; 12 | final Map> bridgeDeclarations; 13 | final Map exportedLibMappings; 14 | 15 | BindgenContext(this.filename, this.uri, 16 | {required this.all, 17 | required this.bridgeDeclarations, 18 | required this.exportedLibMappings}); 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/throw.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 6 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 7 | import 'package:dart_eval/src/eval/compiler/type.dart'; 8 | 9 | Variable compileThrowExpression(CompilerContext ctx, ThrowExpression e) { 10 | final V = compileExpression(e.expression, ctx).boxIfNeeded(ctx); 11 | ctx.pushOp(Throw.make(V.scopeFrameOffset), Throw.LEN); 12 | return Variable(-1, CoreTypes.never.ref(ctx)); 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/do.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/macros/loop.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | 8 | StatementInfo compileDoStatement( 9 | DoStatement s, CompilerContext ctx, AlwaysReturnType? expectedReturnType) { 10 | return macroLoop(ctx, expectedReturnType, 11 | condition: (ctx) => compileExpression(s.condition, ctx), 12 | body: (ctx, ert) => compileStatement(s.body, ert, ctx), 13 | alwaysLoopOnce: true); 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/while.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/macros/loop.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | 8 | StatementInfo compileWhileStatement(WhileStatement s, CompilerContext ctx, 9 | AlwaysReturnType? expectedReturnType) { 10 | return macroLoop( 11 | ctx, 12 | expectedReturnType, 13 | condition: (ctx) => compileExpression(s.condition, ctx), 14 | body: (ctx, ert) => compileStatement(s.body, ert, ctx), 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /test/variable_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Top-level variable tests', () { 6 | late Compiler compiler; 7 | 8 | setUp(() { 9 | compiler = Compiler(); 10 | }); 11 | 12 | test('Assignment to top-level variable', () { 13 | final runtime = compiler.compileWriteAndLoad({ 14 | 'eval_test': { 15 | 'main.dart': ''' 16 | var x = 1; 17 | void main() { 18 | x = 3; 19 | print(x); 20 | } 21 | ''' 22 | } 23 | }); 24 | 25 | expect(() => runtime.executeLib('package:eval_test/main.dart', 'main'), 26 | prints('3\n')); 27 | }); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/extensions/function.dart: -------------------------------------------------------------------------------- 1 | part of 'extensions.dart'; 2 | 3 | extension BridgeFunctionDefExt on BridgeFunctionDef { 4 | /// Extension to create a method from a function def 5 | BridgeMethodDef get asMethod { 6 | return BridgeMethodDef(this); 7 | } 8 | 9 | /// Extension to create a static method from a function def 10 | BridgeMethodDef get asStaticMethod { 11 | return BridgeMethodDef(this, isStatic: true); 12 | } 13 | 14 | /// Extension to create a constructor from a function def 15 | BridgeConstructorDef get asConstructor { 16 | return BridgeConstructorDef(this); 17 | } 18 | 19 | /// Extension to create a factory constructor from a function def 20 | BridgeConstructorDef get asFactory { 21 | return BridgeConstructorDef(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/cascade.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/method_invocation.dart'; 5 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 6 | 7 | Variable compileCascadeExpression(CascadeExpression e, CompilerContext ctx) { 8 | final target = compileExpression(e.target, ctx); 9 | for (final s in e.cascadeSections) { 10 | if (s is MethodInvocation) { 11 | compileMethodInvocation(ctx, s, cascadeTarget: target); 12 | } else { 13 | compileExpressionAndDiscardResult(s, ctx, cascadeTarget: target); 14 | } 15 | } 16 | return target; 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/model/label.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/compiler/context.dart'; 2 | 3 | class CompilerLabel { 4 | final int offset; 5 | final int Function(CompilerContext ctx) cleanup; 6 | final String? name; 7 | final LabelType type; 8 | 9 | const CompilerLabel(this.type, this.offset, this.cleanup, {this.name}); 10 | } 11 | 12 | class SimpleCompilerLabel implements CompilerLabel { 13 | @override 14 | get offset => -1; 15 | @override 16 | final String? name; 17 | @override 18 | get type => LabelType.block; 19 | 20 | const SimpleCompilerLabel({this.name}); 21 | 22 | @override 23 | get cleanup => (CompilerContext ctx) { 24 | ctx.endAllocScopeQuiet(); 25 | return -1; 26 | }; 27 | } 28 | 29 | enum LabelType { loop, branch, block } 30 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 4 | import 'package:dart_eval/src/eval/compiler/reference.dart'; 5 | 6 | import '../variable.dart'; 7 | 8 | Reference compileIndexExpressionAsReference( 9 | IndexExpression e, CompilerContext ctx, 10 | {Variable? cascadeTarget}) { 11 | final value = cascadeTarget ?? compileExpression(e.realTarget, ctx); 12 | final index = compileExpression(e.index, ctx); 13 | return IndexedReference(value, index); 14 | } 15 | 16 | Variable compileIndexExpression(IndexExpression e, CompilerContext ctx, 17 | {Variable? cascadeTarget}) { 18 | return compileIndexExpressionAsReference(e, ctx, cascadeTarget: cascadeTarget) 19 | .getValue(ctx); 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/assert.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/helpers/assert.dart'; 6 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 7 | import 'package:dart_eval/src/eval/compiler/type.dart'; 8 | 9 | StatementInfo compileAssertStatement(AssertStatement s, CompilerContext ctx, 10 | AlwaysReturnType? expectedReturnType) { 11 | final cond = compileExpression(s.condition, ctx); 12 | final msg = s.message != null 13 | ? compileExpression(s.message!, ctx) 14 | : BuiltinValue().push(ctx); 15 | 16 | doAssert(ctx, cond, msg); 17 | 18 | return StatementInfo(-1); 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/pattern_assignment.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/helpers/pattern.dart'; 6 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 7 | 8 | Variable compilePatternAssignment(CompilerContext ctx, PatternAssignment e) { 9 | final bound = patternTypeBound(ctx, e.pattern); 10 | final result = compileExpression(e.expression, ctx, bound); 11 | if (!result.type.isAssignableTo(ctx, bound)) { 12 | throw CompileError( 13 | 'Type ${result.type} is not assignable to pattern type $bound', 14 | e, 15 | ); 16 | } 17 | 18 | patternMatchAndBind(ctx, e.pattern, result, 19 | patternContext: PatternBindContext.none); 20 | 21 | return result; 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/security/permission.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | 3 | /// A rule that allows or denies a dart_eval [Runtime] access to a potentially 4 | /// sensitive resource, such as the network or file system. 5 | abstract class Permission { 6 | /// The domain specifies the type of resource, such as 'network' or 7 | /// 'filesystem'. 8 | List get domains; 9 | 10 | /// Returns true if the permission allows access to the specified resource. 11 | /// If the permission is granular, the [data] parameter may be used to 12 | /// specify a specific resource (e.g. a URL for a network permission). 13 | bool match([Object? data]); 14 | 15 | @override 16 | bool operator ==(Object other) => 17 | identical(this, other) || 18 | other is Permission && 19 | runtimeType == other.runtimeType && 20 | domains == other.domains; 21 | 22 | @override 23 | int get hashCode => domains.hashCode; 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/helpers/assert.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/type.dart'; 4 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 5 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 6 | 7 | void doAssert(CompilerContext ctx, Variable condition, Variable message) { 8 | message.boxIfNeeded(ctx).pushArg(ctx); 9 | ctx.pushOp( 10 | InvokeExternal.make(ctx.bridgeStaticFunctionIndices[ 11 | ctx.libraryMap['dart:core']]!['AssertionError.']!), 12 | InvokeExternal.LEN); 13 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 14 | final assertionErr = Variable.alloc(ctx, 15 | TypeRef.fromBridgeTypeRef(ctx, BridgeTypeRef(CoreTypes.assertionError))); 16 | 17 | ctx.pushOp( 18 | Assert.make(condition.scopeFrameOffset, assertionErr.scopeFrameOffset), 19 | Assert.LEN); 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/registry.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/compiler/compiler.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | 4 | /// A registry of compile-time bridge declarations. Implemented by [Compiler] 5 | /// and [BridgeSerializer]. 6 | abstract class BridgeDeclarationRegistry { 7 | void defineBridgeClass(BridgeClassDef classDef); 8 | 9 | /// Define a bridged enum definition to be used when compiling. 10 | void defineBridgeEnum(BridgeEnumDef enumDef); 11 | 12 | /// Add a unit source to the list of additional sources which will be compiled 13 | /// alongside the packages specified in [compile]. 14 | void addSource(DartSource source); 15 | 16 | /// Define a bridged top-level function declaration. 17 | void defineBridgeTopLevelFunction(BridgeFunctionDeclaration function); 18 | 19 | /// Add a mapping from a library URI to an exported library URI. 20 | void addExportedLibraryMapping(String libraryUri, String exportUri); 21 | } 22 | -------------------------------------------------------------------------------- /lib/dart_eval_bridge.dart: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | export 'package:pub_semver/pub_semver.dart' show Version; 4 | export 'src/eval/runtime/runtime.dart' show Runtime; 5 | export 'src/eval/runtime/class.dart' hide $InstanceImpl; 6 | export 'src/eval/runtime/declaration.dart' hide EvalClassClass; 7 | export 'src/eval/bridge/declaration/class.dart'; 8 | export 'src/eval/bridge/declaration/enum.dart'; 9 | export 'src/eval/bridge/declaration/type.dart'; 10 | export 'src/eval/bridge/declaration/function.dart'; 11 | export 'src/eval/bridge/registry.dart'; 12 | export 'src/eval/bridge/serializer.dart'; 13 | export 'src/eval/runtime/override.dart' show runtimeOverride; 14 | export 'src/eval/compiler/model/source.dart'; 15 | export 'src/eval/plugin.dart'; 16 | export 'src/eval/shared/types.dart'; 17 | export 'src/eval/runtime/function.dart' 18 | hide EvalFunctionPtr, EvalStaticFunctionPtr; 19 | export 'src/eval/bridge/runtime_bridge.dart' show $Bridge; 20 | export 'src/eval/bridge/declaration.dart' show BridgeDeclaration; 21 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/util/tree_shake.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/visitor.dart'; 3 | 4 | class TreeShakeVisitor extends RecursiveAstVisitor { 5 | final TreeShakeContext ctx = TreeShakeContext(); 6 | 7 | @override 8 | TreeShakeContext? visitSimpleIdentifier(SimpleIdentifier node) { 9 | output(node.name); 10 | super.visitSimpleIdentifier(node); 11 | return ctx; 12 | } 13 | 14 | @override 15 | TreeShakeContext? visitNamedType(NamedType node) { 16 | output(node.name2.lexeme); 17 | super.visitNamedType(node); 18 | return ctx; 19 | } 20 | 21 | @override 22 | TreeShakeContext? visitComment(Comment node) { 23 | // Ignore comments 24 | return ctx; 25 | } 26 | 27 | void output(String? s) { 28 | if (s == null) return; 29 | ctx.identifiers.add(s); 30 | } 31 | } 32 | 33 | class TreeShakeContext { 34 | TreeShakeContext(); 35 | 36 | Set identifiers = {}; 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/source_node_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | 5 | /// A source node wrapper allows you to, at compile time, 'listen' for constructor invocations of classes 6 | /// conforming to a certain bridge type, and surround them with invocations of another class, using a 7 | /// child parameter as well as parameters derived from the original invocation's [AstNode] 8 | class SourceNodeWrapper { 9 | SourceNodeWrapper( 10 | {required this.listenType, 11 | required this.wrapperType, 12 | required this.constructor, 13 | required this.buildArguments}); 14 | 15 | final BridgeTypeRef listenType; 16 | final BridgeTypeRef wrapperType; 17 | final String constructor; 18 | 19 | /// Return a list of (positional only) arguments. Use null to indicate the child. 20 | final List Function(AstNode node) buildArguments; 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/helpers/equality.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 5 | import 'package:dart_eval/src/eval/compiler/type.dart'; 6 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 7 | 8 | Variable checkNotEqual(CompilerContext ctx, Variable L, Variable R) { 9 | ctx.pushOp(CheckEq.make(L.scopeFrameOffset, R.scopeFrameOffset), CheckEq.LEN); 10 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 11 | final cond = Variable.alloc(ctx, CoreTypes.bool.ref(ctx)); 12 | ctx.pushOp(LogicalNot.make(cond.scopeFrameOffset), LogicalNot.LEN); 13 | return Variable.alloc(ctx, CoreTypes.bool.ref(ctx).copyWith(boxed: false)); 14 | } 15 | 16 | Variable checkNotNull(CompilerContext ctx, Variable L) { 17 | final $null = BuiltinValue().push(ctx); 18 | return checkNotEqual(ctx, L, $null); 19 | } 20 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dart_eval 2 | description: A flexible Dart bytecode compiler and interpreter written in Dart, enabling dynamic execution and code push for AOT Dart apps. 3 | version: 0.8.2 4 | homepage: https://github.com/ethanblake4/dart_eval 5 | platforms: 6 | android: 7 | ios: 8 | linux: 9 | macos: 10 | web: 11 | windows: 12 | topics: 13 | - vm 14 | - compiler 15 | - interpreter 16 | - dart 17 | - codepush 18 | 19 | environment: 20 | sdk: ">=3.4.0 <4.0.0" 21 | 22 | executables: 23 | dart_eval: 24 | 25 | dependencies: 26 | args: ^2.4.0 27 | analyzer: ^7.0.0 28 | collection: ^1.18.0 29 | directed_graph: '>=0.3.9 <0.5.0' 30 | glob: ^2.1.3 31 | json_annotation: ^4.7.0 32 | pubspec_parse: ^1.2.0 33 | path: ^1.8.0 34 | pub_semver: ^2.1.0 35 | package_config: ^2.1.0 36 | dart_style: ^3.0.0 37 | change_case: ^2.1.0 38 | yaml: ^3.1.3 39 | 40 | dev_dependencies: 41 | test: ^1.25.0 42 | build_runner: ^2.4.0 43 | json_serializable: ^6.3.0 44 | lints: '>=4.0.0' 45 | -------------------------------------------------------------------------------- /lib/stdlib/core.dart: -------------------------------------------------------------------------------- 1 | /// Provides dart:core bridge classes and wrappers 2 | library; 3 | 4 | export '../src/eval/shared/stdlib/core/base.dart'; 5 | export '../src/eval/shared/stdlib/core/collection.dart'; 6 | export '../src/eval/shared/stdlib/core/iterator.dart'; 7 | export '../src/eval/shared/stdlib/core/comparable.dart'; 8 | export '../src/eval/shared/stdlib/core/date_time.dart'; 9 | export '../src/eval/shared/stdlib/core/duration.dart'; 10 | export '../src/eval/shared/stdlib/core/errors.dart'; 11 | export '../src/eval/shared/stdlib/core/exceptions.dart'; 12 | export '../src/eval/shared/stdlib/core/future.dart'; 13 | export '../src/eval/shared/stdlib/core/num.dart'; 14 | export '../src/eval/shared/stdlib/core/object.dart'; 15 | export '../src/eval/shared/stdlib/core/pattern.dart'; 16 | export '../src/eval/shared/stdlib/core/regexp.dart'; 17 | export '../src/eval/shared/stdlib/core/string_buffer.dart'; 18 | export '../src/eval/shared/stdlib/core/uri.dart'; 19 | export '../src/eval/shared/stdlib/core/stack_trace.dart'; 20 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/symbol.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 8 | 9 | Variable compileSymbolLiteral(SymbolLiteral l, CompilerContext ctx) { 10 | var name = l.components.map((t) => t.lexeme).join('.'); 11 | if (name.startsWith('_')) { 12 | name = name.substring(1); 13 | } 14 | BuiltinValue(stringval: name).push(ctx).pushArg(ctx); 15 | ctx.pushOp( 16 | InvokeExternal.make(ctx.bridgeStaticFunctionIndices[ 17 | ctx.libraryMap['dart:core']!]!['Symbol.']!), 18 | InvokeExternal.LEN); 19 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 20 | return Variable.alloc(ctx, CoreTypes.symbol.ref(ctx)); 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/eval/plugin.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | 4 | /// A plugin that can configure compile-time and runtime code / bindings 5 | /// for dart_eval. 6 | /// 7 | /// The presence of a unique [identifier] allows dart_eval to cache certain 8 | /// results of applying a plugin, improving performance for subsequent 9 | /// compilations. 10 | abstract class EvalPlugin { 11 | /// Unique identifier for this plugin. In most cases this should be the 12 | /// package name. 13 | String get identifier; 14 | 15 | /// Configure this plugin for use in a dart_eval [Compiler] or 16 | /// [BridgeSerializer]. 17 | void configureForCompile(BridgeDeclarationRegistry registry); 18 | 19 | /// Configure this plugin for use in a dart_eval [Runtime]. 20 | void configureForRuntime(Runtime runtime); 21 | } 22 | 23 | /// A registry of [EvalPlugin]s. 24 | abstract class EvalPluginRegistry { 25 | /// Register a plugin with this registry. 26 | void addPlugin(EvalPlugin plugin); 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/funcexpr_invocation.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 4 | import 'package:dart_eval/src/eval/compiler/helpers/closure.dart'; 5 | import 'package:dart_eval/src/eval/compiler/reference.dart'; 6 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 7 | 8 | /// Compile a [FunctionExpressionInvocation] 9 | Variable compileFunctionExpressionInvocation( 10 | FunctionExpressionInvocation e, CompilerContext ctx) { 11 | Reference? target; 12 | Variable? fallback; 13 | 14 | // Using a reference allows us to potentially optimize to static dispatch, if the exact function 15 | // is known at compile-time 16 | if (canReference(e.function)) { 17 | target = compileExpressionAsReference(e.function, ctx); 18 | } else { 19 | fallback = compileExpression(e.function, ctx); 20 | } 21 | 22 | return invokeClosure(ctx, target, fallback, e.argumentList).result; 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/type.dart'; 4 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 5 | 6 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 7 | import 'context.dart'; 8 | 9 | int beginMethod( 10 | CompilerContext ctx, AstNode scopeHost, int offset, String name) { 11 | final position = ctx.out.length; 12 | var op = PushScope.make(ctx.library, offset, name); 13 | ctx.pushOp(op, PushScope.len(op)); 14 | return position; 15 | } 16 | 17 | void setupAsyncFunction(CompilerContext ctx) { 18 | ctx.pushOp( 19 | InvokeExternal.make(ctx.bridgeStaticFunctionIndices[ 20 | ctx.libraryMap['dart:async']!]!['Completer.']!), 21 | InvokeExternal.LEN); 22 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 23 | ctx.setLocal( 24 | '#completer', Variable.alloc(ctx, AsyncTypes.completer.ref(ctx))); 25 | ctx.nearestAsyncFrame = ctx.locals.length - 1; 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/if.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 4 | import 'package:dart_eval/src/eval/compiler/macros/branch.dart'; 5 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | 8 | StatementInfo compileIfStatement( 9 | IfStatement s, CompilerContext ctx, AlwaysReturnType? expectedReturnType) { 10 | final elseStatement = s.elseStatement; 11 | return macroBranch( 12 | ctx, 13 | expectedReturnType, 14 | condition: (ctx) => compileExpression(s.expression, ctx), 15 | thenBranch: (ctx, expectedReturnType) => 16 | compileStatement(s.thenStatement, expectedReturnType, ctx), 17 | elseBranch: elseStatement == null 18 | ? null 19 | : (ctx, expectedReturnType) => 20 | compileStatement(elseStatement, expectedReturnType, ctx), 21 | source: s, 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/print.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | 3 | void configurePrintForCompile(BridgeDeclarationRegistry registry) { 4 | registry.defineBridgeTopLevelFunction(BridgeFunctionDeclaration( 5 | 'dart:core', 6 | 'print', 7 | BridgeFunctionDef( 8 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.voidType)), 9 | params: [ 10 | BridgeParameter( 11 | 'object', 12 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.object), 13 | nullable: true), 14 | false) 15 | ], 16 | namedParams: []))); 17 | } 18 | 19 | void configurePrintForRuntime(Runtime runtime) { 20 | runtime.registerBridgeFunc('dart:core', 'print', const _$print().call); 21 | } 22 | 23 | class _$print implements EvalCallable { 24 | const _$print(); 25 | 26 | @override 27 | $Value? call(Runtime runtime, $Value? target, List<$Value?> args) { 28 | print(runtime.valueToString(args[0])); 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/model/library.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/bridge/declaration.dart'; 3 | 4 | class Library { 5 | Library(this.uri, 6 | {required this.imports, 7 | required this.exports, 8 | required this.declarations, 9 | this.library}); 10 | 11 | /// A `package`, `dart`, or `file` URI 12 | final Uri uri; 13 | 14 | final String? library; 15 | 16 | final List imports; 17 | final List exports; 18 | 19 | List declarations; 20 | 21 | @override 22 | bool operator ==(Object other) => 23 | identical(this, other) || 24 | other is Library && runtimeType == other.runtimeType && uri == other.uri; 25 | 26 | @override 27 | int get hashCode => uri.hashCode; 28 | 29 | Library copyWith({List? declarations}) { 30 | return Library(uri, 31 | library: library, 32 | imports: imports, 33 | exports: exports, 34 | declarations: declarations ?? this.declarations); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/return.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/helpers/return.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | 8 | import 'statement.dart'; 9 | 10 | StatementInfo compileReturn(CompilerContext ctx, ReturnStatement s, 11 | AlwaysReturnType? expectedReturnType) { 12 | AstNode? e = s; 13 | while (e != null) { 14 | if (e is FunctionBody) { 15 | break; 16 | } 17 | e = e.parent; 18 | } 19 | 20 | final expression = s.expression; 21 | 22 | final value = expression == null 23 | ? null 24 | : compileExpression(s.expression!, ctx, expectedReturnType?.type); 25 | return doReturn( 26 | ctx, 27 | expectedReturnType ?? AlwaysReturnType(CoreTypes.dynamic.ref(ctx), true), 28 | value, 29 | isAsync: (e as FunctionBody).isAsynchronous); 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/pattern_variable_declaration.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/token.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/helpers/pattern.dart'; 6 | 7 | import 'statement.dart'; 8 | 9 | StatementInfo compilePatternVariableDeclarationStatement( 10 | PatternVariableDeclarationStatement s, CompilerContext ctx) { 11 | compilePatternVariableDeclaration(s.declaration, ctx); 12 | return StatementInfo(-1); 13 | } 14 | 15 | void compilePatternVariableDeclaration( 16 | PatternVariableDeclaration dec, CompilerContext ctx) { 17 | final bound = patternTypeBound(ctx, dec.pattern, source: dec); 18 | final result = compileExpression(dec.expression, ctx, bound); 19 | patternMatchAndBind(ctx, dec.pattern, result, 20 | patternContext: dec.keyword.keyword == Keyword.FINAL 21 | ? PatternBindContext.declareFinal 22 | : PatternBindContext.declare); 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/extensions/type.dart: -------------------------------------------------------------------------------- 1 | part of 'extensions.dart'; 2 | 3 | extension BridgeTypeRefExt on BridgeTypeRef { 4 | /// Extension to create a type annotation from a [BridgeTypeRef] 5 | /// ```dart 6 | /// // Without extension method 7 | /// final bridged = BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.int)); 8 | /// 9 | /// // With extension method, you can write 10 | /// final bridged = BridgeTypeRef(CoreTypes.int).annotate; 11 | /// ``` 12 | BridgeTypeAnnotation get annotate { 13 | return BridgeTypeAnnotation(this); 14 | } 15 | 16 | /// Extension to create a nullable type annotation from a [BridgeTypeRef] 17 | /// ```dart 18 | /// // Without extension method 19 | /// final bridged = BridgeTypeAnnotation( 20 | /// BridgeTypeRef(CoreTypes.int), 21 | /// nullable: true 22 | /// ); 23 | /// 24 | /// // With extension method, you can write 25 | /// final bridged = BridgeTypeRef(CoreTypes.int).annotateNullable; 26 | /// ``` 27 | BridgeTypeAnnotation get annotateNullable { 28 | return BridgeTypeAnnotation(this, nullable: true); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/block.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/model/label.dart'; 3 | 4 | import '../context.dart'; 5 | import 'statement.dart'; 6 | import '../type.dart'; 7 | 8 | StatementInfo compileBlock( 9 | Block b, AlwaysReturnType? expectedReturnType, CompilerContext ctx, 10 | {String name = ''}) { 11 | final position = ctx.out.length; 12 | ctx.beginAllocScope(); 13 | 14 | var willAlwaysReturn = false; 15 | var willAlwaysThrow = false; 16 | 17 | ctx.labels.add(SimpleCompilerLabel()); 18 | for (final s in b.statements) { 19 | final stInfo = compileStatement(s, expectedReturnType, ctx); 20 | 21 | if (stInfo.willAlwaysThrow) { 22 | willAlwaysThrow = true; 23 | break; 24 | } 25 | if (stInfo.willAlwaysReturn) { 26 | willAlwaysReturn = true; 27 | break; 28 | } 29 | } 30 | ctx.labels.removeLast(); 31 | 32 | ctx.endAllocScope(popValues: !willAlwaysThrow && !willAlwaysReturn); 33 | 34 | return StatementInfo(position, 35 | willAlwaysReturn: willAlwaysReturn, willAlwaysThrow: willAlwaysThrow); 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/eval/utils/wrap_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/stdlib/core.dart'; 3 | 4 | /// Converts a Dart list of generic type `T` into a `$List` of `$Value` by 5 | /// applying a wrapping function to each element in the list. 6 | $List<$Value> wrapList(List list, $Value Function(T element) wrap) { 7 | return $List.wrap([for (var e in list) wrap(e)]); 8 | } 9 | 10 | /// Converts a Dart list of nullable generic type `T` into a `$List` of `$Value?` 11 | /// by applying a wrapping function to each non-null element and using `$null()` for null elements. 12 | $List<$Value?> wrapNullableList( 13 | List list, $Value Function(T element) wrap) { 14 | return $List.wrap([for (var e in list) e == null ? $null() : wrap(e)]); 15 | } 16 | 17 | /// Converts a Dart map with keys of type `K` and values of type `V` into a 18 | /// `$Map` of `$Value` by applying a wrapping function to each entry in the map. 19 | $Map<$Value, $Value> wrapMap( 20 | Map map, MapEntry<$Value, $Value> Function(K key, V value) wrap) { 21 | return $Map.wrap(map.map((key, value) => wrap(key, value))); 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/record.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/runtime/class.dart'; 2 | import 'package:dart_eval/src/eval/runtime/exception.dart'; 3 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 4 | 5 | class $Record implements $Instance { 6 | final List fields; 7 | final Map mapping; 8 | final int typeId; 9 | 10 | const $Record(this.fields, this.mapping, this.typeId); 11 | 12 | @override 13 | int $getRuntimeType(Runtime runtime) => typeId; 14 | 15 | @override 16 | $Value? $getProperty(Runtime runtime, String identifier) { 17 | final index = mapping[identifier]; 18 | if (index != null) { 19 | final value = fields[index]; 20 | if (value is! $Value) { 21 | throw InvalidUnboxedValueException( 22 | 'Record field "$identifier" is not a \$Value'); 23 | } 24 | return value; 25 | } 26 | throw EvalUnknownPropertyException(identifier); 27 | } 28 | 29 | @override 30 | void $setProperty(Runtime runtime, String identifier, $Value value) { 31 | throw EvalUnknownPropertyException(identifier); 32 | } 33 | 34 | @override 35 | $Value? get $value => null; 36 | 37 | @override 38 | $Value? get $reified => null; 39 | } 40 | -------------------------------------------------------------------------------- /test/diagnostic_mode_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/expect.dart'; 3 | import 'package:test/scaffolding.dart'; 4 | 5 | void main() { 6 | test('diagnostic mode ignore ignores parse warnings', () async { 7 | final compiler = Compiler()..diagnosticMode = DiagnosticMode.ignore; 8 | final source = ''' 9 | /// {@nodoc} 10 | bool fn(){ 11 | return true; 12 | } 13 | '''; 14 | expect( 15 | () => compiler.compile({ 16 | 'my_package': { 17 | 'main.dart': source, 18 | } 19 | }), 20 | prints(isEmpty)); 21 | }); 22 | 23 | test('diagnostic mode printErrorsAndWarnings prints parse warnings', 24 | () async { 25 | final compiler = Compiler() 26 | ..diagnosticMode = DiagnosticMode.printErrorsAndWarnings; 27 | final source = ''' 28 | /// {@nodoc} 29 | bool fn(){ 30 | return true; 31 | } 32 | '''; 33 | expect( 34 | () => compiler.compile({ 35 | 'my_package': { 36 | 'main.dart': source, 37 | } 38 | }), 39 | prints(contains('Parsing warning:'))); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/prefixed_import_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/expect.dart'; 3 | import 'package:test/scaffolding.dart'; 4 | 5 | void main() { 6 | group('Prefixed imports', () { 7 | late Compiler compiler; 8 | 9 | setUp(() { 10 | compiler = Compiler(); 11 | }); 12 | 13 | test('Importing constant via prefix', () { 14 | final runtime = compiler.compileWriteAndLoad({ 15 | 'eval_test': { 16 | 'main.dart': ''' 17 | import 'package:eval_test/chars.dart' as chars; 18 | 19 | void main() { 20 | if (chars.plus == 0x2b) { 21 | print('Plus is correct'); 22 | } else { 23 | print('Plus is incorrect'); 24 | } 25 | } 26 | ''', 27 | 'chars.dart': ''' 28 | const plus = 0x2b; 29 | const minus = 0x2d; 30 | const period = 0x2e; 31 | const slash = 0x2f; 32 | ''' 33 | } 34 | }); 35 | 36 | expect( 37 | () { 38 | runtime.executeLib('package:eval_test/main.dart', 'main'); 39 | }, 40 | prints('Plus is correct\n'), 41 | ); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/is.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/type.dart'; 6 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 7 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 8 | import 'package:dart_eval/src/eval/shared/types.dart'; 9 | 10 | Variable compileIsExpression(IsExpression e, CompilerContext ctx) { 11 | var V = compileExpression(e.expression, ctx); 12 | final slot = TypeRef.fromAnnotation(ctx, ctx.library, e.type); 13 | final not = e.notOperator != null; 14 | 15 | V.inferType(ctx, slot); 16 | 17 | /// If the type is definitely a subtype of the slot, we can just return true. 18 | if (V.type.isAssignableTo(ctx, slot, forceAllowDynamic: false)) { 19 | return BuiltinValue(boolval: !not).push(ctx); 20 | } 21 | 22 | V = V.boxIfNeeded(ctx); 23 | 24 | /// Otherwise do a runtime test 25 | ctx.pushOp(IsType.make(V.scopeFrameOffset, ctx.typeRefIndexMap[slot]!, not), 26 | IsType.length); 27 | return Variable.alloc(ctx, CoreTypes.bool.ref(ctx).copyWith(boxed: false)); 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/extensions/parameter.dart: -------------------------------------------------------------------------------- 1 | part of 'extensions.dart'; 2 | 3 | extension BridgeParameterExt on String { 4 | /// Extension to wrap 'key' with [BridgeParameter]() 5 | /// ```dart 6 | /// //Without Extension method 7 | /// final keyUnwrapped = BridgeParameter('icon', BridgeTypeAnnotation($IconData.$type), false); 8 | /// //With Extension method, you can write 9 | /// final keyWrapped = 'icon'.param($IconData.$type); 10 | /// ``` 11 | /// See [paramOptional] for optional named param 12 | /// See [paramNullable] for nullable param 13 | BridgeParameter param(BridgeTypeAnnotation type) { 14 | return BridgeParameter(this, type, false); 15 | } 16 | 17 | /// Extension to wrap 'key' with [BridgeParameter]() 18 | /// ```dart 19 | /// //Without Extension method 20 | /// final keyUnwrapped = BridgeParameter('icon', BridgeTypeAnnotation($IconData.$type), true); 21 | /// //With Extension method you can write 22 | /// final keyWrapped = 'icon'.paramOptional($IconData.$type); 23 | /// ``` 24 | /// See [paramOptional] for optional named param 25 | /// See [paramOptionalNullable] for nullable param 26 | BridgeParameter paramOptional(BridgeTypeAnnotation type) { 27 | return BridgeParameter( 28 | this, 29 | type, 30 | true, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/adjacent_strings.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/literal.dart'; 6 | import 'package:dart_eval/src/eval/compiler/helpers/invoke.dart'; 7 | import 'package:dart_eval/src/eval/compiler/type.dart'; 8 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 9 | 10 | Variable compileAdjacentStrings(CompilerContext ctx, AdjacentStrings str) { 11 | if (str.strings.every((element) => element is SimpleStringLiteral)) { 12 | final el = BuiltinValue( 13 | stringval: str.strings 14 | .map((e) => (e as SimpleStringLiteral).stringValue) 15 | .join('')) 16 | .push(ctx); 17 | return el; 18 | } 19 | 20 | Variable? build; 21 | for (final string in str.strings) { 22 | final V = parseLiteral(string, ctx); 23 | Variable vStr; 24 | if (V.type == CoreTypes.string.ref(ctx)) { 25 | vStr = V; 26 | } else { 27 | vStr = V.invoke(ctx, 'toString', []).result; 28 | } 29 | build = build == null ? vStr : build.invoke(ctx, '+', [vStr]).result; 30 | } 31 | 32 | return build!; 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: Dart 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | branches: [ master ] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | # Note: This workflow uses the latest stable version of the Dart SDK. 22 | # You can specify other versions if desired, see documentation here: 23 | # https://github.com/dart-lang/setup-dart/blob/main/README.md 24 | - uses: dart-lang/setup-dart@v1 25 | 26 | - name: Install dependencies 27 | run: dart pub get 28 | 29 | # Uncomment this step to verify the use of 'dart format' on each commit. 30 | # - name: Verify formatting 31 | # run: dart format --output=none --set-exit-if-changed . 32 | 33 | # Consider passing '--fatal-infos' for slightly stricter analysis. 34 | - name: Analyze project source 35 | run: dart analyze 36 | 37 | # Your project will need to have tests in test/ and a dependency on 38 | # package:test for this step to succeed. 39 | - name: Run tests 40 | run: dart test 41 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/identical.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/shared/stdlib/core/base.dart'; 3 | 4 | void configureIdenticalForCompile(BridgeDeclarationRegistry registry) { 5 | registry.defineBridgeTopLevelFunction(BridgeFunctionDeclaration( 6 | 'dart:core', 7 | 'identical', 8 | BridgeFunctionDef( 9 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.bool)), 10 | params: [ 11 | BridgeParameter( 12 | 'a', 13 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.object), 14 | nullable: true), 15 | false), 16 | BridgeParameter( 17 | 'b', 18 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.object), 19 | nullable: true), 20 | false) 21 | ], 22 | namedParams: []))); 23 | } 24 | 25 | void configureIdenticalForRuntime(Runtime runtime) { 26 | runtime.registerBridgeFunc( 27 | 'dart:core', 'identical', const _$identical().call); 28 | } 29 | 30 | class _$identical implements EvalCallable { 31 | const _$identical(); 32 | 33 | @override 34 | $Value? call(Runtime runtime, $Value? target, List<$Value?> args) { 35 | return $bool(identical(args[0]?.$value, args[1]?.$value)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/eval/cli/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:package_config/package_config_types.dart'; 5 | import 'package:path/path.dart'; 6 | 7 | Directory findProjectRoot(Directory start) { 8 | var projectRoot = start; 9 | while (true) { 10 | final files = projectRoot.listSync(); 11 | if (files 12 | .any((file) => (file is File && file.path.endsWith('pubspec.yaml')))) { 13 | break; 14 | } 15 | projectRoot = projectRoot.parent; 16 | } 17 | return projectRoot; 18 | } 19 | 20 | PackageConfig getPackageConfig(Directory projectRoot) { 21 | String path; 22 | final workspaceRefFile = 23 | File(join(projectRoot.path, '.dart_tool', 'pub', 'workspace_ref.json')); 24 | if (workspaceRefFile.existsSync()) { 25 | final workspaceRef = workspaceRefFile.readAsStringSync(); 26 | final workspaceRefMap = json.decode(workspaceRef) as Map; 27 | final workspacePath = workspaceRefMap['workspaceRoot'] as String; 28 | path = join(normalize('${projectRoot.path}/.dart_tool/pub/$workspacePath'), 29 | '.dart_tool', 'package_config.json'); 30 | } else { 31 | path = join(projectRoot.path, '.dart_tool', 'package_config.json'); 32 | } 33 | final packageConfigFile = File(path).readAsStringSync(); 34 | return PackageConfig.parseString(packageConfigFile, Uri.file(path)); 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/collection/if.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/collection/list.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/macros/branch.dart'; 6 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 7 | import 'package:dart_eval/src/eval/compiler/type.dart'; 8 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 9 | 10 | List compileIfElementForList( 11 | IfElement e, Variable list, CompilerContext ctx, bool box) { 12 | final potentialReturnTypes = []; 13 | final elseElement = e.elseElement; 14 | 15 | macroBranch(ctx, null, 16 | condition: (ctx) => compileExpression(e.expression, ctx), 17 | thenBranch: (ctx, _) { 18 | potentialReturnTypes 19 | .addAll(compileListElement(e.thenElement, list, ctx, box)); 20 | return StatementInfo(-1); 21 | }, 22 | elseBranch: elseElement == null 23 | ? null 24 | : (ctx, _) { 25 | potentialReturnTypes 26 | .addAll(compileListElement(elseElement, list, ctx, box)); 27 | return StatementInfo(-1); 28 | }); 29 | 30 | return potentialReturnTypes; 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/break.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 4 | import 'package:dart_eval/src/eval/compiler/model/label.dart'; 5 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 6 | 7 | StatementInfo compileBreakStatement(BreakStatement s, CompilerContext ctx) { 8 | if (s.label != null) { 9 | throw CompileError('Break labels are not currently supported', s); 10 | } 11 | 12 | final currentState = ctx.saveState(); 13 | 14 | var index = 15 | ctx.labels.lastIndexWhere((label) => label.type == LabelType.loop); 16 | 17 | if (index == -1) { 18 | index = 19 | ctx.labels.lastIndexWhere((label) => label.type == LabelType.branch); 20 | } 21 | 22 | if (index == -1) { 23 | throw CompileError( 24 | 'Cannot use \'break\' outside of a loop or switch context', s); 25 | } 26 | 27 | for (var i = ctx.labels.length - 1; i > index; i--) { 28 | ctx.labels[i].cleanup(ctx); 29 | } 30 | final label = ctx.labels[index]; 31 | final offset = label.cleanup(ctx); 32 | if (!ctx.labelReferences.containsKey(label)) { 33 | ctx.labelReferences[label] = {}; 34 | } 35 | ctx.labelReferences[label]!.add(offset); 36 | ctx.restoreState(currentState); 37 | return StatementInfo(-1); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/typed_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/shared/stdlib/typed_data/typed_data.dart'; 3 | 4 | /// [EvalPlugin] for the `dart:typed_data` library 5 | class DartTypedDataPlugin implements EvalPlugin { 6 | @override 7 | String get identifier => 'dart:typed_data'; 8 | 9 | @override 10 | void configureForCompile(BridgeDeclarationRegistry registry) { 11 | registry.defineBridgeClass($ByteBuffer.$declaration); 12 | registry.defineBridgeClass($TypedData.$declaration); 13 | registry.defineBridgeClass($ByteData.$declaration); 14 | registry.defineBridgeClass($Uint8List.$declaration); 15 | } 16 | 17 | @override 18 | void configureForRuntime(Runtime runtime) { 19 | runtime.registerBridgeFunc('dart:typed_data', 'ByteData.', $ByteData.$new); 20 | runtime.registerBridgeFunc( 21 | 'dart:typed_data', 'ByteData.view', $ByteData.$view); 22 | runtime.registerBridgeFunc( 23 | 'dart:typed_data', 'Uint8List.', $Uint8List.$new); 24 | runtime.registerBridgeFunc( 25 | 'dart:typed_data', 'Uint8List.fromList', $Uint8List.$fromList); 26 | runtime.registerBridgeFunc( 27 | 'dart:typed_data', 'Uint8List.view', $Uint8List.$view); 28 | runtime.registerBridgeFunc( 29 | 'dart:typed_data', 'Uint8List.sublistView', $Uint8List.$sublistView); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/keywords.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 8 | 9 | Variable compileThisExpression(ThisExpression e, CompilerContext ctx) { 10 | if (ctx.currentClass == null) { 11 | throw CompileError("Cannot use 'this' outside of a class context"); 12 | } 13 | return ctx.lookupLocal('#this')!; 14 | } 15 | 16 | Variable compileSuperExpression(SuperExpression e, CompilerContext ctx) { 17 | if (ctx.currentClass == null || ctx.currentClass is! ClassDeclaration) { 18 | throw CompileError("Cannot use 'super' outside of a class context"); 19 | } 20 | 21 | var type = CoreTypes.object.ref(ctx); 22 | final extendsClause = (ctx.currentClass as ClassDeclaration).extendsClause; 23 | if (extendsClause != null) { 24 | type = 25 | ctx.visibleTypes[ctx.library]![extendsClause.superclass.name2.value()]!; 26 | } 27 | 28 | final $this = ctx.lookupLocal('#this')!; 29 | ctx.pushOp(PushSuper.make($this.scopeFrameOffset), PushSuper.length); 30 | final v = Variable.alloc(ctx, type); 31 | return v; 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/string_interpolation.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 6 | import 'package:dart_eval/src/eval/compiler/helpers/invoke.dart'; 7 | import 'package:dart_eval/src/eval/compiler/type.dart'; 8 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 9 | 10 | Variable compileStringInterpolation( 11 | CompilerContext ctx, StringInterpolation str) { 12 | Variable? build; 13 | for (final element in str.elements) { 14 | if (element is InterpolationString) { 15 | final sval = element.value; 16 | if (sval.isNotEmpty) { 17 | final el = BuiltinValue(stringval: element.value).push(ctx); 18 | build = build == null ? el : build.invoke(ctx, '+', [el]).result; 19 | } 20 | } else if (element is InterpolationExpression) { 21 | final V = compileExpression(element.expression, ctx); 22 | Variable vStr; 23 | if (V.type == CoreTypes.string.ref(ctx)) { 24 | vStr = V; 25 | } else { 26 | vStr = V.invoke(ctx, 'toString', []).result; 27 | } 28 | build = build == null ? vStr : build.invoke(ctx, '+', [vStr]).result; 29 | } 30 | } 31 | 32 | return build!; 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/collection.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/shared/stdlib/collection/linked_hash_map.dart'; 3 | import 'package:dart_eval/src/eval/shared/stdlib/collection/list_queue.dart'; 4 | 5 | /// [EvalPlugin] for the `dart:collection` library 6 | class DartCollectionPlugin implements EvalPlugin { 7 | @override 8 | String get identifier => 'dart:collection'; 9 | 10 | @override 11 | void configureForCompile(BridgeDeclarationRegistry registry) { 12 | registry.defineBridgeClass($LinkedHashMap.$declaration); 13 | registry.defineBridgeClass($ListQueue.$declaration); 14 | } 15 | 16 | @override 17 | void configureForRuntime(Runtime runtime) { 18 | runtime.registerBridgeFunc( 19 | 'dart:collection', 'LinkedHashMap.', $LinkedHashMap.$new); 20 | runtime.registerBridgeFunc( 21 | 'dart:collection', 'LinkedHashMap.identity', $LinkedHashMap.$identity); 22 | runtime.registerBridgeFunc( 23 | 'dart:collection', 'LinkedHashMap.from', $LinkedHashMap.$from); 24 | runtime.registerBridgeFunc( 25 | 'dart:collection', 'LinkedHashMap.of', $LinkedHashMap.$of); 26 | runtime.registerBridgeFunc('dart:collection', 'LinkedHashMap.fromIterable', 27 | $LinkedHashMap.$fromIterable); 28 | runtime.registerBridgeFunc('dart:collection', 'LinkedHashMap.fromIterables', 29 | $LinkedHashMap.$fromIterables); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Ethan Elshyeb 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /lib/src/eval/compiler/util.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/type.dart'; 5 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 6 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 7 | 8 | class Pair { 9 | Pair(this.first, this.second); 10 | 11 | T first; 12 | T2 second; 13 | } 14 | 15 | class FunctionSignaturePool { 16 | FunctionSignaturePool(); 17 | 18 | int _idx = 0; 19 | final Map signatures = {}; 20 | 21 | int getSignature(FormalParameterList p) { 22 | final countPos = 23 | p.parameters.where((element) => element.isPositional).length; 24 | 25 | final sig = p.parameters.where((element) => element.isNamed).fold( 26 | '$countPos#', (previousValue, element) => '${element.name!.lexeme}#'); 27 | 28 | return signatures[sig] ?? (signatures[sig] = _idx++); 29 | } 30 | } 31 | 32 | void asyncComplete(CompilerContext ctx, int valueOffset) { 33 | var completer = ctx.lookupLocal('#completer'); 34 | if (completer == null) { 35 | InvokeExternal.make(ctx.bridgeStaticFunctionIndices[ 36 | ctx.libraryMap['dart:async']!]!['Completer.']!); 37 | completer = Variable.alloc(ctx, AsyncTypes.completer.ref(ctx)); 38 | } 39 | ctx.pushOp( 40 | ReturnAsync.make(valueOffset, completer.scopeFrameOffset), Return.LEN); 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/eval/bindgen/permission.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element2.dart'; 2 | 3 | String assertMethodPermissions(MethodElement2 element) { 4 | final metadata = element.metadata2; 5 | 6 | final permissions = metadata.annotations 7 | .where((e) => e.element2?.displayName == 'AssertPermission'); 8 | 9 | String output = ''; 10 | for (final permission in permissions) { 11 | final perm = permission.computeConstantValue(); 12 | if (perm == null) { 13 | print( 14 | 'Warning: skipped permission assertion as the annotation is not a constant value'); 15 | continue; 16 | } 17 | final name = perm.getField('name')!.toStringValue(); 18 | final constData = perm.getField('constData')?.toStringValue(); 19 | final paramData = perm.getField('paramData')?.toStringValue(); 20 | 21 | String data = ''; 22 | 23 | if (constData != null) { 24 | data = ", '$constData'"; 25 | } else if (paramData != null) { 26 | final params = element.formalParameters; 27 | for (var i = 0; i < params.length; i++) { 28 | final param = params[i]; 29 | if (param.name3 == paramData) { 30 | final nullCheck = param.isRequired ? '!' : '?'; 31 | final defaultValue = 32 | param.hasDefaultValue ? ' ?? ${param.defaultValueCode}' : ''; 33 | data = ', args[$i]$nullCheck.\$value$defaultValue'; 34 | break; 35 | } 36 | } 37 | } 38 | 39 | output += '''runtime.assertPermission('$name'$data);'''; 40 | } 41 | 42 | return output; 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/declaration/enum.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/bridge/declaration.dart'; 2 | import 'package:dart_eval/src/eval/bridge/declaration/class.dart'; 3 | import 'package:dart_eval/src/eval/bridge/declaration/type.dart'; 4 | import 'package:json_annotation/json_annotation.dart'; 5 | 6 | part 'enum.g.dart'; 7 | 8 | @JsonSerializable(explicitToJson: true) 9 | class BridgeEnumDef implements BridgeDeclaration { 10 | const BridgeEnumDef(this.type, 11 | {this.values = const [], 12 | this.methods = const {}, 13 | this.getters = const {}, 14 | this.setters = const {}, 15 | this.fields = const {}}); 16 | 17 | final BridgeTypeRef type; 18 | final List values; 19 | final Map methods; 20 | final Map getters; 21 | final Map setters; 22 | final Map fields; 23 | 24 | /// Connect the generated [_$BridgeClassDeclarationFromJson] function to the `fromJson` 25 | /// factory. 26 | factory BridgeEnumDef.fromJson(Map json) => 27 | _$BridgeEnumDefFromJson(json); 28 | 29 | /// Connect the generated [_$BridgeClassDeclarationToJson] function to the `toJson` method. 30 | Map toJson() => _$BridgeEnumDefToJson(this); 31 | 32 | BridgeEnumDef copyWith({BridgeTypeRef? type}) => 33 | BridgeEnumDef(type ?? this.type, 34 | values: values, 35 | methods: methods, 36 | getters: getters, 37 | setters: setters, 38 | fields: fields); 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/exception.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:dart_eval/src/eval/shared/stdlib/core/num.dart'; 4 | 5 | /// Format a dart_eval stack sample for printing. 6 | String formatStackSample(List st, int size, [int? frameOffset]) { 7 | final sb = StringBuffer('['); 8 | var i = 0; 9 | if (frameOffset != null) { 10 | i = max(0, frameOffset - size ~/ 1.3); 11 | } 12 | final end = min(i + size, st.length); 13 | for (; i < end; i++) { 14 | final s = st[i]; 15 | if (i == frameOffset) { 16 | sb.write('*'); 17 | } 18 | sb.write('L$i: '); 19 | if (s is List) { 20 | sb.write(formatStackSample(s, 3)); 21 | } else if (s is String) { 22 | sb.write('"$s"'); 23 | } else if (s is $num) { 24 | sb.write('\$$s'); 25 | } else { 26 | sb.write('$s'); 27 | } 28 | if (i < end - 1) { 29 | sb.write(', '); 30 | } 31 | } 32 | sb.write(']'); 33 | return sb.toString(); 34 | } 35 | 36 | class EvalUnknownPropertyException implements Exception { 37 | const EvalUnknownPropertyException(this.name); 38 | 39 | final String name; 40 | 41 | @override 42 | String toString() => 'EvalUnknownPropertyException ($name)'; 43 | } 44 | 45 | class InvalidUnboxedValueException implements Exception { 46 | const InvalidUnboxedValueException(this.value); 47 | 48 | final Object value; 49 | 50 | @override 51 | String toString() => 'InvalidUnboxedValueException: $value'; 52 | } 53 | 54 | class ProgramExit implements Exception { 55 | final int exitCode; 56 | 57 | ProgramExit(this.exitCode); 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/await.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 8 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 9 | 10 | Variable compileAwaitExpression(AwaitExpression e, CompilerContext ctx) { 11 | AstNode? e0 = e; 12 | while (e0 != null) { 13 | if (e0 is FunctionBody) { 14 | if (!e0.isAsynchronous) { 15 | throw CompileError('Cannot use await in a non-async context'); 16 | } else { 17 | break; 18 | } 19 | } 20 | e0 = e0.parent; 21 | } 22 | 23 | final subject = compileExpression(e.expression, ctx); 24 | final type = subject.type.resolveTypeChain(ctx); 25 | 26 | if (!type.isAssignableTo(ctx, CoreTypes.future.ref(ctx))) { 27 | throw CompileError("Cannot await something that isn't a Future"); 28 | } 29 | 30 | var completer = ctx.lookupLocal('#completer'); 31 | 32 | final awaitOp = 33 | Await.make(completer!.scopeFrameOffset, subject.scopeFrameOffset); 34 | ctx.pushOp(awaitOp, Await.LEN); 35 | 36 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 37 | 38 | return Variable.alloc( 39 | ctx, 40 | type.specifiedTypeArgs.isNotEmpty 41 | ? type.specifiedTypeArgs[0] 42 | : CoreTypes.dynamic.ref(ctx)); 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/symbol.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/stdlib/core.dart'; 3 | 4 | /// dart_eval wrapper for [Symbol] 5 | class $Symbol implements $Instance { 6 | static const _$type = BridgeTypeRef(CoreTypes.symbol); 7 | 8 | static const $declaration = BridgeClassDef(BridgeClassType(_$type), 9 | constructors: { 10 | '': BridgeConstructorDef( 11 | BridgeFunctionDef(returns: BridgeTypeAnnotation(_$type), params: [ 12 | BridgeParameter('name', 13 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)), false), 14 | ])) 15 | }, 16 | methods: {}, 17 | getters: {}, 18 | setters: {}, 19 | fields: {}, 20 | wrap: true); 21 | 22 | static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) { 23 | return $Symbol.wrap(Symbol(args[0]!.$value)); 24 | } 25 | 26 | final $Instance _superclass; 27 | 28 | /// Wrap a [Symbol] in a [$Symbol] 29 | $Symbol.wrap(this.$value) : _superclass = $Object($value); 30 | 31 | @override 32 | final Symbol $value; 33 | 34 | @override 35 | Symbol get $reified => $value; 36 | 37 | @override 38 | int $getRuntimeType(Runtime runtime) => runtime.lookupType(CoreTypes.symbol); 39 | 40 | @override 41 | $Value? $getProperty(Runtime runtime, String identifier) { 42 | return _superclass.$getProperty(runtime, identifier); 43 | } 44 | 45 | @override 46 | void $setProperty(Runtime runtime, String identifier, $Value value) { 47 | return _superclass.$setProperty(runtime, identifier, value); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/override.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/compiler/model/override_spec.dart'; 3 | import 'package:dart_eval/stdlib/core.dart'; 4 | import 'package:pub_semver/pub_semver.dart'; 5 | 6 | /// Mapping of runtime overrides, which can be used to dynamically swap 7 | /// implementations of functions at runtime by a unique ID 8 | Map? runtimeOverrides; 9 | 10 | /// The current semver [Version] of your app. Overriden functions can specify 11 | /// a version constraint in their @RuntimeOverride annotation which will be 12 | /// checked against this version to determine if the override should apply. 13 | Version? runtimeOverrideVersion; 14 | 15 | /// The global runtime instance to be used for overrides 16 | Runtime? globalRuntime; 17 | 18 | /// Lookup and execute an overriden function on the [globalRuntime] by its ID 19 | Object? runtimeOverride(String id, [Iterable args = const []]) { 20 | final spec = runtimeOverrides?[id]; 21 | 22 | if (spec == null) { 23 | return null; 24 | } 25 | 26 | if (runtimeOverrideVersion != null && spec.versionConstraint != null) { 27 | if (!VersionConstraint.parse(spec.versionConstraint!) 28 | .allows(runtimeOverrideVersion!)) { 29 | return null; 30 | } 31 | } 32 | 33 | globalRuntime!.args.addAll(args); 34 | final result = globalRuntime!.execute(spec.offset); 35 | if (result == null) { 36 | return $null(); 37 | } 38 | if (result is List && result is! $List) { 39 | return $List.wrap(result).$reified; 40 | } 41 | return result is $Value ? result.$reified : result; 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/eval/bindgen/methods.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element2.dart'; 2 | import 'package:analyzer/dart/element/type.dart'; 3 | import 'package:dart_eval/src/eval/bindgen/context.dart'; 4 | import 'package:dart_eval/src/eval/bindgen/operator.dart'; 5 | import 'package:dart_eval/src/eval/bindgen/parameters.dart'; 6 | import 'package:dart_eval/src/eval/bindgen/permission.dart'; 7 | import 'package:dart_eval/src/eval/bindgen/type.dart'; 8 | 9 | String $methods(BindgenContext ctx, InterfaceElement2 element) { 10 | final methods = { 11 | if (ctx.implicitSupers) 12 | for (var s in element.allSupertypes) 13 | for (final m in s.element3.methods2) m.name3: m, 14 | for (final m in element.methods2) m.name3: m 15 | }; 16 | 17 | return methods.values 18 | .where((method) => !method.isPrivate && !method.isStatic) 19 | .where( 20 | (m) => !(const ['==', 'toString', 'noSuchMethod'].contains(m.name3))) 21 | .map((e) { 22 | final returnsValue = 23 | e.returnType is! VoidType && !e.returnType.isDartCoreNull; 24 | final op = resolveMethodOperator(e.displayName); 25 | return ''' 26 | static const \$Function __${op.name} = \$Function(_${op.name}); 27 | static \$Value? _${op.name}(Runtime runtime, \$Value? target, List<\$Value?> args) { 28 | ${assertMethodPermissions(e)} 29 | final self = target! as \$${element.name3}; 30 | ${returnsValue ? 'final result = ' : ''}${op.format('self.\$value', argumentAccessors(ctx, e.formalParameters))}; 31 | return ${wrapVar(ctx, e.returnType, 'result')}; 32 | }'''; 33 | }).join('\n'); 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/errors.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:analyzer/dart/ast/ast.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | 6 | class CompileError implements Exception { 7 | final String message; 8 | final AstNode? node; 9 | final int? library; 10 | final CompilerContext? context; 11 | 12 | const CompileError(this.message, [this.node, this.library, this.context]); 13 | 14 | CompileError copyWithContext(CompilerContext context) { 15 | return CompileError(message, node, library, context); 16 | } 17 | 18 | @override 19 | String toString() { 20 | if (context != null) { 21 | return _toStringWithContext(context!); 22 | } 23 | return _toString(); 24 | } 25 | 26 | String _toString() { 27 | final src = node?.toSource(); 28 | return 'CompileError: $message at ${src == null ? "unknown" : '"${src.substring(0, min(20, src.length))}${src.length > 20 ? '..."' : '"'}'}'; 29 | } 30 | 31 | String _toStringWithContext(CompilerContext ctx) { 32 | String? library; 33 | for (final entry in ctx.libraryMap.entries) { 34 | if (entry.value == (library ?? ctx.library)) { 35 | library = entry.key; 36 | } 37 | } 38 | return '${_toString()} (file $library)'; 39 | } 40 | } 41 | 42 | class NotReferencableError extends CompileError { 43 | const NotReferencableError(super.message); 44 | 45 | @override 46 | String toString() { 47 | return 'NotReferencableError: $message'; 48 | } 49 | } 50 | 51 | class PrefixError extends CompileError { 52 | const PrefixError({ 53 | AstNode? node, 54 | int? library, 55 | CompilerContext? context, 56 | }) : super("[internal] unexpected prefix", node, library, context); 57 | 58 | @override 59 | String toString() { 60 | return 'PrefixError: $message'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/util/custom_crawler.dart: -------------------------------------------------------------------------------- 1 | import 'package:directed_graph/directed_graph.dart'; 2 | 3 | /// Custom graph crawler with vastly better performance than the original. 4 | class FastCrawler extends GraphCrawler { 5 | FastCrawler(super.edges); 6 | 7 | @override 8 | List> tree(T start, [T? target]) { 9 | final result = >[ 10 | for (final connected in edges(start)) {connected} 11 | ]; 12 | 13 | if (result.isEmpty) return result; 14 | 15 | var startIndexOld = 0; 16 | var startIndexNew = 0; 17 | final visited = {}; 18 | do { 19 | startIndexNew = result.length; 20 | for (var i = startIndexOld; i < startIndexNew; ++i) { 21 | final path = result[i]; 22 | if (visited.contains(path.last)) continue; 23 | visited.add(path.last); 24 | for (final vertex in edges(path.last)) { 25 | // Discard walks which reach the same (inner) vertex twice. 26 | // Each path starts with [start] even though it is not 27 | // listed! 28 | if (path.contains(vertex) || path.contains(start)) { 29 | continue; 30 | } else { 31 | result.add({...path, vertex}); 32 | } 33 | if (vertex == target) break; 34 | } 35 | } 36 | startIndexOld = startIndexNew; 37 | } while (startIndexNew < result.length); 38 | return result; 39 | } 40 | } 41 | 42 | /// Caching version of the above 43 | class CachedFastCrawler extends GraphCrawler { 44 | CachedFastCrawler(super.edges); 45 | 46 | final _cache = >>{}; 47 | 48 | @override 49 | List> tree(T start, [T? target]) { 50 | if (_cache.containsKey(start)) return _cache[start]!; 51 | return _cache[start] = super.tree(start, target); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/serializer.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | 3 | /// A [BridgeDeclarationRegistry] which serializes registered declarations to a 4 | /// JSON object. Serialized declarations can be loaded with the dart_eval CLI. 5 | class BridgeSerializer 6 | implements BridgeDeclarationRegistry, EvalPluginRegistry { 7 | final List _classes = []; 8 | final List _enums = []; 9 | final List _functions = []; 10 | final List _sources = []; 11 | final Map _exportedLibMappings = {}; 12 | 13 | @override 14 | void addPlugin(EvalPlugin plugin) { 15 | plugin.configureForCompile(this); 16 | } 17 | 18 | @override 19 | void addSource(DartSource source) { 20 | _sources.add(source); 21 | } 22 | 23 | @override 24 | void defineBridgeClass(BridgeClassDef classDef) { 25 | _classes.add(classDef); 26 | } 27 | 28 | @override 29 | void defineBridgeEnum(BridgeEnumDef enumDef) { 30 | _enums.add(enumDef); 31 | } 32 | 33 | @override 34 | void defineBridgeTopLevelFunction(BridgeFunctionDeclaration function) { 35 | _functions.add(function); 36 | } 37 | 38 | @override 39 | void addExportedLibraryMapping(String libraryUri, String exportUri) { 40 | _exportedLibMappings[libraryUri] = exportUri; 41 | } 42 | 43 | /// Serialize all declarations to a JSON object. 44 | Map serialize() { 45 | return { 46 | 'classes': _classes.map((e) => e.toJson()).toList(), 47 | 'enums': _enums.map((e) => e.toJson()).toList(), 48 | 'functions': _functions.map((e) => e.toJson()).toList(), 49 | 'sources': _sources 50 | .map((e) => {'uri': e.uri.toString(), 'source': e.toString()}) 51 | .toList(), 52 | 'exportedLibMappings': _exportedLibMappings, 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/declaration/declaration.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: body_might_complete_normally_nullable 2 | 3 | import 'package:analyzer/dart/ast/ast.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/declaration/class.dart'; 6 | import 'package:dart_eval/src/eval/compiler/declaration/constructor.dart'; 7 | import 'package:dart_eval/src/eval/compiler/declaration/enum.dart'; 8 | import 'package:dart_eval/src/eval/compiler/declaration/field.dart'; 9 | import 'package:dart_eval/src/eval/compiler/declaration/function.dart'; 10 | import 'package:dart_eval/src/eval/compiler/declaration/method.dart'; 11 | import 'package:dart_eval/src/eval/compiler/declaration/variable.dart'; 12 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 13 | 14 | int? compileDeclaration(Declaration d, CompilerContext ctx, 15 | {Declaration? parent, int? fieldIndex, List? fields}) { 16 | if (d is ClassDeclaration) { 17 | compileClassDeclaration(ctx, d); 18 | } else if (d is EnumDeclaration) { 19 | compileEnumDeclaration(ctx, d); 20 | } else if (d is MethodDeclaration) { 21 | return compileMethodDeclaration( 22 | d, ctx, parent as NamedCompilationUnitMember); 23 | } else if (d is FunctionDeclaration) { 24 | compileFunctionDeclaration(d, ctx); 25 | } else if (d is FieldDeclaration) { 26 | compileFieldDeclaration( 27 | fieldIndex!, d, ctx, parent as NamedCompilationUnitMember); 28 | } else if (d is ConstructorDeclaration) { 29 | compileConstructorDeclaration( 30 | ctx, d, parent as NamedCompilationUnitMember, fields!); 31 | } else if (d is VariableDeclaration) { 32 | compileTopLevelVariableDeclaration(d, ctx); 33 | } else if (d is EnumConstantDeclaration) { 34 | // do nothing 35 | } else { 36 | throw CompileError('No support for ${d.runtimeType}'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/async.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/shared/stdlib/async/stream.dart'; 3 | import 'package:dart_eval/src/eval/shared/stdlib/async/stream_controller.dart'; 4 | import 'package:dart_eval/src/eval/shared/stdlib/async/timer.dart'; 5 | import 'package:dart_eval/src/eval/shared/stdlib/async/zone.dart'; 6 | import 'async/future.dart'; 7 | 8 | /// [EvalPlugin] for the `dart:async` library 9 | class DartAsyncPlugin implements EvalPlugin { 10 | @override 11 | String get identifier => 'dart:async'; 12 | 13 | @override 14 | void configureForCompile(BridgeDeclarationRegistry registry) { 15 | registry.defineBridgeClass($Completer.$declaration); 16 | registry.defineBridgeClass($StreamSubscription.$declaration); 17 | registry.defineBridgeClass($StreamSink.$declaration); 18 | registry.defineBridgeClass($StreamController.$declaration); 19 | registry.defineBridgeClass($Zone.$declaration); 20 | registry.defineBridgeClass($StreamView.$declaration); 21 | registry.defineBridgeClass($Timer.$declaration); 22 | } 23 | 24 | @override 25 | void configureForRuntime(Runtime runtime) { 26 | runtime.registerBridgeFunc( 27 | 'dart:async', 'Completer.', const $Completer_new().call); 28 | runtime.registerBridgeFunc( 29 | 'dart:async', 'StreamController.', $StreamController.$new); 30 | runtime.registerBridgeFunc('dart:async', 'Zone.current*g', $Zone.$current); 31 | runtime.registerBridgeFunc('dart:async', 'Zone.root*g', $Zone.$root); 32 | runtime.registerBridgeFunc('dart:async', 'StreamView.', $StreamView.$new); 33 | runtime.registerBridgeFunc('dart:async', 'Timer.', $Timer.$new); 34 | runtime.registerBridgeFunc( 35 | 'dart:async', 'Timer.periodic', $Timer.$periodic); 36 | runtime.registerBridgeFunc('dart:async', 'Timer.run', $Timer.$run); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/assignment.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/token.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 6 | import 'package:dart_eval/src/eval/compiler/helpers/invoke.dart'; 7 | import 'package:dart_eval/src/eval/compiler/macros/branch.dart'; 8 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 9 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 10 | 11 | Variable compileAssignmentExpression( 12 | AssignmentExpression e, CompilerContext ctx) { 13 | final L = compileExpressionAsReference(e.leftHandSide, ctx); 14 | final R = 15 | compileExpression(e.rightHandSide, ctx, L.resolveType(ctx, forSet: true)); 16 | 17 | if (e.operator.type == TokenType.EQ) { 18 | final set = 19 | R.type != L.resolveType(ctx, forSet: true) ? R.boxIfNeeded(ctx) : R; 20 | return L.setValue(ctx, set); 21 | } else if (e.operator.type.binaryOperatorOfCompoundAssignment == 22 | TokenType.QUESTION_QUESTION) { 23 | late Variable result; 24 | macroBranch(ctx, null, condition: (ctx) { 25 | return L 26 | .getValue(ctx) 27 | .invoke(ctx, '==', [BuiltinValue().push(ctx)]).result; 28 | }, thenBranch: (ctx, rt) { 29 | result = L.setValue(ctx, R.boxIfNeeded(ctx)); 30 | return StatementInfo(-1); 31 | }); 32 | return result; 33 | } else { 34 | final method = e.operator.type.binaryOperatorOfCompoundAssignment!.lexeme; 35 | final V = L.getValue(ctx); 36 | final res = V.invoke(ctx, method, [R]).result; 37 | final set = res.type != L.resolveType(ctx, forSet: true) 38 | ? res.boxIfNeeded(ctx) 39 | : res; 40 | return L.setValue(ctx, set); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/util/current_dir_overrides.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:path/path.dart' as p; 3 | 4 | /// An [IOOverrides] implementation that safely overrides the current working directory 5 | /// for file system operations at runtime. 6 | /// 7 | /// This class is useful when you need to execute code with a different current directory, 8 | /// especially on platforms like Android and iOS where directly setting [Directory.current] 9 | /// is not recommended or may cause issues. 10 | /// 11 | /// All file, directory, and link operations performed through this override will be 12 | /// resolved relative to the specified [currentDir], ensuring isolation and safety. 13 | /// 14 | /// Example usage: 15 | /// ```dart 16 | /// IOOverrides.runWithOverrides( 17 | /// () { 18 | /// // File operations here will use the overridden current directory. 19 | /// // like: runtime.executeLib('package:example/main.dart', 'main'); 20 | /// }, 21 | /// CurrentDirIOOverrides('/my/custom/path'), 22 | /// ); 23 | /// ``` 24 | base class CurrentDirIOOverrides extends IOOverrides { 25 | final String currentDir; 26 | CurrentDirIOOverrides(this.currentDir); 27 | 28 | @override 29 | File createFile(String path) => 30 | super.createFile(p.normalize(p.join(currentDir, path))); 31 | 32 | @override 33 | Directory createDirectory(String path) => 34 | super.createDirectory(p.normalize(p.join(currentDir, path))); 35 | 36 | @override 37 | Directory getCurrentDirectory() => Directory(currentDir); 38 | 39 | @override 40 | Link createLink(String path) => 41 | super.createLink(p.normalize(p.join(currentDir, path))); 42 | 43 | @override 44 | Future stat(String path) async => 45 | await FileStat.stat(p.normalize(p.join(currentDir, path))); 46 | 47 | @override 48 | FileStat statSync(String path) => 49 | FileStat.statSync(p.normalize(p.join(currentDir, path))); 50 | } 51 | -------------------------------------------------------------------------------- /test/postfix_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/expect.dart'; 3 | import 'package:test/scaffolding.dart'; 4 | 5 | void main() { 6 | group('Postfix', () { 7 | late Compiler compiler; 8 | 9 | setUp(() { 10 | compiler = Compiler(); 11 | }); 12 | 13 | test('i++', () { 14 | final runtime = compiler.compileWriteAndLoad({ 15 | 'eval_test': { 16 | 'main.dart': ''' 17 | void main() { 18 | double d = 0.0; 19 | double di = d++; 20 | print(di); 21 | print(d); 22 | int i = 0; 23 | int ii = i++; 24 | print(ii); 25 | print(i); 26 | List list = [0]; 27 | print(list[0]++); 28 | print(list[0]); 29 | } 30 | ''' 31 | } 32 | }); 33 | 34 | expect( 35 | () { 36 | runtime.executeLib('package:eval_test/main.dart', 'main'); 37 | }, 38 | prints('0.0\n1.0\n0\n1\n0\n1\n'), 39 | ); 40 | }); 41 | 42 | test('i--', () { 43 | final runtime = compiler.compileWriteAndLoad({ 44 | 'eval_test': { 45 | 'main.dart': ''' 46 | void main() { 47 | double d = 1.0; 48 | double di = d--; 49 | print(di); 50 | print(d); 51 | int i = 1; 52 | int ii = i--; 53 | print(ii); 54 | print(i); 55 | List list = [1]; 56 | print(list[0]--); 57 | print(list[0]); 58 | } 59 | ''' 60 | } 61 | }); 62 | 63 | expect( 64 | () { 65 | runtime.executeLib('package:eval_test/main.dart', 'main'); 66 | }, 67 | prints('1.0\n0.0\n1\n0\n1\n0\n'), 68 | ); 69 | }); 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /test/prefix_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/expect.dart'; 3 | import 'package:test/scaffolding.dart'; 4 | 5 | void main() { 6 | group('Prefix', () { 7 | late Compiler compiler; 8 | 9 | setUp(() { 10 | compiler = Compiler(); 11 | }); 12 | 13 | test('++i', () { 14 | final runtime = compiler.compileWriteAndLoad({ 15 | 'eval_test': { 16 | 'main.dart': ''' 17 | void main() { 18 | double d = 0.0; 19 | double di = ++d; 20 | print(di); 21 | print(d); 22 | int i = 0; 23 | int ii = ++i; 24 | print(ii); 25 | print(i); 26 | List list = [0]; 27 | print(++list[0]); 28 | print(list[0]); 29 | } 30 | ''' 31 | } 32 | }); 33 | 34 | expect( 35 | () { 36 | runtime.executeLib('package:eval_test/main.dart', 'main'); 37 | }, 38 | prints('1.0\n1.0\n1\n1\n1\n1\n'), 39 | ); 40 | }); 41 | 42 | test('--i', () { 43 | final runtime = compiler.compileWriteAndLoad({ 44 | 'eval_test': { 45 | 'main.dart': ''' 46 | void main() { 47 | double d = 1.0; 48 | double di = --d; 49 | print(di); 50 | print(d); 51 | int i = 1; 52 | int ii = --i; 53 | print(ii); 54 | print(i); 55 | List list = [1]; 56 | print(--list[0]); 57 | print(list[0]); 58 | } 59 | ''' 60 | } 61 | }); 62 | 63 | expect( 64 | () { 65 | runtime.executeLib('package:eval_test/main.dart', 'main'); 66 | }, 67 | prints('0.0\n0.0\n0\n0\n0\n0\n'), 68 | ); 69 | }); 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/postfix.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/token.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 6 | import 'package:dart_eval/src/eval/compiler/helpers/assert.dart'; 7 | import 'package:dart_eval/src/eval/compiler/helpers/equality.dart'; 8 | import 'package:dart_eval/src/eval/compiler/helpers/invoke.dart'; 9 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 10 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 11 | 12 | Variable compilePostfixExpression(PostfixExpression e, CompilerContext ctx) { 13 | if (e.operator.type == TokenType.BANG) { 14 | // Null assertion (!) 15 | final L = compileExpression(e.operand, ctx); 16 | final result = checkNotNull(ctx, L); 17 | final msg = 18 | BuiltinValue(stringval: 'Null check operator used on a null value') 19 | .push(ctx); 20 | doAssert(ctx, result, msg); 21 | return L.copyWith(type: L.type.copyWith(nullable: false)); 22 | } 23 | 24 | final V = compileExpressionAsReference(e.operand, ctx); 25 | final L = V.getValue(ctx); 26 | var out = L; 27 | 28 | out = Variable.alloc(ctx, L.type); 29 | ctx.pushOp(PushNull.make(), PushNull.LEN); 30 | ctx.pushOp( 31 | CopyValue.make(out.scopeFrameOffset, L.scopeFrameOffset), CopyValue.LEN); 32 | 33 | const opMap = {TokenType.PLUS_PLUS: '+', TokenType.MINUS_MINUS: '-'}; 34 | 35 | if (!opMap.containsKey(e.operator.type)) { 36 | throw UnsupportedError('Unsupported postfix operator ${e.operator}'); 37 | } 38 | 39 | V.setValue( 40 | ctx, 41 | L.invoke( 42 | ctx, 43 | opMap[e.operator.type]!, 44 | [BuiltinValue(intval: 1).push(ctx)], 45 | ).result, 46 | ); 47 | 48 | return out; 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/model/compilation_unit.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | 3 | /// This class represents a parsed Dart source file (AST), but not yet parsed 4 | /// into a library (library). In Dart, every source file is a part of or an 5 | /// entire library, depending on the existence of `part` directives. 6 | /// 7 | /// DartCompilationUnit is an intermediate stage of source code compilation, 8 | /// after the Dart analyzer has parsed the source code into an AST, but before 9 | /// libraries are created from the AST declarations. 10 | class DartCompilationUnit { 11 | DartCompilationUnit(this.uri, 12 | {required this.imports, 13 | required this.exports, 14 | required this.parts, 15 | required this.declarations, 16 | this.library, 17 | this.partOf}); 18 | 19 | /// A `package`, `dart`, or `file` URI identifying the source file, 20 | /// such as 'package:example/main.dart' 21 | final Uri uri; 22 | 23 | /// Library directive for source code that starts with "library *****" 24 | final LibraryDirective? library; 25 | 26 | /// Corresponds to 'part of' syntax, only supports one 27 | final PartOfDirective? partOf; 28 | 29 | /// Package imports 30 | final List imports; 31 | 32 | /// Package exports 33 | final List exports; 34 | 35 | /// `part` syntax 36 | /// Relationship between `part` and `part of`: 37 | /// https://stackoverflow.com/questions/67096135/how-to-use-part-or-part-of-in-dart 38 | final List parts; 39 | 40 | /// Various specific code declarations for eg. classes and functions 41 | final List declarations; 42 | 43 | @override 44 | bool operator ==(Object other) => 45 | identical(this, other) || 46 | other is DartCompilationUnit && 47 | runtimeType == other.runtimeType && 48 | uri == other.uri; 49 | 50 | @override 51 | int get hashCode => uri.hashCode; 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/declaration/variable.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/scope.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 8 | 9 | void compileTopLevelVariableDeclaration( 10 | VariableDeclaration v, CompilerContext ctx) { 11 | final parent = v.parent!.parent! as TopLevelVariableDeclaration; 12 | final varName = v.name.lexeme; 13 | 14 | final initializer = v.initializer; 15 | if (initializer != null) { 16 | final pos = beginMethod(ctx, v, v.offset, '$varName*i'); 17 | var V = compileExpression(initializer, ctx); 18 | TypeRef type; 19 | final specifiedType = parent.variables.type; 20 | if (specifiedType != null) { 21 | type = TypeRef.fromAnnotation(ctx, ctx.library, specifiedType); 22 | if (!V.type.isAssignableTo(ctx, type)) { 23 | throw CompileError( 24 | 'Variable $varName of inferred type ${V.type} does not conform to type $type'); 25 | } 26 | } else { 27 | type = V.type; 28 | } 29 | if (!type.isUnboxedAcrossFunctionBoundaries) { 30 | V = V.boxIfNeeded(ctx); 31 | type = type.copyWith(boxed: true); 32 | } else { 33 | V = V.unboxIfNeeded(ctx); 34 | type = type.copyWith(boxed: false); 35 | } 36 | final index = ctx.topLevelGlobalIndices[ctx.library]![varName]!; 37 | ctx.pushOp(SetGlobal.make(index, V.scopeFrameOffset), SetGlobal.LEN); 38 | ctx.topLevelVariableInferredTypes[ctx.library]![varName] = type; 39 | ctx.topLevelGlobalInitializers[ctx.library]![varName] = pos; 40 | ctx.runtimeGlobalInitializerMap[index] = pos; 41 | ctx.pushOp(Return.make(V.scopeFrameOffset), Return.LEN); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/declaration/class.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/declaration/constructor.dart'; 4 | import 'package:dart_eval/src/eval/compiler/declaration/declaration.dart'; 5 | import 'package:dart_eval/src/eval/compiler/type.dart'; 6 | 7 | void compileClassDeclaration(CompilerContext ctx, ClassDeclaration d, 8 | {bool statics = false}) { 9 | final $runtimeType = 10 | ctx.typeRefIndexMap[TypeRef.lookupDeclaration(ctx, ctx.library, d)]; 11 | final clsName = d.name.lexeme; 12 | ctx.instanceDeclarationPositions[ctx.library]![clsName] = [ 13 | {}, 14 | {}, 15 | {}, 16 | $runtimeType 17 | ]; 18 | ctx.instanceGetterIndices[ctx.library]![clsName] = {}; 19 | final constructors = []; 20 | final fields = []; 21 | final methods = []; 22 | for (final m in d.members) { 23 | if (m is ConstructorDeclaration) { 24 | constructors.add(m); 25 | } else if (m is FieldDeclaration) { 26 | if (!m.isStatic) { 27 | fields.add(m); 28 | } 29 | } else { 30 | m as MethodDeclaration; 31 | methods.add(m); 32 | } 33 | } 34 | var i = 0; 35 | if (constructors.isEmpty) { 36 | ctx.resetStack(position: 0); 37 | ctx.currentClass = d; 38 | compileDefaultConstructor(ctx, d, fields); 39 | } 40 | for (final m in [...fields, ...methods, ...constructors]) { 41 | ctx.resetStack( 42 | position: m is ConstructorDeclaration || 43 | (m is MethodDeclaration && m.isStatic) 44 | ? 0 45 | : 1); 46 | ctx.currentClass = d; 47 | compileDeclaration(m, ctx, parent: d, fieldIndex: i, fields: fields); 48 | if (m is FieldDeclaration) { 49 | i += m.fields.variables.length; 50 | } 51 | } 52 | ctx.currentClass = null; 53 | ctx.resetStack(); 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/conditional.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/macros/branch.dart'; 6 | import 'package:dart_eval/src/eval/compiler/reference.dart'; 7 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 8 | import 'package:dart_eval/src/eval/compiler/type.dart'; 9 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 10 | 11 | import '../errors.dart'; 12 | import 'expression.dart'; 13 | 14 | /// Compile a [ConditionalExpression] to EVC bytecode 15 | Variable compileConditionalExpression( 16 | CompilerContext ctx, ConditionalExpression e, 17 | [TypeRef? boundType]) { 18 | ctx.setLocal('#conditional', BuiltinValue().push(ctx)); 19 | final vRef = IdentifierReference(null, '#conditional'); 20 | final types = {if (boundType != null) boundType}; 21 | 22 | macroBranch( 23 | ctx, boundType == null ? null : AlwaysReturnType(boundType, false), 24 | condition: (ctx) { 25 | var c = compileExpression(e.condition, ctx); 26 | if (!c.type.isAssignableTo(ctx, CoreTypes.bool.ref(ctx))) { 27 | throw CompileError('Condition must be a boolean'); 28 | } 29 | 30 | return c; 31 | }, thenBranch: (ctx, rt) { 32 | final v = compileExpression(e.thenExpression, ctx, boundType); 33 | types.add(v.type); 34 | vRef.setValue(ctx, v); 35 | return StatementInfo(-1); 36 | }, elseBranch: (ctx, rt) { 37 | final v = compileExpression(e.elseExpression, ctx, boundType); 38 | types.add(v.type); 39 | vRef.setValue(ctx, v); 40 | return StatementInfo(-1); 41 | }, resolveStateToThen: true, source: e); 42 | 43 | final val = vRef.getValue(ctx).updated(ctx); 44 | return val.copyWith( 45 | type: TypeRef.commonBaseType(ctx, types).copyWith(boxed: val.boxed)); 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/declaration/enum.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'enum.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | BridgeEnumDef _$BridgeEnumDefFromJson(Map json) => 10 | BridgeEnumDef( 11 | BridgeTypeRef.fromJson(json['type'] as Map), 12 | values: (json['values'] as List?) 13 | ?.map((e) => e as String) 14 | .toList() ?? 15 | const [], 16 | methods: (json['methods'] as Map?)?.map( 17 | (k, e) => MapEntry( 18 | k, BridgeMethodDef.fromJson(e as Map)), 19 | ) ?? 20 | const {}, 21 | getters: (json['getters'] as Map?)?.map( 22 | (k, e) => MapEntry( 23 | k, BridgeMethodDef.fromJson(e as Map)), 24 | ) ?? 25 | const {}, 26 | setters: (json['setters'] as Map?)?.map( 27 | (k, e) => MapEntry( 28 | k, BridgeMethodDef.fromJson(e as Map)), 29 | ) ?? 30 | const {}, 31 | fields: (json['fields'] as Map?)?.map( 32 | (k, e) => 33 | MapEntry(k, BridgeFieldDef.fromJson(e as Map)), 34 | ) ?? 35 | const {}, 36 | ); 37 | 38 | Map _$BridgeEnumDefToJson(BridgeEnumDef instance) => 39 | { 40 | 'type': instance.type.toJson(), 41 | 'values': instance.values, 42 | 'methods': instance.methods.map((k, e) => MapEntry(k, e.toJson())), 43 | 'getters': instance.getters.map((k, e) => MapEntry(k, e.toJson())), 44 | 'setters': instance.setters.map((k, e) => MapEntry(k, e.toJson())), 45 | 'fields': instance.fields.map((k, e) => MapEntry(k, e.toJson())), 46 | }; 47 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/as.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 6 | import 'package:dart_eval/src/eval/compiler/type.dart'; 7 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 8 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 9 | import 'package:dart_eval/src/eval/shared/types.dart'; 10 | 11 | Variable compileAsExpression(AsExpression e, CompilerContext ctx) { 12 | var V = compileExpression(e.expression, ctx); 13 | final slot = TypeRef.fromAnnotation(ctx, ctx.library, e.type); 14 | 15 | /// If the type is the slot, we can just return 16 | if (V.type == slot) { 17 | return V; 18 | } 19 | 20 | // Special case: if casting null to a nullable type, allow it 21 | if (V.type == CoreTypes.nullType.ref(ctx) && slot.nullable) { 22 | return V.copyWithUpdate(ctx, type: slot); 23 | } 24 | 25 | // Otherwise type-test 26 | ctx.pushOp(IsType.make(V.scopeFrameOffset, ctx.typeRefIndexMap[slot]!, false), 27 | IsType.length); 28 | final vIs = 29 | Variable.alloc(ctx, CoreTypes.bool.ref(ctx).copyWith(boxed: false)); 30 | 31 | // And assert 32 | final errMsg = 33 | BuiltinValue(stringval: "TypeError: Not a subtype of type ${slot.name}") 34 | .push(ctx); 35 | ctx.pushOp( 36 | Assert.make(vIs.scopeFrameOffset, errMsg.scopeFrameOffset), Assert.LEN); 37 | 38 | // If the type changes between num and int/double, unbox/box 39 | if (slot == CoreTypes.num.ref(ctx)) { 40 | V = V.boxIfNeeded(ctx); 41 | } else if (slot == CoreTypes.int.ref(ctx) || 42 | slot == CoreTypes.double.ref(ctx)) { 43 | V = V.unboxIfNeeded(ctx); 44 | } 45 | 46 | // For all other types, just inform the compiler 47 | // (todo) Mixins may need different behavior 48 | return V.copyWithUpdate(ctx, type: slot.copyWith(boxed: V.type.boxed)); 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/io/io_sink.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dart_eval/dart_eval_bridge.dart'; 4 | import 'package:dart_eval/src/eval/shared/stdlib/async/stream.dart'; 5 | import 'package:dart_eval/src/eval/shared/stdlib/io/string_sink.dart'; 6 | 7 | /// dart_eval wrapper for [IOSink] 8 | class $IOSink implements $Instance { 9 | $IOSink.wrap(this.$value); 10 | 11 | /// Compile-time bridged type reference for [$IOSink] 12 | static const $type = BridgeTypeRef(IoTypes.ioSink); 13 | 14 | /// Compile-time bridged class declaration for [$IOSink] 15 | static const $declaration = BridgeClassDef( 16 | BridgeClassType($type, isAbstract: true, $implements: [ 17 | $StringSink.$type, 18 | BridgeTypeRef(AsyncTypes.streamSink, [ 19 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.list, 20 | [BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.int))])) 21 | ]) 22 | ]), 23 | constructors: { 24 | '': BridgeConstructorDef(BridgeFunctionDef( 25 | returns: BridgeTypeAnnotation($type), params: [], namedParams: [])) 26 | }, 27 | methods: {}, 28 | getters: {}, 29 | setters: {}, 30 | fields: {}, 31 | wrap: true); 32 | 33 | late final $Instance _stringSink = $StringSink.wrap($value); 34 | 35 | late final $Instance _streamSink = $StreamSink.wrap($value); 36 | 37 | @override 38 | final IOSink $value; 39 | 40 | @override 41 | get $reified => $value; 42 | 43 | @override 44 | int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!); 45 | 46 | @override 47 | $Value? $getProperty(Runtime runtime, String identifier) { 48 | switch (identifier) { 49 | case 'close': 50 | case 'done': 51 | return _streamSink.$getProperty(runtime, identifier); 52 | } 53 | return _stringSink.$getProperty(runtime, identifier); 54 | } 55 | 56 | @override 57 | void $setProperty(Runtime runtime, String identifier, $Value value) { 58 | return _stringSink.$setProperty(runtime, identifier, value); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/not_equal_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | 4 | void main() { 5 | test('not equal for int', () async { 6 | final source = ''' 7 | bool fn(){ 8 | int x = 1; 9 | if(x != 2){ 10 | return true; 11 | } 12 | return false; 13 | } 14 | '''; 15 | final compiler = Compiler(); 16 | final program = compiler.compile({ 17 | 'my_package': { 18 | 'main.dart': source, 19 | } 20 | }); 21 | var runtime = Runtime.ofProgram(program); 22 | var result = runtime.executeLib( 23 | "package:my_package/main.dart", 24 | "fn", 25 | ); 26 | assert(result); 27 | }); 28 | test('not equal for string', () async { 29 | final source = ''' 30 | bool fn(){ 31 | String x = "a"; 32 | if(x != "axxx"){ 33 | return true; 34 | } 35 | return false; 36 | } 37 | '''; 38 | final compiler = Compiler(); 39 | final program = compiler.compile({ 40 | 'my_package': { 41 | 'main.dart': source, 42 | } 43 | }); 44 | var runtime = Runtime.ofProgram(program); 45 | var result = runtime.executeLib( 46 | "package:my_package/main.dart", 47 | "fn", 48 | ); 49 | assert(result); 50 | }); 51 | 52 | test('not equal for bool', () async { 53 | final source = ''' 54 | bool fn(){ 55 | bool x = !false; 56 | if(x != true){ 57 | return false; 58 | } 59 | if(!x){ 60 | return false; 61 | } 62 | if(!x != false){ 63 | return false; 64 | } 65 | return true; 66 | } 67 | '''; 68 | final compiler = Compiler(); 69 | final program = compiler.compile({ 70 | 'my_package': { 71 | 'main.dart': source, 72 | } 73 | }); 74 | var runtime = Runtime.ofProgram(program); 75 | var result = runtime.executeLib( 76 | "package:my_package/main.dart", 77 | "fn", 78 | ); 79 | assert(result); 80 | }); 81 | } 82 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/property_access.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/token.dart'; 3 | import 'package:dart_eval/dart_eval_bridge.dart'; 4 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 5 | import 'package:dart_eval/src/eval/compiler/helpers/equality.dart'; 6 | import 'package:dart_eval/src/eval/compiler/macros/branch.dart'; 7 | import 'package:dart_eval/src/eval/compiler/reference.dart'; 8 | import 'package:dart_eval/src/eval/compiler/context.dart'; 9 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 10 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 11 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 12 | import 'package:dart_eval/src/eval/compiler/type.dart'; 13 | 14 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 15 | 16 | Variable compilePropertyAccess(PropertyAccess pa, CompilerContext ctx, 17 | {Variable? cascadeTarget}) { 18 | final L = cascadeTarget ?? compileExpression(pa.realTarget, ctx); 19 | 20 | if (pa.operator.type == TokenType.QUESTION_PERIOD) { 21 | var out = BuiltinValue().push(ctx).boxIfNeeded(ctx); 22 | if (L.concreteTypes.length == 1 && 23 | L.concreteTypes[0] == CoreTypes.nullType.ref(ctx)) { 24 | return out; 25 | } 26 | macroBranch(ctx, null, condition: (ctx) { 27 | return checkNotEqual(ctx, L, out); 28 | }, thenBranch: (ctx, rt) { 29 | final V = L.getProperty(ctx, pa.propertyName.name).boxIfNeeded(ctx); 30 | out = out.copyWith(type: V.type.copyWith(nullable: true)); 31 | ctx.pushOp(CopyValue.make(out.scopeFrameOffset, V.scopeFrameOffset), 32 | CopyValue.LEN); 33 | return StatementInfo(-1); 34 | }, source: pa); 35 | return out; 36 | } 37 | 38 | return L.getProperty(ctx, pa.propertyName.name); 39 | } 40 | 41 | Reference compilePropertyAccessAsReference( 42 | PropertyAccess pa, CompilerContext ctx, 43 | {Variable? cascadeTarget}) { 44 | final L = cascadeTarget ?? compileExpression(pa.realTarget, ctx); 45 | return IdentifierReference(L, pa.propertyName.name); 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/eval/eval.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dart_eval/dart_eval.dart'; 4 | import 'package:dart_eval/dart_eval_bridge.dart'; 5 | import 'package:dart_eval/dart_eval_security.dart'; 6 | 7 | /// Evaluate the Dart [source] code. If the source is a raw expression such as 8 | /// "2 + 2" it will be evaluated directly and the result will be returned; 9 | /// otherwise, the function [function] will be called with arguments specified 10 | /// by [args]. You can use [plugins] to configure bridge classes and 11 | /// [permissions] to grant permissions to the runtime. 12 | /// You can also specify [outputFile] to write the generated EVC bytecode to a 13 | /// file. 14 | /// 15 | /// The eval() function automatically unboxes return values for convenience. 16 | dynamic eval(String source, 17 | {String function = 'main', 18 | List args = const [], 19 | List plugins = const [], 20 | List permissions = const [], 21 | String? outputFile}) { 22 | final compiler = Compiler(); 23 | for (final plugin in plugins) { 24 | plugin.configureForCompile(compiler); 25 | } 26 | 27 | var source0 = source; 28 | 29 | if (!RegExp(r'(?:\w* )?' + function + r'\s?\([\s\S]*?\)\s?({|=>)') 30 | .hasMatch(source0)) { 31 | if (!source0.contains(';')) { 32 | source0 = '$source0;'; 33 | if (!source0.contains('return')) { 34 | source0 = 'return $source0'; 35 | } 36 | } 37 | source0 = 'dynamic $function() {$source0}'; 38 | } 39 | 40 | final program = compiler.compile({ 41 | 'default': {'main.dart': source0} 42 | }); 43 | 44 | if (outputFile != null) { 45 | File(outputFile).writeAsBytesSync(program.write()); 46 | } 47 | 48 | final runtime = Runtime.ofProgram(program); 49 | for (final plugin in plugins) { 50 | plugin.configureForRuntime(runtime); 51 | } 52 | 53 | for (final permission in permissions) { 54 | runtime.grant(permission); 55 | } 56 | 57 | runtime.args = args; 58 | final result = runtime.executeLib('package:default/main.dart', function); 59 | 60 | if (result is $Value) { 61 | return result.$reified; 62 | } 63 | return result; 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/security/permissions/process.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_security.dart'; 2 | 3 | /// A permission that allows access to execute a process on the host system. 4 | class ProcessRunPermission implements Permission { 5 | /// The pattern that will be matched against the path. 6 | final Pattern matchPattern; 7 | 8 | /// Create a new process run permission that matches a [Pattern]. 9 | const ProcessRunPermission(this.matchPattern); 10 | 11 | /// A permission that allows access to run any process. 12 | static final ProcessRunPermission any = ProcessRunPermission(RegExp('.*')); 13 | 14 | /// Create a new process run permission that matches a specific executable name 15 | /// in any directory. 16 | factory ProcessRunPermission.namedExecutable(String executable) { 17 | return ProcessRunPermission(RegExp('$executable\$')); 18 | } 19 | 20 | @override 21 | List get domains => ['process:run']; 22 | 23 | @override 24 | bool match([Object? data]) { 25 | if (data is String) { 26 | return matchPattern.allMatches(data).isNotEmpty; 27 | } 28 | return false; 29 | } 30 | 31 | @override 32 | bool operator ==(Object other) { 33 | if (other is ProcessRunPermission) { 34 | return other.matchPattern == matchPattern && other.domains == domains; 35 | } 36 | return false; 37 | } 38 | 39 | @override 40 | int get hashCode => matchPattern.hashCode ^ domains.hashCode; 41 | } 42 | 43 | /// A permission that allows access to kill a process by its PID. 44 | class ProcessKillPermisssion implements Permission { 45 | /// The PID of the process to kill, or null to allow killing any process. 46 | final int? pid; 47 | 48 | const ProcessKillPermisssion([this.pid]); 49 | 50 | @override 51 | List get domains => ['process:kill']; 52 | 53 | @override 54 | bool match([Object? data]) { 55 | if (data is int) { 56 | return pid == null || data == pid; 57 | } 58 | return false; 59 | } 60 | 61 | @override 62 | bool operator ==(Object other) { 63 | return other is ProcessKillPermisssion && other.pid == pid; 64 | } 65 | 66 | @override 67 | int get hashCode => pid.hashCode; 68 | } 69 | -------------------------------------------------------------------------------- /test/tearoff_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Function tests', () { 6 | late Compiler compiler; 7 | 8 | setUp(() { 9 | compiler = Compiler(); 10 | }); 11 | 12 | test('Simple tearoff', () { 13 | final runtime = compiler.compileWriteAndLoad({ 14 | 'example': { 15 | 'main.dart': ''' 16 | int main () { 17 | var fn = fun; 18 | return fn(4); 19 | } 20 | 21 | int fun(int a) { 22 | return a + 1; 23 | } 24 | ''' 25 | } 26 | }); 27 | 28 | expect(runtime.executeLib('package:example/main.dart', 'main'), 5); 29 | }); 30 | 31 | test('Tearoff as argument', () { 32 | final runtime = compiler.compileWriteAndLoad({ 33 | 'example': { 34 | 'main.dart': ''' 35 | int main () { 36 | return fun(4, fun2); 37 | } 38 | 39 | int fun(int a, Function fn) { 40 | return fn(a) + 1; 41 | } 42 | 43 | int fun2(int a) { 44 | return a + 2; 45 | } 46 | ''' 47 | } 48 | }); 49 | 50 | expect(runtime.executeLib('package:example/main.dart', 'main'), 7); 51 | }); 52 | 53 | test('Method tearoffs', () { 54 | final runtime = compiler.compileWriteAndLoad({ 55 | 'example': { 56 | 'main.dart': ''' 57 | int main () { 58 | return M(4).run(); 59 | } 60 | 61 | class M { 62 | M(this.x); 63 | 64 | final int x; 65 | 66 | int run() { 67 | return load(x, add); 68 | } 69 | 70 | int load(int y, Function op) { 71 | return op(y) + 1; 72 | } 73 | 74 | int add(int y) { 75 | return y + 5; 76 | } 77 | } 78 | 79 | ''' 80 | } 81 | }); 82 | 83 | expect(runtime.executeLib('package:example/main.dart', 'main'), 10); 84 | }); 85 | }); 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/iterator.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/stdlib/core.dart'; 3 | 4 | class $Iterator implements Iterator, $Instance { 5 | static const $declaration = BridgeClassDef( 6 | BridgeClassType(BridgeTypeRef(CoreTypes.iterator), 7 | generics: {'E': BridgeGenericParam()}), 8 | constructors: {}, 9 | methods: { 10 | 'moveNext': BridgeMethodDef(BridgeFunctionDef( 11 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.bool)))) 12 | }, 13 | getters: { 14 | 'current': BridgeMethodDef(BridgeFunctionDef( 15 | returns: BridgeTypeAnnotation(BridgeTypeRef.ref('E')))) 16 | }, 17 | setters: {}, 18 | fields: {}, 19 | wrap: true); 20 | $Iterator(String id, Iterator value) 21 | : $value = runtimeOverride(id) as Iterator? ?? value; 22 | 23 | $Iterator.wrap(this.$value); 24 | 25 | @override 26 | final Iterator $value; 27 | 28 | @override 29 | Iterator get $reified { 30 | // iterate through the iterator and map to $value 31 | final values = []; 32 | while ($value.moveNext()) { 33 | values.add(($value.current as $Value).$value); 34 | } 35 | return values.iterator; 36 | } 37 | 38 | late final $Instance _superclass = $Object($value); 39 | 40 | @override 41 | $Value? $getProperty(Runtime runtime, String identifier) { 42 | switch (identifier) { 43 | case 'moveNext': 44 | return __moveNext; 45 | case 'current': 46 | return $value.current as $Value?; 47 | default: 48 | return _superclass.$getProperty(runtime, identifier); 49 | } 50 | } 51 | 52 | static const $Function __moveNext = $Function(_moveNext); 53 | 54 | static $Value? _moveNext( 55 | Runtime runtime, $Value? target, List<$Value?> args) { 56 | return $bool((target!.$value as Iterator).moveNext()); 57 | } 58 | 59 | @override 60 | void $setProperty(Runtime runtime, String identifier, $Value value) {} 61 | 62 | @override 63 | E get current => $value.current; 64 | 65 | @override 66 | bool moveNext() => $value.moveNext(); 67 | 68 | @override 69 | int $getRuntimeType(Runtime runtime) => 70 | runtime.lookupType(CoreTypes.iterator); 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/security/permissions/network.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_security.dart'; 2 | 3 | /// A permission that allows access to a network resource. 4 | class NetworkPermission implements Permission { 5 | /// The pattern that will be matched against the URL. 6 | final Pattern matchPattern; 7 | 8 | /// Create a new network permission that matches a [Pattern]. 9 | const NetworkPermission(this.matchPattern); 10 | 11 | /// Create a new network permission that matches a [String] URL. The URL 12 | /// can exclude the scheme, host, path, query, or fragment, in which case 13 | /// the permission will match any value for that part of the URL. If more 14 | /// customization is needed, use the default constructor to specify a 15 | /// RegExp [Pattern] directly. 16 | factory NetworkPermission.url(String url) { 17 | final uri = Uri.parse(url); 18 | final schemePattern = 19 | uri.scheme == '' ? r'[-a-zA-Z0-9@:%._\+~#=]{0,256}' : uri.scheme; 20 | final hostPattern = 21 | uri.host == '' ? r'[-a-zA-Z0-9@:%._\+~#=]{1,256}' : uri.host; 22 | final pathPattern = 23 | uri.path == '' ? r'[-a-zA-Z0-9@:%_\+.~&//=]*' : uri.path; 24 | final queryPattern = 25 | uri.query == '' ? r'[-a-zA-Z0-9@:%_\+.~?&//=]*' : uri.query; 26 | final fragmentPattern = 27 | uri.fragment == '' ? r'[-a-zA-Z0-9@:%_\+.~#?&//=]*' : uri.fragment; 28 | final pattern = 29 | '^$schemePattern:?\\/*$hostPattern\\/?$pathPattern\\??$queryPattern\\#?$fragmentPattern\$'; 30 | return NetworkPermission(RegExp(pattern)); 31 | } 32 | 33 | /// A permission that allows access to any network resource. 34 | static final NetworkPermission any = NetworkPermission(RegExp('.*')); 35 | 36 | @override 37 | List get domains => ['network']; 38 | 39 | @override 40 | bool match([Object? data]) { 41 | if (data is String) { 42 | return matchPattern.matchAsPrefix(data) != null; 43 | } 44 | return false; 45 | } 46 | 47 | @override 48 | bool operator ==(Object other) { 49 | if (other is NetworkPermission) { 50 | return other.matchPattern == matchPattern && other.domains == domains; 51 | } 52 | return false; 53 | } 54 | 55 | @override 56 | int get hashCode => matchPattern.hashCode ^ domains.hashCode; 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/comparable.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/shared/stdlib/core/num.dart'; 3 | import 'package:dart_eval/src/eval/shared/stdlib/core/object.dart'; 4 | 5 | /// Wrapper for [Comparable] 6 | class $Comparable implements Comparable, $Instance { 7 | static const $declaration = BridgeClassDef( 8 | BridgeClassType(BridgeTypeRef(CoreTypes.comparable), 9 | isAbstract: true, generics: {'T': BridgeGenericParam()}), 10 | constructors: {}, 11 | methods: { 12 | 'compareTo': BridgeMethodDef(BridgeFunctionDef( 13 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.int)), 14 | params: [ 15 | BridgeParameter( 16 | 'other', BridgeTypeAnnotation(BridgeTypeRef.ref('T')), false) 17 | ])) 18 | }, 19 | wrap: true); 20 | 21 | /// Wrap a [Comparable] in a [$Comparable]. 22 | $Comparable.wrap(this.$value) : _superclass = $Object($value); 23 | 24 | @override 25 | final Comparable $value; 26 | 27 | @override 28 | Comparable get $reified => $value; 29 | 30 | final $Instance _superclass; 31 | 32 | @override 33 | $Value? $getProperty(Runtime runtime, String identifier) { 34 | switch (identifier) { 35 | case 'compareTo': 36 | return __compareTo; 37 | default: 38 | return _superclass.$getProperty(runtime, identifier); 39 | } 40 | } 41 | 42 | @override 43 | void $setProperty(Runtime runtime, String identifier, $Value value) {} 44 | 45 | @override 46 | int $getRuntimeType(Runtime runtime) => 47 | runtime.lookupType(CoreTypes.comparable); 48 | 49 | static const $Function __compareTo = $Function(_compareTo); 50 | 51 | static $Value? _compareTo( 52 | Runtime runtime, $Value? target, List<$Value?> args) { 53 | final other = args[0]; 54 | final evalResult = target!.$value.compareTo(other!.$value); 55 | 56 | if (evalResult is int) { 57 | return $int(evalResult); 58 | } 59 | 60 | return null; 61 | } 62 | 63 | @override 64 | int compareTo(T other) => $value.compareTo(other); 65 | 66 | @override 67 | bool operator ==(Object other) => $value == other; 68 | 69 | @override 70 | int get hashCode => $value.hashCode; 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/util/library_graph.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/compiler/model/compilation_unit.dart'; 2 | import 'package:dart_eval/src/eval/compiler/model/library.dart'; 3 | import 'package:dart_eval/src/eval/compiler/util/graph.dart'; 4 | 5 | /// A [Graph] where strongly-connected components representing libraries can be 6 | /// formed from part/part of relationships 7 | class CompilationUnitGraph implements Graph { 8 | final Map compilationUnits; 9 | final Map uriMap; 10 | final Map libraryIdMap; 11 | 12 | CompilationUnitGraph(this.compilationUnits, this.uriMap, this.libraryIdMap); 13 | 14 | @override 15 | Iterable get vertices => compilationUnits.keys; 16 | 17 | @override 18 | Iterable neighborsOf(int vertex) sync* { 19 | final cu = compilationUnits[vertex]; 20 | if (cu == null) { 21 | throw 'Compilation unit not found: $vertex'; 22 | } 23 | 24 | for (final part in cu.parts) { 25 | final id = uriMap[part.uri.stringValue!]; 26 | if (id != null && compilationUnits.containsKey(id)) { 27 | yield id; 28 | } 29 | } 30 | 31 | final partOf = cu.partOf; 32 | if (partOf != null) { 33 | final uriStr = partOf.uri?.stringValue, libId = partOf.libraryName?.name; 34 | if (uriStr != null) { 35 | final id = uriMap[uriStr]; 36 | if (id != null && compilationUnits.containsKey(id)) { 37 | yield id; 38 | } 39 | } else { 40 | final id = libraryIdMap[libId]; 41 | if (id != null && compilationUnits.containsKey(id)) { 42 | yield id; 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | class ExportGraph implements Graph { 50 | final Map libraries; 51 | 52 | ExportGraph(this.libraries); 53 | 54 | @override 55 | Iterable get vertices => libraries.keys; 56 | 57 | @override 58 | Iterable neighborsOf(Uri vertex) sync* { 59 | final library = libraries[vertex]; 60 | if (library == null) { 61 | throw 'Library not found: $vertex'; 62 | } 63 | 64 | for (final export in library.exports) { 65 | final lib = Uri.parse(export.uri.stringValue!); 66 | if (libraries.containsKey(lib)) { 67 | yield lib; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/type.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | 3 | class RuntimeType { 4 | const RuntimeType(this.type, this.typeArgs); 5 | final int type; 6 | final List typeArgs; 7 | 8 | factory RuntimeType.fromJson(List json) { 9 | return RuntimeType( 10 | json[0], [for (final ta in json[1]) RuntimeType.fromJson(ta)]); 11 | } 12 | 13 | List toJson() { 14 | return [ 15 | type, 16 | [for (final ta in typeArgs) ta.toJson()] 17 | ]; 18 | } 19 | 20 | @override 21 | bool operator ==(Object other) => 22 | identical(this, other) || 23 | other is RuntimeType && 24 | runtimeType == other.runtimeType && 25 | type == other.type && 26 | typeArgs == other.typeArgs; 27 | 28 | @override 29 | int get hashCode => type.hashCode ^ typeArgs.hashCode; 30 | } 31 | 32 | /// Represents a type and all of the interfaces it conforms to 33 | class RuntimeTypeSet { 34 | const RuntimeTypeSet(this.rt, this.types, this.typeArgs); 35 | 36 | static const _equality = DeepCollectionEquality(); 37 | 38 | factory RuntimeTypeSet.fromJson(List json) { 39 | return RuntimeTypeSet(json[0], Set.from(json[1]), 40 | [for (final a in json[2]) RuntimeTypeSet.fromJson(a)]); 41 | } 42 | 43 | final int rt; 44 | final Set types; 45 | final List typeArgs; 46 | 47 | bool isAssignableTo(RuntimeType type) { 48 | final ta = typeArgs; 49 | final tta = type.typeArgs; 50 | final len = ta.length; 51 | if (len != tta.length) { 52 | return false; 53 | } 54 | for (var i = 0; i < len; i++) { 55 | if (!ta[i].isAssignableTo(tta[i])) { 56 | return false; 57 | } 58 | } 59 | return types.contains(type.type); 60 | } 61 | 62 | List toJson() => [ 63 | rt, 64 | types.toList(), 65 | [for (final a in typeArgs) a.toJson()] 66 | ]; 67 | 68 | @override 69 | bool operator ==(Object other) => 70 | identical(this, other) || 71 | other is RuntimeTypeSet && 72 | runtimeType == other.runtimeType && 73 | rt == other.rt && 74 | types == other.types && 75 | DeepCollectionEquality().equals(typeArgs, other.typeArgs); 76 | 77 | @override 78 | int get hashCode => 79 | rt.hashCode ^ _equality.hash(types) ^ _equality.hash(typeArgs); 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/literal.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/collection/list.dart'; 3 | import 'package:dart_eval/src/eval/compiler/collection/set_map.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/adjacent_strings.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/record.dart'; 6 | import 'package:dart_eval/src/eval/compiler/expression/string_interpolation.dart'; 7 | import 'package:dart_eval/src/eval/compiler/expression/symbol.dart'; 8 | import 'package:dart_eval/src/eval/compiler/type.dart'; 9 | import 'package:dart_eval/src/eval/shared/types.dart'; 10 | 11 | import '../builtins.dart'; 12 | import '../context.dart'; 13 | import '../errors.dart'; 14 | import '../variable.dart'; 15 | 16 | BuiltinValue parseConstLiteral(Literal l, CompilerContext ctx, 17 | [TypeRef? bound]) { 18 | if (l is IntegerLiteral) { 19 | if (bound != null && bound == CoreTypes.double.ref(ctx)) { 20 | return BuiltinValue(doubleval: l.value!.toDouble()); 21 | } 22 | return BuiltinValue(intval: l.value); 23 | } else if (l is DoubleLiteral) { 24 | return BuiltinValue(doubleval: l.value); 25 | } else if (l is SimpleStringLiteral) { 26 | return BuiltinValue(stringval: l.stringValue); 27 | } else if (l is BooleanLiteral) { 28 | return BuiltinValue(boolval: l.value); 29 | } else if (l is NullLiteral) { 30 | return BuiltinValue(); 31 | } 32 | throw CompileError('Unknown constant literal type ${l.runtimeType}'); 33 | } 34 | 35 | Variable parseLiteral(Literal l, CompilerContext ctx, [TypeRef? bound]) { 36 | if (l is IntegerLiteral || 37 | l is DoubleLiteral || 38 | l is SimpleStringLiteral || 39 | l is NullLiteral || 40 | l is BooleanLiteral) { 41 | return parseConstLiteral(l, ctx, bound).push(ctx); 42 | } 43 | if (l is ListLiteral) { 44 | return compileListLiteral(l, ctx); 45 | } 46 | if (l is SetOrMapLiteral) { 47 | return compileSetOrMapLiteral(l, ctx); 48 | } 49 | if (l is StringInterpolation) { 50 | return compileStringInterpolation(ctx, l); 51 | } 52 | if (l is AdjacentStrings) { 53 | return compileAdjacentStrings(ctx, l); 54 | } 55 | if (l is SymbolLiteral) { 56 | return compileSymbolLiteral(l, ctx); 57 | } 58 | if (l is RecordLiteral) { 59 | return compileRecordLiteral(l, ctx); 60 | } 61 | throw CompileError('Unknown literal type ${l.runtimeType}'); 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/async/zone.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:dart_eval/dart_eval_bridge.dart'; 4 | import 'package:dart_eval/stdlib/core.dart'; 5 | 6 | /// dart_eval wrapper for [Zone] 7 | class $Zone implements $Instance { 8 | static const _$type = BridgeTypeRef(AsyncTypes.zone); 9 | 10 | static const $declaration = BridgeClassDef(BridgeClassType(_$type), 11 | constructors: {}, 12 | methods: { 13 | '[]': BridgeMethodDef(BridgeFunctionDef( 14 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.dynamic)), 15 | params: [ 16 | BridgeParameter( 17 | 'key', 18 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.object), 19 | nullable: true), 20 | false) 21 | ], 22 | )), 23 | }, 24 | getters: { 25 | 'current': BridgeMethodDef( 26 | BridgeFunctionDef(returns: BridgeTypeAnnotation(_$type)), 27 | isStatic: true), 28 | 'root': BridgeMethodDef( 29 | BridgeFunctionDef(returns: BridgeTypeAnnotation(_$type)), 30 | isStatic: true), 31 | }, 32 | setters: {}, 33 | fields: {}, 34 | wrap: true); 35 | 36 | static $Value? $current(Runtime runtime, $Value? target, List<$Value?> args) { 37 | return $Zone.wrap(Zone.current[1]); 38 | } 39 | 40 | static $Value? $root(Runtime runtime, $Value? target, List<$Value?> args) { 41 | return $Zone.wrap(Zone.root); 42 | } 43 | 44 | final $Instance _superclass; 45 | 46 | /// Wrap a [Zone] in a [$Zone] 47 | $Zone.wrap(this.$value) : _superclass = $Object($value); 48 | 49 | @override 50 | final Zone $value; 51 | 52 | @override 53 | Zone get $reified => $value; 54 | 55 | @override 56 | int $getRuntimeType(Runtime runtime) => throw UnimplementedError(); 57 | 58 | @override 59 | $Value? $getProperty(Runtime runtime, String identifier) { 60 | switch (identifier) { 61 | case '[]': 62 | return __$index; 63 | } 64 | return _superclass.$getProperty(runtime, identifier); 65 | } 66 | 67 | static const $Function __$index = $Function(_$index); 68 | 69 | static $Value? _$index(Runtime runtime, $Value? target, List<$Value?> args) { 70 | return runtime.wrap((target!.$value as Zone)[args[0]?.$value]); 71 | } 72 | 73 | @override 74 | void $setProperty(Runtime runtime, String identifier, $Value value) { 75 | return _superclass.$setProperty(runtime, identifier, value); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/type.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/stdlib/core.dart'; 3 | 4 | /// dart_eval [$Value] representation of [Type] 5 | class $Type implements $Instance, Type { 6 | $Type(this.$value) : _superclass = $Object($value); 7 | 8 | static const $declaration = BridgeClassDef( 9 | BridgeClassType(BridgeTypeRef(CoreTypes.type), isAbstract: true), 10 | constructors: {}, 11 | wrap: true); 12 | 13 | final $Instance _superclass; 14 | 15 | @override 16 | final Type $value; 17 | 18 | @override 19 | Type get $reified => $value; 20 | 21 | @override 22 | int $getRuntimeType(Runtime runtime) => runtime.lookupType(CoreTypes.type); 23 | 24 | @override 25 | bool operator ==(Object other) => other is $Type && $value == other.$value; 26 | 27 | @override 28 | int get hashCode => -12121212; 29 | 30 | @override 31 | $Value? $getProperty(Runtime runtime, String identifier) { 32 | return _superclass.$getProperty(runtime, identifier); 33 | } 34 | 35 | @override 36 | void $setProperty(Runtime runtime, String identifier, $Value value) { 37 | return _superclass.$setProperty(runtime, identifier, value); 38 | } 39 | } 40 | 41 | class $TypeImpl implements $Type { 42 | $TypeImpl(this._typeId) : _superclass = $Object(_typeId); 43 | 44 | final int _typeId; 45 | 46 | @override 47 | final $Instance _superclass; 48 | 49 | @override 50 | Type get $value => throw UnimplementedError(); 51 | 52 | @override 53 | Type get $reified => throw UnimplementedError(); 54 | 55 | @override 56 | int $getRuntimeType(Runtime runtime) => runtime.lookupType(CoreTypes.type); 57 | 58 | @override 59 | bool operator ==(Object other) => 60 | other is $TypeImpl && other._typeId == _typeId; 61 | 62 | @override 63 | int get hashCode => _typeId; 64 | 65 | @override 66 | $Value? $getProperty(Runtime runtime, String identifier) { 67 | switch (identifier) { 68 | case 'toString': 69 | return $Function( 70 | ((runtime, target, args) => $String("Instance of 'Type'"))); 71 | case '==': 72 | return $Function((runtime, target, args) { 73 | final other = args[0]; 74 | return $bool(other is $TypeImpl && other._typeId == _typeId); 75 | }); 76 | case 'hashCode': 77 | return $int(_typeId); 78 | } 79 | return _superclass.$getProperty(runtime, identifier); 80 | } 81 | 82 | @override 83 | void $setProperty(Runtime runtime, String identifier, $Value value) { 84 | return _superclass.$setProperty(runtime, identifier, value); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/eval/runtime/declaration.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/runtime/class.dart'; 3 | 4 | /// A class is an instance of [Type] 5 | class EvalClass extends $InstanceImpl { 6 | EvalClass(this.delegatedType, this.superclass, this.mixins, this.getters, 7 | this.setters, this.methods) 8 | : super(EvalClassClass.instance, null, const []); 9 | 10 | factory EvalClass.fromJson(List def) { 11 | return EvalClass(def[3] as int, null, [], (def[0] as Map).cast(), 12 | (def[1] as Map).cast(), (def[2] as Map).cast()); 13 | } 14 | 15 | final int delegatedType; 16 | 17 | @override 18 | int $getRuntimeType(Runtime runtime) => runtime.lookupType(CoreTypes.type); 19 | 20 | @override 21 | // ignore: overridden_fields 22 | final List values = []; 23 | 24 | final EvalClass? superclass; 25 | final List mixins; 26 | 27 | final Map getters; 28 | final Map setters; 29 | final Map methods; 30 | } 31 | 32 | class EvalClassClass implements EvalClass { 33 | static final instance = EvalClassClass(); 34 | 35 | @override 36 | int $getRuntimeType(Runtime runtime) => throw UnimplementedError(); 37 | 38 | @override 39 | $Value? $getProperty(Runtime runtime, String identifier) { 40 | throw UnimplementedError(); 41 | } 42 | 43 | @override 44 | void $setProperty(Runtime runtime, String identifier, $Value value) { 45 | throw UnimplementedError(); 46 | } 47 | 48 | @override 49 | $Instance? get evalSuperclass => throw UnimplementedError(); 50 | 51 | @override 52 | Map get getters => throw UnimplementedError(); 53 | 54 | @override 55 | Map get methods => throw UnimplementedError(); 56 | 57 | @override 58 | List get mixins => throw UnimplementedError(); 59 | 60 | @override 61 | Never get $reified => throw UnimplementedError(); 62 | 63 | @override 64 | Map get setters => throw UnimplementedError(); 65 | 66 | @override 67 | EvalClass? get superclass => throw UnimplementedError(); 68 | 69 | @override 70 | List get values => throw UnimplementedError(); 71 | 72 | @override 73 | set values(List values) => throw UnimplementedError(); 74 | 75 | @override 76 | Never get evalClass => throw UnimplementedError(); 77 | 78 | @override 79 | Never get $value => throw UnimplementedError(); 80 | 81 | @override 82 | int get delegatedType => throw UnimplementedError(); 83 | 84 | @override 85 | $Value? getCoreObjectProperty(String identifier) { 86 | throw UnimplementedError(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/declaration/function.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'function.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | BridgeParameter _$BridgeParameterFromJson(Map json) => 10 | BridgeParameter( 11 | json['name'] as String, 12 | BridgeTypeAnnotation.fromJson(json['type'] as Map), 13 | json['optional'] as bool, 14 | ); 15 | 16 | Map _$BridgeParameterToJson(BridgeParameter instance) => 17 | { 18 | 'name': instance.name, 19 | 'type': instance.type.toJson(), 20 | 'optional': instance.optional, 21 | }; 22 | 23 | BridgeFunctionDef _$BridgeFunctionDefFromJson(Map json) => 24 | BridgeFunctionDef( 25 | returns: BridgeTypeAnnotation.fromJson( 26 | json['returns'] as Map), 27 | params: (json['params'] as List?) 28 | ?.map((e) => BridgeParameter.fromJson(e as Map)) 29 | .toList() ?? 30 | const [], 31 | namedParams: (json['namedParams'] as List?) 32 | ?.map((e) => BridgeParameter.fromJson(e as Map)) 33 | .toList() ?? 34 | const [], 35 | generics: (json['generics'] as Map?)?.map( 36 | (k, e) => MapEntry( 37 | k, BridgeGenericParam.fromJson(e as Map)), 38 | ) ?? 39 | const {}, 40 | ); 41 | 42 | Map _$BridgeFunctionDefToJson(BridgeFunctionDef instance) => 43 | { 44 | 'returns': instance.returns.toJson(), 45 | 'generics': instance.generics.map((k, e) => MapEntry(k, e.toJson())), 46 | 'params': instance.params.map((e) => e.toJson()).toList(), 47 | 'namedParams': instance.namedParams.map((e) => e.toJson()).toList(), 48 | }; 49 | 50 | BridgeFunctionDeclaration _$BridgeFunctionDeclarationFromJson( 51 | Map json) => 52 | BridgeFunctionDeclaration( 53 | json['library'] as String, 54 | json['name'] as String, 55 | BridgeFunctionDef.fromJson(json['function'] as Map), 56 | ); 57 | 58 | Map _$BridgeFunctionDeclarationToJson( 59 | BridgeFunctionDeclaration instance) => 60 | { 61 | 'function': instance.function.toJson(), 62 | 'library': instance.library, 63 | 'name': instance.name, 64 | }; 65 | -------------------------------------------------------------------------------- /test/records_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Records', () { 6 | late Compiler compiler; 7 | 8 | setUp(() { 9 | compiler = Compiler(); 10 | }); 11 | 12 | test('Create and access records', () { 13 | final runtime = compiler.compileWriteAndLoad({ 14 | 'eval_test': { 15 | 'main.dart': r''' 16 | void main() { 17 | var numbers = (0, 1); 18 | print(numbers.$1); 19 | print(numbers.$2); 20 | } 21 | ''' 22 | } 23 | }); 24 | 25 | expect( 26 | () { 27 | runtime.executeLib('package:eval_test/main.dart', 'main'); 28 | }, 29 | prints('0\n1\n'), 30 | ); 31 | }); 32 | 33 | test('Returning record from function', () { 34 | final runtime = compiler.compileWriteAndLoad({ 35 | 'eval_test': { 36 | 'main.dart': r''' 37 | (int, int) add(int a, int b) { 38 | return (a + 1, b + 1); 39 | } 40 | 41 | void main() { 42 | var result = add(1, 2); 43 | print(result.$1); 44 | print(result.$2); 45 | } 46 | ''' 47 | } 48 | }); 49 | expect( 50 | () { 51 | runtime.executeLib('package:eval_test/main.dart', 'main'); 52 | }, 53 | prints('2\n3\n'), 54 | ); 55 | }); 56 | 57 | test('Record with named fields', () { 58 | final runtime = compiler.compileWriteAndLoad({ 59 | 'eval_test': { 60 | 'main.dart': r''' 61 | void main() { 62 | var person = (name: 'Alice', age: 30); 63 | print(person.name); 64 | print(person.age); 65 | } 66 | ''' 67 | } 68 | }); 69 | expect( 70 | () { 71 | runtime.executeLib('package:eval_test/main.dart', 'main'); 72 | }, 73 | prints('Alice\n30\n'), 74 | ); 75 | }); 76 | 77 | test('Record with mixed fields', () { 78 | final runtime = compiler.compileWriteAndLoad({ 79 | 'eval_test': { 80 | 'main.dart': r''' 81 | void main() { 82 | var mixed = (1, name: 'Bob', 3.5); 83 | print(mixed.$1); 84 | print(mixed.name); 85 | print(mixed.$2); 86 | } 87 | ''' 88 | } 89 | }); 90 | expect( 91 | () { 92 | runtime.executeLib('package:eval_test/main.dart', 'main'); 93 | }, 94 | prints('1\nBob\n3.5\n'), 95 | ); 96 | }); 97 | }); 98 | } 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # idea 2 | .idea 3 | 4 | # Files and directories created by pub 5 | .dart_tool/ 6 | .packages 7 | 8 | # Omit commiting pubspec.lock for library packages: 9 | # https://dart.dev/guides/libraries/private-files#pubspeclock 10 | pubspec.lock 11 | 12 | # Conventional directory for build outputs 13 | build/ 14 | 15 | # Directory created by dartdoc 16 | doc/api/ 17 | 18 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 19 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 20 | 21 | # User-specific stuff 22 | .idea/**/workspace.xml 23 | .idea/**/tasks.xml 24 | .idea/**/usage.statistics.xml 25 | .idea/**/dictionaries 26 | .idea/**/shelf 27 | 28 | # AWS User-specific 29 | .idea/**/aws.xml 30 | 31 | # Generated files 32 | .idea/**/contentModel.xml 33 | 34 | # Sensitive or high-churn files 35 | .idea/**/dataSources/ 36 | .idea/**/dataSources.ids 37 | .idea/**/dataSources.local.xml 38 | .idea/**/sqlDataSources.xml 39 | .idea/**/dynamic.xml 40 | .idea/**/uiDesigner.xml 41 | .idea/**/dbnavigator.xml 42 | 43 | # Gradle 44 | .idea/**/gradle.xml 45 | .idea/**/libraries 46 | 47 | # Gradle and Maven with auto-import 48 | # When using Gradle or Maven with auto-import, you should exclude module files, 49 | # since they will be recreated, and may cause churn. Uncomment if using 50 | # auto-import. 51 | # .idea/artifacts 52 | # .idea/compiler.xml 53 | # .idea/jarRepositories.xml 54 | # .idea/modules.xml 55 | # .idea/*.iml 56 | # .idea/modules 57 | # *.iml 58 | # *.ipr 59 | 60 | # CMake 61 | cmake-build-*/ 62 | 63 | # Mongo Explorer plugin 64 | .idea/**/mongoSettings.xml 65 | 66 | # File-based project format 67 | *.iws 68 | 69 | # IntelliJ 70 | out/ 71 | 72 | # mpeltonen/sbt-idea plugin 73 | .idea_modules/ 74 | 75 | # JIRA plugin 76 | atlassian-ide-plugin.xml 77 | 78 | # Cursive Clojure plugin 79 | .idea/replstate.xml 80 | 81 | # SonarLint plugin 82 | .idea/sonarlint/ 83 | 84 | # Crashlytics plugin (for Android Studio and IntelliJ) 85 | com_crashlytics_export_strings.xml 86 | crashlytics.properties 87 | crashlytics-build.properties 88 | fabric.properties 89 | 90 | # Editor-based Rest Client 91 | .idea/httpRequests 92 | 93 | # Android studio 3.1+ serialized cache file 94 | .idea/caches/build_file_checksums.ser 95 | 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | !.vscode/*.code-snippets 102 | 103 | # Local History for Visual Studio Code 104 | .history/ 105 | 106 | # Built Visual Studio Code Extensions 107 | *.vsix 108 | 109 | # Executables 110 | *.exe -------------------------------------------------------------------------------- /test/operator_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval.dart'; 2 | import 'package:dart_eval/stdlib/core.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | void main() { 6 | group('Operator method tests', () { 7 | late Compiler compiler; 8 | 9 | setUp(() { 10 | compiler = Compiler(); 11 | }); 12 | 13 | test('Operator ==', () { 14 | final runtime = compiler.compileWriteAndLoad({ 15 | 'operator_test': { 16 | 'main.dart': ''' 17 | class MyClass { 18 | final int value; 19 | 20 | MyClass(this.value); 21 | 22 | @override 23 | bool operator==(Object other) => other is MyClass && other.value == value; 24 | } 25 | 26 | List main() { 27 | final cls = MyClass(1); 28 | return [ 29 | cls == MyClass(2), 30 | cls == null, 31 | cls == MyClass(1), 32 | cls == cls, 33 | ]; 34 | } 35 | ''' 36 | } 37 | }); 38 | 39 | expect(runtime.executeLib('package:operator_test/main.dart', 'main'), [ 40 | $bool(false), $bool(false), $bool(true), $bool(true), 41 | ]); 42 | }, skip: true); 43 | 44 | test('Operator has object context', () { 45 | final runtime = compiler.compileWriteAndLoad({ 46 | 'operator_test': { 47 | 'main.dart': ''' 48 | class MyClass { 49 | final int value = 1; 50 | int operator+(int add) => value + add; 51 | } 52 | int main() => MyClass() + 1; 53 | ''' 54 | } 55 | }); 56 | 57 | expect(runtime.executeLib('package:operator_test/main.dart', 'main'), 4); 58 | }, skip: true); 59 | 60 | test('Operator []', () { 61 | final runtime = compiler.compileWriteAndLoad({ 62 | 'operator_test': { 63 | 'main.dart': ''' 64 | class MyClass { 65 | final value = [1, 2]; 66 | 67 | MyClass(); 68 | 69 | int operator[](int index) => value[index]; 70 | 71 | void operator[]=(int index, int value) { 72 | this.value[index] = value; 73 | } 74 | } 75 | 76 | List main() { 77 | final cls = MyClass(); 78 | cls[0] = 3; 79 | return [cls[0], cls[1]]; 80 | } 81 | ''' 82 | } 83 | }); 84 | 85 | expect(runtime.executeLib('package:operator_test/main.dart', 'main'), [ 86 | $int(1), $int(2), 87 | ]); 88 | }, skip: true); 89 | }); 90 | } -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/io.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/shared/stdlib/io/directory.dart'; 3 | import 'package:dart_eval/src/eval/shared/stdlib/io/file.dart'; 4 | import 'package:dart_eval/src/eval/shared/stdlib/io/file_system_entity.dart'; 5 | import 'package:dart_eval/src/eval/shared/stdlib/io/http.dart'; 6 | import 'package:dart_eval/src/eval/shared/stdlib/io/http_status.dart'; 7 | import 'package:dart_eval/src/eval/shared/stdlib/io/io_sink.dart'; 8 | import 'package:dart_eval/src/eval/shared/stdlib/io/process.dart'; 9 | import 'package:dart_eval/src/eval/shared/stdlib/io/socket.dart'; 10 | import 'package:dart_eval/src/eval/shared/stdlib/io/string_sink.dart'; 11 | 12 | /// [EvalPlugin] for the `dart:io` library 13 | class DartIoPlugin implements EvalPlugin { 14 | @override 15 | String get identifier => 'dart:io'; 16 | 17 | @override 18 | void configureForCompile(BridgeDeclarationRegistry registry) { 19 | registry.defineBridgeClass($StringSink.$declaration); 20 | registry.defineBridgeClass($IOSink.$declaration); 21 | registry.defineBridgeClass($HttpClient.$declaration); 22 | registry.defineBridgeClass($HttpClientRequest.$declaration); 23 | registry.defineBridgeClass($HttpClientResponse.$declaration); 24 | registry.defineBridgeClass($FileSystemEntity.$declaration); 25 | registry.defineBridgeClass($File.$declaration); 26 | registry.defineBridgeClass($Directory.$declaration); 27 | registry.defineBridgeClass($Process.$declaration); 28 | registry.defineBridgeClass($ProcessInfo.$declaration); 29 | registry.defineBridgeClass($ProcessResult.$declaration); 30 | registry.defineBridgeClass($ProcessSignal.$declaration); 31 | registry.defineBridgeClass($ProcessStartMode.$declaration); 32 | $InternetAddress.configureForCompile(registry); 33 | $InternetAddressType.configureForCompile(registry); 34 | registry.addSource($HttpStatusSource()); 35 | registry.addSource(DartSource('dart:io', ''' 36 | library dart.io; 37 | export 'dart:io/http_status.dart'; 38 | ''')); 39 | } 40 | 41 | @override 42 | void configureForRuntime(Runtime runtime) { 43 | runtime.registerBridgeFunc('dart:io', 'HttpClient.', $HttpClient.$new); 44 | runtime.registerBridgeFunc('dart:io', 'File.', $File.$new); 45 | runtime.registerBridgeFunc('dart:io', 'Directory.', $Directory.$new); 46 | $InternetAddress.configureForRuntime(runtime); 47 | $InternetAddressType.configureForRuntime(runtime); 48 | $Process.configureForRuntime(runtime); 49 | $ProcessInfo.configureForRuntime(runtime); 50 | $ProcessResult.configureForRuntime(runtime); 51 | $ProcessSignal.configureForRuntime(runtime); 52 | $ProcessStartMode.configureForRuntime(runtime); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/declaration/type.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'type.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | BridgeTypeAnnotation _$BridgeTypeAnnotationFromJson( 10 | Map json) => 11 | BridgeTypeAnnotation( 12 | BridgeTypeRef.fromJson(json['type'] as Map), 13 | nullable: json['nullable'] as bool? ?? false, 14 | ); 15 | 16 | Map _$BridgeTypeAnnotationToJson( 17 | BridgeTypeAnnotation instance) => 18 | { 19 | 'type': instance.type.toJson(), 20 | 'nullable': instance.nullable, 21 | }; 22 | 23 | BridgeClassType _$BridgeClassTypeFromJson(Map json) => 24 | BridgeClassType( 25 | BridgeTypeRef.fromJson(json['type'] as Map), 26 | $extends: json[r'$extends'] == null 27 | ? const BridgeTypeRef(CoreTypes.object, []) 28 | : BridgeTypeRef.fromJson(json[r'$extends'] as Map), 29 | $implements: (json[r'$implements'] as List?) 30 | ?.map((e) => BridgeTypeRef.fromJson(e as Map)) 31 | .toList() ?? 32 | const [], 33 | $with: (json[r'$with'] as List?) 34 | ?.map((e) => BridgeTypeRef.fromJson(e as Map)) 35 | .toList() ?? 36 | const [], 37 | isAbstract: json['isAbstract'] as bool? ?? false, 38 | generics: (json['generics'] as Map?)?.map( 39 | (k, e) => MapEntry( 40 | k, BridgeGenericParam.fromJson(e as Map)), 41 | ) ?? 42 | const {}, 43 | ); 44 | 45 | Map _$BridgeClassTypeToJson(BridgeClassType instance) => 46 | { 47 | 'type': instance.type.toJson(), 48 | 'isAbstract': instance.isAbstract, 49 | r'$extends': instance.$extends?.toJson(), 50 | r'$implements': instance.$implements.map((e) => e.toJson()).toList(), 51 | r'$with': instance.$with.map((e) => e.toJson()).toList(), 52 | 'generics': instance.generics.map((k, e) => MapEntry(k, e.toJson())), 53 | }; 54 | 55 | BridgeTypeSpec _$BridgeTypeSpecFromJson(Map json) => 56 | BridgeTypeSpec( 57 | json['library'] as String, 58 | json['name'] as String, 59 | ); 60 | 61 | Map _$BridgeTypeSpecToJson(BridgeTypeSpec instance) => 62 | { 63 | 'library': instance.library, 64 | 'name': instance.name, 65 | }; 66 | -------------------------------------------------------------------------------- /lib/src/eval/bindgen/operator.dart: -------------------------------------------------------------------------------- 1 | abstract class OperatorMethod { 2 | String get name; 3 | String format(String obj, List args); 4 | } 5 | 6 | class FunctionOperator implements OperatorMethod { 7 | @override 8 | final String name; 9 | 10 | const FunctionOperator(this.name); 11 | 12 | @override 13 | String format(String obj, List args) { 14 | return '$obj.$name(${args.join(', ')})'; 15 | } 16 | } 17 | 18 | class BinaryOperator implements OperatorMethod { 19 | final String op; 20 | 21 | @override 22 | final String name; 23 | 24 | const BinaryOperator(this.op, this.name); 25 | 26 | @override 27 | String format(String obj, List args) { 28 | return '($obj $op ${args[0]})'; 29 | } 30 | } 31 | 32 | class UnaryOperator implements OperatorMethod { 33 | final String op; 34 | 35 | @override 36 | final String name; 37 | 38 | const UnaryOperator(this.op, this.name); 39 | 40 | @override 41 | String format(String obj, List args) { 42 | return '$op$obj'; 43 | } 44 | } 45 | 46 | class IndexGetOperator implements OperatorMethod { 47 | @override 48 | final String name; 49 | 50 | const IndexGetOperator(this.name); 51 | 52 | @override 53 | String format(String obj, List args) { 54 | return '$obj[${args[0]}]'; 55 | } 56 | } 57 | 58 | class IndexSetOperator implements OperatorMethod { 59 | @override 60 | final String name; 61 | 62 | const IndexSetOperator(this.name); 63 | 64 | @override 65 | String format(String obj, List args) { 66 | return '$obj[${args[0]}] = ${args[1]}'; 67 | } 68 | } 69 | 70 | OperatorMethod resolveMethodOperator(String name) => 71 | kOperatorNames[name] ?? FunctionOperator(name); 72 | 73 | // https://dart.dev/language/methods#operators 74 | final kOperatorNames = { 75 | '<': BinaryOperator('<', 'operatorLt'), 76 | '>': BinaryOperator('>', 'operatorGt'), 77 | '<=': BinaryOperator('<=', 'operatorLte'), 78 | '>=': BinaryOperator('>=', 'operatorGte'), 79 | '==': BinaryOperator('==', 'operatorEq'), 80 | '~': UnaryOperator('~', 'operatorBitNot'), 81 | '-': BinaryOperator('-', 'operatorMinus'), 82 | '+': BinaryOperator('+', 'operatorPlus'), 83 | '/': BinaryOperator('/', 'operatorDiv'), 84 | '~/': BinaryOperator('~/', 'operatorIntDiv'), 85 | '*': BinaryOperator('*', 'operatorMul'), 86 | '%': BinaryOperator('%', 'operatorMod'), 87 | '|': BinaryOperator('|', 'operatorBitOr'), 88 | '^': BinaryOperator('^', 'operatorBitXor'), 89 | '&': BinaryOperator('&', 'operatorBitAnd'), 90 | '<<': BinaryOperator('<<', 'operatorShl'), 91 | '>>': BinaryOperator('>>', 'operatorShr'), 92 | '>>>': BinaryOperator('>>>', 'operatorUshr'), 93 | '[]=': IndexSetOperator('operatorIndexSet'), 94 | '[]': IndexGetOperator('operatorIndexGet'), 95 | }; 96 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/convert.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/shared/stdlib/convert/base64.dart'; 3 | import 'package:dart_eval/src/eval/shared/stdlib/convert/byte_conversion.dart'; 4 | import 'package:dart_eval/src/eval/shared/stdlib/convert/chunked_conversion.dart'; 5 | import 'package:dart_eval/src/eval/shared/stdlib/convert/codec.dart'; 6 | import 'package:dart_eval/src/eval/shared/stdlib/convert/converter.dart'; 7 | import 'package:dart_eval/src/eval/shared/stdlib/convert/encoding.dart'; 8 | import 'package:dart_eval/src/eval/shared/stdlib/convert/json.dart'; 9 | import 'package:dart_eval/src/eval/shared/stdlib/convert/utf.dart'; 10 | 11 | const convertSource = ''' 12 | final utf8 = Utf8Codec(); 13 | final json = JsonCodec(); 14 | final Base64Codec base64Url = Base64Codec.urlSafe(); 15 | final base64 = Base64Codec(); 16 | '''; 17 | 18 | /// [EvalPlugin] for the `dart:convert` library 19 | class DartConvertPlugin implements EvalPlugin { 20 | @override 21 | String get identifier => 'dart:convert'; 22 | 23 | @override 24 | void configureForCompile(BridgeDeclarationRegistry registry) { 25 | registry.defineBridgeClass($Converter.$declaration); 26 | registry.defineBridgeClass($Codec.$declaration); 27 | registry.defineBridgeClass($Encoding.$declaration); 28 | registry.defineBridgeClass($Utf8Decoder.$declaration); 29 | registry.defineBridgeClass($Utf8Codec.$declaration); 30 | registry.defineBridgeClass($Base64Encoder.$declaration); 31 | registry.defineBridgeClass($Base64Decoder.$declaration); 32 | registry.defineBridgeClass($Base64Codec.$declaration); 33 | registry.defineBridgeClass($JsonDecoder.$declaration); 34 | registry.defineBridgeClass($JsonEncoder.$declaration); 35 | registry.defineBridgeClass($JsonCodec.$declaration); 36 | registry.defineBridgeClass($ChunkedConversionSink.$declaration); 37 | registry.defineBridgeClass($ByteConversionSink.$declaration); 38 | registry.addSource(DartSource('dart:convert', convertSource)); 39 | $JsonEncodeAndDecode.configureForCompile(registry); 40 | } 41 | 42 | @override 43 | void configureForRuntime(Runtime runtime) { 44 | runtime.registerBridgeFunc( 45 | 'dart:convert', 'Utf8Decoder.', $Utf8Decoder.$new); 46 | runtime.registerBridgeFunc('dart:convert', 'Utf8Codec.', $Utf8Codec.$new); 47 | $Base64Codec.configureForRuntime(runtime); 48 | runtime.registerBridgeFunc( 49 | 'dart:convert', 'JsonDecoder.', $JsonDecoder.$new); 50 | runtime.registerBridgeFunc( 51 | 'dart:convert', 'JsonEncoder.', $JsonEncoder.$new); 52 | runtime.registerBridgeFunc('dart:convert', 'JsonCodec.', $JsonCodec.$new); 53 | $JsonEncodeAndDecode.configureForRuntime(runtime); 54 | $ByteConversionSink.configureForRuntime(runtime); 55 | $ChunkedConversionSink.configureForRuntime(runtime); 56 | $Encoding.configureForRuntime(runtime); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/statement/variable_declaration.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 6 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 7 | 8 | import '../errors.dart'; 9 | import '../type.dart'; 10 | import '../variable.dart'; 11 | import 'statement.dart'; 12 | 13 | StatementInfo compileVariableDeclarationStatement( 14 | VariableDeclarationStatement s, CompilerContext ctx) { 15 | compileVariableDeclarationList(s.variables, ctx); 16 | return StatementInfo(-1); 17 | } 18 | 19 | void compileVariableDeclarationList( 20 | VariableDeclarationList l, CompilerContext ctx) { 21 | TypeRef? type; 22 | if (l.type != null) { 23 | type = TypeRef.fromAnnotation(ctx, ctx.library, l.type!); 24 | } 25 | 26 | for (final li in l.variables) { 27 | if (ctx.locals.last.containsKey(li.name.lexeme)) { 28 | throw CompileError('Cannot declare variable ${li.name.lexeme}' 29 | ' multiple times in the same scope'); 30 | } 31 | final init = li.initializer; 32 | 33 | if (init != null) { 34 | var res = compileExpression(init, ctx, type); 35 | if (type != null && 36 | !res.type.resolveTypeChain(ctx).isAssignableTo(ctx, type)) { 37 | throw CompileError( 38 | 'Type mismatch: variable "${li.name.lexeme} is specified' 39 | ' as type $type, but is initialized to an incompatible value of type ${res.type}'); 40 | } 41 | if (!((type ?? res.type).isUnboxedAcrossFunctionBoundaries)) { 42 | res = res.boxIfNeeded(ctx); 43 | } 44 | if (res.name != null) { 45 | final type0 = type ?? res.type; 46 | var v = Variable.alloc( 47 | ctx, 48 | type0.isUnboxedAcrossFunctionBoundaries 49 | ? type0.copyWith(boxed: false) 50 | : type0, 51 | ); 52 | ctx.pushOp(PushNull.make(), PushNull.LEN); 53 | ctx.pushOp(CopyValue.make(v.scopeFrameOffset, res.scopeFrameOffset), 54 | CopyValue.LEN); 55 | ctx.setLocal(li.name.lexeme, v); 56 | } else { 57 | ctx.setLocal( 58 | li.name.lexeme, 59 | Variable(res.scopeFrameOffset, 60 | (type ?? res.type).copyWith(boxed: res.boxed), 61 | isFinal: l.isFinal || l.isConst, 62 | methodOffset: res.methodOffset, 63 | methodReturnType: res.methodReturnType, 64 | callingConvention: res.callingConvention)); 65 | } 66 | } else { 67 | ctx.setLocal( 68 | li.name.lexeme, 69 | BuiltinValue() 70 | .push(ctx) 71 | .boxIfNeeded(ctx) 72 | .copyWith(type: type ?? CoreTypes.dynamic.ref(ctx))); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/declaration/function.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | part 'function.g.dart'; 5 | 6 | /// Describes a parameter of a bridged function. 7 | @JsonSerializable(explicitToJson: true) 8 | class BridgeParameter { 9 | const BridgeParameter(this.name, this.type, this.optional); 10 | 11 | /// The name of the parameter. 12 | final String name; 13 | 14 | /// The type of the parameter. 15 | final BridgeTypeAnnotation type; 16 | 17 | /// Whether the parameter is optional 18 | final bool optional; 19 | 20 | /// Connect the generated [_$BridgeParameterFromJson] function to the `fromJson` 21 | /// factory. 22 | factory BridgeParameter.fromJson(Map json) => 23 | _$BridgeParameterFromJson(json); 24 | 25 | /// Connect the generated [_$BridgeParameterToJson] function to the `toJson` method. 26 | Map toJson() => _$BridgeParameterToJson(this); 27 | } 28 | 29 | /// A bridged function definition. 30 | @JsonSerializable(explicitToJson: true) 31 | class BridgeFunctionDef { 32 | const BridgeFunctionDef( 33 | {required this.returns, 34 | this.params = const [], 35 | this.namedParams = const [], 36 | this.generics = const {}}); 37 | 38 | /// The return type of the function. 39 | final BridgeTypeAnnotation returns; 40 | 41 | /// The generic type parameters of the function. 42 | final Map generics; 43 | 44 | /// The positional parameters of the function. 45 | final List params; 46 | 47 | /// The named parameters of the function. 48 | final List namedParams; 49 | 50 | /// Connect the generated [_$BridgeFunctionDescriptorFromJson] function to the `fromJson` 51 | /// factory. 52 | factory BridgeFunctionDef.fromJson(Map json) => 53 | _$BridgeFunctionDefFromJson(json); 54 | 55 | /// Connect the generated [_$BridgeFunctionDescriptorToJson] function to the `toJson` method. 56 | Map toJson() => _$BridgeFunctionDefToJson(this); 57 | } 58 | 59 | /// Represents a bridged function declaration. 60 | @JsonSerializable(explicitToJson: true) 61 | class BridgeFunctionDeclaration implements BridgeDeclaration { 62 | const BridgeFunctionDeclaration(this.library, this.name, this.function); 63 | 64 | /// The function definition. 65 | final BridgeFunctionDef function; 66 | 67 | /// The library name. 68 | final String library; 69 | 70 | /// The function name. 71 | final String name; 72 | 73 | /// Connect the generated [_$BridgeFunctionDeclarationFromJson] function to the `fromJson` 74 | /// factory. 75 | factory BridgeFunctionDeclaration.fromJson(Map json) => 76 | _$BridgeFunctionDeclarationFromJson(json); 77 | 78 | /// Connect the generated [_$BridgeFunctionDeclarationToJson] function to the `toJson` method. 79 | Map toJson() => _$BridgeFunctionDeclarationToJson(this); 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/prefix.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/token.dart'; 3 | import 'package:dart_eval/dart_eval_bridge.dart'; 4 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 5 | import 'package:dart_eval/src/eval/compiler/context.dart'; 6 | import 'package:dart_eval/src/eval/compiler/helpers/invoke.dart'; 7 | import 'package:dart_eval/src/eval/compiler/reference.dart'; 8 | import 'package:dart_eval/src/eval/compiler/type.dart'; 9 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 10 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 11 | 12 | import '../errors.dart'; 13 | import 'expression.dart'; 14 | 15 | const _opMap = { 16 | TokenType.MINUS: '-', 17 | TokenType.BANG: '!', 18 | TokenType.PLUS_PLUS: '+', 19 | TokenType.MINUS_MINUS: '-', 20 | }; 21 | 22 | /// Compile a [PrefixExpression] to EVC bytecode 23 | Variable compilePrefixExpression(CompilerContext ctx, PrefixExpression e) { 24 | final method = _opMap[e.operator.type] ?? 25 | (throw CompileError('Unknown unary operator ${e.operator.type}')); 26 | 27 | if ([TokenType.PLUS_PLUS, TokenType.MINUS_MINUS].contains(e.operator.type)) { 28 | final V = compileExpressionAsReference(e.operand, ctx); 29 | final L = V.getValue(ctx); 30 | return _handleDoubleOperands(e, ctx, V, L); 31 | } 32 | 33 | final V = compileExpression(e.operand, ctx); 34 | 35 | if (method == '-' && 36 | V.type != CoreTypes.int.ref(ctx) && 37 | V.type != CoreTypes.double.ref(ctx)) { 38 | throw CompileError( 39 | 'Unary prefix "-" is currently only supported for ints and doubles (type: ${V.type})', 40 | e); 41 | } else if (method == '!' && V.type != CoreTypes.bool.ref(ctx)) { 42 | throw CompileError( 43 | 'Unary prefix "!" is currently only supported for bools (type: ${V.type})', 44 | e); 45 | } 46 | 47 | if (method == "!") { 48 | return V.invoke(ctx, method, []).result; 49 | } 50 | 51 | return _zeroForType(V.type, ctx).push(ctx).invoke(ctx, method, [V]).result; 52 | } 53 | 54 | BuiltinValue _zeroForType(TypeRef type, CompilerContext ctx) => 55 | type == CoreTypes.int.ref(ctx) 56 | ? BuiltinValue(intval: 0) 57 | : BuiltinValue(doubleval: 0.0); 58 | 59 | BuiltinValue _oneForType(TypeRef type, CompilerContext ctx) => 60 | type == CoreTypes.int.ref(ctx) 61 | ? BuiltinValue(intval: 1) 62 | : BuiltinValue(doubleval: 1.0); 63 | 64 | Variable _handleDoubleOperands( 65 | PrefixExpression e, 66 | CompilerContext ctx, 67 | Reference V, 68 | Variable L, 69 | ) { 70 | var l = L; 71 | 72 | l = Variable.alloc(ctx, L.type); 73 | ctx.pushOp(PushNull.make(), PushNull.LEN); 74 | ctx.pushOp( 75 | CopyValue.make(l.scopeFrameOffset, L.scopeFrameOffset), 76 | CopyValue.LEN, 77 | ); 78 | 79 | final result = l.invoke( 80 | ctx, 81 | _opMap[e.operator.type]!, 82 | [_oneForType(l.type, ctx).push(ctx)], 83 | ).result; 84 | 85 | return V.setValue(ctx, result); 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/optimizer/prescan.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: body_might_complete_normally_nullable 2 | 3 | import 'package:analyzer/dart/ast/ast.dart'; 4 | import 'package:analyzer/dart/ast/visitor.dart'; 5 | import 'package:dart_eval/src/eval/compiler/type.dart'; 6 | import 'package:dart_eval/src/eval/compiler/context.dart'; 7 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 8 | import 'package:dart_eval/src/eval/runtime/ops/all_ops.dart'; 9 | 10 | class PrescanVisitor extends RecursiveAstVisitor { 11 | final PrescanContext ctx = PrescanContext(); 12 | late final TypeRef dynamicType; 13 | 14 | @override 15 | PrescanContext? visitFunctionDeclaration(FunctionDeclaration node) { 16 | ctx.beginAllocScope(); 17 | super.visitFunctionDeclaration(node); 18 | ctx.endAllocScope(); 19 | } 20 | 21 | @override 22 | PrescanContext? visitMethodDeclaration(MethodDeclaration node) { 23 | ctx.beginAllocScope(); 24 | super.visitMethodDeclaration(node); 25 | ctx.endAllocScope(); 26 | } 27 | 28 | @override 29 | PrescanContext? visitVariableDeclaration(VariableDeclaration node) { 30 | node.initializer?.accept(this); 31 | ctx.setLocal(node.name.lexeme, Variable.alloc(ctx, dynamicType)); 32 | } 33 | 34 | @override 35 | PrescanContext? visitBlock(Block node) { 36 | ctx.beginAllocScope(); 37 | node.visitChildren(this); 38 | ctx.endAllocScope(); 39 | } 40 | 41 | @override 42 | PrescanContext? visitIfStatement(IfStatement node) { 43 | ctx.beginAllocScope(); 44 | node.expression.accept(this); 45 | final initialState = ctx.saveState(); 46 | ctx.beginAllocScope(); 47 | node.thenStatement.accept(this); 48 | ctx.endAllocScope(); 49 | ctx.resolveBranchStateDiscontinuity(initialState); 50 | if (node.elseStatement != null) { 51 | ctx.beginAllocScope(); 52 | node.elseStatement!.accept(this); 53 | ctx.endAllocScope(); 54 | ctx.resolveBranchStateDiscontinuity(initialState); 55 | } 56 | ctx.endAllocScope(); 57 | } 58 | 59 | @override 60 | PrescanContext? visitFunctionExpression(FunctionExpression node) { 61 | if (node.parent is Statement || node.parent is Expression) { 62 | ctx.inClosure = true; 63 | node.visitChildren(this); 64 | ctx.inClosure = false; 65 | } 66 | return null; 67 | } 68 | 69 | @override 70 | PrescanContext? visitSimpleIdentifier(SimpleIdentifier node) { 71 | if (ctx.inClosure) { 72 | final l = ctx.lookupLocal(node.name); 73 | if (l != null && l.frameIndex != ctx.locals.length - 1) { 74 | ctx.localsReferencedFromClosure.add(l); 75 | ctx.closedFrames.add(l.frameIndex!); 76 | } 77 | } 78 | node.visitChildren(this); 79 | } 80 | } 81 | 82 | class PrescanContext with ScopeContext { 83 | PrescanContext(); 84 | 85 | var inClosure = false; 86 | List localsReferencedFromClosure = []; 87 | Set closedFrames = {}; 88 | 89 | @override 90 | int pushOp(EvcOp op, int length) { 91 | return 0; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/helpers/return.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 4 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 5 | import 'package:dart_eval/src/eval/compiler/type.dart'; 6 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 7 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 8 | 9 | StatementInfo doReturn( 10 | CompilerContext ctx, AlwaysReturnType expectedReturnType, Variable? value, 11 | {bool isAsync = false}) { 12 | if (value == null) { 13 | if (isAsync) { 14 | final completer = ctx.lookupLocal('#completer')!; 15 | ctx.pushOp(ReturnAsync.make(-1, completer.scopeFrameOffset), Return.LEN); 16 | } else { 17 | ctx.pushOp(Return.make(-1), Return.LEN); 18 | } 19 | } else { 20 | if (isAsync) { 21 | final ta = expectedReturnType.type?.specifiedTypeArgs; 22 | final expected = 23 | (ta?.isEmpty ?? true) ? CoreTypes.dynamic.ref(ctx) : ta![0]; 24 | var value0 = value.boxIfNeeded(ctx); 25 | 26 | if (!value0.type.isAssignableTo(ctx, expected)) { 27 | if (value0.type.isAssignableTo(ctx, CoreTypes.future.ref(ctx))) { 28 | final vta = value0.type.specifiedTypeArgs; 29 | final vtype = vta.isEmpty ? CoreTypes.dynamic.ref(ctx) : vta[0]; 30 | if (vtype.isAssignableTo(ctx, expected)) { 31 | final completer = ctx.lookupLocal('#completer')!; 32 | final awaitOp = 33 | Await.make(completer.scopeFrameOffset, value0.scopeFrameOffset); 34 | ctx.pushOp(awaitOp, Await.LEN); 35 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 36 | final result = Variable.alloc(ctx, CoreTypes.dynamic.ref(ctx)); 37 | ctx.pushOp( 38 | ReturnAsync.make( 39 | result.scopeFrameOffset, completer.scopeFrameOffset), 40 | ReturnAsync.LEN); 41 | return StatementInfo(-1, willAlwaysReturn: true); 42 | } 43 | } 44 | throw CompileError( 45 | 'Cannot return ${value0.type} (expected: $expected)'); 46 | } 47 | final completer = ctx.lookupLocal('#completer')!; 48 | ctx.pushOp( 49 | ReturnAsync.make(value0.scopeFrameOffset, completer.scopeFrameOffset), 50 | ReturnAsync.LEN); 51 | return StatementInfo(-1, willAlwaysReturn: true); 52 | } 53 | 54 | final expected = expectedReturnType.type ?? CoreTypes.dynamic.ref(ctx); 55 | var value0 = value; 56 | if (!value0.type.isAssignableTo(ctx, expected)) { 57 | throw CompileError('Cannot return ${value0.type} (expected: $expected)'); 58 | } 59 | if (expected.isUnboxedAcrossFunctionBoundaries && 60 | ctx.currentClass == null) { 61 | value0 = value0.unboxIfNeeded(ctx); 62 | } else { 63 | value0 = value0.boxIfNeeded(ctx); 64 | } 65 | ctx.pushOp(Return.make(value.scopeFrameOffset), Return.LEN); 66 | } 67 | 68 | return StatementInfo(-1, willAlwaysReturn: true); 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/macros/branch.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 5 | import 'package:dart_eval/src/eval/compiler/macros/macro.dart'; 6 | import 'package:dart_eval/src/eval/compiler/model/label.dart'; 7 | import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; 8 | import 'package:dart_eval/src/eval/compiler/type.dart'; 9 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 10 | 11 | StatementInfo macroBranch( 12 | CompilerContext ctx, AlwaysReturnType? expectedReturnType, 13 | {required MacroVariableClosure condition, 14 | required MacroStatementClosure thenBranch, 15 | MacroStatementClosure? elseBranch, 16 | bool resolveStateToThen = false, 17 | AstNode? source}) { 18 | ctx.beginAllocScope(); 19 | ctx.enterTypeInferenceContext(); 20 | 21 | final conditionResult = condition(ctx).unboxIfNeeded(ctx); 22 | if (!conditionResult.type.isAssignableTo(ctx, CoreTypes.bool.ref(ctx))) { 23 | throw CompileError("Conditions must have a static type of 'bool'", source); 24 | } 25 | 26 | final rewriteCond = JumpIfFalse.make(conditionResult.scopeFrameOffset, -1); 27 | final rewritePos = ctx.pushOp(rewriteCond, JumpIfFalse.LEN); 28 | 29 | var initialState = ctx.saveState(); 30 | 31 | ctx.inferTypes(); 32 | ctx.beginAllocScope(); 33 | final label = CompilerLabel(LabelType.branch, -1, (ctx) { 34 | ctx.endAllocScopeQuiet(); 35 | if (!resolveStateToThen) { 36 | //_ctx.resolveBranchStateDiscontinuity(_initialState); 37 | } 38 | //_ctx.endAllocScopeQuiet(); 39 | return -1; 40 | }); 41 | ctx.labels.add(label); 42 | final thenResult = thenBranch(ctx, expectedReturnType); 43 | ctx.labels.removeLast(); 44 | ctx.endAllocScope(); 45 | ctx.uninferTypes(); 46 | 47 | if (!resolveStateToThen) { 48 | ctx.resolveBranchStateDiscontinuity(initialState); 49 | } else { 50 | initialState = ctx.saveState(); 51 | } 52 | 53 | int? rewriteOut; 54 | if (elseBranch != null) { 55 | rewriteOut = ctx.pushOp(JumpConstant.make(-1), JumpConstant.LEN); 56 | } 57 | 58 | ctx.rewriteOp(rewritePos, 59 | JumpIfFalse.make(conditionResult.scopeFrameOffset, ctx.out.length), 0); 60 | 61 | if (elseBranch != null) { 62 | ctx.beginAllocScope(); 63 | final label = CompilerLabel(LabelType.branch, -1, (ctx) { 64 | ctx.endAllocScope(); 65 | ctx.resolveBranchStateDiscontinuity(initialState); 66 | ctx.endAllocScope(); 67 | return -1; 68 | }); 69 | ctx.labels.add(label); 70 | final elseResult = elseBranch(ctx, expectedReturnType); 71 | ctx.labels.removeLast(); 72 | ctx.endAllocScope(); 73 | ctx.resolveBranchStateDiscontinuity(initialState); 74 | ctx.rewriteOp(rewriteOut!, JumpConstant.make(ctx.out.length), 0); 75 | ctx.endAllocScope(); 76 | return thenResult | elseResult; 77 | } 78 | 79 | ctx.endAllocScope(); 80 | 81 | return thenResult | 82 | StatementInfo(thenResult.position, 83 | willAlwaysThrow: false, willAlwaysReturn: false); 84 | } 85 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/helpers/fpl.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 4 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 5 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 6 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 7 | 8 | List resolveFPLDefaults( 9 | CompilerContext ctx, FormalParameterList? fpl, bool isInstanceMethod, 10 | {bool allowUnboxed = true, 11 | bool sortNamed = false, 12 | bool ignoreDefaults = false, 13 | bool isEnum = false}) { 14 | final normalized = []; 15 | var hasEncounteredOptionalPositionalParam = false; 16 | var hasEncounteredNamedParam = false; 17 | var paramIndex = isEnum ? 2 : (isInstanceMethod || sortNamed ? 1 : 0); 18 | 19 | final named = []; 20 | final positional = []; 21 | 22 | for (final param in fpl?.parameters ?? []) { 23 | if (param.isNamed) { 24 | if (hasEncounteredOptionalPositionalParam) { 25 | throw CompileError( 26 | 'Cannot mix named and optional positional parameters'); 27 | } 28 | hasEncounteredNamedParam = true; 29 | named.add(param); 30 | } else { 31 | if (param.isOptionalPositional) { 32 | if (hasEncounteredNamedParam) { 33 | throw CompileError( 34 | 'Cannot mix named and optional positional parameters'); 35 | } 36 | hasEncounteredOptionalPositionalParam = true; 37 | } 38 | positional.add(param); 39 | } 40 | } 41 | 42 | if (sortNamed) { 43 | named.sort((a, b) => (a.name!.lexeme).compareTo(b.name!.lexeme)); 44 | } 45 | 46 | for (final param in [...positional, ...named]) { 47 | if (param is DefaultFormalParameter) { 48 | if (param.defaultValue != null && !ignoreDefaults) { 49 | ctx.beginAllocScope(); 50 | final reserve = JumpIfNonNull.make(paramIndex, -1); 51 | final reserveOffset = ctx.pushOp(reserve, JumpIfNonNull.LEN); 52 | var V = compileExpression(param.defaultValue!, ctx); 53 | if (!allowUnboxed || !V.type.isUnboxedAcrossFunctionBoundaries) { 54 | V = V.boxIfNeeded(ctx); 55 | } else if (allowUnboxed && V.type.isUnboxedAcrossFunctionBoundaries) { 56 | V = V.unboxIfNeeded(ctx); 57 | } 58 | ctx.pushOp( 59 | CopyValue.make(paramIndex, V.scopeFrameOffset), CopyValue.LEN); 60 | ctx.endAllocScope(); 61 | ctx.rewriteOp( 62 | reserveOffset, JumpIfNonNull.make(paramIndex, ctx.out.length), 0); 63 | normalized.add(PossiblyValuedParameter(param.parameter, V)); 64 | } else { 65 | if (param.defaultValue == null /* todo && param.type.nullable */) { 66 | ctx.pushOp(MaybeBoxNull.make(paramIndex), MaybeBoxNull.LEN); 67 | } 68 | normalized.add(PossiblyValuedParameter(param.parameter, null)); 69 | } 70 | } else { 71 | param as NormalFormalParameter; 72 | normalized.add(PossiblyValuedParameter(param, null)); 73 | } 74 | 75 | paramIndex++; 76 | } 77 | return normalized; 78 | } 79 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/core/sink.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_import 2 | // ignore_for_file: unnecessary_import 3 | // ignore_for_file: no_leading_underscores_for_local_identifiers 4 | 5 | import 'package:dart_eval/dart_eval.dart'; 6 | import 'package:dart_eval/dart_eval_bridge.dart'; 7 | import 'sink.dart'; 8 | import 'package:dart_eval/stdlib/core.dart'; 9 | 10 | /// dart_eval wrapper binding for [Sink] 11 | class $Sink implements $Instance { 12 | /// Compile-time type declaration of [$Sink] 13 | static const $type = BridgeTypeRef(CoreTypes.sink); 14 | 15 | /// Compile-time class declaration of [$Sink] 16 | static const $declaration = BridgeClassDef( 17 | BridgeClassType( 18 | $type, 19 | isAbstract: true, 20 | generics: {'T': BridgeGenericParam()}, 21 | ), 22 | constructors: { 23 | '': BridgeConstructorDef( 24 | BridgeFunctionDef( 25 | returns: BridgeTypeAnnotation($type), 26 | namedParams: [], 27 | params: [], 28 | ), 29 | isFactory: false, 30 | ), 31 | }, 32 | methods: { 33 | 'add': BridgeMethodDef( 34 | BridgeFunctionDef( 35 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.voidType)), 36 | namedParams: [], 37 | params: [ 38 | BridgeParameter( 39 | 'data', 40 | BridgeTypeAnnotation(BridgeTypeRef.ref('T')), 41 | false, 42 | ), 43 | ], 44 | ), 45 | ), 46 | 'close': BridgeMethodDef( 47 | BridgeFunctionDef( 48 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.voidType)), 49 | namedParams: [], 50 | params: [], 51 | ), 52 | ), 53 | }, 54 | getters: {}, 55 | setters: {}, 56 | fields: {}, 57 | wrap: true, 58 | ); 59 | 60 | final $Instance _superclass; 61 | 62 | @override 63 | final Sink $value; 64 | 65 | @override 66 | Sink get $reified => $value; 67 | 68 | /// Wrap a [Sink] in a [$Sink] 69 | $Sink.wrap(this.$value) : _superclass = $Object($value); 70 | 71 | @override 72 | int $getRuntimeType(Runtime runtime) => runtime.lookupType(CoreTypes.sink); 73 | 74 | @override 75 | $Value? $getProperty(Runtime runtime, String identifier) { 76 | switch (identifier) { 77 | case 'add': 78 | return __add; 79 | 80 | case 'close': 81 | return __close; 82 | } 83 | return _superclass.$getProperty(runtime, identifier); 84 | } 85 | 86 | static const $Function __add = $Function(_add); 87 | static $Value? _add(Runtime runtime, $Value? target, List<$Value?> args) { 88 | final self = target as $Sink; 89 | self.$value.add(args[0]!.$value); 90 | return null; 91 | } 92 | 93 | static const $Function __close = $Function(_close); 94 | static $Value? _close(Runtime runtime, $Value? target, List<$Value?> args) { 95 | final self = target as $Sink; 96 | self.$value.close(); 97 | return null; 98 | } 99 | 100 | @override 101 | void $setProperty(Runtime runtime, String identifier, $Value value) { 102 | return _superclass.$setProperty(runtime, identifier, value); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/convert/converter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:dart_eval/dart_eval_bridge.dart'; 5 | import 'package:dart_eval/src/eval/shared/stdlib/async/stream.dart'; 6 | import 'package:dart_eval/src/eval/shared/stdlib/core/object.dart'; 7 | 8 | /// dart_eval wrapper for [Converter]. 9 | class $Converter implements $Instance { 10 | /// Wrap a [Converter] in a [$Converter]. 11 | $Converter.wrap(this.$value); 12 | 13 | /// Bridge type reference for [$Converter]. 14 | static const $type = 15 | BridgeTypeRef(BridgeTypeSpec('dart:convert', 'Converter')); 16 | 17 | /// Bridge class definition for [$Converter]. 18 | static const $declaration = BridgeClassDef(BridgeClassType($type), 19 | constructors: { 20 | '': BridgeConstructorDef(BridgeFunctionDef( 21 | returns: BridgeTypeAnnotation($type), params: [], namedParams: [])) 22 | }, 23 | methods: { 24 | 'bind': BridgeMethodDef(BridgeFunctionDef( 25 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.stream)), 26 | params: [ 27 | BridgeParameter('stream', 28 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.stream)), false) 29 | ], 30 | namedParams: [])) 31 | }, 32 | getters: { 33 | 'startChunkedConversion': BridgeMethodDef(BridgeFunctionDef( 34 | returns: BridgeTypeAnnotation(BridgeTypeRef(AsyncTypes.streamSink)), 35 | params: [ 36 | BridgeParameter( 37 | 'sink', 38 | BridgeTypeAnnotation(BridgeTypeRef(AsyncTypes.streamSink)), 39 | false) 40 | ], 41 | namedParams: [])) 42 | }, 43 | setters: {}, 44 | fields: {}, 45 | wrap: true); 46 | 47 | late final $Instance _superclass = $Object($value); 48 | 49 | @override 50 | final Converter $value; 51 | 52 | @override 53 | get $reified => $value; 54 | 55 | @override 56 | $Value? $getProperty(Runtime runtime, String identifier) { 57 | switch (identifier) { 58 | case 'startChunkedConversion': 59 | return __startChunkedConversion; 60 | case 'bind': 61 | return __bind; 62 | default: 63 | return _superclass.$getProperty(runtime, identifier); 64 | } 65 | } 66 | 67 | static const $Function __startChunkedConversion = 68 | $Function(_startChunkedConversion); 69 | 70 | static $Value _startChunkedConversion( 71 | Runtime runtime, $Value? target, List<$Value?> args) { 72 | final sink = args[0]!.$value as StreamSink; 73 | return $StreamSink.wrap((target!.$value as Converter) 74 | .startChunkedConversion(sink) as StreamSink); 75 | } 76 | 77 | static const $Function __bind = $Function(_bind); 78 | 79 | static $Value _bind(Runtime runtime, $Value? target, List<$Value?> args) { 80 | final stream = args[0]!.$value as Stream; 81 | return $Stream.wrap((target!.$value as Converter).bind(stream)); 82 | } 83 | 84 | @override 85 | int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!); 86 | 87 | @override 88 | void $setProperty(Runtime runtime, String identifier, $Value value) { 89 | return _superclass.$setProperty(runtime, identifier, value); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/offset_tracker.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/src/eval/compiler/context.dart'; 2 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 3 | import 'package:dart_eval/src/eval/runtime/ops/all_ops.dart'; 4 | 5 | class OffsetTracker { 6 | OffsetTracker(this.context); 7 | 8 | final Map _deferredOffsets = {}; 9 | CompilerContext context; 10 | 11 | void setOffset(int location, DeferredOrOffset offset) { 12 | _deferredOffsets[location] = offset; 13 | } 14 | 15 | List apply(List source) { 16 | _deferredOffsets.forEach((pos, offset) { 17 | final op = source[pos]; 18 | if (op is Call) { 19 | int resolvedOffset; 20 | 21 | // Just removing this for consistency for now 22 | /*if (offset.offset != null) { 23 | resolvedOffset = offset.offset!; 24 | } else*/ 25 | if (offset.methodType == 2) { 26 | resolvedOffset = context.instanceDeclarationPositions[offset.file!]![ 27 | offset.className!]![2]![offset.name!]!; 28 | } else { 29 | resolvedOffset = context 30 | .topLevelDeclarationPositions[offset.file!]![offset.name!]!; 31 | } 32 | final newOp = Call.make(resolvedOffset); 33 | source[pos] = newOp; 34 | } else if (op is PushObjectPropertyImpl) { 35 | final resolvedOffset = context.instanceGetterIndices[offset.file!]![ 36 | offset.className!]![offset.name!]!; 37 | final newOp = 38 | PushObjectPropertyImpl.make(op.objectOffset, resolvedOffset); 39 | source[pos] = newOp; 40 | } 41 | }); 42 | return source; 43 | } 44 | } 45 | 46 | /// An structure pointing to a function that may or may not have been generated already. If it hasn't, the exact program 47 | /// offset will be resolved later by the [OffsetTracker] 48 | class DeferredOrOffset { 49 | DeferredOrOffset( 50 | {this.offset, 51 | this.file, 52 | this.name, 53 | this.className, 54 | this.methodType, 55 | this.targetScopeFrameOffset}) 56 | : assert(offset != null || name != null); 57 | 58 | final int? offset; 59 | final int? file; 60 | final String? className; 61 | final int? methodType; 62 | final String? name; 63 | final int? targetScopeFrameOffset; 64 | 65 | factory DeferredOrOffset.lookupStatic( 66 | CompilerContext ctx, int library, String parent, String name) { 67 | if (ctx.topLevelDeclarationPositions[library] 68 | ?.containsKey('$parent.$name') ?? 69 | false) { 70 | return DeferredOrOffset( 71 | file: library, 72 | offset: ctx.topLevelDeclarationPositions[library]!['$parent.$name'], 73 | name: '$parent.$name'); 74 | } else { 75 | return DeferredOrOffset(file: library, name: '$parent.$name'); 76 | } 77 | } 78 | 79 | @override 80 | String toString() { 81 | return 'DeferredOrOffset{offset: $offset, file: $file, name: $name}'; 82 | } 83 | 84 | @override 85 | bool operator ==(Object other) => 86 | other is DeferredOrOffset && 87 | other.offset == offset && 88 | other.file == file && 89 | other.className == className && 90 | other.name == name; 91 | 92 | @override 93 | int get hashCode => 94 | offset.hashCode ^ className.hashCode ^ file.hashCode ^ name.hashCode; 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/io/http_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | 3 | class $HttpStatusSource extends DartSource { 4 | $HttpStatusSource() : super(IoTypes.httpStatus.library, _source); 5 | 6 | static const String _source = ''' 7 | abstract class HttpStatus { 8 | static const int continue_ = 100; 9 | static const int switchingProtocols = 101; 10 | static const int processing = 102; 11 | static const int ok = 200; 12 | static const int created = 201; 13 | static const int accepted = 202; 14 | static const int nonAuthoritativeInformation = 203; 15 | static const int noContent = 204; 16 | static const int resetContent = 205; 17 | static const int partialContent = 206; 18 | static const int multiStatus = 207; 19 | static const int alreadyReported = 208; 20 | static const int imUsed = 226; 21 | static const int multipleChoices = 300; 22 | static const int movedPermanently = 301; 23 | static const int found = 302; 24 | static const int movedTemporarily = 302; 25 | static const int seeOther = 303; 26 | static const int notModified = 304; 27 | static const int useProxy = 305; 28 | static const int temporaryRedirect = 307; 29 | static const int permanentRedirect = 308; 30 | static const int badRequest = 400; 31 | static const int unauthorized = 401; 32 | static const int paymentRequired = 402; 33 | static const int forbidden = 403; 34 | static const int notFound = 404; 35 | static const int methodNotAllowed = 405; 36 | static const int notAcceptable = 406; 37 | static const int proxyAuthenticationRequired = 407; 38 | static const int requestTimeout = 408; 39 | static const int conflict = 409; 40 | static const int gone = 410; 41 | static const int lengthRequired = 411; 42 | static const int preconditionFailed = 412; 43 | static const int requestEntityTooLarge = 413; 44 | static const int requestUriTooLong = 414; 45 | static const int unsupportedMediaType = 415; 46 | static const int requestedRangeNotSatisfiable = 416; 47 | static const int expectationFailed = 417; 48 | static const int misdirectedRequest = 421; 49 | static const int unprocessableEntity = 422; 50 | static const int locked = 423; 51 | static const int failedDependency = 424; 52 | static const int upgradeRequired = 426; 53 | static const int preconditionRequired = 428; 54 | static const int tooManyRequests = 429; 55 | static const int requestHeaderFieldsTooLarge = 431; 56 | static const int connectionClosedWithoutResponse = 444; 57 | static const int unavailableForLegalReasons = 451; 58 | static const int clientClosedRequest = 499; 59 | static const int internalServerError = 500; 60 | static const int notImplemented = 501; 61 | static const int badGateway = 502; 62 | static const int serviceUnavailable = 503; 63 | static const int gatewayTimeout = 504; 64 | static const int httpVersionNotSupported = 505; 65 | static const int variantAlsoNegotiates = 506; 66 | static const int insufficientStorage = 507; 67 | static const int loopDetected = 508; 68 | static const int notExtended = 510; 69 | static const int networkAuthenticationRequired = 511; 70 | static const int networkConnectTimeoutError = 599; 71 | } 72 | '''; 73 | } 74 | -------------------------------------------------------------------------------- /lib/src/eval/bridge/runtime_bridge.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names 2 | 3 | import 'package:dart_eval/dart_eval_bridge.dart'; 4 | 5 | /// A bridge class can be extended inside the dart_eval VM and used both in 6 | /// and outside of it. 7 | mixin $Bridge on Object implements $Value, $Instance { 8 | $Value? $bridgeGet(String identifier); 9 | 10 | void $bridgeSet(String identifier, $Value value); 11 | 12 | @override 13 | $Value? $getProperty(Runtime runtime, String identifier) { 14 | try { 15 | return Runtime.bridgeData[this]!.subclass! 16 | .$getProperty(runtime, identifier); 17 | } on UnimplementedError catch (_) { 18 | return $bridgeGet(identifier); 19 | } 20 | } 21 | 22 | @override 23 | void $setProperty(Runtime runtime, String identifier, $Value value) { 24 | try { 25 | return Runtime.bridgeData[this]!.subclass! 26 | .$setProperty(runtime, identifier, value); 27 | } on UnimplementedError catch (_) { 28 | $bridgeSet(identifier, value); 29 | } 30 | } 31 | 32 | dynamic $_get(String prop) { 33 | final runtime = Runtime.bridgeData[this]!.runtime; 34 | return ($getProperty(runtime, prop) as $Value).$reified; 35 | } 36 | 37 | void $_set(String prop, $Value value) { 38 | final runtime = Runtime.bridgeData[this]!.runtime; 39 | $setProperty(runtime, prop, value); 40 | } 41 | 42 | dynamic $_invoke(String method, List<$Value?> args) { 43 | final runtime = Runtime.bridgeData[this]!.runtime; 44 | return ($getProperty(runtime, method) as EvalFunction) 45 | .call(runtime, this, [this, ...args])?.$reified; 46 | } 47 | 48 | @override 49 | $Bridge get $value => this; 50 | 51 | @override 52 | T get $reified => this as T; 53 | 54 | Runtime get $runtime => Runtime.bridgeData[this]!.runtime; 55 | 56 | @override 57 | int $getRuntimeType(Runtime runtime) { 58 | final data = Runtime.bridgeData[this]!; 59 | return data.subclass?.$getRuntimeType(runtime) ?? data.$runtimeType; 60 | } 61 | } 62 | 63 | class BridgeSuperShim implements $Instance { 64 | BridgeSuperShim(); 65 | 66 | late $Bridge bridge; 67 | 68 | @override 69 | $Value? $getProperty(Runtime runtime, String name) => bridge.$bridgeGet(name); 70 | 71 | @override 72 | void $setProperty(Runtime runtime, String name, $Value value) => 73 | bridge.$bridgeSet(name, value); 74 | 75 | @override 76 | $Bridge get $reified => bridge; 77 | 78 | @override 79 | $Bridge get $value => bridge; 80 | 81 | @override 82 | int $getRuntimeType(Runtime runtime) => bridge.$getRuntimeType(runtime); 83 | } 84 | 85 | class BridgeDelegatingShim implements $Instance { 86 | const BridgeDelegatingShim(); 87 | 88 | @override 89 | $Value? $getProperty(Runtime runtime, String name) => 90 | throw UnimplementedError(); 91 | 92 | @override 93 | void $setProperty(Runtime runtime, String name, $Value value) => 94 | throw UnimplementedError(); 95 | 96 | @override 97 | $Bridge get $reified => throw UnimplementedError(); 98 | 99 | @override 100 | $Bridge get $value => throw UnimplementedError(); 101 | 102 | @override 103 | int $getRuntimeType(Runtime runtime) => throw UnimplementedError(); 104 | } 105 | 106 | class BridgeData { 107 | final Runtime runtime; 108 | final $Instance? subclass; 109 | final int $runtimeType; 110 | 111 | const BridgeData(this.runtime, this.$runtimeType, this.subclass); 112 | } 113 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/expression/instance_creation.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/builtins.dart'; 4 | import 'package:dart_eval/src/eval/compiler/context.dart'; 5 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 6 | import 'package:dart_eval/src/eval/compiler/expression/method_invocation.dart'; 7 | import 'package:dart_eval/src/eval/compiler/helpers/argument_list.dart'; 8 | import 'package:dart_eval/src/eval/compiler/offset_tracker.dart'; 9 | import 'package:dart_eval/src/eval/compiler/reference.dart'; 10 | import 'package:dart_eval/src/eval/compiler/type.dart'; 11 | import 'package:dart_eval/src/eval/compiler/variable.dart'; 12 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 13 | 14 | Variable compileInstanceCreation( 15 | CompilerContext ctx, InstanceCreationExpression e) { 16 | final type = e.constructorName.type; 17 | final name = type.importPrefix == null 18 | ? (e.constructorName.name?.name ?? '') 19 | : type.name2.lexeme; 20 | final typeName = type.importPrefix?.name.lexeme ?? type.name2.lexeme; 21 | final $resolved = IdentifierReference(null, typeName).getValue(ctx); 22 | 23 | if ($resolved.concreteTypes.isEmpty) { 24 | throw CompileError('Cannot create instance of a non-type $typeName'); 25 | } 26 | 27 | final staticType = $resolved.concreteTypes.first; 28 | final dec0 = resolveStaticMethod(ctx, staticType, name); 29 | 30 | //final List _args; 31 | //final Map _namedArgs; 32 | 33 | if (dec0.isBridge) { 34 | final bridge = dec0.bridge; 35 | final fnDescriptor = (bridge as BridgeConstructorDef).functionDescriptor; 36 | compileArgumentListWithBridge(ctx, e.argumentList, fnDescriptor); 37 | 38 | //_args = argsPair.first; 39 | //_namedArgs = argsPair.second; 40 | } else { 41 | final dec = dec0.declaration!; 42 | final fpl = (dec as ConstructorDeclaration).parameters.parameters; 43 | 44 | compileArgumentList(ctx, e.argumentList, staticType.file, fpl, dec, 45 | source: e); 46 | //_args = argsPair.first; 47 | //_namedArgs = argsPair.second; 48 | } 49 | 50 | //final _argTypes = _args.map((e) => e.type).toList(); 51 | //final _namedArgTypes = _namedArgs.map((key, value) => MapEntry(key, value.type)); 52 | 53 | if (dec0.isBridge) { 54 | final bridge = dec0.bridge!; 55 | if (bridge is BridgeClassDef && !bridge.wrap) { 56 | final type = TypeRef.fromBridgeTypeRef(ctx, bridge.type.type); 57 | 58 | final $null = BuiltinValue().push(ctx); 59 | final op = BridgeInstantiate.make($null.scopeFrameOffset, 60 | ctx.bridgeStaticFunctionIndices[type.file]!['${type.name}.']!); 61 | ctx.pushOp(op, BridgeInstantiate.len(op)); 62 | } else { 63 | final op = InvokeExternal.make(ctx.bridgeStaticFunctionIndices[ 64 | staticType.file]!['${staticType.name}.$name']!); 65 | ctx.pushOp(op, InvokeExternal.LEN); 66 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 67 | } 68 | } else { 69 | final offset = DeferredOrOffset.lookupStatic( 70 | ctx, staticType.file, staticType.name, name); 71 | final loc = ctx.pushOp(Call.make(offset.offset ?? -1), Call.length); 72 | if (offset.offset == null) { 73 | ctx.offsetTracker.setOffset(loc, offset); 74 | } 75 | ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); 76 | } 77 | 78 | return Variable.alloc( 79 | ctx, $resolved.concreteTypes.first.copyWith(boxed: true)); 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/eval/shared/stdlib/async/future.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: body_might_complete_normally_nullable, camel_case_types 2 | 3 | import 'dart:async'; 4 | 5 | import 'package:dart_eval/dart_eval_bridge.dart'; 6 | import 'package:dart_eval/src/eval/shared/stdlib/core/future.dart'; 7 | 8 | /// dart_eval wrapper for [Completer] 9 | class $Completer implements Completer, $Instance { 10 | $Completer.wrap(this.$value); 11 | 12 | static const _$type = BridgeTypeRef(AsyncTypes.completer, []); 13 | 14 | static const $declaration = BridgeClassDef(BridgeClassType(_$type), 15 | constructors: { 16 | '': BridgeConstructorDef(BridgeFunctionDef( 17 | returns: BridgeTypeAnnotation(_$type), params: [], namedParams: [])) 18 | }, 19 | methods: { 20 | 'complete': BridgeMethodDef(BridgeFunctionDef( 21 | returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.voidType)), 22 | params: [ 23 | BridgeParameter('value', 24 | BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.dynamic)), false) 25 | ], 26 | namedParams: [])) 27 | }, 28 | getters: { 29 | 'future': BridgeMethodDef(BridgeFunctionDef( 30 | returns: BridgeTypeAnnotation( 31 | BridgeTypeRef(BridgeTypeSpec('dart:core', 'Future'))))) 32 | }, 33 | setters: {}, 34 | fields: {}, 35 | wrap: true); 36 | 37 | @override 38 | final Completer $value; 39 | 40 | @override 41 | Completer get $reified => $value; 42 | 43 | @override 44 | $Value? $getProperty(Runtime runtime, String identifier) { 45 | switch (identifier) { 46 | case 'complete': 47 | return const _$Completer_complete(); 48 | case 'future': 49 | return const _$Completer_future(); 50 | } 51 | } 52 | 53 | @override 54 | int $getRuntimeType(Runtime runtime) => 55 | runtime.lookupType(AsyncTypes.completer); 56 | 57 | @override 58 | void $setProperty(Runtime runtime, String identifier, $Value value) { 59 | throw UnimplementedError(); 60 | } 61 | 62 | @override 63 | void complete([FutureOr? value]) => $value.complete(value); 64 | 65 | @override 66 | void completeError(Object error, [StackTrace? stackTrace]) => 67 | $value.completeError(error, stackTrace); 68 | 69 | @override 70 | Future get future => $value.future; 71 | 72 | @override 73 | bool get isCompleted => $value.isCompleted; 74 | } 75 | 76 | class $Completer_new implements EvalCallable { 77 | const $Completer_new(); 78 | 79 | @override 80 | $Value? call(Runtime runtime, $Value? target, List<$Value?> args) { 81 | return $Completer.wrap(Completer()); 82 | } 83 | } 84 | 85 | class _$Completer_complete extends EvalFunction { 86 | const _$Completer_complete() : super(); 87 | 88 | @override 89 | $Value? call(Runtime runtime, $Value? target, List<$Value?> args) { 90 | (target as $Completer).complete(args[0]); 91 | } 92 | 93 | @override 94 | int $getRuntimeType(Runtime runtime) => 95 | runtime.lookupType(CoreTypes.function); 96 | } 97 | 98 | class _$Completer_future extends EvalFunction { 99 | const _$Completer_future() : super(); 100 | 101 | @override 102 | $Value? call(Runtime runtime, $Value? target, List<$Value?> args) { 103 | return $Future.wrap((target as $Completer).future); 104 | } 105 | 106 | @override 107 | int $getRuntimeType(Runtime runtime) => 108 | runtime.lookupType(CoreTypes.function); 109 | } 110 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/model/function_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_eval/dart_eval_bridge.dart'; 2 | import 'package:dart_eval/src/eval/compiler/context.dart'; 3 | import 'package:dart_eval/src/eval/compiler/type.dart'; 4 | 5 | /// Represents either a real [TypeRef] or an unresolved type name e.g. "T" 6 | class FunctionTypeAnnotation { 7 | final TypeRef? type; 8 | final String? name; 9 | 10 | const FunctionTypeAnnotation.type(this.type) : name = null; 11 | 12 | const FunctionTypeAnnotation.name(this.name) : type = null; 13 | 14 | factory FunctionTypeAnnotation.fromBridgeAnnotation( 15 | CompilerContext ctx, BridgeTypeAnnotation annotation) { 16 | final type = annotation.type; 17 | if (type.ref != null) { 18 | return FunctionTypeAnnotation.name(type.ref!); 19 | } else { 20 | return FunctionTypeAnnotation.type( 21 | TypeRef.fromBridgeAnnotation(ctx, annotation)); 22 | } 23 | } 24 | 25 | factory FunctionTypeAnnotation.fromBridgeTypeRef( 26 | CompilerContext ctx, BridgeTypeRef ref) { 27 | return FunctionTypeAnnotation.type(TypeRef.fromBridgeTypeRef(ctx, ref)); 28 | } 29 | } 30 | 31 | class FunctionFormalParameter { 32 | final String? name; 33 | final FunctionTypeAnnotation type; 34 | final bool isRequired; 35 | 36 | const FunctionFormalParameter(this.name, this.type, this.isRequired); 37 | } 38 | 39 | class FunctionGenericParam { 40 | final String name; 41 | final FunctionTypeAnnotation? bound; 42 | 43 | const FunctionGenericParam(this.name, {this.bound}); 44 | } 45 | 46 | class EvalFunctionType { 47 | final List normalParameters; 48 | final List optionalParameters; 49 | final Map namedParameters; 50 | final FunctionTypeAnnotation returnType; 51 | final List generics; 52 | 53 | const EvalFunctionType(this.normalParameters, this.optionalParameters, 54 | this.namedParameters, this.returnType, this.generics); 55 | 56 | factory EvalFunctionType.fromBridgeFunctionDef( 57 | CompilerContext ctx, BridgeFunctionDef def) { 58 | final fReturnType = 59 | FunctionTypeAnnotation.fromBridgeAnnotation(ctx, def.returns); 60 | 61 | final fNormalParameters = []; 62 | final fOptionalParameters = []; 63 | final fNamedParameters = {}; 64 | 65 | for (final param in def.params) { 66 | final fType = 67 | FunctionTypeAnnotation.fromBridgeAnnotation(ctx, param.type); 68 | final fParam = 69 | FunctionFormalParameter(param.name, fType, !param.optional); 70 | if (param.optional) { 71 | fOptionalParameters.add(fParam); 72 | } else { 73 | fNormalParameters.add(fParam); 74 | } 75 | } 76 | 77 | for (final param in def.namedParams) { 78 | final fType = 79 | FunctionTypeAnnotation.fromBridgeAnnotation(ctx, param.type); 80 | final fParam = 81 | FunctionFormalParameter(param.name, fType, !param.optional); 82 | fNamedParameters[param.name] = fParam; 83 | } 84 | 85 | final fGenerics = def.generics.entries.map((entry) => FunctionGenericParam( 86 | entry.key, 87 | bound: entry.value.$extends != null 88 | ? FunctionTypeAnnotation.fromBridgeTypeRef( 89 | ctx, entry.value.$extends!) 90 | : null)); 91 | 92 | return EvalFunctionType(fNormalParameters, fOptionalParameters, 93 | fNamedParameters, fReturnType, fGenerics.toList()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/eval/compiler/declaration/field.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:dart_eval/dart_eval_bridge.dart'; 3 | import 'package:dart_eval/src/eval/compiler/context.dart'; 4 | import 'package:dart_eval/src/eval/compiler/errors.dart'; 5 | import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; 6 | import 'package:dart_eval/src/eval/compiler/scope.dart'; 7 | import 'package:dart_eval/src/eval/compiler/type.dart'; 8 | import 'package:dart_eval/src/eval/runtime/runtime.dart'; 9 | 10 | void compileFieldDeclaration(int fieldIndex, FieldDeclaration d, 11 | CompilerContext ctx, NamedCompilationUnitMember parent) { 12 | final parentName = parent.name.lexeme; 13 | var fieldIndex0 = fieldIndex; 14 | for (final field in d.fields.variables) { 15 | final fieldName = field.name.lexeme; 16 | if (d.isStatic) { 17 | final initializer = field.initializer; 18 | TypeRef? type; 19 | final specifiedType = d.fields.type; 20 | if (specifiedType != null) { 21 | type = TypeRef.fromAnnotation(ctx, ctx.library, specifiedType); 22 | } 23 | if (initializer != null) { 24 | final pos = beginMethod(ctx, field, field.offset, '$fieldName*i'); 25 | ctx.beginAllocScope(); 26 | var V = compileExpression(initializer, ctx, type); 27 | if (type != null) { 28 | if (!V.type.isAssignableTo(ctx, type)) { 29 | throw CompileError( 30 | 'Static field $parentName.$fieldName of inferred type ${V.type} ' 31 | 'does not conform to type $type'); 32 | } 33 | } else { 34 | type = V.type; 35 | } 36 | if (!type.isUnboxedAcrossFunctionBoundaries) { 37 | V = V.boxIfNeeded(ctx); 38 | type = type.copyWith(boxed: true); 39 | } else { 40 | V = V.unboxIfNeeded(ctx); 41 | type = type.copyWith(boxed: false); 42 | } 43 | final name = '$parentName.$fieldName'; 44 | final index = ctx.topLevelGlobalIndices[ctx.library]![name]!; 45 | ctx.pushOp(SetGlobal.make(index, V.scopeFrameOffset), SetGlobal.LEN); 46 | ctx.topLevelVariableInferredTypes[ctx.library]![name] = type; 47 | ctx.topLevelGlobalInitializers[ctx.library]![name] = pos; 48 | ctx.runtimeGlobalInitializerMap[index] = pos; 49 | ctx.pushOp(Return.make(V.scopeFrameOffset), Return.LEN); 50 | ctx.endAllocScope(popValues: false); 51 | } else { 52 | ctx.topLevelVariableInferredTypes[ctx.library]![ 53 | '$parentName.$fieldName'] = type ?? CoreTypes.dynamic.ref(ctx); 54 | } 55 | } else { 56 | final pos = beginMethod(ctx, d, d.offset, '$parentName.$fieldName (get)'); 57 | ctx.pushOp(PushObjectPropertyImpl.make(0, fieldIndex0), 58 | PushObjectPropertyImpl.length); 59 | ctx.pushOp(Return.make(1), Return.LEN); 60 | ctx.instanceDeclarationPositions[ctx.library]![parentName]![0] 61 | [fieldName] = pos; 62 | ctx.instanceGetterIndices[ctx.library]![parentName]![fieldName] = 63 | fieldIndex0; 64 | 65 | if (!(field.isFinal || field.isConst)) { 66 | final setterPos = 67 | beginMethod(ctx, d, d.offset, '$parentName.$fieldName (set)'); 68 | ctx.pushOp(SetObjectPropertyImpl.make(0, fieldIndex0, 1), 69 | SetObjectPropertyImpl.length); 70 | ctx.pushOp(Return.make(1), Return.LEN); 71 | ctx.instanceDeclarationPositions[ctx.library]![parentName]![1] 72 | [fieldName] = setterPos; 73 | } 74 | 75 | fieldIndex0++; 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------