Skip to content

Commit

Permalink
Support static fields and boxed Map
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanblake4 committed May 14, 2022
1 parent d2ff728 commit 18999d0
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 26 deletions.
43 changes: 34 additions & 9 deletions lib/src/eval/compiler/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:dart_eval/src/eval/bridge/declaration/class.dart';
import 'package:dart_eval/src/eval/bridge/declaration/type.dart';
import 'package:dart_eval/src/eval/compiler/builtins.dart';
import 'package:dart_eval/src/eval/compiler/declaration/declaration.dart';
import 'package:dart_eval/src/eval/compiler/declaration/field.dart';
import 'package:dart_eval/src/eval/compiler/model/library.dart';
import 'package:dart_eval/src/eval/compiler/source.dart';
import 'package:dart_eval/src/eval/compiler/type.dart';
Expand Down Expand Up @@ -208,19 +209,24 @@ class Compiler {

ctx.topLevelGlobalIndices = _topLevelGlobalIndices;

/// Compile top-level variables first so we can infer their type
/// Compile statics first so we can infer their type
_topLevelDeclarationsMap.forEach((key, value) {
value.forEach((lib, _declaration) {
if (_declaration.isBridge) {
return;
}
final declaration = _declaration.declaration!;
if (!(declaration is VariableDeclaration)) {
return;
}
ctx.library = key;
compileDeclaration(declaration, ctx);
ctx.resetStack();
if (declaration is VariableDeclaration && declaration.parent!.parent is TopLevelVariableDeclaration) {
compileDeclaration(declaration, ctx);
ctx.resetStack();
} else if (declaration is ClassDeclaration) {
ctx.currentClass = declaration;
for (final d in declaration.members.whereType<FieldDeclaration>().where((e) => e.isStatic)) {
compileFieldDeclaration(-1, d, ctx, declaration);
ctx.resetStack();
}
}
});
});

Expand Down Expand Up @@ -355,9 +361,28 @@ class Compiler {
_instanceDeclarationsMap[libraryIndex]![name]![member.name.name] = member;
}
} else if (member is FieldDeclaration) {
member.fields.variables.forEach((field) {
_instanceDeclarationsMap[libraryIndex]![name]![field.name.name] = field;
});
if (member.isStatic) {
if (!_topLevelGlobalIndices.containsKey(libraryIndex)) {
_topLevelGlobalIndices[libraryIndex] = {};
ctx.topLevelGlobalInitializers[libraryIndex] = {};
ctx.topLevelVariableInferredTypes[libraryIndex] = {};
}

for (final field in member.fields.variables) {
final name = declaration.name.name + '.' + field.name.name;

if (_topLevelDeclarationsMap[libraryIndex]!.containsKey(name)) {
throw CompileError('Cannot define "$name twice in the same library"');
}

_topLevelDeclarationsMap[libraryIndex]![name] = DeclarationOrBridge(libraryIndex, declaration: field);
_topLevelGlobalIndices[libraryIndex]![name] = ctx.globalIndex++;
}
} else {
for (final field in member.fields.variables) {
_instanceDeclarationsMap[libraryIndex]![name]![field.name.name] = field;
}
}
} else if (member is ConstructorDeclaration) {
_topLevelDeclarationsMap[libraryIndex]!['$name.${member.name?.name ?? ""}'] =
DeclarationOrBridge(libraryIndex, declaration: member);
Expand Down
4 changes: 2 additions & 2 deletions lib/src/eval/compiler/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class CompilerContext with ScopeContext {
@override
void beginAllocScope({int existingAllocLen = 0, bool requireNonlinearAccess = false, bool closure = false}) {
super.beginAllocScope(existingAllocLen: existingAllocLen, requireNonlinearAccess: requireNonlinearAccess);
if (preScan!.closedFrames.contains(locals.length - 1)) {
if (preScan?.closedFrames.contains(locals.length - 1) ?? false) {
final ps = PushScope.make(sourceFile, -1, '#');
pushOp(ps, PushScope.len(ps));
scopeDoesClose.add(true);
Expand Down Expand Up @@ -224,7 +224,7 @@ class CompilerContext with ScopeContext {

@override
int endAllocScope({bool popValues = true, int popAdjust = 0}) {
if (preScan!.closedFrames.contains(locals.length - 1)) {
if (preScan?.closedFrames.contains(locals.length - 1) ?? false) {
pushOp(PopScope.make(), PopScope.LEN);
popValues = false;
}
Expand Down
6 changes: 4 additions & 2 deletions lib/src/eval/compiler/declaration/class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:dart_eval/src/eval/compiler/context.dart';
import 'package:dart_eval/src/eval/compiler/declaration/declaration.dart';
import 'package:dart_eval/src/eval/compiler/type.dart';

void compileClassDeclaration(CompilerContext ctx, ClassDeclaration d) {
void compileClassDeclaration(CompilerContext ctx, ClassDeclaration d, {bool statics = false}) {
final $runtimeType = ctx.typeRefIndexMap[TypeRef.lookupClassDeclaration(ctx, ctx.library, d)];
ctx.instanceDeclarationPositions[ctx.library]![d.name.name] = [{}, {}, {}, $runtimeType];
final constructors = <ConstructorDeclaration>[];
Expand All @@ -13,7 +13,9 @@ void compileClassDeclaration(CompilerContext ctx, ClassDeclaration d) {
if (m is ConstructorDeclaration) {
constructors.add(m);
} else if (m is FieldDeclaration) {
fields.add(m);
if (!m.isStatic) {
fields.add(m);
}
} else {
m as MethodDeclaration;
methods.add(m);
Expand Down
60 changes: 49 additions & 11 deletions lib/src/eval/compiler/declaration/field.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,61 @@
import 'package:analyzer/dart/ast/ast.dart';
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/scope.dart';
import 'package:dart_eval/src/eval/compiler/type.dart';
import 'package:dart_eval/src/eval/runtime/runtime.dart';

void compileFieldDeclaration(int fieldIndex, FieldDeclaration d, CompilerContext ctx, ClassDeclaration parent) {
var _fieldIndex = fieldIndex;
for (final field in d.fields.variables) {
final pos = beginMethod(ctx, d, d.offset, parent.name.name + '.' + field.name.name + ' (get)');
ctx.pushOp(PushObjectPropertyImpl.make(0, _fieldIndex), PushObjectPropertyImpl.LEN);
ctx.pushOp(Return.make(1), Return.LEN);
ctx.instanceDeclarationPositions[ctx.library]![parent.name.name]![0][field.name.name] = pos;

if (!(field.isFinal || field.isConst)) {
final setterPos = beginMethod(ctx, d, d.offset, parent.name.name + '.' + field.name.name + ' (set)');
ctx.pushOp(SetObjectPropertyImpl.make(0, _fieldIndex, 1), SetObjectPropertyImpl.LEN);
if (d.isStatic) {
final initializer = field.initializer;
if (initializer != null) {
final pos = beginMethod(ctx, field, field.offset, parent.name.name + '.' + field.name.name + ' (init)');
var V = compileExpression(initializer, ctx);
TypeRef type;
final specifiedType = d.fields.type;
if (specifiedType != null) {
type = TypeRef.fromAnnotation(ctx, ctx.library, specifiedType);
if (!V.type.isAssignableTo(ctx, type)) {
throw CompileError(
'Static field ${parent.name.name}.${field.name.name} of inferred type ${V.type} '
'does not conform to type $type');
}
} else {
type = V.type;
}
if (!unboxedAcrossFunctionBoundaries.contains(type)) {
V = V.boxIfNeeded(ctx);
type = type.copyWith(boxed: true);
} else {
V = V.unboxIfNeeded(ctx);
type = type.copyWith(boxed: false);
}
final _name = '${parent.name.name}.${field.name.name}';
final _index = ctx.topLevelGlobalIndices[ctx.library]![_name]!;
ctx.pushOp(SetGlobal.make(_index, V.scopeFrameOffset), SetGlobal.LEN);
ctx.topLevelVariableInferredTypes[ctx.library]![_name] = type;
ctx.topLevelGlobalInitializers[ctx.library]![_name] = pos;
ctx.runtimeGlobalInitializerMap[_index] = pos;
ctx.pushOp(Return.make(V.scopeFrameOffset), Return.LEN);
}
} else {
final pos = beginMethod(ctx, d, d.offset, parent.name.name + '.' + field.name.name + ' (get)');
ctx.pushOp(PushObjectPropertyImpl.make(0, _fieldIndex), PushObjectPropertyImpl.LEN);
ctx.pushOp(Return.make(1), Return.LEN);
ctx.instanceDeclarationPositions[ctx.library]![parent.name.name]![1][field.name.name] = setterPos;
}
ctx.instanceDeclarationPositions[ctx.library]![parent.name.name]![0][field.name.name] = pos;

_fieldIndex++;
if (!(field.isFinal || field.isConst)) {
final setterPos = beginMethod(ctx, d, d.offset, parent.name.name + '.' + field.name.name + ' (set)');
ctx.pushOp(SetObjectPropertyImpl.make(0, _fieldIndex, 1), SetObjectPropertyImpl.LEN);
ctx.pushOp(Return.make(1), Return.LEN);
ctx.instanceDeclarationPositions[ctx.library]![parent.name.name]![1][field.name.name] = setterPos;
}

_fieldIndex++;
}
}
}
3 changes: 3 additions & 0 deletions lib/src/eval/compiler/declaration/variable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ void compileTopLevelVariableDeclaration(VariableDeclaration v, CompilerContext c
if (!unboxedAcrossFunctionBoundaries.contains(type)) {
V = V.boxIfNeeded(ctx);
type = type.copyWith(boxed: true);
} else {
V = V.unboxIfNeeded(ctx);
type = type.copyWith(boxed: false);
}
final _index = ctx.topLevelGlobalIndices[ctx.library]![v.name.name]!;
ctx.pushOp(SetGlobal.make(_index, V.scopeFrameOffset), SetGlobal.LEN);
Expand Down
17 changes: 17 additions & 0 deletions lib/src/eval/compiler/reference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ class IdentifierReference implements Reference {
@override
Variable getValue(CompilerContext ctx) {
if (object != null) {
if (object!.type == EvalTypes.typeType) {
final classType = object!.concreteTypes[0];
final _name = classType.name + '.' + name;
final type = ctx.topLevelVariableInferredTypes[classType.file]![_name]!;
final gIndex = ctx.topLevelGlobalIndices[classType.file]![_name]!;
ctx.pushOp(LoadGlobal.make(gIndex), LoadGlobal.LEN);
ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN);
return Variable.alloc(ctx, type);
}
object = object!.boxIfNeeded(ctx);
final op = PushObjectProperty.make(object!.scopeFrameOffset, name);
ctx.pushOp(op, PushObjectProperty.len(op));
Expand Down Expand Up @@ -151,6 +160,14 @@ class IdentifierReference implements Reference {
if (_dec is MethodDeclaration) {
return Variable(-1, EvalTypes.functionType,
methodOffset: DeferredOrOffset.lookupStatic(ctx, ctx.library, ctx.currentClass!.name.name, name));
} else if (_dec is VariableDeclaration) {
final name = ctx.currentClass!.name.name + '.' + _dec.name.name;
final type = ctx.topLevelVariableInferredTypes[ctx.library]![name]!;
final gIndex = ctx.topLevelGlobalIndices[ctx.library]![name]!;
ctx.pushOp(LoadGlobal.make(gIndex), LoadGlobal.LEN);
ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN);

return Variable.alloc(ctx, type);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/src/eval/compiler/type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ class TypeRef {
}
final spec = typeReference.spec;
if (spec != null) {
return ctx.visibleTypes[ctx.libraryMap[spec.library]!]![spec.name]!;
final lib = ctx.libraryMap[spec.library] ?? (throw CompileError('Bridge: cannot find library ${spec.library}'));
return ctx.visibleTypes[lib]![spec.name]!;
}
throw CompileError('No support for looking up types by other bridge annotation types');
}
Expand Down
2 changes: 2 additions & 0 deletions lib/src/eval/compiler/variable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class Variable {
V2 = boxListContents(ctx, this);
}
ctx.pushOp(BoxList.make(V2.scopeFrameOffset), BoxList.LEN);
} else if (type == EvalTypes.mapType) {
ctx.pushOp(BoxMap.make(scopeFrameOffset), BoxMap.LEN);
} else if (type == EvalTypes.stringType) {
ctx.pushOp(BoxString.make(scopeFrameOffset), BoxInt.LEN);
} else {
Expand Down
6 changes: 5 additions & 1 deletion lib/src/eval/runtime/ops/all_ops.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ class Dbc {
/// [LoadGlobal]
static const OP_LOAD_GLOBAL = 50;

/// [BoxMap]
static const OP_BOXMAP = 51;

static List<int> i16b(int i16) {
final x = ByteData(2);
x.setInt16(0, i16);
Expand Down Expand Up @@ -252,5 +255,6 @@ final List<OpLoader> ops = [
(Runtime rt) => IndexMap(rt), // 47
(Runtime rt) => PushConstantDouble(rt), // 48
(Runtime rt) => SetGlobal(rt), // 49
(Runtime rt) => LoadGlobal(rt) // 50
(Runtime rt) => LoadGlobal(rt), // 50
(Runtime rt) => BoxMap(rt), // 37
];
20 changes: 20 additions & 0 deletions lib/src/eval/runtime/ops/primitives.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,26 @@ class BoxList implements DbcOp {
String toString() => 'BoxList (L$_reg)';
}

class BoxMap implements DbcOp {
BoxMap(Runtime runtime) : _reg = runtime._readInt16();

BoxMap.make(this._reg);

final int _reg;

static const int LEN = Dbc.BASE_OPLEN + Dbc.I16_LEN;

@override
void run(Runtime runtime) {
final reg = _reg;
runtime.frame[reg] = $Map.wrap(<$Value, $Value>{...(runtime.frame[reg] as Map)});
}

@override
String toString() => 'BoxMap (L$_reg)';
}


class Unbox implements DbcOp {
Unbox(Runtime runtime) : _reg = runtime._readInt16();

Expand Down
3 changes: 3 additions & 0 deletions lib/src/eval/runtime/runtime.dart
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ class Runtime {
case BoxList:
op as BoxList;
return [Dbc.OP_BOXLIST, ...Dbc.i16b(op._reg)];
case BoxMap:
op as BoxMap;
return [Dbc.OP_BOXMAP, ...Dbc.i16b(op._reg)];
case PushCaptureScope:
op as PushCaptureScope;
return [Dbc.OP_CAPTURE_SCOPE];
Expand Down
1 change: 1 addition & 0 deletions lib/src/eval/shared/stdlib/core/collection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ import 'package:dart_eval/src/eval/runtime/override.dart';

part 'iterable.dart';
part 'list.dart';
part 'map.dart';
51 changes: 51 additions & 0 deletions lib/src/eval/shared/stdlib/core/map.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
part of 'collection.dart';

class $Map<K, V> implements $Instance {
$Map(String id, Map<K, V> value) : $value = runtimeOverride(id) as Map<K, V>? ?? value;

$Map.wrap(this.$value);

@override
final Map<K, V> $value;

late final $Object $super = $Object($value);

@override
$Value? $getProperty(Runtime runtime, String identifier) {
switch (identifier) {
case '[]':
return __indexGet;
case '[]=':
return __indexSet;
case 'length':
return $int($value.length);
}
return $super.$getProperty(runtime, identifier);
}

@override
void $setProperty(Runtime runtime, String identifier, $Value value) {
throw EvalUnknownPropertyException(identifier);
}

static const $Function __indexGet = $Function(_indexGet);

static $Value? _indexGet(Runtime runtime, $Value? target, List<$Value?> args) {
final idx = args[0]!;
return (target!.$value as Map)[idx.$value];
}

static const $Function __indexSet = $Function(_indexSet);

static $Value? _indexSet(Runtime runtime, $Value? target, List<$Value?> args) {
final idx = args[0]!;
final value = args[1]!;
return (target!.$value as Map)[idx.$value] = value;
}

@override
Map get $reified => $value.map((k, v) => MapEntry(k is $Value ? k.$reified : k, v is $Value ? v.$reified : v));

@override
int get $runtimeType => RuntimeTypes.mapType;
}

0 comments on commit 18999d0

Please sign in to comment.