Skip to content

Commit

Permalink
WIP bindgen implicit supers
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanblake4 committed Nov 17, 2024
1 parent bc1d432 commit 46df712
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 89 deletions.
89 changes: 27 additions & 62 deletions lib/src/eval/bindgen/bindgen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import 'package:collection/collection.dart';
import 'package:dart_eval/src/eval/bindgen/bridge_declaration.dart';
import 'package:dart_eval/src/eval/bindgen/configure.dart';
import 'package:dart_eval/src/eval/bindgen/context.dart';
import 'package:dart_eval/src/eval/bindgen/errors.dart';
import 'package:dart_eval/src/eval/bindgen/methods.dart';
import 'package:dart_eval/src/eval/bindgen/properties.dart';
import 'package:dart_eval/src/eval/bindgen/statics.dart';
import 'package:dart_eval/src/eval/bindgen/type.dart';
import 'dart:io' as io;
Expand Down Expand Up @@ -104,8 +106,11 @@ class Bindgen {
if (bindAnno == null) {
return null;
}
final override =
bindAnno.computeConstantValue()?.getField('overrideLibrary');
final bindAnnoValue = bindAnno.computeConstantValue();
final implicitSupers =
bindAnnoValue?.getField('implicitSupers')?.toBoolValue() ?? false;
ctx.implicitSupers = implicitSupers;
final override = bindAnnoValue?.getField('overrideLibrary');
if (override != null && !override.isNull) {
final overrideUri = override.toStringValue();
if (overrideUri != null) {
Expand Down Expand Up @@ -141,9 +146,26 @@ ${$setProperty(ctx, element)}
String $superclassWrapper(BindgenContext ctx, ClassElement element) {
final supertype = element.supertype;
final objectWrapper = '\$Object(\$value)';
return supertype == null
? objectWrapper
: wrapType(ctx, supertype, '\$value') ?? objectWrapper;
if (supertype == null || ctx.implicitSupers) {
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.name},'
' falling back to \$Object. Add a @Bind annotation to $supertype'
' or set `implicitSupers: true`');
ctx.imports.add('package:dart_eval/stdlib/core.dart');
return objectWrapper;
}
return narrowWrapper;
}

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

String $wrap(BindgenContext ctx, ClassElement element) {
Expand All @@ -160,61 +182,4 @@ ${$setProperty(ctx, element)}
\$${element.name}.wrap(this.\$value) : _superclass = ${$superclassWrapper(ctx, element)};
''';
}

String $getProperty(BindgenContext ctx, ClassElement element) {
return '''
@override
\$Value? \$getProperty(Runtime runtime, String identifier) {
${propertyGetters(ctx, element)}
return _superclass.\$getProperty(runtime, identifier);
}
''';
}

String propertyGetters(BindgenContext ctx, ClassElement element) {
final _getters = element.accessors.where((accessor) =>
accessor.isGetter && !accessor.isStatic && !accessor.isPrivate);
final _methods = element.methods.where((method) => !method.isStatic);
if (_getters.isEmpty && _methods.isEmpty) {
return '';
}
return 'switch (identifier) {\n' + _getters.map((e) => '''
case '${e.displayName}':
final _${e.displayName} = \$value.${e.displayName};
return ${wrapVar(ctx, e.type.returnType, '_${e.displayName}', metadata: e.nonSynthetic.metadata)};
''').join('\n') + _methods.map((e) => '''
case '${e.displayName}':
return __${e.displayName};
''').join('\n') + '\n' + '}';
}

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

String $setProperty(BindgenContext ctx, ClassElement element) {
return '''
@override
void \$setProperty(Runtime runtime, String identifier, \$Value value) {
${propertySetters(ctx, element)}
return _superclass.\$setProperty(runtime, identifier, value);
}
''';
}

String propertySetters(BindgenContext ctx, ClassElement element) {
final _setters = element.accessors.where((element) =>
element.isSetter && !element.isStatic && !element.isPrivate);
if (_setters.isEmpty) {
return '';
}
return 'switch (identifier) {\n' + _setters.map((e) => '''
case '${e.displayName}':
\$value.${e.displayName} = value.\$value;
return;
''').join('\n') + '\n' + '}';
}
}
73 changes: 49 additions & 24 deletions lib/src/eval/bindgen/bridge_declaration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ String bindTypeSpec(BindgenContext ctx, ClassElement element) {
return '''
static const \$spec = BridgeTypeSpec(
'${uri}',
'${element.name}',
'${element.name.replaceAll(r'$', r'\$')}',
);
''';
}
Expand All @@ -29,7 +29,7 @@ String? bindBridgeDeclaration(BindgenContext ctx, ClassElement element) {
if (typeParams.isNotEmpty) {
genericsStr = '''\ngenerics: {
${typeParams.map((e) {
final boundStr = e.bound != null
final boundStr = e.bound != null && !ctx.implicitSupers
? '\$extends: ${bridgeTypeRefFromType(ctx, e.bound!)}'
: '';
return '\'${e.name}\': BridgeGenericParam($boundStr)';
Expand Down Expand Up @@ -85,40 +85,65 @@ String constructors(BindgenContext ctx, ClassElement element) {
}

String methods(BindgenContext ctx, ClassElement element) {
return element.methods.map((e) => bridgeMethodDef(ctx, method: e)).join('\n');
final methods = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final m in s.element.methods.where((m) => !m.isStatic)) m.name: m,
for (final m in element.methods) m.name: m
};
return methods.values
.where(
(m) => !(const ['==', 'toString', 'noSuchMethod'].contains(m.name)))
.map((m) => bridgeMethodDef(ctx, method: m))
.join('\n');
}

String getters(BindgenContext ctx, ClassElement element) {
var getters = element.accessors
.where((element) => element.isGetter && !element.isSynthetic)
.map((element) => element.displayName)
.toList();

return getters
.map((e) => bridgeGetterDef(ctx, getter: element.getGetter(e)!))
.join('\n');
final accessors = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final a in s.element.accessors.where((a) => !a.isStatic))
a.name: a,
for (final a in element.accessors) a.name: a
};

var getters = accessors.values
.where((m) => !(const ['hashCode', 'runtimeType'].contains(m.name)))
.where((element) => element.isGetter && !element.isSynthetic);

return getters.map((e) => bridgeGetterDef(ctx, getter: e)).join('\n');
}

String setters(BindgenContext ctx, ClassElement element) {
var setters = element.accessors
.where((element) => element.isSetter && !element.isSynthetic)
.map((element) => element.displayName)
.toList();

return setters
.map((e) => bridgeSetterDef(ctx, setter: element.getSetter(e)!))
.join('\n');
final accessors = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final a in s.element.accessors.where((a) => !a.isStatic))
a.name: a,
for (final a in element.accessors) a.name: a
};

var setters = accessors.values
.where((element) => element.isSetter && !element.isSynthetic);

return setters.map((e) => bridgeSetterDef(ctx, setter: e)).join('\n');
}

