Skip to content

Commit

Permalink
Null coalesce, fix boxing via arg type
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanblake4 committed Oct 7, 2023
1 parent da40f5f commit 6dea21c
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 18 deletions.
12 changes: 7 additions & 5 deletions lib/src/eval/compiler/builtins.dart
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,14 @@ const listIndexAssignOp =
const toStringOp = KnownMethod(AlwaysReturnType(EvalTypes.stringType, false), [], {});

final _knownObject = <String, KnownMethod>{
'==': objectComparisonOp,
'toString': toStringOp,
};

final Map<TypeRef, Map<String, KnownMethod>> knownMethods = {
EvalTypes.nullType: {..._knownObject},
EvalTypes.intType: {
..._knownObject,
'+': intBinaryOp,
'-': intBinaryOp,
'*': intBinaryOp,
Expand All @@ -191,9 +194,9 @@ final Map<TypeRef, Map<String, KnownMethod>> knownMethods = {
'>=': numComparisonOp,
'==': numComparisonOp,
'!=': numComparisonOp,
..._knownObject
},
EvalTypes.doubleType: {
..._knownObject,
'+': doubleBinaryOp,
'-': doubleBinaryOp,
'*': doubleBinaryOp,
Expand All @@ -205,9 +208,9 @@ final Map<TypeRef, Map<String, KnownMethod>> knownMethods = {
'>=': numComparisonOp,
'==': numComparisonOp,
'!=': numComparisonOp,
..._knownObject
},
EvalTypes.numType: {
..._knownObject,
'+': numBinaryOp,
'-': numBinaryOp,
'*': numBinaryOp,
Expand All @@ -219,10 +222,10 @@ final Map<TypeRef, Map<String, KnownMethod>> knownMethods = {
'>=': numComparisonOp,
'==': numComparisonOp,
'!=': numComparisonOp,
..._knownObject
},
EvalTypes.boolType: {'&&': boolBinaryOp, '||': boolBinaryOp, '==': boolBinaryOp, '!=': boolBinaryOp},
EvalTypes.stringType: {
..._knownObject,
'+': KnownMethod(
AlwaysReturnType(EvalTypes.stringType, false), [KnownMethodArg('other', EvalTypes.stringType, false)], {}),
'==': KnownMethod(
Expand Down Expand Up @@ -288,9 +291,8 @@ final Map<TypeRef, Map<String, KnownMethod>> knownMethods = {
'toUpperCase': KnownMethod(AlwaysReturnType(EvalTypes.stringType, false), [], {}),
'trimLeft': KnownMethod(AlwaysReturnType(EvalTypes.stringType, false), [], {}),
'trimRight': KnownMethod(AlwaysReturnType(EvalTypes.stringType, false), [], {}),
..._knownObject
},
EvalTypes.enumType: {'==': objectComparisonOp}
EvalTypes.enumType: {..._knownObject}
};

final Map<TypeRef, Map<String, KnownField>> knownFields = {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/compiler/declaration/constructor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ void compileConstructorDeclaration(

for (final init in otherInitializers) {
if (init is ConstructorFieldInitializer) {
final V = compileExpression(init.expression, ctx);
final V = compileExpression(init.expression, ctx).boxIfNeeded(ctx);
ctx.pushOp(SetObjectPropertyImpl.make(instOffset, fieldIndices[init.fieldName.name]!, V.scopeFrameOffset),
SetObjectPropertyImpl.LEN);
usedNames.add(init.fieldName.name);
Expand Down
10 changes: 10 additions & 0 deletions lib/src/eval/compiler/expression/assignment.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:dart_eval/source_node_wrapper.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/macros/branch.dart';
import 'package:dart_eval/src/eval/compiler/statement/statement.dart';
import 'package:dart_eval/src/eval/compiler/variable.dart';

import '../reference.dart';
Expand All @@ -21,6 +24,13 @@ Reference compileAssignmentExpressionAsReference(AssignmentExpression e, Compile
throw CompileError('Syntax error: cannot assign value of type ${R.type} to $Ltype');
}
L.setValue(ctx, R);
} else if (e.operator.type.binaryOperatorOfCompoundAssignment == TokenType.QUESTION_QUESTION) {
macroBranch(ctx, null, condition: (_ctx) {
return L.getValue(_ctx).invoke(ctx, '==', [BuiltinValue().push(_ctx)]).result;
}, thenBranch: (_ctx, rt) {
L.setValue(ctx, R.boxIfNeeded(ctx));
return StatementInfo(-1);
});
} else {
final method = e.operator.type.binaryOperatorOfCompoundAssignment!.lexeme;
L.setValue(ctx, L.getValue(ctx).invoke(ctx, method, [R]).result);
Expand Down
8 changes: 4 additions & 4 deletions lib/src/eval/compiler/helpers/argument_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ Pair<List<Variable>, Map<String, Variable>> compileArgumentList(CompilerContext
}

var _arg = compileExpression(arg, ctx, paramType);
if (parameterHost is MethodDeclaration || !_arg.type.isUnboxedAcrossFunctionBoundaries) {
if (parameterHost is MethodDeclaration || !paramType.isUnboxedAcrossFunctionBoundaries) {
_arg = _arg.boxIfNeeded(ctx);
} else if (_arg.type.isUnboxedAcrossFunctionBoundaries) {
} else if (paramType.isUnboxedAcrossFunctionBoundaries) {
_arg = _arg.unboxIfNeeded(ctx);
}

Expand Down Expand Up @@ -117,9 +117,9 @@ Pair<List<Variable>, Map<String, Variable>> compileArgumentList(CompilerContext
}
if (namedExpr.containsKey(name)) {
var _arg = compileExpression(namedExpr[name]!, ctx, paramType);
if (parameterHost is MethodDeclaration || !_arg.type.isUnboxedAcrossFunctionBoundaries) {
if (parameterHost is MethodDeclaration || !paramType.isUnboxedAcrossFunctionBoundaries) {
_arg = _arg.boxIfNeeded(ctx);
} else if (_arg.type.isUnboxedAcrossFunctionBoundaries) {
} else if (paramType.isUnboxedAcrossFunctionBoundaries) {
_arg = _arg.unboxIfNeeded(ctx);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/src/eval/compiler/reference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ class IdentifierReference implements Reference {
}

ctx.pushOp(CopyValue.make(local.scopeFrameOffset, value.scopeFrameOffset), CopyValue.LEN);
final type = TypeRef.commonBaseType(ctx, {local.type, value.type});
local.copyWithUpdate(ctx, type: type);
return;
}

Expand Down
9 changes: 6 additions & 3 deletions lib/src/eval/compiler/statement/variable_declaration.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/source_node_wrapper.dart';
import 'package:dart_eval/src/eval/compiler/context.dart';
import 'package:dart_eval/src/eval/compiler/expression/expression.dart';
import 'package:dart_eval/src/eval/runtime/runtime.dart';
Expand All @@ -20,12 +21,12 @@ void compileVariableDeclarationList(VariableDeclarationList l, CompilerContext c
}

for (final li in l.variables) {
if (ctx.locals.last.containsKey(li.name.value() as String)) {
throw CompileError('Cannot declare variable ${li.name.value() as String} multiple times in the same scope');
}
final init = li.initializer;
if (init != null) {
final res = compileExpression(init, ctx, type);
if (ctx.locals.last.containsKey(li.name.value() as String)) {
throw CompileError('Cannot declare variable ${li.name.value() as String} multiple times in the same scope');
}
if (type != null && !res.type.resolveTypeChain(ctx).isAssignableTo(ctx, type)) {
throw CompileError(
'Type mismatch: variable "${li.name.value() as String}" is specified as type $type, but is initialized '
Expand All @@ -45,6 +46,8 @@ void compileVariableDeclarationList(VariableDeclarationList l, CompilerContext c
methodReturnType: res.methodReturnType,
callingConvention: res.callingConvention));
}
} else {
ctx.setLocal(li.name.value() as String, BuiltinValue().push(ctx));
}
}
}
6 changes: 5 additions & 1 deletion lib/src/eval/compiler/type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ class TypeRef {
/// Given a set of [TypeRef]s, find their closest common ancestor type.
factory TypeRef.commonBaseType(CompilerContext ctx, Set<TypeRef> types) {
assert(types.isNotEmpty);
var makeNullable = types.remove(EvalTypes.nullType);
if (types.isEmpty) {
return EvalTypes.nullType;
}
final chains = types.map((e) => e.resolveTypeChain(ctx).getTypeChain(ctx)).toList();

// Cross-level type deduplication
Expand Down Expand Up @@ -125,7 +129,7 @@ class TypeRef {

final sorted = refCount.keys.toList()..sort((k1, k2) => layer[k1]! - layer[k2]!);

return sorted[0];
return makeNullable ? sorted[0].copyWith(nullable: true) : sorted[0];
}

/// Create a [TypeRef] from a [TypeAnnotation] and library ID.
Expand Down
8 changes: 4 additions & 4 deletions lib/src/eval/shared/stdlib/core/print.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ void configurePrintForCompile(BridgeDeclarationRegistry registry) {
registry.defineBridgeTopLevelFunction(BridgeFunctionDeclaration(
'dart:core',
'print',
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.voidType)),
params: [BridgeParameter('object', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.objectType)), false)],
namedParams: [])));
BridgeFunctionDef(returns: BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.voidType)), params: [
BridgeParameter(
'object', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.objectType), nullable: true), false)
], namedParams: [])));
}

void configurePrintForRuntime(Runtime runtime) {
Expand Down
24 changes: 24 additions & 0 deletions test/class_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -347,5 +347,29 @@ void main() {

expect(runtime.executeLib('package:example/main.dart', 'main'), $String('Julian the animal (cat)'));
});

test('Constructor field initializers', () {
final runtime = compiler.compileWriteAndLoad({
'example': {
'main.dart': '''
void main() {
var x = X("Hi");
x.printValues();
}
class X {
X(String s) : _a = 1, this._b = s + "!";
final int _a;
final String _b;
void printValues() => print(_a + _b.length);
}
'''
}
});

expect(() {
runtime.executeLib('package:example/main.dart', 'main');
}, prints('4\n'));
});
});
}
20 changes: 20 additions & 0 deletions test/expression_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,25 @@ void main() {
runtime.executeLib('package:example/main.dart', 'main');
}, prints('1\n2\n'));
});

test('Null coalescing assignment', () {
final runtime = compiler.compileWriteAndLoad({
'example': {
'main.dart': '''
void main() {
var x;
x ??= 1;
print(x);
x ??= 2;
print(x);
}
'''
}
});

expect(() {
runtime.executeLib('package:example/main.dart', 'main');
}, prints('1\n1\n'));
});
});
}
18 changes: 18 additions & 0 deletions test/stdlib_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -266,5 +266,23 @@ void main() {
runtime.executeLib('package:example/main.dart', 'main');
}, prints('a\nbird\n'));
});

test('Num add', () {
final runtime = compiler.compileWriteAndLoad({
'example': {
'main.dart': '''
num ADD(num a, num b){
return a + b;
}
void main() {
print(ADD(3,4));
}
''',
}
});
expect(() {
runtime.executeLib('package:example/main.dart', 'main');
}, prints('7\n'));
});
});
}

0 comments on commit 6dea21c

Please sign in to comment.