Skip to content

Commit

Permalink
v0.7.9
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanblake4 committed Mar 17, 2024
1 parent 72eb4cd commit 0d6861a
Show file tree
Hide file tree
Showing 79 changed files with 673 additions and 129 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## 0.7.9
- Expose base `Object` properties (eg toString, hashCode) for classes
declared inside dart_eval
- Type autowrappers can now be added to the runtime to expose any type
for automatic wrapping using `runtime.wrap()`.
- Support for testing function equality
- Deprecated `runtime.wrapRecursive()`. Use `runtime.wrap(recursive: true)`
instead.
- Optimized generated bytecode for string interpolation
- Fixed $Future.$reified to unwrap its value
- Fix error when accessing or setting nullable static class fields without an
initializer
- Support for factory constructors in the binding generator
- Add $runtime Runtime getter to $Bridge mixin

## 0.7.8
- Support for runtime methods, static setters, type parameters, class
inheritance, anonymous function types, and typedefs in the binding generator
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,14 @@ dart_eval VM, and not any code it interacts with. For example, most Flutter apps
the vast majority of their performance budget in the Flutter framework itself, so the
speed impact of dart_eval is usually negligible.

### Is this allowed in the App Store?

Though Apple's official guidelines are unclear, many popular apps use similar
techniques to dynamically update their code. For example, apps built on
React Native often use its custom Hermes JavaScript engine to enable dynamic
code updates. Note that Apple is likely to remove apps if they introduce policy
violations in updates, regardless of the technology used.

## Language feature support table

The following table details the language features supported by dart_eval with native Dart code. Feature support
Expand Down
1 change: 1 addition & 0 deletions lib/dart_eval_bridge.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
library dart_eval.bridge;

