Skip to content

Commit

Permalink
v0.7.6
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanblake4 committed Feb 26, 2024
1 parent b753fa0 commit 96ecba4
Show file tree
Hide file tree
Showing 22 changed files with 292 additions and 15 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
3 changes: 3 additions & 0 deletions lib/src/eval/compiler/collection/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -49,10 +50,12 @@ Variable compileListLiteral(ListLiteral l, CompilerContext ctx,
);

ctx.beginAllocScope();
ctx.labels.add(SimpleCompilerLabel());
final resultTypes = <TypeRef>[];
for (final e in elements) {
resultTypes.addAll(compileListElement(e, _list, ctx, _boxListElements));
}
ctx.labels.removeLast();
ctx.endAllocScope();

if (listSpecifiedType == null) {
Expand Down
1 change: 1 addition & 0 deletions lib/src/eval/compiler/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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!;
Expand Down
21 changes: 21 additions & 0 deletions lib/src/eval/compiler/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -189,6 +198,8 @@ class CompilerContext with ScopeContext {
List<bool> scopeDoesClose = [];
List<ContextSaveState> typeInferenceSaveStates = [];
List<ContextSaveState> typeUninferenceSaveStates = [];
List<CompilerLabel> labels = [];
Map<CompilerLabel, Set<int>> labelReferences = {};
List<bool> inTypeInferenceContext = [];
final List<Variable> caughtExceptions = [];
PrescanContext? preScan;
Expand Down Expand Up @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions lib/src/eval/compiler/declaration/constructor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions lib/src/eval/compiler/macros/branch.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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();

Expand All @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions lib/src/eval/compiler/macros/loop.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -85,6 +106,7 @@ StatementInfo macroLoop(
}

ctx.endAllocScope(popAdjust: pops);
ctx.resolveLabel(label);

return statementResult;
}
25 changes: 25 additions & 0 deletions lib/src/eval/compiler/model/label.dart
Original file line number Diff line number Diff line change
@@ -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 }
17 changes: 17 additions & 0 deletions lib/src/eval/compiler/reference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) ??
Expand Down
3 changes: 3 additions & 0 deletions lib/src/eval/compiler/statement/block.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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);

Expand All @@ -25,6 +27,7 @@ StatementInfo compileBlock(
break;
}
}
ctx.labels.removeLast();

ctx.endAllocScope(popValues: !willAlwaysThrow && !willAlwaysReturn);

Expand Down
32 changes: 32 additions & 0 deletions lib/src/eval/compiler/statement/break.dart
Original file line number Diff line number Diff line change
@@ -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);
}
3 changes: 3 additions & 0 deletions lib/src/eval/compiler/statement/statement.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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}');
}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/eval/compiler/statement/try.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 7 additions & 3 deletions lib/src/eval/runtime/exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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('*');
Expand All @@ -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(', ');
}
}
Expand Down
6 changes: 4 additions & 2 deletions lib/src/eval/runtime/runtime.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}

Expand Down
Loading

0 comments on commit 96ecba4

Please sign in to comment.