String fields(BindgenContext ctx, ClassElement element) {
var fields = element.fields
.where((element) => !element.isSynthetic)
.map((element) => element.displayName)
.toList();
final allFields = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
if (s is ClassElement)
for (final f in (s as ClassElement).fields.where((f) => !f.isStatic))
f.name: f,
for (final f in element.fields) f.name: f
};

final fields = allFields.values.where((element) => !element.isSynthetic);

return fields
.map(
(e) => bridgeFieldDef(ctx, field: element.getField(e)!),
(e) => bridgeFieldDef(ctx, field: e),
)
.join('\n');
}
Expand Down
1 change: 1 addition & 0 deletions lib/src/eval/bindgen/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class BindgenContext {
final bool wrap;
final bool all;
final Map<String, String> libOverrides = {};
bool implicitSupers = false;

BindgenContext(this.uri, {required this.wrap, required this.all});
}
11 changes: 10 additions & 1 deletion lib/src/eval/bindgen/methods.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ import 'package:dart_eval/src/eval/bindgen/permission.dart';
import 'package:dart_eval/src/eval/bindgen/type.dart';

String $methods(BindgenContext ctx, ClassElement element) {
return element.methods
final methods = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
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.name)))
.map((e) {
final returnsValue =
e.returnType is! VoidType && !e.returnType.isDartCoreNull;
Expand Down
77 changes: 77 additions & 0 deletions lib/src/eval/bindgen/properties.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:dart_eval/src/eval/bindgen/context.dart';
import 'package:dart_eval/src/eval/bindgen/type.dart';

String $getProperty(BindgenContext ctx, ClassElement element) {
return '''
@override
\$Value? \$getProperty(Runtime runtime, String identifier) {
${propertyGetters(ctx, element)}
return _superclass.\$getProperty(runtime, identifier);
}
''';
}

String propertyGetters(BindgenContext ctx, ClassElement element) {
final accessors = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final a in s.element.accessors) a.name: a,
for (final a in element.accessors) a.name: a
};
final methods = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final m in s.element.methods) m.name: m,
for (final m in element.methods) m.name: m
};
final _getters = accessors.values
.where((accessor) =>
accessor.isGetter && !accessor.isStatic && !accessor.isPrivate)
.where((a) => !(const ['hashCode', 'runtimeType'].contains(a.name)));

