diff --git a/lib/src/eval/bindgen/bindgen.dart b/lib/src/eval/bindgen/bindgen.dart index e853ed0..29aec87 100644 --- a/lib/src/eval/bindgen/bindgen.dart +++ b/lib/src/eval/bindgen/bindgen.dart @@ -10,6 +10,7 @@ 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/enum.dart'; +import 'package:dart_eval/src/eval/bindgen/function.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'; @@ -29,6 +30,7 @@ class Bindgen implements BridgeDeclarationRegistry { final _exportedLibMappings = {}; final List<({String file, String uri, String name})> registerClasses = []; final List<({String file, String uri, String name})> registerEnums = []; + final List<({String file, String uri, String name})> registerFunctions = []; AnalysisContextCollection? _contextCollection; @@ -155,15 +157,26 @@ class Bindgen implements BridgeDeclarationRegistry { final units = analysisResult.unit.declarations; - final resolved = units - .where((declaration) => declaration.declaredFragment != null) - .map((declaration) => declaration is ClassDeclaration - ? _$instance(ctx, declaration.declaredFragment!.element) - : (declaration is EnumDeclaration - ? _$enum(ctx, declaration.declaredFragment!.element) - : null)) - .toList() - .nonNulls; + final Iterable resolved; + try { + resolved = units + .where((declaration) => declaration.declaredFragment != null) + .map((declaration) { + if (declaration is ClassDeclaration) { + return _$instance(ctx, declaration.declaredFragment!.element); + } else if (declaration is EnumDeclaration) { + return _$enum(ctx, declaration.declaredFragment!.element); + } else if (declaration is FunctionDeclaration) { + return _$function(ctx, declaration.declaredFragment!.element); + } + return null; + }) + .toList() + .nonNulls; + } on Error { + print('Failed to resolve $filePath:'); + rethrow; + } if (resolved.isEmpty) { return null; @@ -182,7 +195,7 @@ class Bindgen implements BridgeDeclarationRegistry { } ({bool process, bool isBridge}) _shouldProcess( - BindgenContext ctx, TypeDefiningElement2 element) { + BindgenContext ctx, Annotatable element) { final metadata = element.metadata2; final bindAnno = metadata.annotations .firstWhereOrNull((element) => element.element2?.displayName == 'Bind'); @@ -195,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) { + if (override != null && !override.isNull && element is Element2) { final overrideUri = override.toStringValue(); if (overrideUri != null) { - ctx.libOverrides[element.name3!] = overrideUri; + ctx.libOverrides[(element as Element2).name3!] = overrideUri; } } @@ -310,6 +323,31 @@ class \$${element.name3} implements \$Instance { '''; } + String? _$function(BindgenContext ctx, ExecutableElement2 element) { + if (element is! TopLevelFunctionElement) return null; + final (:process, :isBridge) = _shouldProcess(ctx, element); + if (!process) { + return null; + } + + registerFunctions.add(( + file: ctx.filename, + uri: ctx.libOverrides[element.name3!] ?? ctx.uri, + name: element.name3!, + )); + + return ''' +/// dart_eval function wrapper binding for [${element.name3}] +class \$${element.name3}Fn implements EvalCallable { + const \$${element.name3}Fn(); + + ${bindConfigureFunctionForRuntime(ctx, element)} + ${bindFunctionDeclaration(ctx, element)} + ${$function(ctx, element)} +} +'''; + } + String $superclassWrapper(BindgenContext ctx, InterfaceElement2 element) { final supertype = element.supertype; final objectWrapper = '\$Object(\$value)'; diff --git a/lib/src/eval/bindgen/bridge_declaration.dart b/lib/src/eval/bindgen/bridge_declaration.dart index e336dc6..586be4f 100644 --- a/lib/src/eval/bindgen/bridge_declaration.dart +++ b/lib/src/eval/bindgen/bridge_declaration.dart @@ -19,6 +19,17 @@ String bindBridgeType(BindgenContext ctx, InterfaceElement2 element) { '''; } +String bindFunctionDeclaration(BindgenContext ctx, TopLevelFunctionElement element) { + final uri = ctx.libOverrides[element.name3] ?? ctx.uri; + return ''' + static const \$declaration = BridgeFunctionDeclaration( + '$uri', + '${element.name3!.replaceAll(r'$', r'\$')}', + ${bridgeFunctionDef(ctx, function: element)} + ); +'''; +} + String? bindBridgeDeclaration(BindgenContext ctx, InterfaceElement2 element, {bool isBridge = false}) { if (element is ClassElement2 && element.constructors2.isEmpty) { @@ -185,14 +196,20 @@ String bridgeConstructorDef(BindgenContext ctx, '''; } -String bridgeMethodDef(BindgenContext ctx, {required MethodElement2 method}) { +String bridgeFunctionDef(BindgenContext ctx, {required ExecutableElement2 function}) { return ''' - '${method.name3}': BridgeMethodDef( BridgeFunctionDef( - returns: ${bridgeTypeAnnotationFrom(ctx, method.returnType)}, - namedParams: [${namedParameters(ctx, element: method)}], - params: [${positionalParameters(ctx, element: method)}], + returns: ${bridgeTypeAnnotationFrom(ctx, function.returnType)}, + namedParams: [${namedParameters(ctx, element: function)}], + params: [${positionalParameters(ctx, element: function)}], ), +'''; +} + +String bridgeMethodDef(BindgenContext ctx, {required MethodElement2 method}) { + return ''' + '${method.name3}': BridgeMethodDef( + ${bridgeFunctionDef(ctx, function: method)} ${method.isStatic ? 'isStatic: true,' : ''} ), '''; @@ -202,11 +219,7 @@ String bridgeGetterDef(BindgenContext ctx, {required PropertyAccessorElement2 getter}) { return ''' '${getter.name3}': BridgeMethodDef( - BridgeFunctionDef( - returns: ${bridgeTypeAnnotationFrom(ctx, getter.returnType)}, - namedParams: [${namedParameters(ctx, element: getter)}], - params: [${positionalParameters(ctx, element: getter)}], - ), + ${bridgeFunctionDef(ctx, function: getter)} ${getter.isStatic ? 'isStatic: true,' : ''} ), '''; @@ -216,11 +229,7 @@ String bridgeSetterDef(BindgenContext ctx, {required PropertyAccessorElement2 setter}) { return ''' '${setter.name3}': BridgeMethodDef( - BridgeFunctionDef( - returns: ${bridgeTypeAnnotationFrom(ctx, setter.returnType)}, - namedParams: [${namedParameters(ctx, element: setter)}], - params: [${positionalParameters(ctx, element: setter)}], - ), + ${bridgeFunctionDef(ctx, function: setter)} ${setter.isStatic ? 'isStatic: true,' : ''} ), '''; diff --git a/lib/src/eval/bindgen/configure.dart b/lib/src/eval/bindgen/configure.dart index 10e6fc7..8fbe630 100644 --- a/lib/src/eval/bindgen/configure.dart +++ b/lib/src/eval/bindgen/configure.dart @@ -22,6 +22,16 @@ static void configureForRuntime(Runtime runtime) { } '''; +String bindConfigureFunctionForRuntime( + BindgenContext ctx, TopLevelFunctionElement element) { + final uri = ctx.libOverrides[element.name3] ?? ctx.uri; + return ''' +static void configureForRuntime(Runtime runtime) { + return runtime.registerBridgeFunc('$uri', '${element.name3!.replaceAll(r'$', r'\$')}', const \$${element.name3}Fn().call); +} +'''; +} + String enumValuesForRuntime(BindgenContext ctx, EnumElement2 element) { final uri = ctx.libOverrides[element.name3] ?? ctx.uri; return ''' diff --git a/lib/src/eval/bindgen/function.dart b/lib/src/eval/bindgen/function.dart new file mode 100644 index 0000000..05b8bac --- /dev/null +++ b/lib/src/eval/bindgen/function.dart @@ -0,0 +1,16 @@ +import 'package:analyzer/dart/element/element2.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:dart_eval/src/eval/bindgen/context.dart'; +import 'package:dart_eval/src/eval/bindgen/parameters.dart'; +import 'package:dart_eval/src/eval/bindgen/type.dart'; + +String $function(BindgenContext ctx, ExecutableElement2 element) { + final returnsValue = + element.returnType is! VoidType && !element.returnType.isDartCoreNull; + return ''' + @override + \$Value? call(Runtime runtime, \$Value? target, List<\$Value?> args) { + ${returnsValue ? 'final result = ' : ''}${element.displayName}(${argumentAccessors(ctx, element.formalParameters).join(', ')}); + return ${wrapVar(ctx, element.returnType, 'result')}; + }'''; +} \ No newline at end of file diff --git a/lib/src/eval/cli/bind.dart b/lib/src/eval/cli/bind.dart index 94d9479..b3926d0 100644 --- a/lib/src/eval/cli/bind.dart +++ b/lib/src/eval/cli/bind.dart @@ -138,12 +138,14 @@ class ${packageName.toPascalCase()}Plugin implements EvalPlugin { void configureForCompile(BridgeDeclarationRegistry registry) { ${bindgen.registerClasses.map((e) => 'registry.defineBridgeClass(\$${e.name}.\$declaration);').join('\n')} ${bindgen.registerEnums.map((e) => 'registry.defineBridgeEnum(\$${e.name}.\$declaration);').join('\n')} + ${bindgen.registerFunctions.map((e) => 'registry.defineBridgeTopLevelFunction(\$${e.name}Fn.\$declaration);').join('\n')} } @override void configureForRuntime(Runtime runtime) { ${bindgen.registerClasses.map((e) => '\$${e.name}.configureForRuntime(runtime);').join('\n')} ${bindgen.registerEnums.map((e) => '\$${e.name}.configureForRuntime(runtime);').join('\n')} + ${bindgen.registerFunctions.map((e) => '\$${e.name}Fn.configureForRuntime(runtime);').join('\n')} } } ''';