export 'package:pub_semver/pub_semver.dart' show Version;
export 'src/eval/runtime/runtime.dart' show Runtime;
export 'src/eval/runtime/class.dart' hide $InstanceImpl;
export 'src/eval/runtime/declaration.dart' hide EvalClassClass;
export 'src/eval/bridge/declaration/class.dart';
Expand Down
1 change: 1 addition & 0 deletions lib/src/eval/bindgen/bridge_declaration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ String bridgeConstructorDef({required ConstructorElement constructor}) {
namedParams: [${namedParameters(element: constructor)}],
params: [${positionalParameters(element: constructor)}],
),
isFactory: ${constructor.isFactory},
),
''';
}
Expand Down
3 changes: 2 additions & 1 deletion lib/src/eval/bindgen/configure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ static void configureForRuntime(Runtime runtime) {

String constructorsForRuntime(BindgenContext ctx, ClassElement element) {
return element.constructors
.where((cstr) => !element.isAbstract && !cstr.isPrivate)
.where(
(cstr) => (!element.isAbstract || cstr.isFactory) && !cstr.isPrivate)
.map((e) => constructorForRuntime(ctx, element, e))
.join('\n');
}
Expand Down
3 changes: 2 additions & 1 deletion lib/src/eval/bridge/runtime_bridge.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// ignore_for_file: non_constant_identifier_names

import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/src/eval/runtime/runtime.dart';

/// A bridge class can be extended inside the dart_eval VM and used both in
/// and outside of it.
Expand Down Expand Up @@ -52,6 +51,8 @@ mixin $Bridge<T> on Object implements $Value, $Instance {
@override
T get $reified => this as T;

Runtime get $runtime => Runtime.bridgeData[this]!.runtime;

@override
int $getRuntimeType(Runtime runtime) {
final data = Runtime.bridgeData[this]!;
Expand Down
1 change: 0 additions & 1 deletion lib/src/eval/cli/run.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:io';

import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';

void cliRun(String path, String library, String function) {
Expand Down
1 change: 0 additions & 1 deletion lib/src/eval/compiler/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import 'package:dart_eval/src/eval/compiler/util/custom_crawler.dart';
import 'package:dart_eval/src/eval/compiler/util/graph.dart';
import 'package:dart_eval/src/eval/compiler/util/library_graph.dart';
import 'package:dart_eval/src/eval/compiler/util/tree_shake.dart';
import 'package:dart_eval/src/eval/runtime/runtime.dart';
import 'package:dart_eval/src/eval/shared/stdlib/async.dart';
import 'package:dart_eval/src/eval/shared/stdlib/collection.dart';
import 'package:dart_eval/src/eval/shared/stdlib/convert.dart';
Expand Down
7 changes: 6 additions & 1 deletion lib/src/eval/compiler/declaration/constructor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,12 @@ void compileConstructorDeclaration(

for (final init in otherInitializers) {
if (init is ConstructorFieldInitializer) {
final V = compileExpression(init.expression, ctx).boxIfNeeded(ctx);
final fType = TypeRef.lookupFieldType(
ctx,
TypeRef.lookupDeclaration(ctx, ctx.library, parent),
init.fieldName.name,
source: init);
final V = compileExpression(init.expression, ctx, fType).boxIfNeeded(ctx);
ctx.pushOp(
SetObjectPropertyImpl.make(instOffset,
fieldIndices[init.fieldName.name]!, V.scopeFrameOffset),
Expand Down
14 changes: 9 additions & 5 deletions lib/src/eval/compiler/declaration/field.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/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/expression/expression.dart';
Expand All @@ -14,14 +15,14 @@ void compileFieldDeclaration(int fieldIndex, FieldDeclaration d,
final fieldName = field.name.lexeme;
if (d.isStatic) {
final initializer = field.initializer;
TypeRef? type;
final specifiedType = d.fields.type;
if (specifiedType != null) {
type = TypeRef.fromAnnotation(ctx, ctx.library, specifiedType);
}
if (initializer != null) {
final pos = beginMethod(ctx, field, field.offset, '$fieldName*i');
ctx.beginAllocScope();
TypeRef? type;
final specifiedType = d.fields.type;
if (specifiedType != null) {
type = TypeRef.fromAnnotation(ctx, ctx.library, specifiedType);
}
var V = compileExpression(initializer, ctx, type);
if (type != null) {
if (!V.type.isAssignableTo(ctx, type)) {
Expand All @@ -47,6 +48,9 @@ void compileFieldDeclaration(int fieldIndex, FieldDeclaration d,
ctx.runtimeGlobalInitializerMap[_index] = pos;
ctx.pushOp(Return.make(V.scopeFrameOffset), Return.LEN);
ctx.endAllocScope(popValues: false);
} else {
ctx.topLevelVariableInferredTypes[ctx.library]![
'$parentName.$fieldName'] = type ?? CoreTypes.dynamic.ref(ctx);
}
} else {
final pos = beginMethod(ctx, d, d.offset, '$parentName.$fieldName (get)');
Expand Down
3 changes: 2 additions & 1 deletion lib/src/eval/compiler/expression/assignment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import 'package:dart_eval/src/eval/compiler/variable.dart';
Variable compileAssignmentExpression(
AssignmentExpression e, CompilerContext ctx) {
final L = compileExpressionAsReference(e.leftHandSide, ctx);
final R = compileExpression(e.rightHandSide, ctx);
final R =
compileExpression(e.rightHandSide, ctx, L.resolveType(ctx, forSet: true));

if (e.operator.type == TokenType.EQ) {
final set =
Expand Down
4 changes: 2 additions & 2 deletions lib/src/eval/compiler/expression/expression.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Variable compileExpression(Expression e, CompilerContext ctx,
} else if (e is Identifier) {
return compileIdentifier(e, ctx);
} else if (e is MethodInvocation) {
return compileMethodInvocation(ctx, e);
return compileMethodInvocation(ctx, e, bound: bound);
} else if (e is BinaryExpression) {
return compileBinaryExpression(ctx, e, bound);
} else if (e is PrefixExpression) {
Expand All @@ -51,7 +51,7 @@ Variable compileExpression(Expression e, CompilerContext ctx,
} else if (e is IndexExpression) {
return compileIndexExpression(e, ctx);
} else if (e is FunctionExpression) {
return compileFunctionExpression(e, ctx);
return compileFunctionExpression(e, ctx, bound);
} else if (e is FunctionExpressionInvocation) {
return compileFunctionExpressionInvocation(e, ctx);
} else if (e is AwaitExpression) {
Expand Down
61 changes: 52 additions & 9 deletions lib/src/eval/compiler/expression/function.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:collection/collection.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/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/helpers/fpl.dart';
import 'package:dart_eval/src/eval/compiler/helpers/return.dart';
import 'package:dart_eval/src/eval/compiler/model/function_type.dart';
import 'package:dart_eval/src/eval/compiler/offset_tracker.dart';
import 'package:dart_eval/src/eval/compiler/scope.dart';
import 'package:dart_eval/src/eval/compiler/statement/block.dart';
Expand All @@ -17,7 +19,8 @@ import 'package:dart_eval/src/eval/runtime/runtime.dart';

enum CallingConvention { static, dynamic }

Variable compileFunctionExpression(FunctionExpression e, CompilerContext ctx) {
Variable compileFunctionExpression(FunctionExpression e, CompilerContext ctx,
[TypeRef? bound]) {
final jumpOver = ctx.pushOp(JumpConstant.make(-1), JumpConstant.LEN);

final fnOffset = ctx.out.length;
Expand All @@ -38,7 +41,24 @@ Variable compileFunctionExpression(FunctionExpression e, CompilerContext ctx) {
final resolvedParams = resolveFPLDefaults(ctx, e.parameters, false,
allowUnboxed: false, sortNamed: true);

var i = 1;
List<FunctionFormalParameter> boundNormalParams = [];
List<FunctionFormalParameter> boundOptionalParams = [];
List<FunctionFormalParameter> boundNamedParams = [];
if (bound != null) {
final functionType = bound.functionType;
if (functionType != null) {
boundNormalParams = functionType.normalParameters;
boundOptionalParams = functionType.optionalParameters;
boundNamedParams = functionType.namedParameters.entries
.map((e) => e.value)
.sorted((a, b) => a.name!.compareTo(b.name!));
}
}

final boundPositionalParams = [...boundNormalParams, ...boundOptionalParams];
final inorderBoundParams = [...boundPositionalParams, ...boundNamedParams];

var i = 0;

for (final param in resolvedParams) {
final p = param.parameter;
Expand All @@ -48,8 +68,13 @@ Variable compileFunctionExpression(FunctionExpression e, CompilerContext ctx) {
var type = CoreTypes.dynamic.ref(ctx);
if (p.type != null) {
type = TypeRef.fromAnnotation(ctx, ctx.library, p.type!);
} else if (i < inorderBoundParams.length) {
final fType = inorderBoundParams[i].type;
if (fType.type != null) {
type = fType.type!;
}
}
vRep = Variable(i, type.copyWith(boxed: true))..name = p.name!.lexeme;
vRep = Variable(i + 1, type.copyWith(boxed: true))..name = p.name!.lexeme;

ctx.setLocal(vRep.name!, vRep);

Expand Down Expand Up @@ -106,9 +131,18 @@ Variable compileFunctionExpression(FunctionExpression e, CompilerContext ctx) {
? a
: (a as DefaultFormalParameter).parameter)
.cast<SimpleFormalParameter>()
.map((a) => a.type == null
? CoreTypes.dynamic.ref(ctx)
: TypeRef.fromAnnotation(ctx, ctx.library, a.type!))
.mapIndexed((i, a) {
if (a.type != null) {
return TypeRef.fromAnnotation(ctx, ctx.library, a.type!);
}
if (i < boundPositionalParams.length) {
final fType = boundPositionalParams[i].type;
if (fType.type != null) {
return fType.type!;
}
}
return CoreTypes.dynamic.ref(ctx);
})
.map((t) => t.toRuntimeType(ctx))
.map((rt) => rt.toJson())
.toList();
Expand All @@ -123,9 +157,18 @@ Variable compileFunctionExpression(FunctionExpression e, CompilerContext ctx) {
final sortedNamedArgTypes = sortedNamedArgs
.map((e) => e is DefaultFormalParameter ? e.parameter : e)
.cast<SimpleFormalParameter>()
.map((a) => a.type == null
? CoreTypes.dynamic.ref(ctx)
: TypeRef.fromAnnotation(ctx, ctx.library, a.type!))
.mapIndexed((i, a) {
if (a.type != null) {
return TypeRef.fromAnnotation(ctx, ctx.library, a.type!);
}
if (i < boundNamedParams.length) {
final fType = boundNamedParams[i].type;
if (fType.type != null) {
return fType.type!;
}
}
return CoreTypes.dynamic.ref(ctx);
})
.map((t) => t.toRuntimeType(ctx))
.map((rt) => rt.toJson())
.toList();
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/compiler/expression/method_invocation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import 'expression.dart';
import 'identifier.dart';

Variable compileMethodInvocation(CompilerContext ctx, MethodInvocation e,
{Variable? cascadeTarget}) {
{TypeRef? bound, Variable? cascadeTarget}) {
Variable? L = cascadeTarget;
var isPrefix = false;
if (e.target != null && cascadeTarget == null) {
Expand Down
7 changes: 5 additions & 2 deletions lib/src/eval/compiler/expression/string_interpolation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ Variable compileStringInterpolation(
Variable? build;
for (final element in str.elements) {
if (element is InterpolationString) {
final _el = BuiltinValue(stringval: element.value).push(ctx);
build = build == null ? _el : build.invoke(ctx, '+', [_el]).result;
final sval = element.value;
if (sval.isNotEmpty) {
final _el = BuiltinValue(stringval: element.value).push(ctx);
build = build == null ? _el : build.invoke(ctx, '+', [_el]).result;
}
} else if (element is InterpolationExpression) {
final V = compileExpression(element.expression, ctx);
Variable vStr;
Expand Down
95 changes: 95 additions & 0 deletions lib/src/eval/compiler/model/function_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/src/eval/compiler/context.dart';
import 'package:dart_eval/src/eval/compiler/type.dart';

/// Represents either a real [TypeRef] or an unresolved type name e.g. <T>
class FunctionTypeAnnotation {
final TypeRef? type;
final String? name;

const FunctionTypeAnnotation.type(this.type) : name = null;

const FunctionTypeAnnotation.name(this.name) : type = null;

factory FunctionTypeAnnotation.fromBridgeAnnotation(
CompilerContext ctx, BridgeTypeAnnotation annotation) {
final _type = annotation.type;
if (_type.ref != null) {
return FunctionTypeAnnotation.name(_type.ref!);
} else {
return FunctionTypeAnnotation.type(
TypeRef.fromBridgeAnnotation(ctx, annotation));
}
}

factory FunctionTypeAnnotation.fromBridgeTypeRef(
CompilerContext ctx, BridgeTypeRef ref) {
return FunctionTypeAnnotation.type(TypeRef.fromBridgeTypeRef(ctx, ref));
}
}

class FunctionFormalParameter {
final String? name;
final FunctionTypeAnnotation type;
final bool isRequired;

const FunctionFormalParameter(this.name, this.type, this.isRequired);
}

class FunctionGenericParam {
final String name;
final FunctionTypeAnnotation? bound;

const FunctionGenericParam(this.name, {this.bound});
}

class EvalFunctionType {
final List<FunctionFormalParameter> normalParameters;
final List<FunctionFormalParameter> optionalParameters;
final Map<String, FunctionFormalParameter> namedParameters;
final FunctionTypeAnnotation returnType;
final List<FunctionGenericParam> generics;

const EvalFunctionType(this.normalParameters, this.optionalParameters,
this.namedParameters, this.returnType, this.generics);

factory EvalFunctionType.fromBridgeFunctionDef(
CompilerContext ctx, BridgeFunctionDef def) {
final fReturnType =
FunctionTypeAnnotation.fromBridgeAnnotation(ctx, def.returns);

final fNormalParameters = <FunctionFormalParameter>[];
final fOptionalParameters = <FunctionFormalParameter>[];
final fNamedParameters = <String, FunctionFormalParameter>{};

for (final param in def.params) {
final fType =
FunctionTypeAnnotation.fromBridgeAnnotation(ctx, param.type);
final fParam =
FunctionFormalParameter(param.name, fType, !param.optional);
if (param.optional) {
fOptionalParameters.add(fParam);
} else {
fNormalParameters.add(fParam);
}
}

for (final param in def.namedParams) {
final fType =
FunctionTypeAnnotation.fromBridgeAnnotation(ctx, param.type);
final fParam =
FunctionFormalParameter(param.name, fType, !param.optional);
fNamedParameters[param.name] = fParam;
}

final fGenerics = def.generics.entries.map((entry) => FunctionGenericParam(
entry.key,
bound: entry.value.$extends != null
? FunctionTypeAnnotation.fromBridgeTypeRef(
ctx, entry.value.$extends!)
: null));

return EvalFunctionType(fNormalParameters, fOptionalParameters,
fNamedParameters, fReturnType, fGenerics.toList());
}
}

0 comments on commit 0d6861a

Please sign in to comment.