GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
JavaEmitter.java
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010-2023 JogAmp Community. All rights reserved.
3 * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistribution in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of Sun Microsystems, Inc. or the names of
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * This software is provided "AS IS," without a warranty of any kind. ALL
21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * You acknowledge that this software is not designed or intended for use
34 * in the design, construction, operation or maintenance of any nuclear
35 * facility.
36 *
37 * Sun gratefully acknowledges that this software was originally authored
38 * and developed by Kenneth Bradley Russell and Christopher John Kline.
39 */
40
41package com.jogamp.gluegen;
42
43import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PACKAGE_PRIVATE;
44import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PRIVATE;
45import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PROTECTED;
46import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC;
47import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC_ABSTRACT;
48import static java.util.logging.Level.FINE;
49import static java.util.logging.Level.INFO;
50import static java.util.logging.Level.WARNING;
51import static java.util.logging.Level.SEVERE;
52
53import java.io.File;
54import java.io.IOException;
55import java.io.PrintWriter;
56import java.nio.Buffer;
57import java.nio.ByteBuffer;
58import java.text.MessageFormat;
59import java.util.ArrayList;
60import java.util.Arrays;
61import java.util.Collection;
62import java.util.Collections;
63import java.util.Comparator;
64import java.util.HashMap;
65import java.util.HashSet;
66import java.util.Iterator;
67import java.util.List;
68import java.util.Map;
69import java.util.Optional;
70import java.util.Set;
71
72import jogamp.common.os.MachineDataInfoRuntime;
73
74import com.jogamp.common.nio.Buffers;
75import com.jogamp.common.os.DynamicLookupHelper;
76import com.jogamp.common.os.MachineDataInfo;
77import com.jogamp.common.util.ArrayHashMap;
78import com.jogamp.common.util.HashUtil;
79import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider;
80import com.jogamp.gluegen.JavaConfiguration.JavaCallbackDef;
81import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo;
82import com.jogamp.gluegen.JavaType.PascalStringElem;
83import com.jogamp.gluegen.Logging.LoggerIf;
84import com.jogamp.gluegen.cgram.types.AliasedSymbol;
85import com.jogamp.gluegen.cgram.types.ArrayType;
86import com.jogamp.gluegen.cgram.types.CompoundType;
87import com.jogamp.gluegen.cgram.types.Field;
88import com.jogamp.gluegen.cgram.types.FunctionSymbol;
89import com.jogamp.gluegen.cgram.types.FunctionType;
90import com.jogamp.gluegen.cgram.types.PointerType;
91import com.jogamp.gluegen.cgram.types.SizeThunk;
92import com.jogamp.gluegen.cgram.types.StructLayout;
93import com.jogamp.gluegen.cgram.types.Type;
94import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol;
95import com.jogamp.gluegen.cgram.types.TypeDictionary;
96
97// PROBLEMS:
98// - what if something returns 'const int *'? Could we
99// return an IntBuffer that has read-only behavior? Or do we copy the array
100// (but we don't know its size!). What do we do if it returns a non-const
101// int*? Should the user be allowed to write back to the returned pointer?
102//
103// - Non-const array types must be properly released with JNI_COMMIT
104// in order to see side effects if the array was copied.
105
106
107public class JavaEmitter implements GlueEmitter {
108
109 private StructLayout layout;
110 private Map<Type, Type> canonMap;
112
113 /**
114 * Style of code emission. Can emit everything into one class
115 * (AllStatic), separate interface and implementing classes
116 * (InterfaceAndImpl), only the interface (InterfaceOnly), or only
117 * the implementation (ImplOnly).
118 */
119 public enum EmissionStyle {AllStatic, InterfaceAndImpl, InterfaceOnly, ImplOnly}
120
121 /**
122 * Access control for emitted Java methods.
123 */
124 public enum MethodAccess {
125 PUBLIC("public"), PROTECTED("protected"), PRIVATE("private"), PACKAGE_PRIVATE("/* pp */"), PUBLIC_ABSTRACT("abstract");
126
127 public final String getJavaName() { return javaName; }
128
129 MethodAccess(final String javaName) {
130 this.javaName = javaName;
131 }
132 private final String javaName;
133 }
134
135 /**
136 * Resource ownership.
137 */
138 public enum Ownership {
139 /** Parent ownership of resource, i.e. derived-from and shared-with compounding parent resource. */
141 /** Java ownership of resource. */
143 /** Native ownership of resource. */
145 /** Ambiguous mixed ownership of resource, i.e. {@Link #Java} or {@link #Native}. */
147 }
148
149 private JavaCodeUnit javaUnit; // Covers either interface or, in AllStatic mode, everything
150 private JavaCodeUnit javaImplUnit; // Only used in non-AllStatic modes for impl class
151 private CCodeUnit cUnit;
152 private final MachineDataInfo machDescJava = MachineDataInfo.StaticConfig.LP64_UNIX.md;
153 private final MachineDataInfo.StaticConfig[] machDescTargetConfigs = MachineDataInfo.StaticConfig.values();
154
155 protected final LoggerIf LOG;
156
157 public JavaEmitter() {
158 LOG = Logging.getLogger(JavaEmitter.class.getPackage().getName(), JavaEmitter.class.getSimpleName());
159 }
160
162 this();
163 this.cfg = cfg;
164 }
165
166 @Override
167 public void readConfigurationFile(final String filename) throws Exception {
168 cfg = createConfig();
169 cfg.read(filename);
170 }
171
172 @Override
173 public JavaConfiguration getConfig() { return cfg; }
174
175 class ConstFuncRenamer implements SymbolFilter {
176 private List<ConstantDefinition> constants;
177 private List<FunctionSymbol> functions;
178
179 @Override
180 public List<ConstantDefinition> getConstants() {
181 return constants;
182 }
183 @Override
184 public List<FunctionSymbol> getFunctions() {
185 return functions;
186 }
187
188 private <T extends AliasedSemanticSymbol> List<T> filterSymbolsInt(final List<T> inList,
189 final boolean preserveOrder,
190 final List<T> outList) {
191 final JavaConfiguration cfg = getConfig();
192 final ArrayHashMap<String, T> symMap =
193 new ArrayHashMap<String, T>(false, 100, ArrayHashMap.DEFAULT_LOAD_FACTOR);
194 for (final T sym : inList) {
195 final String origName = sym.getName();
196 final String newName = cfg.getJavaSymbolRename(origName);
197 final T dupSym;
198 if( null != newName ) {
199 // Alias Name
200 dupSym = symMap.get(newName);
201 if( null != dupSym ) {
202 // only rename to allow 'equalSemantics' to not care ..
203 sym.rename(newName);
204 }
205 } else {
206 // Original Name
207 dupSym = symMap.get(origName);
208 }
209 if( null != dupSym ) {
210 // Duplicate alias .. check
211 if( !dupSym.equalSemantics(sym) ) {
212 final ASTLocusTag loc;
213 final String preLoc;
214 if( sym instanceof ASTLocusTagProvider ) {
215 loc = ((ASTLocusTagProvider)sym).getASTLocusTag();
216 } else {
217 loc = null;
218 }
219 if( dupSym instanceof ASTLocusTagProvider ) {
220 preLoc = String.format(",%n %s: previous definition is here",
221 ((ASTLocusTagProvider)dupSym).getASTLocusTag().toString(new StringBuilder(), "note", true));
222 } else {
223 preLoc = "";
224 }
225 final String mode = null != newName ? "alias" : "orig";
226 final String message =
227 String.format("Duplicate Name (%s) w/ incompatible value:%n this '%s',%n have '%s'%s",
228 mode, sym.getAliasedString(), dupSym.getAliasedString(), preLoc);
229 throw new GlueGenException(message, loc);
230 }
231 }
232 if( null != newName ) {
233 // Alias Name
234 if( null != dupSym ) {
235 // Duplicate alias .. add aliased name
236 dupSym.addAliasedName(origName);
237 } else {
238 // No duplicate .. rename and add
239 sym.rename(newName);
240 symMap.put(newName, sym);
241 }
242 } else {
243 // Original Name
244 if( null != dupSym ) {
245 // Duplicate orig .. drop
246 } else {
247 // No duplicate orig .. add
248 symMap.put(origName, sym);
249 }
250 }
251 }
252 outList.addAll(symMap.getData());
253 if( !preserveOrder ) {
254 // sort constants to make them easier to find in native code
255 Collections.sort(outList, new Comparator<T>() {
256 @Override
257 public int compare(final T o1, final T o2) {
258 return o1.getName().compareTo(o2.getName());
259 }
260 });
261 }
262 return outList;
263 }
264
265 @Override
266 public void filterSymbols(final List<ConstantDefinition> inConstList, final List<FunctionSymbol> inFuncList) {
267 constants = filterSymbolsInt(inConstList, true, new ArrayList<ConstantDefinition>(100));
268 functions = filterSymbolsInt(inFuncList, true, new ArrayList<FunctionSymbol>(100));
269 }
270 }
271
272 @Override
273 public void beginEmission(final GlueEmitterControls controls) throws IOException {
274 // Handle renaming of constants and functions
275 controls.runSymbolFilter(new ConstFuncRenamer());
276
277 // Request emission of any structs requested
278 for (final String structs : cfg.forcedStructs()) {
279 controls.forceStructEmission(structs);
280 }
281
282 if ( !cfg.structsOnly() ) {
283 try {
284 openCodeUnits();
285 } catch (final Exception e) {
286 throw new RuntimeException("Unable to open files for writing", e);
287 }
289 }
290 }
291
292 @Override
293 public void endEmission() {
294 if ( !cfg.structsOnly() ) {
296
297 try {
298 closeWriters();
299 } catch (final Exception e) {
300 throw new RuntimeException("Unable to close open files", e);
301 }
302 }
303 }
304
305 @Override
306 public void beginDefines() throws Exception {
307 if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) {
308 javaUnit().emitln();
309 }
310 }
311
312 /** Mangle a class, package or function name for JNI usage, i.e. replace all '_' w/ '_1' and '.' w/ '_' */
313 protected static String jniMangle(final String name) {
314 return name.replaceAll("_", "_1").replace('.', '_');
315 }
316 /** Returns the JNI method prefix consisting our of mangled package- and class-name */
317 protected static String getJNIMethodNamePrefix(final String javaPackageName, final String javaClassName) {
318 return "Java_"+jniMangle(javaPackageName)+"_"+jniMangle(javaClassName);
319 }
320
321 private final Map<String, ConstantDefinition.JavaExpr> constMap =
322 new HashMap<String, ConstantDefinition.JavaExpr>();
323
324 @Override
325 public void emitDefine(final ConstantDefinition def, final String optionalComment) throws Exception {
326 if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) {
327 // TODO: Some defines (e.g., GL_DOUBLE_EXT in gl.h) are defined in terms
328 // of other defines -- should we emit them as references to the original
329 // define (not even sure if the lexer supports this)? Right now they're
330 // emitted as the numeric value of the original definition. If we decide
331 // emit them as references we'll also have to emit them in the correct
332 // order. It's probably not an issue right now because the emitter
333 // currently only emits only numeric defines -- if it handled #define'd
334 // objects it would make a bigger difference.
335
336 if ( !cfg.shouldIgnoreInInterface(def) ) {
337 final ConstantDefinition.JavaExpr constExpr = def.computeJavaExpr(constMap);
338 constMap.put(def.getName(), constExpr);
339 javaUnit().emit(" /** ");
340 if (optionalComment != null && optionalComment.length() != 0) {
341 javaUnit().emit(optionalComment);
342 javaUnit().emit(", ");
343 }
344 javaUnit().emit("CType: ");
345 if( constExpr.resultType.isUnsigned ) {
346 javaUnit().emit("unsigned ");
347 }
348 javaUnit().emit(constExpr.resultJavaTypeName);
349 javaUnit().emitln(" */");
350 javaUnit().emitln(" public static final " + constExpr.resultJavaTypeName +
351 " " + def.getName() + " = " + constExpr.javaExpression + ";");
352 }
353 }
354 }
355
356 @Override
357 public void endDefines() throws Exception {
358 }
359
360 @Override
361 public void beginFunctions(final TypeDictionary typedefDictionary,
362 final TypeDictionary structDictionary,
363 final Map<Type, Type> canonMap,
364 final List<FunctionSymbol> cFunctions) throws Exception {
365
366 // this.typedefDictionary = typedefDictionary;
367 this.canonMap = canonMap;
368
369 if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) {
370 javaUnit().emitln();
371 if( !cfg.getJavaCallbackList().isEmpty() ) {
372 final List<JavaCallbackDef> javaCallbacks = cfg.getJavaCallbackList();
373 for(final JavaCallbackDef jcbd : javaCallbacks) {
374 final Type funcPtr = typedefDictionary.get(jcbd.cbFuncTypeName);
375 if( null != funcPtr && funcPtr.isFunctionPointer() ) {
376 final Optional<FunctionSymbol> setter = cFunctions.stream()
377 .filter( cFunction -> jcbd.setFuncName.equals(cFunction.getName()) )
378 .findFirst();
379 if( setter.isPresent() ) {
380 generateJavaCallbackCode(jcbd, funcPtr.getTargetFunction());
381 } else {
382 LOG.log(WARNING, "JavaCallback '{0}' setter not available", jcbd);
383 }
384 } else {
385 LOG.log(WARNING, "JavaCallback '{0}' function-pointer type not available", jcbd);
386 }
387 }
388 }
389 }
390 }
391
392 @Override
393 public Iterator<FunctionSymbol> emitFunctions(final List<FunctionSymbol> funcsToBind) throws Exception {
394 if ( !cfg.structsOnly() ) {
395 // Bind all the C funcs to Java methods
396 final ArrayList<FunctionEmitter> methodBindingEmitters = new ArrayList<FunctionEmitter>(2*funcsToBind.size());
397 {
398 int i=0;
399 for (final FunctionSymbol cFunc : funcsToBind) {
400 // Check to see whether this function should be ignored
401 if ( !cfg.shouldIgnoreInImpl(cFunc) ) {
402 methodBindingEmitters.addAll(generateMethodBindingEmitters(cFunc));
403 LOG.log(INFO, cFunc.getASTLocusTag(), "Non-Ignored Impl[{0}]: {1}", i++, cFunc);
404 }
405
406 }
407 }
408
409 // Emit all the methods
410 {
411 int i=0;
412 for (final FunctionEmitter emitter : methodBindingEmitters) {
413 try {
414 final FunctionSymbol cFunc = emitter.getCSymbol();
415 if ( !emitter.isInterface() || !cfg.shouldIgnoreInInterface(cFunc) ) {
416 emitter.emit();
417 emitter.getUnit().emitln(); // put newline after method body
418 LOG.log(INFO, cFunc.getASTLocusTag(), "Non-Ignored Intf[{0}]: {1}", i++, cFunc);
419 }
420 } catch (final Exception e) {
421 throw new GlueGenException(
422 "Error while emitting binding for \"" + emitter.getCSymbol().getAliasedString() + "\"",
423 emitter.getCSymbol().getASTLocusTag(), e);
424 }
425 }
426 }
427 }
428 // Return the list of FunctionSymbols that we generated gluecode for
429 return funcsToBind.iterator();
430 }
431
432 /**
433 * Create the object that will read and store configuration information for
434 * this JavaEmitter.
435 */
437 return new JavaConfiguration();
438 }
439
440 /**
441 * Generates the public emitters for this MethodBinding which will
442 * produce either simply signatures (for the interface class, if
443 * any) or function definitions with or without a body (depending on
444 * whether or not the implementing function can go directly to
445 * native code because it doesn't need any processing of the
446 * outgoing arguments).
447 */
448 protected void generatePublicEmitters(final MethodBinding binding, final List<FunctionEmitter> allEmitters,
449 final boolean signatureOnly) {
450 final FunctionSymbol cSymbol = binding.getCSymbol();
451 if ( !signatureOnly && cfg.manuallyImplement(cSymbol) ) {
452 // We only generate signatures for manually-implemented methods;
453 // user provides the implementation
454 return;
455 }
456
457 final MethodAccess accessControl;
458
459 if ( !signatureOnly && null != binding.getDelegationImplName() ) {
460 // private access for delegation implementation methods
461 accessControl = PRIVATE;
462 } else {
463 accessControl = cfg.accessControl(binding.getName());
464 }
465
466 // We should not emit anything except public APIs into interfaces
467 if ( signatureOnly && PUBLIC != accessControl ) {
468 return;
469 }
470
471 // It's possible we may not need a body even if signatureOnly is
472 // set to false; for example, if the routine doesn't take any
473 // arrays or buffers as arguments
474 final boolean isUnimplemented = cfg.isUnimplemented(cSymbol);
475 final List<String> prologue = cfg.javaPrologueForMethod(binding, false, false);
476 final List<String> epilogue = cfg.javaEpilogueForMethod(binding, false, false);
477 final boolean needsJavaCallbackCode = cfg.requiresJavaCallbackCode( binding.getName() );
478 final boolean needsBody = isUnimplemented ||
480 binding.signatureUsesJavaPrimitiveArrays() ||
481 needsJavaCallbackCode ||
482 null != prologue ||
483 null != epilogue;
484
485 final boolean emitBody = !signatureOnly && needsBody;
486 final boolean isNativeMethod = !isUnimplemented && !needsBody && !signatureOnly;
487
488 final CodeUnit unit = ((signatureOnly || cfg.allStatic()) ? javaUnit() : javaImplUnit());
489
490 final JavaMethodBindingEmitter emitter =
491 new JavaMethodBindingEmitter(binding,
492 unit,
493 cfg.runtimeExceptionType(),
494 cfg.unsupportedExceptionType(),
495 emitBody, // emitBody
496 cfg.tagNativeBinding(),
497 false, // eraseBufferAndArrayTypes
498 cfg.useNIOOnly(binding.getName()),
499 cfg.useNIODirectOnly(binding.getName()),
500 false, // forDirectBufferImplementation
501 false, // forIndirectBufferAndArrayImplementation
502 isUnimplemented, // isUnimplemented
503 signatureOnly, // isInterface
504 isNativeMethod, // isNativeMethod
505 false, // isPrivateNativeMethod
506 cfg);
507 switch (accessControl) {
508 case PUBLIC: emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); break;
509 case PROTECTED: emitter.addModifier(JavaMethodBindingEmitter.PROTECTED); break;
510 case PRIVATE: emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); break;
511 default: break; // package-private adds no modifiers
512 }
513 if (cfg.allStatic()) {
515 }
516 if (isNativeMethod) {
518 }
520 emitter.setPrologue(prologue);
521 emitter.setEpilogue(epilogue);
522 allEmitters.add(emitter);
523 }
524
525 /**
526 * Generates the private emitters for this MethodBinding. On the
527 * Java side these will simply produce signatures for native
528 * methods. On the C side these will create the emitters which will
529 * write the JNI code to interface to the functions. We need to be
530 * careful to make the signatures all match up and not produce too
531 * many emitters which would lead to compilation errors from
532 * creating duplicated methods / functions.
533 */
534 protected void generatePrivateEmitters(final MethodBinding binding,
535 final List<FunctionEmitter> allEmitters) {
536 final FunctionSymbol cSymbol = binding.getCSymbol();
537 if (cfg.manuallyImplement(cSymbol)) {
538 // Don't produce emitters for the implementation class
539 return;
540 }
541
542 final boolean hasPrologueOrEpilogue =
543 cfg.javaPrologueForMethod(binding, false, false) != null ||
544 cfg.javaEpilogueForMethod(binding, false, false) != null ;
545 final boolean needsJavaCallbackCode = cfg.requiresJavaCallbackCode( binding.getName() );
546
547 if ( !cfg.isUnimplemented( cSymbol ) ) {
548 // If we already generated a public native entry point for this
549 // method, don't emit another one
550 //
551 // !binding.signatureUsesJavaPrimitiveArrays():
552 // If the binding uses primitive arrays, we are going to emit
553 // the private native entry point for it along with the version
554 // taking only NIO buffers
555 if ( !binding.signatureUsesJavaPrimitiveArrays() &&
556 ( binding.needsNIOWrappingOrUnwrapping() || hasPrologueOrEpilogue || needsJavaCallbackCode )
557 )
558 {
559 final CodeUnit unit = (cfg.allStatic() ? javaUnit() : javaImplUnit());
560
561 // (Always) emit the entry point taking only direct buffers
562 final JavaMethodBindingEmitter emitter =
563 new JavaMethodBindingEmitter(binding,
564 unit,
565 cfg.runtimeExceptionType(),
566 cfg.unsupportedExceptionType(),
567 false, // emitBody
568 cfg.tagNativeBinding(),
569 true, // eraseBufferAndArrayTypes
570 cfg.useNIOOnly(binding.getName()),
571 cfg.useNIODirectOnly(binding.getName()),
572 true, // forDirectBufferImplementation
573 false, // forIndirectBufferAndArrayImplementation
574 false, // isUnimplemented
575 false, // isInterface
576 true, // isNativeMethod
577 true, // isPrivateNativeMethod
578 cfg);
580 if (cfg.allStatic()) {
582 }
585 allEmitters.add(emitter);
586 }
587
588 // Now generate the C emitter(s). We need to produce one for every
589 // Java native entry point (public or private). The only
590 // situations where we don't produce one are (a) when the method
591 // is unimplemented, and (b) when the signature contains primitive
592 // arrays, since the latter is handled by the method binding
593 // variant taking only NIO Buffers.
594 if ( !binding.signatureUsesJavaPrimitiveArrays() ) {
595 // Generate a binding without mixed access (NIO-direct, -indirect, array)
596 final CMethodBindingEmitter cEmitter =
597 new CMethodBindingEmitter(binding,
598 cUnit(),
599 cfg.implPackageName(),
600 cfg.implClassName(),
601 true, // NOTE: we always disambiguate with a suffix now, so this is optional
602 cfg.allStatic(),
603 (binding.needsNIOWrappingOrUnwrapping() || hasPrologueOrEpilogue || needsJavaCallbackCode),
604 !cfg.useNIODirectOnly(binding.getName()),
605 machDescJava, getConfig());
606 prepCEmitter(binding.getName(), binding.getJavaReturnType(), cEmitter);
607 allEmitters.add(cEmitter);
608 }
609 }
610 }
611
612 protected void prepCEmitter(final String returnSizeLookupName, final JavaType javaReturnType, final CMethodBindingEmitter cEmitter)
613 {
614 // See whether we need an expression to help calculate the
615 // length of any return type
616 if (javaReturnType.isNIOBuffer() ||
617 javaReturnType.isCompoundTypeWrapper()) {
618 // See whether capacity has been specified
619 final String capacity = cfg.returnValueCapacity(returnSizeLookupName);
620 if (capacity != null) {
621 cEmitter.setReturnValueCapacityExpression( new MessageFormat(capacity) );
622 }
623 } else if (javaReturnType.isArray() ||
624 javaReturnType.isArrayOfCompoundTypeWrappers()) {
625 // NOTE: adding a check here because the CMethodBindingEmitter
626 // also doesn't yet handle returning scalar arrays. In order
627 // to implement this, return the type as a Buffer instead
628 // (i.e., IntBuffer, FloatBuffer) and add code as necessary.
629 if (javaReturnType.isPrimitiveArray()) {
630 throw new RuntimeException("Primitive array return types not yet supported");
631 }
632
633 // See whether length has been specified
634 final String len = cfg.returnValueLength(returnSizeLookupName);
635 if (len != null) {
636 cEmitter.setReturnValueLengthExpression( new MessageFormat(len) );
637 }
638 }
639 cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(returnSizeLookupName));
640 cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(returnSizeLookupName));
641 }
642
643 /**
644 * Generate all appropriate Java bindings for the specified C function
645 * symbols.
646 */
647 protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final FunctionSymbol sym) throws Exception {
648 final ArrayList<FunctionEmitter> allEmitters = new ArrayList<FunctionEmitter>();
649 try {
650 if( cfg.emitInterface() ) {
651 generateMethodBindingEmittersImpl(allEmitters, sym, true);
652 }
653 if( cfg.emitImpl() ) {
654 generateMethodBindingEmittersImpl(allEmitters, sym, false);
655 }
656 } catch (final Exception e) {
657 throw new GlueGenException("Error while generating bindings for \"" + sym + "\"", sym.getASTLocusTag(), e);
658 }
659
660 return allEmitters;
661 }
662 private void generateMethodBindingEmittersImpl(final ArrayList<FunctionEmitter> allEmitters,
663 final FunctionSymbol sym,
664 final boolean forInterface) throws Exception
665 {
666 // Get Java binding for the function
667 final MethodBinding mb = bindFunction(sym, forInterface, machDescJava, null, null);
668
669 // JavaTypes representing C pointers in the initial
670 // MethodBinding have not been lowered yet to concrete types
671 final List<MethodBinding> bindings = expandMethodBinding(mb);
672
673 final HashSet<MethodBinding> methodBindingSet = new HashSet<MethodBinding>();
674
675 for (final MethodBinding binding : bindings) {
676
677 if(!methodBindingSet.add(binding)) {
678 // skip .. already exisiting binding ..
679 continue;
680 }
681
682 if (cfg.allStatic() && binding.hasContainingType()) {
683 // This should not currently happen since structs are emitted using a different mechanism
684 throw new IllegalArgumentException("Cannot create binding in AllStatic mode because method has containing type: \"" +
685 binding + "\"");
686 }
687
688 // The structure of the generated glue code looks something like this:
689 // Simple method (no arrays, void pointers, etc.):
690 // Interface class:
691 // public void fooMethod();
692 // Implementation class:
693 // public native void fooMethod();
694 //
695 // Method taking void* argument:
696 // Interface class:
697 // public void fooMethod(Buffer arg);
698 // Implementation class:
699 // public void fooMethod(Buffer arg) {
700 // ... bounds checks, etc. ...
701 //
702 // boolean arg_direct = arg != null && Buffers.isDirect(arg);
703 //
704 // fooMethod0(arg_direct?arg:Buffers.getArray(arg),
705 // arg_direct?Buffers.getDirectBufferByteOffset(arg):Buffers.getIndirectBufferByteOffset(arg),
706 // arg_direct,
707 // ... );
708 // }
709 // private native void fooMethod1(Object arg, int arg_byte_offset, boolean arg_is_direct, ...);
710 //
711 // Method taking primitive array argument:
712 // Interface class:
713 // public void fooMethod(int[] arg, int arg_offset);
714 // public void fooMethod(IntBuffer arg);
715 // Implementing class:
716 // public void fooMethod(int[] arg, int arg_offset) {
717 // ... range checks, etc. ...
718 // fooMethod1(arg, SIZEOF_INT * arg_offset);
719 // }
720 // public void fooMethod(IntBuffer arg) {
721 // ... bounds checks, etc. ...
722 //
723 // boolean arg_direct = BufferFactory.isDirect(arg);
724 //
725 // fooMethod1(arg_direct?arg:BufferFactory.getArray(arg),
726 // arg_direct?BufferFactory.getDirectBufferByteOffset(arg):BufferFactory.getIndirectBufferByteOffset(arg),
727 // arg_direct,
728 // ... );
729 // }
730 // private native void fooMethod1(Object arg, int arg_byte_offset, boolean arg_is_direct, ...);
731 //
732 // Note in particular that the public entry point taking an
733 // array is merely a special case of the indirect buffer case.
734
735 if ( forInterface ) {
736 generatePublicEmitters(binding, allEmitters, true);
737 } else {
738 generatePublicEmitters(binding, allEmitters, false);
739 generatePrivateEmitters(binding, allEmitters);
740 }
741 } // end iteration over expanded bindings
742 }
743
744
745 @Override
746 public void endFunctions() throws Exception {
747 if ( !cfg.structsOnly() ) {
748 if (cfg.allStatic() || cfg.emitInterface()) {
749 emitCustomJavaCode(javaUnit(), cfg.className());
750 }
751 if( cfg.allStatic() && cfg.emitImpl()) {
752 emitCustomJNICode(cUnit(), cfg.className());
753 }
754 if (!cfg.allStatic() && cfg.emitImpl()) {
755 emitCustomJavaCode(javaImplUnit(), cfg.implClassName());
756 emitCustomJNICode(cUnit(), cfg.implClassName());
757 }
758 }
759 }
760
761 @Override
762 public void beginStructLayout() throws Exception {}
763 @Override
764 public void layoutStruct(final CompoundType t) throws Exception {
765 getLayout().layout(t);
766 }
767 @Override
768 public void endStructLayout() throws Exception {}
769
770 @Override
771 public void beginStructs(final TypeDictionary typedefDictionary,
772 final TypeDictionary structDictionary,
773 final Map<Type, Type> canonMap) throws Exception {
774 // this.typedefDictionary = typedefDictionary;
775 this.canonMap = canonMap;
776 }
777
778 @Override
779 public void emitStruct(final CompoundType structCType, final Type structCTypedefPtr) throws Exception {
780 final String structCTypeName, typedefedName;
781 final boolean immutableStruct;
782 {
783 final String _name = structCType.getName();
784 if ( null != structCTypedefPtr && null != structCTypedefPtr.getName() ) {
785 // always use typedef'ed name if available
786 typedefedName = structCTypedefPtr.getName();
787 structCTypeName = typedefedName;
788 } else {
789 // fall back to actual struct type name
790 typedefedName = null;
791 structCTypeName = _name;
792 }
793 LOG.log(INFO, structCType.getASTLocusTag(), "Struct emission of structCType {0}", structCType);
794 LOG.log(INFO, structCType.getASTLocusTag()," structCTypedefPtr {0}", structCTypedefPtr);
795 LOG.log(INFO, structCType.getASTLocusTag()," : structCTypeName \"{0}\" -> typedefedName \"{1}\" -> \"{2}\"",
796 _name, typedefedName, structCTypeName);
797 if ( null == structCTypeName ) {
798 LOG.log(INFO, structCType.getASTLocusTag(),
799 "skipping emission of unnamed struct {0} w/o typedef", structCType);
800 return;
801 }
802 final AliasedSymbol.AliasedSymbolImpl aliases = new AliasedSymbol.AliasedSymbolImpl(structCTypeName);
803 aliases.addAliasedName(_name);
804 aliases.addAliasedName(typedefedName);
805 if ( cfg.shouldIgnoreInInterface(aliases) ) {
806 LOG.log(INFO, structCType.getASTLocusTag(),
807 "skipping emission of ignored \"{0}\": {1}", aliases, structCType);
808 return;
809 }
810 immutableStruct = cfg.immutableAccess(aliases);
811 }
812
813 if( null != structCTypedefPtr && isOpaque(structCTypedefPtr) ) {
814 LOG.log(INFO, structCType.getASTLocusTag(),
815 "skipping emission of opaque typedef {0}", structCTypedefPtr);
816 return;
817 }
818 if( isOpaque(structCType) ) {
819 LOG.log(INFO, structCType.getASTLocusTag(),
820 "skipping emission of opaque c-struct {0}", structCType);
821 return;
822 }
823
824 final Type containingCType;
825 {
826 // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser)
827 final Type aptr;
828 int mode;
829 if( null != typedefedName ) {
830 aptr = structCTypedefPtr;
831 mode = 1;
832 } else {
833 aptr = new PointerType(SizeThunk.POINTER, structCType, 0);
834 aptr.setTypedefName(typedefedName);
835 mode = 2;
836 }
837 containingCType = canonicalize(aptr);
838 LOG.log(INFO, structCType.getASTLocusTag(), "containingCType[{0}]: {1} -canon-> {2}", mode, aptr, containingCType);
839 }
840 final JavaType containingJType = typeToJavaType(containingCType, null);
841 if( containingJType.isOpaqued() ) {
842 LOG.log(INFO, structCType.getASTLocusTag(),
843 "skipping emission of opaque {0}, {1}", containingJType, structCType);
844 return;
845 }
846 if( !containingJType.isCompoundTypeWrapper() ) {
847 LOG.log(WARNING, structCType.getASTLocusTag(),
848 "skipping emission of non-compound {0}, {1}", containingJType, structCType);
849 return;
850 }
851 final String containingJTypeName = containingJType.getName();
852 LOG.log(INFO, structCType.getASTLocusTag(),
853 "perform emission of \"{0}\" -> \"{1}\": {2}", structCTypeName, containingJTypeName, structCType);
854
855 if( 0 == structCType.getNumFields() ) {
856 LOG.log(INFO, structCType.getASTLocusTag(),
857 "emission of \"{0}\" with zero fields {1}", containingJTypeName, structCType);
858 }
859
860 // machDescJava global MachineDataInfo is the one used to determine
861 // the sizes of the primitive types seen in the public API in Java.
862 // For example, if a C long is an element of a struct, it is the size
863 // of a Java int on a 32-bit machine but the size of a Java long
864 // on a 64-bit machine. To support both of these sizes with the
865 // same API, the abstract base class must take and return a Java
866 // long from the setter and getter for this field. However the
867 // implementation on a 32-bit platform must downcast this to an
868 // int and set only an int's worth of data in the struct.
869 //
870 // The machDescTarget MachineDataInfo is the one used to determine how
871 // much data to set in or get from the struct and exactly from
872 // where it comes.
873 //
874 // Note that machDescJava MachineDataInfo is always 64bit unix,
875 // which complies w/ Java types.
876
877 boolean needsNativeCode = !cfg.customJNICodeForClass(containingJTypeName).isEmpty();
878
879 // Native code for calls through function pointers gets emitted
880 // into the abstract base class; Java code which accesses fields
881 // gets emitted into the concrete classes
882 for (int i = 0; i < structCType.getNumFields(); i++) {
883 final Field field = structCType.getField(i);
884 final Type fieldType = field.getType();
885 final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName());
886
887 if (!cfg.shouldIgnoreInInterface(cfgFieldName0)) {
888 final String renamed = cfg.getJavaSymbolRename(cfgFieldName0);
889 final String fieldName = renamed==null ? field.getName() : renamed;
890 final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName);
891 final TypeInfo opaqueField = cfg.canonicalNameOpaque(cfgFieldName1);
892 final boolean isOpaqueField = null != opaqueField;
893
894 if ( fieldType.isFunctionPointer() && !isOpaqueField ) {
895 needsNativeCode = true;
896 break;
897 }
898 }
899 }
900
901 final String structClassPkgName = cfg.packageForStruct(structCTypeName);
902 final JavaCodeUnit javaUnit;
903 final CCodeUnit jniUnit;
904 try {
905 {
906 final String javaFileName = cfg.javaOutputDir() + File.separator +
907 CodeGenUtils.packageAsPath(structClassPkgName) +
908 File.separator + containingJTypeName + ".java";
909
910 javaUnit = openJavaUnit(javaFileName, structClassPkgName, containingJTypeName);
911 }
912
913 if( null == javaUnit ) {
914 // suppress output if openFile deliberately returns null.
915 return;
916 }
917 if (needsNativeCode) {
918 String nRoot = cfg.nativeOutputDir();
919 if (cfg.nativeOutputUsesJavaHierarchy()) {
920 nRoot += File.separator + CodeGenUtils.packageAsPath(cfg.packageName());
921 }
922 final String cUnitName = containingJTypeName + "_JNI.c";
923 final String fname = nRoot + File.separator + cUnitName;
924 jniUnit = openCUnit(fname, cUnitName);
925 // jniUnit.emitHeader(structClassPkgName, containingJTypeName, Collections.emptyList());
926 jniUnit.emitHeader(structClassPkgName, containingJTypeName, cfg.customCCode());
927 } else {
928 jniUnit = null;
929 }
930 } catch(final Exception e) {
931 throw new RuntimeException("Unable to open files for emission of struct class", e);
932 }
933
934 javaUnit.emitln();
935 javaUnit.emitln("package " + structClassPkgName + ";");
936 javaUnit.emitln();
937 javaUnit.emitln("import java.nio.*;");
938 javaUnit.emitln("import java.nio.charset.Charset;");
939 javaUnit.emitln("import java.nio.charset.StandardCharsets;");
940 javaUnit.emitln();
941
942 javaUnit.emitln("import " + cfg.gluegenRuntimePackage() + ".*;");
943 javaUnit.emitln("import " + DynamicLookupHelper.class.getPackage().getName() + ".*;");
944 javaUnit.emitln("import " + Buffers.class.getPackage().getName() + ".*;");
945 javaUnit.emitln("import " + MachineDataInfoRuntime.class.getName() + ";");
946 javaUnit.emitln();
947 final List<String> imports = cfg.imports();
948 for (final String str : imports) {
949 javaUnit.emit("import ");
950 javaUnit.emit(str);
951 javaUnit.emitln(";");
952 }
953 javaUnit.emitln();
954 final List<String> javadoc = cfg.javadocForClass(containingJTypeName);
955 for (final String doc : javadoc) {
956 javaUnit.emitln(doc);
957 }
958 javaUnit.emit("public class " + containingJTypeName + " ");
959 boolean firstIteration = true;
960 final List<String> userSpecifiedInterfaces = cfg.implementedInterfaces(containingJTypeName);
961 for (final String userInterface : userSpecifiedInterfaces) {
962 if (firstIteration) {
963 javaUnit.emit("implements ");
964 }
965 firstIteration = false;
966 javaUnit.emit(userInterface);
967 javaUnit.emit(" ");
968 }
969 javaUnit.emitln("{");
970 javaUnit.emitln();
971 javaUnit.emitln(" StructAccessor accessor;");
972 javaUnit.emitln();
973 final String cfgMachDescrIdxCode = cfg.returnStructMachineDataInfoIndex(containingJTypeName);
974 final String machDescrIdxCode = null != cfgMachDescrIdxCode ? cfgMachDescrIdxCode : "private static final int mdIdx = MachineDataInfoRuntime.getStatic().ordinal();";
975 javaUnit.emitln(" "+machDescrIdxCode);
976 javaUnit.emitln(" private final MachineDataInfo md;");
977 javaUnit.emitln();
978 // generate all offset and size arrays
979 generateOffsetAndSizeArrays(javaUnit, " ", containingJTypeName, structCType, null, null); /* w/o offset */
980 if( GlueGen.debug() ) {
981 System.err.printf("SE.__: structCType %s%n", structCType.getDebugString());
982 System.err.printf("SE.__: contCTypeName %s%n", containingCType.getDebugString());
983 System.err.printf("SE.__: contJTypeName %s%n", containingJType.getDebugString());
984 }
985 for (int i = 0; i < structCType.getNumFields(); i++) {
986 final Field field = structCType.getField(i);
987 final Type fieldType = field.getType();
988 final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName());
989 if ( !cfg.shouldIgnoreInInterface(cfgFieldName0) ) {
990 final String renamed = cfg.getJavaSymbolRename(cfgFieldName0);
991 final String fieldName = null==renamed ? field.getName() : renamed;
992 final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName);
993 if (fieldType.isFunctionPointer()) {
994 if( GlueGen.debug() ) {
995 System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "FuncPtr");
996 }
997 generateOffsetAndSizeArrays(javaUnit, " ", fieldName, null, field, null); /* w/o size */
998 generateOffsetAndSizeArrays(javaUnit, "//", fieldName, fieldType, null, null);
999 } else if (fieldType.isCompound()) {
1000 // FIXME: will need to support this at least in order to
1001 // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate
1002 // a name?)
1003 if (fieldType.getName() == null) {
1004 throw new GlueGenException("Anonymous structs as fields not supported yet, field \"" +
1005 cfgFieldName1 + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag());
1006 }
1007 if( GlueGen.debug() ) {
1008 System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "compound");
1009 }
1010 generateOffsetAndSizeArrays(javaUnit, " ", fieldName, fieldType, field, null);
1011 } else if (fieldType.isArray()) {
1012 final Type baseElementType = fieldType.getBaseType();
1013 if( GlueGen.debug() ) {
1014 System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "array");
1015 System.err.printf("SE.os.%02d: baseType %s%n", (i+1), baseElementType.getDebugString());
1016 }
1017 generateOffsetAndSizeArrays(javaUnit, " ", fieldName, null, field, null); /* w/o size */
1018 generateOffsetAndSizeArrays(javaUnit, "//", fieldName, fieldType, null, null);
1019 } else {
1020 final JavaType externalJavaType;
1021 try {
1022 externalJavaType = typeToJavaType(fieldType, machDescJava);
1023 } catch (final Exception e) {
1024 throw new GlueGenException("Error occurred while creating accessor for field \"" +
1025 cfgFieldName1 + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e);
1026 }
1027 if( GlueGen.debug() ) {
1028 System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "MISC");
1029 System.err.printf("SE.os.%02d: javaType %s%n", (i+1), externalJavaType.getDebugString());
1030 }
1031 if (externalJavaType.isPrimitive()) {
1032 // Primitive type
1033 generateOffsetAndSizeArrays(javaUnit, " ", fieldName, null, field, null); /* w/o size */
1034 generateOffsetAndSizeArrays(javaUnit, "//", fieldName, fieldType, null, null);
1035 } else if (externalJavaType.isCPrimitivePointerType()) {
1036 if( requiresGetCStringLength(fieldType, cfgFieldName1) ) {
1037 generateOffsetAndSizeArrays(javaUnit, " ", fieldName, null, field, null); /* w/o size */
1038 generateOffsetAndSizeArrays(javaUnit, "//", fieldName, fieldType, null, "// "+externalJavaType.getDebugString());
1039 } else {
1040 generateOffsetAndSizeArrays(javaUnit, " ", fieldName, null, field, null); /* w/o size */
1041 generateOffsetAndSizeArrays(javaUnit, "//", fieldName, fieldType, field, "// "+externalJavaType.getDebugString());
1042 }
1043 } else {
1044 generateOffsetAndSizeArrays(javaUnit, " ", fieldName, null, field, null); /* w/o size */
1045 generateOffsetAndSizeArrays(javaUnit, "//", fieldName, fieldType, null, "// "+externalJavaType.getDebugString());
1046 }
1047 }
1048 } else if( GlueGen.debug() ) {
1049 System.err.printf("SE.os.%02d: %s, %s (IGNORED)%n", (i+1), field, fieldType.getDebugString());
1050 }
1051 }
1052 javaUnit.emitln();
1053 javaUnit.emitln(" /** Returns true if this generated implementation uses native code, otherwise false. */");
1054 javaUnit.emitln(" public static boolean usesNativeCode() {");
1055 javaUnit.emitln(" return "+needsNativeCode+";");
1056 javaUnit.emitln(" }");
1057 javaUnit.emitln();
1058
1059 // getDelegatedImplementation
1060 if( !cfg.manuallyImplement(JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, "size")) ) {
1061 javaUnit.emitln(" /** Returns the aligned total size of a native instance. */");
1062 javaUnit.emitln(" public static int size() {");
1063 javaUnit.emitln(" return "+containingJTypeName+"_size[mdIdx];");
1064 javaUnit.emitln(" }");
1065 javaUnit.emitln();
1066 }
1067 if( !cfg.manuallyImplement(JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, "create")) ) {
1068 javaUnit.emitln(" /** Returns a new instance with all bytes set to zero. */");
1069 javaUnit.emitln(" public static " + containingJTypeName + " create() {");
1070 javaUnit.emitln(" return create(Buffers.newDirectByteBuffer(size()));");
1071 javaUnit.emitln(" }");
1072 javaUnit.emitln();
1073 javaUnit.emitln(" /** Returns a new instance using the given ByteBuffer having at least {#link size()} bytes capacity. The ByteBuffer will be {@link ByteBuffer#rewind()} and native-order set. */");
1074 javaUnit.emitln(" public static " + containingJTypeName + " create(java.nio.ByteBuffer buf) {");
1075 javaUnit.emitln(" return new " + containingJTypeName + "(buf);");
1076 javaUnit.emitln(" }");
1077 javaUnit.emitln();
1078 }
1079 javaUnit.emitln(" /** Returns new instance dereferencing ByteBuffer at given native address `addr` with size {@link #size()}. */");
1080 javaUnit.emitln(" public static " + containingJTypeName + " derefPointer(final long addr) {");
1081 javaUnit.emitln(" return create( ElementBuffer.derefPointer(size(), addr, 1).getByteBuffer() );");
1082 javaUnit.emitln(" }");
1083 javaUnit.emitln();
1084 if( !cfg.manuallyImplement(JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, containingJTypeName)) ) {
1085 javaUnit.emitln(" " + containingJTypeName + "(java.nio.ByteBuffer buf) {");
1086 javaUnit.emitln(" md = MachineDataInfo.StaticConfig.values()[mdIdx].md;");
1087 javaUnit.emitln(" accessor = new StructAccessor(buf);");
1088 javaUnit.emitln(" }");
1089 javaUnit.emitln();
1090 }
1091 javaUnit.emitln(" /** Return the underlying native direct ByteBuffer */");
1092 javaUnit.emitln(" public final java.nio.ByteBuffer getBuffer() {");
1093 javaUnit.emitln(" return accessor.getBuffer();");
1094 javaUnit.emitln(" }");
1095 javaUnit.emitln();
1096 javaUnit.emitln(" /** Returns the native address of the underlying native ByteBuffer {@link #getBuffer()} */");
1097 javaUnit.emitln(" public final long getDirectBufferAddress() {");
1098 javaUnit.emitln(" return accessor.getDirectBufferAddress();");
1099 javaUnit.emitln(" }");
1100 javaUnit.emitln();
1101
1102 final Set<MethodBinding> methodBindingSet = new HashSet<MethodBinding>();
1103
1104 for (int i = 0; i < structCType.getNumFields(); i++) {
1105 final Field field = structCType.getField(i);
1106 final Type fieldType = field.getType();
1107
1108 final String fqStructFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName()); // containingJTypeName.field.getName()
1109 if (!cfg.shouldIgnoreInInterface(fqStructFieldName0)) {
1110 final String renamed = cfg.getJavaSymbolRename(fqStructFieldName0);
1111 final String fieldName = renamed==null ? field.getName() : renamed;
1112 final String fqStructFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName); // containingJTypeName.fieldName
1113 final TypeInfo opaqueFieldType = cfg.typeInfo(fieldType);
1114 final boolean isOpaqueFieldType = null != opaqueFieldType;
1115 final TypeInfo opaqueField = cfg.canonicalNameOpaque(fqStructFieldName1);
1116 final boolean isOpaqueField = null != opaqueField;
1117 final boolean immutableField = immutableStruct || cfg.immutableAccess(fqStructFieldName1);
1118
1119 if( GlueGen.debug() ) {
1120 System.err.printf("SE.ac.%02d: field %s / %s / rename %s -> %s / opaque %b, fieldType %s (opaque %b), immutable[struct %b, field %b]%n", (i+1),
1121 field, fqStructFieldName1, renamed, fieldName, isOpaqueField, fieldType.getDebugString(), isOpaqueFieldType,
1122 immutableStruct, immutableField);
1123 System.err.printf("SE.ac.%02d: opaqueFieldType %s%n", (i+1), opaqueFieldType);
1124 }
1125 if ( fieldType.isFunctionPointer() && !isOpaqueField ) {
1126 final FunctionSymbol func = new FunctionSymbol(field.getName(), fieldType.getTargetFunction());
1127 func.rename(renamed); // null is OK
1128 final String javaTypeName = "long";
1129 final String capFieldName = CodeGenUtils.capitalizeString(fieldName);
1130 if( !immutableField && !fieldType.isConst() ) {
1131 // Setter
1132 generateSetterSignature(javaUnit, MethodAccess.PUBLIC, false, false, fieldName, fieldType, Ownership.Parent, containingJTypeName, capFieldName, null, javaTypeName, null, false, false, null, null, null);
1133 javaUnit.emitln(" {");
1134 javaUnit.emitln(" accessor.setLongAt(" + fieldName+"_offset[mdIdx], src, md.pointerSizeInBytes());");
1135 javaUnit.emitln(" return this;");
1136 javaUnit.emitln(" }");
1137 javaUnit.emitln();
1138 }
1139 // Getter
1140 generateGetterSignature(javaUnit, false, false, fieldName, fieldType, Ownership.Parent, javaTypeName, capFieldName, null, false, false, null, null);
1141 javaUnit.emitln(" {");
1142 javaUnit.emitln(" return accessor.getLongAt(" + fieldName+"_offset[mdIdx], md.pointerSizeInBytes());");
1143 javaUnit.emitln(" }");
1144 javaUnit.emitln();
1145 generateFunctionPointerCode(methodBindingSet, javaUnit, jniUnit, structCTypeName,
1146 containingCType, containingJType, i, func, fqStructFieldName1);
1147 } else if ( fieldType.isCompound() && !isOpaqueField ) {
1148 // FIXME: will need to support this at least in order to
1149 // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate a name?)
1150 if (fieldType.getName() == null) {
1151 throw new GlueGenException("Anonymous structs as fields not supported yet (field \"" +
1152 field + "\" in type \"" + structCTypeName + "\")",
1153 fieldType.getASTLocusTag());
1154 }
1155 if( !immutableField && !fieldType.isConst() ) {
1156 // Setter
1157 generateSetterSignature(javaUnit, MethodAccess.PUBLIC, false, false, fieldName, fieldType, Ownership.Parent, containingJTypeName, CodeGenUtils.capitalizeString(fieldName), null, fieldType.getName(), null, false, false, null, null, null);
1158 javaUnit.emitln(" {");
1159 javaUnit.emitln(" final ByteBuffer bb = src.getBuffer();");
1160 javaUnit.emitln(" final int size = "+fieldName+"_size[mdIdx];");
1161 javaUnit.emitln(" final byte[] content = new byte[size];");
1162 javaUnit.emitln(" bb.get(content, 0, size);");
1163 javaUnit.emitln(" accessor.setBytesAt("+fieldName+"_offset[mdIdx], content);");
1164 javaUnit.emitln(" return this;");
1165 javaUnit.emitln(" }");
1166 javaUnit.emitln();
1167 }
1168 // Getter
1169 generateGetterSignature(javaUnit, false, false, fieldName, fieldType, Ownership.Parent, fieldType.getName(), CodeGenUtils.capitalizeString(fieldName), null, false, false, null, null);
1170 javaUnit.emitln(" {");
1171 javaUnit.emitln(" return " + fieldType.getName() + ".create( accessor.slice( " +
1172 fieldName+"_offset[mdIdx], "+fieldName+"_size[mdIdx] ) );");
1173 javaUnit.emitln(" }");
1174 javaUnit.emitln();
1175 } else if ( ( fieldType.isArray() || fieldType.isPointer() ) && !isOpaqueField ) {
1176 generateArrayGetterSetterCode(javaUnit, structCType, containingJType,
1177 i, field, fieldName, immutableField, fqStructFieldName1);
1178 } else {
1179 final JavaType javaType;
1180 try {
1181 javaType = typeToJavaType(fieldType, machDescJava);
1182 } catch (final Exception e) {
1183 throw new GlueGenException("Error occurred while creating accessor for field \"" +
1184 field.getName() + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e);
1185 }
1186 if ( isOpaqueFieldType || isOpaqueField || javaType.isPrimitive()) {
1187 // Primitive type
1188 final boolean fieldTypeNativeSizeFixed = fieldType.getSize().hasFixedNativeSize();
1189 final String javaTypeName;
1190 if ( isOpaqueFieldType ) {
1191 javaTypeName = opaqueFieldType.javaType().getName();
1192 } else if ( isOpaqueField ) {
1193 javaTypeName = opaqueField.javaType().getName();
1194 // javaTypeName = compatiblePrimitiveJavaTypeName(fieldType, javaType, machDescJava);
1195 } else {
1196 javaTypeName = javaType.getName();
1197 }
1198 final String capJavaTypeName = CodeGenUtils.capitalizeString(javaTypeName);
1199 final String capFieldName = CodeGenUtils.capitalizeString(fieldName);
1200 final String sizeDenominator = fieldType.isPointer() ? "pointer" : javaTypeName ;
1201
1202 LOG.log(FINE, structCType.getASTLocusTag(),
1203 "Java.StructEmitter.Primitive: "+field.getName()+", "+fieldType+", "+javaTypeName+", "+
1204 ", fixedSize "+fieldTypeNativeSizeFixed+", opaque[t "+isOpaqueFieldType+", f "+isOpaqueField+"], sizeDenominator "+sizeDenominator);
1205
1206 if( !immutableField && !fieldType.isConst() ) {
1207 // Setter
1208 generateSetterSignature(javaUnit, MethodAccess.PUBLIC, false, false, fieldName, fieldType, Ownership.Parent, containingJTypeName, capFieldName, null, javaTypeName, null, false, false, null, null, null);
1209 javaUnit.emitln(" {");
1210 if( fieldTypeNativeSizeFixed ) {
1211 javaUnit.emitln(" accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], src);");
1212 } else {
1213 javaUnit.emitln(" accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], src, md."+sizeDenominator+"SizeInBytes());");
1214 }
1215 javaUnit.emitln(" return this;");
1216 javaUnit.emitln(" }");
1217 javaUnit.emitln();
1218 }
1219
1220 // Getter
1221 generateGetterSignature(javaUnit, false, false, fieldName, fieldType, Ownership.Parent, javaTypeName, capFieldName, null, false, false, null, null);
1222 javaUnit.emitln(" {");
1223 javaUnit.emit (" return ");
1224 if( fieldTypeNativeSizeFixed ) {
1225 javaUnit.emitln("accessor.get" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx]);");
1226 } else {
1227 javaUnit.emitln("accessor.get" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], md."+sizeDenominator+"SizeInBytes());");
1228 }
1229 javaUnit.emitln(" }");
1230 } else {
1231 javaUnit.emitln(" /** UNKNOWN: "+fqStructFieldName1 +": "+fieldType.getDebugString()+", "+javaType.getDebugString()+" */");
1232 }
1233 javaUnit.emitln();
1234 }
1235 }
1236 }
1237 emitCustomJavaCode(javaUnit, containingJTypeName);
1238 javaUnit.emitTailCode();
1239 javaUnit.emitln("}");
1240 javaUnit.close();
1241 if (needsNativeCode) {
1242 emitCustomJNICode(jniUnit, containingJTypeName);
1243 jniUnit.close();
1244 }
1245 if( GlueGen.debug() ) {
1246 System.err.printf("SE.XX: structCType %s%n", structCType.getDebugString());
1247 System.err.printf("SE.XX: contCTypeName %s%n", containingCType.getDebugString());
1248 System.err.printf("SE.XX: contJTypeName %s%n", containingJType.getDebugString());
1249 }
1250 }
1251 @Override
1252 public void endStructs() throws Exception {}
1253
1254 public static int addStrings2Buffer(StringBuilder buf, final String sep, final String first, final Collection<String> col) {
1255 int num = 0;
1256 if(null==buf) {
1257 buf = new StringBuilder();
1258 }
1259
1260 final Iterator<String> iter = col.iterator();
1261 if(null!=first) {
1262 buf.append(first);
1263 if( iter.hasNext() ) {
1264 buf.append(sep);
1265 }
1266 num++;
1267 }
1268 while( iter.hasNext() ) {
1269 buf.append(iter.next());
1270 if( iter.hasNext() ) {
1271 buf.append(sep);
1272 }
1273 num++;
1274 }
1275 return num;
1276 }
1277
1278 //----------------------------------------------------------------------
1279 // Internals only below this point
1280 //
1281 private void generateArrayFieldNote(final CodeUnit unit, final String leadIn, final String leadOut,
1282 final String fieldName, final Type fieldType, final Ownership ownership,
1283 final boolean constElemCount, final boolean maxOneElem, final String elemCountExpr,
1284 final boolean multiline, final boolean startNewPara) {
1285 final boolean isArray;
1286 final Type referencedType;
1287 final String relationship;
1288 {
1289 if( fieldType.isFunctionPointer() ) {
1290 isArray = false;
1291 referencedType = null;
1292 relationship = "being";
1293 } else if( fieldType.isPointer() ) {
1294 isArray = true;
1295 referencedType = fieldType.getTargetType();
1296 relationship = "referencing";
1297 } else if( fieldType.isArray() ) {
1298 isArray = true;
1299 final Type t = fieldType.getArrayBaseOrPointerTargetType();
1300 if( t != fieldType && t.isPointer() ) {
1301 referencedType = t;
1302 } else {
1303 referencedType = null;
1304 }
1305 relationship = "being";
1306 } else {
1307 isArray = false;
1308 referencedType = null;
1309 relationship = "being";
1310 }
1311 }
1312 // isPointer = true;
1313 if( multiline ) {
1314 if( startNewPara ) {
1315 unit.emitln(" * <p>");
1316 }
1317 unit.emit (" * ");
1318 }
1319 if( null != leadIn ) {
1320 unit.emit(leadIn+" ");
1321 }
1322 final String ownershipTerm;
1323 switch( ownership ) {
1324 case Parent: ownershipTerm = "a <i>struct</i> owned"; break;
1325 case Java: ownershipTerm = "a <i>Java</i> owned"; break;
1326 case Native: ownershipTerm = "a <i>natively</i> owned"; break;
1327 default: ownershipTerm = "a <i>mixed and ambigously</i> owned (<b>warning</b>)"; break;
1328 }
1329 final String what;
1330 if( fieldType.isFunctionPointer() ) {
1331 what = "function pointer";
1332 } else if( isArray ) {
1333 what = "array";
1334 } else {
1335 what = fieldType.getClass().getSimpleName();
1336 }
1337 unit.emit("native field <code>"+fieldName+"</code>, "+relationship+" "+ownershipTerm+" "+what);
1338 if( isArray ) {
1339 unit.emit(" with "+(constElemCount?"fixed":"variable")+" element count");
1340 if( null != elemCountExpr ) {
1341 if( elemCountExpr.startsWith("get") && elemCountExpr.endsWith("()") ) {
1342 unit.emit(" of {@link #"+elemCountExpr+"} ");
1343 } else {
1344 unit.emit(" of <code>"+elemCountExpr+"</code> ");
1345 }
1346 if( constElemCount || Ownership.Mixed == ownership ) {
1347 unit.emit("elements.");
1348 } else {
1349 unit.emit("initial elements.");
1350 }
1351 } else {
1352 unit.emit(".");
1353 }
1354 } else {
1355 unit.emit(".");
1356 }
1357 if( multiline ) {
1358 unit.emitln();
1359 if( startNewPara ) {
1360 unit.emitln(" * </p>");
1361 }
1362 }
1363 if( maxOneElem ) {
1364 if( multiline ) {
1365 unit.emitln(" * <p>");
1366 unit.emitln(" * Maximum element count is <code>1</code>.");
1367 unit.emitln(" * </p>");
1368 } else {
1369 unit.emit(" Maximum element count is <code>1</code>.");
1370 }
1371 }
1372 if( multiline ) {
1373 unit.emitln(" * <p>");
1374 if( null == referencedType ) {
1375 unit.emitln(" * Native Field Signature <code>"+fieldType.getSignature(null).toString()+"</code>");
1376 } else {
1377 unit.emitln(" * Native Signature:");
1378 unit.emitln(" * <ul>");
1379 unit.emitln(" * <li>field-type <code>"+fieldType.getSignature(null).toString()+"</code></li>");
1380 unit.emitln(" * <li>referenced <code>"+referencedType.getSignature(null).toString()+"</code></li>");
1381 unit.emitln(" * </ul>");
1382 }
1383 unit.emitln(" * </p>");
1384 } else {
1385 unit.emit(" NativeSig <code>"+fieldType.getSignature(null).toString()+"</code>");
1386 }
1387 if( null != leadOut ) {
1388 unit.emit(" "+leadOut);
1389 }
1390 if( !multiline) {
1391 unit.emitln();
1392 }
1393 }
1394 private void generateIsNullSignature(final CodeUnit unit, final boolean abstractMethod,
1395 final String fieldName, final Type fieldType, final Ownership ownership,
1396 final String capitalizedFieldName, final boolean constElemCount, final boolean maxOneElem, final String elemCountExpr) {
1397 unit.emitln(" /**");
1398 unit.emitln(" * Returns `true` if native pointer <code>"+fieldName+"</code> is `null`, otherwise `false`.");
1399 generateArrayFieldNote(unit, "Corresponds to", null, fieldName, fieldType, ownership, constElemCount, maxOneElem, elemCountExpr, true, true);
1400 unit.emitln(" */");
1401 unit.emit(" public " + (abstractMethod ? "abstract " : "final ") + "boolean is" + capitalizedFieldName + "Null()");
1402 }
1403 private void generateReleaseSignature(final CodeUnit unit, final boolean abstractMethod,
1404 final String fieldName, final Type fieldType, final Ownership ownership, final String returnTypeName,
1405 final String capitalizedFieldName, final boolean constElemCount, final boolean maxOneElement, final String elemCountExpr) {
1406 unit.emitln(" /**");
1407 generateArrayFieldNote(unit, "Releases memory referenced by", null, fieldName, fieldType, ownership, constElemCount, maxOneElement, elemCountExpr, true, false);
1408 unit.emitln(" */");
1409 unit.emit(" public " + (abstractMethod ? "abstract " : "final ") + returnTypeName + " release" + capitalizedFieldName + "()");
1410 }
1411 private void generateGetterSignature(final CodeUnit unit, final boolean staticMethod, final boolean abstractMethod,
1412 final String fieldName, final Type fieldType, final Ownership ownership, final String returnTypeName,
1413 final String capitalizedFieldName, final String customArgs, final boolean constElemCount,
1414 final boolean maxOneElem, final String elemCountExpr, final String apiDocTail) {
1415 unit.emitln(" /**");
1416 generateArrayFieldNote(unit, "Getter for", null, fieldName, fieldType, ownership, constElemCount, maxOneElem, elemCountExpr, true, false);
1417 if( null != apiDocTail ) {
1418 unit.emitln(" * "+apiDocTail);
1419 }
1420 unit.emitln(" */");
1421 unit.emit(" public " + (staticMethod ? "static " : "final ") + (abstractMethod ? "abstract " : "") + returnTypeName + " get" + capitalizedFieldName + "(");
1422 if( null != customArgs ) {
1423 unit.emit(customArgs);
1424 }
1425 unit.emit(")");
1426 }
1427 private void generateSetterSignature(final CodeUnit unit, final MethodAccess accessMod, final boolean staticMethod, final boolean abstractMethod,
1428 final String fieldName, final Type fieldType, final Ownership ownership, final String returnTypeName,
1429 final String capitalizedFieldName, final String customArgsPre, final String paramTypeName,
1430 final String customArgsPost, final boolean constElemCount, final boolean maxOneElem, final String elemCountExpr,
1431 final String apiDocDetail, final String apiDocArgs) {
1432 unit.emitln(" /**");
1433 generateArrayFieldNote(unit, "Setter for", null, fieldName, fieldType, ownership, constElemCount, maxOneElem, elemCountExpr, true, false);
1434 if( null != apiDocDetail ) {
1435 unit.emitln(" * <p>");
1436 unit.emitln(" * "+apiDocDetail);
1437 unit.emitln(" * </p>");
1438 }
1439 if( null != apiDocArgs ) {
1440 unit.emitln(apiDocArgs);
1441 }
1442 unit.emitln(" */");
1443 unit.emit(" "+accessMod.getJavaName() + " " + (staticMethod ? "static " : "final ") + (abstractMethod ? "abstract " : "") + returnTypeName + " set" + capitalizedFieldName + "(");
1444 if( null != customArgsPre ) {
1445 unit.emit(customArgsPre+", ");
1446 }
1447 unit.emit(paramTypeName + " src");
1448 if( null != customArgsPost ) {
1449 unit.emit(", "+customArgsPost);
1450 }
1451 unit.emit(")");
1452 }
1453
1454 private void generateOffsetAndSizeArrays(final CodeUnit unit, final String prefix,
1455 final String fieldName, final Type fieldType,
1456 final Field field, final String postfix) {
1457 if(null != field) {
1458 unit.emit(prefix+"private static final int[] "+fieldName+"_offset = new int[] { ");
1459 for( int i=0; i < machDescTargetConfigs.length; i++ ) {
1460 if(0<i) {
1461 unit.emit(", ");
1462 }
1463 unit.emit(field.getOffset(machDescTargetConfigs[i].md) +
1464 " /* " + machDescTargetConfigs[i].name() + " */");
1465 }
1466 unit.emitln(" };");
1467 }
1468 if(null!=fieldType) {
1469 unit.emit(prefix+"private static final int[] "+fieldName+"_size = new int[] { ");
1470 for( int i=0; i < machDescTargetConfigs.length; i++ ) {
1471 if(0<i) {
1472 unit.emit(", ");
1473 }
1474 unit.emit(fieldType.getSize(machDescTargetConfigs[i].md) +
1475 " /* " + machDescTargetConfigs[i].name() + " */");
1476 }
1477 unit.emit(" };");
1478 if( null != postfix ) {
1479 unit.emitln(postfix);
1480 } else {
1481 unit.emitln();
1482 }
1483 }
1484 }
1485
1486 private void generateJavaCallbackCode(final JavaCallbackDef jcbd, final FunctionType funcType) {
1487 final FunctionSymbol funcSym = new FunctionSymbol("callback", funcType);
1488 funcSym.addAliasedName(jcbd.cbFuncTypeName);
1489 LOG.log(INFO, "JavaCallback: fSym {0}, {1}", funcSym.getAliasedString(), jcbd);
1490
1491 final String cbSimpleClazzName = CodeGenUtils.capitalizeString(jcbd.cbFuncTypeName);
1492 final String cbFQClazzName = cfg.packageName()+"."+cfg.className()+"."+cbSimpleClazzName;
1493
1494 final JavaCallbackInfo jcbi0 = javaCallbackInterfaceMap.get(cbFQClazzName);
1495 if( null != jcbi0 ) {
1496 // Reuse callback-func interface, can't duplicate
1497 if( jcbi0.cbFuncUserParamIdx != jcbd.cbFuncUserParamIdx ) {
1498 throw new UnsupportedOperationException("Reused FuncTypeName "+jcbd.cbFuncTypeName+" used with different FuncUserParamIdx "+jcbi0.cbFuncUserParamIdx+" -> "+jcbd.cbFuncUserParamIdx+". Func "+
1499 funcType.toString(jcbd.cbFuncTypeName, false, true));
1500 }
1501 final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, jcbi0.staticCBMethodSignature,
1502 funcType, jcbi0.cbFuncBinding, jcbi0.cbFuncUserParamIdx, jcbd.cbFuncKeyIndices,
1503 jcbd.setFuncName, jcbd.setFuncUserParamIdx, jcbd.setFuncKeyIndices,
1504 jcbd.userParamClassName, jcbd.customKeyClassName);
1505 cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1);
1506 LOG.log(INFO, "JavaCallbackInfo: Reusing {0} -> {1}", jcbd.setFuncName, jcbi0);
1507 } else {
1508 final StringBuilder cbMethodSignature = new StringBuilder();
1509 final List<MethodBinding> mbs;
1510 if( !cfg.shouldIgnoreInInterface(jcbd.cbFuncTypeName) ) {
1511 javaUnit.emitln(" /** JavaCallback interface: "+jcbd.cbFuncTypeName+" -> "+funcType.toString(jcbd.cbFuncTypeName, false, true)+" */");
1512 javaUnit.emitln(" public static interface "+cbSimpleClazzName+" {");
1513 mbs = generateFunctionInterfaceCode(javaUnit, funcSym, jcbd, cbMethodSignature);
1514 javaUnit.emitln(" }");
1515 javaUnit.emitln();
1516 } else {
1517 LOG.log(WARNING, "JavaCallbackInfo: Java Configuration indicate current JavaCallback must be ignored so assume JavaCallback meet presents requirements of {0}", jcbd.setFuncName);
1518 mbs = generateFunctionInterfaceCode(null, funcSym, jcbd, cbMethodSignature);
1519 }
1520 if( 1 != mbs.size() ) {
1521 throw new UnsupportedOperationException("Multiple bindings generated where only 1 is allowed for func "+funcType.toString(jcbd.cbFuncTypeName, false, true));
1522 }
1523 final MethodBinding cbFuncBinding = mbs.get(0);
1524 if( !cbFuncBinding.getJavaReturnType().isVoid() && !cbFuncBinding.getJavaReturnType().isPrimitive() ) {
1525 throw new UnsupportedOperationException("Non void or non-primitive callback return types not suppored. Java "+
1526 cbFuncBinding.getJavaReturnType()+", func "+funcType.toString(jcbd.cbFuncTypeName, false, true));
1527 }
1528 final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, cbMethodSignature.toString(),
1529 funcType, cbFuncBinding, jcbd.cbFuncUserParamIdx, jcbd.cbFuncKeyIndices,
1530 jcbd.setFuncName, jcbd.setFuncUserParamIdx, jcbd.setFuncKeyIndices,
1531 jcbd.userParamClassName, jcbd.customKeyClassName);
1532 cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1);
1533 javaCallbackInterfaceMap.put(cbFQClazzName, jcbi1);
1534 LOG.log(INFO, "JavaCallbackInfo: Added {0} -> {1}", jcbd.setFuncName, jcbi1);
1535 }
1536 }
1537 private final Map<String, JavaCallbackInfo> javaCallbackInterfaceMap = new HashMap<String, JavaCallbackInfo>();
1538
1539 private List<MethodBinding> generateFunctionInterfaceCode(final JavaCodeUnit javaUnit, final FunctionSymbol funcSym, final JavaCallbackDef jcbd, final StringBuilder methodSignature) {
1540 // Emit method call and associated native code
1541 MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, null, null);
1542
1543 // Replace optional userParam argument 'void*' with Object
1544 final int userParamIdx = jcbd.cbFuncUserParamIdx;
1545 if( 0 <= userParamIdx && userParamIdx < mb.getNumArguments() ) {
1546 final JavaType t = mb.getJavaArgumentType(userParamIdx);
1547 if ( t.isCVoidPointerType() ) {
1548 final JavaType mappedType;
1549 if( null != jcbd.userParamClassName ) {
1550 mappedType = JavaType.createForNamedClass( jcbd.userParamClassName );
1551 mb = mb.replaceJavaArgumentType(userParamIdx, JavaType.forObjectClass());
1552 } else {
1553 mappedType = JavaType.forObjectClass();
1554 }
1555 mb = mb.replaceJavaArgumentType(userParamIdx, mappedType);
1556 }
1557 }
1558 // Produce the method signature
1559 {
1560 methodSignature.append("(");
1561 for(int i=0; i<mb.getNumArguments(); ++i) {
1562 final JavaType t = mb.getJavaArgumentType(i);
1563 methodSignature.append(t.getDescriptor(cfg));
1564 }
1565 methodSignature.append(")");
1566 final JavaType rt = mb.getJavaReturnType();
1567 methodSignature.append(rt.getDescriptor(cfg));
1568 }
1569
1570 // JavaTypes representing C pointers in the initial
1571 // MethodBinding have not been lowered yet to concrete types
1572 final List<MethodBinding> bindings = expandMethodBinding(mb);
1573
1574 final boolean useNIOOnly = true;
1575 final boolean useNIODirectOnly = true;
1576
1577 if( null != javaUnit) {
1578 for (final MethodBinding binding : bindings) {
1579 // Emit public Java entry point for calling this function pointer
1580 final JavaMethodBindingEmitter emitter = new JavaMethodBindingEmitter(binding,
1581 javaUnit,
1582 cfg.runtimeExceptionType(),
1583 cfg.unsupportedExceptionType(),
1584 false, // emitBody
1585 cfg.tagNativeBinding(),
1586 false, // eraseBufferAndArrayTypes
1587 useNIOOnly,
1588 useNIODirectOnly,
1589 false, // forDirectBufferImplementation
1590 false, // forIndirectBufferAndArrayImplementation
1591 true, // isUnimplemented
1592 true, // isInterface
1593 false, // isNativeMethod
1594 false, // isPrivateNativeMethod
1595 cfg) {
1596 @Override
1597 protected String getBaseIndentString() { return " "; }
1598 };
1599 emitter.addModifier(JavaMethodBindingEmitter.PUBLIC);
1600 emitter.emit();
1601 }
1602 }
1603 return bindings;
1604 }
1605
1606 private void generateFunctionPointerCode(final Set<MethodBinding> methodBindingSet,
1607 final JavaCodeUnit javaUnit, final CCodeUnit jniUnit,
1608 final String structCTypeName, final Type containingCType, final JavaType containingJType,
1609 final int i, final FunctionSymbol funcSym, final String returnSizeLookupName) {
1610 // Emit method call and associated native code
1611 final MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, containingJType, containingCType);
1612 mb.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis
1613
1614 // JavaTypes representing C pointers in the initial
1615 // MethodBinding have not been lowered yet to concrete types
1616 final List<MethodBinding> bindings = expandMethodBinding(mb);
1617
1618 final boolean useNIOOnly = true;
1619 final boolean useNIODirectOnly = true;
1620
1621 for (final MethodBinding binding : bindings) {
1622 if(!methodBindingSet.add(binding)) {
1623 // skip .. already exisiting binding ..
1624 continue;
1625 }
1626 // Emit public Java entry point for calling this function pointer
1627 JavaMethodBindingEmitter emitter =
1628 new JavaMethodBindingEmitter(binding,
1629 javaUnit,
1630 cfg.runtimeExceptionType(),
1631 cfg.unsupportedExceptionType(),
1632 true, // emitBody
1633 cfg.tagNativeBinding(),
1634 false, // eraseBufferAndArrayTypes
1635 useNIOOnly,
1636 useNIODirectOnly,
1637 false, // forDirectBufferImplementation
1638 false, // forIndirectBufferAndArrayImplementation
1639 false, // isUnimplemented
1640 false, // isInterface
1641 false, // isNativeMethod
1642 false, // isPrivateNativeMethod
1643 cfg);
1644 emitter.addModifier(JavaMethodBindingEmitter.PUBLIC);
1645 emitter.addModifier(JavaMethodBindingEmitter.FINAL);
1646 emitter.emit();
1647 javaUnit.emitln();
1648
1649 // Emit private native Java entry point for calling this function pointer
1650 emitter =
1651 new JavaMethodBindingEmitter(binding,
1652 javaUnit,
1653 cfg.runtimeExceptionType(),
1654 cfg.unsupportedExceptionType(),
1655 false, // emitBody
1656 cfg.tagNativeBinding(),
1657 true, // eraseBufferAndArrayTypes
1658 useNIOOnly,
1659 useNIODirectOnly,
1660 true, // forDirectBufferImplementation
1661 false, // forIndirectBufferAndArrayImplementation
1662 false, // isUnimplemented
1663 false, // isInterface
1664 false, // isNativeMethod
1665 true, cfg);
1666 emitter.addModifier(JavaMethodBindingEmitter.PRIVATE);
1667 emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
1668 emitter.emit();
1669 javaUnit.emitln();
1670
1671 // Emit (private) C entry point for calling this function pointer
1672 final CMethodBindingEmitter cEmitter =
1673 new CMethodBindingEmitter(binding,
1674 jniUnit,
1675 javaUnit.pkgName,
1676 containingJType.getName(),
1677 true, // FIXME: this is optional at this point
1678 false,
1679 true,
1680 false, // forIndirectBufferAndArrayImplementation
1681 machDescJava, getConfig());
1682 cEmitter.setIsCStructFunctionPointer(true);
1683 prepCEmitter(returnSizeLookupName, binding.getJavaReturnType(), cEmitter);
1684 cEmitter.emit();
1685 jniUnit.emitln();
1686 }
1687 }
1688
1689 private String getArrayArrayLengthExpr(final ArrayType type, final String returnSizeLookupName, final boolean hasFixedTypeLen[], final int[][] lengthRes) {
1690 final int[] length = new int[type.arrayDimension()];
1691 lengthRes[0] = length;
1692 final StringBuilder lengthExpr = new StringBuilder();
1693 hasFixedTypeLen[0] = true;
1694 ArrayType typeIter = type;
1695 for(int i=0; i<length.length; i++) {
1696 if( null!=typeIter && typeIter.hasLength() ) {
1697 length[i] = typeIter.getLength();
1698 if( 0 < i ) {
1699 lengthExpr.append("*");
1700 }
1701 lengthExpr.append(length[i]);
1702 } else {
1703 length[i] = -1;
1704 hasFixedTypeLen[0] = false;
1705 }
1706 if( null != typeIter ) {
1707 typeIter = typeIter.getTargetType().asArray();
1708 }
1709 }
1710 final String cfgVal = cfg.returnedArrayLength(returnSizeLookupName);
1711 if( null != cfgVal ) {
1712 if( hasFixedTypeLen[0] ) {
1713 LOG.log(WARNING, type.getASTLocusTag(),
1714 "struct array field '"+returnSizeLookupName+"' of '"+type+"' length '"+Arrays.toString(length)+"' overwritten by cfg-expression: "+cfgVal);
1715 }
1716 return cfgVal;
1717 }
1718 if( hasFixedTypeLen[0] ) {
1719 return lengthExpr.toString();
1720 } else {
1721 LOG.log(WARNING, type.getASTLocusTag(),
1722 "struct array field '"+returnSizeLookupName+"' length '"+Arrays.toString(length)+"' without fixed- nor configured-size: {0}", type);
1723 return null;
1724 }
1725 }
1726
1727 private boolean requiresGetCStringLength(final Type fieldType, final String returnSizeLookupName) {
1728 if( !cfg.returnsString(returnSizeLookupName) && !cfg.returnsStringOnly(returnSizeLookupName) ) {
1729 return false;
1730 }
1731 final PointerType pointerType = fieldType.asPointer();
1732 if( null != pointerType ) {
1733 return null == cfg.returnedArrayLength(returnSizeLookupName);
1734 }
1735 return false;
1736 }
1737
1738 private static final String GetElemValueApiDocTail = "@return element value of the corresponding field-array";
1739 private static final String GetElemCountApiDocTail = "@return element count of the corresponding field-array";
1740
1741 private static final String SetSubArrayArgsPost = "final int srcPos, final int destPos, final int length";
1742 private static final String SetSubArrayArgsCheck = " if( 0 > srcPos || 0 > destPos || 0 > length || srcPos + length > src.length ) { throw new IndexOutOfBoundsException(\"src[pos \"+srcPos+\", length \"+src.length+\"], destPos \"+destPos+\", length \"+length); }";
1743 private static final String SetSubArrayApiDocDetail = "Copies the given source elements into the respective field's existing memory.";
1744 private static final String SetSubArrayApiDocArgs =
1745 " * @param src the source array of elements\n"+
1746 " * @param srcPos starting element position within the source array with 'srcPos >= 0` && `srcPos + length <= src.length`, otherwise an {@link IndexOutOfBoundsException} is thrown\n"+
1747 " * @param destPos starting element position within the destination with 'destPos >= 0` && `destPos + length <= elemCount`, otherwise an exception is thrown\n"+
1748 " * @param length the element count to be copied with 'length >= 0` && `srcPos + length <= src.length` && `destPos + length <= elemCount`, otherwise an {@link IndexOutOfBoundsException} is thrown\n"+
1749 " * @return this instance of chaining";
1750
1751 private static final String SetArrayArgsPre = "final boolean subset";
1752 private static final String SetArrayArgsPost = "final int srcPos, final int destPos, final int length";
1753 private static final String SetArrayArgsCheck = " if( 0 > srcPos || 0 > destPos || 0 > length || srcPos + length > src.length ) { throw new IndexOutOfBoundsException(\"subset \"+subset+\", src[pos \"+srcPos+\", length \"+src.length+\"], destPos \"+destPos+\", length \"+length); }";
1754 private static final String SetArrayApiDocDetail = "Copies the given source elements into the respective field, either writing into the existing memory or creating a new memory and referencing it.";
1755 private static final String SetArrayApiDocArgs =
1756 " * @param subset if `true` keeps the underlying memory and only allows to set up to `elemCount` elements. Otherwise may replace the underlying memory if `destPos + length != elemCount`.\n"+
1757 " * @param src the source array of elements\n"+
1758 " * @param srcPos starting element position within the source array with 'srcPos >= 0` && `srcPos + length <= src.length`, otherwise an {@link IndexOutOfBoundsException} is thrown\n"+
1759 " * @param destPos starting element position within the destination with 'destPos >= 0`. If `subset == true`, `destPos + length <= elemCount` also must be be `true`. Otherwise an exception is thrown\n"+
1760 " * @param length the element count to be copied with 'length >= 0` && `srcPos + length <= src.length`, otherwise an {@link IndexOutOfBoundsException} is thrown\n"+
1761 " * @return this instance of chaining";
1762
1763 private static final String SetReplaceArrayArgsPost = "final int srcPos, final int length";
1764 private static final String SetReplaceArrayArgsCheck = " if( 0 > srcPos || 0 > length || srcPos + length > src.length ) { throw new IndexOutOfBoundsException(\"src[pos \"+srcPos+\", length \"+src.length+\"], length \"+length); }";
1765 private static final String SetReplaceArrayApiDocDetail = "Replaces the respective field's memory with a new memory segment containing given source elements and referencing it.";
1766 private static final String SetReplaceArrayApiDocArgs =
1767 " * @param src the source array of elements\n"+
1768 " * @param srcPos starting element position within the source array with 'srcPos >= 0` && `srcPos + length <= src.length`, otherwise an {@link IndexOutOfBoundsException} is thrown\n"+
1769 " * @param length the element count to be copied with 'length >= 0` && `srcPos + length <= src.length`, otherwise an {@link IndexOutOfBoundsException} is thrown\n"+
1770 " * @return this instance of chaining";
1771
1772 private static final String GetArrayArgs = "final int destPos, final int length";
1773 private static final String GetArrayArgsCheck = " if( 0 > srcPos || 0 > destPos || 0 > length || destPos + length > dest.length ) { throw new IndexOutOfBoundsException(\"dest[pos \"+destPos+\", length \"+dest.length+\"], srcPos \"+srcPos+\", length \"+length); }";
1774
1775
1776 private void generateArrayGetterSetterCode(final JavaCodeUnit unit,
1777 final CompoundType structCType,
1778 final JavaType containingJType,
1779 final int i, final Field field, final String fieldName,
1780 final boolean immutableAccess,
1781 final String fqStructFieldName) throws Exception {
1782 final Type fieldType = field.getType();
1783 final TypeInfo opaqueTypeInfo = cfg.typeInfo(fieldType);
1784 final boolean isOpaque = null != opaqueTypeInfo;
1785 final Type baseElemType = fieldType.getArrayBaseOrPointerTargetType();
1786 if( GlueGen.debug() ) {
1787 System.err.printf("SE.ac.%02d: fieldName %s, fqName %s%n", (i+1), fieldName, fqStructFieldName);
1788 System.err.printf("SE.ac.%02d: structCType %s, %s%n", (i+1), structCType.toString(), structCType.getSignature(null).toString());
1789 System.err.printf("SE.ac.%02d: fieldType %s, %s%n", (i+1), fieldType.toString(), fieldType.getSignature(null).toString());
1790 System.err.printf("SE.ac.%02d: opaqueInfo %b, %s%n", (i+1), isOpaque, opaqueTypeInfo);
1791 System.err.printf("SE.ac.%02d: baseElemType %s, %s%n", (i+1), baseElemType.toString(), baseElemType.getSignature(null).toString());
1792 }
1793
1794 //
1795 // Collect all required information including considering Opaque types
1796 //
1797 final String containingJTypeName = containingJType.getName();
1798 final boolean isStringOnly = cfg.returnsStringOnly(fqStructFieldName); // exclude alternative ByteBuffer representation to String
1799 final boolean isString = isStringOnly || cfg.returnsString(fqStructFieldName);
1800 if( isString ) {
1801 unit.addTailCode(optStringCharsetCode);
1802 }
1803 final boolean isPointer;
1804 final boolean isPrimitive;
1805 final boolean isConstValue; // Immutable 'const type value', immutable array 'const type value[]', or as mutable pointer 'const type * value'
1806 final MethodAccess accessMod = MethodAccess.PUBLIC;
1807 final Ownership ownership;
1808 final String elemCountExpr;
1809 final boolean constElemCount; // if true, implies native ownership for pointer referenced memory!
1810 final boolean staticElemCount;
1811 final JavaType baseJElemType;
1812 final String baseJElemTypeName;
1813 final boolean primElemFixedSize; // Is Primitive element size fixed? If not, use md.*_Size[]
1814 final boolean baseIsPointer; // Is Primitive element a pointer?
1815 final String baseElemSizeDenominator;
1816 final boolean useGetCStringLength;
1817 final boolean maxOneElement; // zero or one element
1818 if( isOpaque && opaqueTypeInfo.pointerDepth() <= 1 || // explicit 'Opaque' (config)
1819 ( fieldType.isPrimitive() && !baseElemType.isFunctionPointer() ) || // a primitive and non-function-ptr
1820 ( fieldType.isPointer() && baseElemType.isVoid() ) // like 'void*' -> 'void'
1821 )
1822 {
1823 // Emulating array w/ 1 element
1824 isPrimitive = true;
1825 isPointer = false;
1826 isConstValue = fieldType.isConst();
1827 elemCountExpr = "1";
1828 constElemCount = true;
1829 ownership = Ownership.Parent;
1830 staticElemCount = true;
1831 baseJElemType = null;
1832 baseJElemTypeName = compatiblePrimitiveJavaTypeName(fieldType, machDescJava);
1833 primElemFixedSize = false;
1834 baseIsPointer = fieldType.isPointer();
1835 baseElemSizeDenominator = baseIsPointer ? "pointer" : baseJElemTypeName ;
1836 useGetCStringLength = false;
1837 maxOneElement = true;
1838 } else {
1839 if( fieldType.arrayDimension() > 0 ) {
1840 final int[][] arrayLengthRes = new int[1][];
1841 final boolean[] _useFixedArrayLen = { false };
1842 elemCountExpr = getArrayArrayLengthExpr(fieldType.asArray(), fqStructFieldName, _useFixedArrayLen, arrayLengthRes);
1843 if( null == elemCountExpr ) {
1844 final String msg = "SKIP unsized array in struct: "+fqStructFieldName+": "+fieldType.getSignature(null).toString();
1845 unit.emitln(" // "+msg);
1846 unit.emitln();
1847 LOG.log(WARNING, structCType.getASTLocusTag(), msg);
1848 return;
1849 }
1850 // final int arrayLength = arrayLengthRes[0][0];
1851 constElemCount = _useFixedArrayLen[0];
1852 ownership = Ownership.Parent; // a fixed linear array
1853 staticElemCount = constElemCount;
1854 // baseCElemType = pointerType.getBaseType();
1855 isPointer = false;
1856 useGetCStringLength = false;
1857 } else {
1858 final String _elemCountExpr = cfg.returnedArrayLength(fqStructFieldName);
1859 isPointer = true;
1860 if( null == _elemCountExpr && isString ) {
1861 useGetCStringLength = true;
1862 unit.addTailCode(optStringMaxStrnlenCode);
1863 elemCountExpr = "Buffers.strnlen(pString, _max_strnlen)+1";
1864 constElemCount = false;
1865 ownership = Ownership.Java;
1866 staticElemCount = constElemCount;
1867 } else if( null == _elemCountExpr ) {
1868 useGetCStringLength = false;
1869 elemCountExpr = "0";
1870 constElemCount = false;
1871 ownership = Ownership.Java;
1872 staticElemCount = constElemCount;
1873 } else {
1874 // null != _elemCountExpr
1875 useGetCStringLength = false;
1876 elemCountExpr = _elemCountExpr;
1877 boolean _constElemCount = false;
1878 boolean _staticElemCount = false;
1879
1880 // try constant intenger 1st
1881 try {
1882 Integer.parseInt(elemCountExpr);
1883 _constElemCount = true;
1884 _staticElemCount = true;
1885 } catch (final Exception e ) {}
1886 if( !_constElemCount ) {
1887 // check for const length field
1888 if( elemCountExpr.startsWith("get") && elemCountExpr.endsWith("()") ) {
1889 final String baseLenFieldName = elemCountExpr.substring(3, elemCountExpr.length()-2);
1890 String lenFieldName = CodeGenUtils.decapitalizeString( baseLenFieldName );
1891 Field lenField = structCType.getField(lenFieldName);
1892 if( null == lenField ) {
1893 lenFieldName = baseLenFieldName;
1894 lenField = structCType.getField(lenFieldName);
1895 }
1896 if( null == lenField ) {
1897 throw new GlueGenException("Unable to creating array accessors for field \"" +
1898 fqStructFieldName + "\", because elemCountExpr specify following getter \"" +
1899 elemCountExpr + "\" and host structure doesn't contain following field \"" +
1900 CodeGenUtils.decapitalizeString( baseLenFieldName ) + "\" or \"" +
1901 baseLenFieldName + "\"",
1902 fieldType.getASTLocusTag());
1903 }
1904 _constElemCount = lenField.getType().isConst();
1905 LOG.log(INFO, structCType.getASTLocusTag(),
1906 unit.className+": elemCountExpr "+elemCountExpr+", lenFieldName "+lenFieldName+" -> "+lenField.toString()+", isConst "+_constElemCount);
1907 }
1908 }
1909 constElemCount = _constElemCount;
1910 if( constElemCount ) {
1911 ownership = Ownership.Native;
1912 } else {
1913 ownership = Ownership.Mixed;
1914 }
1915 staticElemCount = _staticElemCount;
1916 }
1917 }
1918 boolean _maxOneElement = cfg.maxOneElement(fqStructFieldName);
1919 if( !_maxOneElement ) {
1920 try {
1921 _maxOneElement = 1 == Integer.parseInt(elemCountExpr);
1922 } catch (final Exception e ) {}
1923 }
1924 maxOneElement = _maxOneElement;
1925 if( GlueGen.debug() ) {
1926 System.err.printf("SE.ac.%02d: ownership %s%n", (i+1), ownership);
1927 }
1928 baseIsPointer = baseElemType.isPointer();
1929 isConstValue = baseElemType.isConst();
1930 if( baseIsPointer ) {
1931 baseJElemType = javaType(Long.TYPE); // forced mapping pointer-pointer -> long
1932 } else {
1933 try {
1934 baseJElemType = typeToJavaType(baseElemType, machDescJava);
1935 } catch (final Exception e ) {
1936 throw new GlueGenException("Error occurred while creating array/pointer accessor for field \"" +
1937 fqStructFieldName + "\", baseType "+baseElemType.getDebugString()+", topType "+fieldType.getDebugString(),
1938 fieldType.getASTLocusTag(), e);
1939 }
1940 }
1941 baseJElemTypeName = baseJElemType.getName();
1942 isPrimitive = baseJElemType.isPrimitive() || baseElemType.isPrimitive() || baseElemType.isFunctionPointer();
1943 primElemFixedSize = isPrimitive ? baseElemType.getSize().hasFixedNativeSize() : false;
1944 baseElemSizeDenominator = baseIsPointer ? "pointer" : baseJElemTypeName ;
1945 }
1946 if( GlueGen.debug() ) {
1947 System.err.printf("SE.ac.%02d: baseJElemType %s%n", (i+1), (null != baseJElemType ? baseJElemType.getDebugString() : null));
1948 }
1949 // Collect fixed primitive-type mapping metrics
1950 final String primJElemTypeBufferName;
1951 final int primElemSize;
1952 final String primElemSizeExpr;
1953 final boolean isByteBuffer;
1954 if( isPrimitive ) {
1955 final Class<? extends Buffer> primJElemTypeBufferClazz = Buffers.typeNameToBufferClass(baseJElemTypeName);
1956 if( null == primJElemTypeBufferClazz ) {
1957 final String msg = "Failed to map '"+baseJElemTypeName+"' to Buffer class, field "+field+", j-type "+baseJElemType;
1958 unit.emitln(" // ERROR: "+msg);
1959 unit.emitln();
1960 LOG.log(SEVERE, structCType.getASTLocusTag(), msg);
1961 throw new InternalError(msg);
1962 }
1963 primJElemTypeBufferName = primJElemTypeBufferClazz.getSimpleName();
1964 primElemSize = Buffers.sizeOfBufferElem(primJElemTypeBufferClazz);
1965 isByteBuffer = null != primJElemTypeBufferClazz ? ByteBuffer.class.isAssignableFrom(primJElemTypeBufferClazz) : false;
1966 if( primElemFixedSize ) {
1967 primElemSizeExpr = String.valueOf(primElemSize);
1968 } else {
1969 primElemSizeExpr = "md."+baseElemSizeDenominator+"SizeInBytes()";
1970 }
1971 } else {
1972 primJElemTypeBufferName = null;
1973 primElemSize = 0;
1974 isByteBuffer = false;
1975 primElemSizeExpr = null;
1976 }
1977
1978 final String capitalFieldName = CodeGenUtils.capitalizeString(fieldName);
1979 final boolean ownElemCountHandling;
1980 final String getElemCountFuncExpr, setElemCountLengthFunc;
1981 if( constElemCount ) {
1982 ownElemCountHandling = true;
1983 getElemCountFuncExpr = "get"+capitalFieldName+"ElemCount()";
1984 setElemCountLengthFunc = null;
1985 } else {
1986 if( useGetCStringLength ) {
1987 ownElemCountHandling = true;
1988 getElemCountFuncExpr = "get"+capitalFieldName+"ElemCount()";
1989 setElemCountLengthFunc = null;
1990 } else if( elemCountExpr.startsWith("get") && elemCountExpr.endsWith("()") ) {
1991 ownElemCountHandling = false;
1992 getElemCountFuncExpr = elemCountExpr;
1993 setElemCountLengthFunc = "set" + elemCountExpr.substring(3, elemCountExpr.length()-2);
1994 } else {
1995 ownElemCountHandling = true;
1996 getElemCountFuncExpr = "get"+capitalFieldName+"ElemCount()";
1997 setElemCountLengthFunc = "set"+capitalFieldName+"ElemCount";
1998 }
1999 }
2000 if( GlueGen.debug() ) {
2001 System.err.printf("SE.ac.%02d: baseJElemTypeName %s%n", (i+1), baseJElemTypeName);
2002 System.err.printf("SE.ac.%02d: elemCountExpr: %s (const %b, ownership %s), ownArrayLenCpde %b, maxOneElement %b, "+
2003 "Primitive[is %b, aptr %b, buffer %s, fixedSize %b, elemSize %d, sizeDenom %s, sizeExpr %s, isByteBuffer %b], "+
2004 "isString[%b, only %b, strnlen %b], isPointer %b, isOpaque %b, constVal %b, immutableAccess %b%n",
2005 (i+1), elemCountExpr, constElemCount, ownership, ownElemCountHandling, maxOneElement,
2006 isPrimitive, baseIsPointer, primJElemTypeBufferName, primElemFixedSize, primElemSize, baseElemSizeDenominator, primElemSizeExpr, isByteBuffer,
2007 isString, isStringOnly, useGetCStringLength,
2008 isPointer, isOpaque, isConstValue, immutableAccess);
2009 }
2010
2011 //
2012 // Emit ..
2013 //
2014 if( ownElemCountHandling ) {
2015 if( constElemCount ) {
2016 if( !( isPrimitive && !isPointer && staticElemCount && maxOneElement ) ) { // drop useless `static get*ElemCount() { return 1; }`
2017 generateGetterSignature(unit, staticElemCount, false, fieldName, fieldType, ownership, "int", capitalFieldName+"ElemCount", null, constElemCount, maxOneElement, elemCountExpr, GetElemCountApiDocTail);
2018 unit.emitln(" { return "+elemCountExpr+"; }");
2019 }
2020 } else if( useGetCStringLength ) {
2021 generateGetterSignature(unit, staticElemCount, false, fieldName, fieldType, ownership, "int", capitalFieldName+"ElemCount", null, constElemCount, maxOneElement, elemCountExpr, GetElemCountApiDocTail);
2022 unit.emitln(" {");
2023 unit.emitln(" final long pString = PointerBuffer.wrap( accessor.slice(" + fieldName+"_offset[mdIdx], PointerBuffer.POINTER_SIZE) ).get(0);");
2024 unit.emitln(" return 0 != pString ? "+elemCountExpr+" : 0;");
2025 unit.emitln(" }");
2026 } else {
2027 unit.emitln(" private int _"+fieldName+"ArrayLen = "+elemCountExpr+"; // "+(constElemCount ? "const" : "initial")+" array length");
2028 generateGetterSignature(unit, staticElemCount, false, fieldName, fieldType, ownership, "int", capitalFieldName+"ElemCount", null, constElemCount, maxOneElement, elemCountExpr, GetElemCountApiDocTail);
2029 unit.emitln(" { return _"+fieldName+"ArrayLen; }");
2030 if( !immutableAccess ) {
2031 generateSetterSignature(unit, MethodAccess.PRIVATE, staticElemCount, false, fieldName, fieldType, ownership, "void", capitalFieldName+"ElemCount", null, "int",
2032 null, constElemCount, maxOneElement, elemCountExpr, null, null);
2033 unit.emitln(" { _"+fieldName+"ArrayLen = src; }");
2034 }
2035 }
2036 unit.emitln();
2037 }
2038
2039 // Null query for pointer
2040 if( isPointer ) {
2041 generateIsNullSignature(unit, false, fieldName, fieldType, ownership, capitalFieldName, constElemCount, maxOneElement, elemCountExpr);
2042 unit.emitln(" {");
2043 unit.emitln(" return 0 == PointerBuffer.wrap(getBuffer(), "+fieldName+"_offset[mdIdx], 1).get(0);");
2044 unit.emitln(" }");
2045 unit.emitln();
2046 if( !constElemCount && !immutableAccess ) {
2047 generateReleaseSignature(unit, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, constElemCount, maxOneElement, elemCountExpr);
2048 unit.emitln(" {");
2049 unit.emitln(" accessor.setLongAt("+fieldName+"_offset[mdIdx], 0, md.pointerSizeInBytes()); // write nullptr");
2050 unit.emitln(" _eb"+capitalFieldName+" = null;");
2051 emitSetElemCount(unit, setElemCountLengthFunc, "0", !useGetCStringLength, capitalFieldName, structCType, " ");
2052 unit.emitln(" return this;");
2053 unit.emitln(" }");
2054 unit.emitln(" @SuppressWarnings(\"unused\")");
2055 if( baseIsPointer ) {
2056 unit.emitln(" private PointerBuffer _eb"+capitalFieldName+"; // cache new memory buffer ensuring same lifecycle");
2057 } else {
2058 unit.emitln(" private ElementBuffer _eb"+capitalFieldName+"; // cache new memory buffer ensuring same lifecycle");
2059 }
2060 unit.emitln();
2061 }
2062 }
2063
2064 // Setter
2065 if( immutableAccess ) {
2066 generateArrayFieldNote(unit, " /** SKIP setter for immutable", " */", fieldName, fieldType, ownership, constElemCount, maxOneElement, elemCountExpr, false, false);
2067 unit.emitln();
2068 } else if( isPointer && isConstValue && ( Ownership.Native == ownership || constElemCount ) ) {
2069 generateArrayFieldNote(unit, " /** SKIP setter for constValue constElemCount Pointer w/ native ownership", " */", fieldName, fieldType, ownership, constElemCount, maxOneElement, elemCountExpr, false, false);
2070 unit.emitln();
2071 } else if( !isPointer && isConstValue ) {
2072 generateArrayFieldNote(unit, " /** SKIP setter for constValue Array", " */", fieldName, fieldType, ownership, constElemCount, maxOneElement, elemCountExpr, false, false);
2073 unit.emitln();
2074 } else if( isPrimitive ) {
2075 // Setter Primitive
2076 if( maxOneElement ) {
2077 // Setter Primitive Single Pointer + Array
2078 if( isPointer ) {
2079 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null, baseJElemTypeName,
2080 null, constElemCount, maxOneElement, elemCountExpr, null, null);
2081 if( isConstValue ) {
2082 // constElemCount/Ownership.Native excluded: SKIP setter for constValue constElemCount Pointer w/ native ownership
2083 if( Ownership.Native == ownership ) {
2084 throw new InternalError("Native ownership but adding potential memory-replacement for '"+fqStructFieldName+"': "+fieldType.getSignature(null).toString());
2085 }
2086 unit.emitln(" {");
2087 if( baseIsPointer ) {
2088 unit.emitln(" final PointerBuffer eb = PointerBuffer.allocateDirect(1);");
2089 unit.emitln(" eb.put(0, src);");
2090 } else {
2091 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+primElemSizeExpr+", 1);");
2092 unit.emit (" eb.getByteBuffer()");
2093 if( !isByteBuffer ) {
2094 unit.emit(".as"+primJElemTypeBufferName+"()");
2095 }
2096 unit.emitln(".put(0, src);");
2097 }
2098 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2099 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2100 emitSetElemCount(unit, setElemCountLengthFunc, "1", !useGetCStringLength, capitalFieldName, structCType, " ");
2101 unit.emitln(" return this;");
2102 unit.emitln(" }");
2103 } else {
2104 unit.emitln(" {");
2105 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2106 unit.emitln(" if( 1 == elemCount ) {");
2107 if( baseIsPointer ) {
2108 unit.emitln(" PointerBuffer.derefPointer(getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2109 unit.emitln(" .put(0, src);");
2110 } else {
2111 unit.emitln(" ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2112 unit.emit (" .getByteBuffer()");
2113 if( !isByteBuffer ) {
2114 unit.emit(".as"+primJElemTypeBufferName+"()");
2115 }
2116 unit.emitln(".put(0, src);");
2117 }
2118 unit.emitln(" } else {");
2119 if( constElemCount || Ownership.Native == ownership ) {
2120 unit.emitln(" throw new RuntimeException(\"Primitive '"+fieldName+"' of "+ownership+" ownership and maxOneElement has "
2121 +(constElemCount?"const":"")+"elemCount \"+elemCount);");
2122 unit.emitln(" }");
2123 unit.emitln(" return this;");
2124 unit.emitln(" }");
2125 } else {
2126 if( baseIsPointer ) {
2127 unit.emitln(" final PointerBuffer eb = PointerBuffer.allocateDirect(1);");
2128 unit.emitln(" eb.put(0, src);");
2129 } else {
2130 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+primElemSizeExpr+", 1);");
2131 unit.emit (" eb.getByteBuffer()");
2132 if( !isByteBuffer ) {
2133 unit.emit(".as"+primJElemTypeBufferName+"()");
2134 }
2135 unit.emitln(".put(0, src);");
2136 }
2137 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2138 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2139 emitSetElemCount(unit, setElemCountLengthFunc, "1", !useGetCStringLength, capitalFieldName, structCType, " ");
2140 unit.emitln(" }");
2141 unit.emitln(" return this;");
2142 unit.emitln(" }");
2143 }
2144 }
2145 } else { // array && !isConstValue
2146 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null, baseJElemTypeName,
2147 null, constElemCount, maxOneElement, elemCountExpr, null, null);
2148 unit.emitln(" {");
2149 if( baseIsPointer ) {
2150 unit.emitln(" PointerBuffer.wrap(getBuffer(), "+fieldName+"_offset[mdIdx], 1).put(0, src);");
2151 } else {
2152 unit.emitln(" ElementBuffer.wrap("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2153 unit.emit (" .getByteBuffer()");
2154 if( !isByteBuffer ) {
2155 unit.emit(".as"+primJElemTypeBufferName+"()");
2156 }
2157 unit.emitln(".put(0, src);");
2158 }
2159 unit.emitln(" return this;");
2160 unit.emitln(" }");
2161 } // else SKIP setter for constValue Array
2162 unit.emitln();
2163 } else {
2164 // Setter Primitive n Pointer + Array
2165 boolean doneString = false;
2166
2167 if( isString && isByteBuffer && isPointer ) { // isConst is OK
2168 // isConst && constElemCount/Ownership.Native excluded: SKIP setter for constValue constElemCount Pointer w/ native ownership
2169 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null, "String",
2170 null, constElemCount, maxOneElement, elemCountExpr, null, null);
2171 unit.emitln(" {");
2172 unit.emitln(" final byte[] srcBytes = src.getBytes(_charset);");
2173 if( constElemCount || Ownership.Native == ownership ) {
2174 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2175 unit.emitln(" if( srcBytes.length + 1 != elemCount ) { throw new IllegalArgumentException(\"strlen+1 \"+(srcBytes.length+1)+\" != "
2176 +(constElemCount?"const":"")+" elemCount \"+elemCount+\" of "+ownership+" ownership\"); };");
2177 unit.emitln(" final ElementBuffer eb = ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2178 } else {
2179 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+primElemSizeExpr+", srcBytes.length + 1);");
2180 }
2181 unit.emitln(" eb.getByteBuffer().put(srcBytes, 0, srcBytes.length).put((byte)0).rewind(); // w/ EOS");
2182 if( !constElemCount ) {
2183 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2184 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2185 emitSetElemCount(unit, setElemCountLengthFunc, "srcBytes.length + 1", !useGetCStringLength, capitalFieldName, structCType, " ");
2186 }
2187 unit.emitln(" return this;");
2188 unit.emitln(" }");
2189 unit.emitln();
2190 doneString = true;
2191 }
2192 if( doneString && isStringOnly ) {
2193 generateArrayFieldNote(unit, " /** SKIP setter for String alternative (ByteBuffer)", " */", fieldName, fieldType, ownership, constElemCount, maxOneElement, elemCountExpr, false, false);
2194 } else if( isConstValue ) {
2195 if( isPointer ) {
2196 // constElemCount/Ownership.Native excluded: SKIP setter for constValue constElemCount Pointer w/ native ownership
2197 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null,
2198 baseJElemTypeName+"[]", SetReplaceArrayArgsPost, constElemCount, maxOneElement, elemCountExpr, SetReplaceArrayApiDocDetail, SetReplaceArrayApiDocArgs);
2199 if( Ownership.Native == ownership ) {
2200 throw new InternalError("Native ownership but adding potential memory-replacement for '"+fqStructFieldName+"': "+fieldType.getSignature(null).toString());
2201 }
2202 unit.emitln(" {");
2203 // JAU01 unit.emitln(SetReplaceArrayArgsCheck);
2204 if( baseIsPointer ) {
2205 unit.emitln(" final PointerBuffer eb = PointerBuffer.allocateDirect(length);");
2206 } else {
2207 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+primElemSizeExpr+", length);");
2208 }
2209 unit.emitln(" eb.put(src, srcPos, 0, length).storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2210 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2211 emitSetElemCount(unit, setElemCountLengthFunc, "length", !useGetCStringLength, capitalFieldName, structCType, " ");
2212 unit.emitln(" return this;");
2213 unit.emitln(" }");
2214 } // else SKIP setter for constValue Array
2215 } else if( constElemCount || !isPointer ) {
2216 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null,
2217 baseJElemTypeName+"[]", SetSubArrayArgsPost, constElemCount, maxOneElement, elemCountExpr, SetSubArrayApiDocDetail, SetSubArrayApiDocArgs);
2218 unit.emitln(" {");
2219 // JAU01 unit.emitln(SetSubArrayArgsCheck);
2220 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2221 // JAU01 unit.emitln(" if( destPos + length > elemCount ) { throw new IndexOutOfBoundsException(\"destPos \"+destPos+\" + length \"+length+\" > elemCount \"+elemCount); };");
2222 if( baseIsPointer ) {
2223 if( isPointer ) {
2224 unit.emitln(" final PointerBuffer eb = PointerBuffer.derefPointer(getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2225 } else {
2226 unit.emitln(" final PointerBuffer eb = PointerBuffer.wrap(getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2227 }
2228 } else {
2229 if( isPointer ) {
2230 unit.emitln(" final ElementBuffer eb = ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2231 } else {
2232 unit.emitln(" final ElementBuffer eb = ElementBuffer.wrap("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2233 }
2234 }
2235 unit.emitln(" eb.put(src, srcPos, destPos, length);");
2236 unit.emitln(" return this;");
2237 unit.emitln(" }");
2238 } else /* if( !constElemCount && isPointer ) */ {
2239 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, SetArrayArgsPre,
2240 baseJElemTypeName+"[]", SetArrayArgsPost, constElemCount, maxOneElement, elemCountExpr, SetArrayApiDocDetail, SetArrayApiDocArgs);
2241 if( Ownership.Native == ownership ) {
2242 throw new InternalError("Native ownership but adding potential memory-replacement for '"+fqStructFieldName+"': "+fieldType.getSignature(null).toString());
2243 }
2244 unit.emitln(" {");
2245 // JAU01 unit.emitln(SetArrayArgsCheck);
2246 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2247 unit.emitln(" if( subset || destPos + length == elemCount ) {");
2248 // JAU01 unit.emitln(" if( destPos + length > elemCount ) { throw new IndexOutOfBoundsException(\"subset \"+subset+\", destPos \"+destPos+\" + length \"+length+\" > elemCount \"+elemCount); };");
2249 if( baseIsPointer ) {
2250 unit.emitln(" final PointerBuffer eb = PointerBuffer.derefPointer(getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2251 } else {
2252 unit.emitln(" final ElementBuffer eb = ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2253 }
2254 unit.emitln(" eb.put(src, srcPos, destPos, length);");
2255 unit.emitln(" } else {");
2256 unit.emitln(" final int newElemCount = destPos + length;");
2257 if( baseIsPointer ) {
2258 unit.emitln(" final PointerBuffer eb = PointerBuffer.allocateDirect(newElemCount);");
2259 unit.emitln(" if( 0 < destPos ) {");
2260 unit.emitln(" final PointerBuffer pre_eb = PointerBuffer.derefPointer(getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2261 unit.emitln(" pre_eb.position(0).limit(destPos);");
2262 unit.emitln(" eb.put(pre_eb).rewind();");
2263 unit.emitln(" }");
2264 } else {
2265 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+primElemSizeExpr+", newElemCount);");
2266 unit.emitln(" if( 0 < destPos ) {");
2267 unit.emitln(" final ElementBuffer pre_eb = ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2268 unit.emitln(" eb.put(pre_eb.getByteBuffer(), 0, 0, destPos);");
2269 unit.emitln(" }");
2270 }
2271 unit.emitln(" eb.put(src, srcPos, destPos, length);");
2272 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2273 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2274 emitSetElemCount(unit, setElemCountLengthFunc, "newElemCount", !useGetCStringLength, capitalFieldName, structCType, " ");
2275 unit.emitln(" }");
2276 unit.emitln(" return this;");
2277 unit.emitln(" }");
2278 }
2279 unit.emitln();
2280 }
2281 } else {
2282 // Setter Struct
2283 if( maxOneElement ) {
2284 // Setter Struct Single Pointer + Array
2285 if( isPointer ) {
2286 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null, baseJElemTypeName,
2287 null, constElemCount, maxOneElement, elemCountExpr, null, null);
2288 if( isConstValue ) {
2289 // constElemCount/Ownership.Native excluded: SKIP setter for constValue constElemCount Pointer w/ native ownership
2290 if( Ownership.Native == ownership ) {
2291 throw new InternalError("Native ownership but adding potential memory-replacement for '"+fqStructFieldName+"': "+fieldType.getSignature(null).toString());
2292 }
2293 unit.emitln(" {");
2294 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+baseJElemTypeName+".size(), 1);");
2295 unit.emitln(" eb.put(0, src.getBuffer());");
2296 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2297 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2298 emitSetElemCount(unit, setElemCountLengthFunc, "1", !useGetCStringLength, capitalFieldName, structCType, " ");
2299 unit.emitln(" return this;");
2300 unit.emitln(" }");
2301 } else {
2302 unit.emitln(" {");
2303 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2304 unit.emitln(" if( 1 == elemCount ) {");
2305 unit.emitln(" ElementBuffer.derefPointer("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2306 unit.emitln(" .put(0, src.getBuffer());");
2307 unit.emitln(" } else {");
2308 if( constElemCount || Ownership.Native == ownership ) {
2309 unit.emitln(" throw new RuntimeException(\"Primitive '"+fieldName+"' of "+ownership+" ownership and maxOneElement has "
2310 +(constElemCount?"const":"")+"elemCount \"+elemCount);");
2311 unit.emitln(" }");
2312 unit.emitln(" return this;");
2313 unit.emitln(" }");
2314 } else {
2315 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+baseJElemTypeName+".size(), 1);");
2316 unit.emitln(" eb.put(0, src.getBuffer());");
2317 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2318 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2319 emitSetElemCount(unit, setElemCountLengthFunc, "1", !useGetCStringLength, capitalFieldName, structCType, " ");
2320 unit.emitln(" }");
2321 unit.emitln(" return this;");
2322 unit.emitln(" }");
2323 }
2324 }
2325 } else if( !isConstValue ) { // array && !isConstValue
2326 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null, baseJElemTypeName,
2327 null, constElemCount, maxOneElement, elemCountExpr, null, null);
2328 unit.emitln(" {");
2329 unit.emitln(" ElementBuffer.wrap("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2330 unit.emitln(" .put(0, src.getBuffer());");
2331 unit.emitln(" return this;");
2332 unit.emitln(" }");
2333 } // else SKIP setter for constValue Array
2334 unit.emitln();
2335 } else {
2336 // Setter Struct n Pointer + Array
2337 if( isConstValue ) {
2338 if( isPointer ) {
2339 // constElemCount/Ownership.Native excluded: SKIP setter for constValue constElemCount Pointer w/ native ownership
2340 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null,
2341 baseJElemTypeName+"[]", SetReplaceArrayArgsPost, constElemCount, maxOneElement, elemCountExpr, SetReplaceArrayApiDocDetail, SetReplaceArrayApiDocArgs);
2342 if( Ownership.Native == ownership ) {
2343 throw new InternalError("Native ownership but adding potential memory-replacement for '"+fqStructFieldName+"': "+fieldType.getSignature(null).toString());
2344 }
2345 unit.emitln(" {");
2346 unit.emitln(SetReplaceArrayArgsCheck);
2347 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+baseJElemTypeName+".size(), length);");
2348 unit.emitln(" for(int i=0; i<length; ++i) {");
2349 unit.emitln(" eb.put(i, src[srcPos+i].getBuffer());");
2350 unit.emitln(" }");
2351 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2352 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2353 emitSetElemCount(unit, setElemCountLengthFunc, "length", !useGetCStringLength, capitalFieldName, structCType, " ");
2354 unit.emitln(" return this;");
2355 unit.emitln(" }");
2356 } // else SKIP setter for constValue Array
2357 } else if( constElemCount || !isPointer ) {
2358 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, null,
2359 baseJElemTypeName+"[]", SetSubArrayArgsPost, constElemCount, maxOneElement, elemCountExpr, SetSubArrayApiDocDetail, SetSubArrayApiDocArgs);
2360 unit.emitln(" {");
2361 unit.emitln(SetSubArrayArgsCheck);
2362 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2363 unit.emitln(" if( destPos + length > elemCount ) { throw new IndexOutOfBoundsException(\"destPos \"+destPos+\" + length \"+length+\" > elemCount \"+elemCount); };");
2364 if( isPointer ) {
2365 unit.emitln(" final ElementBuffer eb = ElementBuffer.derefPointer("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2366 } else {
2367 unit.emitln(" final ElementBuffer eb = ElementBuffer.wrap("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2368 }
2369 unit.emitln(" for(int i=0; i<length; ++i) {");
2370 unit.emitln(" eb.put(destPos+i, src[srcPos+i].getBuffer());");
2371 unit.emitln(" }");
2372 unit.emitln(" return this;");
2373 unit.emitln(" }");
2374 } else /* if( !constElemCount && isPointer ) */ {
2375 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, SetArrayArgsPre,
2376 baseJElemTypeName+"[]", SetArrayArgsPost, constElemCount, maxOneElement, elemCountExpr, SetArrayApiDocDetail, SetArrayApiDocArgs);
2377 if( Ownership.Native == ownership ) {
2378 throw new InternalError("Native ownership but adding potential memory-replacement for '"+fqStructFieldName+"': "+fieldType.getSignature(null).toString());
2379 }
2380 unit.emitln(" {");
2381 unit.emitln(SetArrayArgsCheck);
2382 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2383 unit.emitln(" if( subset || destPos + length == elemCount ) {");
2384 unit.emitln(" if( destPos + length > elemCount ) { throw new IndexOutOfBoundsException(\"subset \"+subset+\", destPos \"+destPos+\" + length \"+length+\" > elemCount \"+elemCount); };");
2385 unit.emitln(" final ElementBuffer eb = ElementBuffer.derefPointer("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2386 unit.emitln(" for(int i=0; i<length; ++i) {");
2387 unit.emitln(" eb.put(destPos+i, src[srcPos+i].getBuffer());");
2388 unit.emitln(" }");
2389 unit.emitln(" } else {");
2390 unit.emitln(" final int newElemCount = destPos + length;");
2391 unit.emitln(" final ElementBuffer eb = ElementBuffer.allocateDirect("+baseJElemTypeName+".size(), newElemCount);");
2392
2393 unit.emitln(" if( 0 < destPos ) {");
2394 unit.emitln(" final ElementBuffer pre_eb = ElementBuffer.derefPointer("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2395 unit.emitln(" eb.put(pre_eb.getByteBuffer(), 0, 0, destPos);");
2396 unit.emitln(" }");
2397 unit.emitln(" for(int i=0; i<length; ++i) {");
2398 unit.emitln(" eb.put(destPos+i, src[srcPos+i].getBuffer());");
2399 unit.emitln(" }");
2400 unit.emitln(" eb.storeDirectAddress(getBuffer(), "+fieldName+"_offset[mdIdx]);");
2401 unit.emitln(" _eb"+capitalFieldName+" = eb;");
2402 emitSetElemCount(unit, setElemCountLengthFunc, "newElemCount", !useGetCStringLength, capitalFieldName, structCType, " ");
2403 unit.emitln(" }");
2404 unit.emitln(" return this;");
2405 unit.emitln(" }");
2406 }
2407 unit.emitln();
2408 if( !isConstValue ) {
2409 generateSetterSignature(unit, accessMod, false, false, fieldName, fieldType, ownership, containingJTypeName, capitalFieldName, "final int destPos", baseJElemTypeName,
2410 null, constElemCount, maxOneElement, elemCountExpr, null, null);
2411 unit.emitln(" {");
2412 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2413 unit.emitln(" if( destPos + 1 > elemCount ) { throw new IndexOutOfBoundsException(\"destPos \"+destPos+\" + 1 > elemCount \"+elemCount); };");
2414 if( isPointer ) {
2415 unit.emitln(" ElementBuffer.derefPointer("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount)");
2416 } else {
2417 unit.emitln(" ElementBuffer.wrap("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount)");
2418 }
2419 unit.emitln(" .put(destPos, src.getBuffer());");
2420 unit.emitln(" return this;");
2421 unit.emitln(" }");
2422 unit.emitln();
2423 }
2424 }
2425 }
2426
2427 // Getter
2428 if( isPrimitive ) {
2429 // Getter Primitive Pointer + Array
2430 if( maxOneElement ) {
2431 generateGetterSignature(unit, false, false, fieldName, fieldType, ownership, baseJElemTypeName, capitalFieldName,
2432 null, constElemCount, maxOneElement, elemCountExpr, GetElemValueApiDocTail);
2433 unit.emitln(" {");
2434 if( baseIsPointer ) {
2435 if( isPointer ) {
2436 unit.emit (" return PointerBuffer.derefPointer(getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2437 } else {
2438 unit.emit (" return PointerBuffer.wrap(getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2439 }
2440 } else {
2441 if( isPointer ) {
2442 unit.emitln(" return ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2443 } else {
2444 unit.emitln(" return ElementBuffer.wrap("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], 1)");
2445 }
2446 unit.emit (" .getByteBuffer()");
2447 if( !isByteBuffer ) {
2448 unit.emit(".as"+primJElemTypeBufferName+"()");
2449 }
2450 }
2451 unit.emitln(".get(0);");
2452 unit.emitln(" }");
2453 unit.emitln();
2454 } else {
2455 boolean doneString = false;
2456 if( isString && isByteBuffer ) {
2457 generateGetterSignature(unit, false, false, fieldName, fieldType, ownership, "String", capitalFieldName+(isStringOnly?"":"AsString"),
2458 null, constElemCount, maxOneElement, elemCountExpr, GetElemValueApiDocTail);
2459 unit.emitln(" {");
2460 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2461 if( isPointer ) {
2462 unit.emitln(" final ByteBuffer bb = ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount).getByteBuffer();");
2463 } else {
2464 unit.emitln(" final ByteBuffer bb = ElementBuffer.wrap("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount).getByteBuffer();");
2465 }
2466 unit.emitln(" final byte[] ba = new byte[elemCount];");
2467 unit.emitln(" int i = -1;");
2468 unit.emitln(" while( ++i < elemCount ) {");
2469 unit.emitln(" ba[i] = bb.get(i);");
2470 unit.emitln(" if( (byte)0 == ba[i] ) break;");
2471 unit.emitln(" }");
2472 unit.emitln(" return new String(ba, 0, i, _charset);");
2473 unit.emitln(" }");
2474 unit.emitln();
2475 doneString = true;
2476 }
2477 if( doneString && isStringOnly ) {
2478 generateArrayFieldNote(unit, " /** SKIP getter for String alternative (ByteBuffer)", " */", fieldName, fieldType, ownership, constElemCount, maxOneElement, elemCountExpr, false, false);
2479 unit.emitln();
2480 } else if( !baseIsPointer ) {
2481 generateGetterSignature(unit, false, false, fieldName, fieldType, ownership, primJElemTypeBufferName, capitalFieldName,
2482 null, constElemCount, maxOneElement, elemCountExpr, GetElemValueApiDocTail);
2483 unit.emitln(" {");
2484 if( isPointer ) {
2485 unit.emitln(" return ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], "+getElemCountFuncExpr+")");
2486 } else {
2487 unit.emitln(" return ElementBuffer.wrap("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], "+getElemCountFuncExpr+")");
2488 }
2489 unit.emit (" .getByteBuffer()");
2490 if( !isByteBuffer ) {
2491 unit.emit(".as"+primJElemTypeBufferName+"()");
2492 }
2493 unit.emitln(";");
2494 unit.emitln(" }");
2495 unit.emitln();
2496 }
2497 if( !doneString ) {
2498 generateGetterSignature(unit, false, false, fieldName, fieldType, ownership, baseJElemTypeName+"[]", capitalFieldName,
2499 "final int srcPos, "+baseJElemTypeName+" dest[], "+GetArrayArgs, constElemCount, maxOneElement, elemCountExpr, GetElemValueApiDocTail);
2500 unit.emitln(" {");
2501 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2502 if( baseIsPointer ) {
2503 if( isPointer ) {
2504 unit.emit (" PointerBuffer.derefPointer(getBuffer(), "+fieldName+"_offset[mdIdx], elemCount)");
2505 } else {
2506 unit.emit (" PointerBuffer.wrap(getBuffer(), "+fieldName+"_offset[mdIdx], elemCount)");
2507 }
2508 } else {
2509 if( isPointer ) {
2510 unit.emit(" ElementBuffer.derefPointer("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount)");
2511 } else {
2512 unit.emit(" ElementBuffer.wrap("+primElemSizeExpr+", getBuffer(), "+fieldName+"_offset[mdIdx], elemCount)");
2513 }
2514 }
2515 unit.emitln(".get(srcPos, dest, destPos, length);");
2516 unit.emitln(" return dest;");
2517 unit.emitln(" }");
2518 unit.emitln();
2519 }
2520 }
2521 } else {
2522 // Getter Struct Pointer + Array
2523 if( maxOneElement ) {
2524 generateGetterSignature(unit, false, false, fieldName, fieldType, ownership, baseJElemTypeName, capitalFieldName,
2525 null, constElemCount, maxOneElement, elemCountExpr, GetElemValueApiDocTail);
2526 unit.emitln(" {");
2527 unit.emitln(" return "+baseJElemTypeName+".create(");
2528 if( isPointer ) {
2529 unit.emitln(" ElementBuffer.derefPointer("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], 1).getByteBuffer() );");
2530 } else {
2531 unit.emitln(" ElementBuffer.wrap("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], 1).getByteBuffer() );");
2532 }
2533 unit.emitln(" }");
2534 unit.emitln();
2535 } else {
2536 generateGetterSignature(unit, false, false, fieldName, fieldType, ownership, baseJElemTypeName+"[]", capitalFieldName,
2537 "final int srcPos, "+baseJElemTypeName+" dest[], "+GetArrayArgs, constElemCount, maxOneElement, elemCountExpr, GetElemValueApiDocTail);
2538 unit.emitln(" {");
2539 unit.emitln(GetArrayArgsCheck);
2540 unit.emitln(" final int elemCount = "+getElemCountFuncExpr+";");
2541 unit.emitln(" if( srcPos + length > elemCount ) { throw new IndexOutOfBoundsException(\"srcPos \"+srcPos+\" + length \"+length+\" > elemCount \"+elemCount); };");
2542 if( isPointer ) {
2543 unit.emitln(" final ElementBuffer eb = ElementBuffer.derefPointer("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2544 } else {
2545 unit.emitln(" final ElementBuffer eb = ElementBuffer.wrap("+baseJElemTypeName+".size(), getBuffer(), "+fieldName+"_offset[mdIdx], elemCount);");
2546 }
2547 unit.emitln(" for(int i=0; i<length; ++i) {");
2548 unit.emitln(" dest[destPos+i] = "+baseJElemTypeName+".create( eb.slice(srcPos+i, 1) );");
2549 unit.emitln(" }");
2550 unit.emitln(" return dest;");
2551 unit.emitln(" }");
2552 unit.emitln();
2553 }
2554 }
2555 }
2556 private void emitSetElemCount(final JavaCodeUnit unit, final String setElemCountFunc, final String newElemCountExpr, final boolean mandatory, final String capitalFieldName, final Type structCType, final String indentation) {
2557 if( null != setElemCountFunc ) {
2558 unit.emitln(indentation+setElemCountFunc+"( "+newElemCountExpr+" );");
2559 } else if( mandatory ) {
2560 final String msg = "Missing set"+capitalFieldName+"ElemCount( "+newElemCountExpr+" )";
2561 unit.emitln(indentation+"// ERROR: "+msg);
2562 unit.emitln();
2563 LOG.log(SEVERE, structCType.getASTLocusTag(), msg);
2564 throw new RuntimeException(msg);
2565 }
2566 }
2567
2568 private JavaType typeToJavaType(final Type cType, final MachineDataInfo curMachDesc) {
2569 final JavaType jt = typeToJavaTypeImpl(cType, curMachDesc);
2570 LOG.log(FINE, cType.getASTLocusTag(), "typeToJavaType: {0} -> {1}", cType, jt);
2571 return jt;
2572 }
2573 private boolean isJNIEnvPointer(final Type cType) {
2574 final PointerType opt = cType.asPointer();
2575 return (opt != null) &&
2576 (opt.getTargetType().getName() != null) &&
2577 (opt.getTargetType().getName().equals("JNIEnv"));
2578 }
2579 private JavaType typeToJavaTypeImpl(final Type cType, final MachineDataInfo curMachDesc) {
2580 // Recognize JNIEnv* case up front
2581 if( isJNIEnvPointer(cType) ) {
2582 return JavaType.createForJNIEnv();
2583 }
2584 // Opaque specifications override automatic conversions
2585 // in case the identity is being used .. not if ptr-ptr
2586 final TypeInfo info = cfg.typeInfo(cType);
2587 if (info != null) {
2588 boolean isPointerPointer = false;
2589 if (cType.pointerDepth() > 0 || cType.arrayDimension() > 0) {
2590 // t is `<type>*`, `<type>[]` or `<type>[][]`, we need to get <type>
2591 final Type targetType = cType.getArrayBaseOrPointerTargetType();
2592 if (cType.pointerDepth() == 2 || cType.arrayDimension() == 2) {
2593 // Get the target type of the target type (targetType was computer earlier
2594 // as to be a pointer to the target type, so now we need to get its
2595 // target type)
2596 if (targetType.isPointer()) {
2597 isPointerPointer = true;
2598 if( GlueGen.debug() ) {
2599 // t is<type>**, targetType is <type>*, we need to get <type>
2600 final Type bottomType = targetType.asPointer().getTargetType();
2601 LOG.log(INFO, cType.getASTLocusTag(), "Opaque Type: {0}, targetType: {1}, bottomType: {2} is ptr-ptr",
2602 cType, targetType, bottomType);
2603 }
2604 }
2605 }
2606 }
2607 if( !isPointerPointer ) {
2608 return info.javaType();
2609 }
2610 }
2611
2612 if (cType.isInt() || cType.isEnum()) {
2613 switch ((int) cType.getSize(curMachDesc)) {
2614 case 1: return javaType(Byte.TYPE);
2615 case 2: return javaType(Short.TYPE);
2616 case 4: return javaType(Integer.TYPE);
2617 case 8: return javaType(Long.TYPE);
2618 default: throw new GlueGenException("Unknown integer type of size " +
2619 cType.getSize(curMachDesc) + " and name " + cType.getName(),
2620 cType.getASTLocusTag());
2621 }
2622 } else if (cType.isFloat()) {
2623 return javaType(Float.TYPE);
2624 } else if (cType.isDouble()) {
2625 return javaType(Double.TYPE);
2626 } else if (cType.isVoid()) {
2627 return javaType(Void.TYPE);
2628 } else if (cType.pointerDepth() > 0 || cType.arrayDimension() > 0) {
2629 // <type>[][]
2630 final Type targetType = cType.getArrayBaseOrPointerTargetType();
2631
2632 // Handle Types of form pointer-to-type or array-of-type, like
2633 // char* or int[]; these are expanded out into Java primitive
2634 // arrays, NIO buffers, or both in expandMethodBinding
2635 if (cType.pointerDepth() == 1 || cType.arrayDimension() == 1) {
2636 if (targetType.isVoid()) {
2637 return JavaType.createForCVoidPointer();
2638 } else if( targetType.isFunctionPointer() ) {
2639 return javaType(Long.TYPE);
2640 } else if (targetType.isInt()) {
2641 final SizeThunk targetSizeThunk = targetType.getSize();
2642 if( null != targetSizeThunk && SizeThunk.POINTER == targetSizeThunk ) {
2643 // Map intptr_t*, uintptr_t*, ptrdiff_t* and size_t* to PointerBuffer, since referenced memory-size is arch dependent
2644 return JavaType.forNIOPointerBufferClass();
2645 }
2646 switch ((int) targetType.getSize(curMachDesc)) {
2647 case 1: return JavaType.createForCCharPointer();
2648 case 2: return JavaType.createForCShortPointer();
2649 case 4: return JavaType.createForCInt32Pointer();
2650 case 8: return JavaType.createForCInt64Pointer();
2651 default: throw new GlueGenException("Unknown integer array type of size " +
2652 cType.getSize(curMachDesc) + " and name " + cType.getName()+", "+cType.getDebugString()+
2653 ", target "+targetType.getDebugString(),
2654 cType.getASTLocusTag());
2655 }
2656 } else if (targetType.isFloat()) {
2657 return JavaType.createForCFloatPointer();
2658 } else if (targetType.isDouble()) {
2659 return JavaType.createForCDoublePointer();
2660 } else if (targetType.isCompound()) {
2661 if (cType.isArray()) { // FIXME: Compound and Compound-Arrays
2662 return JavaType.createForCArray(targetType);
2663 }
2664 // Special cases for known JNI types (in particular for converting jawt.h)
2665 if (cType.getName() != null &&
2666 cType.getName().equals("jobject")) {
2667 return javaType(java.lang.Object.class);
2668 }
2669 // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser)
2670 String name;
2671 if( !targetType.isTypedef() && cType.isTypedef() ) {
2672 // If compound is not a typedef _and_ containing pointer is typedef, use the latter.
2673 name = cType.getName();
2674 } else {
2675 // .. otherwise try compound name
2676 name = targetType.getName();
2677 if( null == name ) {
2678 // .. fall back to pointer type name
2679 name = cType.getName();
2680 if (name == null) {
2681 throw new GlueGenException("Couldn't find a proper type name for pointer type " + cType.getDebugString()+
2682 ", target "+targetType.getDebugString(),
2683 cType.getASTLocusTag());
2684 }
2685 }
2686 }
2687 return JavaType.createForCStruct(cfg.renameJavaType(name));
2688 } else {
2689 throw new GlueGenException("Don't know how to convert pointer/array type " +
2690 cType.getDebugString() + ", target "+targetType.getDebugString(),
2691 cType.getASTLocusTag());
2692 }
2693 }
2694 // Handle Types of form pointer-to-pointer-to-type or
2695 // array-of-arrays-of-type, like char** or int[][]
2696 else if (cType.pointerDepth() == 2 || cType.arrayDimension() == 2) {
2697 // Get the target type of the target type (targetType was computer earlier
2698 // as to be a pointer to the target type, so now we need to get its
2699 // target type)
2700 Type bottomType;
2701 if (targetType.isPointer()) {
2702 // t is<type>**, targetType is <type>*, we need to get <type>
2703 bottomType = targetType.asPointer().getTargetType();
2704 if( GlueGen.debug() ) {
2705 LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr): {0}, targetType: {1}, bottomType: {2}",
2706 cType.getDebugString(), targetType.getDebugString(), bottomType.getDebugString());
2707 }
2708 return JavaType.forNIOPointerBufferClass();
2709 } else if(targetType.isArray()) {
2710 // t is<type>[][], targetType is <type>[], we need to get <type>
2711 bottomType = targetType.getBaseType();
2712 if( GlueGen.debug() ) {
2713 LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr.array): {0}, targetType: {1}, bottomType: {2}",
2714 cType.getDebugString(), targetType.getDebugString(), bottomType.getDebugString());
2715 }
2716 } else {
2717 bottomType = targetType;
2718 if( GlueGen.debug() ) {
2719 LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr.primitive): {0}, targetType: {1}, bottomType: {2}",
2720 cType.getDebugString(), targetType.getDebugString(), bottomType.getDebugString());
2721 }
2722 }
2723
2724 // Warning: The below code is not backed up by an implementation,
2725 // the only working variant is a ptr-ptr type which results in a PointerBuffer.
2726 //
2727 if (bottomType.isPrimitive()) {
2728 if (bottomType.isInt()) {
2729 switch ((int) bottomType.getSize(curMachDesc)) {
2730 case 1: return javaType(ArrayTypes.byteBufferArrayClass);
2731 case 2: return javaType(ArrayTypes.shortBufferArrayClass);
2732 case 4: return javaType(ArrayTypes.intBufferArrayClass);
2733 case 8: return javaType(ArrayTypes.longBufferArrayClass);
2734 default: throw new GlueGenException("Unknown two-dimensional integer array type of element size " +
2735 bottomType.getSize(curMachDesc) + " and name " + bottomType.getName()+", "+bottomType.getDebugString(),
2736 bottomType.getASTLocusTag());
2737 }
2738 } else if (bottomType.isFloat()) {
2739 return javaType(ArrayTypes.floatBufferArrayClass);
2740 } else if (bottomType.isDouble()) {
2741 return javaType(ArrayTypes.doubleBufferArrayClass);
2742 } else {
2743 throw new GlueGenException("Unexpected primitive type " + bottomType.getDebugString() +
2744 " in two-dimensional array", bottomType.getASTLocusTag());
2745 }
2746 } else if (bottomType.isVoid()) {
2747 return javaType(ArrayTypes.bufferArrayClass);
2748 } else if (targetType.isPointer() && (targetType.pointerDepth() == 1) &&
2749 targetType.asPointer().getTargetType().isCompound()) {
2750 // Array of pointers; convert as array of StructAccessors
2751 return JavaType.createForCArray(bottomType);
2752 } else {
2753 throw new GlueGenException(
2754 "Could not convert C type " + cType.getDebugString() + " " +
2755 "to appropriate Java type; need to add more support for " +
2756 "depth=2 pointer/array types [debug info: targetType=" +
2757 targetType.getDebugString() + "]", cType.getASTLocusTag());
2758 }
2759 } else {
2760 // can't handle this type of pointer/array argument
2761 throw new GlueGenException(
2762 "Could not convert C pointer/array " + cType.getDebugString() + " to " +
2763 "appropriate Java type; types with pointer/array depth " +
2764 "greater than 2 are not yet supported [debug info: " +
2765 "pointerDepth=" + cType.pointerDepth() + " arrayDimension=" +
2766 cType.arrayDimension() + " targetType=" + targetType.getDebugString() + "]",
2767 cType.getASTLocusTag());
2768 }
2769
2770 } else if( cType.isCompound() ) { // FIXME: Compound and Compound-Arrays
2771 String name = cType.getName();
2772 if (name == null) {
2773 name = cType.asCompound().getStructName();
2774 if (name == null) {
2775 throw new GlueGenException("Couldn't find a proper type name for pointer type " + cType.getDebugString(),
2776 cType.getASTLocusTag());
2777 }
2778 }
2779 return JavaType.createForCStruct(cfg.renameJavaType(name));
2780 } else {
2781 throw new GlueGenException(
2782 "Could not convert C type " + cType.getDebugString() + " (class " +
2783 cType.getClass().getName() + ") to appropriate Java type",
2784 cType.getASTLocusTag());
2785 }
2786 }
2787
2788 private StructLayout getLayout() {
2789 if (layout == null) {
2790 layout = StructLayout.create(0);
2791 }
2792 return layout;
2793 }
2794
2795 /**
2796 * @param filename the class's full filename to open w/ write access
2797 * @param cUnitName the base c-unit name, i.e. c-file basename with suffix
2798 * @param generator informal optional object that is creating this unit, used to be mentioned in a warning message if not null.
2799 * @throws IOException
2800 */
2801 protected CCodeUnit openCUnit(final String filename, final String cUnitName) throws IOException {
2802 return new CCodeUnit(filename, cUnitName, this);
2803 }
2804
2805 /**
2806 * @param filename the class's full filename to open w/ write access
2807 * @param packageName the package name of the class
2808 * @param simpleClassName the simple class name, i.e. w/o package name or c-file basename
2809 * @param generator informal optional object that is creating this unit, used to be mentioned in a warning message if not null.
2810 * @throws IOException
2811 */
2812 protected JavaCodeUnit openJavaUnit(final String filename, final String packageName, final String simpleClassName) throws IOException {
2813 return new JavaCodeUnit(filename, packageName, simpleClassName, this);
2814 }
2815
2816 private boolean isOpaque(final Type type) {
2817 return null != cfg.typeInfo(type);
2818 }
2819
2820 private String compatiblePrimitiveJavaTypeName(final Type fieldType,
2821 final MachineDataInfo curMachDesc) {
2822 if ( !fieldType.isInt() && !fieldType.isPointer() && !fieldType.isArray() ) {
2823 throw new GlueGenException("Can't yet handle opaque definitions of structs' fields to non-integer types (byte, short, int, long, etc.): type: "+
2824 fieldType, fieldType.getASTLocusTag());
2825 }
2826 switch ((int) fieldType.getSize(curMachDesc)) {
2827 case 1: return "byte";
2828 case 2: return "short";
2829 case 4: return "int";
2830 case 8: return "long";
2831 default: throw new GlueGenException("Can't handle opaque definitions if the starting type isn't compatible with integral types, type "+
2832 fieldType.getDebugString(), fieldType.getASTLocusTag());
2833 }
2834 }
2835
2836 private void openCodeUnits() throws IOException {
2837 String jRoot = null;
2838 if (cfg.allStatic() || cfg.emitInterface()) {
2839 jRoot = cfg.javaOutputDir() + File.separator +
2840 CodeGenUtils.packageAsPath(cfg.packageName());
2841 }
2842 String jImplRoot = null;
2843 if (!cfg.allStatic()) {
2844 jImplRoot =
2845 cfg.javaOutputDir() + File.separator +
2846 CodeGenUtils.packageAsPath(cfg.implPackageName());
2847 }
2848 String nRoot = cfg.nativeOutputDir();
2849 if (cfg.nativeOutputUsesJavaHierarchy())
2850 {
2851 nRoot +=
2852 File.separator + CodeGenUtils.packageAsPath(cfg.packageName());
2853 }
2854
2855 if (cfg.allStatic() || cfg.emitInterface()) {
2856 final String javaFileName = jRoot + File.separator + cfg.className() + ".java";
2857 javaUnit = openJavaUnit(javaFileName, cfg.packageName(), cfg.className());
2858 }
2859 if (!cfg.allStatic() && cfg.emitImpl()) {
2860 final String javaFileName = jImplRoot + File.separator + cfg.implClassName() + ".java";
2861 javaImplUnit = openJavaUnit(javaFileName, cfg.implPackageName(), cfg.implClassName());
2862 }
2863 if (cfg.emitImpl()) {
2864 final String cUnitName = cfg.implClassName() + "_JNI.c";
2865 final String cFileName = nRoot + File.separator + cUnitName;
2866 cUnit = openCUnit(cFileName, cUnitName);
2867 }
2868 }
2869
2871 if (!cfg.allStatic() && !cfg.emitInterface()) {
2872 throw new InternalError("Should not call this");
2873 }
2874 return javaUnit;
2875 }
2876
2878 if (cfg.allStatic() || !cfg.emitImpl()) {
2879 throw new InternalError("Should not call this");
2880 }
2881 return javaImplUnit;
2882 }
2883
2884 protected CCodeUnit cUnit() {
2885 if (!cfg.emitImpl()) {
2886 throw new InternalError("Should not call this");
2887 }
2888 return cUnit;
2889 }
2890
2891 private void closeWriters() throws IOException {
2892 if( javaUnit != null ) {
2893 javaUnit.close();
2894 javaUnit = null;
2895 }
2896 if( javaImplUnit != null ) {
2897 javaImplUnit.close();
2898 javaImplUnit = null;
2899 }
2900 if( cUnit != null ) {
2901 cUnit.close();
2902 cUnit = null;
2903 }
2904 }
2905
2906 /**
2907 * Returns the value that was specified by the configuration directive
2908 * "JavaOutputDir", or the default if none was specified.
2909 */
2910 protected String getJavaOutputDir() {
2911 return cfg.javaOutputDir();
2912 }
2913
2914 /**
2915 * Returns the value that was specified by the configuration directive
2916 * "Package", or the default if none was specified.
2917 */
2918 protected String getJavaPackageName() {
2919 return cfg.packageName();
2920 }
2921
2922 /**
2923 * Returns the value that was specified by the configuration directive
2924 * "ImplPackage", or the default if none was specified.
2925 */
2926 protected String getImplPackageName() {
2927 return cfg.implPackageName();
2928 }
2929
2930 /**
2931 * Emit all the strings specified in the "CustomJavaCode" parameters of
2932 * the configuration file.
2933 */
2934 protected void emitCustomJavaCode(final CodeUnit unit, final String className) throws Exception {
2935 final List<String> code = cfg.customJavaCodeForClass(className);
2936 if (code.isEmpty())
2937 return;
2938
2939 unit.emitln();
2940 unit.emitln(" // --- Begin CustomJavaCode .cfg declarations");
2941 for (final String line : code) {
2942 unit.emitln(line);
2943 }
2944 unit.emitln(" // ---- End CustomJavaCode .cfg declarations");
2945 }
2946
2947 /**
2948 * Emit all the strings specified in the "CustomJNICode" parameters of
2949 * the configuration file.
2950 */
2951 protected void emitCustomJNICode(final CodeUnit unit, final String className) throws Exception {
2952 final List<String> code = cfg.customJNICodeForClass(className);
2953 if (code.isEmpty())
2954 return;
2955
2956 unit.emitln();
2957 unit.emitln(" // --- Begin CustomJNICode .cfg declarations");
2958 for (final String line : code) {
2959 unit.emitln(line);
2960 }
2961 unit.emitln(" // ---- End CustomJNICode .cfg declarations");
2962 }
2963
2964 public String[] getClassAccessModifiers(final String classFQName) {
2965 String[] accessModifiers;
2966 final MethodAccess acc = cfg.accessControl(classFQName);
2967 if( PUBLIC_ABSTRACT == acc ) {
2968 accessModifiers = new String[] { PUBLIC.getJavaName(), PUBLIC_ABSTRACT.getJavaName() };
2969 } else if( PACKAGE_PRIVATE == acc ) {
2970 accessModifiers = new String[] { PACKAGE_PRIVATE.getJavaName() };
2971 } else if( PRIVATE == acc ) {
2972 throw new IllegalArgumentException("Class access "+classFQName+" cannot be private");
2973 } else if( PROTECTED == acc ) {
2974 accessModifiers = new String[] { PROTECTED.getJavaName() };
2975 } else { // default PUBLIC
2976 accessModifiers = new String[] { PUBLIC.getJavaName() };
2977 }
2978 return accessModifiers;
2979 }
2980
2981 /**
2982 * Write out any header information for the output files (class declaration
2983 * and opening brace, import statements, etc).
2984 */
2985 protected void emitAllFileHeaders() throws IOException {
2986 try {
2987 final List<String> imports = new ArrayList<String>(cfg.imports());
2988 imports.add(cfg.gluegenRuntimePackage()+".*");
2989 imports.add(DynamicLookupHelper.class.getPackage().getName()+".*");
2990 imports.add(Buffers.class.getPackage().getName()+".*");
2991 imports.add(Buffer.class.getPackage().getName()+".*");
2992 imports.add(HashUtil.class.getPackage().getName()+".*");
2993 imports.add("java.util.Set");
2994 imports.add("java.util.Map");
2995 imports.add("java.util.HashMap");
2996 imports.add("java.nio.charset.Charset");
2997 imports.add("java.nio.charset.StandardCharsets");
2998
2999 if (cfg.allStatic() || cfg.emitInterface()) {
3000
3001 String[] interfaces;
3002 List<String> userSpecifiedInterfaces = null;
3003 if (cfg.emitInterface()) {
3004 userSpecifiedInterfaces = cfg.extendedInterfaces(cfg.className());
3005 } else {
3006 userSpecifiedInterfaces = cfg.implementedInterfaces(cfg.className());
3007 }
3008 interfaces = new String[userSpecifiedInterfaces.size()];
3009 userSpecifiedInterfaces.toArray(interfaces);
3010
3011 final List<String> intfDocs = cfg.javadocForClass(cfg.className());
3012 final CodeGenUtils.EmissionCallback docEmitter =
3014 @Override
3015 public void emit(final PrintWriter w) {
3016 for (final Iterator<String> iter = intfDocs.iterator(); iter.hasNext(); ) {
3017 w.println(iter.next());
3018 }
3019 }
3020 };
3021
3022 final String[] accessModifiers = getClassAccessModifiers(cfg.className());
3024 javaUnit().output,
3025 cfg.packageName(),
3026 cfg.className(),
3027 cfg.allStatic() ? true : false,
3028 imports,
3029 accessModifiers,
3030 interfaces,
3031 cfg.extendedParentClass(cfg.className()),
3032 docEmitter);
3033 }
3034
3035 if (!cfg.allStatic() && cfg.emitImpl()) {
3036 final List<String> implDocs = cfg.javadocForClass(cfg.implClassName());
3037 final CodeGenUtils.EmissionCallback docEmitter =
3039 @Override
3040 public void emit(final PrintWriter w) {
3041 for (final Iterator<String> iter = implDocs.iterator(); iter.hasNext(); ) {
3042 w.println(iter.next());
3043 }
3044 }
3045 };
3046
3047 String[] interfaces;
3048 List<String> userSpecifiedInterfaces = null;
3049 userSpecifiedInterfaces = cfg.implementedInterfaces(cfg.implClassName());
3050 int additionalNum = 0;
3051 if (cfg.className() != null) {
3052 additionalNum = 1;
3053 }
3054 interfaces = new String[additionalNum + userSpecifiedInterfaces.size()];
3055 userSpecifiedInterfaces.toArray(interfaces);
3056 if (additionalNum == 1) {
3057 interfaces[userSpecifiedInterfaces.size()] = cfg.className();
3058 }
3059
3060 final String[] accessModifiers = getClassAccessModifiers(cfg.implClassName());
3062 javaImplUnit().output,
3063 cfg.implPackageName(),
3064 cfg.implClassName(),
3065 true,
3066 imports,
3067 accessModifiers,
3068 interfaces,
3069 cfg.extendedParentClass(cfg.implClassName()),
3070 docEmitter);
3071 }
3072
3073 if (cfg.emitImpl()) {
3074 if( !cfg.getJavaCallbackList().isEmpty() && null == cfg.libraryOnLoadName() ) {
3075 LOG.log(WARNING, "JavaCallback used, but no 'LibraryOnLoad' basename specified for JNI_OnLoad(..). Exactly one native code-unit for the library must specify 'LibraryOnLoad' basename");
3076 }
3077 cUnit().emitHeader(getImplPackageName(), cfg.implClassName(), cfg.customCCode());
3078 }
3079 } catch (final Exception e) {
3080 throw new RuntimeException(
3081 "Error emitting all file headers: cfg.allStatic()=" + cfg.allStatic() +
3082 " cfg.emitImpl()=" + cfg.emitImpl() + " cfg.emitInterface()=" + cfg.emitInterface(),
3083 e);
3084 }
3085 }
3086
3087 /**
3088 * Write out any footer information for the output files (closing brace of
3089 * class definition, etc).
3090 */
3091 protected void emitAllFileFooters() {
3092 if (cfg.allStatic() || cfg.emitInterface()) {
3093 javaUnit.emitTailCode();
3094 javaUnit().emitln("} // end of class " + cfg.className());
3095 }
3096 if (!cfg.allStatic() && cfg.emitImpl()) {
3097 javaImplUnit.emitTailCode();
3098 javaImplUnit().emitln("} // end of class " + cfg.implClassName());
3099 }
3100 if (cfg.emitImpl() && null != cfg.libraryOnLoadName() ) {
3101 cUnit.emitJNIOnLoadJNIEnvCode(cfg.libraryOnLoadName());
3102 }
3103 }
3104
3105 private JavaType javaType(final Class<?> c) {
3106 return JavaType.createForClass(c);
3107 }
3108 private JavaType javaStringType(final Class<?> c, final PascalStringElem pascalStrElem) {
3109 return JavaType.createForStringClass(c, pascalStrElem);
3110 }
3111
3112 /** Maps the C types in the specified function to Java types through
3113 the MethodBinding interface. Note that the JavaTypes in the
3114 returned MethodBinding are "intermediate" JavaTypes (some
3115 potentially representing C pointers rather than true Java types)
3116 and must be lowered to concrete Java types before creating
3117 emitters for them. */
3118 private MethodBinding bindFunction(FunctionSymbol sym,
3119 final boolean forInterface,
3120 final MachineDataInfo curMachDesc,
3121 final JavaType containingType, final Type containingCType) {
3122
3123 final String delegationImplName = null == containingType && null == containingCType ?
3124 cfg.getDelegatedImplementation(sym) : null;
3125 if( !forInterface && null != delegationImplName ) {
3126 // We need to reflect the 'delegationImplName' for implementations
3127 // to allow all subsequent type/cfg checks to hit on AliasedSymbol!
3128 sym = FunctionSymbol.cloneWithDeepAliases(sym);
3129 sym.addAliasedName(delegationImplName);
3130 }
3131 final JavaType javaReturnType;
3132
3133 if (cfg.returnsString(sym)) {
3134 final PointerType prt = sym.getReturnType().asPointer();
3135 if (prt == null ||
3136 prt.getTargetType().asInt() == null ||
3137 prt.getTargetType().getSize(curMachDesc) != 1) {
3138 throw new GlueGenException(
3139 "Cannot apply ReturnsString configuration directive to \"" + sym +
3140 "\". ReturnsString requires native method to have return type \"char *\"",
3141 sym.getASTLocusTag());
3142 }
3143 javaReturnType = javaStringType(java.lang.String.class, null);
3144 } else {
3145 final JavaType r = cfg.getOpaqueReturnType(sym);
3146 if( null != r ) {
3147 javaReturnType = r;
3148 } else {
3149 javaReturnType = typeToJavaType(sym.getReturnType(), curMachDesc);
3150 }
3151 }
3152
3153 // List of the indices of the arguments in this function that should be
3154 // converted from byte[] or short[] to String
3155 final List<JavaType> javaArgumentTypes = new ArrayList<JavaType>();
3156 List<Integer> stringValueIndices = cfg.stringArguments(sym);
3157 final List<JavaType.PascalStringElem> pascalStringArgs = cfg.pascalStringArgument(sym);
3158 if( null != pascalStringArgs ) {
3159 stringValueIndices = JavaType.PascalStringElem.pushValueIndex(pascalStringArgs, stringValueIndices);
3160 }
3161 final JavaCallbackInfo jcbi = cfg.setFuncToJavaCallbackMap.get( sym.getName() );
3162 int jcbiSetFuncCBParamIdx=-1;
3163
3164 for (int i = 0; i < sym.getNumArguments(); i++) {
3165 final Type cArgType = sym.getArgumentType(i);
3166 final String cArgName = sym.getArgumentName(i);
3167 JavaType mappedType = typeToJavaType(cArgType, curMachDesc);
3168 int mapMode = 0;
3169
3170 if( null != jcbi && jcbi.cbFuncTypeName.equals( cArgType.getName() ) &&
3171 ( !jcbi.setFuncProcessed || i == jcbi.setFuncCBParamIdx ) )
3172 {
3173 // Replace JavaCallback type with generated interface name
3174 jcbiSetFuncCBParamIdx=i;
3175 mappedType = JavaType.createForNamedClass( jcbi.cbFQClazzName );
3176 mapMode = 10;
3177 } else if( null != jcbi && i == jcbi.setFuncUserParamIdx && cArgType.isPointer() ) {
3178 // Replace userParam argument '<userParamType>*' if 'void*' with Object
3179 if( cArgType.getTargetType().isVoid() ) {
3180 if( jcbi.cbFuncUserParamType.isCompound() ) {
3181 mappedType = JavaType.createForClass(long.class);
3182 mapMode = 20;
3183 } else if( null != jcbi.userParamClassName ) {
3184 mappedType = JavaType.createForNamedClass( jcbi.userParamClassName );
3185 mapMode = 21;
3186 } else {
3187 mappedType = JavaType.forObjectClass();
3188 mapMode = 22;
3189 }
3190 } else {
3191 // fallthrough intended
3192 }
3193 }
3194 if( 0 == mapMode ) {
3195 if ( stringValueIndices != null && stringValueIndices.contains(i) ) {
3196 // Take into account any ArgumentIsString configuration directives that apply
3197 // System.out.println("Forcing conversion of " + binding.getName() + " arg #" + i + " from byte[] to String ");
3198 if ( mappedType.isCVoidPointerType() ||
3199 mappedType.isCCharPointerType() ||
3200 mappedType.isCShortPointerType() ||
3201 mappedType.isNIOPointerBuffer() ||
3202 ( mappedType.isArray() &&
3203 ( mappedType.getJavaClass() == ArrayTypes.byteBufferArrayClass ) ||
3204 ( mappedType.getJavaClass() == ArrayTypes.shortBufferArrayClass ) ) )
3205 {
3206 // convert mapped type from:
3207 // void*, byte[], and short[] to String
3208 // ByteBuffer[] and ShortBuffer[] to String[]
3209 final JavaType.PascalStringElem pascalStrElem = JavaType.PascalStringElem.getByValueIdx(pascalStringArgs, i);
3210 if (mappedType.isArray() || mappedType.isNIOPointerBuffer()) {
3211 mappedType = javaStringType(ArrayTypes.stringArrayClass, pascalStrElem);
3212 mapMode = 30;
3213 } else {
3214 mappedType = javaStringType(String.class, pascalStrElem);
3215 mapMode = 31;
3216 }
3217 } else {
3218 mapMode = 99;
3219 throw new GlueGenException(
3220 "Cannot apply ArgumentIsString configuration directive to " +
3221 "argument " + i + " of \"" + sym + "\": argument type is not " +
3222 "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent",
3223 sym.getASTLocusTag());
3224 }
3225 } else if ( ( mappedType.isInt() || mappedType.isLong() ) && null != pascalStringArgs ) {
3226 final JavaType.PascalStringElem pascalStrElem = JavaType.PascalStringElem.getByLengthIdx(pascalStringArgs, i);
3227 if( null != pascalStrElem ) {
3228 // mark pascal-string length in Java API to be erased for using Java String length
3229 mapMode = 40;
3230 mappedType = new JavaType(mappedType, pascalStrElem);
3231 }
3232 }
3233 }
3234 javaArgumentTypes.add(mappedType);
3235 LOG.log(INFO, "BindFunc: {0}: added mapping ({1}) for {2} from C type: {3} to Java type: {4}",
3236 sym.getName(), mapMode, cArgName, cArgType, mappedType);
3237 }
3238 if( null != jcbi ) {
3239 jcbi.setFuncProcessed(sym.getType(), jcbiSetFuncCBParamIdx);
3240 LOG.log(INFO, "BindFunc.JavaCallback: {0}: set[cbParamIdx {1}], {3}, {4}",
3241 sym.getName(), jcbiSetFuncCBParamIdx, sym.getType().toString(sym.getName(), false, true), jcbi);
3242 }
3243 final MethodBinding mb = new MethodBinding(sym, delegationImplName,
3244 javaReturnType, javaArgumentTypes,
3245 containingType, containingCType);
3246 mangleBinding(mb);
3247 return mb;
3248 }
3249
3250 private MethodBinding lowerMethodBindingPointerTypes(final MethodBinding inputBinding,
3251 final boolean convertToArrays,
3252 final boolean[] canProduceArrayVariant) {
3253 MethodBinding result = inputBinding;
3254 boolean arrayPossible = false;
3255
3256 // System.out.println("lowerMethodBindingPointerTypes(0): "+result);
3257
3258 for (int i = 0; i < inputBinding.getNumArguments(); i++) {
3259 final JavaType t = inputBinding.getJavaArgumentType(i);
3260 if (t.isCPrimitivePointerType()) {
3261 if (t.isCVoidPointerType()) {
3262 // These are always bound to java.nio.Buffer
3263 result = result.replaceJavaArgumentType(i, JavaType.forNIOBufferClass());
3264 } else if (t.isCCharPointerType()) {
3265 arrayPossible = true;
3266 if (convertToArrays) {
3267 result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.byteArrayClass));
3268 } else {
3269 result = result.replaceJavaArgumentType(i, JavaType.forNIOByteBufferClass());
3270 }
3271 } else if (t.isCShortPointerType()) {
3272 arrayPossible = true;
3273 if (convertToArrays) {
3274 result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.shortArrayClass));
3275 } else {
3276 result = result.replaceJavaArgumentType(i, JavaType.forNIOShortBufferClass());
3277 }
3278 } else if (t.isCInt32PointerType()) {
3279 arrayPossible = true;
3280 if (convertToArrays) {
3281 result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.intArrayClass));
3282 } else {
3283 result = result.replaceJavaArgumentType(i, JavaType.forNIOIntBufferClass());
3284 }
3285 } else if (t.isCInt64PointerType()) {
3286 arrayPossible = true;
3287 if (convertToArrays) {
3288 result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.longArrayClass));
3289 } else {
3290 result = result.replaceJavaArgumentType(i, JavaType.forNIOLongBufferClass());
3291 }
3292 } else if (t.isCFloatPointerType()) {
3293 arrayPossible = true;
3294 if (convertToArrays) {
3295 result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.floatArrayClass));
3296 } else {
3297 result = result.replaceJavaArgumentType(i, JavaType.forNIOFloatBufferClass());
3298 }
3299 } else if (t.isCDoublePointerType()) {
3300 arrayPossible = true;
3301 if (convertToArrays) {
3302 result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.doubleArrayClass));
3303 } else {
3304 result = result.replaceJavaArgumentType(i, JavaType.forNIODoubleBufferClass());
3305 }
3306 } else {
3307 throw new GlueGenException("Unknown C pointer type " + t);
3308 }
3309 }
3310 }
3311
3312 // System.out.println("lowerMethodBindingPointerTypes(1): "+result);
3313
3314 // Always return primitive pointer types as NIO buffers
3315 final JavaType t = result.getJavaReturnType();
3316 if (t.isCPrimitivePointerType()) {
3317 if (t.isCVoidPointerType()) {
3318 result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass());
3319 } else if (t.isCCharPointerType()) {
3320 result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass());
3321 } else if (t.isCShortPointerType()) {
3322 result = result.replaceJavaArgumentType(-1, JavaType.forNIOShortBufferClass());
3323 } else if (t.isCInt32PointerType()) {
3324 result = result.replaceJavaArgumentType(-1, JavaType.forNIOIntBufferClass());
3325 } else if (t.isCInt64PointerType()) {
3326 result = result.replaceJavaArgumentType(-1, JavaType.forNIOLongBufferClass());
3327 } else if (t.isCFloatPointerType()) {
3328 result = result.replaceJavaArgumentType(-1, JavaType.forNIOFloatBufferClass());
3329 } else if (t.isCDoublePointerType()) {
3330 result = result.replaceJavaArgumentType(-1, JavaType.forNIODoubleBufferClass());
3331 } else {
3332 throw new GlueGenException("Unknown C pointer type " + t, result.getCReturnType().getASTLocusTag());
3333 }
3334 }
3335
3336 // System.out.println("lowerMethodBindingPointerTypes(2): "+result);
3337
3338 if (canProduceArrayVariant != null) {
3339 canProduceArrayVariant[0] = arrayPossible;
3340 }
3341
3342 return result;
3343 }
3344
3345 /**
3346 * Allow specializations to modify the given {@link MethodBinding}
3347 * before {@link #expandMethodBinding(MethodBinding) expanding} and emission.
3348 */
3349 protected void mangleBinding(final MethodBinding binding) {
3350 // NOP
3351 }
3352
3353 // Expands a MethodBinding containing C primitive pointer types into
3354 // multiple variants taking Java primitive arrays and NIO buffers, subject
3355 // to the per-function "NIO only" rule in the configuration file
3356 protected List<MethodBinding> expandMethodBinding(final MethodBinding binding) {
3357
3358 final List<MethodBinding> result = new ArrayList<MethodBinding>();
3359 // Indicates whether it is possible to produce an array variant
3360 // Prevents e.g. char* -> String conversions from emitting two entry points
3361 final boolean[] canProduceArrayVariant = new boolean[1];
3362
3363 if (binding.signatureUsesCPrimitivePointers() ||
3364 binding.signatureUsesCVoidPointers() ||
3365 binding.signatureUsesCArrays()) {
3366
3367 result.add(lowerMethodBindingPointerTypes(binding, false, canProduceArrayVariant));
3368
3369 // FIXME: should add new configuration flag for this
3370 if (canProduceArrayVariant[0] && (binding.signatureUsesCPrimitivePointers() || binding.signatureUsesCArrays()) &&
3371 !cfg.useNIOOnly(binding.getName()) ) {
3372 result.add(lowerMethodBindingPointerTypes(binding, true, null));
3373 }
3374 } else {
3375 result.add(binding);
3376 }
3377
3378 return result;
3379 }
3380
3381 private Type canonicalize(final Type t) {
3382 final Type res = canonMap.get(t);
3383 if (res != null) {
3384 return res;
3385 } else {
3386 canonMap.put(t, t);
3387 return t;
3388 }
3389 }
3390
3391 private static final String optStringCharsetCode =
3392 " private static Charset _charset = StandardCharsets.UTF_8;\n" +
3393 "\n"+
3394 " /** Returns the Charset for this class's String mapping, default is StandardCharsets.UTF_8. */\n"+
3395 " public static Charset getCharset() { return _charset; };\n"+
3396 "\n"+
3397 " /** Sets the Charset for this class's String mapping, default is StandardCharsets.UTF_8. */\n"+
3398 " public static void setCharset(Charset cs) { _charset = cs; }\n";
3399
3400 private static final String optStringMaxStrnlenCode =
3401 " private static int _max_strnlen = 8192;\n"+
3402 "\n"+
3403 " /** Returns the maximum number of bytes to read to determine native string length using `strnlen(..)`, default is 8192. */\n"+
3404 " public static int getMaxStrnlen() { return _max_strnlen; };\n"+
3405 "\n"+
3406 " /** Sets the maximum number of bytes to read to determine native string length using `strnlen(..)`, default is 8192. */\n"+
3407 " public static void setMaxStrnlen(int v) { _max_strnlen = v; }\n";
3408}
Utility methods allowing easy java.nio.Buffer manipulations.
Definition: Buffers.java:70
Machine data description for alignment and size onle, see com.jogamp.gluegen.
C code unit (a generated C source file), covering multiple FunctionEmitter allowing to unify output,...
Definition: CCodeUnit.java:37
void emitJNIOnLoadJNIEnvCode(final String libraryBasename)
Emits getJNIOnLoadJNIEnvCode(String).
Definition: CCodeUnit.java:82
void emitHeader(final String packageName, final String className, final List< String > customCode)
Definition: CCodeUnit.java:53
Emits the C-side component of the Java<->C JNI binding to its CodeUnit, see FunctionEmitter.
final void setTemporaryCVariableDeclarations(final List< String > arg)
Sets up a List of Strings containing declarations for temporary C variables to be assigned to after t...
final void setReturnValueCapacityExpression(final MessageFormat expression)
If this function returns a void* encapsulated in a java.nio.Buffer (or compound type wrapper),...
final void setReturnValueLengthExpression(final MessageFormat expression)
If this function returns an array, sets the expression for the length of the returned array.
final void setTemporaryCVariableAssignments(final List< String > arg)
Sets up a List of Strings containing assignments for temporary C variables which are made after the u...
static void emitJavaHeaders(final PrintWriter w, final String packageName, final String className, final boolean isClassNotInterface, final List< String > imports, final String[] accessModifiers, final String[] interfaces, final String classExtended, final EmissionCallback classDocComment)
Emit the opening headers for one java class/interface file.
static String packageAsPath(final String packageName)
Given a java package name (e.g., "java.lang"), return the package as a directory path (i....
static String capitalizeString(final String string)
Converts first letter to upper case.
General code unit (a generated C or Java source file), covering multiple FunctionEmitter allowing to ...
Definition: CodeUnit.java:42
void emit(final String s)
Definition: CodeUnit.java:81
A valid java expression, including its result type, usually generated from a native [C] expression,...
Represents a [native] constant expression, comprises the [native] expression, see getNativeExpr() and...
Generic function emitter to produce C (JNI) or Java code stubs to its CodeUnit, invoking a native fun...
void addModifier(final EmissionModifier m)
static final EmissionModifier STATIC
A generic exception for Jogamp errors used throughout the binding as a substitute for RuntimeExceptio...
Glue code generator for C functions and data structures.
Definition: GlueGen.java:59
Java code unit (a generated Java source file), covering multiple FunctionEmitter allowing to unify ou...
JavaCallback configuration definition (static)
Parses and provides access to the contents of .cfg files for the JavaEmitter.
List< Integer > stringArguments(final AliasedSymbol symbol)
Returns a list of Integers which are the indices of const char* arguments that should be converted to...
String extendedParentClass(final String className)
Returns a List of Strings indicating the interfaces the passed class should declare it implements.
List< String > customJavaCodeForClass(final String className)
Returns a list of Strings containing user-implemented code for the given Java type name (not fully-qu...
List< JavaType.PascalStringElem > pascalStringArgument(final AliasedSymbol symbol)
Returns a list of PascalStringIdx which are tuples of indices of int len, const char* arguments that ...
boolean allStatic()
Returns true if the emission style is AllStatic.
final void read(final String filename)
Reads the configuration file.
TypeInfo canonicalNameOpaque(final String canonicalName)
If the given canonicalName should be considered opaque, returns the TypeInfo describing the replaceme...
boolean shouldIgnoreInImpl(final AliasedSymbol symbol)
Returns true if this aliased symbol should be ignored during glue code generation of implementation o...
String returnValueLength(final String symbol)
Returns a MessageFormat string of the C expression calculating the length of the array being returned...
String returnedArrayLength(final String symbol)
Returns a MessageFormat string of the Java expression calculating the number of elements in the retur...
boolean emitInterface()
Returns true if an interface should be emitted during glue code generation.
boolean emitImpl()
Returns true if an implementing class should be emitted during glue code generation.
boolean isUnimplemented(final AliasedSymbol symbol)
Returns true if this function should be given a body which throws a run-time exception with an "unimp...
static String canonicalStructFieldSymbol(final String structName, final String fieldName)
Returns the canonical configuration name for a struct field name, i.e.
String returnStructMachineDataInfoIndex(final String structName)
Returns a MessageFormat string of the Java code defining mdIdx, i.e.
List< JavaCallbackDef > getJavaCallbackList()
Returns the list of all configured JavaCallback definitions.
List< String > javaPrologueForMethod(final MethodBinding binding, final boolean forImplementingMethodCall, final boolean eraseBufferAndArrayTypes)
Returns a list of Strings which should be emitted as a prologue to the body for the Java-side glue co...
final boolean immutableAccess(final AliasedSymbol symbol)
Returns true if the glue code for the given aliased symbol shall produce code for immutable access on...
JavaType getOpaqueReturnType(final String functionName)
Variant of getOpaqueReturnType(AliasedSymbol), where this method only considers the current-name of t...
String packageForStruct(final String structName)
Returns the package into which to place the glue code for accessing the specified struct.
String getJavaSymbolRename(final String origName)
Returns a replacement name for this function or definition which should be used as the Java name for ...
List< String > javaEpilogueForMethod(final MethodBinding binding, final boolean forImplementingMethodCall, final boolean eraseBufferAndArrayTypes)
Returns a list of Strings which should be emitted as an epilogue to the body for the Java-side glue c...
String returnValueCapacity(final String functionName)
Returns a MessageFormat string of the C expression calculating the capacity of the java....
String getDelegatedImplementation(final String functionName)
Variant of getDelegatedImplementation(AliasedSymbol), where this method only considers the current-na...
String renameJavaType(final String javaTypeName)
Returns a replacement name for this type, which should be the name of a Java wrapper class for a C st...
TypeInfo typeInfo(Type type)
If this type should be considered opaque, returns the TypeInfo describing the replacement type.
List< String > customJNICodeForClass(final String className)
Returns a list of Strings containing user-implemented JNI code for the given Java type name (not full...
final boolean shouldIgnoreInInterface(final String symbol)
Variant of shouldIgnoreInInterface(AliasedSymbol), where this method only considers the current-name ...
List< String > javadocForClass(final String className)
Returns a list of Strings containing Javadoc documentation for the given Java type name (not fully-qu...
void endEmission()
Finish the emission of glue code.
void emitStruct(final CompoundType structCType, final Type structCTypedefPtr)
Emit glue code for the given CompoundType.
CCodeUnit openCUnit(final String filename, final String cUnitName)
void beginFunctions(final TypeDictionary typedefDictionary, final TypeDictionary structDictionary, final Map< Type, Type > canonMap, final List< FunctionSymbol > cFunctions)
void generatePublicEmitters(final MethodBinding binding, final List< FunctionEmitter > allEmitters, final boolean signatureOnly)
Generates the public emitters for this MethodBinding which will produce either simply signatures (for...
Iterator< FunctionSymbol > emitFunctions(final List< FunctionSymbol > funcsToBind)
Emit glue code for the list of FunctionSymbols.
String getJavaOutputDir()
Returns the value that was specified by the configuration directive "JavaOutputDir",...
void generatePrivateEmitters(final MethodBinding binding, final List< FunctionEmitter > allEmitters)
Generates the private emitters for this MethodBinding.
void beginEmission(final GlueEmitterControls controls)
Begin the emission of glue code.
JavaCodeUnit openJavaUnit(final String filename, final String packageName, final String simpleClassName)
void readConfigurationFile(final String filename)
void emitDefine(final ConstantDefinition def, final String optionalComment)
static String getJNIMethodNamePrefix(final String javaPackageName, final String javaClassName)
Returns the JNI method prefix consisting our of mangled package- and class-name.
void beginStructLayout()
Begins the process of computing field offsets and type sizes for the structs to be emitted.
List< MethodBinding > expandMethodBinding(final MethodBinding binding)
String getImplPackageName()
Returns the value that was specified by the configuration directive "ImplPackage",...
void emitCustomJNICode(final CodeUnit unit, final String className)
Emit all the strings specified in the "CustomJNICode" parameters of the configuration file.
void emitAllFileHeaders()
Write out any header information for the output files (class declaration and opening brace,...
JavaConfiguration getConfig()
void emitAllFileFooters()
Write out any footer information for the output files (closing brace of class definition,...
void mangleBinding(final MethodBinding binding)
Allow specializations to modify the given MethodBinding before expanding and emission.
void beginStructs(final TypeDictionary typedefDictionary, final TypeDictionary structDictionary, final Map< Type, Type > canonMap)
static String jniMangle(final String name)
Mangle a class, package or function name for JNI usage, i.e.
JavaEmitter(final JavaConfiguration cfg)
void emitCustomJavaCode(final CodeUnit unit, final String className)
Emit all the strings specified in the "CustomJavaCode" parameters of the configuration file.
String getJavaPackageName()
Returns the value that was specified by the configuration directive "Package", or the default if none...
void endStructLayout()
Finishes the struct layout process.
static int addStrings2Buffer(StringBuilder buf, final String sep, final String first, final Collection< String > col)
String[] getClassAccessModifiers(final String classFQName)
void prepCEmitter(final String returnSizeLookupName, final JavaType javaReturnType, final CMethodBindingEmitter cEmitter)
JavaConfiguration createConfig()
Create the object that will read and store configuration information for this JavaEmitter.
void layoutStruct(final CompoundType t)
Lays out one struct which will be emitted later.
List<? extends FunctionEmitter > generateMethodBindingEmitters(final FunctionSymbol sym)
Generate all appropriate Java bindings for the specified C function symbols.
Emits the Java-side component (interface and.or implementation) of the Java<->C JNI binding to its Co...
void setEpilogue(final List< String > epilogue)
Sets the manually-generated epilogue code for this emitter.
void setReturnedArrayLengthExpression(final String expr)
If the underlying function returns an array (currently only arrays of compound types are supported) a...
void setPrologue(final List< String > prologue)
Sets the manually-generated prologue code for this emitter.
Describes a java-side representation of a type that is used to represent the same data on both the Ja...
Definition: JavaType.java:54
String getName()
Returns the Java type name corresponding to this type.
Definition: JavaType.java:339
static JavaType createForClass(final Class<?> clazz)
Creates a JavaType corresponding to the given Java type.
Definition: JavaType.java:197
static JavaType createForStringClass(final Class<?> clazz, final PascalStringElem pascalStrElem)
Definition: JavaType.java:200
boolean isArrayOfCompoundTypeWrappers()
Definition: JavaType.java:709
static LoggerIf getLogger()
Returns the root package logger.
Definition: Logging.java:355
Represents the binding of a C function to a Java method.
String getName()
Returns the FunctionSymbol's current aliased API name.
FunctionSymbol getCSymbol()
Returns the FunctionSymbol.
String getDelegationImplName()
The implementation delegation name, or null for no delegation.
boolean needsNIOWrappingOrUnwrapping()
Returns true if the function needs NIO-related wrapping/unwrapping or conversion of various arguments...
Utility class for handling Opaque directives for JavaEmitter.
Definition: TypeInfo.java:45
void rename(final String newName)
Rename this symbol with the given newName if not equal current-name.
void addAliasedName(final String origName)
Add the given origName to the list of aliases if not equal current-name.
Models all compound types, i.e., those containing fields: structs and unions.
Represents a field in a struct or union.
Definition: Field.java:47
String getName()
Name of this field in the containing data structure.
Definition: Field.java:98
Type getType()
Type of this field.
Definition: Field.java:101
Describes a function symbol, which includes the name and type.
ASTLocusTag getASTLocusTag()
Returns this instance's ASTLocusTag, if available, otherwise returns null.
Provides a level of indirection between the definition of a type's size and the absolute value of thi...
Definition: SizeThunk.java:51
Encapsulates algorithm for laying out data structures.
static StructLayout create(final int baseOffset)
Utility class for recording names of typedefs and structs.
final boolean isArray()
Indicates whether this is an ArrayType.
Definition: Type.java:409
final StringBuilder getSignature(StringBuilder sb)
Definition: Type.java:173
final ASTLocusTag getASTLocusTag()
Returns this instance's ASTLocusTag, if available, otherwise returns null.
Definition: Type.java:125
Type getArrayBaseOrPointerTargetType()
Return getBaseType() if isArray() or returns getTargetType() otherwise.
Definition: Type.java:612
final boolean isConst()
Indicates whether this type is const.
Definition: Type.java:420
Type getBaseType()
Helper method to returns the bottom-most element type of this type, i.e.
Definition: Type.java:588
boolean isFunctionPointer()
Convenience routine indicating whether this Type is a pointer to a function.
Definition: Type.java:430
final String getName()
Returns the name of this type.
Definition: Type.java:142
boolean setTypedefName(final String name)
Set the typedef name of this type and renders this type a typedef, if given name has a length.
Definition: Type.java:327
FunctionType getTargetFunction()
Returns the target FunctionType if this type is isFunctionPointer().
Definition: Type.java:619
Type getTargetType()
Helper method to returns the target type of this type, in case another type is being referenced,...
Definition: Type.java:605
final SizeThunk getSize()
SizeThunk which computes size of this type in bytes.
Definition: Type.java:360
final boolean isPointer()
Indicates whether this is a PointerType.
Definition: Type.java:407
final boolean isCompound()
Indicates whether this is a CompoundType.
Definition: Type.java:411
Static enumeration of MachineDataInfo instances used for high performance data size and alignment loo...
Access control for emitted Java methods.
Mixed
Ambiguous mixed ownership of resource, i.e.
Parent
Parent ownership of resource, i.e.
Native
Native ownership of resource.
Interface callers may use ProcAddressHelper's reset helper method to install function pointers into a...
A class that emits source code of some time when activated.
Specifies the interface by which a GlueEmitter can request additional information from the glue gener...
Specifies the interface by which GlueGen requests glue code to be generated.
An interface for Logger.
Definition: Logging.java:59
void log(final Level level, final String msg)
See Logger#log(Level, String).
Provides a mechanism by which the GlueEmitter can look at all of the #defines, enum values and functi...