Skip to content

Commit

Permalink
Merge branch 'ethanblake4:master' into fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Noobware1 authored Feb 28, 2024
2 parents af70089 + 6407645 commit 5e57768
Show file tree
Hide file tree
Showing 30 changed files with 477 additions and 74 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## 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
- 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
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand All @@ -647,16 +647,16 @@ 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 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) |
| 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) |
Expand All @@ -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) |
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
33 changes: 18 additions & 15 deletions lib/src/eval/compiler/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down 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 Expand Up @@ -912,7 +913,7 @@ Map<Library, Map<String, DeclarationOrPrefix>> _resolveImportsAndExports(
final crawler = CachedFastCrawler(exportGraph.edges);

final result = <Library, Map<String, DeclarationOrPrefix>>{};
final usedDeclarationsForLibrary = <Library, Set<String>>{};
final usedDeclarationsForLibrary = <int, Set<String>>{};

final worklist = <Library>[];
final importMap = <Library, List<_Import>>{};
Expand Down Expand Up @@ -1014,8 +1015,8 @@ Map<Library, Map<String, DeclarationOrPrefix>> _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);
}
Expand Down Expand Up @@ -1046,8 +1047,8 @@ Map<Library, Map<String, DeclarationOrPrefix>> _resolveImportsAndExports(
/// Run tree-shaking
while (worklist.isNotEmpty) {
final library = worklist.removeLast();
Map<Library, Set<String>> applyUsedDeclarations = {};
for (final dec in usedDeclarationsForLibrary[library]!) {
Map<int, Set<String>> applyUsedDeclarations = {};
for (final dec in (usedDeclarationsForLibrary[libraryIds[library]] ?? {})) {
final ids = usedIdentifiers[library]?[dec];
if (ids == null) continue;
final importsWithImplicitSelf = [
Expand All @@ -1061,22 +1062,24 @@ Map<Library, Map<String, DeclarationOrPrefix>> _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);
}
}
}
}
}
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]!);
}
}

Expand All @@ -1087,8 +1090,8 @@ Map<Library, Map<String, DeclarationOrPrefix>> _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();
}

Expand Down
23 changes: 21 additions & 2 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,7 +198,8 @@ class CompilerContext with ScopeContext {
List<bool> scopeDoesClose = [];
List<ContextSaveState> typeInferenceSaveStates = [];
List<ContextSaveState> typeUninferenceSaveStates = [];
List<bool> inTypeInferenceContext = [];
List<CompilerLabel> labels = [];
Map<CompilerLabel, Set<int>> labelReferences = {};
final List<Variable> caughtExceptions = [];
PrescanContext? preScan;
int nearestAsyncFrame = -1;
Expand Down Expand Up @@ -296,7 +306,6 @@ class CompilerContext with ScopeContext {

void enterTypeInferenceContext() {
typeInferenceSaveStates.add(saveState());
inTypeInferenceContext.add(true);
}

void inferTypes() {
Expand Down Expand Up @@ -337,6 +346,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
54 changes: 54 additions & 0 deletions lib/src/eval/compiler/declaration/constructor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -39,10 +41,18 @@ void compileConstructorDeclaration(
ctx.scopeFrameOffset = d.parameters.parameters.length + (isEnum ? 2 : 0);

SuperConstructorInvocation? $superInitializer;
RedirectingConstructorInvocation? $redirectingInitializer;
final otherInitializers = <ConstructorInitializer>[];
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',
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -262,6 +301,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
2 changes: 1 addition & 1 deletion lib/src/eval/compiler/declaration/function.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion lib/src/eval/compiler/declaration/method.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}');
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/compiler/expression/conditional.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/compiler/expression/identifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/compiler/expression/property_access.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Loading

0 comments on commit 5e57768

Please sign in to comment.