Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.8.4
- Update analyzer to v8.

## 0.8.3
- Support for Dart 3.10
- Support for top level functions in the binding generator
Expand Down Expand Up @@ -481,4 +484,4 @@ with previous versions.
## 0.0.1

- Create parser and interpreter
- Add interop
- Add interop
94 changes: 47 additions & 47 deletions lib/src/eval/bindgen/bindgen.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:collection/collection.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
Expand Down Expand Up @@ -195,10 +195,10 @@ class Bindgen implements BridgeDeclarationRegistry {
}

({bool process, bool isBridge, bool alsoWrap}) _shouldProcess(
BindgenContext ctx, Annotatable element) {
final metadata = element.metadata2;
BindgenContext ctx, Element element) {
final metadata = element.metadata;
final bindAnno = metadata.annotations
.firstWhereOrNull((element) => element.element2?.displayName == 'Bind');
.firstWhereOrNull((element) => element.element?.displayName == 'Bind');
final bindAnnoValue = bindAnno?.computeConstantValue();

if (bindAnnoValue == null && !ctx.all) {
Expand All @@ -208,10 +208,10 @@ class Bindgen implements BridgeDeclarationRegistry {
bindAnnoValue?.getField('implicitSupers')?.toBoolValue() ?? false;
ctx.implicitSupers = implicitSupers;
final override = bindAnnoValue?.getField('overrideLibrary');
if (override != null && !override.isNull && element is Element2) {
if (override != null && !override.isNull) {
final overrideUri = override.toStringValue();
if (overrideUri != null) {
ctx.libOverrides[(element as Element2).name3!] = overrideUri;
ctx.libOverrides[element.name!] = overrideUri;
}
}

Expand All @@ -225,36 +225,36 @@ class Bindgen implements BridgeDeclarationRegistry {
);
}

String? _$instance(BindgenContext ctx, ClassElement2 element) {
String? _$instance(BindgenContext ctx, ClassElement element) {
final (:process, :isBridge, :alsoWrap) = _shouldProcess(ctx, element);
if (!process) {
return null;
}

if (isBridge && element.isSealed) {
throw CompileError(
'Cannot bind sealed class ${element.name3} as a bridge type. '
'Cannot bind sealed class ${element.name} as a bridge type. '
'Please remove the @Bind annotation, use a wrapper, or make the class non-sealed.');
}

registerClasses.add((
file: ctx.filename,
uri: ctx.libOverrides[element.name3!] ?? ctx.uri,
name: '${element.name3!}${isBridge ? '\$bridge' : ''}',
uri: ctx.libOverrides[element.name!] ?? ctx.uri,
name: '${element.name!}${isBridge ? '\$bridge' : ''}',
));

if (isBridge) {
String code = '''
/// dart_eval bridge binding for [${element.name3}]
class \$${element.name3}\$bridge extends ${element.name3} with \$Bridge<${element.name3}> {
/// dart_eval bridge binding for [${element.name}]
class \$${element.name}\$bridge extends ${element.name} with \$Bridge<${element.name}> {
${bindForwardedConstructors(ctx, element)}
/// Configure this class for use in a [Runtime]
${bindConfigureForRuntime(ctx, element, isBridge: true)}
/// Compile-time type specification of [\$${element.name3}\$bridge]
/// Compile-time type specification of [\$${element.name}\$bridge]
${bindTypeSpec(ctx, element)}
/// Compile-time type declaration of [\$${element.name3}\$bridge]
/// Compile-time type declaration of [\$${element.name}\$bridge]
${bindBridgeType(ctx, element)}
/// Compile-time class declaration of [\$${element.name3}]
/// Compile-time class declaration of [\$${element.name}]
${bindBridgeDeclaration(ctx, element, isBridge: true)}
${$constructors(ctx, element, isBridge: true)}
${$staticMethods(ctx, element)}
Expand All @@ -270,11 +270,11 @@ ${bindDecoratorMethods(ctx, element)}
if (alsoWrap) {
// Add a rudimentary wrapper, because you cannot wrap things in a bridge.
code += '''
/// dart_eval lightweight wrapper binding for [${element.name3}]
class \$${element.name3} implements \$Instance {
/// Compile-time type specification of [\$${element.name3}]
/// dart_eval lightweight wrapper binding for [${element.name}]
class \$${element.name} implements \$Instance {
/// Compile-time type specification of [\$${element.name}]
${bindTypeSpec(ctx, element)}
/// Compile-time type declaration of [\$${element.name3}]
/// Compile-time type declaration of [\$${element.name}]
${bindBridgeType(ctx, element)}
${$wrap(ctx, element)}
${$getRuntimeType(element)}
Expand All @@ -289,15 +289,15 @@ ${$setProperty(ctx, element)}
}

return '''
/// dart_eval wrapper binding for [${element.name3}]
class \$${element.name3} implements \$Instance {
/// dart_eval wrapper binding for [${element.name}]
class \$${element.name} implements \$Instance {
/// Configure this class for use in a [Runtime]
${bindConfigureForRuntime(ctx, element)}
/// Compile-time type specification of [\$${element.name3}]
/// Compile-time type specification of [\$${element.name}]
${bindTypeSpec(ctx, element)}
/// Compile-time type declaration of [\$${element.name3}]
/// Compile-time type declaration of [\$${element.name}]
${bindBridgeType(ctx, element)}
/// Compile-time class declaration of [\$${element.name3}]
/// Compile-time class declaration of [\$${element.name}]
${bindBridgeDeclaration(ctx, element)}
${$constructors(ctx, element)}
${$staticMethods(ctx, element)}
Expand All @@ -312,28 +312,28 @@ ${$setProperty(ctx, element)}
''';
}

String? _$enum(BindgenContext ctx, EnumElement2 element) {
String? _$enum(BindgenContext ctx, EnumElement element) {
final (:process, :isBridge, :alsoWrap) = _shouldProcess(ctx, element);
if (!process) {
return null;
}

registerEnums.add((
file: ctx.filename,
uri: ctx.libOverrides[element.name3!] ?? ctx.uri,
name: element.name3!,
uri: ctx.libOverrides[element.name!] ?? ctx.uri,
name: element.name!,
));

return '''
/// dart_eval enum wrapper binding for [${element.name3}]
class \$${element.name3} implements \$Instance {
/// dart_eval enum wrapper binding for [${element.name}]
class \$${element.name} implements \$Instance {
/// Configure this enum for use in a [Runtime]
${bindConfigureEnumForRuntime(ctx, element)}
/// Compile-time type specification of [\$${element.name3}]
/// Compile-time type specification of [\$${element.name}]
${bindTypeSpec(ctx, element)}
/// Compile-time type declaration of [\$${element.name3}]
/// Compile-time type declaration of [\$${element.name}]
${bindBridgeType(ctx, element)}
/// Compile-time class declaration of [\$${element.name3}]
/// Compile-time class declaration of [\$${element.name}]
${bindBridgeDeclaration(ctx, element)}
${$enumValues(ctx, element)}
${$staticMethods(ctx, element)}
Expand All @@ -348,7 +348,7 @@ class \$${element.name3} implements \$Instance {
''';
}

String? _$function(BindgenContext ctx, ExecutableElement2 element) {
String? _$function(BindgenContext ctx, ExecutableElement element) {
if (element is! TopLevelFunctionElement) return null;
final (:process, :isBridge, :alsoWrap) = _shouldProcess(ctx, element);
if (!process) {
Expand All @@ -357,14 +357,14 @@ class \$${element.name3} implements \$Instance {

registerFunctions.add((
file: ctx.filename,
uri: ctx.libOverrides[element.name3!] ?? ctx.uri,
name: element.name3!,
uri: ctx.libOverrides[element.name!] ?? ctx.uri,
name: element.name!,
));

return '''
/// dart_eval function wrapper binding for [${element.name3}]
class \$${element.name3}Fn implements EvalCallable {
const \$${element.name3}Fn();
/// dart_eval function wrapper binding for [${element.name}]
class \$${element.name}Fn implements EvalCallable {
const \$${element.name}Fn();

${bindConfigureFunctionForRuntime(ctx, element)}
${bindFunctionDeclaration(ctx, element)}
Expand All @@ -373,16 +373,16 @@ class \$${element.name3}Fn implements EvalCallable {
''';
}

String $superclassWrapper(BindgenContext ctx, InterfaceElement2 element) {
String $superclassWrapper(BindgenContext ctx, InterfaceElement element) {
final supertype = element.supertype;
final objectWrapper = '\$Object(\$value)';
if (supertype == null || ctx.implicitSupers || element is EnumElement2) {
if (supertype == null || ctx.implicitSupers || element is EnumElement) {
ctx.imports.add('package:dart_eval/stdlib/core.dart');
return objectWrapper;
}
final narrowWrapper = wrapType(ctx, supertype, '\$value');
if (narrowWrapper == null) {
print('Warning: Could not wrap supertype $supertype of ${element.name3},'
print('Warning: Could not wrap supertype $supertype of ${element.name},'
' falling back to \$Object. Add a @Bind annotation to $supertype'
' or set `implicitSupers: true`');
ctx.imports.add('package:dart_eval/stdlib/core.dart');
Expand All @@ -391,25 +391,25 @@ class \$${element.name3}Fn implements EvalCallable {
return narrowWrapper;
}

String $getRuntimeType(InterfaceElement2 element) {
String $getRuntimeType(InterfaceElement element) {
return '''
@override
int \$getRuntimeType(Runtime runtime) => runtime.lookupType(\$spec);
''';
}

String $wrap(BindgenContext ctx, InterfaceElement2 element) {
String $wrap(BindgenContext ctx, InterfaceElement element) {
return '''
final \$Instance _superclass;

@override
final ${element.name3} \$value;
final ${element.name} \$value;

@override
${element.name3} get \$reified => \$value;
${element.name} get \$reified => \$value;

/// Wrap a [${element.name3}] in a [\$${element.name3}]
\$${element.name3}.wrap(this.\$value) : _superclass = ${$superclassWrapper(ctx, element)};
/// Wrap a [${element.name}] in a [\$${element.name}]
\$${element.name}.wrap(this.\$value) : _superclass = ${$superclassWrapper(ctx, element)};
''';
}
}
41 changes: 20 additions & 21 deletions lib/src/eval/bindgen/bridge.dart
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:dart_eval/src/eval/bindgen/context.dart';
import 'package:dart_eval/src/eval/bindgen/type.dart';

String bindForwardedConstructors(BindgenContext ctx, ClassElement2 element,
String bindForwardedConstructors(BindgenContext ctx, ClassElement element,
{bool isBridge = false}) {
return element.constructors2
return element.constructors
.where((cstr) => !cstr.isPrivate)
.map((e) => _$forwardedConstructor(ctx, element, e, isBridge: isBridge))
.join('\n');
}

String _$forwardedConstructor(
BindgenContext ctx, ClassElement2 element, ConstructorElement2 constructor,
BindgenContext ctx, ClassElement element, ConstructorElement constructor,
{bool isBridge = false}) {
final name = constructor.name3 ?? '';
final namedConstructor =
constructor.name3 != null && constructor.name3 != 'new'
? '.${constructor.name3}'
: '';
final name = constructor.name ?? '';
final namedConstructor = constructor.name != null && constructor.name != 'new'
? '.${constructor.name}'
: '';
final fullyQualifiedConstructorId =
'\$${element.name3}\$bridge$namedConstructor';
'\$${element.name}\$bridge$namedConstructor';

return '''
/// Forwarded constructor for [${element.name3}.$name]
/// Forwarded constructor for [${element.name}.$name]
$fullyQualifiedConstructorId(${parameterHeader(constructor.formalParameters, forConstructor: true)});
''';
}

String bindDecoratorMethods(BindgenContext ctx, ClassElement2 element) {
String bindDecoratorMethods(BindgenContext ctx, ClassElement element) {
final methods = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final m in s.element3.methods2) m.name3: m,
for (final m in element.methods2) m.name3: m
for (final m in s.element.methods) m.name: m,
for (final m in element.methods) m.name: m
};

return methods.values
.where((method) => !method.isPrivate && !method.isStatic)
.where(
(m) => !(const ['==', 'toString', 'noSuchMethod'].contains(m.name3)))
(m) => !(const ['==', 'toString', 'noSuchMethod'].contains(m.name)))
.map((e) {
final returnType = e.returnType;
final needsCast = returnType.isDartCoreList ||
Expand All @@ -53,18 +52,18 @@ String bindDecoratorMethods(BindgenContext ctx, ClassElement2 element) {
@override
$returnType ${e.displayName}(${parameterHeader(e.formalParameters)}) =>
${needsCast ? '(' : ''}\$_invoke('${e.displayName}', [
${e.formalParameters.map((p) => wrapVar(ctx, p.type, p.name3 ?? '')).join(', ')}
])${needsCast ? 'as ${returnType.element3!.name3}$q)$q.cast()' : ''};
${e.formalParameters.map((p) => wrapVar(ctx, p.type, p.name ?? '')).join(', ')}
])${needsCast ? 'as ${returnType.element!.name}$q)$q.cast()' : ''};
''';
}).join('\n');
}

String bindDecoratorProperties(BindgenContext ctx, ClassElement2 element) {
String bindDecoratorProperties(BindgenContext ctx, ClassElement element) {
final properties = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final p in s.element3.fields2) p.name3: p,
for (final p in element.fields2) p.name3: p
for (final p in s.element.fields) p.name: p,
for (final p in element.fields) p.name: p
};

return properties.values
Expand Down Expand Up @@ -109,7 +108,7 @@ String parameterHeader(List<FormalParameterElement> params,
}
}
paramBuffer.write(
param.name3 == null || param.name3!.isEmpty ? 'arg$i' : param.name3);
param.name == null || param.name!.isEmpty ? 'arg$i' : param.name);
if (i < params.length - 1) {
paramBuffer.write(', ');
}
Expand Down
Loading