1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.ochafik.lang.jnaerator;
20
21 import java.io.File;
22
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.logging.Level;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40
41 import javax.tools.Diagnostic;
42 import javax.tools.DiagnosticCollector;
43 import javax.tools.JavaCompiler;
44 import javax.tools.JavaFileObject;
45 import javax.tools.Diagnostic.Kind;
46
47 import org.anarres.cpp.LexerException;
48 import org.antlr.runtime.RecognitionException;
49
50 import org.rococoa.cocoa.foundation.NSClass;
51
52 import com.ochafik.io.FileListUtils;
53 import com.ochafik.io.ReadText;
54 import com.ochafik.lang.compiler.CompilerUtils;
55 import com.ochafik.lang.compiler.MemoryFileManager;
56 import com.ochafik.lang.compiler.MemoryJavaFile;
57 import com.ochafik.lang.compiler.URLFileObject;
58 import com.ochafik.lang.jnaerator.JNAeratorCommandLineArgs.OptionDef;
59 import com.ochafik.lang.jnaerator.nativesupport.DllExport;
60 import com.ochafik.lang.jnaerator.parser.Arg;
61 import com.ochafik.lang.jnaerator.parser.Declaration;
62 import com.ochafik.lang.jnaerator.parser.Declarator;
63 import com.ochafik.lang.jnaerator.parser.Define;
64 import com.ochafik.lang.jnaerator.parser.Element;
65 import com.ochafik.lang.jnaerator.parser.Expression;
66 import com.ochafik.lang.jnaerator.parser.Function;
67 import com.ochafik.lang.jnaerator.parser.Identifier;
68 import com.ochafik.lang.jnaerator.parser.ModifiableElement;
69 import com.ochafik.lang.jnaerator.parser.Modifier;
70 import com.ochafik.lang.jnaerator.parser.ObjCppParser;
71 import com.ochafik.lang.jnaerator.parser.Scanner;
72 import com.ochafik.lang.jnaerator.parser.SourceFile;
73 import com.ochafik.lang.jnaerator.parser.Struct;
74 import com.ochafik.lang.jnaerator.parser.TypeRef;
75 import com.ochafik.lang.jnaerator.parser.VariablesDeclaration;
76 import com.ochafik.lang.jnaerator.parser.Expression.MemberRefStyle;
77 import com.ochafik.lang.jnaerator.parser.Identifier.QualificationSeparator;
78 import com.ochafik.lang.jnaerator.parser.Identifier.QualifiedIdentifier;
79 import com.ochafik.lang.jnaerator.parser.Identifier.SimpleIdentifier;
80 import com.ochafik.lang.jnaerator.parser.ModifierKind;
81 import com.ochafik.lang.jnaerator.parser.ObjCppLexer;
82 import com.ochafik.lang.jnaerator.parser.Struct.Type;
83 import com.ochafik.lang.jnaerator.runtime.LibraryExtractor;
84 import com.ochafik.lang.jnaerator.runtime.MangledFunctionMapper;
85 import com.ochafik.lang.jnaerator.runtime.Mangling;
86 import com.ochafik.lang.jnaerator.studio.JNAeratorStudio;
87 import com.ochafik.lang.jnaerator.studio.JNAeratorStudio.SyntaxException;
88 import com.ochafik.util.listenable.Adapter;
89 import com.ochafik.util.listenable.Pair;
90 import com.ochafik.util.string.RegexUtils;
91 import com.ochafik.util.string.StringUtils;
92 import com.sun.jna.Library;
93 import com.sun.jna.Native;
94 import com.sun.jna.NativeLibrary;
95 import com.sun.jna.Pointer;
96 import com.sun.jna.PointerType;
97 import java.io.BufferedReader;
98 import java.io.BufferedWriter;
99 import java.io.FileOutputStream;
100 import java.io.FileReader;
101 import java.io.StringReader;
102 import org.antlr.runtime.ANTLRReaderStream;
103 import org.antlr.runtime.CommonTokenStream;
104 import static com.ochafik.lang.jnaerator.parser.ElementsHelper.*;
105 import static com.ochafik.lang.jnaerator.nativesupport.NativeExportUtils.*;
106
107
108
109
110
111
112
113
114
115
116 public class JNAerator {
117
118 public static interface Feedback {
119 void setStatus(final String string);
120 void setFinished(File toOpen);
121 void setFinished(Throwable e);
122 void sourcesParsed(SourceFiles sourceFiles);
123 void wrappersGenerated(Result result);
124 }
125 private static Pattern argTokenPattern = Pattern.compile("(?m)\"[^\"]*\"|[^\\s]+");
126 private static Pattern argVariablePattern = Pattern.compile("\\$\\(([^)]+)\\)");
127 final JNAeratorConfig config;
128
129 public JNAerator(JNAeratorConfig config) {
130 this.config = config;
131 }
132
133 static final Pattern definePattern = Pattern.compile("#\\s*define\\s+(\\w+)\\s+(.*)");
134 static final boolean fullFilePathInComments = true;
135
136 private static final String DEFAULT_CONFIG_FILE = "config.jnaerator";
137 protected static final Pattern fileRadixPattern = Pattern.compile("(?:[/\\\\]|^)(.*?)(?:Full\\.bridgesupport|\\.[^.]+)$");
138 private static final Pattern classAndMethodNamePattern = Pattern.compile("(.+?)::([^:]+)");
139
140
141
142
143 public static String[] getJNAeratorArgsFromPref() {
144 String argsPref = System.getProperty("jnaerator.args");
145 if (argsPref == null)
146 return null;
147 return argsPref.split(",");
148 }
149 public static void main(String[] argsArray) {
150 String[] jnAeratorArgsFromPref = getJNAeratorArgsFromPref();
151 if (jnAeratorArgsFromPref != null) {
152 ArrayList<String> list = new ArrayList<String>();
153 list.addAll(Arrays.asList(jnAeratorArgsFromPref));
154 list.addAll(Arrays.asList(argsArray));
155 argsArray = list.toArray(new String[list.size()]);
156 }
157 if (argsArray.length == 0) {
158 if (new File("/Users/ochafik").exists()) {
159 argsArray = new String[] {
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 "-library", "Test",
223 "/Users/ochafik/Prog/Java/versionedSources/jnaerator/trunk/test/classes.h",
224 "-o", "/Users/ochafik/Prog/Java/versionedSources/jnaerator/trunk/test",
225 "-noComp", "-noJar",
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 "-v"
242 };
243 } else if (new File(DEFAULT_CONFIG_FILE).exists()){
244 argsArray = new String[] { "@", DEFAULT_CONFIG_FILE };
245 } else {
246 JNAeratorCommandLineArgs.displayHelp(false);
247 return;
248 }
249 }
250
251 try {
252 List<String> args = new ArrayList<String>(Arrays.asList(argsArray));
253
254 final JNAeratorConfig config = new JNAeratorConfig();
255 config.preprocessorConfig.frameworksPath.addAll(JNAeratorConfigUtils.DEFAULT_FRAMEWORKS_PATH);
256 new JNAeratorCommandLineArgs.ArgsParser() {
257
258 Feedback feedback = null;
259
260 List<String> frameworks = new ArrayList<String>();
261 boolean simpleGUI = false;
262 String arch = LibraryExtractor.getCurrentOSAndArchString();
263 String currentLibrary = null;
264
265 @Override
266 List<String> parsed(ParsedArg a) throws Exception {
267 switch (a.def) {
268
269 case AddIncludePath:
270 config.preprocessorConfig.includes.add(a.getFileParam(0).toString());
271 break;
272 case AddFrameworksPath:
273 config.preprocessorConfig.frameworksPath.add(a.getFileParam(0).toString());
274 break;
275 case NoPreprocessing:
276 config.preprocessorConfig.preprocess = false;
277 break;
278 case MaxConstructedFields:
279 config.maxConstructedFields = a.getIntParam(0);
280 break;
281 case NoPrimitiveArrays:
282 config.noPrimitiveArrays = true;
283 break;
284 case IfRegexMatch:
285 String javaProperty = a.getStringParam(0),
286 regex = a.getStringParam(1),
287 thenCmd = a.getStringParam(2),
288 elseCmd = a.getStringParam(3);
289 String propValue = System.getProperty(javaProperty);
290 if (propValue == null)
291 propValue = "";
292 return Arrays.asList(propValue.matches(regex) ? thenCmd : elseCmd);
293 case NoCompile:
294 config.compile = false;
295 break;
296 case NoStringReturns:
297 config.stringifyConstCStringReturnValues = false;
298 break;
299 case CPlusPlusGen:
300 config.genCPlusPlus = true;
301 break;
302 case CurrentLibrary:
303 currentLibrary = a.getStringParam(0);
304 break;
305 case CurrentPackage:
306 config.packageName = a.getStringParam(0);
307 break;
308 case NoLibBundle:
309 config.bundleLibraries = false;
310 break;
311 case DefaultLibrary:
312 config.defaultLibrary = a.getStringParam(0);
313 break;
314 case RecursedExtensions:
315 config.fileFilter = "*".equals(a.getStringParam(0)) ? null : new JNAeratorConfigUtils.FileExtensionFilter(a.getStringParam(0).split("[:;"));
316 break;
317 case DefineMacro:
318 config.preprocessorConfig.macros.put(a.getStringParam(0), a.getStringParam(1));
319 break;
320 case Direct:
321 config.useJNADirectCalls = true;
322 break;
323 case EntryName:
324 config.entryName = a.getStringParam(0);
325 break;
326 case ExtractSymbols:
327 config.extractLibSymbols = true;
328 break;
329 case File:
330 return parsedFile(a);
331 case FrameworksPath:
332 config.preprocessorConfig.frameworksPath.clear();
333 config.preprocessorConfig.frameworksPath.addAll(Arrays.asList(a.getStringParam(0).split(":")));
334 break;
335 case GUI:
336 simpleGUI = true;
337 break;
338 case Help:
339 case WikiDoc:
340 JNAeratorCommandLineArgs.displayHelp(a.def == OptionDef.WikiDoc);
341 System.exit(0);
342 break;
343 case WCharAsShort:
344 config.wcharAsShort = true;
345 break;
346 case JarOut:
347 config.outputJar = a.getFileParam(0);
348 break;
349 case NoMangling:
350 config.noMangling = true;
351 break;
352 case NoComments:
353 config.noComments = true;
354 break;
355 case LimitComments:
356 config.limitComments = true;
357 break;
358 case MacrosOut:
359 config.macrosOutFile = a.getFileParam(0);
360 break;
361 case GCCLong:
362 config.gccLong = true;
363 break;
364 case SizeAsLong:
365 config.sizeAsLong = true;
366 break;
367 case NoAuto:
368 config.autoConf = false;
369 break;
370 case NoCPP:
371 config.noCPlusPlus = true;
372 break;
373 case ScalaOut:
374 config.scalaOut = a.getFileParam(0);
375 break;
376 case NoRuntime:
377 config.bundleRuntime = false;
378 break;
379 case OutputDir:
380 config.outputDir = a.getFileParam(0);
381 break;
382 case PreferJavac:
383 config.preferJavac = true;
384 break;
385 case BridgeSupportOutFile:
386 config.bridgesupportOutFile = a.getFileParam(0);
387 break;
388 case ChoicesOut:
389 config.choicesOutFile = a.getFileParam(0);
390 break;
391 case ChoicesIn:
392 config.choicesInputFile = a.getFileParam(0);
393 break;
394 case PreprocessingOut:
395 config.preprocessingOutFile = a.getFileParam(0);
396 break;
397 case ExtractionOut:
398 config.extractedSymbolsOut = a.getFileParam(0);
399 break;
400
401 case Project:
402 JNAeratorConfigUtils.readProjectConfig(a.getFileParam(0), a.getStringParam(1), config);
403 break;
404 case RootPackage:
405 config.rootPackageName = a.getStringParam(0);
406 break;
407 case StructsInLibrary:
408 config.putTopStructsInSeparateFiles = false;
409 break;
410 case Studio:
411 try {
412 JNAeratorStudio.main(new String[0]);
413 return null;
414 } catch (Exception ex) {
415 ex.printStackTrace();
416 System.exit(1);
417 }
418 break;
419
420
421
422
423
424
425
426
427
428 case Verbose:
429 config.verbose = true;
430 break;
431 case Framework:
432 frameworks.add(a.getStringParam(0));
433 break;
434 case IncludeArgs:
435 return parsedArgsInclude(a);
436 case Arch:
437 arch = a.getStringParam(0);
438 break;
439
440 }
441 return Collections.emptyList();
442 }
443
444 private List<String> parsedFile(ParsedArg a) throws Exception {
445 File file = a.getFileParam(0);
446 if (file != null) {
447 String fn = file.getName();
448 if (file.isDirectory() && fn.matches(".*\\.framework"))
449 frameworks.add(file.toString());
450 else if (file.isFile() && fn.matches(".*\\.jnaerator"))
451 return parsedArgsInclude(a);
452 else if (fn.matches(".*\\.bridgesupport"))
453 config.bridgeSupportFiles.add(file);
454 else if (file.isFile() && isLibraryFile(file)) {
455 if (config.verbose)
456 System.out.println("Adding file '" + file + "' for arch '" + arch +"'.");
457 config.addLibraryFile(file, arch);
458 } else {
459 String lib = currentLibrary;
460 if (file.isDirectory() && fn.endsWith(".xcode") ||
461 file.isFile() && fn.toLowerCase().endsWith(".sln"))
462 {
463 JNAeratorConfigUtils.readProjectConfig(file, null, config);
464 } else {
465 if (lib == null) {
466 String name = fn;
467 int i = name.indexOf('.');
468 if (i >= 0)
469 name = name.substring(0, i).trim();
470 if (name.length() > 0)
471 lib = name;
472 System.out.println("Warning: no -library option for file '" + fn + "', using \"" + lib + "\".");
473 }
474 config.addSourceFile(file, lib, !file.isFile());
475 }
476 }
477 }
478 return Collections.emptyList();
479 }
480
481 private List<String> parsedArgsInclude(ParsedArg a) throws IOException {
482 final File argsFile = a.getFileParam(0);
483
484 String argsFileContent = ReadText.readText(argsFile);
485 Adapter<String[], String> argVariableReplacer = new Adapter<String[], String>() {
486 @Override
487 public String adapt(String[] value) {
488 String n = value[1];
489 String v = System.getProperty(n);
490 if (v == null)
491 v = System.getenv(n);
492 if (v == null && n.equals("DIR"))
493 v = argsFile.getAbsoluteFile().getParent();
494 return v;
495 }
496 };
497
498
499 argsFileContent = argsFileContent.replaceAll("(?m)//[^\n]*(\n|$)", "\n");
500 argsFileContent = argsFileContent.replaceAll("(?m)/\\*([^*]|\\*[^/])*\\*/", "");
501
502
503 argsFileContent = RegexUtils.regexReplace(argVariablePattern, argsFileContent, argVariableReplacer);
504
505 List<String> ret = new ArrayList<String>();
506 List<String[]> tokens = RegexUtils.find(argsFileContent, argTokenPattern);
507 for (String[] tokenMatch : tokens) {
508 String token = tokenMatch[0];
509 token = token.trim();
510 if (token.startsWith("\"") && token.endsWith("\""))
511 token = token.substring(1, token.length() - 1);
512
513 if (token.length() == 0 || token.matches("^(//|#).*"))
514 continue;
515
516 boolean allowMissing = token.endsWith("?");
517 if (token.contains("*")) {
518 Collection<String> rs = FileListUtils.resolveShellLikeFileList(allowMissing ? token.substring(0, token.length() - 1) : token);
519 for (String r : rs)
520 ret.add(allowMissing ? r + "?" : r);
521 if (!rs.isEmpty())
522 continue;
523 }
524 ret.add(token);
525 }
526 return ret;
527 }
528
529 @Override
530 void finished() throws IOException {
531 for (String framework : frameworks)
532 JNAeratorConfigUtils.addFramework(config, framework);
533
534 config.addRootDir(new File("."));
535 for (String i : config.preprocessorConfig.includes) {
536 try {
537 config.addRootDir(new File(i));
538 } catch (Exception ex) {}
539 }
540
541 if (config.sourceFiles.isEmpty() && config.bridgeSupportFiles.isEmpty() && !config.libraryFiles.isEmpty())
542 config.extractLibSymbols = true;
543
544 Collection<File> inputFiles = config.getInputFiles();
545 File firstFile = inputFiles.isEmpty() ? null : inputFiles.iterator().next().getAbsoluteFile();
546 String firstFileName = firstFile == null ? null : firstFile.getName();
547 String entry = config.entryName == null ? RegexUtils.findFirst(firstFileName, fileRadixPattern, 1) : config.entryName;
548 if (entry != null)
549 entry = TypeConversion.getValidJavaIdentifier(ident(entry)).toString();
550
551 if (config.outputDir == null)
552 config.outputDir = firstFile == null ? new File(".") : firstFile.getAbsoluteFile().getParentFile();
553
554 if (config.outputJar == null && config.compile)
555 config.outputJar = new File(config.outputDir, (entry == null ? "out" : entry) + ".jar");
556
557 if (config.verbose) {
558 if (config.macrosOutFile == null)
559 config.macrosOutFile = new File("_jnaerator.macros.cpp");
560 if (config.choicesOutFile == null)
561 config.choicesOutFile = new File("_jnaerator.choices");
562 if (config.preprocessingOutFile == null)
563 config.preprocessingOutFile = new File("_jnaerator.preprocessed.c");
564 if (config.extractedSymbolsOut == null)
565 config.extractedSymbolsOut = new File("_jnaerator.extractedSymbols.h");
566 if (config.bridgesupportOutFile == null)
567 config.bridgesupportOutFile = new File("_jnaerator.bridgesupport.h");
568 }
569
570 config.cacheDir = getDir("cache");
571
572 if (simpleGUI) {
573 SimpleGUI gui = new SimpleGUI(config);
574 feedback = gui;
575 gui.show();
576 } else {
577 feedback = new Feedback() {
578
579 @Override
580 public void setStatus(String string) {
581 if (config.verbose)
582 System.out.println(string);
583 }
584
585 @Override
586 public void setFinished(Throwable e) {
587 System.out.println("JNAeration failed !");
588 e.printStackTrace();
589 System.exit(1);
590 }
591
592 @Override
593 public void setFinished(File toOpen) {
594 System.out.println("JNAeration completed !");
595 System.out.println(toOpen.getAbsolutePath());
596 System.exit(0);
597 }
598
599 @Override
600 public void sourcesParsed(SourceFiles sourceFiles) {
601
602 }
603
604 @Override
605 public void wrappersGenerated(
606 com.ochafik.lang.jnaerator.Result result) {
607
608
609 }
610 };
611 }
612
613 new JNAerator(config).jnaerate(feedback);
614 if (!simpleGUI)
615 System.exit(0);
616 }
617
618 }.parse(args);
619
620 } catch (Exception e) {
621 e.printStackTrace();
622 System.exit(1);
623 }
624 }
625 public PrintWriter getClassSourceWriter(ClassOutputter outputter, String className) throws IOException {
626 return outputter.getClassSourceWriter(className);
627 }
628 private static boolean isLibraryFile(File file) {
629 String arg = file.getName().toLowerCase();
630 return
631 arg.endsWith(".dll") ||
632 arg.endsWith(".pdb") ||
633 arg.endsWith(".dylib") ||
634 arg.endsWith(".so") ||
635 arg.endsWith(".jnilib");
636 }
637 public void jnaerate(final Feedback feedback) {
638 try {
639 if (config.autoConf) {
640 feedback.setStatus("Auto-configuring parser...");
641 JNAeratorConfigUtils.autoConfigure(config);
642 }
643
644 if (config.verbose)
645 JNAeratorConfigUtils.logger.log(Level.INFO, "Include path : \n\t" + StringUtils.implode(config.preprocessorConfig.includes, "\n\t"));
646
647 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
648 JavaCompiler c = CompilerUtils.getJavaCompiler(config.preferJavac);
649 final MemoryFileManager mfm = new MemoryFileManager(c.getStandardFileManager(diagnostics, null, null));
650
651 final ClassOutputter[] classOutputter = new ClassOutputter[1];
652 if (config.compile) {
653 classOutputter[0] = new ClassOutputter() {
654 @Override
655 public PrintWriter getClassSourceWriter(String className) throws FileNotFoundException {
656 String path = "file:///" + className.replace('.', '/') + ".java";
657 MemoryJavaFile c = new MemoryJavaFile(path, (String)null, JavaFileObject.Kind.SOURCE);
658 mfm.inputs.put(c.getPath().toString(), c);
659 return new PrintWriter(c.openWriter());
660 }
661 };
662 } else {
663 classOutputter[0] = new ClassOutputter() {
664 public PrintWriter getClassSourceWriter(String className) throws FileNotFoundException {
665 File file = new File(JNAerator.this.config.outputDir, className.replace('.', File.separatorChar) + ".java");
666 File parent = file.getParentFile();
667 if (!parent.exists())
668 parent.mkdirs();
669
670 feedback.setStatus("Generating " + file.getName());
671
672 return new PrintWriter(file) {
673 @Override
674 public void print(String s) {
675 super.print(s.replace("\r", "").replace("\n", StringUtils.LINE_SEPARATOR));
676 }
677 };
678 }
679 };
680 }
681
682 Result result = createResult(new ClassOutputter() {
683
684 @Override
685 public PrintWriter getClassSourceWriter(String className) throws IOException {
686 return JNAerator.this.getClassSourceWriter(classOutputter[0], className);
687 }
688 }, feedback);
689
690 SourceFiles sourceFiles = parseSources(feedback, result.typeConverter);
691 if (config.extractLibSymbols)
692 parseLibSymbols(sourceFiles, result);
693
694 feedback.sourcesParsed(sourceFiles);
695
696 ScalaGenerator sgen = null;
697 if (config.scalaOut != null)
698 sgen = new ScalaGenerator(result);
699
700 jnaerationCore(sourceFiles, result);
701 if (sgen != null)
702 sgen.jnaerationCompleted();
703 feedback.wrappersGenerated(result);
704
705 if (config.compile) {
706 for (Map.Entry<String, String> cnAndSrc : config.extraJavaSourceFilesContents.entrySet()) {
707 mfm.addSourceInput(cnAndSrc.getKey(), cnAndSrc.getValue());
708 }
709 feedback.setStatus("Compiling JNAerated files...");
710 CompilerUtils.compile(c, mfm, diagnostics, "1.5", config.cacheDir, NativeLibrary.class, JNAerator.class, NSClass.class, Mangling.class);
711 CompilerUtils.CompilationError.throwErrors(diagnostics.getDiagnostics(), mfm.inputs, c.getClass().getName());
712
713 if (config.outputJar != null && result.config.bundleRuntime) {
714 feedback.setStatus("Copying runtime classes...");
715 addRuntimeClasses(result, mfm);
716 }
717 }
718 if (config.outputJar != null) {
719
720 feedback.setStatus("Generating " + config.outputJar.getName());
721 mfm.writeJar(config.outputJar, config.bundleSources, getAdditionalFiles());
722 }
723
724
725 feedback.setFinished(config.outputJar != null ? config.outputJar : config.outputDir);
726 } catch (Throwable th) {
727 feedback.setFinished(th);
728 }
729 }
730
731 public void parseLibSymbols(SourceFiles sourceFiles, Result result) throws FileNotFoundException {
732 PrintWriter fileOut = null;
733 if (config.extractedSymbolsOut != null) {
734 if (config.verbose)
735 System.out.println("Writing symbols extracted from libraries to '" + config.extractedSymbolsOut + "'");
736 fileOut = new PrintWriter(config.extractedSymbolsOut);
737 }
738
739 for (File libFile : config.libraryFiles) {
740 if (libFile.getName().toLowerCase().endsWith(".dll")) {
741 try {
742 result.feedback.setStatus("Extracting symbols from " + libFile.getName() + "...");
743
744 SourceFile sf = new SourceFile();
745 sf.setElementFile(libFile.toString());
746 List<ParsedExport> dllExports = DllExport.parseDllExports(libFile);
747 Map<String, Struct> cppClasses = new HashMap<String, Struct>();
748 Pattern pubPat = Pattern.compile("(public|private|protected):(.*)");
749 for (ParsedExport dllExport : dllExports) {
750
751 String dem = dllExport.demangled;
752 Matcher m = pubPat.matcher(dem);
753 String pub = null;
754 if (m.matches()) {
755 dem = m.group(2);
756 pub = m.group(1);
757 }
758 String text = "// @mangling " + dllExport.mangling + "\n" +
759 dem + ";";
760 ObjCppParser parser = JNAeratorParser.newObjCppParser(result.typeConverter, text, false);
761 parser.setupSymbolsStack();
762 List<Declaration> decls = parser.declarationEOF();
763 if (decls == null)
764 continue;
765
766 for (Declaration decl : decls) {
767 if (decl instanceof VariablesDeclaration && decl.getValueType() != null)
768 decl.getValueType().addModifiers(Modifier.Extern);
769 decl.addModifiers(Modifier.parseModifier(pub));
770 if (decl instanceof Function) {
771 Function f = (Function)decl;
772 List<SimpleIdentifier> si = new ArrayList<SimpleIdentifier>(f.getName().resolveSimpleIdentifiers());
773 Identifier ci;
774 if (si.size() == 1) {
775 String name = si.get(0) == null ? null : si.get(0).toString();
776 String[] cm = name == null ? null : RegexUtils.match(name, classAndMethodNamePattern);
777 if (cm == null) {
778 sf.addDeclaration(decl);
779 continue;
780 }
781 ci = ident(cm[0]);
782 f.setName(ident(cm[1]));
783 } else {
784 si.remove(si.size() - 1);
785 ci = new QualifiedIdentifier(QualificationSeparator.Colons, si);
786 }
787 if (dem.contains("__thiscall"))
788 f.addModifiers(Modifier.__thiscall);
789 if (dem.contains("__fastcall"))
790 f.addModifiers(Modifier.__fastcall);
791
792 Struct s = cppClasses.get(ci.toString());
793 if (s == null) {
794 s = new Struct();
795 cppClasses.put(ci.toString(), s);
796 s.setType(Struct.Type.CPPClass);
797 s.setTag(ci.clone());
798 sf.addDeclaration(decl(s));
799 }
800 Identifier n = f.getName().resolveLastSimpleIdentifier();
801
802
803
804 f.setName(n);
805 s.addDeclaration(f);
806 } else
807 sf.addDeclaration(decl);
808 }
809 }
810 if (!sf.getDeclarations().isEmpty()) {
811 sourceFiles.add(sf);
812 if (fileOut != null)
813 fileOut.println(sf);
814 }
815
816 } catch (Throwable ex) {
817 ex.printStackTrace();
818 }
819 }
820 }
821 if (fileOut != null)
822 fileOut.close();
823 }
824 private Map<String, File> getAdditionalFiles() {
825
826 Map<String, File> additionalFiles = new HashMap<String,File>();
827
828 if (config.bundleLibraries) {
829 for (Map.Entry<String, List<File>> e : config.libraryFilesByArch.entrySet()) {
830 String arch = e.getKey();
831 for (File libraryFile : e.getValue())
832 additionalFiles.put(
833 "libraries/" + (arch == null || arch.length() == 0 ? "" : arch + "/") + libraryFile.getName(),
834 libraryFile
835 );
836 }
837 for (String library : new HashSet<String>(config.libraryByFile.values())) {
838 String libraryFileName = System.mapLibraryName(library);
839 File libraryFile = new File(libraryFileName);
840
841 if (!libraryFile.exists() && libraryFileName.endsWith(".jnilib"))
842 libraryFile = new File(libraryFileName = libraryFileName.substring(0, libraryFileName.length() - ".jnilib".length()) + ".dylib");
843
844 String key = "libraries/" + LibraryExtractor.getCurrentOSAndArchString() + "/" + libraryFile.getName();
845 if (additionalFiles.containsKey(key))
846 continue;
847
848 if (libraryFile.exists()) {
849 System.out.println("Bundling " + libraryFile);
850 additionalFiles.put(key, libraryFile);
851 }
852
853
854 }
855 }
856 return additionalFiles;
857 }
858 protected void addRuntimeClasses(Result result, MemoryFileManager mfm) throws IOException {
859
860 ClassLoader classLoader = JNAerator.class.getClassLoader();
861 String listingFile = "jnaerator-runtime.jar.files";
862 List<String> files = ReadText.readLines(classLoader.getResourceAsStream(listingFile ));
863
864 try {
865 if (files == null)
866 files = ReadText.readLines("/Users/ochafik/Prog/Java/bin/jnaerator-runtime.jar.files");
867 } catch (Exception ex) {}
868
869
870
871 if (files == null) {
872 throw new FileNotFoundException("Warning: Could not find JNAerator listing file '" + listingFile + "' : JNAerated files will need JNAerator in the path to execute.");
873
874
875
876 }
877
878 boolean needsObjCRuntime = result.hasObjectiveC();
879 for (String file : files) {
880 if (!needsObjCRuntime) {
881 if (!file.startsWith("com/ochafik") &&
882 !file.startsWith("com/sun/jna"))
883 continue;
884 }
885
886 URL url = classLoader.getResource(file);
887 if (url == null) {
888 if (file.matches("com/sun/jna/[^/]+/(lib\\w+\\.(jnilib|so)|\\w+\\.dll)")) {
889 System.out.println("JNA library missing : " + file);
890 continue;
891 }
892 if (file.matches("com/ochafik/lang/jnaerator/runtime/scala/.*\\.part")) {
893 System.out.println("Scala code missing : " + file);
894 continue;
895 }
896 throw new FileNotFoundException(file);
897 }
898
899 file = "file:///" + file;
900 if (!mfm.outputs.containsKey(file)) {
901 mfm.outputs.put(file, new URLFileObject(url));
902 }
903 }
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919 }
920 public static File getDir(String name) {
921 File dir = new File(getDir(), name);
922 dir.mkdirs();
923 return dir;
924 }
925 public static File getDir() {
926 File dir = new File(System.getProperty("user.home"));
927 dir = new File(dir, ".jnaerator");
928 dir = new File(dir, "temp");
929 dir.mkdirs();
930 return dir;
931 }
932 static boolean isHexDigit(char c) {
933 return
934 c >= 'A' && c <= 'F' ||
935 c >= 'a' && c <= 'f' ||
936 Character.isDigit(c);
937 }
938 static void escapeUnicode(String s, StringBuilder bout) {
939 bout.setLength(0);
940 char[] chars = s.toCharArray();
941 for (int iChar = 0, nChars = chars.length; iChar < nChars; iChar++) {
942 char c = chars[iChar];
943 int v = (int)c;
944 if (v > 127) {
945 bout.append("\\u");
946 String h = Integer.toHexString(v);
947 for (int i = 4 - h.length(); i-- != 0;)
948 bout.append('0');
949 bout.append(h);
950 } else {
951
952
953
954
955
956
957
958
959
960
961
962 bout.append(c);
963 }
964 }
965 }
966
967 public SourceFiles parseSources(Feedback feedback, TypeConversion typeConverter) throws IOException, LexerException {
968 feedback.setStatus("Parsing native headers...");
969 return JNAeratorParser.parse(config, typeConverter);
970 }
971 public void addFile(File file, List<File> out) throws IOException {
972 if (file.isFile()) {
973 out.add(file);
974 } else {
975 File[] fs = file.listFiles();
976 if (fs != null) {
977 for (File f : fs) {
978 addFile(f, out);
979 }
980 }
981 }
982 }
983
984 private static void generateLibraryFiles(SourceFiles sourceFiles, Result result) throws IOException {
985
986 Struct librariesHub = null;
987 PrintWriter hubOut = null;
988 if (result.config.entryName != null) {
989 librariesHub = new Struct();
990 librariesHub.addToCommentBefore("JNA Wrappers instances");
991 librariesHub.setType(Type.JavaClass);
992 librariesHub.addModifiers(Modifier.Public, Modifier.Abstract);
993 Identifier hubName = result.getHubFullClassName();
994 librariesHub.setTag(hubName.resolveLastSimpleIdentifier());
995 hubOut = result.classOutputter.getClassSourceWriter(hubName.toString());
996 hubOut.println("package " + hubName.resolveAllButLastIdentifier() + ";");
997 for (Identifier pn : result.javaPackages)
998 if (!pn.equals(""))
999 hubOut.println("import " + pn + ".*;");
1000 }
1001 for (String library : result.libraries) {
1002 if (library == null)
1003 continue;
1004
1005
1006 Identifier javaPackage = result.javaPackageByLibrary.get(library);
1007 Identifier simpleLibraryClassName = result.getLibraryClassSimpleName(library);
1008
1009 Identifier fullLibraryClassName = result.getLibraryClassFullName(library);
1010
1011
1012
1013
1014 Struct interf = new Struct();
1015 interf.addToCommentBefore("JNA Wrapper for library <b>" + library + "</b>",
1016 result.declarationsConverter.getFileCommentContent(result.config.libraryProjectSources.get(library), null)
1017 );
1018 if (hubOut != null)
1019 interf.addToCommentBefore("@see " + result.config.entryName + "." + library);
1020
1021 interf.addModifiers(Modifier.Public);
1022 interf.setTag(simpleLibraryClassName);
1023 Identifier libSuperInter = ident(Library.class);
1024 if (result.config.useJNADirectCalls) {
1025 interf.addProtocols(libSuperInter);
1026 interf.setType(Type.JavaClass);
1027 } else {
1028 interf.setParents(libSuperInter);
1029 interf.setType(Type.JavaInterface);
1030 }
1031
1032 Expression libNameExpr = opaqueExpr(result.getLibraryFileExpression(library));
1033 TypeRef libTypeRef = typeRef(fullLibraryClassName);
1034 Expression libClassLiteral = classLiteral(libTypeRef);
1035
1036 Expression libraryPathGetterExpr = methodCall(
1037 expr(typeRef(LibraryExtractor.class)),
1038 MemberRefStyle.Dot,
1039 "getLibraryPath",
1040 libNameExpr,
1041 expr(true),
1042 libClassLiteral
1043 );
1044
1045 String libNameStringFieldName = "JNA_LIBRARY_NAME", nativeLibFieldName = "JNA_NATIVE_LIB";
1046 interf.addDeclaration(new VariablesDeclaration(typeRef(String.class), new Declarator.DirectDeclarator(
1047 libNameStringFieldName,
1048 libraryPathGetterExpr
1049 )).addModifiers(Modifier.Public, Modifier.Static, Modifier.Final));
1050
1051 Expression libraryNameFieldExpr = memberRef(expr(libTypeRef.clone()), MemberRefStyle.Dot, ident(libNameStringFieldName));
1052 Expression optionsMapExpr = memberRef(expr(typeRef(MangledFunctionMapper.class)), MemberRefStyle.Dot, "DEFAULT_OPTIONS");
1053 interf.addDeclaration(new VariablesDeclaration(typeRef(NativeLibrary.class), new Declarator.DirectDeclarator(
1054 nativeLibFieldName,
1055 methodCall(
1056 expr(typeRef(NativeLibrary.class)),
1057 MemberRefStyle.Dot,
1058 "getInstance",
1059 libraryNameFieldExpr.clone(),
1060 optionsMapExpr.clone()
1061 )
1062 )).addModifiers(Modifier.Public, Modifier.Static, Modifier.Final));
1063 Expression nativeLibFieldExpr = memberRef(expr(libTypeRef.clone()), MemberRefStyle.Dot, ident(nativeLibFieldName));
1064
1065 if (result.config.useJNADirectCalls) {
1066 interf.addDeclaration(new Function(Function.Type.StaticInit, null, null).setBody(block(
1067 stat(methodCall(
1068 expr(typeRef(Native.class)),
1069 MemberRefStyle.Dot,
1070 "register",
1071 libraryNameFieldExpr.clone()
1072 ))
1073 )).addModifiers(Modifier.Static));
1074 } else {
1075 VariablesDeclaration instanceDecl = new VariablesDeclaration(libTypeRef, new Declarator.DirectDeclarator(
1076 librariesHub == null ? "INSTANCE" : library,
1077 cast(
1078 libTypeRef,
1079 methodCall(
1080 expr(typeRef(Native.class)),
1081 MemberRefStyle.Dot,
1082 "loadLibrary",
1083 libraryNameFieldExpr.clone(),
1084 libClassLiteral,
1085 optionsMapExpr.clone()
1086 )
1087 )
1088 )).addModifiers(Modifier.Public, Modifier.Static, Modifier.Final);
1089 if (librariesHub != null) {
1090 librariesHub.addDeclaration(instanceDecl);
1091 librariesHub.addProtocol(fullLibraryClassName.clone());
1092 } else
1093 interf.addDeclaration(instanceDecl);
1094 }
1095
1096
1097
1098 Signatures signatures = result.getSignaturesForOutputClass(fullLibraryClassName);
1099 result.typeConverter.allowFakePointers = true;
1100 result.declarationsConverter.convertEnums(result.enumsByLibrary.get(library), signatures, interf, fullLibraryClassName);
1101 result.declarationsConverter.convertConstants(library, result.definesByLibrary.get(library), sourceFiles, signatures, interf, fullLibraryClassName);
1102 result.declarationsConverter.convertStructs(result.structsByLibrary.get(library), signatures, interf, fullLibraryClassName);
1103 result.declarationsConverter.convertCallbacks(result.callbacksByLibrary.get(library), signatures, interf, fullLibraryClassName);
1104 result.declarationsConverter.convertFunctions(result.functionsByLibrary.get(library), signatures, interf, fullLibraryClassName);
1105 result.globalsGenerator.convertGlobals(result.globalsByLibrary.get(library), signatures, interf, nativeLibFieldExpr, fullLibraryClassName, library);
1106
1107 result.typeConverter.allowFakePointers = false;
1108
1109 Set<String> fakePointers = result.fakePointersByLibrary.get(fullLibraryClassName);
1110 if (fakePointers != null)
1111 for (String fakePointerName : fakePointers) {
1112 if (fakePointerName.contains("::"))
1113 continue;
1114
1115 Identifier fakePointer = ident(fakePointerName);
1116 if (!signatures.classSignatures.add(fakePointer))
1117 continue;
1118
1119 Struct ptClass = result.declarationsConverter.publicStaticClass(fakePointer, ident(PointerType.class), Struct.Type.JavaClass, null);
1120 ptClass.addToCommentBefore("Pointer to unknown (opaque) type");
1121 ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null,
1122 new Arg("pointer", typeRef(Pointer.class))
1123 ).addModifiers(Modifier.Public).setBody(
1124 block(stat(methodCall("super", varRef("pointer")))))
1125 );
1126 ptClass.addDeclaration(new Function(Function.Type.JavaMethod, fakePointer, null)
1127 .addModifiers(Modifier.Public)
1128 .setBody(
1129 block(stat(methodCall("super")))
1130 ));
1131 interf.addDeclaration(decl(ptClass));
1132 }
1133
1134 interf = result.notifyBeforeWritingClass(fullLibraryClassName, interf, signatures, library);
1135 if (interf != null) {
1136 final PrintWriter out = result.classOutputter.getClassSourceWriter(fullLibraryClassName.toString());
1137
1138
1139 result.printJavaHeader(javaPackage, out);
1140 out.println(interf);
1141 out.close();
1142 }
1143 }
1144 if (hubOut != null) {
1145 hubOut.println(librariesHub.toString());
1146 hubOut.close();
1147 }
1148 }
1149
1150 public Result createResult(final ClassOutputter outputter, Feedback feedback) {
1151 Result r = new Result(config, new ClassOutputter() {
1152 @Override
1153 public PrintWriter getClassSourceWriter(String className)
1154 throws IOException {
1155 PrintWriter w = outputter.getClassSourceWriter(className);
1156 return new PrintWriter(w) {
1157 StringBuilder bout = new StringBuilder();
1158 @Override
1159 public void print(String s) {
1160 escapeUnicode(s, bout);
1161 super.print(bout.toString());
1162 }
1163 };
1164 }
1165 });
1166 r.feedback = feedback;
1167 return r;
1168 }
1169
1170 public static ObjCppParser newParser(String s) throws IOException {
1171 Result result = new Result(new JNAeratorConfig(), null);
1172 ObjCppParser parser = new ObjCppParser(new CommonTokenStream(new ObjCppLexer(
1173 new ANTLRReaderStream(new StringReader(s))))
1174
1175 );
1176 parser.typeConverter = result.typeConverter;
1177 return parser;
1178 }
1179 protected void readChoices(Result result) throws IOException, RecognitionException {
1180 BufferedReader in = new BufferedReader(new FileReader(result.config.choicesInputFile));
1181 String line;
1182
1183 List<Function> functions = null;
1184
1185 int iLine = 0;
1186 while ((line = in.readLine()) != null) {
1187 iLine++;
1188 line = line.trim();
1189 if (line.startsWith("//"))
1190 continue;
1191 if (line.length() == 0)
1192 functions = null;
1193
1194
1195
1196 Function function = null;
1197 if (functions == null) {
1198 function = newParser(line).javaMethodDeclaration();
1199 } else {
1200 function = newParser(line).functionDeclaration().function;
1201 }
1202 if (function == null) {
1203 System.err.println("Error: failed to parse function at line " + iLine + ": '" + line + "'");
1204 continue;
1205 }
1206 if (functions == null)
1207 result.declarationsConverter.functionAlternativesByNativeSignature.put(
1208 function.computeSignature(false),
1209 new Pair<Function, List<Function>>(
1210 function,
1211 functions = new ArrayList<Function>()
1212 )
1213 );
1214 else
1215 functions.add(function);
1216 }
1217
1218 System.err.println("Read " + result.declarationsConverter.functionAlternativesByNativeSignature.size() + " custom declarations from " + result.config.choicesInputFile);
1219 }
1220
1221 public void jnaerationCore(SourceFiles sourceFiles, Result result) throws IOException, LexerException, RecognitionException {
1222 result.feedback.setStatus("Normalizing parsed code...");
1223
1224 if (result.config.choicesInputFile != null)
1225 readChoices(result);
1226
1227
1228 sourceFiles.accept(new ObjectiveCToJavaPreScanner());
1229
1230
1231 sourceFiles.accept(new CToJavaPreScanner());
1232
1233
1234 sourceFiles.accept(new MissingNamesChooser(result));
1235
1236
1237 sourceFiles.accept(new Scanner() {
1238 @Override
1239 protected void visitTypeRef(TypeRef tr) {
1240 super.visitTypeRef(tr);
1241 Element parent = tr.getParentElement();
1242 if (parent instanceof TypeRef) {
1243 List<Modifier> stoMods = getStoMods(tr.getModifiers());
1244 if (stoMods != null) {
1245 List<Modifier> newMods = new ArrayList<Modifier>(tr.getModifiers());
1246 newMods.removeAll(stoMods);
1247 tr.setModifiers(newMods);
1248 ((ModifiableElement)parent).addModifiers(stoMods);
1249 }
1250 }
1251 }
1252 public List<Modifier> getStoMods(List<Modifier> mods) {
1253 List<Modifier> ret = null;
1254 for (Modifier mod : mods) {
1255 if (mod.isA(ModifierKind.StorageClassSpecifier)) {
1256 if (ret == null)
1257 ret = new ArrayList<Modifier>();
1258 ret.add(mod);
1259 }
1260 }
1261 return ret;
1262 }
1263 });
1264
1265
1266 sourceFiles.accept(new JavaDocCreator(result));
1267
1268 assert checkNoCycles(sourceFiles);
1269
1270
1271
1272
1273
1274 if (result.feedback != null && !result.config.bridgeSupportFiles.isEmpty())
1275 result.feedback.setStatus("Parsing BridgeSupport files...");
1276
1277
1278
1279
1280 sourceFiles.accept(result);
1281 result.rehabilitateWeakTypeDefs();
1282
1283 result.chooseLibraryClasses(config.packageName, config.rootPackageName);
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307 if (!result.classes.isEmpty()) {
1308 result.feedback.setStatus("Generating Objective-C classes...");
1309 result.objectiveCGenerator.generateObjectiveCClasses();
1310 }
1311
1312 result.feedback.setStatus("Generating libraries...");
1313
1314 if (result.libraries.size() == 1) {
1315 List<Define> list = result.definesByLibrary.get(null);
1316 if (list != null) {
1317 String lib = result.libraries.iterator().next();
1318 Result.getList(result.definesByLibrary, lib).addAll(list);
1319 }
1320 }
1321
1322 generateLibraryFiles(sourceFiles, result);
1323
1324
1325 for (String unknownType : result.typeConverter.unknownTypes)
1326 System.out.println("Unknown Type: " + unknownType);
1327
1328 if (result.config.choicesOutFile != null) {
1329 PrintWriter out = new PrintWriter(result.config.choicesOutFile);
1330 for (Map.Entry<String, Pair<Function, List<Function>>> e : result.declarationsConverter.functionAlternativesByNativeSignature.entrySet()) {
1331 Function f = e.getValue().getKey();
1332 String ff = f.getElementFile();
1333 if (ff != null)
1334 out.println("// " + ff + (f.getElementLine() > 0 ? ":" + f.getElementLine() : ""));
1335
1336 out.println(f);
1337 for (Function alt : e.getValue().getValue()) {
1338 out.println(alt);
1339 }
1340 out.println();
1341 }
1342 out.close();
1343 }
1344 }
1345 private boolean checkNoCycles(SourceFiles sourceFiles) {
1346 final HashSet<Integer> ids = new HashSet<Integer>(new Arg().getId());
1347 sourceFiles.accept(new Scanner() {
1348 @Override
1349 protected void visitElement(Element d) {
1350 if (d != null && !ids.add(d.getId()))
1351 throw new RuntimeException("Cycle : " + d);
1352 super.visitElement(d);
1353 }
1354 });
1355 return true;
1356 }
1357
1358 }