final _methods = methods.values
.where((method) => !method.isPrivate && !method.isStatic)
.where(
(m) => !(const ['==', 'toString', 'noSuchMethod'].contains(m.name)));
if (_getters.isEmpty && _methods.isEmpty) {
return '';
}
return 'switch (identifier) {\n' + _getters.map((e) => '''
case '${e.displayName}':
final _${e.displayName} = \$value.${e.displayName};
return ${wrapVar(ctx, e.type.returnType, '_${e.displayName}', metadata: e.nonSynthetic.metadata)};
''').join('\n') + _methods.map((e) => '''
case '${e.displayName}':
return __${e.displayName};
''').join('\n') + '\n' + '}';
}

String $setProperty(BindgenContext ctx, ClassElement element) {
return '''
@override
void \$setProperty(Runtime runtime, String identifier, \$Value value) {
${propertySetters(ctx, element)}
return _superclass.\$setProperty(runtime, identifier, value);
}
''';
}

String propertySetters(BindgenContext ctx, ClassElement element) {
final accessors = {
if (ctx.implicitSupers)
for (var s in element.allSupertypes)
for (final a in s.element.accessors) a.name: a,
for (final a in element.accessors) a.name: a
};
final _setters = accessors.values.where(
(element) => element.isSetter && !element.isStatic && !element.isPrivate);
if (_setters.isEmpty) {
return '';
}
return 'switch (identifier) {\n' + _setters.map((e) => '''
case '${e.displayName}':
\$value.${e.displayName} = value.\$value;
return;
''').join('\n') + '\n' + '}';
}
2 changes: 1 addition & 1 deletion lib/src/eval/bindgen/statics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:dart_eval/src/eval/bindgen/type.dart';

String $constructors(BindgenContext ctx, ClassElement element) {
return element.constructors
.where((cstr) => !cstr.isPrivate && !element.isAbstract)
.where((cstr) => !cstr.isPrivate)
.map((e) => _$constructor(ctx, element, e))
.join('\n');
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/bindgen/type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ String bridgeTypeSpecFrom(BindgenContext ctx, DartType type) {
final element = type.element!;
final lib = element.library!;
final uri = ctx.libOverrides[element.name] ?? lib.source.uri.toString();
return 'BridgeTypeSpec(\'${uri}\', \'${element.name}\')';
return 'BridgeTypeSpec(\'${uri}\', \'${element.name!.replaceAll(r'$', r'\$')}\')';
}

String? builtinTypeFrom(DartType type) {
Expand Down

0 comments on commit 46df712

Please sign in to comment.