From 96ecba453a7058bb7b6bf1e862b072e3c4a34289 Mon Sep 17 00:00:00 2001 From: Ethan Date: Sun, 25 Feb 2024 17:22:35 -0700 Subject: [PATCH 1/3] v0.7.6 --- CHANGELOG.md | 8 ++++ README.md | 2 +- lib/src/eval/compiler/collection/list.dart | 3 ++ lib/src/eval/compiler/compiler.dart | 1 + lib/src/eval/compiler/context.dart | 21 +++++++++ .../compiler/declaration/constructor.dart | 15 ++++++ lib/src/eval/compiler/macros/branch.dart | 19 ++++++++ lib/src/eval/compiler/macros/loop.dart | 22 +++++++++ lib/src/eval/compiler/model/label.dart | 25 ++++++++++ lib/src/eval/compiler/reference.dart | 17 +++++++ lib/src/eval/compiler/statement/block.dart | 3 ++ lib/src/eval/compiler/statement/break.dart | 32 +++++++++++++ .../eval/compiler/statement/statement.dart | 3 ++ lib/src/eval/compiler/statement/try.dart | 3 ++ lib/src/eval/runtime/exception.dart | 10 ++-- lib/src/eval/runtime/runtime.dart | 6 ++- lib/src/eval/shared/stdlib/core/map.dart | 7 +-- pubspec.yaml | 2 +- test/class_test.dart | 42 +++++++++++++++++ test/collection_test.dart | 16 +++++++ test/expression_test.dart | 4 +- test/loop_test.dart | 46 +++++++++++++++++++ 22 files changed, 292 insertions(+), 15 deletions(-) create mode 100644 lib/src/eval/compiler/model/label.dart create mode 100644 lib/src/eval/compiler/statement/break.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b588b5..e85706b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.7.6 +- Support for 'break' inside loops +- Support for constructor block bodies +- Allow changing the value of static class fields +- Fix various errors caused by tree-shaking bridge class data that is required + to set up the runtime +- Fix error when setting values on Maps + ## 0.7.5 - Create writeback-capable List wrapper. Use `$List.view()` with a type mapper function to create a view of an underlying List that can be written to. diff --git a/README.md b/README.md index d74722b..a7862f4 100644 --- a/README.md +++ b/README.md @@ -650,7 +650,7 @@ may vary when bridging. | For-each loops | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/statement_test.dart#L52) | | Async for-each | ❌ | N/A | | Switch statements | ❌ | N/A | -| Labels and `break` | ❌ | N/A | +| Labels, `break` & `continue` | Partial | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L126), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L146) | | If statements | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L28) | | Try-catch | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#L13), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#31), [[3]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#49), [[4]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#71), [[5]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#92) | | Try-catch-finally | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#L132), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#147), [[3]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#187), [[4]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#209), [[5]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#231) | diff --git a/lib/src/eval/compiler/collection/list.dart b/lib/src/eval/compiler/collection/list.dart index 794355e..1b147e3 100644 --- a/lib/src/eval/compiler/collection/list.dart +++ b/lib/src/eval/compiler/collection/list.dart @@ -7,6 +7,7 @@ import 'package:dart_eval/src/eval/compiler/context.dart'; import 'package:dart_eval/src/eval/compiler/errors.dart'; import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; import 'package:dart_eval/src/eval/compiler/macros/loop.dart'; +import 'package:dart_eval/src/eval/compiler/model/label.dart'; import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; import 'package:dart_eval/src/eval/compiler/type.dart'; import 'package:dart_eval/src/eval/compiler/variable.dart'; @@ -49,10 +50,12 @@ Variable compileListLiteral(ListLiteral l, CompilerContext ctx, ); ctx.beginAllocScope(); + ctx.labels.add(SimpleCompilerLabel()); final resultTypes = []; for (final e in elements) { resultTypes.addAll(compileListElement(e, _list, ctx, _boxListElements)); } + ctx.labels.removeLast(); ctx.endAllocScope(); if (listSpecifiedType == null) { diff --git a/lib/src/eval/compiler/compiler.dart b/lib/src/eval/compiler/compiler.dart index 036ced9..fddd8e1 100644 --- a/lib/src/eval/compiler/compiler.dart +++ b/lib/src/eval/compiler/compiler.dart @@ -283,6 +283,7 @@ class Compiler implements BridgeDeclarationRegistry, EvalPluginRegistry { /// Discover entrypoints for (final declaration in library.declarations) { if (declaration.isBridge) { + _entrypoints.add(library.uri); continue; } final d = declaration.declaration!; diff --git a/lib/src/eval/compiler/context.dart b/lib/src/eval/compiler/context.dart index c3d5378..f12fa3e 100644 --- a/lib/src/eval/compiler/context.dart +++ b/lib/src/eval/compiler/context.dart @@ -4,6 +4,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:dart_eval/dart_eval_bridge.dart'; import 'package:dart_eval/src/eval/compiler/builtins.dart'; import 'package:dart_eval/src/eval/compiler/constant_pool.dart'; +import 'package:dart_eval/src/eval/compiler/model/label.dart'; import 'package:dart_eval/src/eval/compiler/model/override_spec.dart'; import 'package:dart_eval/src/eval/compiler/optimizer/prescan.dart'; import 'package:dart_eval/src/eval/compiler/source.dart'; @@ -61,6 +62,14 @@ mixin ScopeContext on Object implements AbstractScopeContext { return nestCount; } + int endAllocScopeQuiet({bool popValues = true, int popAdjust = 0}) { + final nestCount = allocNest.removeLast(); + if (popValues) { + popN(nestCount + popAdjust); + } + return nestCount; + } + void popN(int pops) { if (pops == 0) { return; @@ -189,6 +198,8 @@ class CompilerContext with ScopeContext { List scopeDoesClose = []; List typeInferenceSaveStates = []; List typeUninferenceSaveStates = []; + List labels = []; + Map> labelReferences = {}; List inTypeInferenceContext = []; final List caughtExceptions = []; PrescanContext? preScan; @@ -337,6 +348,16 @@ class CompilerContext with ScopeContext { }); } } + + void resolveLabel(CompilerLabel label) { + final references = labelReferences[label]; + if (references != null) { + for (final ref in references) { + final jump = JumpConstant.make(out.length); + rewriteOp(ref, jump, 0); + } + } + } } class ContextSaveState with ScopeContext { diff --git a/lib/src/eval/compiler/declaration/constructor.dart b/lib/src/eval/compiler/declaration/constructor.dart index 088c690..5b7cd82 100644 --- a/lib/src/eval/compiler/declaration/constructor.dart +++ b/lib/src/eval/compiler/declaration/constructor.dart @@ -262,6 +262,21 @@ void compileConstructorDeclaration( _compileUnusedFields(ctx, fields, {}, instOffset); + final body = d.body; + if (d.factoryKeyword == null && !(body is EmptyFunctionBody)) { + ctx.beginAllocScope(); + ctx.setLocal('#this', Variable(instOffset, TypeRef.$this(ctx)!)); + if (body is BlockFunctionBody) { + compileBlock( + body.block, AlwaysReturnType(CoreTypes.voidType.ref(ctx), false), ctx, + name: '$n()'); + } else if (body is ExpressionFunctionBody) { + final V = compileExpression(body.expression, ctx); + doReturn(ctx, AlwaysReturnType(CoreTypes.voidType.ref(ctx), false), V); + } + ctx.endAllocScope(); + } + if ($extends != null && extendsWhat!.declaration!.isBridge) { final decl = extendsWhat.declaration!; final bridge = decl.bridge! as BridgeClassDef; diff --git a/lib/src/eval/compiler/macros/branch.dart b/lib/src/eval/compiler/macros/branch.dart index a6e6f54..042abfd 100644 --- a/lib/src/eval/compiler/macros/branch.dart +++ b/lib/src/eval/compiler/macros/branch.dart @@ -1,5 +1,6 @@ import 'package:dart_eval/src/eval/compiler/context.dart'; import 'package:dart_eval/src/eval/compiler/macros/macro.dart'; +import 'package:dart_eval/src/eval/compiler/model/label.dart'; import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; import 'package:dart_eval/src/eval/compiler/type.dart'; import 'package:dart_eval/src/eval/runtime/runtime.dart'; @@ -21,7 +22,17 @@ StatementInfo macroBranch( ctx.inferTypes(); ctx.beginAllocScope(); + final label = CompilerLabel(LabelType.branch, -1, (_ctx) { + _ctx.endAllocScopeQuiet(); + if (!resolveStateToThen) { + _ctx.resolveBranchStateDiscontinuity(_initialState); + } + _ctx.endAllocScopeQuiet(); + return -1; + }); + ctx.labels.add(label); final thenResult = thenBranch(ctx, expectedReturnType); + ctx.labels.removeLast(); ctx.endAllocScope(); ctx.uninferTypes(); @@ -41,7 +52,15 @@ StatementInfo macroBranch( if (elseBranch != null) { ctx.beginAllocScope(); + final label = CompilerLabel(LabelType.branch, -1, (_ctx) { + ctx.endAllocScope(); + ctx.resolveBranchStateDiscontinuity(_initialState); + ctx.endAllocScope(); + return -1; + }); + ctx.labels.add(label); final elseResult = elseBranch(ctx, expectedReturnType); + ctx.labels.removeLast(); ctx.endAllocScope(); ctx.resolveBranchStateDiscontinuity(_initialState); ctx.rewriteOp(rewriteOut!, JumpConstant.make(ctx.out.length), 0); diff --git a/lib/src/eval/compiler/macros/loop.dart b/lib/src/eval/compiler/macros/loop.dart index 2a770d0..483d10f 100644 --- a/lib/src/eval/compiler/macros/loop.dart +++ b/lib/src/eval/compiler/macros/loop.dart @@ -1,5 +1,6 @@ import 'package:dart_eval/src/eval/compiler/context.dart'; import 'package:dart_eval/src/eval/compiler/macros/macro.dart'; +import 'package:dart_eval/src/eval/compiler/model/label.dart'; import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; import 'package:dart_eval/src/eval/compiler/type.dart'; import 'package:dart_eval/src/eval/compiler/variable.dart'; @@ -46,7 +47,27 @@ StatementInfo macroLoop( update(ctx); } + final label = CompilerLabel(LabelType.loop, loopStart, (_ctx) { + _ctx.endAllocScopeQuiet(); + + /// Box/unbox variables that were declared outside the loop and changed in + /// the loop body to match the save state + _ctx.resolveBranchStateDiscontinuity(save); + + if (conditionSaveState != null) { + ctx.restoreBoxingState(conditionSaveState); + ctx.resolveBranchStateDiscontinuity(save); + } + + ctx.endAllocScopeQuiet(); + final result = ctx.pushOp(JumpConstant.make(-1), JumpConstant.LEN); + return result; + }); + + ctx.labels.add(label); final statementResult = body(ctx, expectedReturnType); + ctx.labels.removeLast(); + if (!(statementResult.willAlwaysThrow || statementResult.willAlwaysReturn)) { if (update != null && !updateBeforeBody) { update(ctx); @@ -85,6 +106,7 @@ StatementInfo macroLoop( } ctx.endAllocScope(popAdjust: pops); + ctx.resolveLabel(label); return statementResult; } diff --git a/lib/src/eval/compiler/model/label.dart b/lib/src/eval/compiler/model/label.dart new file mode 100644 index 0000000..ef23c63 --- /dev/null +++ b/lib/src/eval/compiler/model/label.dart @@ -0,0 +1,25 @@ +import 'package:dart_eval/src/eval/compiler/context.dart'; + +class CompilerLabel { + final int offset; + final int Function(CompilerContext ctx) cleanup; + final String? name; + final LabelType type; + + const CompilerLabel(this.type, this.offset, this.cleanup, {this.name}); +} + +class SimpleCompilerLabel implements CompilerLabel { + get offset => -1; + final String? name; + get type => LabelType.block; + + const SimpleCompilerLabel({this.name}); + + get cleanup => (CompilerContext ctx) { + ctx.endAllocScopeQuiet(); + return -1; + }; +} + +enum LabelType { loop, branch, block } diff --git a/lib/src/eval/compiler/reference.dart b/lib/src/eval/compiler/reference.dart index 93df5b3..055701c 100644 --- a/lib/src/eval/compiler/reference.dart +++ b/lib/src/eval/compiler/reference.dart @@ -94,6 +94,23 @@ class IdentifierReference implements Reference { @override Variable setValue(CompilerContext ctx, Variable value, [AstNode? source]) { if (object != null) { + // If the object is a class name, access static fields + if (object!.type == CoreTypes.type.ref(ctx)) { + final classType = object!.concreteTypes[0].resolveTypeChain(ctx); + final _name = '${classType.name}.$name'; + final type = ctx.topLevelVariableInferredTypes[classType.file]![_name]!; + final gIndex = ctx.topLevelGlobalIndices[classType.file]![_name]!; + if (!value.type.isAssignableTo(ctx, type)) { + throw CompileError( + 'Cannot assign value of type ${value.type} to field "$name" of type $type', + source); + } + final _value = + type.boxed ? value.boxIfNeeded(ctx) : value.unboxIfNeeded(ctx); + ctx.pushOp( + SetGlobal.make(gIndex, _value.scopeFrameOffset), SetGlobal.LEN); + return _value; + } object = object!.boxIfNeeded(ctx); final fieldType = TypeRef.lookupFieldType(ctx, object!.type, name, forSet: true, source: source) ?? diff --git a/lib/src/eval/compiler/statement/block.dart b/lib/src/eval/compiler/statement/block.dart index 06b190d..a76d619 100644 --- a/lib/src/eval/compiler/statement/block.dart +++ b/lib/src/eval/compiler/statement/block.dart @@ -1,4 +1,5 @@ import 'package:analyzer/dart/ast/ast.dart'; +import 'package:dart_eval/src/eval/compiler/model/label.dart'; import '../context.dart'; import 'statement.dart'; @@ -13,6 +14,7 @@ StatementInfo compileBlock( var willAlwaysReturn = false; var willAlwaysThrow = false; + ctx.labels.add(SimpleCompilerLabel()); for (final s in b.statements) { final stInfo = compileStatement(s, expectedReturnType, ctx); @@ -25,6 +27,7 @@ StatementInfo compileBlock( break; } } + ctx.labels.removeLast(); ctx.endAllocScope(popValues: !willAlwaysThrow && !willAlwaysReturn); diff --git a/lib/src/eval/compiler/statement/break.dart b/lib/src/eval/compiler/statement/break.dart new file mode 100644 index 0000000..bc6e1bd --- /dev/null +++ b/lib/src/eval/compiler/statement/break.dart @@ -0,0 +1,32 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:dart_eval/src/eval/compiler/context.dart'; +import 'package:dart_eval/src/eval/compiler/errors.dart'; +import 'package:dart_eval/src/eval/compiler/model/label.dart'; +import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; + +StatementInfo compileBreakStatement(BreakStatement s, CompilerContext ctx) { + if (s.label != null) { + throw CompileError('Break labels are not currently supported', s); + } + + final currentState = ctx.saveState(); + + final index = + ctx.labels.lastIndexWhere((label) => label.type == LabelType.loop); + + if (index == -1) { + throw CompileError('Cannot use \'break\' outside of a loop context', s); + } + + for (var i = ctx.labels.length - 1; i > index; i--) { + ctx.labels[i].cleanup(ctx); + } + final label = ctx.labels[index]; + final offset = label.cleanup(ctx); + if (!ctx.labelReferences.containsKey(label)) { + ctx.labelReferences[label] = {}; + } + ctx.labelReferences[label]!.add(offset); + ctx.restoreState(currentState); + return StatementInfo(-1); +} diff --git a/lib/src/eval/compiler/statement/statement.dart b/lib/src/eval/compiler/statement/statement.dart index e813406..671ded4 100644 --- a/lib/src/eval/compiler/statement/statement.dart +++ b/lib/src/eval/compiler/statement/statement.dart @@ -4,6 +4,7 @@ import 'package:dart_eval/src/eval/compiler/context.dart'; import 'package:dart_eval/src/eval/compiler/errors.dart'; import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; import 'package:dart_eval/src/eval/compiler/statement/assert.dart'; +import 'package:dart_eval/src/eval/compiler/statement/break.dart'; import 'package:dart_eval/src/eval/compiler/statement/do.dart'; import 'package:dart_eval/src/eval/compiler/statement/for.dart'; import 'package:dart_eval/src/eval/compiler/statement/if.dart'; @@ -41,6 +42,8 @@ StatementInfo compileStatement( return compileTryStatement(s, ctx, expectedReturnType); } else if (s is AssertStatement) { return compileAssertStatement(s, ctx, expectedReturnType); + } else if (s is BreakStatement) { + return compileBreakStatement(s, ctx); } else { throw CompileError('Unknown statement type ${s.runtimeType}'); } diff --git a/lib/src/eval/compiler/statement/try.dart b/lib/src/eval/compiler/statement/try.dart index 0b9f6b0..f70928d 100644 --- a/lib/src/eval/compiler/statement/try.dart +++ b/lib/src/eval/compiler/statement/try.dart @@ -1,6 +1,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:dart_eval/src/eval/compiler/context.dart'; import 'package:dart_eval/src/eval/compiler/macros/branch.dart'; +import 'package:dart_eval/src/eval/compiler/model/label.dart'; import 'package:dart_eval/src/eval/compiler/statement/block.dart'; import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; import 'package:dart_eval/src/eval/compiler/type.dart'; @@ -31,7 +32,9 @@ StatementInfo compileTryStatement( final _initialState = ctx.saveState(); ctx.beginAllocScope(); + ctx.labels.add(SimpleCompilerLabel()); final bodyInfo = compileBlock(s.body, expectedReturnType, ctx); + ctx.labels.removeLast(); ctx.endAllocScope(); ctx.resolveBranchStateDiscontinuity(_initialState); diff --git a/lib/src/eval/runtime/exception.dart b/lib/src/eval/runtime/exception.dart index c896d61..23d0df1 100644 --- a/lib/src/eval/runtime/exception.dart +++ b/lib/src/eval/runtime/exception.dart @@ -5,8 +5,12 @@ import 'package:dart_eval/src/eval/shared/stdlib/core/num.dart'; /// Format a dart_eval stack sample for printing. String formatStackSample(List st, int size, [int? frameOffset]) { final sb = StringBuffer('['); - final _size = min(size, st.length); - for (var i = 0; i < _size; i++) { + var i = 0; + if (frameOffset != null) { + i = max(0, frameOffset - size ~/ 1.3); + } + final end = min(i + size, st.length); + for (; i < end; i++) { final s = st[i]; if (i == frameOffset) { sb.write('*'); @@ -21,7 +25,7 @@ String formatStackSample(List st, int size, [int? frameOffset]) { } else { sb.write('$s'); } - if (i < _size - 1) { + if (i < end - 1) { sb.write(', '); } } diff --git a/lib/src/eval/runtime/runtime.dart b/lib/src/eval/runtime/runtime.dart index 52ccf47..fe25b8d 100644 --- a/lib/src/eval/runtime/runtime.dart +++ b/lib/src/eval/runtime/runtime.dart @@ -315,8 +315,10 @@ class Runtime { void assertPermission(String domain, [Object? data]) { if (!checkPermission(domain, data)) { throw Exception( - "Permission '$domain' denied${data == null ? '' : ' for $data'}.\n" - "To grant permissions, use Runtime.grant()."); + "Permission '$domain' denied${data == null ? '' : " for '$data'"}.\n" + "To grant permissions, use Runtime.grant() or add the permission " + "to the permissions array of your HotSwapLoader, EvalWidget, " + "or eval() function."); } } diff --git a/lib/src/eval/shared/stdlib/core/map.dart b/lib/src/eval/shared/stdlib/core/map.dart index 46b1729..c0e38ce 100644 --- a/lib/src/eval/shared/stdlib/core/map.dart +++ b/lib/src/eval/shared/stdlib/core/map.dart @@ -121,10 +121,7 @@ class $Map implements Map, $Instance { Runtime runtime, $Value? target, List<$Value?> args) { final idx = args[0]!; final map = target!.$value as Map; - if (map.values.first is $Value) { - return map[idx]; - } - return map[idx.$value]; + return map[idx]; } static const $Function __indexSet = $Function(_indexSet); @@ -133,7 +130,7 @@ class $Map implements Map, $Instance { Runtime runtime, $Value? target, List<$Value?> args) { final idx = args[0]!; final value = args[1]!; - return (target!.$value as Map)[idx.$value] = value; + return (target!.$value as Map)[idx] = value; } static const $Function __addAll = $Function(_addAll); diff --git a/pubspec.yaml b/pubspec.yaml index d2fd1a9..995c67c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_eval description: A flexible Dart bytecode compiler and interpreter written in Dart, enabling dynamic execution and code push for AOT Dart apps. -version: 0.7.5 +version: 0.7.6 homepage: https://github.com/ethanblake4/dart_eval platforms: android: diff --git a/test/class_test.dart b/test/class_test.dart index f8862dc..662ea3a 100644 --- a/test/class_test.dart +++ b/test/class_test.dart @@ -429,6 +429,48 @@ void main() { }, prints('true\nfalse\ntrue\n')); }); + test('Modifying static class field', () { + final runtime = compiler.compileWriteAndLoad({ + 'example': { + 'main.dart': ''' + class TestA { + static int value = 11; + } + + int main() { + TestA.value = 22; + return TestA.value; + } + ''' + } + }); + + expect(runtime.executeLib('package:example/main.dart', 'main'), 22); + }); + + test('Modifying field value in constructor block', () { + final runtime = compiler.compileWriteAndLoad({ + 'example': { + 'main.dart': ''' + class TestA { + int value = 11; + TestA() { + value = 22; + this.value++; + } + } + + int main() { + var a = TestA(); + return a.value; + } + ''' + } + }); + + expect(runtime.executeLib('package:example/main.dart', 'main'), 23); + }); + /* test('Super parameter multi-level indirection', () { final runtime = compiler.compileWriteAndLoad({ 'example': { diff --git a/test/collection_test.dart b/test/collection_test.dart index d7b38ea..1d6c141 100644 --- a/test/collection_test.dart +++ b/test/collection_test.dart @@ -171,5 +171,21 @@ void main() { expect(runtime.executeLib('package:eval_test/main.dart', 'main'), true); }); + + test('Add key to empty map', () { + final runtime = compiler.compileWriteAndLoad({ + 'eval_test': { + 'main.dart': ''' + bool main() { + final testMap = {}; + testMap['name'] = 'Jon'; + return testMap.isNotEmpty; + } + ''' + } + }); + + expect(runtime.executeLib('package:eval_test/main.dart', 'main'), true); + }); }); } diff --git a/test/expression_test.dart b/test/expression_test.dart index e52e47b..4d2152e 100644 --- a/test/expression_test.dart +++ b/test/expression_test.dart @@ -341,9 +341,7 @@ void main() { }, prints('null\n1\n')); }); - // - - test('idk', () { + test('Named params and ternary', () { final runtime = Compiler().compileWriteAndLoad({ 'example': { 'main.dart': ''' diff --git a/test/loop_test.dart b/test/loop_test.dart index 568a788..9891642 100644 --- a/test/loop_test.dart +++ b/test/loop_test.dart @@ -122,5 +122,51 @@ void main() { expect( runtime.executeLib('package:example/main.dart', 'main'), $int(555)); }); + + test('For loop with break', () { + final runtime = compiler.compileWriteAndLoad({ + 'example': { + 'main.dart': ''' + num main() { + var i = 0; + for (; i < 555; i++) { + print(i); + if (i == 5) { + break; + } + } + return i; + } + ''', + } + }); + expect(runtime.executeLib('package:example/main.dart', 'main'), $int(5)); + }); + + test('Nested for loop with break', () { + final runtime = compiler.compileWriteAndLoad({ + 'example': { + 'main.dart': ''' + num main() { + var i = 0; + var j = 0; + for (; i < 555; i++) { + for (; j < 555; j++) { + if (j == 100) { + break; + } + } + if (i == 100) { + break; + } + } + return i * 1000 + j; + } + ''', + } + }); + expect(runtime.executeLib('package:example/main.dart', 'main'), + $int(100100)); + }); }); } From a73de23e6c54c208561da8f3e4f9830491d2cbc4 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 27 Feb 2024 17:22:39 -0700 Subject: [PATCH 2/3] v0.7.7 --- CHANGELOG.md | 9 ++++ README.md | 11 ++--- lib/src/eval/compiler/compiler.dart | 32 ++++++++------- lib/src/eval/compiler/context.dart | 2 - .../compiler/declaration/constructor.dart | 39 ++++++++++++++++++ .../eval/compiler/declaration/function.dart | 2 +- lib/src/eval/compiler/declaration/method.dart | 3 +- .../eval/compiler/expression/conditional.dart | 2 +- .../eval/compiler/expression/identifier.dart | 2 +- .../compiler/expression/property_access.dart | 2 +- lib/src/eval/compiler/macros/branch.dart | 9 +++- lib/src/eval/compiler/reference.dart | 17 ++++---- lib/src/eval/compiler/statement/if.dart | 1 + lib/src/eval/compiler/statement/try.dart | 41 ++++++++----------- lib/src/eval/shared/stdlib/core/map.dart | 17 ++++++++ lib/src/eval/shared/stdlib/io/file.dart | 7 +++- test/class_test.dart | 22 ++++++++++ test/io_test.dart | 30 ++++++++++++++ 18 files changed, 187 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e85706b..e73c682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.7.7 +- Support for redirecting constructors +- Fixed issue where tree-shaking would not always detect uses of symbols made + visible by an export +- Fixed dart:io File constructor +- Better detection of main.dart files +- Fixed empty function bodies causing scope leakage +- Support for `Map.remove()` + ## 0.7.6 - Support for 'break' inside loops - Support for constructor block bodies diff --git a/README.md b/README.md index a7862f4..f11b4fc 100644 --- a/README.md +++ b/README.md @@ -634,8 +634,8 @@ may vary when bridging. | ------- | ------------- | ----- | | Imports | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/lib_composition_test.dart#L14), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/lib_composition_test.dart#L144), [[3]](https://github.com/ethanblake4/dart_eval/blob/master/test/lib_composition_test.dart#L176) | | Exports | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/lib_composition_test.dart#L45), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/lib_composition_test.dart#L200) | -| `part` / `part of` | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/statement_test.dart#L76) | -| `show` and `hide` | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/statement_test.dart#L14) | +| `part` / `part of` | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/lib_composition_test.dart#L76) | +| `show` and `hide` | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/lib_composition_test.dart#L14) | | Conditional imports | ❌ | N/A | | Deferred imports | ❌ | N/A | | Functions | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/function_test.dart#L36) | @@ -647,7 +647,7 @@ may vary when bridging. | For loops | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L13), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L28) | | While loops | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L69) | | Do-while loops | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L86) | -| For-each loops | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/statement_test.dart#L52) | +| For-each loops | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L54), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L71) | | Async for-each | ❌ | N/A | | Switch statements | ❌ | N/A | | Labels, `break` & `continue` | Partial | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L126), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/loop_test.dart#L146) | @@ -656,7 +656,7 @@ may vary when bridging. | Try-catch-finally | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#L132), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#147), [[3]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#187), [[4]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#209), [[5]](https://github.com/ethanblake4/dart_eval/blob/master/test/exception_test.dart#231) | | Lists | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart) | | Iterable | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L14), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L29) | -| Maps | Partial | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/convert_test.dart#L60) | +| Maps | Partial | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/convert_test.dart#L60), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L145), [[3]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L160), [[4]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L175) | | Sets | ❌ | N/A | | Collection `for` | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L14), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L76) | | Collection `if` | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L14), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/collection_test.dart#L52) | @@ -665,9 +665,10 @@ may vary when bridging. | Class static methods | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L147), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L167) | | Getters and setters | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L253) | | Factory constructors | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L375) | +| Redirecting constructors | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L474) | | `new` keyword | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L195) | | Class inheritance | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/functional1_test.dart) | -| Abstract and `implements` | Partial | ❌ | +| Abstract and `implements` | Partial | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/packages/hlc_test.dart#L8) | | `this` keyword | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L89), [[2]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L116) | | `super` keyword | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L319) | | Super constructor params | ✅ | [[1]](https://github.com/ethanblake4/dart_eval/blob/master/test/class_test.dart#L277) | diff --git a/lib/src/eval/compiler/compiler.dart b/lib/src/eval/compiler/compiler.dart index fddd8e1..3e0d3a2 100644 --- a/lib/src/eval/compiler/compiler.dart +++ b/lib/src/eval/compiler/compiler.dart @@ -72,7 +72,7 @@ class Compiler implements BridgeDeclarationRegistry, EvalPluginRegistry { /// full URIs (e.g. `package:foo/main.dart`) or just filenames (e.g. /// `main.dart`). Adding a file to this list prevents it from being dead-code /// eliminated. - final entrypoints = ['main.dart']; + final entrypoints = ['/main.dart']; // Add a plugin, which will only be run once. @override @@ -913,7 +913,7 @@ Map> _resolveImportsAndExports( final crawler = CachedFastCrawler(exportGraph.edges); final result = >{}; - final usedDeclarationsForLibrary = >{}; + final usedDeclarationsForLibrary = >{}; final worklist = []; final importMap = >{}; @@ -1015,8 +1015,8 @@ Map> _resolveImportsAndExports( } } if (isEntrypoint && ids!.contains(declaration.first)) { - usedDeclarationsForLibrary[lib] ??= {'main'}; - usedDeclarationsForLibrary[lib]!.add(declaration.first); + usedDeclarationsForLibrary[libId] ??= {'main'}; + usedDeclarationsForLibrary[libId]!.add(declaration.first); if (!worklist.contains(lib)) { worklist.add(lib); } @@ -1047,8 +1047,8 @@ Map> _resolveImportsAndExports( /// Run tree-shaking while (worklist.isNotEmpty) { final library = worklist.removeLast(); - Map> applyUsedDeclarations = {}; - for (final dec in usedDeclarationsForLibrary[library]!) { + Map> applyUsedDeclarations = {}; + for (final dec in (usedDeclarationsForLibrary[libraryIds[library]] ?? {})) { final ids = usedIdentifiers[library]?[dec]; if (ids == null) continue; final importsWithImplicitSelf = [ @@ -1062,12 +1062,14 @@ Map> _resolveImportsAndExports( } processedImports.add(iid); final lib = uriMap[import.uri]!; - final decs = importedDeclarationsMap[library]?[lib]; + final decs = result[library]?.entries.toList(); if (decs == null) continue; for (final declaration in decs) { - if (ids.contains(declaration.first)) { - applyUsedDeclarations[lib] ??= {'main'}; - applyUsedDeclarations[lib]!.add(declaration.first); + if (ids.contains(declaration.key)) { + final applyLib = + declaration.value.declaration?.sourceLib ?? libraryIds[lib]!; + applyUsedDeclarations[applyLib] ??= {'main'}; + applyUsedDeclarations[applyLib]!.add(declaration.key); if (!worklist.contains(lib)) { worklist.add(lib); } @@ -1075,9 +1077,9 @@ Map> _resolveImportsAndExports( } } } - for (final lib in applyUsedDeclarations.keys) { - usedDeclarationsForLibrary[lib] ??= {}; - usedDeclarationsForLibrary[lib]!.addAll(applyUsedDeclarations[lib]!); + for (final libId in applyUsedDeclarations.keys) { + usedDeclarationsForLibrary[libId] ??= {}; + usedDeclarationsForLibrary[libId]!.addAll(applyUsedDeclarations[libId]!); } } @@ -1088,8 +1090,8 @@ Map> _resolveImportsAndExports( l.declarations = l.declarations .where((declaration) => declaration.isBridge || - DeclarationOrBridge.nameOf(declaration).any( - (name) => {...?usedDeclarationsForLibrary[l]}.contains(name))) + DeclarationOrBridge.nameOf(declaration).any((name) => + {...?usedDeclarationsForLibrary[libraryIds[l]]}.contains(name))) .toList(); } diff --git a/lib/src/eval/compiler/context.dart b/lib/src/eval/compiler/context.dart index f12fa3e..c7bd8e1 100644 --- a/lib/src/eval/compiler/context.dart +++ b/lib/src/eval/compiler/context.dart @@ -200,7 +200,6 @@ class CompilerContext with ScopeContext { List typeUninferenceSaveStates = []; List labels = []; Map> labelReferences = {}; - List inTypeInferenceContext = []; final List caughtExceptions = []; PrescanContext? preScan; int nearestAsyncFrame = -1; @@ -307,7 +306,6 @@ class CompilerContext with ScopeContext { void enterTypeInferenceContext() { typeInferenceSaveStates.add(saveState()); - inTypeInferenceContext.add(true); } void inferTypes() { diff --git a/lib/src/eval/compiler/declaration/constructor.dart b/lib/src/eval/compiler/declaration/constructor.dart index 5b7cd82..c658a46 100644 --- a/lib/src/eval/compiler/declaration/constructor.dart +++ b/lib/src/eval/compiler/declaration/constructor.dart @@ -4,9 +4,11 @@ import 'package:dart_eval/src/eval/compiler/builtins.dart'; import 'package:dart_eval/src/eval/compiler/context.dart'; import 'package:dart_eval/src/eval/compiler/errors.dart'; import 'package:dart_eval/src/eval/compiler/expression/expression.dart'; +import 'package:dart_eval/src/eval/compiler/expression/method_invocation.dart'; import 'package:dart_eval/src/eval/compiler/helpers/argument_list.dart'; import 'package:dart_eval/src/eval/compiler/helpers/fpl.dart'; import 'package:dart_eval/src/eval/compiler/helpers/return.dart'; +import 'package:dart_eval/src/eval/compiler/offset_tracker.dart'; import 'package:dart_eval/src/eval/compiler/reference.dart'; import 'package:dart_eval/src/eval/compiler/scope.dart'; import 'package:dart_eval/src/eval/compiler/source.dart'; @@ -39,10 +41,18 @@ void compileConstructorDeclaration( ctx.scopeFrameOffset = d.parameters.parameters.length + (isEnum ? 2 : 0); SuperConstructorInvocation? $superInitializer; + RedirectingConstructorInvocation? $redirectingInitializer; final otherInitializers = []; for (final initializer in d.initializers) { if (initializer is SuperConstructorInvocation) { $superInitializer = initializer; + } else if (initializer is RedirectingConstructorInvocation) { + if (d.initializers.length > 1) { + throw CompileError( + 'Redirecting constructor invocation must be the only initializer', + d); + } + $redirectingInitializer = initializer; } else if ($superInitializer != null) { throw CompileError( 'Super constructor invocation must be last in the initializer list', @@ -70,6 +80,11 @@ void compileConstructorDeclaration( final p = param.parameter; final V = param.V; Variable vrep; + if ($redirectingInitializer != null && !(p is SimpleFormalParameter)) { + throw CompileError( + 'Redirecting constructor invocation cannot have super or this parameters', + d); + } if (p is FieldFormalParameter) { TypeRef? _type; if (p.type != null) { @@ -112,6 +127,7 @@ void compileConstructorDeclaration( final clsType = TypeRef.lookupDeclaration(ctx, ctx.library, parent); + // Handle factory constructor if (d.factoryKeyword != null) { final b = d.body; @@ -144,6 +160,29 @@ void compileConstructorDeclaration( return; } + // Handle redirecting constructor + if ($redirectingInitializer != null) { + final name = $redirectingInitializer.constructorName?.name ?? ''; + final _dec = resolveStaticMethod(ctx, clsType, name); + final dec = _dec.declaration!; + final fpl = (dec as ConstructorDeclaration).parameters.parameters; + + compileArgumentList( + ctx, $redirectingInitializer.argumentList, clsType.file, fpl, dec, + source: $redirectingInitializer.argumentList); + + final offset = + DeferredOrOffset.lookupStatic(ctx, clsType.file, clsType.name, name); + final loc = ctx.pushOp(Call.make(offset.offset ?? -1), Call.length); + if (offset.offset == null) { + ctx.offsetTracker.setOffset(loc, offset); + } + ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); + final V = Variable.alloc(ctx, clsType); + doReturn(ctx, AlwaysReturnType(clsType, false), V); + return; + } + final $extends = parent is EnumDeclaration ? null : (parent as ClassDeclaration).extendsClause; diff --git a/lib/src/eval/compiler/declaration/function.dart b/lib/src/eval/compiler/declaration/function.dart index 47185c1..0b07670 100644 --- a/lib/src/eval/compiler/declaration/function.dart +++ b/lib/src/eval/compiler/declaration/function.dart @@ -16,7 +16,7 @@ import 'package:dart_eval/src/eval/compiler/variable.dart'; import 'package:dart_eval/src/eval/runtime/runtime.dart'; void compileFunctionDeclaration(FunctionDeclaration d, CompilerContext ctx) { - ctx.runPrescan(d); + //ctx.runPrescan(d); final pos = beginMethod(ctx, d, d.offset, '${d.name.lexeme}()'); ctx.topLevelDeclarationPositions[ctx.library]![d.name.lexeme] = pos; diff --git a/lib/src/eval/compiler/declaration/method.dart b/lib/src/eval/compiler/declaration/method.dart index 2e5dab1..413b349 100644 --- a/lib/src/eval/compiler/declaration/method.dart +++ b/lib/src/eval/compiler/declaration/method.dart @@ -15,7 +15,7 @@ import 'package:dart_eval/src/eval/runtime/runtime.dart'; int compileMethodDeclaration(MethodDeclaration d, CompilerContext ctx, NamedCompilationUnitMember parent) { - ctx.runPrescan(d); + ///ctx.runPrescan(d); final b = d.body; final parentName = parent.name.lexeme; final methodName = d.name.lexeme; @@ -70,6 +70,7 @@ int compileMethodDeclaration(MethodDeclaration d, CompilerContext ctx, isAsync: b.isAsynchronous); ctx.endAllocScope(); } else if (b is EmptyFunctionBody) { + ctx.endAllocScope(); return -1; } else { throw CompileError('Unknown function body type ${b.runtimeType}'); diff --git a/lib/src/eval/compiler/expression/conditional.dart b/lib/src/eval/compiler/expression/conditional.dart index e5d12f2..b373958 100644 --- a/lib/src/eval/compiler/expression/conditional.dart +++ b/lib/src/eval/compiler/expression/conditional.dart @@ -38,7 +38,7 @@ Variable compileConditionalExpression( types.add(v.type); vRef.setValue(ctx, v); return StatementInfo(-1); - }, resolveStateToThen: true); + }, resolveStateToThen: true, source: e); final val = vRef.getValue(ctx).updated(ctx); return val.copyWith( diff --git a/lib/src/eval/compiler/expression/identifier.dart b/lib/src/eval/compiler/expression/identifier.dart index 722e92c..16ebdc4 100644 --- a/lib/src/eval/compiler/expression/identifier.dart +++ b/lib/src/eval/compiler/expression/identifier.dart @@ -10,7 +10,7 @@ import 'package:dart_eval/src/eval/compiler/variable.dart'; import '../util.dart'; Variable compileIdentifier(Identifier id, CompilerContext ctx) { - return compileIdentifierAsReference(id, ctx).getValue(ctx); + return compileIdentifierAsReference(id, ctx).getValue(ctx, id); } Reference compileIdentifierAsReference(Identifier id, CompilerContext ctx) { diff --git a/lib/src/eval/compiler/expression/property_access.dart b/lib/src/eval/compiler/expression/property_access.dart index 99bbd46..2fc52ba 100644 --- a/lib/src/eval/compiler/expression/property_access.dart +++ b/lib/src/eval/compiler/expression/property_access.dart @@ -31,7 +31,7 @@ Variable compilePropertyAccess(PropertyAccess pa, CompilerContext ctx, ctx.pushOp(CopyValue.make(out.scopeFrameOffset, V.scopeFrameOffset), CopyValue.LEN); return StatementInfo(-1); - }); + }, source: pa); return out; } diff --git a/lib/src/eval/compiler/macros/branch.dart b/lib/src/eval/compiler/macros/branch.dart index 042abfd..f15658f 100644 --- a/lib/src/eval/compiler/macros/branch.dart +++ b/lib/src/eval/compiler/macros/branch.dart @@ -1,4 +1,7 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:dart_eval/dart_eval_bridge.dart'; import 'package:dart_eval/src/eval/compiler/context.dart'; +import 'package:dart_eval/src/eval/compiler/errors.dart'; import 'package:dart_eval/src/eval/compiler/macros/macro.dart'; import 'package:dart_eval/src/eval/compiler/model/label.dart'; import 'package:dart_eval/src/eval/compiler/statement/statement.dart'; @@ -10,10 +13,14 @@ StatementInfo macroBranch( {required MacroVariableClosure condition, required MacroStatementClosure thenBranch, MacroStatementClosure? elseBranch, - bool resolveStateToThen = false}) { + bool resolveStateToThen = false, + AstNode? source}) { ctx.beginAllocScope(); ctx.enterTypeInferenceContext(); final conditionResult = condition(ctx).unboxIfNeeded(ctx); + if (!conditionResult.type.isAssignableTo(ctx, CoreTypes.bool.ref(ctx))) { + throw CompileError("Conditions must have a static type of 'bool'", source); + } final rewriteCond = JumpIfFalse.make(conditionResult.scopeFrameOffset, -1); final rewritePos = ctx.pushOp(rewriteCond, JumpIfFalse.LEN); diff --git a/lib/src/eval/compiler/reference.dart b/lib/src/eval/compiler/reference.dart index 055701c..c0b89ef 100644 --- a/lib/src/eval/compiler/reference.dart +++ b/lib/src/eval/compiler/reference.dart @@ -105,13 +105,14 @@ class IdentifierReference implements Reference { 'Cannot assign value of type ${value.type} to field "$name" of type $type', source); } - final _value = - type.boxed ? value.boxIfNeeded(ctx) : value.unboxIfNeeded(ctx); + final _value = type.boxed + ? value.boxIfNeeded(ctx, source) + : value.unboxIfNeeded(ctx); ctx.pushOp( SetGlobal.make(gIndex, _value.scopeFrameOffset), SetGlobal.LEN); return _value; } - object = object!.boxIfNeeded(ctx); + object = object!.boxIfNeeded(ctx, source); final fieldType = TypeRef.lookupFieldType(ctx, object!.type, name, forSet: true, source: source) ?? CoreTypes.dynamic.ref(ctx); @@ -120,7 +121,7 @@ class IdentifierReference implements Reference { 'Cannot assign value of type ${value.type} to field "$name" of type $fieldType', source); } - final _v = value.boxIfNeeded(ctx); + final _v = value.boxIfNeeded(ctx, source); final op = SetObjectProperty.make( object!.scopeFrameOffset, name, _v.scopeFrameOffset); ctx.pushOp(op, SetObjectProperty.len(op)); @@ -160,7 +161,7 @@ class IdentifierReference implements Reference { } final $this = ctx.lookupLocal('#this')!; final op = SetObjectProperty.make($this.scopeFrameOffset, name, - value.boxIfNeeded(ctx).scopeFrameOffset); + value.boxIfNeeded(ctx, source).scopeFrameOffset); ctx.pushOp(op, SetObjectProperty.len(op)); return value; } @@ -240,7 +241,7 @@ class IdentifierReference implements Reference { ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN); return Variable.alloc(ctx, type); } - object = object!.boxIfNeeded(ctx); + object = object!.boxIfNeeded(ctx, source); return object!.getProperty(ctx, name); } @@ -500,7 +501,7 @@ class IndexedReference implements Reference { final map = _variable.unboxIfNeeded(ctx); _index = (_variable.type.specifiedTypeArgs.isEmpty || _variable.type.specifiedTypeArgs[0].boxed) - ? _index.boxIfNeeded(ctx) + ? _index.boxIfNeeded(ctx, source) : _index.unboxIfNeeded(ctx); ctx.pushOp(IndexMap.make(map.scopeFrameOffset, _index.scopeFrameOffset), IndexMap.LEN); @@ -533,7 +534,7 @@ class IndexedReference implements Reference { final elementType = list.type.specifiedTypeArgs[0]; var _value = value; if (elementType.boxed) { - _value = _value.boxIfNeeded(ctx); + _value = _value.boxIfNeeded(ctx, source); } else { _value = _value.unboxIfNeeded(ctx); } diff --git a/lib/src/eval/compiler/statement/if.dart b/lib/src/eval/compiler/statement/if.dart index a0c7a04..4afc491 100644 --- a/lib/src/eval/compiler/statement/if.dart +++ b/lib/src/eval/compiler/statement/if.dart @@ -18,5 +18,6 @@ StatementInfo compileIfStatement( ? null : (_ctx, expectedReturnType) => compileStatement(elseStatement, expectedReturnType, _ctx), + source: s, ); } diff --git a/lib/src/eval/compiler/statement/try.dart b/lib/src/eval/compiler/statement/try.dart index f70928d..233d206 100644 --- a/lib/src/eval/compiler/statement/try.dart +++ b/lib/src/eval/compiler/statement/try.dart @@ -86,29 +86,24 @@ StatementInfo _compileCatchClause( return compileBlock(catchClause.body, expectedReturnType, ctx); } final slot = TypeRef.fromAnnotation(ctx, ctx.library, exceptionType); - return macroBranch( - ctx, - expectedReturnType, - condition: (_ctx) { - ctx.pushOp( - IsType.make( - exceptionVar.scopeFrameOffset, ctx.typeRefIndexMap[slot]!, false), - IsType.length); - return Variable.alloc( - ctx, CoreTypes.bool.ref(ctx).copyWith(boxed: false)); - }, - thenBranch: (_ctx, _expectedReturnType) { - ctx.setLocal(catchClause.exceptionParameter!.name.lexeme, - exceptionVar.copyWith(type: slot)); - return compileBlock(catchClause.body, expectedReturnType, ctx); - }, - elseBranch: clauses.length <= index + 1 - ? null - : (ctx, expectedReturnType) { - return _compileCatchClause( - ctx, clauses, index + 1, exceptionVar, expectedReturnType); - }, - ); + return macroBranch(ctx, expectedReturnType, condition: (_ctx) { + ctx.pushOp( + IsType.make( + exceptionVar.scopeFrameOffset, ctx.typeRefIndexMap[slot]!, false), + IsType.length); + return Variable.alloc(ctx, CoreTypes.bool.ref(ctx).copyWith(boxed: false)); + }, thenBranch: (_ctx, _expectedReturnType) { + ctx.setLocal(catchClause.exceptionParameter!.name.lexeme, + exceptionVar.copyWith(type: slot)); + return compileBlock(catchClause.body, expectedReturnType, ctx); + }, + elseBranch: clauses.length <= index + 1 + ? null + : (ctx, expectedReturnType) { + return _compileCatchClause( + ctx, clauses, index + 1, exceptionVar, expectedReturnType); + }, + source: catchClause); } /// diff --git a/lib/src/eval/shared/stdlib/core/map.dart b/lib/src/eval/shared/stdlib/core/map.dart index c0e38ce..082774a 100644 --- a/lib/src/eval/shared/stdlib/core/map.dart +++ b/lib/src/eval/shared/stdlib/core/map.dart @@ -61,6 +61,15 @@ class $Map implements Map, $Instance { ], returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.bool))), isStatic: false, ), + 'remove': BridgeMethodDef( + BridgeFunctionDef(params: [ + BridgeParameter( + 'key', + BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.object), + nullable: true), + false), + ], returns: BridgeTypeAnnotation(BridgeTypeRef.ref('V'))), + isStatic: false), }, getters: { 'entries': BridgeMethodDef(BridgeFunctionDef( @@ -100,6 +109,8 @@ class $Map implements Map, $Instance { return $int($value.length); case 'containsKey': return __containsKey; + case 'remove': + return __remove; case 'entries': return $Iterable.wrap(entries.map((e) => $MapEntry.wrap(e))); case 'isEmpty': @@ -154,6 +165,12 @@ class $Map implements Map, $Instance { return $bool((target!.$value as Map).containsKey(args[0])); } + static const $Function __remove = $Function(_remove); + + static $Value? _remove(Runtime runtime, $Value? target, List<$Value?> args) { + return (target!.$value as Map).remove(args[0]); + } + @override Map get $reified => $value.map((k, v) => MapEntry(k is $Value ? k.$reified : k, v is $Value ? v.$reified : v)); diff --git a/lib/src/eval/shared/stdlib/io/file.dart b/lib/src/eval/shared/stdlib/io/file.dart index 5c40596..b30b2ab 100644 --- a/lib/src/eval/shared/stdlib/io/file.dart +++ b/lib/src/eval/shared/stdlib/io/file.dart @@ -29,8 +29,11 @@ class $File implements $Instance { static const $declaration = BridgeClassDef( BridgeClassType($type, $extends: $FileSystemEntity.$type), constructors: { - '': BridgeConstructorDef(BridgeFunctionDef( - returns: BridgeTypeAnnotation($type), params: [], namedParams: [])) + '': BridgeConstructorDef( + BridgeFunctionDef(returns: BridgeTypeAnnotation($type), params: [ + BridgeParameter('path', + BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)), false) + ], namedParams: [])) }, methods: { 'create': BridgeMethodDef(BridgeFunctionDef( diff --git a/test/class_test.dart b/test/class_test.dart index 662ea3a..3286608 100644 --- a/test/class_test.dart +++ b/test/class_test.dart @@ -471,6 +471,28 @@ void main() { expect(runtime.executeLib('package:example/main.dart', 'main'), 23); }); + test('Simple redirecting constructor', () { + final runtime = compiler.compileWriteAndLoad({ + 'example': { + 'main.dart': ''' + class TestA { + int value; + int value2; + TestA(int b) : this._(11, b); + TestA._(this.value, this.value2); + } + + int main() { + var a = TestA(4); + return a.value + a.value2; + } + ''' + } + }); + + expect(runtime.executeLib('package:example/main.dart', 'main'), 15); + }); + /* test('Super parameter multi-level indirection', () { final runtime = compiler.compileWriteAndLoad({ 'example': { diff --git a/test/io_test.dart b/test/io_test.dart index 868be8f..6f7e8b7 100644 --- a/test/io_test.dart +++ b/test/io_test.dart @@ -68,5 +68,35 @@ void main() { expect(result, contains('Example Domain')); }); + + test('Write/read a file', () async { + final compiler = Compiler(); + final runtime = compiler.compileWriteAndLoad({ + 'example': { + 'main.dart': ''' + import 'dart:io'; + import 'dart:convert'; + + Future main() async { + final file = File('example.txt'); + await file.writeAsString('Hello, World!'); + final contents = await file.readAsString(); + file.delete(); + return contents; + } + ''' + } + }); + + runtime.grant(FilesystemPermission.any); + + final result = (await runtime.executeLib( + 'package:example/main.dart', + 'main', + )) + .$value; + + expect(result, 'Hello, World!'); + }); }); } From 640764579bd57bffdebc238351c411a7c2c79b9f Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 27 Feb 2024 17:23:11 -0700 Subject: [PATCH 3/3] Update 0.7.7 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 995c67c..da1d739 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_eval description: A flexible Dart bytecode compiler and interpreter written in Dart, enabling dynamic execution and code push for AOT Dart apps. -version: 0.7.6 +version: 0.7.7 homepage: https://github.com/ethanblake4/dart_eval platforms: android: