44import io .avaje .prism .GeneratePrism ;
55import io .github .digitalsmile .annotation .ArenaType ;
66import io .github .digitalsmile .annotation .NativeMemory ;
7- import io .github .digitalsmile .annotation .NativeMemoryException ;
87import io .github .digitalsmile .annotation .NativeMemoryOptions ;
98import io .github .digitalsmile .annotation .function .ByAddress ;
109import io .github .digitalsmile .annotation .function .NativeManualFunction ;
1110import io .github .digitalsmile .annotation .function .Returns ;
12- import io .github .digitalsmile .annotation .structure . Enums ;
13- import io .github .digitalsmile .annotation .structure .Structs ;
14- import io .github .digitalsmile .annotation .structure .Unions ;
11+ import io .github .digitalsmile .annotation .library . NativeMemoryLibrary ;
12+ import io .github .digitalsmile .annotation .structure .* ;
13+ import io .github .digitalsmile .annotation .structure .Enum ;
1514import io .github .digitalsmile .annotation .types .interfaces .NativeMemoryLayout ;
1615import io .github .digitalsmile .composers .*;
1716import io .github .digitalsmile .functions .FunctionNode ;
2524import io .github .digitalsmile .headers .mapping .OriginalType ;
2625import io .github .digitalsmile .headers .model .NativeMemoryNode ;
2726import io .github .digitalsmile .headers .model .NodeType ;
27+ import io .github .digitalsmile .validation .NativeProcessorValidator ;
28+ import io .github .digitalsmile .validation .ValidationException ;
2829import org .openjdk .jextract .Declaration ;
2930import org .openjdk .jextract .JextractTool ;
3031import org .openjdk .jextract .Position ;
5051import java .util .regex .Pattern ;
5152import java .util .stream .Stream ;
5253
54+ import static java .util .Collections .emptyList ;
55+
5356@ GeneratePrism (NativeManualFunction .class )
5457@ SupportedSourceVersion (SourceVersion .RELEASE_22 )
5558public class NativeProcessor extends AbstractProcessor {
5659
60+ private NativeProcessorValidator validator ;
61+
62+
5763 @ Override
5864 public Set <String > getSupportedAnnotationTypes () {
59- return Set .of (NativeMemory .class .getName ());
65+ return Set .of (NativeMemory .class .getName (), NativeMemoryLibrary . class . getName (), NativeManualFunction . class . getName () );
6066 }
6167
6268
@@ -65,75 +71,105 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
6571 if (roundEnv .processingOver ()) {
6672 return true ;
6773 }
68- for (Element rootElement : roundEnv .getElementsAnnotatedWith (NativeMemory .class )) {
74+ this .validator = new NativeProcessorValidator (processingEnv .getMessager (), processingEnv .getElementUtils (), processingEnv .getTypeUtils ());
75+
76+ for (Element rootElement : roundEnv .getRootElements ()) {
77+ if (!rootElement .getKind ().isInterface ()) {
78+ continue ;
79+ }
80+ var nativeMemory = rootElement .getAnnotation (NativeMemory .class );
81+ var manualFunctionElements = rootElement .getEnclosedElements ().stream ().filter (p -> p .getAnnotation (NativeManualFunction .class ) != null ).toList ();
82+ var automaticFunctionElements = rootElement .getAnnotation (NativeMemoryLibrary .class );
83+ if (nativeMemory == null && manualFunctionElements .isEmpty () && automaticFunctionElements == null ) {
84+ continue ;
85+ }
86+
6987 try {
70- var nativeAnnotation = rootElement .getAnnotation (NativeMemory .class );
71- var nativeOptions = rootElement .getAnnotation (NativeMemoryOptions .class );
72- var headerFiles = nativeAnnotation .headers ();
88+ var parsed = Collections .<NativeMemoryNode >emptyList ();
7389 var packageName = processingEnv .getElementUtils ().getPackageOf (rootElement ).getQualifiedName ().toString ();
90+ var nativeOptions = rootElement .getAnnotation (NativeMemoryOptions .class );
7491 if (nativeOptions != null && !nativeOptions .packageName ().isEmpty ()) {
75- packageName = nativeOptions .packageName ();
92+ packageName = validator . validatePackageName ( nativeOptions .packageName () );
7693 }
7794 PackageName .setDefaultPackageName (packageName );
7895
79- var parsed = processHeaderFiles (rootElement , headerFiles , packageName , nativeOptions );
8096
81- List <Element > functionElements = new ArrayList <Element >(roundEnv .getElementsAnnotatedWith (NativeManualFunction .class )).stream ()
82- .filter (f -> f .getEnclosingElement ().equals (rootElement )).toList ();
83- processFunctions (rootElement , functionElements , packageName , parsed , nativeOptions );
97+ if (nativeMemory != null ) {
98+ var headerFiles = nativeMemory .headers ();
99+ parsed = processHeaderFiles (rootElement , headerFiles , packageName , nativeOptions );
100+ }
101+
102+ List <Element > manualFunctions = new ArrayList <>();
103+ for (Element manualFunction : manualFunctionElements ) {
104+ validator .validateManualFunction (manualFunction );
105+ manualFunctions .add (manualFunction );
106+ }
107+
108+ if (automaticFunctionElements != null ) {
109+ validator .validateAutomaticFunctions (parsed , automaticFunctionElements );
110+ }
84111
112+ processFunctions (rootElement , manualFunctions , packageName , parsed , nativeOptions );
113+ } catch (ValidationException e ) {
114+ processingEnv .getMessager ().printMessage (Diagnostic .Kind .ERROR , e .getMessage (), e .getElement ());
85115 } catch (Throwable e ) {
86116 printStackTrace (e );
87- processingEnv .getMessager ().printMessage (Diagnostic .Kind .ERROR , e .getMessage ());
117+ processingEnv .getMessager ().printMessage (Diagnostic .Kind .ERROR , e .getMessage (), rootElement );
88118 }
89119 }
90120 return true ;
91121 }
92122
93- public record Type (String name , String javaName ) {
94- }
95-
96- private List <NativeMemoryNode > processHeaderFiles (Element element , String [] headerFiles , String packageName , NativeMemoryOptions options ) {
123+ private List <NativeMemoryNode > processHeaderFiles (Element element , String [] headerFiles , String packageName , NativeMemoryOptions options ) throws ValidationException {
97124 if (headerFiles .length == 0 ) {
98- return Collections . emptyList ();
125+ return emptyList ();
99126 }
100127 List <Path > headerPaths = getHeaderPaths (headerFiles );
101128 for (Path path : headerPaths ) {
102129 if (!path .toFile ().exists ()) {
103130 processingEnv .getMessager ().printMessage (Diagnostic .Kind .WARNING , "Cannot find header file '" + path + "'! Please, check file location" , element );
104- return Collections . emptyList ();
131+ return emptyList ();
105132 }
106133 }
107134
108135 var structsAnnotation = element .getAnnotation (Structs .class );
109136 var unionsAnnotation = element .getAnnotation (Unions .class );
110137 var enumsAnnotation = element .getAnnotation (Enums .class );
111138
112- List <Type > structs = null ;
139+ List <String > structs = null ;
113140 if (structsAnnotation != null ) {
114- structs = Arrays .stream (structsAnnotation .value ()).map (struct -> new Type (struct .name (), struct .javaName ())).toList ();
115- Arrays .stream (structsAnnotation .value ()).forEach (struct -> PrettyName .addName (struct .name (), struct .javaName ()));
141+ structs = Arrays .stream (structsAnnotation .value ()).map (Struct ::name ).toList ();
142+ for (Struct struct : structsAnnotation .value ()) {
143+ var javaName = validator .validateJavaName (struct .javaName ());
144+ PrettyName .addName (struct .name (), javaName );
145+ }
116146 }
117- List <Type > enums = null ;
147+ List <String > enums = null ;
118148 if (enumsAnnotation != null ) {
119- enums = Arrays .stream (enumsAnnotation .value ()).map (enoom -> new Type (enoom .name (), enoom .javaName ())).toList ();
120- Arrays .stream (enumsAnnotation .value ()).forEach (enoom -> PrettyName .addName (enoom .name (), enoom .javaName ()));
149+ enums = Arrays .stream (enumsAnnotation .value ()).map (Enum ::name ).toList ();
150+ for (Enum enoom : enumsAnnotation .value ()) {
151+ var javaName = validator .validateJavaName (enoom .javaName ());
152+ PrettyName .addName (enoom .name (), javaName );
153+ }
121154 }
122- List <Type > unions = null ;
155+ List <String > unions = null ;
123156 if (unionsAnnotation != null ) {
124- unions = Arrays .stream (unionsAnnotation .value ()).map (union -> new Type (union .name (), union .javaName ())).toList ();
125- Arrays .stream (unionsAnnotation .value ()).forEach (union -> PrettyName .addName (union .name (), union .javaName ()));
157+ unions = Arrays .stream (unionsAnnotation .value ()).map (Union ::name ).toList ();
158+ for (Union union : unionsAnnotation .value ()) {
159+ var javaName = validator .validateJavaName (union .javaName ());
160+ PrettyName .addName (union .name (), javaName );
161+ }
126162 }
127163
128164 var rootConstants = false ;
129165 var debug = false ;
130166 var systemHeader = false ;
131- List <String > includes = Collections . emptyList ();
132- List <String > systemIncludes = Collections . emptyList ();
167+ List <String > includes = emptyList ();
168+ List <String > systemIncludes = emptyList ();
133169 if (options != null ) {
134170 rootConstants = options .processRootConstants ();
135171 includes = getHeaderPaths (options .includes ()).stream ().map (p -> "-I" + p .toFile ().getAbsolutePath ()).toList ();
136- systemIncludes = Arrays . stream (options .systemIncludes ()).map (p -> "-isystem" + p ).toList ();
172+ systemIncludes = getHeaderPaths (options .systemIncludes ()).stream (). map (p -> "-isystem" + p . toFile (). getAbsolutePath () ).toList ();
137173 debug = options .debugMode ();
138174 systemHeader = options .systemHeader ();
139175 }
@@ -149,7 +185,7 @@ private List<NativeMemoryNode> processHeaderFiles(Element element, String[] head
149185 printStackTrace (e );
150186 }
151187 processingEnv .getMessager ().printMessage (Diagnostic .Kind .ERROR , e .getMessage ());
152- return Collections . emptyList ();
188+ return emptyList ();
153189 }
154190 }
155191 var parser = new Parser (packageName , processingEnv .getMessager (), processingEnv .getFiler ());
@@ -167,11 +203,11 @@ private List<NativeMemoryNode> processHeaderFiles(Element element, String[] head
167203 }
168204 return parsed ;
169205 } catch (Throwable e ) {
170- if (debug ) {
171- printStackTrace (e );
172- }
206+ // if (debug) {
207+ printStackTrace (e );
208+ // }
173209 processingEnv .getMessager ().printMessage (Diagnostic .Kind .ERROR , e .getMessage ());
174- return Collections . emptyList ();
210+ return emptyList ();
175211 }
176212 }
177213
@@ -191,9 +227,6 @@ private void processStructs(NativeMemoryNode node) {
191227 }
192228
193229 private void processEnums (NativeMemoryNode node , boolean rootConstants ) {
194- if (node .nodes ().isEmpty ()) {
195- return ;
196- }
197230 var name = node .getName ();
198231 if (node .getName ().endsWith ("_constants" )) {
199232 if (!rootConstants ) {
@@ -225,11 +258,6 @@ private void processFunctions(Element rootElement, List<Element> functionElement
225258 if (!(element instanceof ExecutableElement functionElement )) {
226259 continue ;
227260 }
228- var throwType = processingEnv .getElementUtils ().getTypeElement (NativeMemoryException .class .getName ()).asType ();
229- if (!functionElement .getThrownTypes ().contains (throwType )) {
230- processingEnv .getMessager ().printMessage (Diagnostic .Kind .ERROR , "Method '" + functionElement + "' must throw NativeMemoryException!" , functionElement );
231- break ;
232- }
233261 var instance = NativeManualFunctionPrism .getInstanceOn (functionElement );
234262 if (instance == null ) {
235263 break ;
@@ -240,7 +268,7 @@ private void processFunctions(Element rootElement, List<Element> functionElement
240268 break ;
241269 }
242270 List <ParameterNode > parameters = new ArrayList <>();
243- var returnType = OriginalType .of (processingEnv . getTypeUtils (). erasure ( functionElement .getReturnType () ));
271+ var returnType = OriginalType .of (functionElement .getReturnType ());
244272 var returnNode = flatten .stream ().filter (p -> PrettyName .getObjectName (p .getName ()).equals (returnType .typeName ())).findFirst ().orElse (null );
245273 if (returnNode == null ) {
246274 var type = functionElement .getReturnType ();
@@ -319,7 +347,7 @@ private OriginalType getBoundsOriginalType(ExecutableElement functionElement) {
319347
320348 private FileObject tmpFile ;
321349
322- private List <Path > getHeaderPaths (String ... headerFiles ) {
350+ private List <Path > getHeaderPaths (String ... headerFiles ) throws ValidationException {
323351 List <Path > paths = new ArrayList <>();
324352 for (String headerFile : headerFiles ) {
325353 var beginVariable = headerFile .indexOf ("${" );
@@ -334,6 +362,7 @@ private List<Path> getHeaderPaths(String... headerFiles) {
334362 continue ;
335363 }
336364 }
365+ headerFile = validator .validatePath (headerFile );
337366 Path headerPath ;
338367 if (headerFile .startsWith ("/" )) {
339368 headerPath = Path .of (headerFile );
0 commit comments