diff options
Diffstat (limited to 'src')
135 files changed, 11037 insertions, 3649 deletions
diff --git a/src/antlr/com/jogamp/gluegen/cgram/GnuCParser.g b/src/antlr/com/jogamp/gluegen/cgram/GnuCParser.g index e8ca8c5..bf01b12 100644 --- a/src/antlr/com/jogamp/gluegen/cgram/GnuCParser.g +++ b/src/antlr/com/jogamp/gluegen/cgram/GnuCParser.g @@ -23,6 +23,7 @@ header { import antlr.CommonAST; import antlr.DumpASTVisitor; + import com.jogamp.gluegen.ASTLocusTag; } @@ -715,7 +716,7 @@ tokens { public void addDefine(String name, String value) { - defines.add(new Define(name, value)); + defines.add(new Define(name, value, new ASTLocusTag(lineObject.getSource(), lineObject.getLine()+deferredLineCount, -1, name))); } /** Returns a list of Define objects corresponding to the diff --git a/src/antlr/com/jogamp/gluegen/cgram/HeaderParser.g b/src/antlr/com/jogamp/gluegen/cgram/HeaderParser.g index 01f10c3..59eaca3 100644 --- a/src/antlr/com/jogamp/gluegen/cgram/HeaderParser.g +++ b/src/antlr/com/jogamp/gluegen/cgram/HeaderParser.g @@ -45,7 +45,14 @@ header { import java.util.*; import antlr.CommonAST; + import com.jogamp.gluegen.ASTLocusTag; + import com.jogamp.gluegen.ConstantDefinition; + import com.jogamp.gluegen.ConstantDefinition.CNumber; + import com.jogamp.gluegen.GlueGenException; + import com.jogamp.gluegen.JavaConfiguration; import com.jogamp.gluegen.cgram.types.*; + import com.jogamp.gluegen.cgram.types.EnumType; + import com.jogamp.gluegen.cgram.types.EnumType.Enumerator; } class HeaderParser extends GnuCTreeParser; @@ -67,6 +74,12 @@ options { this.debug = debug; } + /** Set the configuration for this + HeaderParser. Must be done before parsing. */ + public void setJavaConfiguration(JavaConfiguration cfg) { + this.cfg = cfg; + } + /** Set the dictionary mapping typedef names to types for this HeaderParser. Must be done before parsing. */ public void setTypedefDictionary(TypeDictionary dict) { @@ -105,7 +118,7 @@ options { // the enumerates from each EnumType, and fill in the enumHash // so that each enumerate maps to the enumType to which it // belongs. - throw new RuntimeException("setEnums is Unimplemented!"); + throw new RuntimeException("setEnums is Unimplemented!"); } /** Returns the EnumTypes this HeaderParser processed. */ @@ -125,22 +138,25 @@ options { return functions; } - private CompoundType lookupInStructDictionary(String typeName, + private CompoundType lookupInStructDictionary(String structName, CompoundTypeKind kind, - int cvAttrs) { - CompoundType t = (CompoundType) structDictionary.get(typeName); + int cvAttrs, final ASTLocusTag locusTag) + { + CompoundType t = (CompoundType) structDictionary.get(structName); if (t == null) { - t = CompoundType.create(null, null, kind, cvAttrs); - t.setStructName(typeName); - structDictionary.put(typeName, t); + t = CompoundType.create(structName, null, kind, cvAttrs, locusTag); + structDictionary.put(structName, t); + debugPrintln("Adding compound mapping: [" + structName + "] -> "+getDebugTypeString(t)+" @ "+locusTag); + debugPrintln(t.getStructString()); } return t; } - private Type lookupInTypedefDictionary(String typeName) { + private Type lookupInTypedefDictionary(final AST _t, String typeName) { Type t = typedefDictionary.get(typeName); if (t == null) { - throw new RuntimeException("Undefined reference to typedef name " + typeName); + throwGlueGenException(_t, + "Undefined reference to typedef name " + typeName); } return t; } @@ -153,8 +169,10 @@ options { this.id = id; this.type = type; } - String id() { return id; } - Type type() { return type; } + String id() { return id; } + Type type() { return type; } + void setType(final Type t) { type = t; } + public String toString() { return "ParamDecl["+id+": "+type.getDebugString()+"]"; } } // A box for a Type. Allows type to be passed down to be modified by recursive rules. @@ -207,22 +225,27 @@ options { } } + private String getDebugTypeString(Type t) { + if(debug) { + return getTypeString(t); + } else { + return null; + } + } private String getTypeString(Type t) { StringBuilder sb = new StringBuilder(); sb.append("["); - sb.append(t); - sb.append(", size: "); if(null!=t) { - SizeThunk st = t.getSize(); - if(null!=st) { - sb.append(st.getClass().getName()); - } else { - sb.append("undef"); - } + sb.append(t.getDebugString()); + sb.append(", opaque ").append(isOpaque(t)).append("]"); + } else { + sb.append("nil]"); } - sb.append("]"); return sb.toString(); } + private boolean isOpaque(final Type type) { + return (cfg.typeInfo(type) != null); + } private void debugPrintln(String msg) { if(debug) { @@ -236,14 +259,13 @@ options { } } - private boolean doDeclaration; // Used to only process function typedefs - private String declId; - private List parameters; + private JavaConfiguration cfg; private TypeDictionary typedefDictionary; private TypeDictionary structDictionary; private List<FunctionSymbol> functions = new ArrayList<FunctionSymbol>(); // hash from name of an enumerated value to the EnumType to which it belongs private HashMap<String, EnumType> enumHash = new HashMap<String, EnumType>(); + private HashMap<String, EnumType> enumMap = new HashMap<String, EnumType>(); // Storage class specifiers private static final int AUTO = 1 << 0; @@ -259,25 +281,41 @@ options { private static final int SIGNED = 1 << 8; private static final int UNSIGNED = 1 << 9; - private void initDeclaration() { - doDeclaration = false; - declId = null; - } + private boolean isFuncDeclaration; // Used to only process function typedefs + private String funcDeclName; + private List<ParameterDeclaration> funcDeclParams; + private ASTLocusTag funcLocusTag; - private void doDeclaration() { - doDeclaration = true; + private void resetFuncDeclaration() { + isFuncDeclaration = false; + funcDeclName = null; + funcDeclParams = null; + funcLocusTag = null; + } + private void setFuncDeclaration(final String name, final List<ParameterDeclaration> p, final ASTLocusTag locusTag) { + isFuncDeclaration = true; + funcDeclName = name; + funcDeclParams = p; + funcLocusTag = locusTag; } private void processDeclaration(Type returnType) { - if (doDeclaration) { - FunctionSymbol sym = new FunctionSymbol(declId, new FunctionType(null, null, returnType, 0)); - if (parameters != null) { // handle funcs w/ empty parameter lists (e.g., "foo()") - for (Iterator iter = parameters.iterator(); iter.hasNext(); ) { - ParameterDeclaration pd = (ParameterDeclaration) iter.next(); + if (isFuncDeclaration) { + final FunctionSymbol sym = new FunctionSymbol(funcDeclName, + new FunctionType(null, null, returnType, 0, funcLocusTag), + funcLocusTag); + debugPrintln("Function ... "+sym.toString()+" @ "+funcLocusTag); + if (funcDeclParams != null) { // handle funcs w/ empty parameter lists (e.g., "foo()") + for (Iterator<ParameterDeclaration> iter = funcDeclParams.iterator(); iter.hasNext(); ) { + ParameterDeclaration pd = iter.next(); + pd.setType(pd.type()); + debugPrintln(" add "+pd.toString()); sym.addArgument(pd.type(), pd.id()); } - } + } + debugPrintln("Function Added "+sym.toString()); functions.add(sym); + resetFuncDeclaration(); } } @@ -294,19 +332,18 @@ options { /** Helper routine which handles creating a pointer or array type for [] expressions */ - private void handleArrayExpr(TypeBox tb, AST t) { + private void handleArrayExpr(TypeBox tb, AST t, ASTLocusTag locusTag) { if (t != null) { try { final int len = parseIntConstExpr(t); - tb.setType(canonicalize(new ArrayType(tb.type(), SizeThunk.mul(SizeThunk.constant(len), tb.type().getSize()), len, 0))); + tb.setType(canonicalize(new ArrayType(tb.type(), SizeThunk.mul(SizeThunk.constant(len), tb.type().getSize()), len, 0, locusTag))); return; } catch (RecognitionException e) { // Fall through } } tb.setType(canonicalize(new PointerType(SizeThunk.POINTER, - tb.type(), - 0))); + tb.type(), 0, locusTag))); } private int parseIntConstExpr(AST t) throws RecognitionException { @@ -315,49 +352,104 @@ options { /** Utility function: creates a new EnumType with the given name, or returns an existing one if it has already been created. */ - private EnumType getEnumType(String enumTypeName) { + private EnumType getEnumType(String enumTypeName, ASTLocusTag locusTag) { EnumType enumType = null; Iterator<EnumType> it = enumHash.values().iterator(); while (it.hasNext()) { EnumType potentialMatch = it.next(); if (potentialMatch.getName().equals(enumTypeName)) { - enumType = potentialMatch; - break; + enumType = potentialMatch; + break; } } if (enumType == null) { - // This isn't quite correct. In theory the enum should expand to - // the size of the largest element, so if there were a long long - // entry the enum should expand to e.g. int64. However, using - // "long" here (which is what used to be the case) was - // definitely incorrect and caused problems. - enumType = new EnumType(enumTypeName, SizeThunk.INT32); + // This isn't quite correct. In theory the enum should expand to + // the size of the largest element, so if there were a long long + // entry the enum should expand to e.g. int64. However, using + // "long" here (which is what used to be the case) was + // definitely incorrect and caused problems. + enumType = new EnumType(enumTypeName, SizeThunk.INT32, locusTag); } return enumType; - } + } // Map used to canonicalize types. For example, we may typedef // struct foo { ... } *pfoo; subsequent references to struct foo* should // point to the same PointerType object that had its name set to "pfoo". - private Map canonMap = new HashMap(); + // Opaque canonical types are excluded. + private Map<Type, Type> canonMap = new HashMap<Type, Type>(); private Type canonicalize(Type t) { Type res = (Type) canonMap.get(t); if (res != null) { return res; + } else { + canonMap.put(t, t); + return t; + } + } + + private void throwGlueGenException(final AST t, final String message) throws GlueGenException { + // dumpASTTree("XXX", t); + throw new GlueGenException(message, findASTLocusTag(t)); + } + private void throwGlueGenException(final ASTLocusTag locusTag, final String message) throws GlueGenException { + // dumpASTTree("XXX", t); + throw new GlueGenException(message, locusTag); + } + + /** + * Return ASTLocusTag in tree, or null if not found. + */ + private ASTLocusTag findASTLocusTag(final AST astIn) { + AST ast = astIn; + while(null != ast) { + if( ast instanceof TNode ) { + final TNode tn = (TNode) ast; + final ASTLocusTag tag = tn.getASTLocusTag(); + if( null != tag ) { + return tag; + } + } + ast = ast.getFirstChild(); + } + return null; + } + private void dumpASTTree(final String pre, final AST t) { + int i=0; + AST it = t; + while( null != it ) { + it = dumpAST(pre+"."+i, it); + i++; + } + } + private AST dumpAST(final String pre, final AST ast) { + if( null == ast ) { + System.err.println(pre+".0: AST NULL"); + return null; + } else { + System.err.println(pre+".0: AST Type: "+ast.getClass().getName()); + System.err.println(pre+".1: line:col "+ast.getLine()+":"+ast.getColumn()+" -> "+ast.getText()); + if( ast instanceof TNode ) { + final TNode tn = (TNode) ast; + final ASTLocusTag tag = tn.getASTLocusTag(); + System.err.println(pre+".TN.1: "+tag); + final Hashtable<String, Object> attributes = tn.getAttributesTable(); + System.err.println(pre+".TN.2: "+attributes); + } + return ast.getFirstChild(); } - canonMap.put(t, t); - return t; } } declarator[TypeBox tb] returns [String s] { - initDeclaration(); + resetFuncDeclaration(); s = null; - List params = null; + List<ParameterDeclaration> params = null; String funcPointerName = null; TypeBox dummyTypeBox = null; + final ASTLocusTag locusTag = findASTLocusTag(declarator_AST_in); } : #( NDeclarator ( pointerGroup[tb] )? @@ -374,32 +466,29 @@ declarator[TypeBox tb] returns [String s] { RPAREN ) { if (id != null) { - declId = id.getText(); - parameters = params; // FIXME: Ken, why are we setting this class member here? - doDeclaration(); + setFuncDeclaration(id.getText(), params, locusTag); } else if ( funcPointerName != null ) { /* TypeBox becomes function pointer in this case */ - FunctionType ft = new FunctionType(null, null, tb.type(), 0); + final FunctionType ft = new FunctionType(null, null, tb.type(), 0, locusTag); if (params == null) { - // If the function pointer has no declared parameters, it's a - // void function. I'm not sure if the parameter name is - // ever referenced anywhere when the type is VoidType, so + // If the function pointer has no declared parameters, it's a + // void function. I'm not sure if the parameter name is + // ever referenced anywhere when the type is VoidType, so // just in case I'll set it to a comment string so it will - // still compile if written out to code anywhere. - ft.addArgument(new VoidType(0), "/*unnamed-void*/"); - } else { - for (Iterator iter = params.iterator(); iter.hasNext(); ) { - ParameterDeclaration pd = (ParameterDeclaration) iter.next(); - ft.addArgument(pd.type(), pd.id()); - } + // still compile if written out to code anywhere. + ft.addArgument(new VoidType(0, locusTag), "/*unnamed-void*/"); + } else { + for (Iterator iter = params.iterator(); iter.hasNext(); ) { + ParameterDeclaration pd = (ParameterDeclaration) iter.next(); + ft.addArgument(pd.type(), pd.id()); + } } tb.setType(canonicalize(new PointerType(SizeThunk.POINTER, - ft, - 0))); + ft, 0, locusTag))); s = funcPointerName; } } - | LBRACKET ( e:expr )? RBRACKET { handleArrayExpr(tb, e); } + | LBRACKET ( e:expr )? RBRACKET { handleArrayExpr(tb, e, locusTag); } )* ) ; @@ -422,8 +511,8 @@ declaration { ) { processDeclaration(tb.type()); } ; -parameterTypeList returns [List l] { l = new ArrayList(); ParameterDeclaration decl = null; } - : ( decl = parameterDeclaration { if (decl != null) l.add(decl); } ( COMMA | SEMI )? )+ ( VARARGS )? +parameterTypeList returns [List<ParameterDeclaration> l] { l = new ArrayList<ParameterDeclaration>(); ParameterDeclaration decl = null; } + : ( decl = parameterDeclaration { if (decl != null) { l.add(decl); } } ( COMMA | SEMI )? )+ ( VARARGS )? ; parameterDeclaration returns [ParameterDeclaration pd] { @@ -435,7 +524,13 @@ parameterDeclaration returns [ParameterDeclaration pd] { : #( NParameterDeclaration tb = declSpecifiers (decl = declarator[tb] | nonemptyAbstractDeclarator[tb])? - ) { pd = new ParameterDeclaration(decl, tb.type()); } + ) { + if( null == tb ) { + throwGlueGenException(parameterDeclaration_AST_in, + String.format("Undefined type for declaration '%s'", decl)); + } + pd = new ParameterDeclaration(decl, tb.type()); + } ; functionDef { @@ -462,7 +557,10 @@ declSpecifiers returns [TypeBox tb] { { if (t == null && (x & (SIGNED | UNSIGNED)) != 0) { - t = new IntType("int", SizeThunk.INTxx, ((x & UNSIGNED) != 0), attrs2CVAttrs(x)); + t = new IntType("int", SizeThunk.INTxx, + ((x & UNSIGNED) != 0), + attrs2CVAttrs(x), + findASTLocusTag(declSpecifiers_AST_in)); } tb = new TypeBox(t, ((x & TYPEDEF) != 0)); } @@ -493,30 +591,35 @@ typeQualifier returns [int x] { x = 0; } typeSpecifier[int attributes] returns [Type t] { t = null; int cvAttrs = attrs2CVAttrs(attributes); - boolean unsigned = ((attributes & UNSIGNED) != 0); + boolean unsig = ((attributes & UNSIGNED) != 0); + final ASTLocusTag locusTag = findASTLocusTag(typeSpecifier_AST_in); } - : "void" { t = new VoidType(cvAttrs); } - | "char" { t = new IntType("char" , SizeThunk.INT8, unsigned, cvAttrs); } - | "short" { t = new IntType("short", SizeThunk.INT16, unsigned, cvAttrs); } - | "int" { t = new IntType("int" , SizeThunk.INTxx, unsigned, cvAttrs); } - | "long" { t = new IntType("long" , SizeThunk.LONG, unsigned, cvAttrs); } - | "float" { t = new FloatType("float", SizeThunk.FLOAT, cvAttrs); } - | "double" { t = new DoubleType("double", SizeThunk.DOUBLE, cvAttrs); } - | "__int32" { t = new IntType("__int32", SizeThunk.INT32, unsigned, cvAttrs); } - | "__int64" { t = new IntType("__int64", SizeThunk.INT64, unsigned, cvAttrs); } - | "int8_t" { t = new IntType("int8_t", SizeThunk.INT8, false, cvAttrs); /* TS: always signed */ } - | "uint8_t" { t = new IntType("uint8_t", SizeThunk.INT8, true, cvAttrs); /* TS: always unsigned */ } - | "int16_t" { t = new IntType("int16_t", SizeThunk.INT16, false, cvAttrs); /* TS: always signed */ } - | "uint16_t" { t = new IntType("uint16_t", SizeThunk.INT16, true, cvAttrs); /* TS: always unsigned */ } - | "int32_t" { t = new IntType("int32_t", SizeThunk.INT32, false, cvAttrs); /* TS: always signed */ } - | "wchar_t" { t = new IntType("wchar_t", SizeThunk.INT32, false, cvAttrs); /* TS: always signed */ } - | "uint32_t" { t = new IntType("uint32_t", SizeThunk.INT32, true, cvAttrs, true); /* TS: always unsigned */ } - | "int64_t" { t = new IntType("int64_t", SizeThunk.INT64, false, cvAttrs); /* TS: always signed */ } - | "uint64_t" { t = new IntType("uint64_t", SizeThunk.INT64, true, cvAttrs, true); /* TS: always unsigned */ } - | "ptrdiff_t" { t = new IntType("ptrdiff_t", SizeThunk.POINTER, false, cvAttrs); /* TS: always signed */ } - | "intptr_t" { t = new IntType("intptr_t", SizeThunk.POINTER, false, cvAttrs); /* TS: always signed */ } - | "size_t" { t = new IntType("size_t", SizeThunk.POINTER, true, cvAttrs, true); /* TS: always unsigned */ } - | "uintptr_t" { t = new IntType("uintptr_t", SizeThunk.POINTER, true, cvAttrs, true); /* TS: always unsigned */ } + // TYPEDEF + // | TYPEDEF-UNSIGNED + // UNSIGNED | | + // TOKEN TYPE NAME SIZE | ATTRIBS | | LOCUS + : "void" { t = new VoidType( cvAttrs, locusTag); } + | "char" { t = new IntType("char" , SizeThunk.INT8, unsig, cvAttrs, false, false, locusTag); } + | "short" { t = new IntType("short", SizeThunk.INT16, unsig, cvAttrs, false, false, locusTag); } + | "int" { t = new IntType("int" , SizeThunk.INTxx, unsig, cvAttrs, false, false, locusTag); } + | "long" { t = new IntType("long" , SizeThunk.LONG, unsig, cvAttrs, false, false, locusTag); } + | "float" { t = new FloatType("float", SizeThunk.FLOAT, cvAttrs, locusTag); } + | "double" { t = new DoubleType("double", SizeThunk.DOUBLE, cvAttrs, locusTag); } + | "__int32" { t = new IntType("__int32", SizeThunk.INT32, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "__int64" { t = new IntType("__int64", SizeThunk.INT64, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "int8_t" { t = new IntType("int8_t", SizeThunk.INT8, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "uint8_t" { t = new IntType("uint8_t", SizeThunk.INT8, unsig, cvAttrs, true, true, locusTag); } /* TD: unsigned */ + | "int16_t" { t = new IntType("int16_t", SizeThunk.INT16, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "uint16_t" { t = new IntType("uint16_t", SizeThunk.INT16, unsig, cvAttrs, true, true, locusTag); } /* TD: unsigned */ + | "int32_t" { t = new IntType("int32_t", SizeThunk.INT32, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "wchar_t" { t = new IntType("wchar_t", SizeThunk.INT32, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "uint32_t" { t = new IntType("uint32_t", SizeThunk.INT32, unsig, cvAttrs, true, true, locusTag); } /* TS: unsigned */ + | "int64_t" { t = new IntType("int64_t", SizeThunk.INT64, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "uint64_t" { t = new IntType("uint64_t", SizeThunk.INT64, unsig, cvAttrs, true, true, locusTag); } /* TD: unsigned */ + | "ptrdiff_t" { t = new IntType("ptrdiff_t", SizeThunk.POINTER, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "intptr_t" { t = new IntType("intptr_t", SizeThunk.POINTER, unsig, cvAttrs, true, false, locusTag); } /* TD: signed */ + | "size_t" { t = new IntType("size_t", SizeThunk.POINTER, unsig, cvAttrs, true, true, locusTag); } /* TD: unsigned */ + | "uintptr_t" { t = new IntType("uintptr_t", SizeThunk.POINTER, unsig, cvAttrs, true, true, locusTag); } /* TD: unsigned */ | t = structSpecifier[cvAttrs] ( attributeDecl )* | t = unionSpecifier [cvAttrs] ( attributeDecl )* | t = enumSpecifier [cvAttrs] @@ -533,9 +636,12 @@ typeSpecifier[int attributes] returns [Type t] { typedefName[int cvAttrs] returns [Type t] { t = null; } : #(NTypedefName id : ID) { - Type tdict = lookupInTypedefDictionary(id.getText()); - t = canonicalize(tdict.getCVVariant(cvAttrs)); - debugPrintln("Adding typedef canon : [" + id.getText() + "] -> [" + tdict + "] -> "+getTypeString(t)); + final Type t0 = lookupInTypedefDictionary(typedefName_AST_in, id.getText()); + debugPrint("Adding typedef lookup: [" + id.getText() + "] -> "+getDebugTypeString(t0)); + final Type t1 = t0.newCVVariant(cvAttrs); + debugPrintln(" - cvvar -> "+getDebugTypeString(t1)); + t = canonicalize(t1); + debugPrintln(" - canon -> "+getDebugTypeString(t)); } ; @@ -549,31 +655,50 @@ unionSpecifier[int cvAttrs] returns [Type t] { t = null; } structOrUnionBody[CompoundTypeKind kind, int cvAttrs] returns [CompoundType t] { t = null; + boolean addedAny = false; + final ASTLocusTag locusTag = findASTLocusTag(structOrUnionBody_AST_in); } : ( (ID LCURLY) => id:ID LCURLY { - t = (CompoundType) canonicalize(lookupInStructDictionary(id.getText(), kind, cvAttrs)); - } ( structDeclarationList[t] )? + // fully declared struct, i.e. not anonymous + t = (CompoundType) canonicalize(lookupInStructDictionary(id.getText(), kind, cvAttrs, locusTag)); + } ( addedAny = structDeclarationList[t] )? RCURLY { t.setBodyParsed(); } - | LCURLY { t = CompoundType.create(null, null, kind, cvAttrs); } - ( structDeclarationList[t] )? + | LCURLY { + // anonymous declared struct + t = CompoundType.create(null, null, kind, cvAttrs, locusTag); + } ( structDeclarationList[t] )? RCURLY { t.setBodyParsed(); } - | id2:ID { t = (CompoundType) canonicalize(lookupInStructDictionary(id2.getText(), kind, cvAttrs)); } - ) + | id2:ID { + // anonymous struct + t = (CompoundType) canonicalize(lookupInStructDictionary(id2.getText(), kind, cvAttrs, locusTag)); + } + ) { + debugPrintln("Adding compound body: [" + t.getName() + "] -> "+getDebugTypeString(t)+" @ "+locusTag); + debugPrintln(t.getStructString()); + } ; -structDeclarationList[CompoundType t] - : ( structDeclaration[t] )+ +structDeclarationList[CompoundType t] returns [boolean addedAny] { + addedAny = false; + boolean addedOne = false; +} + : ( addedOne = structDeclaration[t] { addedAny |= addedOne; } )+ ; -structDeclaration[CompoundType containingType] { +structDeclaration[CompoundType containingType] returns [boolean addedAny] { + addedAny = false; Type t = null; - boolean addedAny = false; } : t = specifierQualifierList addedAny = structDeclaratorList[containingType, t] { if (!addedAny) { if (t != null) { CompoundType ct = t.asCompound(); - if (ct.isUnion()) { + if( null == ct ) { + throwGlueGenException(structDeclaration_AST_in, + String.format("Anonymous compound, w/ NULL type:%n containing '%s'", + getTypeString(containingType))); + } + if ( ct.isUnion() ) { // Anonymous union containingType.addField(new Field(null, t, null)); } @@ -591,7 +716,8 @@ specifierQualifierList returns [Type t] { )+ { if (t == null && (x & (SIGNED | UNSIGNED)) != 0) { - t = new IntType("int", SizeThunk.INTxx, ((x & UNSIGNED) != 0), attrs2CVAttrs(x)); + t = new IntType("int", SizeThunk.INTxx, ((x & UNSIGNED) != 0), attrs2CVAttrs(x), + findASTLocusTag(specifierQualifierList_AST_in)); } } ; @@ -616,7 +742,7 @@ structDeclarator[CompoundType containingType, Type t] returns [boolean addedAny] ) ; -// FIXME: this will not correctly set the name of the enumeration when +// This will not correctly set the name of the enumeration when // encountering a declaration like this: // // typedef enum { } enumName; @@ -625,65 +751,88 @@ structDeclarator[CompoundType containingType, Type t] returns [boolean addedAny] // incorrectly return HeaderParser.ANONYMOUS_ENUM_NAME instead of // "enumName" // -// I haven't implemented it yet because I'm not sure how to get the -// "enumName" *before* executing the enumList rule. +// The followup typedef, see 'initDecl', will alias this name, +// hence correct the issue! enumSpecifier [int cvAttrs] returns [Type t] { t = null; + EnumType e = null; + ASTLocusTag locusTag = findASTLocusTag(enumSpecifier_AST_in); } : #( "enum" - ( ( ID LCURLY )=> i:ID LCURLY enumList[(EnumType)(t = getEnumType(i.getText()))] RCURLY - | LCURLY enumList[(EnumType)(t = getEnumType(ANONYMOUS_ENUM_NAME))] RCURLY - | ID { t = getEnumType(i.getText()); } - ) - ) + ( ( ID LCURLY )=> i:ID LCURLY enumList[(EnumType)(e = getEnumType(i.getText(), locusTag))] RCURLY + | LCURLY enumList[(EnumType)(e = getEnumType(ANONYMOUS_ENUM_NAME, locusTag))] RCURLY + | ID { e = getEnumType(i.getText(), locusTag); } + ) { + debugPrintln("Adding enum mapping: "+getDebugTypeString(e)); + if( null != e ) { + final String eName = e.getName(); + if( null != eName && !eName.equals(ANONYMOUS_ENUM_NAME) ) { // validate only non-anonymous enum + final EnumType dupE = enumMap.get(eName); + if( null != dupE && !dupE.equalSemantics(e) ) { + throwGlueGenException(enumSpecifier_AST_in, + String.format("Duplicate enum w/ incompatible type:%n this '%s',%n have '%s',%n %s: previous definition is here", + getTypeString(e), getTypeString(dupE), dupE.getASTLocusTag().toString(new StringBuilder(), "note", true))); + } + enumMap.put(eName, (EnumType)e.clone(locusTag)); + } + } + t = e; // return val + } + ) ; enumList[EnumType enumeration] { - long defaultEnumerantValue = 0; + ConstantDefinition defEnumerant = new ConstantDefinition("def", "0", new CNumber(true, false, 0), findASTLocusTag(enumList_AST_in)); } - : ( defaultEnumerantValue = enumerator[enumeration, defaultEnumerantValue] )+ + : ( defEnumerant = enumerator[enumeration, defEnumerant] )+ ; -enumerator[EnumType enumeration, long defaultValue] returns [long newDefaultValue] { +enumerator[EnumType enumeration, ConstantDefinition defaultValue] returns [ConstantDefinition newDefaultValue] { newDefaultValue = defaultValue; } : eName:ID ( ASSIGN eVal:expr )? { - long value = 0; + final String eTxt = eName.getText(); + final Enumerator newEnum; if (eVal != null) { - String vTxt = eVal.getAllChildrenText(); + String vTxt = eVal.getAllChildrenText(eTxt); if (enumHash.containsKey(vTxt)) { EnumType oldEnumType = enumHash.get(vTxt); - value = oldEnumType.getEnumValue(vTxt); + Enumerator oldEnum = oldEnumType.getEnum(vTxt); + newEnum = oldEnum; } else { - try { - value = Long.decode(vTxt).longValue(); - } catch (NumberFormatException e) { - System.err.println("NumberFormatException: ID[" + eName.getText() + "], VALUE=[" + vTxt + "]"); - throw e; - } + newEnum = new Enumerator(eTxt, vTxt); } + } else if( defaultValue.hasNumber() ) { + newEnum = new Enumerator(eTxt, defaultValue.getNumber()); } else { - value = defaultValue; + newEnum = new Enumerator(eTxt, defaultValue.getNativeExpr()); } - - newDefaultValue = value+1; - String eTxt = eName.getText(); - if (enumHash.containsKey(eTxt)) { - EnumType oldEnumType = enumHash.get(eTxt); - long oldValue = oldEnumType.getEnumValue(eTxt); - System.err.println("WARNING: redefinition of enumerated value '" + eTxt + "';" + - " existing definition is in enumeration '" + oldEnumType.getName() + - "' with value " + oldValue + " and new definition is in enumeration '" + - enumeration.getName() + "' with value " + value); - // remove old definition - oldEnumType.removeEnumerate(eTxt); - } - // insert new definition - enumeration.addEnum(eTxt, value); - enumHash.put(eTxt, enumeration); - debugPrintln("ENUM [" + enumeration.getName() + "]: " + eTxt + " = " + enumeration.getEnumValue(eTxt) + - " (new default = " + newDefaultValue + ")"); - } + final ASTLocusTag locus = findASTLocusTag(enumerator_AST_in); + final CNumber newEnumNum = newEnum.getNumber(); + if( null != newEnumNum && newEnumNum.isInteger ) { + final long n = newEnumNum.i+1; + newDefaultValue = new ConstantDefinition("def", String.valueOf(n), new CNumber(newEnumNum.isLong, newEnumNum.isUnsigned, n), locus); + } else { + newDefaultValue = new ConstantDefinition("def", "("+newEnum.getExpr()+")+1", null, locus); + } + if (enumHash.containsKey(eTxt)) { + EnumType oldEnumType = enumHash.get(eTxt); + final Enumerator oldEnum = oldEnumType.getEnum(eTxt); + final String oldExpr = oldEnum.getExpr(); + if( !oldExpr.equals(newEnum.getExpr()) ) { + throwGlueGenException(enumerator_AST_in, + String.format("Duplicate enum value '%s.%s' w/ diff value:%n this %s,%n have %s", + oldEnumType.getName(), eTxt, newEnum, oldEnum)); + } + // remove old definition + oldEnumType.removeEnumerate(eTxt); + } + // insert new definition + enumeration.addEnum(eTxt, newEnum); + enumHash.put(eTxt, enumeration); + debugPrintln("ENUM [" + enumeration.getName() + "]: " + eTxt + " = " + newEnum + + " (new default = " + newDefaultValue + ")"); + } ; initDeclList[TypeBox tb] @@ -692,6 +841,7 @@ initDeclList[TypeBox tb] initDecl[TypeBox tb] { String declName = null; + final ASTLocusTag locusTag = findASTLocusTag(initDecl_AST_in); } : #( NInitDecl declName = declarator[tb] { @@ -705,23 +855,99 @@ initDecl[TypeBox tb] { { if ((declName != null) && (tb != null) && tb.isTypedef()) { Type t = tb.type(); - debugPrint("Adding typedef mapping: [" + declName + "] -> "+getTypeString(t)); - if (!t.hasTypedefName()) { - t.setName(declName); - debugPrint(" - declName -> "+getTypeString(t)); + debugPrintln("Adding typedef mapping: [" + declName + "] -> "+getDebugTypeString(t)); + final Type tg; + if( t.isPointer() ) { + tg = t.getTargetType(); + debugPrintln(" - has target: "+getDebugTypeString(tg)); } else { - // copy type to preserve declName ! - t = (Type) t.clone(); - t.setName(declName); - debugPrint(" - copy -> "+getTypeString(t)); + tg = null; + } + // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser) + // Also see NOTE below. + if (!t.isTypedef()) { + if( t.isCompound() || t.isEnum() ) { + // This aliases '_a' -> 'A' for 'typedef struct _a { } A;' in-place + // This aliases '_a' -> 'A' for 'typedef enum _a { } A;' in-place + t.setTypedefName(declName); + debugPrintln(" - alias.11 -> "+getDebugTypeString(t)); + } else { + // Use new typedef, using a copy to preserve canonicalized base type + t = t.clone(locusTag); + t.setTypedefName(declName); + debugPrintln(" - newdefine.12 -> "+getDebugTypeString(t)); + } + } else { + // Adds typeInfo alias w/ t's typeInfo, if exists + cfg.addTypeInfo(declName, t); + final Type alias; + if( t.isCompound() ) { + // This aliases 'D' -> 'A' for 'typedef struct _a { } A, D;' in-place + debugPrintln(" - alias.21 -> "+getDebugTypeString(t)); + } else { + // copy to preserve canonicalized base type + t = t.clone(locusTag); + t.setTypedefName(declName); + debugPrintln(" - copy.22 -> "+getDebugTypeString(t)); + } + } + final Type dupT = typedefDictionary.get(declName); + if( null != dupT && !dupT.equalSemantics(t) ) { + throwGlueGenException(locusTag, + String.format("Duplicate typedef w/ incompatible type:%n this '%s',%n have '%s',%n %s: previous definition is here", + getTypeString(t), getTypeString(dupT), dupT.getASTLocusTag().toString(new StringBuilder(), "note", true))); } t = canonicalize(t); - debugPrintln(" - canon -> "+getTypeString(t)); + debugPrintln(" - canon -> "+getDebugTypeString(t)); typedefDictionary.put(declName, t); // Clear out PointerGroup effects in case another typedef variant follows tb.reset(); } } + /* + // Below just shows a different handling using copying + // and enforcing aliased names, which is not desired. + // Keeping it in here for documentation. + // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser) + if ( !t.isTypedef() ) { + if( t.isCompound() ) { + // This aliases '_a' -> 'A' for 'typedef struct _a { } A;' + t.setTypedefName(declName); + debugPrintln(" - alias.10 -> "+getDebugTypeString(t)); + } else if( null != tg && tg.isCompound() ) { + if( !tg.isTypedef() ) { + // This aliases '_a *' -> 'A*' for 'typedef struct _a { } *A;' + t.setTypedefName(declName); + debugPrintln(" - alias.11 -> "+getDebugTypeString(t)); + } else { + // This aliases 'B' -> 'A*' for 'typedef struct _a { } A, *B;' and 'typedef A * B;' + t = new PointerType(SizeThunk.POINTER, tg, 0, locusTag); // name: 'A*' + t.setTypedefName(t.getName()); // make typedef + debugPrintln(" - alias.12 -> "+getDebugTypeString(t)); + } + } else { + // Use new typedef, using a copy to preserve canonicalized base type + t = t.clone(locusTag); + t.setTypedefName(declName); + debugPrintln(" - newdefine.13 -> "+getDebugTypeString(t)); + } + } else { + // Adds typeInfo alias w/ t's typeInfo, if exists + cfg.addTypeInfo(declName, t); + if( t.isCompound() ) { + // This aliases 'D' -> 'A' for 'typedef struct _a { } A, D;' + debugPrintln(" - alias.20 -> "+getDebugTypeString(t)); + } else if( null != tg && tg.isCompound() ) { + // This aliases 'B' -> 'A' for 'typedef A B;', where A is pointer to compound + debugPrintln(" - alias.21 -> "+getDebugTypeString(t)); + } else { + // copy to preserve canonicalized base type + t = t.clone(locusTag); + t.setTypedefName(declName); + debugPrintln(" - copy.22 -> "+getDebugTypeString(t)); + } + } +*/ ; pointerGroup[TypeBox tb] { int x = 0; int y = 0; } @@ -731,7 +957,8 @@ pointerGroup[TypeBox tb] { int x = 0; int y = 0; } if (tb != null) { tb.setType(canonicalize(new PointerType(SizeThunk.POINTER, tb.type(), - attrs2CVAttrs(x)))); + attrs2CVAttrs(x), + findASTLocusTag(pointerGroup_AST_in)))); } } )+ ) @@ -756,7 +983,9 @@ typeName { /* FIXME: the handling of types in this rule has not been well thought out and is known to be incomplete. Currently it is only used to handle pointerGroups for unnamed parameters. */ -nonemptyAbstractDeclarator[TypeBox tb] +nonemptyAbstractDeclarator[TypeBox tb] { + final ASTLocusTag locusTag = findASTLocusTag(nonemptyAbstractDeclarator_AST_in); +} : #( NNonemptyAbstractDeclarator ( pointerGroup[tb] ( (LPAREN @@ -764,7 +993,7 @@ nonemptyAbstractDeclarator[TypeBox tb] | parameterTypeList )? RPAREN) - | (LBRACKET (e1:expr)? RBRACKET) { handleArrayExpr(tb, e1); } + | (LBRACKET (e1:expr)? RBRACKET) { handleArrayExpr(tb, e1, locusTag); } )* | ( (LPAREN @@ -772,7 +1001,7 @@ nonemptyAbstractDeclarator[TypeBox tb] | parameterTypeList )? RPAREN) - | (LBRACKET (e2:expr)? RBRACKET) { handleArrayExpr(tb, e2); } + | (LBRACKET (e2:expr)? RBRACKET) { handleArrayExpr(tb, e2, locusTag); } )+ ) ) @@ -786,13 +1015,17 @@ intConstExpr returns [int i] { i = -1; } final String enumName = e.getText(); final EnumType enumType = enumHash.get(enumName); if( null == enumType ) { - throw new IllegalArgumentException("Error: intConstExpr ID "+enumName+" recognized, but no containing enum-type found"); + throwGlueGenException(intConstExpr_AST_in, + "Error: intConstExpr ID "+enumName+" recognized, but no containing enum-type found"); } - final long enumValue = enumType.getEnumValue(enumName); - System.err.println("INFO: intConstExpr: enum[Type "+enumType.getName()+", name "+enumName+", value "+enumValue+"]"); - if( (long)Integer.MIN_VALUE > enumValue || (long)Integer.MAX_VALUE < enumValue ) { - throw new IndexOutOfBoundsException("Error: intConstExpr ID "+enumName+" enum-value "+enumValue+" out of int range"); + final Enumerator enumerator = enumType.getEnum(enumName); + final CNumber number = enumerator.getNumber(); + if( null != number && number.isInteger && !number.isLong ) { + debugPrintln("INFO: intConstExpr: enum[Type "+enumType.getName()+", "+enumerator+"]"); + } else { + throwGlueGenException(intConstExpr_AST_in, + "Error: intConstExpr ID "+enumName+" enum "+enumerator+" not an int32_t"); } - return (int)enumValue; + return (int)number.i; } ; diff --git a/src/antlr/com/jogamp/gluegen/cgram/StdCParser.g b/src/antlr/com/jogamp/gluegen/cgram/StdCParser.g index 7b34656..26da996 100644 --- a/src/antlr/com/jogamp/gluegen/cgram/StdCParser.g +++ b/src/antlr/com/jogamp/gluegen/cgram/StdCParser.g @@ -1131,11 +1131,12 @@ options { nw:NonWhitespace ("\r\n" | "\r" | "\n") ) { if (n != null) { - //System.out.println("addDefine: #define " + i.getText() + " " + n.getText()); + // System.out.println("addDefine: #define " + i.getText() + " " + n.getText()+" @ "+lineObject.getSource()+":"+(lineObject.line+deferredLineCount)); addDefine(i.getText(), n.getText()); } else { setPreprocessingDirective("#define " + i.getText() + " " + nw.getText()); } + deferredNewline(); } | (~'\n')* { setPreprocessingDirective(getText()); } ) @@ -1165,15 +1166,19 @@ protected LineDirective } : { - lineObject = new LineObject(); - deferredLineCount = 0; + lineObject = new LineObject(); + deferredLineCount = 0; } ("line")? //this would be for if the directive started "#line", but not there for GNU directives (Space)+ - n:Number { lineObject.setLine(Integer.parseInt(n.getText())); } + n:Number { + lineObject.setLine(Integer.parseInt(n.getText())); + } (Space)+ ( fn:StringLiteral { try { - lineObject.setSource(fn.getText().substring(1,fn.getText().length()-1)); + final String newSource = fn.getText().substring(1,fn.getText().length()-1); + // System.out.println("line: "+lineObject.getSource()+" -> "+newSource+", line "+(lineObject.line+deferredLineCount)); + lineObject.setSource(newSource); } catch (StringIndexOutOfBoundsException e) { /*not possible*/ } } diff --git a/src/antlr/com/jogamp/gluegen/jgram/JavaParser.g b/src/antlr/com/jogamp/gluegen/jgram/JavaParser.g index f67579e..1c06bfd 100644 --- a/src/antlr/com/jogamp/gluegen/jgram/JavaParser.g +++ b/src/antlr/com/jogamp/gluegen/jgram/JavaParser.g @@ -8,105 +8,105 @@ * Run 'java Main <directory full of java files>' * * Contributing authors: - * John Mitchell [email protected] - * Terence Parr [email protected] - * John Lilley [email protected] - * Scott Stanchfield [email protected] - * Markus Mohnen [email protected] + * John Mitchell [email protected] + * Terence Parr [email protected] + * John Lilley [email protected] + * Scott Stanchfield [email protected] + * Markus Mohnen [email protected] * Peter Williams [email protected] * Allan Jacobs [email protected] * Steve Messick [email protected] - * John Pybus [email protected] + * John Pybus [email protected] * * Version 1.00 December 9, 1997 -- initial release * Version 1.01 December 10, 1997 - * fixed bug in octal def (0..7 not 0..8) + * fixed bug in octal def (0..7 not 0..8) * Version 1.10 August 1998 (parrt) - * added tree construction - * fixed definition of WS,comments for mac,pc,unix newlines - * added unary plus + * added tree construction + * fixed definition of WS,comments for mac,pc,unix newlines + * added unary plus * Version 1.11 (Nov 20, 1998) - * Added "shutup" option to turn off last ambig warning. - * Fixed inner class def to allow named class defs as statements - * synchronized requires compound not simple statement - * add [] after builtInType DOT class in primaryExpression - * "const" is reserved but not valid..removed from modifiers + * Added "shutup" option to turn off last ambig warning. + * Fixed inner class def to allow named class defs as statements + * synchronized requires compound not simple statement + * add [] after builtInType DOT class in primaryExpression + * "const" is reserved but not valid..removed from modifiers * Version 1.12 (Feb 2, 1999) - * Changed LITERAL_xxx to xxx in tree grammar. - * Updated java.g to use tokens {...} now for 2.6.0 (new feature). + * Changed LITERAL_xxx to xxx in tree grammar. + * Updated java.g to use tokens {...} now for 2.6.0 (new feature). * * Version 1.13 (Apr 23, 1999) - * Didn't have (stat)? for else clause in tree parser. - * Didn't gen ASTs for interface extends. Updated tree parser too. - * Updated to 2.6.0. + * Didn't have (stat)? for else clause in tree parser. + * Didn't gen ASTs for interface extends. Updated tree parser too. + * Updated to 2.6.0. * Version 1.14 (Jun 20, 1999) - * Allowed final/abstract on local classes. - * Removed local interfaces from methods - * Put instanceof precedence where it belongs...in relationalExpr - * It also had expr not type as arg; fixed it. - * Missing ! on SEMI in classBlock - * fixed: (expr) + "string" was parsed incorrectly (+ as unary plus). - * fixed: didn't like Object[].class in parser or tree parser + * Allowed final/abstract on local classes. + * Removed local interfaces from methods + * Put instanceof precedence where it belongs...in relationalExpr + * It also had expr not type as arg; fixed it. + * Missing ! on SEMI in classBlock + * fixed: (expr) + "string" was parsed incorrectly (+ as unary plus). + * fixed: didn't like Object[].class in parser or tree parser * Version 1.15 (Jun 26, 1999) - * Screwed up rule with instanceof in it. :( Fixed. - * Tree parser didn't like (expr).something; fixed. - * Allowed multiple inheritance in tree grammar. oops. + * Screwed up rule with instanceof in it. :( Fixed. + * Tree parser didn't like (expr).something; fixed. + * Allowed multiple inheritance in tree grammar. oops. * Version 1.16 (August 22, 1999) - * Extending an interface built a wacky tree: had extra EXTENDS. - * Tree grammar didn't allow multiple superinterfaces. - * Tree grammar didn't allow empty var initializer: {} + * Extending an interface built a wacky tree: had extra EXTENDS. + * Tree grammar didn't allow multiple superinterfaces. + * Tree grammar didn't allow empty var initializer: {} * Version 1.17 (October 12, 1999) - * ESC lexer rule allowed 399 max not 377 max. - * java.tree.g didn't handle the expression of synchronized - * statements. + * ESC lexer rule allowed 399 max not 377 max. + * java.tree.g didn't handle the expression of synchronized + * statements. * Version 1.18 (August 12, 2001) - * Terence updated to Java 2 Version 1.3 by - * observing/combining work of Allan Jacobs and Steve - * Messick. Handles 1.3 src. Summary: - * o primary didn't include boolean.class kind of thing - * o constructor calls parsed explicitly now: - * see explicitConstructorInvocation - * o add strictfp modifier - * o missing objBlock after new expression in tree grammar - * o merged local class definition alternatives, moved after declaration - * o fixed problem with ClassName.super.field - * o reordered some alternatives to make things more efficient - * o long and double constants were not differentiated from int/float - * o whitespace rule was inefficient: matched only one char - * o add an examples directory with some nasty 1.3 cases - * o made Main.java use buffered IO and a Reader for Unicode support - * o supports UNICODE? - * Using Unicode charVocabulay makes code file big, but only - * in the bitsets at the end. I need to make ANTLR generate - * unicode bitsets more efficiently. + * Terence updated to Java 2 Version 1.3 by + * observing/combining work of Allan Jacobs and Steve + * Messick. Handles 1.3 src. Summary: + * o primary didn't include boolean.class kind of thing + * o constructor calls parsed explicitly now: + * see explicitConstructorInvocation + * o add strictfp modifier + * o missing objBlock after new expression in tree grammar + * o merged local class definition alternatives, moved after declaration + * o fixed problem with ClassName.super.field + * o reordered some alternatives to make things more efficient + * o long and double constants were not differentiated from int/float + * o whitespace rule was inefficient: matched only one char + * o add an examples directory with some nasty 1.3 cases + * o made Main.java use buffered IO and a Reader for Unicode support + * o supports UNICODE? + * Using Unicode charVocabulay makes code file big, but only + * in the bitsets at the end. I need to make ANTLR generate + * unicode bitsets more efficiently. * Version 1.19 (April 25, 2002) - * Terence added in nice fixes by John Pybus concerning floating - * constants and problems with super() calls. John did a nice - * reorg of the primary/postfix expression stuff to read better - * and makes f.g.super() parse properly (it was METHOD_CALL not - * a SUPER_CTOR_CALL). Also: + * Terence added in nice fixes by John Pybus concerning floating + * constants and problems with super() calls. John did a nice + * reorg of the primary/postfix expression stuff to read better + * and makes f.g.super() parse properly (it was METHOD_CALL not + * a SUPER_CTOR_CALL). Also: * - * o "finally" clause was a root...made it a child of "try" - * o Added stuff for asserts too for Java 1.4, but *commented out* - * as it is not backward compatible. + * o "finally" clause was a root...made it a child of "try" + * o Added stuff for asserts too for Java 1.4, but *commented out* + * as it is not backward compatible. * * Version 1.20 (October 27, 2002) * * Terence ended up reorging John Pybus' stuff to * remove some nondeterminisms and some syntactic predicates. * Note that the grammar is stricter now; e.g., this(...) must - * be the first statement. + * be the first statement. * * Trinary ?: operator wasn't working as array name: * (isBig ? bigDigits : digits)[i]; * * Checked parser/tree parser on source for * Resin-2.0.5, jive-2.1.1, jdk 1.3.1, Lucene, antlr 2.7.2a4, - * and the 110k-line jGuru server source. + * and the 110k-line jGuru server source. * * Version 1.21 (October 17, 2003) - * Fixed lots of problems including: - * Ray Waldin: add typeDefinition to interfaceBlock in java.tree.g + * Fixed lots of problems including: + * Ray Waldin: add typeDefinition to interfaceBlock in java.tree.g * He found a problem/fix with floating point that start with 0 * Ray also fixed problem that (int.class) was not recognized. * Thorsten van Ellen noticed that \n are allowed incorrectly in strings. @@ -129,24 +129,24 @@ header { class JavaParser extends Parser; options { - k = 2; // two token lookahead - exportVocab=Java; // Call its vocabulary "Java" - codeGenMakeSwitchThreshold = 2; // Some optimizations - codeGenBitsetTestThreshold = 3; - defaultErrorHandler = false; // Don't generate parser error handlers - buildAST = true; - //buildAST = false; + k = 2; // two token lookahead + exportVocab=Java; // Call its vocabulary "Java" + codeGenMakeSwitchThreshold = 2; // Some optimizations + codeGenBitsetTestThreshold = 3; + defaultErrorHandler = false; // Don't generate parser error handlers + buildAST = true; + //buildAST = false; } tokens { - BLOCK; MODIFIERS; OBJBLOCK; SLIST; CTOR_DEF; METHOD_DEF; VARIABLE_DEF; - INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF; - PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE; - PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP; - POST_INC; POST_DEC; METHOD_CALL; EXPR; ARRAY_INIT; - IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION; - FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract"; - STRICTFP="strictfp"; SUPER_CTOR_CALL; CTOR_CALL; + BLOCK; MODIFIERS; OBJBLOCK; SLIST; CTOR_DEF; METHOD_DEF; VARIABLE_DEF; + INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF; + PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE; + PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP; + POST_INC; POST_DEC; METHOD_CALL; EXPR; ARRAY_INIT; + IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION; + FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract"; + STRICTFP="strictfp"; SUPER_CTOR_CALL; CTOR_CALL; } { @@ -181,250 +181,250 @@ tokens { // Compilation Unit: In Java, this is a single file. This is the start // rule for this parser compilationUnit - : // A compilation unit starts with an optional package definition - ( packageDefinition - | /* nothing */ - ) + : // A compilation unit starts with an optional package definition + ( packageDefinition + | /* nothing */ + ) - // Next we have a series of zero or more import statements - ( importDefinition )* + // Next we have a series of zero or more import statements + ( importDefinition )* - // Wrapping things up with any number of class or interface - // definitions - ( typeDefinition )* + // Wrapping things up with any number of class or interface + // definitions + ( typeDefinition )* - EOF! - ; + EOF! + ; // Package statement: "package" followed by an identifier. packageDefinition - options {defaultErrorHandler = true;} // let ANTLR handle errors - : p:"package"^ {#p.setType(PACKAGE_DEF);} identifier SEMI! - ; + options {defaultErrorHandler = true;} // let ANTLR handle errors + : p:"package"^ {#p.setType(PACKAGE_DEF);} identifier SEMI! + ; // Import statement: import followed by a package or class name importDefinition - options {defaultErrorHandler = true;} - : i:"import"^ {#i.setType(IMPORT);} identifierStar SEMI! - ; + options {defaultErrorHandler = true;} + : i:"import"^ {#i.setType(IMPORT);} identifierStar SEMI! + ; // A type definition in a file is either a class or interface definition. typeDefinition - options {defaultErrorHandler = true;} - : m:modifiers! - ( classDefinition[#m] - | interfaceDefinition[#m] - ) - | SEMI! - ; + options {defaultErrorHandler = true;} + : m:modifiers! + ( classDefinition[#m] + | interfaceDefinition[#m] + ) + | SEMI! + ; /** A declaration is the creation of a reference or primitive-type variable * Create a separate Type/Var tree for each var in the var list. */ declaration! - : m:modifiers t:typeSpec[false] v:variableDefinitions[#m,#t] - {#declaration = #v;} - ; + : m:modifiers t:typeSpec[false] v:variableDefinitions[#m,#t] + {#declaration = #v;} + ; // A type specification is a type name with possible brackets afterwards // (which would make it an array type). typeSpec[boolean addImagNode] - : classTypeSpec[addImagNode] - | builtInTypeSpec[addImagNode] - ; + : classTypeSpec[addImagNode] + | builtInTypeSpec[addImagNode] + ; // A class type specification is a class type with possible brackets afterwards // (which would make it an array type). classTypeSpec[boolean addImagNode] - : identifier (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* - { - if ( addImagNode ) { - #classTypeSpec = #(#[TYPE,"TYPE"], #classTypeSpec); - } - } - ; + : identifier (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* + { + if ( addImagNode ) { + #classTypeSpec = #(#[TYPE,"TYPE"], #classTypeSpec); + } + } + ; // A builtin type specification is a builtin type with possible brackets // afterwards (which would make it an array type). builtInTypeSpec[boolean addImagNode] - : builtInType (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* - { - if ( addImagNode ) { - #builtInTypeSpec = #(#[TYPE,"TYPE"], #builtInTypeSpec); - } - } - ; + : builtInType (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* + { + if ( addImagNode ) { + #builtInTypeSpec = #(#[TYPE,"TYPE"], #builtInTypeSpec); + } + } + ; // A type name. which is either a (possibly qualified) class name or // a primitive (builtin) type type - : identifier - | builtInType - ; + : identifier + | builtInType + ; // The primitive types. builtInType - : "void" - | "boolean" - | "byte" - | "char" - | "short" - | "int" - | "float" - | "long" - | "double" - ; + : "void" + | "boolean" + | "byte" + | "char" + | "short" + | "int" + | "float" + | "long" + | "double" + ; // A (possibly-qualified) java identifier. We start with the first IDENT // and expand its name by adding dots and following IDENTS identifier - : IDENT ( DOT^ IDENT )* - ; + : IDENT ( DOT^ IDENT )* + ; identifierStar - : IDENT - ( DOT^ IDENT )* - ( DOT^ STAR )? - ; + : IDENT + ( DOT^ IDENT )* + ( DOT^ STAR )? + ; // A list of zero or more modifiers. We could have used (modifier)* in // place of a call to modifiers, but I thought it was a good idea to keep // this rule separate so they can easily be collected in a Vector if // someone so desires modifiers - : ( modifier )* - {#modifiers = #([MODIFIERS, "MODIFIERS"], #modifiers);} - ; + : ( modifier )* + {#modifiers = #([MODIFIERS, "MODIFIERS"], #modifiers);} + ; // modifiers for Java classes, interfaces, class/instance vars and methods modifier - : "private" - | "public" - | "protected" - | "static" - | "transient" - | "final" - | "abstract" - | "native" - | "threadsafe" - | "synchronized" -// | "const" // reserved word, but not valid - | "volatile" - | "strictfp" - ; + : "private" + | "public" + | "protected" + | "static" + | "transient" + | "final" + | "abstract" + | "native" + | "threadsafe" + | "synchronized" +// | "const" // reserved word, but not valid + | "volatile" + | "strictfp" + ; // Definition of a Java class classDefinition![AST modifiers] - : "class" IDENT - // it _might_ have a superclass... - sc:superClassClause - // it might implement some interfaces... - ic:implementsClause - // now parse the body of the class - cb:classBlock - {#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"], - modifiers,IDENT,sc,ic,cb);} - ; + : "class" IDENT + // it _might_ have a superclass... + sc:superClassClause + // it might implement some interfaces... + ic:implementsClause + // now parse the body of the class + cb:classBlock + {#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"], + modifiers,IDENT,sc,ic,cb);} + ; superClassClause! - : ( "extends" id:identifier )? - {#superClassClause = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"],id);} - ; + : ( "extends" id:identifier )? + {#superClassClause = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"],id);} + ; // Definition of a Java Interface interfaceDefinition![AST modifiers] - : "interface" IDENT - // it might extend some other interfaces - ie:interfaceExtends - // now parse the body of the interface (looks like a class...) - cb:classBlock - {#interfaceDefinition = #(#[INTERFACE_DEF,"INTERFACE_DEF"], - modifiers,IDENT,ie,cb);} - ; + : "interface" IDENT + // it might extend some other interfaces + ie:interfaceExtends + // now parse the body of the interface (looks like a class...) + cb:classBlock + {#interfaceDefinition = #(#[INTERFACE_DEF,"INTERFACE_DEF"], + modifiers,IDENT,ie,cb);} + ; // This is the body of a class. You can have fields and extra semicolons, // That's about it (until you see what a field is...) classBlock - : LCURLY! { blockDepth++; } - ( field | SEMI! )* - RCURLY! { blockDepth--; } - {#classBlock = #([OBJBLOCK, "OBJBLOCK"], #classBlock);} - ; + : LCURLY! { blockDepth++; } + ( field | SEMI! )* + RCURLY! { blockDepth--; } + {#classBlock = #([OBJBLOCK, "OBJBLOCK"], #classBlock);} + ; // An interface can extend several other interfaces... interfaceExtends - : ( - e:"extends"! - identifier ( COMMA! identifier )* - )? - {#interfaceExtends = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"], - #interfaceExtends);} - ; + : ( + e:"extends"! + identifier ( COMMA! identifier )* + )? + {#interfaceExtends = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"], + #interfaceExtends);} + ; // A class can implement several interfaces... implementsClause - : ( - i:"implements"! identifier ( COMMA! identifier )* - )? - {#implementsClause = #(#[IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE"], - #implementsClause);} - ; + : ( + i:"implements"! identifier ( COMMA! identifier )* + )? + {#implementsClause = #(#[IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE"], + #implementsClause);} + ; // Now the various things that can be defined inside a class or interface... // Note that not all of these are really valid in an interface (constructors, // for example), and if this grammar were used for a compiler there would // need to be some semantic checks to make sure we're doing the right thing... field! - : // method, constructor, or variable declaration - mods:modifiers - ( h:ctorHead s:constructorBody // constructor - {#field = #(#[CTOR_DEF,"CTOR_DEF"], mods, h, s);} + : // method, constructor, or variable declaration + mods:modifiers + ( h:ctorHead s:constructorBody // constructor + {#field = #(#[CTOR_DEF,"CTOR_DEF"], mods, h, s);} - | cd:classDefinition[#mods] // inner class - {#field = #cd;} + | cd:classDefinition[#mods] // inner class + {#field = #cd;} - | id:interfaceDefinition[#mods] // inner interface - {#field = #id;} + | id:interfaceDefinition[#mods] // inner interface + {#field = #id;} - | t:typeSpec[false] // method or variable declaration(s) - ( fn:IDENT // the name of the method + | t:typeSpec[false] // method or variable declaration(s) + ( fn:IDENT // the name of the method - // parse the formal parameter declarations. - LPAREN! param:parameterDeclarationList RPAREN! + // parse the formal parameter declarations. + LPAREN! param:parameterDeclarationList RPAREN! - rt:declaratorBrackets[#t] + rt:declaratorBrackets[#t] - // get the list of exceptions that this method is - // declared to throw - (tc:throwsClause)? + // get the list of exceptions that this method is + // declared to throw + (tc:throwsClause)? - ( s2:compoundStatement | SEMI ) - {#field = #(#[METHOD_DEF,"METHOD_DEF"], - mods, - #(#[TYPE,"TYPE"],rt), - fn, - param, - tc, - s2); + ( s2:compoundStatement | SEMI ) + {#field = #(#[METHOD_DEF,"METHOD_DEF"], + mods, + #(#[TYPE,"TYPE"],rt), + fn, + param, + tc, + s2); if(blockDepth==1) { functionNames.add(fn.getText()); } } - | v:variableDefinitions[#mods,#t] SEMI -// {#field = #(#[VARIABLE_DEF,"VARIABLE_DEF"], v);} - {#field = #v;} - ) - ) + | v:variableDefinitions[#mods,#t] SEMI +// {#field = #(#[VARIABLE_DEF,"VARIABLE_DEF"], v);} + {#field = #v;} + ) + ) // "static { ... }" class initializer - | "static" s3:compoundStatement - {#field = #(#[STATIC_INIT,"STATIC_INIT"], s3);} + | "static" s3:compoundStatement + {#field = #(#[STATIC_INIT,"STATIC_INIT"], s3);} // "{ ... }" instance initializer - | s4:compoundStatement - {#field = #(#[INSTANCE_INIT,"INSTANCE_INIT"], s4);} - ; + | s4:compoundStatement + {#field = #(#[INSTANCE_INIT,"INSTANCE_INIT"], s4);} + ; constructorBody : lc:LCURLY^ {#lc.setType(SLIST); blockDepth++; } @@ -436,108 +436,108 @@ constructorBody /** Catch obvious constructor calls, but not the expr.super(...) calls */ explicitConstructorInvocation : "this"! lp1:LPAREN^ argList RPAREN! SEMI! - {#lp1.setType(CTOR_CALL);} + {#lp1.setType(CTOR_CALL);} | "super"! lp2:LPAREN^ argList RPAREN! SEMI! - {#lp2.setType(SUPER_CTOR_CALL);} + {#lp2.setType(SUPER_CTOR_CALL);} ; variableDefinitions[AST mods, AST t] - : variableDeclarator[getASTFactory().dupTree(mods), - getASTFactory().dupTree(t)] - ( COMMA! - variableDeclarator[getASTFactory().dupTree(mods), - getASTFactory().dupTree(t)] - )* - ; + : variableDeclarator[getASTFactory().dupTree(mods), + getASTFactory().dupTree(t)] + ( COMMA! + variableDeclarator[getASTFactory().dupTree(mods), + getASTFactory().dupTree(t)] + )* + ; /** Declaration of a variable. This can be a class/instance variable, * or a local variable in a method * It can also include possible initialization. */ variableDeclarator![AST mods, AST t] - : id:IDENT d:declaratorBrackets[t] v:varInitializer - {#variableDeclarator = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods, #(#[TYPE,"TYPE"],d), id, v); + : id:IDENT d:declaratorBrackets[t] v:varInitializer + {#variableDeclarator = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods, #(#[TYPE,"TYPE"],d), id, v); if(blockDepth==1) { enumNames.add(id.getText()); } } - ; + ; declaratorBrackets[AST typ] - : {#declaratorBrackets=typ;} - (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* - ; + : {#declaratorBrackets=typ;} + (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* + ; varInitializer - : ( ASSIGN^ initializer )? - ; + : ( ASSIGN^ initializer )? + ; // This is an initializer used to set up an array. arrayInitializer - : lc:LCURLY^ {#lc.setType(ARRAY_INIT); blockDepth++; } - ( initializer - ( - // CONFLICT: does a COMMA after an initializer start a new - // initializer or start the option ',' at end? - // ANTLR generates proper code by matching - // the comma as soon as possible. - options { - warnWhenFollowAmbig = false; - } - : - COMMA! initializer - )* - (COMMA!)? - )? - RCURLY! { blockDepth--; } - ; + : lc:LCURLY^ {#lc.setType(ARRAY_INIT); blockDepth++; } + ( initializer + ( + // CONFLICT: does a COMMA after an initializer start a new + // initializer or start the option ',' at end? + // ANTLR generates proper code by matching + // the comma as soon as possible. + options { + warnWhenFollowAmbig = false; + } + : + COMMA! initializer + )* + (COMMA!)? + )? + RCURLY! { blockDepth--; } + ; // The two "things" that can initialize an array element are an expression // and another (nested) array initializer. initializer - : expression - | arrayInitializer - ; + : expression + | arrayInitializer + ; // This is the header of a method. It includes the name and parameters // for the method. // This also watches for a list of exception classes in a "throws" clause. ctorHead - : IDENT // the name of the method + : IDENT // the name of the method - // parse the formal parameter declarations. - LPAREN! parameterDeclarationList RPAREN! + // parse the formal parameter declarations. + LPAREN! parameterDeclarationList RPAREN! - // get the list of exceptions that this method is declared to throw - (throwsClause)? - ; + // get the list of exceptions that this method is declared to throw + (throwsClause)? + ; // This is a list of exception classes that the method is declared to throw throwsClause - : "throws"^ identifier ( COMMA! identifier )* - ; + : "throws"^ identifier ( COMMA! identifier )* + ; // A list of formal parameters parameterDeclarationList - : ( parameterDeclaration ( COMMA! parameterDeclaration )* )? - {#parameterDeclarationList = #(#[PARAMETERS,"PARAMETERS"], - #parameterDeclarationList);} - ; + : ( parameterDeclaration ( COMMA! parameterDeclaration )* )? + {#parameterDeclarationList = #(#[PARAMETERS,"PARAMETERS"], + #parameterDeclarationList);} + ; // A formal parameter. parameterDeclaration! - : pm:parameterModifier t:typeSpec[false] id:IDENT - pd:declaratorBrackets[#t] - {#parameterDeclaration = #(#[PARAMETER_DEF,"PARAMETER_DEF"], - pm, #([TYPE,"TYPE"],pd), id);} - ; + : pm:parameterModifier t:typeSpec[false] id:IDENT + pd:declaratorBrackets[#t] + {#parameterDeclaration = #(#[PARAMETER_DEF,"PARAMETER_DEF"], + pm, #([TYPE,"TYPE"],pd), id);} + ; parameterModifier - : (f:"final")? - {#parameterModifier = #(#[MODIFIERS,"MODIFIERS"], f);} - ; + : (f:"final")? + {#parameterModifier = #(#[MODIFIERS,"MODIFIERS"], f);} + ; // Compound statement. This is used in many contexts: // Inside a class definition prefixed with "static": @@ -549,151 +549,151 @@ parameterModifier // it starts a new scope for variable definitions compoundStatement - : lc:LCURLY^ {#lc.setType(SLIST); blockDepth++; } - // include the (possibly-empty) list of statements - (statement)* - RCURLY! { blockDepth--; } - ; + : lc:LCURLY^ {#lc.setType(SLIST); blockDepth++; } + // include the (possibly-empty) list of statements + (statement)* + RCURLY! { blockDepth--; } + ; statement - // A list of statements in curly braces -- start a new scope! - : compoundStatement - - // declarations are ambiguous with "ID DOT" relative to expression - // statements. Must backtrack to be sure. Could use a semantic - // predicate to test symbol table to see what the type was coming - // up, but that's pretty hard without a symbol table ;) - | (declaration)=> declaration SEMI! - - // An expression statement. This could be a method call, - // assignment statement, or any other expression evaluated for - // side-effects. - | expression SEMI! - - // class definition - | m:modifiers! classDefinition[#m] - - // Attach a label to the front of a statement - | IDENT c:COLON^ {#c.setType(LABELED_STAT);} statement - - // If-else statement - | "if"^ LPAREN! expression RPAREN! statement - ( - // CONFLICT: the old "dangling-else" problem... - // ANTLR generates proper code matching - // as soon as possible. Hush warning. - options { - warnWhenFollowAmbig = false; - } - : - "else"! statement - )? - - // For statement - | "for"^ - LPAREN! - forInit SEMI! // initializer - forCond SEMI! // condition test - forIter // updater - RPAREN! - statement // statement to loop over - - // While statement - | "while"^ LPAREN! expression RPAREN! statement - - // do-while statement - | "do"^ statement "while"! LPAREN! expression RPAREN! SEMI! - - // get out of a loop (or switch) - | "break"^ (IDENT)? SEMI! - - // do next iteration of a loop - | "continue"^ (IDENT)? SEMI! - - // Return an expression - | "return"^ (expression)? SEMI! - - // switch/case statement - | "switch"^ LPAREN! expression RPAREN! LCURLY! { blockDepth++; } - ( casesGroup )* - RCURLY! { blockDepth--; } - - // exception try-catch block - | tryBlock - - // throw an exception - | "throw"^ expression SEMI! - - // synchronize a statement - | "synchronized"^ LPAREN! expression RPAREN! compoundStatement - - // asserts (uncomment if you want 1.4 compatibility) - // | "assert"^ expression ( COLON! expression )? SEMI! - - // empty statement - | s:SEMI {#s.setType(EMPTY_STAT);} - ; + // A list of statements in curly braces -- start a new scope! + : compoundStatement + + // declarations are ambiguous with "ID DOT" relative to expression + // statements. Must backtrack to be sure. Could use a semantic + // predicate to test symbol table to see what the type was coming + // up, but that's pretty hard without a symbol table ;) + | (declaration)=> declaration SEMI! + + // An expression statement. This could be a method call, + // assignment statement, or any other expression evaluated for + // side-effects. + | expression SEMI! + + // class definition + | m:modifiers! classDefinition[#m] + + // Attach a label to the front of a statement + | IDENT c:COLON^ {#c.setType(LABELED_STAT);} statement + + // If-else statement + | "if"^ LPAREN! expression RPAREN! statement + ( + // CONFLICT: the old "dangling-else" problem... + // ANTLR generates proper code matching + // as soon as possible. Hush warning. + options { + warnWhenFollowAmbig = false; + } + : + "else"! statement + )? + + // For statement + | "for"^ + LPAREN! + forInit SEMI! // initializer + forCond SEMI! // condition test + forIter // updater + RPAREN! + statement // statement to loop over + + // While statement + | "while"^ LPAREN! expression RPAREN! statement + + // do-while statement + | "do"^ statement "while"! LPAREN! expression RPAREN! SEMI! + + // get out of a loop (or switch) + | "break"^ (IDENT)? SEMI! + + // do next iteration of a loop + | "continue"^ (IDENT)? SEMI! + + // Return an expression + | "return"^ (expression)? SEMI! + + // switch/case statement + | "switch"^ LPAREN! expression RPAREN! LCURLY! { blockDepth++; } + ( casesGroup )* + RCURLY! { blockDepth--; } + + // exception try-catch block + | tryBlock + + // throw an exception + | "throw"^ expression SEMI! + + // synchronize a statement + | "synchronized"^ LPAREN! expression RPAREN! compoundStatement + + // asserts (uncomment if you want 1.4 compatibility) + // | "assert"^ expression ( COLON! expression )? SEMI! + + // empty statement + | s:SEMI {#s.setType(EMPTY_STAT);} + ; casesGroup - : ( // CONFLICT: to which case group do the statements bind? - // ANTLR generates proper code: it groups the - // many "case"/"default" labels together then - // follows them with the statements - options { - greedy = true; - } - : - aCase - )+ - caseSList - {#casesGroup = #([CASE_GROUP, "CASE_GROUP"], #casesGroup);} - ; + : ( // CONFLICT: to which case group do the statements bind? + // ANTLR generates proper code: it groups the + // many "case"/"default" labels together then + // follows them with the statements + options { + greedy = true; + } + : + aCase + )+ + caseSList + {#casesGroup = #([CASE_GROUP, "CASE_GROUP"], #casesGroup);} + ; aCase - : ("case"^ expression | "default") COLON! - ; + : ("case"^ expression | "default") COLON! + ; caseSList - : (statement)* - {#caseSList = #(#[SLIST,"SLIST"],#caseSList);} - ; + : (statement)* + {#caseSList = #(#[SLIST,"SLIST"],#caseSList);} + ; // The initializer for a for loop forInit - // if it looks like a declaration, it is - : ( (declaration)=> declaration - // otherwise it could be an expression list... - | expressionList - )? - {#forInit = #(#[FOR_INIT,"FOR_INIT"],#forInit);} - ; + // if it looks like a declaration, it is + : ( (declaration)=> declaration + // otherwise it could be an expression list... + | expressionList + )? + {#forInit = #(#[FOR_INIT,"FOR_INIT"],#forInit);} + ; forCond - : (expression)? - {#forCond = #(#[FOR_CONDITION,"FOR_CONDITION"],#forCond);} - ; + : (expression)? + {#forCond = #(#[FOR_CONDITION,"FOR_CONDITION"],#forCond);} + ; forIter - : (expressionList)? - {#forIter = #(#[FOR_ITERATOR,"FOR_ITERATOR"],#forIter);} - ; + : (expressionList)? + {#forIter = #(#[FOR_ITERATOR,"FOR_ITERATOR"],#forIter);} + ; // an exception handler try/catch block tryBlock - : "try"^ compoundStatement - (handler)* - ( finallyClause )? - ; + : "try"^ compoundStatement + (handler)* + ( finallyClause )? + ; finallyClause - : "finally"^ compoundStatement - ; + : "finally"^ compoundStatement + ; // an exception handler handler - : "catch"^ LPAREN! parameterDeclaration RPAREN! compoundStatement - ; + : "catch"^ LPAREN! parameterDeclaration RPAREN! compoundStatement + ; // expressions @@ -732,22 +732,22 @@ handler // the mother of all expressions expression - : assignmentExpression - {#expression = #(#[EXPR,"EXPR"],#expression);} - ; + : assignmentExpression + {#expression = #(#[EXPR,"EXPR"],#expression);} + ; // This is a list of expressions. expressionList - : expression (COMMA! expression)* - {#expressionList = #(#[ELIST,"ELIST"], expressionList);} - ; + : expression (COMMA! expression)* + {#expressionList = #(#[ELIST,"ELIST"], expressionList);} + ; // assignment expression (level 13) assignmentExpression - : conditionalExpression - ( ( ASSIGN^ + : conditionalExpression + ( ( ASSIGN^ | PLUS_ASSIGN^ | MINUS_ASSIGN^ | STAR_ASSIGN^ @@ -760,99 +760,99 @@ assignmentExpression | BXOR_ASSIGN^ | BOR_ASSIGN^ ) - assignmentExpression - )? - ; + assignmentExpression + )? + ; // conditional test (level 12) conditionalExpression - : logicalOrExpression - ( QUESTION^ assignmentExpression COLON! conditionalExpression )? - ; + : logicalOrExpression + ( QUESTION^ assignmentExpression COLON! conditionalExpression )? + ; // logical or (||) (level 11) logicalOrExpression - : logicalAndExpression (LOR^ logicalAndExpression)* - ; + : logicalAndExpression (LOR^ logicalAndExpression)* + ; // logical and (&&) (level 10) logicalAndExpression - : inclusiveOrExpression (LAND^ inclusiveOrExpression)* - ; + : inclusiveOrExpression (LAND^ inclusiveOrExpression)* + ; // bitwise or non-short-circuiting or (|) (level 9) inclusiveOrExpression - : exclusiveOrExpression (BOR^ exclusiveOrExpression)* - ; + : exclusiveOrExpression (BOR^ exclusiveOrExpression)* + ; // exclusive or (^) (level 8) exclusiveOrExpression - : andExpression (BXOR^ andExpression)* - ; + : andExpression (BXOR^ andExpression)* + ; // bitwise or non-short-circuiting and (&) (level 7) andExpression - : equalityExpression (BAND^ equalityExpression)* - ; + : equalityExpression (BAND^ equalityExpression)* + ; // equality/inequality (==/!=) (level 6) equalityExpression - : relationalExpression ((NOT_EQUAL^ | EQUAL^) relationalExpression)* - ; + : relationalExpression ((NOT_EQUAL^ | EQUAL^) relationalExpression)* + ; // boolean relational expressions (level 5) relationalExpression - : shiftExpression - ( ( ( LT^ - | GT^ - | LE^ - | GE^ - ) - shiftExpression - )* - | "instanceof"^ typeSpec[true] - ) - ; + : shiftExpression + ( ( ( LT^ + | GT^ + | LE^ + | GE^ + ) + shiftExpression + )* + | "instanceof"^ typeSpec[true] + ) + ; // bit shift expressions (level 4) shiftExpression - : additiveExpression ((SL^ | SR^ | BSR^) additiveExpression)* - ; + : additiveExpression ((SL^ | SR^ | BSR^) additiveExpression)* + ; // binary addition/subtraction (level 3) additiveExpression - : multiplicativeExpression ((PLUS^ | MINUS^) multiplicativeExpression)* - ; + : multiplicativeExpression ((PLUS^ | MINUS^) multiplicativeExpression)* + ; // multiplication/division/modulo (level 2) multiplicativeExpression - : unaryExpression ((STAR^ | DIV^ | MOD^ ) unaryExpression)* - ; + : unaryExpression ((STAR^ | DIV^ | MOD^ ) unaryExpression)* + ; unaryExpression - : INC^ unaryExpression - | DEC^ unaryExpression - | MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression - | PLUS^ {#PLUS.setType(UNARY_PLUS);} unaryExpression - | unaryExpressionNotPlusMinus - ; + : INC^ unaryExpression + | DEC^ unaryExpression + | MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression + | PLUS^ {#PLUS.setType(UNARY_PLUS);} unaryExpression + | unaryExpressionNotPlusMinus + ; unaryExpressionNotPlusMinus - : BNOT^ unaryExpression - | LNOT^ unaryExpression + : BNOT^ unaryExpression + | LNOT^ unaryExpression - // use predicate to skip cases like: (int.class) + // use predicate to skip cases like: (int.class) | (LPAREN builtInTypeSpec[true] RPAREN) => lpb:LPAREN^ {#lpb.setType(TYPECAST);} builtInTypeSpec[true] RPAREN! unaryExpression @@ -860,108 +860,108 @@ unaryExpressionNotPlusMinus // Have to backtrack to see if operator follows. If no operator // follows, it's a typecast. No semantic checking needed to parse. // if it _looks_ like a cast, it _is_ a cast; else it's a "(expr)" - | (LPAREN classTypeSpec[true] RPAREN unaryExpressionNotPlusMinus)=> + | (LPAREN classTypeSpec[true] RPAREN unaryExpressionNotPlusMinus)=> lp:LPAREN^ {#lp.setType(TYPECAST);} classTypeSpec[true] RPAREN! unaryExpressionNotPlusMinus - | postfixExpression - ; + | postfixExpression + ; // qualified names, array expressions, method invocation, post inc/dec postfixExpression - : + : /* "this"! lp1:LPAREN^ argList RPAREN! - {#lp1.setType(CTOR_CALL);} + {#lp1.setType(CTOR_CALL);} | "super"! lp2:LPAREN^ argList RPAREN! - {#lp2.setType(SUPER_CTOR_CALL);} + {#lp2.setType(SUPER_CTOR_CALL);} | */ primaryExpression - ( + ( /* options { - // the use of postfixExpression in SUPER_CTOR_CALL adds DOT - // to the lookahead set, and gives loads of false non-det - // warnings. - // shut them off. - generateAmbigWarnings=false; - } - : */ + // the use of postfixExpression in SUPER_CTOR_CALL adds DOT + // to the lookahead set, and gives loads of false non-det + // warnings. + // shut them off. + generateAmbigWarnings=false; + } + : */ DOT^ IDENT - ( lp:LPAREN^ {#lp.setType(METHOD_CALL);} - argList - RPAREN! - )? - | DOT^ "this" + ( lp:LPAREN^ {#lp.setType(METHOD_CALL);} + argList + RPAREN! + )? + | DOT^ "this" - | DOT^ "super" + | DOT^ "super" ( // (new Outer()).super() (create enclosing instance) lp3:LPAREN^ argList RPAREN! {#lp3.setType(SUPER_CTOR_CALL);} - | DOT^ IDENT - ( lps:LPAREN^ {#lps.setType(METHOD_CALL);} + | DOT^ IDENT + ( lps:LPAREN^ {#lps.setType(METHOD_CALL);} argList RPAREN! )? ) - | DOT^ newExpression - | lb:LBRACK^ {#lb.setType(INDEX_OP);} expression RBRACK! - )* + | DOT^ newExpression + | lb:LBRACK^ {#lb.setType(INDEX_OP);} expression RBRACK! + )* - ( // possibly add on a post-increment or post-decrement. + ( // possibly add on a post-increment or post-decrement. // allows INC/DEC on too much, but semantics can check - in:INC^ {#in.setType(POST_INC);} - | de:DEC^ {#de.setType(POST_DEC);} - )? - ; + in:INC^ {#in.setType(POST_INC);} + | de:DEC^ {#de.setType(POST_DEC);} + )? + ; // the basic element of an expression primaryExpression - : identPrimary ( options {greedy=true;} : DOT^ "class" )? + : identPrimary ( options {greedy=true;} : DOT^ "class" )? | constant - | "true" - | "false" - | "null" + | "true" + | "false" + | "null" | newExpression - | "this" - | "super" - | LPAREN! assignmentExpression RPAREN! - // look for int.class and int[].class - | builtInType - ( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )* - DOT^ "class" - ; + | "this" + | "super" + | LPAREN! assignmentExpression RPAREN! + // look for int.class and int[].class + | builtInType + ( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )* + DOT^ "class" + ; /** Match a, a.b.c refs, a.b.c(...) refs, a.b.c[], a.b.c[].class, * and a.b.c.class refs. Also this(...) and super(...). Match * this or super. */ identPrimary - : IDENT - ( + : IDENT + ( options { - // .ident could match here or in postfixExpression. - // We do want to match here. Turn off warning. - greedy=true; - } - : DOT^ IDENT - )* - ( + // .ident could match here or in postfixExpression. + // We do want to match here. Turn off warning. + greedy=true; + } + : DOT^ IDENT + )* + ( options { - // ARRAY_DECLARATOR here conflicts with INDEX_OP in - // postfixExpression on LBRACK RBRACK. - // We want to match [] here, so greedy. This overcomes + // ARRAY_DECLARATOR here conflicts with INDEX_OP in + // postfixExpression on LBRACK RBRACK. + // We want to match [] here, so greedy. This overcomes // limitation of linear approximate lookahead. - greedy=true; - } - : ( lp:LPAREN^ {#lp.setType(METHOD_CALL);} argList RPAREN! ) - | ( options {greedy=true;} : + greedy=true; + } + : ( lp:LPAREN^ {#lp.setType(METHOD_CALL);} argList RPAREN! ) + | ( options {greedy=true;} : lbc:LBRACK^ {#lbc.setType(ARRAY_DECLARATOR);} RBRACK! )+ - )? + )? ; /** object instantiation. @@ -1014,53 +1014,53 @@ identPrimary * */ newExpression - : "new"^ type - ( LPAREN! argList RPAREN! (classBlock)? - - //java 1.1 - // Note: This will allow bad constructs like - // new int[4][][3] {exp,exp}. - // There needs to be a semantic check here... - // to make sure: - // a) [ expr ] and [ ] are not mixed - // b) [ expr ] and an init are not used together - - | newArrayDeclarator (arrayInitializer)? - ) - ; + : "new"^ type + ( LPAREN! argList RPAREN! (classBlock)? + + //java 1.1 + // Note: This will allow bad constructs like + // new int[4][][3] {exp,exp}. + // There needs to be a semantic check here... + // to make sure: + // a) [ expr ] and [ ] are not mixed + // b) [ expr ] and an init are not used together + + | newArrayDeclarator (arrayInitializer)? + ) + ; argList - : ( expressionList - | /*nothing*/ - {#argList = #[ELIST,"ELIST"];} - ) - ; + : ( expressionList + | /*nothing*/ + {#argList = #[ELIST,"ELIST"];} + ) + ; newArrayDeclarator - : ( - // CONFLICT: - // newExpression is a primaryExpression which can be - // followed by an array index reference. This is ok, - // as the generated code will stay in this loop as - // long as it sees an LBRACK (proper behavior) - options { - warnWhenFollowAmbig = false; - } - : - lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} - (expression)? - RBRACK! - )+ - ; + : ( + // CONFLICT: + // newExpression is a primaryExpression which can be + // followed by an array index reference. This is ok, + // as the generated code will stay in this loop as + // long as it sees an LBRACK (proper behavior) + options { + warnWhenFollowAmbig = false; + } + : + lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} + (expression)? + RBRACK! + )+ + ; constant - : NUM_INT - | CHAR_LITERAL - | STRING_LITERAL - | NUM_FLOAT - | NUM_LONG - | NUM_DOUBLE - ; + : NUM_INT + | CHAR_LITERAL + | STRING_LITERAL + | NUM_FLOAT + | NUM_LONG + | NUM_DOUBLE + ; //---------------------------------------------------------------------------- // The Java scanner @@ -1068,121 +1068,121 @@ constant class JavaLexer extends Lexer; options { - exportVocab=Java; // call the vocabulary "Java" - testLiterals=false; // don't automatically test for literals - k=4; // four characters of lookahead - charVocabulary='\u0003'..'\u7FFE'; - // without inlining some bitset tests, couldn't do unicode; - // I need to make ANTLR generate smaller bitsets; see - // bottom of JavaLexer.java - codeGenBitsetTestThreshold=20; + exportVocab=Java; // call the vocabulary "Java" + testLiterals=false; // don't automatically test for literals + k=4; // four characters of lookahead + charVocabulary='\u0003'..'\u7FFE'; + // without inlining some bitset tests, couldn't do unicode; + // I need to make ANTLR generate smaller bitsets; see + // bottom of JavaLexer.java + codeGenBitsetTestThreshold=20; } // OPERATORS -QUESTION : '?' ; -LPAREN : '(' ; -RPAREN : ')' ; -LBRACK : '[' ; -RBRACK : ']' ; -LCURLY : '{' ; -RCURLY : '}' ; -COLON : ':' ; -COMMA : ',' ; -//DOT : '.' ; -ASSIGN : '=' ; -EQUAL : "==" ; -LNOT : '!' ; -BNOT : '~' ; -NOT_EQUAL : "!=" ; -DIV : '/' ; -DIV_ASSIGN : "/=" ; -PLUS : '+' ; -PLUS_ASSIGN : "+=" ; -INC : "++" ; -MINUS : '-' ; -MINUS_ASSIGN : "-=" ; -DEC : "--" ; -STAR : '*' ; -STAR_ASSIGN : "*=" ; -MOD : '%' ; -MOD_ASSIGN : "%=" ; -SR : ">>" ; -SR_ASSIGN : ">>=" ; -BSR : ">>>" ; -BSR_ASSIGN : ">>>=" ; -GE : ">=" ; -GT : ">" ; -SL : "<<" ; -SL_ASSIGN : "<<=" ; -LE : "<=" ; -LT : '<' ; -BXOR : '^' ; -BXOR_ASSIGN : "^=" ; -BOR : '|' ; -BOR_ASSIGN : "|=" ; -LOR : "||" ; -BAND : '&' ; -BAND_ASSIGN : "&=" ; -LAND : "&&" ; -SEMI : ';' ; +QUESTION : '?' ; +LPAREN : '(' ; +RPAREN : ')' ; +LBRACK : '[' ; +RBRACK : ']' ; +LCURLY : '{' ; +RCURLY : '}' ; +COLON : ':' ; +COMMA : ',' ; +//DOT : '.' ; +ASSIGN : '=' ; +EQUAL : "==" ; +LNOT : '!' ; +BNOT : '~' ; +NOT_EQUAL : "!=" ; +DIV : '/' ; +DIV_ASSIGN : "/=" ; +PLUS : '+' ; +PLUS_ASSIGN : "+=" ; +INC : "++" ; +MINUS : '-' ; +MINUS_ASSIGN : "-=" ; +DEC : "--" ; +STAR : '*' ; +STAR_ASSIGN : "*=" ; +MOD : '%' ; +MOD_ASSIGN : "%=" ; +SR : ">>" ; +SR_ASSIGN : ">>=" ; +BSR : ">>>" ; +BSR_ASSIGN : ">>>=" ; +GE : ">=" ; +GT : ">" ; +SL : "<<" ; +SL_ASSIGN : "<<=" ; +LE : "<=" ; +LT : '<' ; +BXOR : '^' ; +BXOR_ASSIGN : "^=" ; +BOR : '|' ; +BOR_ASSIGN : "|=" ; +LOR : "||" ; +BAND : '&' ; +BAND_ASSIGN : "&=" ; +LAND : "&&" ; +SEMI : ';' ; // Whitespace -- ignored -WS : ( ' ' - | '\t' - | '\f' - // handle newlines - | ( options {generateAmbigWarnings=false;} - : "\r\n" // Evil DOS - | '\r' // Macintosh - | '\n' // Unix (the right way) - ) - { newline(); } - )+ - { _ttype = Token.SKIP; } - ; +WS : ( ' ' + | '\t' + | '\f' + // handle newlines + | ( options {generateAmbigWarnings=false;} + : "\r\n" // Evil DOS + | '\r' // Macintosh + | '\n' // Unix (the right way) + ) + { newline(); } + )+ + { _ttype = Token.SKIP; } + ; // Single-line comments SL_COMMENT - : "//" - (~('\n'|'\r'))* ('\n'|'\r'('\n')?)? - {$setType(Token.SKIP); newline();} - ; + : "//" + (~('\n'|'\r'))* ('\n'|'\r'('\n')?)? + {$setType(Token.SKIP); newline();} + ; // multiple-line comments ML_COMMENT - : "/*" - ( /* '\r' '\n' can be matched in one alternative or by matching - '\r' in one iteration and '\n' in another. I am trying to - handle any flavor of newline that comes in, but the language - that allows both "\r\n" and "\r" and "\n" to all be valid - newline is ambiguous. Consequently, the resulting grammar - must be ambiguous. I'm shutting this warning off. - */ - options { - generateAmbigWarnings=false; - } - : - { LA(2)!='/' }? '*' - | '\r' '\n' {newline();} - | '\r' {newline();} - | '\n' {newline();} - | ~('*'|'\n'|'\r') - )* - "*/" - {$setType(Token.SKIP);} - ; + : "/*" + ( /* '\r' '\n' can be matched in one alternative or by matching + '\r' in one iteration and '\n' in another. I am trying to + handle any flavor of newline that comes in, but the language + that allows both "\r\n" and "\r" and "\n" to all be valid + newline is ambiguous. Consequently, the resulting grammar + must be ambiguous. I'm shutting this warning off. + */ + options { + generateAmbigWarnings=false; + } + : + { LA(2)!='/' }? '*' + | '\r' '\n' {newline();} + | '\r' {newline();} + | '\n' {newline();} + | ~('*'|'\n'|'\r') + )* + "*/" + {$setType(Token.SKIP);} + ; // character literals CHAR_LITERAL - : '\'' ( ESC | ~('\''|'\n'|'\r'|'\\') ) '\'' - ; + : '\'' ( ESC | ~('\''|'\n'|'\r'|'\\') ) '\'' + ; // string literals STRING_LITERAL - : '"' (ESC|~('"'|'\\'|'\n'|'\r'))* '"' - ; + : '"' (ESC|~('"'|'\\'|'\n'|'\r'))* '"' + ; // escape sequence -- note that this is protected; it can only be called @@ -1195,121 +1195,132 @@ STRING_LITERAL // the FOLLOW ambig warnings. protected ESC - : '\\' - ( 'n' - | 'r' - | 't' - | 'b' - | 'f' - | '"' - | '\'' - | '\\' - | ('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT - | '0'..'3' - ( - options { - warnWhenFollowAmbig = false; - } - : '0'..'7' - ( - options { - warnWhenFollowAmbig = false; - } - : '0'..'7' - )? - )? - | '4'..'7' - ( - options { - warnWhenFollowAmbig = false; - } - : '0'..'7' - )? - ) - ; + : '\\' + ( 'n' + | 'r' + | 't' + | 'b' + | 'f' + | '"' + | '\'' + | '\\' + | ('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT + | '0'..'3' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + )? + | '4'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + ) + ; // hexadecimal digit (again, note it's protected!) protected HEX_DIGIT - : ('0'..'9'|'A'..'F'|'a'..'f') - ; + : ('0'..'9'|'A'..'F'|'a'..'f') + ; // an identifier. Note that testLiterals is set to true! This means // that after we match the rule, we look in the literals table to see // if it's a literal or really an identifer IDENT - options {testLiterals=true;} - : ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')* - ; + options {testLiterals=true;} + : ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')* + ; // a numeric literal NUM_INT - {boolean isDecimal=false; Token t=null;} + {boolean isDecimal=false, isHexadecimal=false; Token t=null;} : '.' {_ttype = DOT;} - ( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})? + ( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})? { - if (t != null && t.getText().toUpperCase().indexOf('F')>=0) { - _ttype = NUM_FLOAT; - } - else { - _ttype = NUM_DOUBLE; // assume double - } - } + if (t != null && t.getText().toUpperCase().indexOf('F')>=0) { + _ttype = NUM_FLOAT; + } + else { + _ttype = NUM_DOUBLE; // assume double + } + } )? - | ( '0' {isDecimal = true;} // special case for just '0' - ( ('x'|'X') - ( // hex - // the 'e'|'E' and float suffix stuff look - // like hex digits, hence the (...)+ doesn't - // know when to stop: ambig. ANTLR resolves - // it correctly by matching immediately. It - // is therefor ok to hush warning. - options { - warnWhenFollowAmbig=false; - } - : HEX_DIGIT - )+ - - | //float or double with leading zero - (('0'..'9')+ ('.'|EXPONENT|FLOAT_SUFFIX)) => ('0'..'9')+ - - | ('0'..'7')+ // octal - )? - | ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal - ) - ( ('l'|'L') { _ttype = NUM_LONG; } - - // only check to see if it's a float if looks like decimal so far - | {isDecimal}? - ( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})? - | EXPONENT (f3:FLOAT_SUFFIX {t=f3;})? - | f4:FLOAT_SUFFIX {t=f4;} - ) - { - if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) { - _ttype = NUM_FLOAT; - } - else { - _ttype = NUM_DOUBLE; // assume double - } - } + | ( '0' {isDecimal = true;} // special case for just '0' + ( ('x'|'X') { isHexadecimal=true; } // hex + ( + // the 'e'|'E' and float suffix stuff look + // like hex digits, hence the (...)+ doesn't + // know when to stop: ambig. ANTLR resolves + // it correctly by matching immediately. It + // is therefor ok to hush warning. + options { + warnWhenFollowAmbig=false; + } + : HEX_DIGIT + )+ + + | //float or double with leading zero + (('0'..'9')+ ('.'|EXPONENT|FLOAT_SUFFIX)) => ('0'..'9')+ + + | ('0'..'7')+ // octal + )? + | ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal + ) + ( + ('l'|'L') { _ttype = NUM_LONG; } + + // check to see if it's a hexadecimal w/ binary exponent float if looks like hexadecimal so far + | {isHexadecimal}? + ( '.' ( options { warnWhenFollowAmbig=false; } : HEX_DIGIT )* )? + ('p'|'P') ('+'|'-')? ('0'..'9')+ (f5:FLOAT_SUFFIX {t=f5;})? + { + if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) { + _ttype = NUM_FLOAT; + } else { + _ttype = NUM_DOUBLE; // assume double + } + } + // check to see if it's a float if looks like decimal so far + | {isDecimal}? + ( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})? + | EXPONENT (f3:FLOAT_SUFFIX {t=f3;})? + | f4:FLOAT_SUFFIX {t=f4;} + ) + { + if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) { + _ttype = NUM_FLOAT; + } else { + _ttype = NUM_DOUBLE; // assume double + } + } )? - ; + ; // a couple protected methods to assist in matching floating point numbers protected EXPONENT - : ('e'|'E') ('+'|'-')? ('0'..'9')+ - ; + : ('e'|'E') (PLUS|MINUS)? ('0'..'9')+ + ; protected FLOAT_SUFFIX - : 'f'|'F'|'d'|'D' - ; + : 'f'|'F'|'d'|'D' + ; diff --git a/src/java/com/jogamp/common/ExceptionUtils.java b/src/java/com/jogamp/common/ExceptionUtils.java index c848a99..386f42b 100644 --- a/src/java/com/jogamp/common/ExceptionUtils.java +++ b/src/java/com/jogamp/common/ExceptionUtils.java @@ -58,21 +58,97 @@ public class ExceptionUtils { } /** - * Dumps a {@link Throwable} in a decorating message including the current thread name, + * Interface allowing {@link Throwable} specializations to provide their custom stack trace presentation. + * @since 2.3.2 + */ + public static interface CustomStackTrace { + /** + * Prints this {@link Throwable} as a cause to the output {@link PrintStream} {@code s}, + * not iterating over all inner causes! + * @param s output stream + * @param causeStr the cause title + * @param causeIdx the cause index over all causes known by caller + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + void printCauseStack(final PrintStream s, final String causeStr, final int causeIdx, final int stackDepth); + /** + * Custom {@code printStackTrace} method, similar to {@link Throwable#printStackTrace(PrintStream, int, int)}. + * @param s output stream + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + */ + void printStackTrace(final PrintStream s, final int causeDepth, final int stackDepth); + } + + /** + * Prints the given {@link Throwable} cause to the output {@link PrintStream} {@code s}. + * @param s output stream + * @param causeStr the cause title + * @param cause the {@link Throwable} cause for output + * @param causeIdx the cause index over all causes known by caller + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + public static int printCause(final PrintStream s, final String causeStr, Throwable cause, final int causeIdx, final int causeDepth, final int stackDepth) { + int i=causeIdx; + for(; null != cause && ( -1 == causeDepth || i < causeDepth ); cause = cause.getCause()) { + if( cause instanceof CustomStackTrace ) { + ((CustomStackTrace)cause).printCauseStack(s, causeStr, i, stackDepth); + } else { + s.println(causeStr+"["+i+"] by "+cause.getClass().getSimpleName()+": "+cause.getMessage()+" on thread "+Thread.currentThread().getName()); + dumpStack(s, cause.getStackTrace(), 0, stackDepth); + } + i++; + } + return i; + } + + /** + * Prints the given {@link Throwable} to the output {@link PrintStream} {@code s}. + * @param s output stream + * @param t the {@link Throwable} for output + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + public static void printStackTrace(final PrintStream s, final Throwable t, final int causeDepth, final int stackDepth) { + if( t instanceof CustomStackTrace ) { + ((CustomStackTrace)t).printStackTrace(s, causeDepth, stackDepth); + } else { + s.println(t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName()); + dumpStack(s, t.getStackTrace(), 0, stackDepth); + printCause(s, "Caused", t.getCause(), 0, causeDepth, stackDepth); + } + } + + /** + * Dumps a {@link Throwable} to {@link System.err} in a decorating message including the current thread name, * and its {@link #dumpStack(PrintStream, StackTraceElement[], int, int) stack trace}. * <p> * Implementation will iterate through all {@link Throwable#getCause() causes}. * </p> + * @param additionalDescr additional text placed before the {@link Throwable} details. + * @param t the {@link Throwable} for output */ public static void dumpThrowable(final String additionalDescr, final Throwable t) { - System.err.println("Caught "+additionalDescr+" "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName()); - dumpStack(System.err, t.getStackTrace(), 0, -1); - int causeDepth = 1; - for( Throwable cause = t.getCause(); null != cause; cause = cause.getCause() ) { - System.err.println("Caused["+causeDepth+"] by "+cause.getClass().getSimpleName()+": "+cause.getMessage()+" on thread "+Thread.currentThread().getName()); - dumpStack(System.err, cause.getStackTrace(), 0, -1); - causeDepth++; - } + dumpThrowable(additionalDescr, t, -1, -1); + } + /** + * Dumps a {@link Throwable} to {@link System.err} in a decorating message including the current thread name, + * and its {@link #dumpStack(PrintStream, StackTraceElement[], int, int) stack trace}. + * <p> + * Implementation will iterate through all {@link Throwable#getCause() causes}. + * </p> + * @param additionalDescr additional text placed before the {@link Throwable} details. + * @param t the {@link Throwable} for output + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + public static void dumpThrowable(final String additionalDescr, final Throwable t, final int causeDepth, final int stackDepth) { + System.err.print("Caught "+additionalDescr+" "); + printStackTrace(System.err, t, causeDepth, stackDepth); } - } diff --git a/src/java/com/jogamp/common/JogampRuntimeException.java b/src/java/com/jogamp/common/JogampRuntimeException.java index d33d498..524bb93 100644 --- a/src/java/com/jogamp/common/JogampRuntimeException.java +++ b/src/java/com/jogamp/common/JogampRuntimeException.java @@ -28,9 +28,10 @@ package com.jogamp.common; -/** A generic exception for Jogamp errors used throughout the binding - as a substitute for {@link RuntimeException}. */ - +/** + * A generic <i>unchecked exception</i> for Jogamp errors used throughout the binding + * as a substitute for {@link RuntimeException}. + */ @SuppressWarnings("serial") public class JogampRuntimeException extends RuntimeException { /** Constructs a JogampRuntimeException object. */ diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java index 9b1865f..3ba8dff 100644 --- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java +++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java @@ -61,7 +61,18 @@ import jogamp.common.Debug; import jogamp.common.os.PlatformPropsImpl; public class JNILibLoaderBase { - public static final boolean DEBUG = Debug.debug("JNILibLoader"); + public static final boolean DEBUG; + protected static final boolean PERF; + + static { + Debug.initSingleton(); + DEBUG = Debug.debug("JNILibLoader"); + PERF = DEBUG || PropertyAccess.isPropertyDefined("jogamp.debug.JNILibLoader.Perf", true); + } + + private static final Object perfSync = new Object(); + private static long perfTotal = 0; + private static long perfCount = 0; public interface LoaderAction { /** @@ -177,6 +188,7 @@ public class JNILibLoaderBase { msg.append(")"); System.err.println(msg.toString()); } + final long t0 = PERF ? System.currentTimeMillis() : 0; // 'Platform.currentTimeMillis()' not yet available! boolean ok = false; @@ -195,28 +207,9 @@ public class JNILibLoaderBase { if (DEBUG) { System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: nativeLibraryPath: %s%n", nativeLibraryPath); } - final ClassLoader cl = classFromJavaJar.getClassLoader(); - final URL nativeLibraryURI = cl.getResource(nativeLibraryPath); - if (null != nativeLibraryURI) { - // We probably have one big-fat jar file, containing java classes - // and all native platform libraries under 'natives/os.and.arch'! - final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(jarBasename) ); - try { - if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) { - ok = true; - if (DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: fat: %s -> %s%n", jarBasename, nativeJarURI); - } - } - } catch(final Exception e) { - if(DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); - e.printStackTrace(); - } - } - } - if (!ok) { - // We assume one slim native jar file per 'os.and.arch'! + { + // Attempt-1 a 'one slim native jar file' per 'os.and.arch' layout + // with native platform libraries under 'natives/os.and.arch'! final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(nativeJarBasename) ); if (DEBUG) { @@ -224,7 +217,7 @@ public class JNILibLoaderBase { } try { - ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, null /* nativeLibraryPath */); + ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath); } catch(final Exception e) { if(DEBUG) { System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); @@ -233,40 +226,75 @@ public class JNILibLoaderBase { } } if (!ok) { - // Attempt to find via ClassLoader and Native-Jar-Tag, - // assuming one slim native jar file per 'os.and.arch'! - final String moduleName; + final ClassLoader cl = classFromJavaJar.getClassLoader(); { - final String packageName = classFromJavaJar.getPackage().getName(); - final int idx = packageName.lastIndexOf('.'); - if( 0 <= idx ) { - moduleName = packageName.substring(idx+1); - } else { - moduleName = packageName; + // Attempt-2 a 'one big-fat jar file' layout, containing java classes + // and all native platform libraries under 'natives/os.and.arch' per platform! + final URL nativeLibraryURI = cl.getResource(nativeLibraryPath); + if (null != nativeLibraryURI) { + final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(jarBasename) ); + try { + if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) { + ok = true; + if (DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: fat: %s -> %s%n", jarBasename, nativeJarURI); + } + } + } catch(final Exception e) { + if(DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); + e.printStackTrace(); + } + } } } - final String os_and_arch_dot = PlatformPropsImpl.os_and_arch.replace('-', '.'); - final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG"; // TODO: sync with gluegen-cpptasks-base.xml - try { - if(DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: Locating module %s, os.and.arch %s: %s%n", - moduleName, os_and_arch_dot, nativeJarTagClassName); - } - final Uri nativeJarTagClassJarURI = JarUtil.getJarUri(nativeJarTagClassName, cl); - if (DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: %s -> %s%n", nativeJarTagClassName, nativeJarTagClassJarURI); + if (!ok) { + // Attempt-3 to find via ClassLoader and Native-Jar-Tag, + // assuming one slim native jar file per 'os.and.arch' + // and native platform libraries under 'natives/os.and.arch'! + final String moduleName; + { + final String packageName = classFromJavaJar.getPackage().getName(); + final int idx = packageName.lastIndexOf('.'); + if( 0 <= idx ) { + moduleName = packageName.substring(idx+1); + } else { + moduleName = packageName; + } } - ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarTagClassJarURI, null /* nativeLibraryPath */); - } catch (final Exception e ) { - if(DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); - e.printStackTrace(); + final String os_and_arch_dot = PlatformPropsImpl.os_and_arch.replace('-', '.'); + final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG"; // TODO: sync with gluegen-cpptasks-base.xml + try { + if(DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: Locating module %s, os.and.arch %s: %s%n", + moduleName, os_and_arch_dot, nativeJarTagClassName); + } + final Uri nativeJarTagClassJarURI = JarUtil.getJarUri(nativeJarTagClassName, cl); + if (DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: %s -> %s%n", nativeJarTagClassName, nativeJarTagClassJarURI); + } + ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarTagClassJarURI, nativeLibraryPath); + } catch (final Exception e ) { + if(DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); + e.printStackTrace(); + } } } } - if (DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ok: %b%n", ok); + if (DEBUG || PERF) { + final long tNow = System.currentTimeMillis() - t0; + final long tTotal, tCount; + synchronized(perfSync) { + tCount = perfCount+1; + tTotal = perfTotal + tNow; + perfTotal = tTotal; + perfCount = tCount; + } + final double tAvrg = tTotal / (double)tCount; + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl.X: %s / %s -> ok: %b; duration: now %d ms, total %d ms (count %d, avrg %.3f ms)%n", + jarBasename, nativeJarBasename, ok, tNow, tTotal, tCount, tAvrg); } return ok; } @@ -585,7 +613,7 @@ public class JNILibLoaderBase { if(DEBUG) { System.err.println("ERROR (retry w/ enumLibPath) - "+ex1.getMessage()); } - final List<String> possiblePaths = NativeLibrary.enumerateLibraryPaths(libraryName, libraryName, libraryName, true, cl); + final List<String> possiblePaths = NativeLibrary.enumerateLibraryPaths(libraryName, libraryName, libraryName, cl); // Iterate down these and see which one if any we can actually find. for (final Iterator<String> iter = possiblePaths.iterator(); 0 == mode && iter.hasNext(); ) { final String path = iter.next(); diff --git a/src/java/com/jogamp/common/net/AssetURLContext.java b/src/java/com/jogamp/common/net/AssetURLContext.java index af90c01..8462a41 100644 --- a/src/java/com/jogamp/common/net/AssetURLContext.java +++ b/src/java/com/jogamp/common/net/AssetURLContext.java @@ -164,7 +164,7 @@ public abstract class AssetURLContext implements PiggybackURLContext { url = new URL(path); conn = open(url); type = null != conn ? 1 : -1; - } catch(final MalformedURLException e1) { if(DEBUG) { System.err.println("ERR(0): "+e1.getMessage()); } } + } catch(final MalformedURLException e1) { if(DEBUG) { System.err.println("FAIL(1): "+e1.getMessage()); } } if(null == conn && null != cl) { // lookup via ClassLoader .. cleanup leading '/' @@ -189,7 +189,7 @@ public abstract class AssetURLContext implements PiggybackURLContext { conn = open(url); type = null != conn ? 3 : -1; } - } catch (final Throwable e) { if(DEBUG) { System.err.println("ERR(1): "+e.getMessage()); } } + } catch (final Throwable e) { if(DEBUG) { System.err.println("FAIL(3): "+e.getMessage()); } } } if(DEBUG) { @@ -209,7 +209,7 @@ public abstract class AssetURLContext implements PiggybackURLContext { final URLConnection c = url.openConnection(); c.connect(); // redundant return c; - } catch (final IOException ioe) { if(DEBUG) { System.err.println("ERR: "+ioe.getMessage()); } } + } catch (final IOException ioe) { if(DEBUG) { System.err.println("FAIL(2): "+ioe.getMessage()); } } return null; } diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java index 6bafba2..bca90bf 100644 --- a/src/java/com/jogamp/common/net/Uri.java +++ b/src/java/com/jogamp/common/net/Uri.java @@ -1232,7 +1232,15 @@ public class Uri { /** Returns true, if this instance is a {@code file} {@code scheme}, otherwise false. */ public final boolean isFileScheme() { - return FILE_SCHEME.equals( scheme.get() ); + return null != scheme && FILE_SCHEME.equals( scheme.get() ); + } + + /** + * Returns true, if this instance is a {@code jar} {@code scheme}, otherwise false. + * @since 2.3.2 + */ + public final boolean isJarScheme() { + return null != scheme && JAR_SCHEME.equals( scheme.get() ); } /** @@ -1386,7 +1394,7 @@ public class Uri { if( !emptyString(schemeSpecificPart) ) { final StringBuilder sb = new StringBuilder(); - if( scheme.equals(JAR_SCHEME) ) { + if( isJarScheme() ) { final int idx = schemeSpecificPart.lastIndexOf(JAR_SCHEME_SEPARATOR); if (0 > idx) { throw new URISyntaxException(input.get(), "missing jar separator"); diff --git a/src/java/com/jogamp/common/nio/Buffers.java b/src/java/com/jogamp/common/nio/Buffers.java index aae2be8..fb23627 100644 --- a/src/java/com/jogamp/common/nio/Buffers.java +++ b/src/java/com/jogamp/common/nio/Buffers.java @@ -39,6 +39,7 @@ */ package com.jogamp.common.nio; +import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -48,9 +49,14 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.ValueConv; +import jogamp.common.Debug; + /** * Utility methods allowing easy {@link java.nio.Buffer} manipulations. * @@ -60,6 +66,11 @@ import com.jogamp.common.util.ValueConv; */ public class Buffers { + static final boolean DEBUG; + static { + DEBUG = Debug.debug("Buffers"); + } + public static final int SIZEOF_BYTE = 1; public static final int SIZEOF_SHORT = 2; public static final int SIZEOF_CHAR = 2; @@ -1150,4 +1161,64 @@ public class Buffers { return sb; } + /** + * Access to NIO {@link sun.misc.Cleaner}, allowing caller to deterministically clean a given {@link sun.nio.ch.DirectBuffer}. + */ + public static class Cleaner { + private static final Method mbbCleaner; + private static final Method cClean; + private static final boolean hasCleaner; + /** OK to be lazy on thread synchronization, just for early out **/ + private static volatile boolean cleanerError; + static { + final Method[] _mbbCleaner = { null }; + final Method[] _cClean = { null }; + if( AccessController.doPrivileged(new PrivilegedAction<Boolean>() { + @Override + public Boolean run() { + try { + _mbbCleaner[0] = ReflectionUtil.getMethod("sun.nio.ch.DirectBuffer", "cleaner", null, Buffers.class.getClassLoader()); + _mbbCleaner[0].setAccessible(true); + _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean"); + _cClean[0].setAccessible(true); + return Boolean.TRUE; + } catch(final Throwable t) { + if( DEBUG ) { + System.err.println("Caught "+t.getMessage()); + t.printStackTrace(); + } + return Boolean.FALSE; + } } } ).booleanValue() ) { + mbbCleaner = _mbbCleaner[0]; + cClean = _cClean[0]; + hasCleaner = null != mbbCleaner && null != cClean; + } else { + mbbCleaner = null; + cClean = null; + hasCleaner = false; + } + cleanerError = !hasCleaner; + } + /** + * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer}, + * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method. + * @return {@code true} if successful, otherwise {@code false}. + */ + public static boolean clean(final Buffer b) { + if( !hasCleaner || cleanerError || !b.isDirect() ) { + return false; + } + try { + cClean.invoke(mbbCleaner.invoke(b)); + return true; + } catch(final Throwable t) { + cleanerError = true; + if( DEBUG ) { + System.err.println("Caught "+t.getMessage()); + t.printStackTrace(); + } + return false; + } + } + } } diff --git a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java index f8d5857..6a56d6e 100644 --- a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java +++ b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java @@ -33,13 +33,10 @@ import java.io.OutputStream; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; -import java.security.AccessController; -import java.security.PrivilegedAction; import jogamp.common.Debug; @@ -163,10 +160,6 @@ public class MappedByteBufferInputStream extends InputStream { private int refCount; - private Method mbbCleaner; - private Method cClean; - private boolean cleanerInit; - private boolean hasCleaner; private CacheMode cmode; private int sliceIdx; @@ -191,10 +184,12 @@ public class MappedByteBufferInputStream extends InputStream { } } long fcSz = 0, pos = 0, rem = 0; - try { - fcSz = fc.size(); - } catch (final IOException e) { - e.printStackTrace(); + if( fc.isOpen() ) { + try { + fcSz = fc.size(); + } catch (final IOException e) { + e.printStackTrace(); + } } if( 0 < refCount ) { try { @@ -229,14 +224,16 @@ public class MappedByteBufferInputStream extends InputStream { notifyLengthChange( totalSize ); this.refCount = 1; - this.cleanerInit = false; - this.hasCleaner = false; this.cmode = cmode; this.sliceIdx = currSliceIdx; this.mark = -1; currentSlice().position(0); + + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("CTOR", System.err); + } } /** @@ -330,6 +327,9 @@ public class MappedByteBufferInputStream extends InputStream { } } } + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("Close", System.err); + } } final FileChannel.MapMode getMapMode() { return mmode; } @@ -441,10 +441,9 @@ public class MappedByteBufferInputStream extends InputStream { } position2( Math.min(prePosition, newTotalSize) ); // -> clipped position (set currSlice and re-map/-pos buffer) } - /* if( DEBUG ) { - System.err.println("notifyLengthChange.X: "+slices[currSlice]); - dbgDump("notifyLengthChange.X:", System.err); - } */ + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("NotifyLengthChange", System.err); + } } /** @@ -551,6 +550,21 @@ public class MappedByteBufferInputStream extends InputStream { } } + /** + * Releases the mapped {@link ByteBuffer} slices. + * @throws IOException if a buffer slice operation failed. + */ + public final synchronized void flushSlices() throws IOException { + if( null != slices ) { + for(int i=0; i<sliceCount; i++) { + flushSlice(i, synchronous); + } + } + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("FlushSlices", System.err); + } + } + synchronized void syncSlice(final ByteBuffer s) throws IOException { syncSlice(s, synchronous); } @@ -630,58 +644,16 @@ public class MappedByteBufferInputStream extends InputStream { } } private synchronized boolean cleanBuffer(final ByteBuffer mbb, final boolean syncBuffer) throws IOException { - if( !cleanerInit ) { - initCleaner(mbb); - } syncSlice(mbb, syncBuffer); if( !mbb.isDirect() ) { return false; } - boolean res = false; - if ( hasCleaner ) { - try { - cClean.invoke(mbbCleaner.invoke(mbb)); - res = true; - } catch(final Throwable t) { - hasCleaner = false; - if( DEBUG ) { - System.err.println("Caught "+t.getMessage()); - t.printStackTrace(); - } - } - } - if( !res && CacheMode.FLUSH_PRE_HARD == cmode ) { + if( !Buffers.Cleaner.clean(mbb) && CacheMode.FLUSH_PRE_HARD == cmode ) { cmode = CacheMode.FLUSH_PRE_SOFT; + return false; + } else { + return true; } - return res; - } - private synchronized void initCleaner(final ByteBuffer bb) { - final Method[] _mbbCleaner = { null }; - final Method[] _cClean = { null }; - AccessController.doPrivileged(new PrivilegedAction<Object>() { - @Override - public Object run() { - try { - _mbbCleaner[0] = bb.getClass().getMethod("cleaner"); - _mbbCleaner[0].setAccessible(true); - _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean"); - _cClean[0].setAccessible(true); - } catch(final Throwable t) { - if( DEBUG ) { - System.err.println("Caught "+t.getMessage()); - t.printStackTrace(); - } - } - return null; - } } ); - mbbCleaner = _mbbCleaner[0]; - cClean = _cClean[0]; - final boolean res = null != mbbCleaner && null != cClean; - if( DEBUG ) { - System.err.println("initCleaner: Has cleaner: "+res+", mbbCleaner "+mbbCleaner+", cClean "+cClean); - } - hasCleaner = res; - cleanerInit = true; } /** diff --git a/src/java/com/jogamp/common/nio/StructAccessor.java b/src/java/com/jogamp/common/nio/StructAccessor.java index af7b6d1..8ae0c29 100644 --- a/src/java/com/jogamp/common/nio/StructAccessor.java +++ b/src/java/com/jogamp/common/nio/StructAccessor.java @@ -83,6 +83,16 @@ public class StructAccessor { bb.put(byteOffset, v); } + /** Retrieves the boolean at the specified byteOffset. */ + public final boolean getBooleanAt(final int byteOffset) { + return (byte)0 != bb.get(byteOffset); + } + + /** Puts a boolean at the specified byteOffset. */ + public final void setBooleanAt(final int byteOffset, final boolean v) { + bb.put(byteOffset, v?(byte)1:(byte)0); + } + /** Retrieves the char at the specified byteOffset. */ public final char getCharAt(final int byteOffset) { return bb.getChar(byteOffset); @@ -213,6 +223,19 @@ public class StructAccessor { return v; } + public final void setBooleansAt(int byteOffset, final boolean[] v) { + for (int i = 0; i < v.length; i++) { + bb.put(byteOffset++, v[i]?(byte)1:(byte)0); + } + } + + public final boolean[] getBooleansAt(int byteOffset, final boolean[] v) { + for (int i = 0; i < v.length; i++) { + v[i] = (byte)0 != bb.get(byteOffset++); + } + return v; + } + public final void setCharsAt(int byteOffset, final char[] v) { for (int i = 0; i < v.length; i++, byteOffset+=2) { bb.putChar(byteOffset, v[i]); diff --git a/src/java/com/jogamp/common/os/DynamicLibraryBundle.java b/src/java/com/jogamp/common/os/DynamicLibraryBundle.java index c578565..a3d6198 100644 --- a/src/java/com/jogamp/common/os/DynamicLibraryBundle.java +++ b/src/java/com/jogamp/common/os/DynamicLibraryBundle.java @@ -189,7 +189,10 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { * @see DynamicLibraryBundleInfo#getToolLibNames() */ public final boolean isToolLibComplete() { - return toolGetProcAddressComplete && null != dynLinkGlobal && getToolLibNumber() == getToolLibLoadedNumber(); + final int toolLibNumber = getToolLibNumber(); + return toolGetProcAddressComplete && + ( 0 == toolLibNumber || null != dynLinkGlobal ) && + toolLibNumber == getToolLibLoadedNumber(); } public final boolean isToolLibLoaded() { @@ -246,9 +249,12 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { return aptr; } - protected static final NativeLibrary loadFirstAvailable(final List<String> libNames, final ClassLoader loader, final boolean global) throws SecurityException { + protected static final NativeLibrary loadFirstAvailable(final List<String> libNames, + final boolean searchSystemPath, + final boolean searchSystemPathFirst, + final ClassLoader loader, final boolean global) throws SecurityException { for (int i=0; i < libNames.size(); i++) { - final NativeLibrary lib = NativeLibrary.open(libNames.get(i), loader, global); + final NativeLibrary lib = NativeLibrary.open(libNames.get(i), searchSystemPath, searchSystemPathFirst, loader, global); if (lib != null) { return lib; } @@ -266,7 +272,10 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { for (i=0; i < toolLibNames.size(); i++) { final List<String> libNames = toolLibNames.get(i); if( null != libNames && libNames.size() > 0 ) { - lib = loadFirstAvailable(libNames, cl, info.shallLinkGlobal()); + lib = loadFirstAvailable(libNames, + info.searchToolLibInSystemPath(), + info.searchToolLibSystemPathFirst(), + cl, info.shallLinkGlobal()); if ( null == lib ) { if(DEBUG) { System.err.println("Unable to load any Tool library of: "+libNames); @@ -358,7 +367,7 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { final long addr = info.toolGetProcAddress(toolGetProcAddressHandle, funcName); if(DEBUG_LOOKUP) { if(0!=addr) { - System.err.println("Lookup-Tool: <"+funcName+"> 0x"+Long.toHexString(addr)); + System.err.println("Lookup-Tool: <"+funcName+"> 0x"+Long.toHexString(addr)+", via tool 0x"+Long.toHexString(toolGetProcAddressHandle)); } } return addr; diff --git a/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java b/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java index 7be5f25..01068b4 100644 --- a/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java +++ b/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java @@ -37,6 +37,21 @@ public interface DynamicLibraryBundleInfo { public static final boolean DEBUG = DynamicLibraryBundle.DEBUG; /** + * Returns {@code true} if tool libraries shall be searched in the system path <i>(default)</i>, otherwise {@code false}. + * @since 2.4.0 + */ + public boolean searchToolLibInSystemPath(); + + /** + * Returns {@code true} if system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last. + * <p> + * If {@link #searchToolLibInSystemPath()} is {@code false} the return value is ignored. + * </p> + * @since 2.4.0 + */ + public boolean searchToolLibSystemPathFirst(); + + /** * If a {@link SecurityManager} is installed, user needs link permissions * for the named libraries. * diff --git a/src/java/com/jogamp/common/os/NativeLibrary.java b/src/java/com/jogamp/common/os/NativeLibrary.java index 747f92d..2ba2581 100644 --- a/src/java/com/jogamp/common/os/NativeLibrary.java +++ b/src/java/com/jogamp/common/os/NativeLibrary.java @@ -138,32 +138,47 @@ public final class NativeLibrary implements DynamicLookupHelper { } /** Opens the given native library, assuming it has the same base - name on all platforms, looking first in the system's search - path, and in the context of the specified ClassLoader, which is - used to help find the library in the case of e.g. Java Web Start. - * @throws SecurityException if user is not granted access for the named library. - */ - public static final NativeLibrary open(final String libName, final ClassLoader loader) throws SecurityException { - return open(libName, libName, libName, true, loader, true); - } - - /** Opens the given native library, assuming it has the same base - name on all platforms, looking first in the system's search - path, and in the context of the specified ClassLoader, which is - used to help find the library in the case of e.g. Java Web Start. + name on all platforms. + <p> + The {@code searchSystemPath} argument changes the behavior to + either use the default system path or not at all. + </p> + <p> + Assuming {@code searchSystemPath} is {@code true}, + the {@code searchSystemPathFirst} argument changes the behavior to first + search the default system path rather than searching it last. + </p> + * @param libName library name, with or without prefix and suffix + * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}. + * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last. + * if {@code searchSystemPath} is {@code false} this argument is ignored. + * @param loader {@link ClassLoader} to locate the library + * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process. + * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded. * @throws SecurityException if user is not granted access for the named library. + * @since 2.4.0 */ - public static final NativeLibrary open(final String libName, final ClassLoader loader, final boolean global) throws SecurityException { - return open(libName, libName, libName, true, loader, global); + public static final NativeLibrary open(final String libName, + final boolean searchSystemPath, + final boolean searchSystemPathFirst, + final ClassLoader loader, final boolean global) throws SecurityException { + return open(libName, libName, libName, searchSystemPath, searchSystemPathFirst, loader, global); } /** Opens the given native library, assuming it has the given base names (no "lib" prefix or ".dll/.so/.dylib" suffix) on the Windows, Unix and Mac OS X platforms, respectively, and in the context of the specified ClassLoader, which is used to help find - the library in the case of e.g. Java Web Start. The - searchSystemPathFirst argument changes the behavior to first + the library in the case of e.g. Java Web Start. + <p> + The {@code searchSystemPath} argument changes the behavior to + either use the default system path or not at all. + </p> + <p> + Assuming {@code searchSystemPath} is {@code true}, + the {@code searchSystemPathFirst} argument changes the behavior to first search the default system path rather than searching it last. + </p> Note that we do not currently handle DSO versioning on Unix. Experience with JOAL and OpenAL has shown that it is extremely problematic to rely on a specific .so version (for one thing, @@ -171,28 +186,27 @@ public final class NativeLibrary implements DynamicLookupHelper { ending in .so, for example .so.0), and in general if this dynamic loading facility is used correctly the version number will be irrelevant. + * @param windowsLibName windows library name, with or without prefix and suffix + * @param unixLibName unix library name, with or without prefix and suffix + * @param macOSXLibName mac-osx library name, with or without prefix and suffix + * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}. + * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last. + * if {@code searchSystemPath} is {@code false} this argument is ignored. + * @param loader {@link ClassLoader} to locate the library + * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process. + * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded. * @throws SecurityException if user is not granted access for the named library. */ public static final NativeLibrary open(final String windowsLibName, final String unixLibName, final String macOSXLibName, - final boolean searchSystemPathFirst, - final ClassLoader loader) throws SecurityException { - return open(windowsLibName, unixLibName, macOSXLibName, searchSystemPathFirst, loader, true); - } - - /** - * @throws SecurityException if user is not granted access for the named library. - */ - public static final NativeLibrary open(final String windowsLibName, - final String unixLibName, - final String macOSXLibName, + final boolean searchSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader, final boolean global) throws SecurityException { final List<String> possiblePaths = enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName, - searchSystemPathFirst, + searchSystemPath, searchSystemPathFirst, loader); Platform.initSingleton(); // loads native gluegen-rt library @@ -367,12 +381,33 @@ public final class NativeLibrary implements DynamicLookupHelper { /** Given the base library names (no prefixes/suffixes) for the various platforms, enumerate the possible locations and names of - the indicated native library on the system. */ + the indicated native library on the system not using the system path. */ + public static final List<String> enumerateLibraryPaths(final String windowsLibName, + final String unixLibName, + final String macOSXLibName, + final ClassLoader loader) { + return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName, + false /* searchSystemPath */, false /* searchSystemPathFirst */, + loader); + } + /** Given the base library names (no prefixes/suffixes) for the + various platforms, enumerate the possible locations and names of + the indicated native library on the system using the system path. */ public static final List<String> enumerateLibraryPaths(final String windowsLibName, final String unixLibName, final String macOSXLibName, final boolean searchSystemPathFirst, final ClassLoader loader) { + return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName, + true /* searchSystemPath */, searchSystemPathFirst, + loader); + } + private static final List<String> enumerateLibraryPaths(final String windowsLibName, + final String unixLibName, + final String macOSXLibName, + final boolean searchSystemPath, + final boolean searchSystemPathFirst, + final ClassLoader loader) { final List<String> paths = new ArrayList<String>(); final String libName = selectName(windowsLibName, unixLibName, macOSXLibName); if (libName == null) { @@ -388,11 +423,18 @@ public final class NativeLibrary implements DynamicLookupHelper { final String[] baseNames = buildNames(libName); - if (searchSystemPathFirst) { - // Add just the library names to use the OS's search algorithm - for (int i = 0; i < baseNames.length; i++) { - paths.add(baseNames[i]); - } + if( searchSystemPath && searchSystemPathFirst ) { + // Add just the library names to use the OS's search algorithm + for (int i = 0; i < baseNames.length; i++) { + paths.add(baseNames[i]); + } + // Add probable Mac OS X-specific paths + if ( isOSX ) { + // Add historical location + addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + // Add current location + addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + } } // The idea to ask the ClassLoader to find the library is borrowed @@ -412,24 +454,25 @@ public final class NativeLibrary implements DynamicLookupHelper { if(null != usrPath) { count++; } - final String sysPath = System.getProperty("sun.boot.library.path"); - if(null != sysPath) { - count++; + final String sysPath; + if( searchSystemPath ) { + sysPath = System.getProperty("sun.boot.library.path"); + if(null != sysPath) { + count++; + } + } else { + sysPath = null; } final String[] res = new String[count]; int i=0; - if (searchSystemPathFirst) { - if(null != sysPath) { - res[i++] = sysPath; - } + if( null != sysPath && searchSystemPathFirst ) { + res[i++] = sysPath; } if(null != usrPath) { res[i++] = usrPath; } - if (!searchSystemPathFirst) { - if(null != sysPath) { - res[i++] = sysPath; - } + if( null != sysPath && !searchSystemPathFirst ) { + res[i++] = sysPath; } return res; } @@ -453,19 +496,22 @@ public final class NativeLibrary implements DynamicLookupHelper { }); addPaths(userDir, baseNames, paths); - if (!searchSystemPathFirst) { - // Add just the library names to use the OS's search algorithm - for (int i = 0; i < baseNames.length; i++) { - paths.add(baseNames[i]); - } - } + // Add current working directory + natives/os-arch/ + library names + // to handle Bug 1145 cc1 using an unpacked fat-jar + addPaths(userDir+File.separator+"natives"+File.separator+PlatformPropsImpl.os_and_arch+File.separator, baseNames, paths); - // Add probable Mac OS X-specific paths - if ( isOSX ) { - // Add historical location - addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths); - // Add current location - addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + if( searchSystemPath && !searchSystemPathFirst ) { + // Add just the library names to use the OS's search algorithm + for (int i = 0; i < baseNames.length; i++) { + paths.add(baseNames[i]); + } + // Add probable Mac OS X-specific paths + if ( isOSX ) { + // Add historical location + addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + // Add current location + addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + } } return paths; diff --git a/src/java/com/jogamp/common/util/ArrayHashMap.java b/src/java/com/jogamp/common/util/ArrayHashMap.java new file mode 100644 index 0000000..35a484b --- /dev/null +++ b/src/java/com/jogamp/common/util/ArrayHashMap.java @@ -0,0 +1,305 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * {@link HashMap} implementation backed by an {@link ArrayList} to preserve order of values. + * + * Implementation properties are: + * <ul> + * <li> Unique elements utilizing {@link java.lang.Object#hashCode()} for O(1) operations, see below.</li> + * <li> Java 1.5 compatible</li> + * </ul> + * + * O(1) operations: + * <ul> + * <li> put new key-value-pair(s) </li> + * <li> test for containment </li> + * <li> trying to remove non existent elements </li> + * </ul> + * + * O(n) operations: + * <ul> + * <li> put existing key-value-pair(s) </li> + * <li> removing existing elements</li> + * </ul> + * + * For thread safety, the application shall decorate access to instances via + * {@link com.jogamp.common.util.locks.RecursiveLock}. + * +*/ +public class ArrayHashMap<K, V> + implements Cloneable, Map<K, V> +{ + /** + * Default load factor: {@value} + */ + public static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** + * The default initial capacity: {@value} + */ + public static final int DEFAULT_INITIAL_CAPACITY = 16; + + private final HashMap<K,V> map; // key -> object + private final ArrayList<V> data; // list of objects + private final boolean supportNullValue; + + /** + * + * @param supportNullValue Use {@code true} for default behavior, i.e. {@code null} can be a valid value. + * Use {@code false} if {@code null} is not a valid value, + * here {@link #put(Object, Object)} and {@link #remove(Object)} will be optimized. + * @param initialCapacity use {@link #DEFAULT_INITIAL_CAPACITY} for default + * @param loadFactor use {@link #DEFAULT_LOAD_FACTOR} for default + * @see #supportsNullValue() + */ + public ArrayHashMap(final boolean supportNullValue, final int initialCapacity, final float loadFactor) { + this.map = new HashMap<K,V>(initialCapacity, loadFactor); + this.data = new ArrayList<V>(initialCapacity); + this.supportNullValue = supportNullValue; + } + + /** + * @return a shallow copy of this ArrayHashMap, elements are not copied. + */ + public ArrayHashMap(final ArrayHashMap<K, V> o) { + map = new HashMap<K, V>(o.map); + data = new ArrayList<V>(o.data); + supportNullValue = o.supportNullValue; + } + + /** + * Returns {@code true} for default behavior, i.e. {@code null} can be a valid value. + * <p> + * Returns {@code false} if {@code null} is not a valid value, + * here {@link #put(Object, Object)} and {@link #remove(Object)} are optimized operations. + * </p> + * @see #ArrayHashMap(boolean, int, float) + */ + public final boolean supportsNullValue() { return supportNullValue; } + + // + // Cloneable + // + + /** + * Implementation uses {@link #ArrayHashMap(ArrayHashMap)}. + * @return a shallow copy of this ArrayHashMap, elements are not copied. + */ + @Override + public final Object clone() { + return new ArrayHashMap<K, V>(this); + } + + /** + * Returns this object ordered ArrayList. Use w/ care, it's not a copy. + * @see #toArrayList() + */ + public final ArrayList<V> getData() { return data; } + + /** + * @return a shallow copy of this ArrayHashMap's ArrayList, elements are not copied. + * @see #getData() + */ + public final ArrayList<V> toArrayList() { + return new ArrayList<V>(data); + } + + /** Returns this object hash map. Use w/ care, it's not a copy. */ + public final HashMap<K,V> getMap() { return map; } + + @Override + public final String toString() { return data.toString(); } + + // + // Map + // + + @Override + public final void clear() { + data.clear(); + map.clear(); + } + + @Override + public Set<K> keySet() { + return map.keySet(); + } + + /** + * {@inheritDoc} + * <p> + * See {@link #getData()} and {@link #toArrayList()}. + * </p> + * @see #getData() + * @see #toArrayList() + */ + @Override + public Collection<V> values() { + return map.values(); + } + + @Override + public Set<java.util.Map.Entry<K, V>> entrySet() { + return map.entrySet(); + } + + @Override + public final V get(final Object key) { + return map.get(key); + } + + /** + * {@inheritDoc} + * <p> + * This is an O(1) operation, in case the key does not exist, + * otherwise O(n). + * </p> + * @throws NullPointerException if {@code value} is {@code null} but {@link #supportsNullValue()} == {@code false} + */ + @Override + public final V put(final K key, final V value) throws NullPointerException { + final V oldValue; + if( supportNullValue ) { + // slow path + final boolean exists = map.containsKey(key); + if(!exists) { + // !exists + if( null != ( oldValue = map.put(key, value) ) ) { + // slips a valid null .. + throw new InternalError("Already existing, but checked before: "+key+" -> "+oldValue); + } + } else { + // exists + oldValue = map.put(key, value); + if( !data.remove(oldValue) ) { + throw new InternalError("Already existing, but not in list: "+oldValue); + } + } + } else { + checkNullValue(value); + // fast path + if( null != ( oldValue = map.put(key, value) ) ) { + // exists + if( !data.remove(oldValue) ) { + throw new InternalError("Already existing, but not in list: "+oldValue); + } + } + } + if(!data.add(value)) { + throw new InternalError("Couldn't add value to list: "+value); + } + return oldValue; + } + + @Override + public void putAll(final Map<? extends K, ? extends V> m) { + for (final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) { + final Map.Entry<? extends K, ? extends V> e = i.next(); + put(e.getKey(), e.getValue()); + } + } + + /** + * {@inheritDoc} + * <p> + * This is an O(1) operation, in case the key does not exist, + * otherwise O(n). + * </p> + */ + @Override + public final V remove(final Object key) { + if( supportNullValue ) { + if( map.containsKey(key) ) { + // exists + final V oldValue = map.remove(key); + if ( !data.remove(oldValue) ) { + throw new InternalError("Couldn't remove prev mapped pair: "+key+" -> "+oldValue); + } + return oldValue; + } + } else { + final V oldValue; + if ( null != (oldValue = map.remove(key) ) ) { + // exists + if ( !data.remove(oldValue) ) { + throw new InternalError("Couldn't remove prev mapped pair: "+key+" -> "+oldValue); + } + } + return oldValue; + } + return null; + } + + @Override + public final boolean containsKey(final Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(final Object value) { + return map.containsValue(value); + } + + @Override + public final boolean equals(final Object arrayHashMap) { + if ( !(arrayHashMap instanceof ArrayHashMap) ) { + return false; + } + return map.equals(((ArrayHashMap<?,?>)arrayHashMap).map); + } + + @Override + public final int hashCode() { + return map.hashCode(); + } + + @Override + public final boolean isEmpty() { + return data.isEmpty(); + } + + @Override + public final int size() { + return data.size(); + } + + private static final void checkNullValue(final Object value) throws NullPointerException { + if( null == value ) { + throw new NullPointerException("Null value not supported"); + } + } +} diff --git a/src/java/com/jogamp/common/util/ArrayHashSet.java b/src/java/com/jogamp/common/util/ArrayHashSet.java index 34e84c4..c0ac2fa 100644 --- a/src/java/com/jogamp/common/util/ArrayHashSet.java +++ b/src/java/com/jogamp/common/util/ArrayHashSet.java @@ -68,23 +68,52 @@ import java.util.ListIterator; public class ArrayHashSet<E> implements Cloneable, Collection<E>, List<E> { + /** + * Default load factor: {@value} + */ + public static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** + * The default initial capacity: {@value} + */ + public static final int DEFAULT_INITIAL_CAPACITY = 16; + private final HashMap<E,E> map; // object -> object private final ArrayList<E> data; // list of objects + private final boolean supportNullValue; - public ArrayHashSet() { - map = new HashMap<E,E>(); - data = new ArrayList<E>(); + /** + * + * @param supportNullValue Use {@code true} for default behavior, i.e. {@code null} can be a valid value. + * Use {@code false} if {@code null} is not a valid value, + * here {@link #remove(E)} and {@link #getOrAdd(Object)} will be optimized. + * @param initialCapacity use {@link #DEFAULT_INITIAL_CAPACITY} for default + * @param loadFactor use {@link #DEFAULT_LOAD_FACTOR} for default + * @see #supportsNullValue() + */ + public ArrayHashSet(final boolean supportNullValue, final int initialCapacity, final float loadFactor) { + this.map = new HashMap<E,E>(initialCapacity, loadFactor); + this.data = new ArrayList<E>(initialCapacity); + this.supportNullValue = supportNullValue; } - public ArrayHashSet(final int initialCapacity) { - map = new HashMap<E,E>(initialCapacity); - data = new ArrayList<E>(initialCapacity); + /** + * @return a shallow copy of this ArrayHashSet, elements are not copied. + */ + public ArrayHashSet(final ArrayHashSet<E> o) { + map = new HashMap<E, E>(o.map); + data = new ArrayList<E>(o.data); + supportNullValue = o.supportNullValue; } - public ArrayHashSet(final int initialCapacity, final float loadFactor) { - map = new HashMap<E,E>(initialCapacity, loadFactor); - data = new ArrayList<E>(initialCapacity); - } + /** + * Returns {@code true} for default behavior, i.e. {@code null} can be a valid value. + * <p> + * Returns {@code false} if {@code null} is not a valid value, + * here {@link #remove(E)} and {@link #getOrAdd(Object)} are optimized operations. + * </p> + * @see #ArrayHashSet(boolean, int, float) + */ + public final boolean supportsNullValue() { return supportNullValue; } // // Cloneable @@ -95,12 +124,7 @@ public class ArrayHashSet<E> */ @Override public final Object clone() { - final ArrayList<E> clonedList = new ArrayList<E>(data); - - final ArrayHashSet<E> newObj = new ArrayHashSet<E>(); - newObj.addAll(clonedList); - - return newObj; + return new ArrayHashSet<E>(this); } /** Returns this object ordered ArrayList. Use w/ care, it's not a copy. */ @@ -125,40 +149,66 @@ public class ArrayHashSet<E> * Add element at the end of this list, if it is not contained yet. * <br> * This is an O(1) operation + * <p> + * {@inheritDoc} + * </p> * * @return true if the element was added to this list, * otherwise false (already contained). + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ @Override - public final boolean add(final E element) { - final boolean exists = map.containsKey(element); - if(!exists) { + public final boolean add(final E element) throws NullPointerException { + if( !supportNullValue ) { + checkNull(element); + } + if( !map.containsKey(element) ) { + // !exists if(null != map.put(element, element)) { + // slips a valid null .. throw new InternalError("Already existing, but checked before: "+element); } if(!data.add(element)) { throw new InternalError("Couldn't add element: "+element); } + return true; } - return !exists; + return false; } /** * Remove element from this list. * <br> - * This is an O(1) operation, in case it does not exist, + * This is an O(1) operation, in case the element does not exist, * otherwise O(n). + * <p> + * {@inheritDoc} + * </p> * * @return true if the element was removed from this list, * otherwise false (not contained). + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ @Override - public final boolean remove(final Object element) { - if ( null != map.remove(element) ) { - if ( ! data.remove(element) ) { - throw new InternalError("Couldn't remove prev mapped element: "+element); + public final boolean remove(final Object element) throws NullPointerException { + if( supportNullValue ) { + if( map.containsKey(element) ) { + // exists + map.remove(element); + if ( !data.remove(element) ) { + throw new InternalError("Couldn't remove prev mapped element: "+element); + } + return true; + } + } else { + checkNull(element); + if ( null != map.remove(element) ) { + // exists + if ( !data.remove(element) ) { + throw new InternalError("Couldn't remove prev mapped element: "+element); + } + return true; } - return true; } return false; } @@ -167,6 +217,9 @@ public class ArrayHashSet<E> * Add all elements of given {@link java.util.Collection} at the end of this list. * <br> * This is an O(n) operation, over the given Collection size. + * <p> + * {@inheritDoc} + * </p> * * @return true if at least one element was added to this list, * otherwise false (completely container). @@ -184,6 +237,9 @@ public class ArrayHashSet<E> * Test for containment * <br> * This is an O(1) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if the given element is contained by this list using fast hash map, * otherwise false. @@ -197,6 +253,9 @@ public class ArrayHashSet<E> * Test for containment of given {@link java.util.Collection} * <br> * This is an O(n) operation, over the given Collection size. + * <p> + * {@inheritDoc} + * </p> * * @return true if the given Collection is completly contained by this list using hash map, * otherwise false. @@ -215,6 +274,9 @@ public class ArrayHashSet<E> * Remove all elements of given {@link java.util.Collection} from this list. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if at least one element of this list was removed, * otherwise false. @@ -233,6 +295,9 @@ public class ArrayHashSet<E> * remove all elements not contained by the given {@link java.util.Collection} c. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if at least one element of this list was removed, * otherwise false. @@ -250,6 +315,9 @@ public class ArrayHashSet<E> /** * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if arrayHashSet is of type ArrayHashSet and all entries are equal * Performance: arrayHashSet(1) @@ -264,6 +332,9 @@ public class ArrayHashSet<E> /** * This is an O(n) operation over the size of this list. + * <p> + * {@inheritDoc} + * </p> * * @return the hash code of this list as define in {@link java.util.List#hashCode()}, * ie hashing all elements of this list. @@ -316,30 +387,44 @@ public class ArrayHashSet<E> * Add element at the given index in this list, if it is not contained yet. * <br> * This is an O(1) operation + * <p> + * {@inheritDoc} + * </p> * * @throws IllegalArgumentException if the given element was already contained + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ @Override - public final void add(final int index, final E element) { + public final void add(final int index, final E element) throws IllegalArgumentException, NullPointerException { + if( !supportNullValue ) { + checkNull(element); + } if ( map.containsKey(element) ) { throw new IllegalArgumentException("Element "+element+" is already contained"); } if(null != map.put(element, element)) { + // slips a valid null .. throw new InternalError("Already existing, but checked before: "+element); } + // !exists data.add(index, element); } /** + * <p> + * {@inheritDoc} + * </p> * @throws UnsupportedOperationException */ @Override - public final boolean addAll(final int index, final Collection<? extends E> c) { + public final boolean addAll(final int index, final Collection<? extends E> c) throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } /** - * @throws UnsupportedOperationException + * <p> + * {@inheritDoc} + * </p> */ @Override public final E set(final int index, final E element) { @@ -354,6 +439,9 @@ public class ArrayHashSet<E> * Remove element at given index from this list. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return the removed object */ @@ -370,6 +458,9 @@ public class ArrayHashSet<E> * Since this list is unique, equivalent to {@link #indexOf(java.lang.Object)}. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return index of element, or -1 if not found */ @@ -409,34 +500,44 @@ public class ArrayHashSet<E> * <br> * This is an O(1) operation. * - * @param key hash source to find the identical Object within this list + * @param element hash source to find the identical Object within this list * @return object from this list, identical to the given <code>key</code> hash code, * or null if not contained */ - public final E get(final Object key) { - return map.get(key); + public final E get(final Object element) { + return map.get(element); } /** * Identity method allowing to get the identical object, using the internal hash map.<br> - * If the <code>key</code> is not yet contained, add it. + * If the <code>element</code> is not yet contained, add it. * <br> * This is an O(1) operation. * - * @param key hash source to find the identical Object within this list + * @param element hash source to find the identical Object within this list * @return object from this list, identical to the given <code>key</code> hash code, * or add the given <code>key</code> and return it. + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ - public final E getOrAdd(final E key) { - final E identity = get(key); - if(null == identity) { - // object not contained yet, add it - if(!this.add(key)) { - throw new InternalError("Key not mapped, but contained in list: "+key); + public final E getOrAdd(final E element) throws NullPointerException { + if( supportNullValue ) { + if( map.containsKey(element) ) { + // existent + return map.get(element); + } + } else { + checkNull(element); + final E identity = map.get(element); + if(null != identity) { + // existent + return identity; } - return key; } - return identity; + // !existent + if(!this.add(element)) { + throw new InternalError("Element not mapped, but contained in list: "+element); + } + return element; } /** @@ -455,4 +556,9 @@ public class ArrayHashSet<E> return data.contains(element); } + private static final void checkNull(final Object element) throws NullPointerException { + if( null == element ) { + throw new NullPointerException("Null element not supported"); + } + } } diff --git a/src/java/com/jogamp/common/util/Bitfield.java b/src/java/com/jogamp/common/util/Bitfield.java new file mode 100644 index 0000000..4b2b9d5 --- /dev/null +++ b/src/java/com/jogamp/common/util/Bitfield.java @@ -0,0 +1,208 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import jogamp.common.util.SyncedBitfield; + +/** + * Simple bitfield interface for efficient bit storage access in O(1). + * @since 2.3.2 + */ +public interface Bitfield { + /** Maximum 32 bit Unsigned Integer Value: {@code 0xffffffff} == {@value}. */ + public static final int UNSIGNED_INT_MAX_VALUE = 0xffffffff; + + /** + * Bit operation utilities (static). + */ + public static class Util { + /** + * Returns the 32 bit mask of n-bits, i.e. n low order 1’s. + * <p> + * Implementation handles n == 32. + * </p> + * @throws IndexOutOfBoundsException if {@code b} is out of bounds, i.e. > 32 + */ + public static int getBitMask(final int n) { + if( 32 > n ) { + return ( 1 << n ) - 1; + } else if ( 32 == n ) { + return UNSIGNED_INT_MAX_VALUE; + } else { + throw new IndexOutOfBoundsException("n <= 32 expected, is "+n); + } + } + + /** + * Returns the number of set bits within given 32bit integer in O(1) + * using a <i>HAKEM 169 Bit Count</i> inspired implementation: + * <pre> + * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html + * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169 + * http://tekpool.wordpress.com/category/bit-count/ + * http://www.hackersdelight.org/ + * </pre> + */ + public static final int bitCount(int n) { + // Note: Original used 'unsigned int', + // hence we use the unsigned right-shift '>>>' + /** + * Original does not work due to lack of 'unsigned' right-shift and modulo, + * we need 2-complementary solution, i.e. 'signed'. + int c = n; + c -= (n >>> 1) & 033333333333; + c -= (n >>> 2) & 011111111111; + return ( (c + ( c >>> 3 ) ) & 030707070707 ) & 0x3f; // % 63 + */ + // Hackers Delight, Figure 5-2, pop1 of pop.c.txt + n = n - ((n >>> 1) & 0x55555555); + n = (n & 0x33333333) + ((n >>> 2) & 0x33333333); + n = (n + (n >>> 4)) & 0x0f0f0f0f; + n = n + (n >>> 8); + n = n + (n >>> 16); + return n & 0x3f; + } + } + /** + * Simple {@link Bitfield} factory for returning the efficient implementation. + */ + public static class Factory { + /** + * Creates am efficient {@link Bitfield} instance based on the requested {@code storageBitSize}. + * <p> + * Implementation returns a plain 32 bit integer field implementation for + * {@code storageBitSize} ≤ 32 bits or an 32 bit integer array implementation otherwise. + * </p> + */ + public static Bitfield create(final int storageBitSize) { + if( 32 >= storageBitSize ) { + return new jogamp.common.util.Int32Bitfield(); + } else { + return new jogamp.common.util.Int32ArrayBitfield(storageBitSize); + } + } + /** + * Creates a synchronized {@link Bitfield} by wrapping the given {@link Bitfield} instance. + */ + public static Bitfield synchronize(final Bitfield impl) { + return new SyncedBitfield(impl); + } + } + /** + * Returns the storage size in bit units, e.g. 32 bit for implementations using one {@code int} field. + */ + int size(); + + + /** + * Set all bits of this bitfield to the given value {@code bit}. + */ + void clearField(final boolean bit); + + /** + * Returns {@code length} bits from this storage, + * starting with the lowest bit from the storage position {@code lowBitnum}. + * @param lowBitnum storage bit position of the lowest bit, restricted to [0..{@link #size()}-{@code length}]. + * @param length number of bits to read, constrained to [0..32]. + * @throws IndexOutOfBoundsException if {@code rightBitnum} is out of bounds + * @see #put32(int, int, int) + */ + int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException; + + /** + * Puts {@code length} bits of given {@code data} into this storage, + * starting w/ the lowest bit to the storage position {@code lowBitnum}. + * @param lowBitnum storage bit position of the lowest bit, restricted to [0..{@link #size()}-{@code length}]. + * @param length number of bits to write, constrained to [0..32]. + * @param data the actual bits to be put into this storage + * @throws IndexOutOfBoundsException if {@code rightBitnum} is out of bounds + * @see #get32(int, int) + */ + void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException; + + /** + * Copies {@code length} bits at position {@code srcLowBitnum} to position {@code dstLowBitnum} + * and returning the bits. + * <p> + * Implementation shall operate as if invoking {@link #get32(int, int)} + * and then {@link #put32(int, int, int)} sequentially. + * </p> + * @param srcLowBitnum source bit number, restricted to [0..{@link #size()}-1]. + * @param dstLowBitnum destination bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + * @see #get32(int, int) + * @see #put32(int, int, int) + */ + int copy32(final int srcLowBitnum, final int dstLowBitnum, final int length) throws IndexOutOfBoundsException; + + /** + * Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + boolean get(final int bitnum) throws IndexOutOfBoundsException; + + /** + * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code> + * and return the previous value. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException; + + /** + * Set the bit at position <code>bitnum</code> according to <code>bit</code>. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + void set(final int bitnum) throws IndexOutOfBoundsException; + + /** + * Clear the bit at position <code>bitnum</code> according to <code>bit</code>. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + void clear(final int bitnum) throws IndexOutOfBoundsException; + + /** + * Copies the bit at position {@code srcBitnum} to position {@code dstBitnum} + * and returning <code>true</code> if the bit is set, otherwise <code>false</code>. + * @param srcBitnum source bit number, restricted to [0..{@link #size()}-1]. + * @param dstBitnum destination bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException; + + /** + * Returns the number of one bits within this bitfield. + * <p> + * Utilizes {#link {@link Bitfield.Util#bitCount(int)}}. + * </p> + */ + int bitCount(); +} diff --git a/src/java/com/jogamp/common/util/CustomCompress.java b/src/java/com/jogamp/common/util/CustomCompress.java new file mode 100644 index 0000000..6bca095 --- /dev/null +++ b/src/java/com/jogamp/common/util/CustomCompress.java @@ -0,0 +1,167 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +/** + * All in memory inflater / deflator for small chunks using streams + * <p> + * Stream header of deflated data: + * <ul> + * <li>4 bytes magic 0xDEF1A7E0 (Big Endian)</li> + * <li>4 bytes integer deflated-size (Big Endian)</li> + * <li>4 bytes integer inflated-size (Big Endian)</li> + * <li>deflated bytes</li> + * </ul> + * </p> + */ +public class CustomCompress { + /** Start of stream header for deflated data */ + public static final int MAGIC = 0xDEF1A7E0; + + /** + * + * @param in {@link InputStream} at start of stream header, i.e. position {@link #MAGIC}. + * @return the inflated bytes from the stream + * @throws IOException if an I/O or deflation exception occurs + * @throws IllegalArgumentException if {@code inLen} ≤ 0 or {@code outLen} ≤ 0, as read from header + */ + public static byte[] inflateFromStream(final InputStream in) + throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException + { + final int inLen; + final int outLen; + { + final DataInputStream din = new DataInputStream(in); + final int _magic = din.readInt(); + if( _magic != MAGIC ) { + throw new IOException("wrong magic: "+Integer.toHexString(_magic)+", expected "+Integer.toHexString(MAGIC)); + } + inLen = din.readInt(); + outLen = din.readInt(); + } + return inflateFromStream(in, inLen, outLen, new byte[outLen], 0); + } + + /** + * + * @param in {@link InputStream} at start of deflated bytes, i.e. after the stream header. + * @param inLen number of deflated bytes in stream {@code in} + * @param outLen number of inflated {@code output} bytes at {@code outOff} + * @param output sink for deflated bytes + * @param outOff offset to {@code output} + * @return the inflated bytes from the stream, passing {@code output} for chaining + * @throws IOException if an I/O or deflation exception occurs + * @throws ArrayIndexOutOfBoundsException if {@code outOff} and {@code outLen} exceeds {@code output} + * @throws IllegalArgumentException if {@code inLen} ≤ 0 or {@code outLen} ≤ 0 + */ + public static byte[] inflateFromStream(final InputStream in, final int inLen, final int outLen, + final byte[] output, final int outOff) + throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException + { + if (inLen <= 0 || outLen <= 0 ) { + throw new IllegalArgumentException("Length[input "+inLen+", output "+outLen+"]"); + } + if (outOff < 0 || output.length < outOff + outLen) { + throw new ArrayIndexOutOfBoundsException("output.length "+output.length+", offset "+outOff+", length "+outLen); + } + final byte[] input = new byte[inLen]; + int numBytes = 0; + try { + while (true) { + final int remBytes = inLen - numBytes; + int count; + if ( 0 >= remBytes || (count = in.read(input, numBytes, remBytes)) == -1 ) { + break; + } + numBytes += count; + } + } finally { + in.close(); + } + if( inLen != numBytes ) { + throw new IOException("Got "+numBytes+" bytes != expected "+inLen); + } + try { + final Inflater inflater = new Inflater(); + inflater.setInput(input, 0, inLen); + final int outSize = inflater.inflate(output, outOff, outLen); + inflater.end(); + if( outLen != outSize ) { + throw new IOException("Got inflated "+outSize+" bytes != expected "+outLen); + } + } catch(final DataFormatException dfe) { + throw new IOException(dfe); + } + return output; + } + + /** + * @param input raw input bytes + * @param inOff offset to {@code input} + * @param inLen number of {@code input} bytes at {@code inOff} + * @param level compression level 0-9 or {@link Deflater#DEFAULT_COMPRESSION} + * @param out sink for deflated bytes + * @return number of deflated bytes written, not including the header. + * @throws IOException if an I/O or deflation exception occurs + * @throws ArrayIndexOutOfBoundsException if {@code inOff} and {@code inLen} exceeds {@code input} + * @throws IllegalArgumentException if {@code inLen} ≤ 0 + */ + public static int deflateToStream(final byte[] input, final int inOff, final int inLen, + final int level, final OutputStream out) throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException { + if (inLen <= 0 ) { + throw new IllegalArgumentException("Length[input "+inLen+"]"); + } + if (inOff < 0 || input.length < inOff + inLen) { + throw new ArrayIndexOutOfBoundsException("input.length "+input.length+", offset "+inOff+", length "+inLen); + } + final byte[] output = new byte[inLen]; + final Deflater deflater = new Deflater(level); + deflater.setInput(input, inOff, inLen); + deflater.finish(); + final int outSize = deflater.deflate(output, 0, inLen); + deflater.end(); + { + final DataOutputStream dout = new DataOutputStream(out); + dout.writeInt(CustomCompress.MAGIC); + dout.writeInt(outSize); + dout.writeInt(inLen); + } + out.write(output, 0, outSize); + return outSize; + } + +} diff --git a/src/java/com/jogamp/common/util/FunctionTask.java b/src/java/com/jogamp/common/util/FunctionTask.java index 4ac64d3..9eb1ca5 100644 --- a/src/java/com/jogamp/common/util/FunctionTask.java +++ b/src/java/com/jogamp/common/util/FunctionTask.java @@ -30,6 +30,8 @@ package com.jogamp.common.util; import java.io.PrintStream; +import com.jogamp.common.JogampRuntimeException; + /** * Helper class to provide a Runnable queue implementation with a Runnable wrapper * which notifies after execution for the <code>invokeAndWait()</code> semantics. @@ -40,34 +42,67 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { protected A[] args; /** - * Invokes <code>func</code>. + * Invokes <code>func</code> on the current {@link Thread}. + * <p> + * The result can be retrieved via {@link FunctionTask#getResult()}, + * using the returned instance. + * </p> + * @param func the {@link Function} to execute. + * @param args the {@link Function} arguments + * @return the newly created and invoked {@link FunctionTask} + * @since 2.4.0 + */ + public static <U,V> FunctionTask<U,V> invokeOnCurrentThread(final Function<U,V> func, final V... args) { + final FunctionTask<U,V> rt = new FunctionTask<U,V>( func, null, false, null); + rt.args = args; + rt.run(); + return rt; + } + + /** + * Invokes <code>func</code> on a new {@link InterruptSource.Thread}, + * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details. + * <p> + * The result can be retrieved via {@link FunctionTask#getResult()}, + * using the returned instance. + * </p> + * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code> + * @param threadName the name for the new thread, maybe <code>null</code> * @param waitUntilDone if <code>true</code>, waits until <code>func</code> execution is completed, otherwise returns immediately. * @param func the {@link Function} to execute. * @param args the {@link Function} arguments - * @return the {@link Function} return value + * @return the newly created and invoked {@link FunctionTask} + * @since 2.3.2 */ - public static <U,V> U invoke(final boolean waitUntilDone, final Function<U,V> func, final V... args) { - Throwable throwable = null; - final Object sync = new Object(); - final FunctionTask<U,V> rt = new FunctionTask<U,V>( func, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); - final U res; - synchronized(sync) { - res = rt.eval(args); - if( waitUntilDone ) { - try { - sync.wait(); - } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rt.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); + public static <U,V> FunctionTask<U,V> invokeOnNewThread(final ThreadGroup tg, final String threadName, + final boolean waitUntilDone, final Function<U,V> func, final V... args) { + final FunctionTask<U,V> rt; + if( !waitUntilDone ) { + rt = new FunctionTask<U,V>( func, null, true, System.err ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + rt.args = args; + t.start(); + } else { + final Object sync = new Object(); + rt = new FunctionTask<U,V>( func, sync, true, null ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + synchronized(sync) { + rt.args = args; + t.start(); + while( rt.isInQueue() ) { + try { + sync.wait(); + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + final Throwable throwable = rt.getThrowable(); + if(null!=throwable) { + throw new JogampRuntimeException(throwable); + } } } } - return res; + return rt; } /** @@ -124,6 +159,8 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { */ @Override public final void run() { + execThread = Thread.currentThread(); + final A[] args = this.args; this.args = null; this.result = null; @@ -144,6 +181,7 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; } } else { synchronized (syncObject) { @@ -161,6 +199,7 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; syncObject.notifyAll(); } } diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index c773b21..0381ebc 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -39,18 +39,25 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; +import java.io.Reader; +import java.io.SyncFailedException; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.regex.Pattern; import jogamp.common.Debug; import jogamp.common.os.AndroidUtils; import jogamp.common.os.PlatformPropsImpl; +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.JogampRuntimeException; import com.jogamp.common.net.AssetURLContext; import com.jogamp.common.net.Uri; import com.jogamp.common.nio.Buffers; @@ -58,7 +65,61 @@ import com.jogamp.common.os.MachineDataInfo; import com.jogamp.common.os.Platform; public class IOUtil { - public static final boolean DEBUG = Debug.debug("IOUtil"); + public static final boolean DEBUG; + private static final boolean DEBUG_EXE; + private static final boolean DEBUG_EXE_NOSTREAM; + private static final boolean DEBUG_EXE_EXISTING_FILE; + private static final boolean testTempDirExec; + private static final Method fileToPathGetter; + private static final Method isExecutableQuery; + private static final boolean useNativeExeFile; + + static { + final boolean _props[] = { false, false, false, false, false, false }; + final Method[] res = AccessController.doPrivileged(new PrivilegedAction<Method[]>() { + @Override + public Method[] run() { + final Method[] res = new Method[] { null, null }; + try { + int i=0; + _props[i++] = Debug.debug("IOUtil"); + _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe", true); + _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.NoStream", true); + // For security reasons, we have to hardcode this, i.e. disable this manual debug feature! + _props[i++] = false; // PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.ExistingFile", true); + _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.TestTempDirExec", true, true); + _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.UseNativeExeFile", true, false); + + // Java 1.7 + i=0; + res[i] = File.class.getDeclaredMethod("toPath"); + res[i++].setAccessible(true); + final Class<?> nioPathClz = ReflectionUtil.getClass("java.nio.file.Path", false, IOUtil.class.getClassLoader()); + final Class<?> nioFilesClz = ReflectionUtil.getClass("java.nio.file.Files", false, IOUtil.class.getClassLoader()); + res[i] = nioFilesClz.getDeclaredMethod("isExecutable", nioPathClz); + res[i++].setAccessible(true); + } catch (final Throwable t) { + if(_props[0]) { + ExceptionUtils.dumpThrowable("ioutil-init", t); + } + } + return res; + } + }); + { + int i=0; + DEBUG = _props[i++]; + DEBUG_EXE = _props[i++]; + DEBUG_EXE_NOSTREAM = _props[i++]; + DEBUG_EXE_EXISTING_FILE = _props[i++]; + testTempDirExec = _props[i++]; + useNativeExeFile = _props[i++]; + + i=0; + fileToPathGetter = res[i++]; + isExecutableQuery = res[i++]; + } + } /** Std. temporary directory property key <code>java.io.tmpdir</code>. */ private static final String java_io_tmpdir_propkey = "java.io.tmpdir"; @@ -188,6 +249,15 @@ public class IOUtil { return numBytes; } + public static StringBuilder appendCharStream(final StringBuilder sb, final Reader r) throws IOException { + final char[] cbuf = new char[1024]; + int count; + while( 0 < ( count = r.read(cbuf) ) ) { + sb.append(cbuf, 0, count); + } + return sb; + } + /** * Copy the specified input stream to a byte array, which is being returned. */ @@ -408,7 +478,7 @@ public class IOUtil { /*** * - * RESOURCE LOCATION STUFF + * RESOURCE LOCATION HELPER * */ @@ -417,7 +487,10 @@ public class IOUtil { * to be {@link #resolve(int) resolved} at a later time. */ public static class ClassResources { - /** Class instance used to {@link #resolve(int)} the {@link #resourcePaths}. */ + /** Optional {@link ClassLoader} used to {@link #resolve(int)} {@link #resourcePaths}. */ + public final ClassLoader classLoader; + + /** Optional class instance used to {@link #resolve(int)} relative {@link #resourcePaths}. */ public final Class<?> contextCL; /** Resource paths, see {@link #resolve(int)}. */ @@ -427,67 +500,73 @@ public class IOUtil { public final int resourceCount() { return resourcePaths.length; } /** - * @param contextCL class instance to {@link #resolve(int)} {@link #resourcePaths}. - * @param resourcePaths array of strings denominating multiple resource paths. None shall be null. + * @param resourcePaths multiple relative or absolute resource locations + * @param classLoader optional {@link ClassLoader}, see {@link IOUtil#getResource(String, ClassLoader, Class)} + * @param relContext optional relative context, see {@link IOUtil#getResource(String, ClassLoader, Class)} */ - public ClassResources(final Class<?> contextCL, final String[] resourcePaths) { + public ClassResources(final String[] resourcePaths, final ClassLoader classLoader, final Class<?> relContext) { for(int i=resourcePaths.length-1; i>=0; i--) { if( null == resourcePaths[i] ) { throw new IllegalArgumentException("resourcePath["+i+"] is null"); } } - this.contextCL = contextCL; + this.classLoader = classLoader; + this.contextCL = relContext; this.resourcePaths = resourcePaths; } /** - * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using {@link #contextCL} and {@link IOUtil#getResource(Class, String)}. + * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using + * {@link #classLoader}, {@link #contextCL} through {@link IOUtil#getResource(String, ClassLoader, Class)}. * @throws ArrayIndexOutOfBoundsException if <code>uriIndex</code> is < 0 or >= {@link #resourceCount()}. */ public URLConnection resolve(final int uriIndex) throws ArrayIndexOutOfBoundsException { - return getResource(contextCL, resourcePaths[uriIndex]); + return getResource(resourcePaths[uriIndex], classLoader, contextCL); } } /** * Locating a resource using {@link #getResource(String, ClassLoader)}: * <ul> - * <li><i>relative</i>: <code>context</code>'s package name-path plus <code>resourcePath</code> via <code>context</code>'s ClassLoader. + * <li><i>relative</i>: <code>relContext</code>'s package name-path plus <code>resourcePath</code> via <code>classLoader</code>. * This allows locations relative to JAR- and other URLs. - * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder.</li> - * <li><i>absolute</i>: <code>context</code>'s ClassLoader and the <code>resourcePath</code> as is (filesystem)</li> + * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder. + * This attempt is skipped if {@code relContext} is {@code null}.</li> + * <li><i>absolute</i>: <code>resourcePath</code> as is via <code>classLoader</code>. * </ul> - * * <p> * Returns the resolved and open URLConnection or null if not found. * </p> * + * @param resourcePath the resource path to locate relative or absolute + * @param classLoader the optional {@link ClassLoader}, recommended + * @param relContext relative context, i.e. position, of the {@code resourcePath}, + * to perform the relative lookup, if not {@code null}. * @see #getResource(String, ClassLoader) * @see ClassLoader#getResource(String) * @see ClassLoader#getSystemResource(String) */ - public static URLConnection getResource(final Class<?> context, final String resourcePath) { + public static URLConnection getResource(final String resourcePath, final ClassLoader classLoader, final Class<?> relContext) { if(null == resourcePath) { return null; } - final ClassLoader contextCL = (null!=context)?context.getClassLoader():IOUtil.class.getClassLoader(); URLConnection conn = null; - if(null != context) { + if(null != relContext) { // scoping the path within the class's package - final String className = context.getName().replace('.', '/'); + final String className = relContext.getName().replace('.', '/'); final int lastSlash = className.lastIndexOf('/'); if (lastSlash >= 0) { final String pkgName = className.substring(0, lastSlash + 1); - conn = getResource(pkgName + resourcePath, contextCL); + conn = getResource(pkgName + resourcePath, classLoader); if(DEBUG) { - System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+context.getName()+">: "+(null!=conn)); + System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+relContext.getName()+">: "+(null!=conn)); } } } else if(DEBUG) { - System.err.println("IOUtil: null context"); + System.err.println("IOUtil: null context, skip rel. lookup"); } if(null == conn) { - conn = getResource(resourcePath, contextCL); + conn = getResource(resourcePath, classLoader); if(DEBUG) { System.err.println("IOUtil: found <"+resourcePath+"> by classloader: "+(null!=conn)); } @@ -518,8 +597,7 @@ public class IOUtil { return AssetURLContext.createURL(resourcePath, cl).openConnection(); } catch (final IOException ioe) { if(DEBUG) { - System.err.println("IOUtil: Caught Exception:"); - ioe.printStackTrace(); + ExceptionUtils.dumpThrowable("IOUtil", ioe); } return null; } @@ -528,8 +606,7 @@ public class IOUtil { return AssetURLContext.resolve(resourcePath, cl); } catch (final IOException ioe) { if(DEBUG) { - System.err.println("IOUtil: Caught Exception:"); - ioe.printStackTrace(); + ExceptionUtils.dumpThrowable("IOUtil", ioe); } } } @@ -557,7 +634,7 @@ public class IOUtil { } /** - * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory. + * @param path assuming a slashified path, either denotes a file or directory, either relative or absolute. * @return parent of path * @throws URISyntaxException if path is empty or has no parent directory available */ @@ -569,11 +646,11 @@ public class IOUtil { final int e = path.lastIndexOf("/"); if( e < 0 ) { - throw new URISyntaxException(path, "path contains no '/'"); + throw new URISyntaxException(path, "path contains no '/': <"+path+">"); } if( e == 0 ) { // path is root directory - throw new URISyntaxException(path, "path has no parents"); + throw new URISyntaxException(path, "path has no parents: <"+path+">"); } if( e < pl - 1 ) { // path is file, return it's parent directory @@ -583,23 +660,45 @@ public class IOUtil { // path is a directory .. final int p = path.lastIndexOf("/", e-1); if( p >= j) { + // parent itself has '/' - post '!' or no '!' at all return path.substring(0, p+1); + } else { + // parent itself has no '/' + final String parent = path.substring(j, e); + if( parent.equals("..") ) { + throw new URISyntaxException(path, "parent is unresolved: <"+path+">"); + } else { + // parent is '!' or empty (relative path) + return path.substring(0, j); + } } - throw new URISyntaxException(path, "parent of path contains no '/'"); } /** - * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory. - * @return clean path string where <code>../</code> and <code>./</code> is resolved. + * @param path assuming a slashified path, either denoting a file or directory, either relative or absolute. + * @return clean path string where {@code ./} and {@code ../} is resolved, + * while keeping a starting {@code ../} at the beginning of a relative path. * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code> */ public static String cleanPathString(String path) throws URISyntaxException { - int idx; - while ( ( idx = path.indexOf("../") ) >= 0 ) { - path = getParentOf(path.substring(0, idx)) + path.substring(idx+3); + // Resolve './' before '../' to handle case 'parent/./../a.txt' properly. + int idx = path.length() - 1; + while ( idx >= 1 && ( idx = path.lastIndexOf("./", idx) ) >= 0 ) { + if( 0 < idx && path.charAt(idx-1) == '.' ) { + idx-=2; // skip '../' -> idx upfront + } else { + path = path.substring(0, idx) + path.substring(idx+2); + idx--; // idx upfront + } } - while ( ( idx = path.indexOf("./") ) >= 0 ) { - path = path.substring(0, idx) + path.substring(idx+2); + idx = 0; + while ( ( idx = path.indexOf("../", idx) ) >= 0 ) { + if( 0 == idx ) { + idx += 3; // skip starting '../' + } else { + path = getParentOf(path.substring(0, idx)) + path.substring(idx+3); + idx = 0; + } } return path; } @@ -642,8 +741,7 @@ public class IOUtil { return c; } catch (final IOException ioe) { if(DEBUG) { - System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false - "+ioe.getClass().getSimpleName()+": "+ioe.getMessage()); - ioe.printStackTrace(); + ExceptionUtils.dumpThrowable("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false -", ioe); } } } else if(DEBUG) { @@ -656,7 +754,7 @@ public class IOUtil { private static String getExeTestFileSuffix() { switch(PlatformPropsImpl.OS_TYPE) { case WINDOWS: - if( Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { + if( useNativeExeFile && Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { return ".exe"; } else { return ".bat"; @@ -670,7 +768,7 @@ public class IOUtil { case WINDOWS: return "echo off"+PlatformPropsImpl.NEWLINE; default: - return null; + return "#!/bin/true"+PlatformPropsImpl.NEWLINE; } } private static String[] getExeTestCommandArgs(final String scriptFile) { @@ -681,48 +779,50 @@ public class IOUtil { return new String[] { scriptFile }; } } - private static final byte[] getBytesFromRelFile(final byte[] res, final String fname, final int size) throws IOException { - final URLConnection con = IOUtil.getResource(IOUtil.class, fname); + + private static final byte[] readCode(final String fname) throws IOException { + final URLConnection con = IOUtil.getResource(fname, IOUtil.class.getClassLoader(), IOUtil.class); final InputStream in = con.getInputStream(); - int numBytes = 0; + byte[] output = null; try { - while (true) { - final int remBytes = size - numBytes; - int count; - if ( 0 >= remBytes || (count = in.read(res, numBytes, remBytes)) == -1 ) { - break; - } - numBytes += count; - } + output = CustomCompress.inflateFromStream(in); } finally { in.close(); } - if( size != numBytes ) { - throw new IOException("Got "+numBytes+" bytes != expected "+size); - } - return res; + return output; } - private static final Object exeTestBytesLock = new Object(); - private static WeakReference<byte[]> exeTestBytesRef = null; + private static final Object exeTestLock = new Object(); + private static WeakReference<byte[]> exeTestCodeRef = null; private static void fillExeTestFile(final File exefile) throws IOException { - if( Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE && + if( useNativeExeFile && + Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE && Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { - final int codeSize = 268; - final byte[] code; - synchronized ( exeTestBytesLock ) { - byte[] _code; - if( null == exeTestBytesRef || null == ( _code = exeTestBytesRef.get() ) ) { - code = getBytesFromRelFile(new byte[512], "bin/exe-windows-i586-268b.bin", codeSize); - exeTestBytesRef = new WeakReference<byte[]>(code); + final byte[] exeTestCode; + synchronized ( exeTestLock ) { + byte[] _exeTestCode = null; + if( null == exeTestCodeRef || null == ( _exeTestCode = exeTestCodeRef.get() ) ) { + final String fname; + if( Platform.CPUType.X86_64 == PlatformPropsImpl.CPU_ARCH ) { + fname = "bin/exe-windows-x86_64.defl"; + } else { + fname = "bin/exe-windows-i386.defl"; + } + exeTestCode = readCode(fname); + exeTestCodeRef = new WeakReference<byte[]>(exeTestCode); } else { - code = _code; + exeTestCode = _exeTestCode; } } - final OutputStream out = new FileOutputStream(exefile); + final FileOutputStream out = new FileOutputStream(exefile); try { - out.write(code, 0, codeSize); + out.write(exeTestCode, 0, exeTestCode.length); + try { + out.getFD().sync(); + } catch (final SyncFailedException sfe) { + ExceptionUtils.dumpThrowable("", sfe); + } } finally { out.close(); } @@ -732,6 +832,11 @@ public class IOUtil { final FileWriter fout = new FileWriter(exefile); try { fout.write(shellCode); + try { + fout.flush(); + } catch (final IOException sfe) { + ExceptionUtils.dumpThrowable("", sfe); + } } finally { fout.close(); } @@ -796,50 +901,70 @@ public class IOUtil { public static class StreamMonitor implements Runnable { private final InputStream[] istreams; + private final boolean[] eos; private final PrintStream ostream; private final String prefix; public StreamMonitor(final InputStream[] streams, final PrintStream ostream, final String prefix) { this.istreams = streams; + this.eos = new boolean[streams.length]; this.ostream = ostream; this.prefix = prefix; - new Thread(this, "StreamMonitor-"+Thread.currentThread().getName()).start(); + final InterruptSource.Thread t = new InterruptSource.Thread(null, this, "StreamMonitor-"+Thread.currentThread().getName()); + t.setDaemon(true); + t.start(); } + @Override public void run() { final byte[] buffer = new byte[4096]; try { - int numRead; + final int streamCount = istreams.length; + int eosCount = 0; do { - numRead = 0; for(int i=0; i<istreams.length; i++) { - final int numReadI = istreams[i].read(buffer); - if (numReadI > 0) { - if( null != ostream ) { - if( null != prefix ) { - ostream.write(prefix.getBytes()); + if( !eos[i] ) { + final int numReadI = istreams[i].read(buffer); + if (numReadI > 0) { + if( null != ostream ) { + if( null != prefix ) { + ostream.write(prefix.getBytes()); + } + ostream.write(buffer, 0, numReadI); } - ostream.write(buffer, 0, numReadI); + } else { + // numReadI == -1 + eosCount++; + eos[i] = true; } - numRead += numReadI; } } if( null != ostream ) { ostream.flush(); } - } while (numRead >= 0); - } - catch (final IOException e) { - for(int i=0; i<istreams.length; i++) { - try { - istreams[i].close(); - } catch (final IOException e2) { } + } while ( eosCount < streamCount ); + } catch (final IOException e) { + } finally { + if( null != ostream ) { + ostream.flush(); } // Should allow clean exit when process shuts down } } } + private static final Boolean isNioExecutableFile(final File file) { + if( null != fileToPathGetter && null != isExecutableQuery ) { + try { + return (Boolean) isExecutableQuery.invoke(null, fileToPathGetter.invoke(file)); + } catch (final Throwable t) { + throw new JogampRuntimeException("error invoking Files.isExecutable(file.toPath())", t); + } + } else { + return null; + } + } + /** * Returns true if the given {@code dir} * <ol> @@ -855,65 +980,114 @@ public class IOUtil { public static boolean testDirExec(final File dir) throws SecurityException { - if (!testFile(dir, true, true)) { + final boolean debug = DEBUG_EXE || DEBUG; + + if( !testTempDirExec ) { if(DEBUG) { + System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Disabled TestTempDirExec"); + } + return false; + } + if (!testFile(dir, true, true)) { + if( debug ) { System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir"); } return false; } if(!getOSHasNoexecFS()) { - if(DEBUG) { + if( debug ) { System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Always executable"); } return true; } - final long t0 = DEBUG ? System.currentTimeMillis() : 0; + final long t0 = debug ? System.currentTimeMillis() : 0; final File exeTestFile; + final boolean existingExe; try { - exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir); + final File permExeTestFile = DEBUG_EXE_EXISTING_FILE ? new File(dir, "jogamp_exe_tst"+getExeTestFileSuffix()) : null; + if( null != permExeTestFile && permExeTestFile.exists() ) { + exeTestFile = permExeTestFile; + existingExe = true; + } else { + exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir); + existingExe = false; + fillExeTestFile(exeTestFile); + } } catch (final SecurityException se) { throw se; // fwd Security exception } catch (final IOException e) { - if(DEBUG) { + if( debug ) { e.printStackTrace(); } return false; } - final long t1 = DEBUG ? System.currentTimeMillis() : 0; + final long t1 = debug ? System.currentTimeMillis() : 0; + long t2; int res = -1; - if(exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */)) { - try { - fillExeTestFile(exeTestFile); - - // Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)' - // and hence splitting up command by spaces! - final Process pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ) ); - /** - * Disable StreamMonitor, which throttles exec-test performance a lot! - * - * if( isStringSet(shellCode) ) { - new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: "); - } - */ - pr.waitFor() ; - res = pr.exitValue(); - } catch (final SecurityException se) { - throw se; // fwd Security exception - } catch (final Throwable t) { - res = -2; - if(DEBUG) { - System.err.println("IOUtil.testDirExec: <"+exeTestFile.getAbsolutePath()+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage()); - t.printStackTrace(); + int exitValue = -1; + Boolean isNioExec = null; + if( existingExe || exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */) ) { + t2 = debug ? System.currentTimeMillis() : 0; + // First soft exec test via NIO's ACL check, if available + isNioExec = isNioExecutableFile(exeTestFile); + if( null != isNioExec ) { + res = isNioExec.booleanValue() ? 0 : -1; + } + if( null == isNioExec || 0 <= res ) { + // Hard exec test via actual execution, if NIO's ACL check succeeded or not available. + // Required, since Windows 'Software Restriction Policies (SRP)' won't be triggered merely by NIO's ACL check. + Process pr = null; + try { + // Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)' + // and hence splitting up command by spaces! + // Note: All no-exec cases throw an IOExceptions at ProcessBuilder.start(), i.e. below exec() call! + pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ), null, null ); + if( DEBUG_EXE && !DEBUG_EXE_NOSTREAM ) { + new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: "); + } + pr.waitFor(); + exitValue = pr.exitValue(); // Note: Bug 1219 Comment 50: On reporter's machine exit value 1 is being returned + if( 0 == exitValue ) { + res++; // file has been executed and exited normally + } else { + res = -2; // abnormal termination + } + } catch (final SecurityException se) { + throw se; // fwd Security exception + } catch (final Throwable t) { + t2 = debug ? System.currentTimeMillis() : 0; + res = -3; + if( debug ) { + System.err.println("IOUtil.testDirExec: <"+exeTestFile.getAbsolutePath()+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage()); + t.printStackTrace(); + } + } finally { + if( null != pr ) { + // Bug 1219 Comment 58: Ensure that the launched process gets terminated! + // This is Process implementation specific and varies on different platforms, + // hence it may be required. + try { + pr.destroy(); + } catch (final Throwable t) { + ExceptionUtils.dumpThrowable("", t); + } + } } } + } else { + t2 = debug ? System.currentTimeMillis() : 0; + } + + final boolean ok = 0 <= res; + if( !DEBUG_EXE && !existingExe ) { + exeTestFile.delete(); } - final boolean ok = 0 == res; - final long t2 = DEBUG ? System.currentTimeMillis() : 0; - exeTestFile.delete(); - if( DEBUG) { - System.err.println("IOUtil.testDirExec(): <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok); - System.err.println("IOUtil.testDirExec(): total "+(t2-t0)+"ms, create "+(t1-t0)+"ms, execute "+(t2-t1)+"ms"); + if( debug ) { + final long t3 = System.currentTimeMillis(); + System.err.println("IOUtil.testDirExec(): test-exe <"+exeTestFile.getAbsolutePath()+">, existingFile "+existingExe+", isNioExec "+isNioExec+", returned "+exitValue); + System.err.println("IOUtil.testDirExec(): abs-path <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok); + System.err.println("IOUtil.testDirExec(): total "+(t3-t0)+"ms, create "+(t1-t0)+"ms, fill "+(t2-t1)+"ms, execute "+(t3-t2)+"ms"); } return ok; } diff --git a/src/java/com/jogamp/common/util/IntBitfield.java b/src/java/com/jogamp/common/util/IntBitfield.java deleted file mode 100644 index 74e37ac..0000000 --- a/src/java/com/jogamp/common/util/IntBitfield.java +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2012 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.common.util; - -/** - * Simple bitfield holder class using an int[] storage. - * <p> - * IntBitfield allows convenient access of a wide field of transient bits using efficient storage in O(1). - * </p> - * <p> - * It can be used e.g. to map key-codes to pressed-state etc. - * </p> - */ -public class IntBitfield { - /** Unit size in bits, here 32 bits for one int unit. */ - public static final int UNIT_SIZE = 32; - - private static final long UNIT_SHIFT_L = 5L; - private static final int UNIT_SHIFT_I = 5; - - private final int[] storage; - private final long bitsCountL; - private final int bitsCountI; - - /** - * @param bitCount - */ - public IntBitfield(final long bitCount) { - final int units = (int) Math.max(1L, ( bitCount + 7L ) >>> UNIT_SHIFT_L); - this.storage = new int[units]; - this.bitsCountL = (long)units << UNIT_SHIFT_L ; - this.bitsCountI = bitsCountL > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)bitsCountL; - } - - /** - * @param bitCount - */ - public IntBitfield(final int bitCount) { - final int units = Math.max(1, ( bitCount + 7 ) >>> UNIT_SHIFT_I); - this.storage = new int[units]; - this.bitsCountI = units << UNIT_SHIFT_I; - this.bitsCountL = bitsCountI; - } - - private final void check(final long bitnum) { - if( 0 > bitnum || bitnum >= bitsCountL ) { - throw new ArrayIndexOutOfBoundsException("Bitnum should be within [0.."+(bitsCountL-1)+"], but is "+bitnum); - } - } - private final void check(final int bitnum) { - if( 0 > bitnum || bitnum >= bitsCountI ) { - throw new ArrayIndexOutOfBoundsException("Bitnum should be within [0.."+(bitsCountI-1)+"], but is "+bitnum); - } - } - - /** Return the capacity of this bit field, i.e. the number of bits stored int this field. */ - public final long capacity() { return bitsCountL; } - - /** Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. */ - public final boolean get(final long bitnum) { - check(bitnum); - final int u = (int) ( bitnum >>> UNIT_SHIFT_L ); - final int b = (int) ( bitnum - ( (long)u << UNIT_SHIFT_L ) ); - return 0 != ( storage[u] & ( 1 << b ) ) ; - } - - /** Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. */ - public final boolean get(final int bitnum) { - check(bitnum); - final int u = bitnum >>> UNIT_SHIFT_I; - final int b = bitnum - ( u << UNIT_SHIFT_I ); - return 0 != ( storage[u] & ( 1 << b ) ) ; - } - - /** - * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code> - * and return the previous value. - */ - public final boolean put(final long bitnum, final boolean bit) { - check(bitnum); - final int u = (int) ( bitnum >>> UNIT_SHIFT_L ); - final int b = (int) ( bitnum - ( (long)u << UNIT_SHIFT_L ) ); - final int m = 1 << b; - final boolean prev = 0 != ( storage[u] & m ) ; - if( prev != bit ) { - if( bit ) { - storage[u] |= m; - } else { - storage[u] &= ~m; - } - } - return prev; - } - - /** - * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code> - * and return the previous value. - */ - public final boolean put(final int bitnum, final boolean bit) { - check(bitnum); - final int u = bitnum >>> UNIT_SHIFT_I; - final int b = bitnum - ( u << UNIT_SHIFT_I ); - final int m = 1 << b; - final boolean prev = 0 != ( storage[u] & m ) ; - if( prev != bit ) { - if( bit ) { - storage[u] |= m; - } else { - storage[u] &= ~m; - } - } - return prev; - } - /** - * Returns the number of set bits within given 32bit integer in O(1) - * using <i>HAKEM Bit Count</i>: - * <pre> - * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html - * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169 - * http://tekpool.wordpress.com/category/bit-count/ - * </pre> - */ - public static final int getBitCount(final int n) { - // Note: Original used 'unsigned int', - // hence we use the unsigned right-shift '>>>' - int c = n; - c -= (n >>> 1) & 033333333333; - c -= (n >>> 2) & 011111111111; - return ( (c + ( c >>> 3 ) ) & 030707070707 ) % 63; - } - - /** - * Returns the number of set bits within this bitfield. - * <p> - * Utilizes {#link {@link #getBitCount(int)}}. - * </p> - */ - public long getBitCount() { - long c = 0; - for(int i = storage.length-1; i>=0; i--) { - c += getBitCount(storage[i]); - } - return c; - } -} diff --git a/src/java/com/jogamp/common/util/InterruptSource.java b/src/java/com/jogamp/common/util/InterruptSource.java new file mode 100644 index 0000000..01fcdb5 --- /dev/null +++ b/src/java/com/jogamp/common/util/InterruptSource.java @@ -0,0 +1,157 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +/** + * Interface exposing {@link java.lang.Thread#interrupt()} source, + * intended for {@link java.lang.Thread} specializations. + * @since 2.3.2 + */ +public interface InterruptSource { + /** + * Returns the source of the last {@link #interrupt()} call. + * @param clear if true, issues {@link #clearInterruptSource()} + */ + Throwable getInterruptSource(final boolean clear); + + /** + * Returns the count of {@link java.lang.Thread#interrupt()} calls. + * @param clear if true, issues {@link #clearInterruptSource()} + */ + int getInterruptCounter(final boolean clear); + + /** + * Clears source and count of {@link java.lang.Thread#interrupt()} calls, if any. + */ + void clearInterruptSource(); + + public static class Util { + /** + * Casts given {@link java.lang.Thread} to {@link InterruptSource} + * if applicable, otherwise returns {@code null}. + */ + public static InterruptSource get(final java.lang.Thread t) { + if(t instanceof InterruptSource) { + return (InterruptSource)t; + } else { + return null; + } + } + /** + * Casts current {@link java.lang.Thread} to {@link InterruptSource} + * if applicable, otherwise returns {@code null}. + */ + public static InterruptSource currentThread() { + return get(java.lang.Thread.currentThread()); + } + } + + /** + * {@link java.lang.Thread} specialization implementing {@link InterruptSource} + * to track {@link java.lang.Thread#interrupt()} calls. + * @since 2.3.2 + */ + public static class Thread extends java.lang.Thread implements InterruptSource { + volatile Throwable interruptSource = null; + volatile int interruptCounter = 0; + final Object sync = new Object(); + + /** + * See {@link Thread#Thread(} for details. + */ + public Thread() { + super(); + } + /** + * See {@link Thread#Thread(ThreadGroup, Runnable)} for details. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + */ + public Thread(final ThreadGroup tg, final Runnable target) { + super(tg, target); + } + /** + * See {@link Thread#Thread(ThreadGroup, Runnable, String)} for details. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + * @param name explicit name of thread, must not be {@code null} + */ + public Thread(final ThreadGroup tg, final Runnable target, final String name) { + super(tg, target, name); + } + + /** + * Depending on whether {@code name} is null, either + * {@link #Thread(ThreadGroup, Runnable, String)} or + * {@link #Thread(ThreadGroup, Runnable)} is being utilized. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + * @param name explicit name of thread, may be {@code null} + */ + public static Thread create(final ThreadGroup tg, final Runnable target, final String name) { + return null != name ? new Thread(tg, target, name) : new Thread(tg, target); + } + + @Override + public final Throwable getInterruptSource(final boolean clear) { + synchronized(sync) { + final Throwable r = interruptSource; + if( clear ) { + clearInterruptSource(); + } + return r; + } + } + @Override + public final int getInterruptCounter(final boolean clear) { + synchronized(sync) { + final int r = interruptCounter; + if( clear ) { + clearInterruptSource(); + } + return r; + } + } + @Override + public final void clearInterruptSource() { + synchronized(sync) { + interruptCounter = 0; + interruptSource = null; + } + } + @Override + public final void interrupt() { + synchronized(sync) { + interruptCounter++; + interruptSource = new Throwable(getName()+".interrupt() #"+interruptCounter); + } + super.interrupt(); + } + } +} diff --git a/src/java/com/jogamp/common/util/InterruptedRuntimeException.java b/src/java/com/jogamp/common/util/InterruptedRuntimeException.java new file mode 100644 index 0000000..af6fea8 --- /dev/null +++ b/src/java/com/jogamp/common/util/InterruptedRuntimeException.java @@ -0,0 +1,80 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import com.jogamp.common.JogampRuntimeException; + +/** + * <i>Unchecked exception</i> propagating an {@link InterruptedException} + * where handling of the latter is not desired. + * <p> + * {@link InterruptedRuntimeException} may be thrown either by waiting for any {@link Runnable} + * to be completed, or during its execution. + * </p> + * <p> + * The propagated {@link InterruptedException} may be of type {@link SourcedInterruptedException}. + * </p> + * <p> + * </p> + */ +@SuppressWarnings("serial") +public class InterruptedRuntimeException extends JogampRuntimeException { + + /** + * Constructor attempts to {@link SourcedInterruptedException#wrap(InterruptedException) wrap} + * the given {@link InterruptedException} {@code cause} into a {@link SourcedInterruptedException}. + * + * @param message the message of this exception + * @param cause the propagated {@link InterruptedException} + */ + public InterruptedRuntimeException(final String message, final InterruptedException cause) { + super(message, SourcedInterruptedException.wrap(cause)); + } + + /** + * Constructor attempts to {@link SourcedInterruptedException#wrap(InterruptedException) wrap} + * the given {@link InterruptedException} {@code cause} into a {@link SourcedInterruptedException}. + * + * @param cause the propagated {@link InterruptedException} + */ + public InterruptedRuntimeException(final InterruptedException cause) { + super(SourcedInterruptedException.wrap(cause)); + } + + /** + * Returns the propagated {@link InterruptedException}, i.e. the cause of this exception. + * <p> + * {@inheritDoc} + * </p> + */ + @Override + public InterruptedException getCause() { + return (InterruptedException)super.getCause(); + } +} diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java index 745dd12..d6c8fd4 100644 --- a/src/java/com/jogamp/common/util/JarUtil.java +++ b/src/java/com/jogamp/common/util/JarUtil.java @@ -163,7 +163,7 @@ public class JarUtil { } } } - if( !uri.scheme.equals( Uri.JAR_SCHEME ) ) { + if( !uri.isJarScheme() ) { throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+uri+">"); } if(DEBUG) { @@ -190,7 +190,7 @@ public class JarUtil { if(null == classJarUri) { throw new IllegalArgumentException("Uri is null"); } - if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) { + if( !classJarUri.isJarScheme() ) { throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">"); } Uri.Encoded ssp = classJarUri.schemeSpecificPart; @@ -262,7 +262,7 @@ public class JarUtil { if(null == classJarUri) { throw new IllegalArgumentException("Uri is null"); } - if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) { + if( !classJarUri.isJarScheme() ) { throw new IllegalArgumentException("Uri is not a using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">"); } final Uri.Encoded uriSSP = classJarUri.schemeSpecificPart; @@ -413,20 +413,6 @@ public class JarUtil { } /** - * See {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}. - * @param classFromJavaJar URI encoded! - * @param cutOffInclSubDir URI encoded! - * @param relResPath URI encoded! - * @return - * @throws IllegalArgumentException - * @throws IOException - * @throws URISyntaxException - * @deprecated Use {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}. - */ - public static java.net.URI getRelativeOf(final Class<?> classFromJavaJar, final String cutOffInclSubDir, final String relResPath) throws IllegalArgumentException, IOException, URISyntaxException { - return getRelativeOf(classFromJavaJar, Uri.Encoded.cast(cutOffInclSubDir), Uri.Encoded.cast(relResPath)).toURI(); - } - /** * Locates the {@link JarUtil#getJarFileUri(Uri) Jar file Uri} of a given resource * relative to a given class's Jar's Uri. * <pre> diff --git a/src/java/com/jogamp/common/util/JogampVersion.java b/src/java/com/jogamp/common/util/JogampVersion.java index e9becc6..e06ce1f 100644 --- a/src/java/com/jogamp/common/util/JogampVersion.java +++ b/src/java/com/jogamp/common/util/JogampVersion.java @@ -46,6 +46,9 @@ public class JogampVersion { /** See {@link #getImplementationCommit()} */ public static final Attributes.Name IMPLEMENTATION_COMMIT = new Attributes.Name("Implementation-Commit"); + /** For FAT JogAmp jar files */ + private static final String packageNameFAT = "com.jogamp"; + private final String packageName; private final Manifest mf; private final int hash; @@ -55,12 +58,27 @@ public class JogampVersion { private final String androidPackageVersionName; protected JogampVersion(final String packageName, final Manifest mf) { - this.packageName = packageName; - this.mf = ( null != mf ) ? mf : new Manifest(); + if( null != mf ) { + // use provided valid data + this.mf = mf; + this.packageName = packageName; + } else { + // try FAT jar file + final Manifest fatMF = VersionUtil.getManifest(JogampVersion.class.getClassLoader(), packageNameFAT); + if( null != fatMF ) { + // use FAT jar file + this.mf = fatMF; + this.packageName = packageNameFAT; + } else { + // use faulty data, unresolvable .. + this.mf = new Manifest(); + this.packageName = packageName; + } + } this.hash = this.mf.hashCode(); mainAttributes = this.mf.getMainAttributes(); mainAttributeNames = mainAttributes.keySet(); - androidPackageVersionName = AndroidUtils.getPackageInfoVersionName(packageName); // null if !Android + androidPackageVersionName = AndroidUtils.getPackageInfoVersionName(this.packageName); // null if !Android } @Override diff --git a/src/java/com/jogamp/common/util/RunnableTask.java b/src/java/com/jogamp/common/util/RunnableTask.java index 6fb98de..2689de1 100644 --- a/src/java/com/jogamp/common/util/RunnableTask.java +++ b/src/java/com/jogamp/common/util/RunnableTask.java @@ -30,6 +30,8 @@ package com.jogamp.common.util; import java.io.PrintStream; +import com.jogamp.common.JogampRuntimeException; + /** * Helper class to provide a Runnable queue implementation with a Runnable wrapper * which notifies after execution for the <code>invokeAndWait()</code> semantics. @@ -38,70 +40,58 @@ public class RunnableTask extends TaskBase { protected final Runnable runnable; /** - * Invokes <code>runnable</code> on the current thread. - * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately. - * @param runnable the {@link Runnable} to execute. + * Invokes <code>runnable</code> on the current {@link Thread}. + * @param runnable the {@link Runnable} to execute on the current thread. + * The runnable <b>must exit</b>, i.e. not loop forever. + * @return the newly created and invoked {@link RunnableTask} + * @since 2.4.0 */ - public static void invoke(final boolean waitUntilDone, final Runnable runnable) { - Throwable throwable = null; - final Object sync = new Object(); - final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); - synchronized(sync) { - rt.run(); - if( waitUntilDone ) { - try { - sync.wait(); - } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rt.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); - } - } - } + public static RunnableTask invokeOnCurrentThread(final Runnable runnable) { + final RunnableTask rt = new RunnableTask( runnable, null, false, null ); + rt.run(); + return rt; } /** - * Invokes <code>runnable</code> on a new thread belonging to the given {@link ThreadGroup}. + * Invokes <code>runnable</code> on a new {@link InterruptSource.Thread}, + * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details. * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code> + * @param threadName the name for the new thread, maybe <code>null</code> * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately. * @param runnable the {@link Runnable} to execute on the new thread. If <code>waitUntilDone</code> is <code>true</code>, - * the runnable <b>must exist</b>, i.e. not loop forever. - * @param threadName the name for the new thread - * @return the newly created {@link Thread} + * the runnable <b>must exit</b>, i.e. not loop forever. + * @return the newly created and invoked {@link RunnableTask} + * @since 2.3.2 */ - public static Thread invokeOnNewThread(final ThreadGroup tg, final boolean waitUntilDone, final Runnable runnable, final String threadName) { - final Thread t = new Thread(tg, threadName) { - @Override - public void run() { - Throwable throwable = null; - final Object sync = new Object(); - final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); - synchronized(sync) { - rt.run(); - if( waitUntilDone ) { - try { - sync.wait(); - } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rt.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); - } + public static RunnableTask invokeOnNewThread(final ThreadGroup tg, final String threadName, + final boolean waitUntilDone, final Runnable runnable) { + final RunnableTask rt; + if( !waitUntilDone ) { + rt = new RunnableTask( runnable, null, true, System.err ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + t.start(); + } else { + final Object sync = new Object(); + rt = new RunnableTask( runnable, sync, true, null ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + synchronized(sync) { + t.start(); + while( rt.isInQueue() ) { + try { + sync.wait(); + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + final Throwable throwable = rt.getThrowable(); + if(null!=throwable) { + throw new JogampRuntimeException(throwable); } } - } }; - t.start(); - return t; + } + } + return rt; } - /** * Create a RunnableTask object w/ synchronization, * ie. suitable for <code>invokeAndWait()</code>, i.e. {@link #invoke(boolean, Runnable) invoke(true, runnable)}. @@ -126,6 +116,8 @@ public class RunnableTask extends TaskBase { @Override public final void run() { + execThread = Thread.currentThread(); + runnableException = null; tStarted = System.currentTimeMillis(); if(null == syncObject) { @@ -143,6 +135,7 @@ public class RunnableTask extends TaskBase { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; } } else { synchronized (syncObject) { @@ -160,6 +153,7 @@ public class RunnableTask extends TaskBase { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; syncObject.notifyAll(); } } diff --git a/src/java/com/jogamp/common/util/SourcedInterruptedException.java b/src/java/com/jogamp/common/util/SourcedInterruptedException.java new file mode 100644 index 0000000..530f1e7 --- /dev/null +++ b/src/java/com/jogamp/common/util/SourcedInterruptedException.java @@ -0,0 +1,166 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.PrintStream; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.ExceptionUtils.CustomStackTrace; + +/** + * {@link InterruptedException}, which may include the source, see {@link #getInterruptSource()}. + * <p> + * This exception may be created directly where {@link #getCause()} returns {@code null}, + * or by propagating an existing {@link InterruptedException} as returned by {@link #getCause()}. + * </p> + * @since 2.3.2 + */ +@SuppressWarnings("serial") +public class SourcedInterruptedException extends InterruptedException implements CustomStackTrace { + final Throwable interruptSource; + + /** + * Wraps the given {@link InterruptedException} into a {@link SourcedInterruptedException} + * if it is not yet of the desired type and + * if the current thread if a {@link InterruptSource}, i.e. the source is known. + * <p> + * Otherwise the given {@link InterruptedException} instance is returned. + * </p> + * <p> + * In case method is creating a new wrapping instance, + * {@link InterruptSource#clearInterruptSource()} is being issued. + * </p> + * + * @param ie the to be wrapped {@link InterruptedException} + */ + public static InterruptedException wrap(final InterruptedException ie) { + return wrap(ie, InterruptSource.Util.currentThread()); + } + + /** + * Wraps the given {@link InterruptedException} into a {@link SourcedInterruptedException} + * if it is not yet of the same type and if {@code source} is not {@code null}. + * <p> + * Otherwise the given {@link InterruptedException} instance is returned. + * </p> + * <p> + * In case method is creating a new wrapping instance, + * {@link InterruptSource#clearInterruptSource()} is being issued. + * </p> + * + * @param ie the to be wrapped {@link InterruptedException} + * @param source the {@link InterruptSource} + */ + public static InterruptedException wrap(final InterruptedException ie, final InterruptSource source) { + if( !(ie instanceof SourcedInterruptedException) && null != source ) { + return new SourcedInterruptedException(ie, source.getInterruptSource(true)); + } else { + return ie; + } + } + + /** + * @param message mandatory message of this exception + * @param cause optional propagated cause + * @param interruptSource optional propagated source of {@link Thread#interrupt()} call + */ + public SourcedInterruptedException(final String message, final InterruptedException cause, final Throwable interruptSource) { + super(message); + if( null != cause ) { + initCause(cause); + } + this.interruptSource = interruptSource; + } + + /** + * @param cause mandatory propagated cause + * @param interruptSource optional propagated source of {@link Thread#interrupt()} call + */ + public SourcedInterruptedException(final InterruptedException cause, final Throwable interruptSource) { + super(cause.getMessage()); + initCause(cause); + this.interruptSource = interruptSource; + } + + /** + * Returns the source of the {@link Thread#interrupt()} call if known, + * otherwise {@code null} is returned. + */ + public final Throwable getInterruptSource() { + return interruptSource; + } + + /** + * Returns the propagated {@link InterruptedException}, i.e. the cause of this exception, + * or {@code null} if not applicable. + * <p> + * {@inheritDoc} + * </p> + */ + @Override + public InterruptedException getCause() { + return (InterruptedException)super.getCause(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(256); + sb.append(getClass().getSimpleName()).append(": "); + if (null != interruptSource) { + sb.append("[sourced]"); + } else { + sb.append("[unknown]"); + } + final String m = getLocalizedMessage(); + if( null != m ) { + sb.append(" ").append(m); + } + return sb.toString(); + } + + @Override + public final void printCauseStack(final PrintStream s, final String causeStr, final int causeIdx, final int stackDepth) { + final String s0 = causeStr+"["+causeIdx+"]"; + s.println(s0+" by "+getClass().getSimpleName()+": "+getMessage()+" on thread "+Thread.currentThread().getName()); + ExceptionUtils.dumpStack(s, getStackTrace(), 0, stackDepth); + if( null != interruptSource ) { + ExceptionUtils.printCause(s, s0, interruptSource, 0, 1, stackDepth); + } + } + + @Override + public final void printStackTrace(final PrintStream s, final int causeDepth, final int stackDepth) { + s.println(getClass().getSimpleName()+": "+getMessage()+" on thread "+Thread.currentThread().getName()); + ExceptionUtils.dumpStack(s, getStackTrace(), 0, stackDepth); + ExceptionUtils.printCause(s, "Caused", getCause(), 0, causeDepth, stackDepth); + if( null != interruptSource ) { + ExceptionUtils.printCause(s, "InterruptSource", interruptSource, 0, causeDepth, stackDepth); + } + } +} diff --git a/src/java/com/jogamp/common/util/TaskBase.java b/src/java/com/jogamp/common/util/TaskBase.java index 59b86c3..64a8313 100644 --- a/src/java/com/jogamp/common/util/TaskBase.java +++ b/src/java/com/jogamp/common/util/TaskBase.java @@ -54,17 +54,29 @@ public abstract class TaskBase implements Runnable { protected Throwable runnableException; protected long tCreated, tStarted; protected volatile long tExecuted; + protected volatile boolean isExecuted; protected volatile boolean isFlushed; + protected volatile Thread execThread; + /** + * @param syncObject The synchronization object if caller wait until <code>runnable</code> execution is completed, + * or <code>null</code> if waiting is not desired. + * @param catchExceptions Influence an occurring exception during <code>runnable</code> execution. + * If <code>true</code>, the exception is silenced and can be retrieved via {@link #getThrowable()}, + * otherwise the exception is thrown. + * @param exceptionOut If not <code>null</code>, exceptions are written to this {@link PrintStream}. + */ protected TaskBase(final Object syncObject, final boolean catchExceptions, final PrintStream exceptionOut) { this.syncObject = syncObject; this.catchExceptions = catchExceptions; this.exceptionOut = exceptionOut; this.sourceStack = TRACE_SOURCE ? new Throwable("Creation @") : null; - tCreated = System.currentTimeMillis(); - tStarted = 0; - tExecuted = 0; - isFlushed = false; + this.tCreated = System.currentTimeMillis(); + this.tStarted = 0; + this.tExecuted = 0; + this.isExecuted = false; + this.isFlushed = false; + this.execThread = null; } protected final String getExceptionOutIntro() { @@ -77,6 +89,14 @@ public abstract class TaskBase implements Runnable { } /** + * Returns the execution thread or {@code null} if not yet {@link #run()}. + * @since 2.3.2 + */ + public final Thread getExecutionThread() { + return execThread; + } + + /** * Return the synchronization object if any. * @see #RunnableTask(Runnable, Object, boolean) */ @@ -126,12 +146,12 @@ public abstract class TaskBase implements Runnable { /** * @return !{@link #isExecuted()} && !{@link #isFlushed()} */ - public final boolean isInQueue() { return 0 != tExecuted && !isFlushed; } + public final boolean isInQueue() { return !isExecuted && !isFlushed; } /** * @return True if executed, otherwise false; */ - public final boolean isExecuted() { return 0 != tExecuted ; } + public final boolean isExecuted() { return isExecuted; } /** * @return True if flushed, otherwise false; @@ -159,7 +179,16 @@ public abstract class TaskBase implements Runnable { @Override public String toString() { - return "RunnableTask[executed "+isExecuted()+", tTotal "+getDurationTotal()+" ms, tExec "+getDurationInExec()+" ms, tQueue "+getDurationInQueue()+" ms, attachment "+attachment+", throwable "+getThrowable()+"]"; + final String etn; + final String eth; + if( null != execThread ) { + etn = execThread.getName(); + eth = "0x"+Integer.toHexString(execThread.hashCode()); + } else { + etn = "n/a"; + eth = "n/a"; + } + return "RunnableTask[enqueued "+isInQueue()+"[executed "+isExecuted()+", flushed "+isFlushed()+", thread["+eth+", "+etn+"]], tTotal "+getDurationTotal()+" ms, tExec "+getDurationInExec()+" ms, tQueue "+getDurationInQueue()+" ms, attachment "+attachment+", throwable "+getThrowable()+"]"; } } diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl b/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl Binary files differnew file mode 100644 index 0000000..d8f1716 --- /dev/null +++ b/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin b/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin Binary files differdeleted file mode 100644 index b0d5f63..0000000 --- a/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin +++ /dev/null diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl b/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl Binary files differnew file mode 100644 index 0000000..be0998b --- /dev/null +++ b/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java index 24f0237..44c7a11 100644 --- a/src/java/com/jogamp/common/util/cache/TempFileCache.java +++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java @@ -35,6 +35,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.InterruptSource; import jogamp.common.Debug; @@ -238,7 +239,7 @@ public class TempFileCache { // Add shutdown hook to cleanup the OutputStream, FileChannel, // and FileLock for the jlnNNNN.lck and jlnNNNN.lck files. // We do this so that the locks never get garbage-collected. - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { /* @Override */ @Override public void run() { @@ -265,7 +266,7 @@ public class TempFileCache { } // Start a new Reaper thread to do stuff... - final Thread reaperThread = new Thread() { + final Thread reaperThread = new InterruptSource.Thread() { /* @Override */ @Override public void run() { diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java index ed69ddc..2ff5140 100644 --- a/src/java/com/jogamp/common/util/cache/TempJarCache.java +++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java @@ -273,19 +273,6 @@ public class TempJarCache { } /** - * See {@link #addResources(Class, Uri)} - * @param certClass - * @param jarURI - * @throws IOException - * @throws SecurityException - * @throws IllegalArgumentException - * @throws URISyntaxException - * @deprecated Use {@link #addResources(Class, Uri)} - */ - public synchronized static final void addResources(final Class<?> certClass, final java.net.URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException { - addResources(certClass, Uri.valueOf(jarURI)); - } - /** * Adds native resources, if not yet added. * * @param certClass if class is certified, the JarFile entries needs to have the same certificate @@ -421,14 +408,6 @@ public class TempJarCache { return null; } - /** - * See {@link #getResourceUri(String)} - * @deprecated Use {@link #getResourceUri(String)} - */ - public synchronized static final java.net.URI getResource(final String name) throws URISyntaxException { - return getResourceUri(name).toURI(); - } - /** Similar to {@link ClassLoader#getResource(String)}. */ public synchronized static final Uri getResourceUri(final String name) throws URISyntaxException { checkInitialized(); diff --git a/src/java/com/jogamp/gluegen/ASTLocusTag.java b/src/java/com/jogamp/gluegen/ASTLocusTag.java new file mode 100644 index 0000000..aea7699 --- /dev/null +++ b/src/java/com/jogamp/gluegen/ASTLocusTag.java @@ -0,0 +1,99 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen; + +/** + * An AST location tag. + */ +public class ASTLocusTag { + /** Source object, might be {@link String}. */ + public final Object source; + /** Line number, {@code -1} if undefined */ + public final int line; + /** Column number, {@code -1} if undefined */ + public final int column; + /** Source text reflecting current location, {@code null} if undefined */ + public final String text; + + public ASTLocusTag(final Object source, final int line, final int column, final String text) { + this.source = source; + this.line = line; + this.column = column; + this.text = text; + } + + public String toString() { + return toString(new StringBuilder(), null, true).toString(); + } + public StringBuilder toString(final StringBuilder sb, final String level, final boolean inclText) { + boolean preCol = false; + if (source != null) { + sb.append(source); + preCol = true; + } + if (line != -1) { + if( preCol ) { + sb.append(":"); + } else { + sb.append("line "); + } + sb.append(line); + if (column != -1) { + sb.append(":" + column); + } + preCol = true; + } + if( null != level && level.length()>0 ) { + if( preCol ) { + sb.append(": "); + } + sb.append(level); + preCol = true; + } + if( inclText && null != text && text.length()>0 ) { + if( preCol ) { + sb.append(": "); + } else { + sb.append("text "); + } + sb.append("'").append(text).append("'"); + } + return sb; + } + + /** + * Interface tag for {@link ASTLocusTag} provider. + */ + public static interface ASTLocusTagProvider { + /** + * Returns this instance's {@link ASTLocusTag}, if available, + * otherwise returns {@code null}. + */ + ASTLocusTag getASTLocusTag(); + } +} diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java index 93a1ecc..7c88c37 100644 --- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -44,20 +44,20 @@ import java.io.*; import java.text.MessageFormat; import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.Logging.LoggerIf; import com.jogamp.gluegen.cgram.types.*; -import java.util.logging.Logger; - /** Emits the C-side component of the Java<->C JNI binding. */ public class CMethodBindingEmitter extends FunctionEmitter { - protected static final Logger LOG = Logger.getLogger(CMethodBindingEmitter.class.getPackage().getName()); protected static final CommentEmitter defaultCommentEmitter = new DefaultCommentEmitter(); protected static final String arrayResLength = "_array_res_length"; protected static final String arrayRes = "_array_res"; protected static final String arrayIdx = "_array_idx"; + protected final LoggerIf LOG; + protected MethodBinding binding; /** Name of the package in which the corresponding Java method resides.*/ @@ -124,9 +124,11 @@ public class CMethodBindingEmitter extends FunctionEmitter { final boolean isJavaMethodStatic, final boolean forImplementingMethodCall, final boolean forIndirectBufferAndArrayImplementation, - final MachineDataInfo machDesc) + final MachineDataInfo machDesc, + final JavaConfiguration configuration) { - super(output, false); + super(output, false, configuration); + LOG = Logging.getLogger(CMethodBindingEmitter.class.getPackage().getName(), CMethodBindingEmitter.class.getSimpleName()); assert(binding != null); assert(javaClassName != null); @@ -148,8 +150,21 @@ public class CMethodBindingEmitter extends FunctionEmitter { public final MethodBinding getBinding() { return binding; } @Override - public String getName() { - return binding.getName(); + public String getInterfaceName() { + return binding.getInterfaceName(); + } + @Override + public String getImplName() { + return binding.getImplName(); + } + @Override + public String getNativeName() { + return binding.getNativeName(); + } + + @Override + public FunctionSymbol getCSymbol() { + return binding.getCSymbol(); } /** @@ -306,12 +321,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.print("_"); if (isOverloadedBinding) { writer.print(jniMangle(binding)); - //System.err.println("OVERLOADED MANGLING FOR " + getName() + - // " = " + jniMangle(binding)); } else { - writer.print(JavaEmitter.jniMangle(getName())); - //System.err.println(" NORMAL MANGLING FOR " + binding.getName() + - // " = " + jniMangle(getName())); + writer.print(JavaEmitter.jniMangle(getImplName())); } } @@ -450,8 +461,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { final JavaType javaReturnType = binding.getJavaReturnType(); if (!cReturnType.isVoid()) { writer.print(" "); - // Note we must respect const/volatile for return argument - writer.print(binding.getCSymbol().getReturnType().getName(true)); + // Note we respect const/volatile in the function return type. + // However, we cannot have it 'const' for our local variable. + // See cast in emitBodyCallCFunction(..)! + writer.print(binding.getCSymbol().getReturnType().getCName(false)); writer.println(" _res;"); if (javaReturnType.isNIOByteBufferArray() || javaReturnType.isArrayOfCompoundTypeWrappers()) { @@ -569,7 +582,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" if ( NULL != " + javaArgName + " ) {"); final Type cArgType = binding.getCArgumentType(i); - String cArgTypeName = cArgType.getName(); + String cArgTypeName = cArgType.getCName(); final String convName = pointerConversionArgumentName(javaArgName); @@ -595,14 +608,14 @@ public class CMethodBindingEmitter extends FunctionEmitter { // // Note that we properly handle only the case of an array of // compound type wrappers in emitBodyVariablePostCallCleanup below - if (!isBaseTypeConst(cArgType) && + if (!cArgType.isBaseTypeConst() && !javaArgType.isArrayOfCompoundTypeWrappers()) { // FIXME: if the arg type is non-const, the sematics might be that // the function modifies the argument -- we don't yet support // this. - throw new RuntimeException( - "Cannot copy data for ptr-to-ptr arg type \"" + cArgType + - "\": support for non-const ptr-to-ptr types not implemented."); + throw new GlueGenException( + "Cannot copy data for ptr-to-ptr arg type \"" + cArgType.getDebugString() + + "\": support for non-const ptr-to-ptr types not implemented: "+binding, binding.getCSymbol().getASTLocusTag()); } writer.println(); @@ -646,16 +659,17 @@ public class CMethodBindingEmitter extends FunctionEmitter { error = 100; } if( 0 < error ) { - throw new RuntimeException( - "Could not copy data for type \"" + cArgType + - "\"; currently only pointer- and array-types are supported. (error "+error+")"); + throw new GlueGenException( + "Could not copy data for type \"" + cArgType.getDebugString() + + "\"; currently only pointer- and array-types are supported. (error "+error+"): "+binding, + binding.getCSymbol().getASTLocusTag()); } } emitMalloc( writer, convName+"_copy", - cArgElementType.getName(), - isBaseTypeConst(cArgType), + cArgElementType.getCName(), + cArgType.isBaseTypeConst(), arrayLenName, "Could not allocate buffer for copying data in argument \\\""+javaArgName+"\\\""); @@ -692,7 +706,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { in the method binding. */ emitGetDirectBufferAddress(writer, "_tmpObj", - cArgElementType.getName(), + cArgElementType.getCName(), convName + "_copy[_copyIndex]", true, "_offsetHandle[_copyIndex]", true); @@ -702,13 +716,14 @@ public class CMethodBindingEmitter extends FunctionEmitter { // offset argument emitGetDirectBufferAddress(writer, "_tmpObj", - cArgElementType.getName(), + cArgElementType.getCName(), "("+convName + "_copy + _copyIndex)", false /* !receivingIsPtrPtr -> linear layout -> use memcpy */, null, true); } else { if( null == cArgElementType2 ) { - throw new RuntimeException("XXX: Type "+cArgType+" not properly handled as ptr-to-ptr"); + throw new GlueGenException("XXX: Type "+cArgType.getDebugString()+" not properly handled as ptr-to-ptr: "+binding, + binding.getCSymbol().getASTLocusTag()); } // Question: do we always need to copy the sub-arrays, or just // GetPrimitiveArrayCritical on each jobjectarray element and @@ -719,14 +734,15 @@ public class CMethodBindingEmitter extends FunctionEmitter { emitMalloc( writer, convName+"_copy[_copyIndex]", - cArgElementType2.getName(), // assumes cArgPtrType is ptr-to-ptr-to-primitive !! - isBaseTypeConst(cArgType), + cArgElementType2.getCName(), // assumes cArgPtrType is ptr-to-ptr-to-primitive !! + cArgType.isBaseTypeConst(), "(*env)->GetArrayLength(env, _tmpObj)", "Could not allocate buffer during copying of data in argument \\\""+javaArgName+"\\\""); // FIXME: copy the data (use matched Get/ReleasePrimitiveArrayCritical() calls) if (true) { - throw new RuntimeException("Cannot yet handle type \"" + cArgType.getName() + - "\"; need to add support for copying ptr-to-ptr-to-primitiveType subarrays"); + throw new GlueGenException("Cannot yet handle type \"" + cArgType.getDebugString() + + "\"; need to add support for copying ptr-to-ptr-to-primitiveType subarrays: "+binding, + binding.getCSymbol().getASTLocusTag()); } } @@ -781,7 +797,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" if ( JNI_FALSE == " + isNIOArgName(i) + " && NULL != " + javaArgName + " ) {"); // Release array - final String modeFlag = isBaseTypeConst(cArgType) ? "JNI_ABORT" : "0" ; + final String modeFlag = cArgType.isBaseTypeConst() ? "JNI_ABORT" : "0" ; writer.print(" (*env)->ReleasePrimitiveArrayCritical(env, " + javaArgName + ", " + convName + ", "+modeFlag+");"); } else { writer.println(" if ( NULL != " + javaArgName + " ) {"); @@ -792,7 +808,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { // // FIXME: should factor out this whole block of code into a separate // method for clarity and maintenance purposes - if (!isBaseTypeConst(cArgType)) { + if (!cArgType.isBaseTypeConst()) { // FIXME: handle any cleanup from treatment of non-const args, // assuming they were treated differently in // emitBodyVariablePreCallSetup() (see the similar section in that @@ -804,15 +820,16 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" _tmpObj = (*env)->GetObjectArrayElement(env, " + javaArgName + ", _copyIndex);"); emitReturnDirectBufferAddress(writer, "_tmpObj", - cArgType.asArray().getBaseElementType().getName(), + cArgType.asArray().getBaseElementType().getCName(), "("+convName + "_copy + _copyIndex)", false /* receivingIsPtrPtr */, null); writer.println(" }"); } else { - throw new RuntimeException( - "Cannot clean up copied data for ptr-to-ptr arg type \"" + cArgType + - "\": support for cleaning up most non-const ptr-to-ptr types not implemented."); + throw new GlueGenException( + "Cannot clean up copied data for ptr-to-ptr arg type \"" + cArgType.getDebugString() + + "\": support for cleaning up most non-const ptr-to-ptr types not implemented.", + binding.getCSymbol().getASTLocusTag()); } } @@ -833,9 +850,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { // free each element final PointerType cArgPtrType = cArgType.asPointer(); if (cArgPtrType == null) { - throw new RuntimeException( - "Could not copy data for type \"" + cArgType + - "\"; currently only pointer types supported."); + throw new GlueGenException( + "Could not copy data for type \"" + cArgType.getDebugString() + + "\"; currently only pointer types supported.", + binding.getCSymbol().getASTLocusTag()); } // process each element in the array @@ -854,9 +872,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.print(convName+"_copy[_copyIndex]"); writer.println(");"); } else { - if (true) throw new RuntimeException( - "Cannot yet handle type \"" + cArgType.getName() + - "\"; need to add support for cleaning up copied ptr-to-ptr-to-primitiveType subarrays"); + throw new GlueGenException( + "Cannot yet handle type \"" + cArgType.getDebugString() + + "\"; need to add support for cleaning up copied ptr-to-ptr-to-primitiveType subarrays", + binding.getCSymbol().getASTLocusTag()); } writer.println(" }"); } @@ -915,20 +934,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { javaArgType.isArray() || javaArgType.isArrayOfCompoundTypeWrappers() || ( javaArgType.isNIOBuffer() && forIndirectBufferAndArrayImplementation ) ); - if (isBaseTypeConst(cArgType)) { - writer.print("const "); - } - - // if this is a pointer to an unsigned type, add unsigned to the name to avoid compiler warnings - if(cArgType.isPointer()) { - final Type baseType = cArgType.getBaseElementType(); - if(baseType.isInt() && (((IntType)baseType).isPrimitiveUnsigned())) { - writer.print("unsigned "); - } - } - - writer.print(cArgType.getName()); + writer.print(cArgType.getCName(true)); writer.print(") "); + if (cArgType.isPointer() && javaArgType.isPrimitive()) { writer.print("(intptr_t) "); } @@ -975,13 +983,18 @@ public class CMethodBindingEmitter extends FunctionEmitter { final Type cReturnType = binding.getCReturnType(); if (!cReturnType.isVoid()) { - writer.print("_res = "); + // Note we respect const/volatile in the function return type. + // However, we cannot have it 'const' for our local variable. + // See return type in emitBodyVariableDeclarations(..)! + writer.print("_res = ("); + writer.print(cReturnType.getCName(false)); + writer.print(") "); } if ( isCStructFunctionPointer && binding.hasContainingType() ) { // Call through function pointer writer.print(CMethodBindingEmitter.cThisArgumentName() + "->"); } - writer.print(binding.getCSymbol().getName()); + writer.print(getNativeName()); writer.print("("); emitBodyPassCArguments(writer); writer.println(");"); @@ -1020,7 +1033,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { if (returnValueCapacityExpression != null) { returnSizeOf = returnValueCapacityExpression.format(argumentNameArray()); } else { - returnSizeOf = "sizeof(" + cReturnType.getName() + ")"; + returnSizeOf = "sizeof(" + cReturnType.getCName() + ")"; } writer.println(" return JVMUtil_NewDirectByteBufferCopy(env, &_res, "+returnSizeOf+");"); } else if (javaReturnType.isNIOBuffer() || javaReturnType.isCompoundTypeWrapper()) { @@ -1029,36 +1042,76 @@ public class CMethodBindingEmitter extends FunctionEmitter { // See whether capacity has been specified if (returnValueCapacityExpression != null) { - writer.print( returnValueCapacityExpression.format( argumentNameArray() ) ); + writer.println( returnValueCapacityExpression.format( argumentNameArray() ) + ");"); } else { - if (cReturnType.isPointer() && - cReturnType.asPointer().getTargetType().isCompound()) { - if (cReturnType.asPointer().getTargetType().getSize() == null) { - throw new RuntimeException( - "Error emitting code for compound return type "+ - "for function \"" + binding + "\": " + - "Structs to be emitted should have been laid out by this point " + - "(type " + cReturnType.asPointer().getTargetType().getName() + " / " + - cReturnType.asPointer().getTargetType() + " was not) for "+binding - ); + final Type cReturnTargetType = cReturnType.isPointer() ? cReturnType.getTargetType() : null; + int mode = 0; + if ( 1 == cReturnType.pointerDepth() && null != cReturnTargetType ) { + if( cReturnTargetType.isCompound() ) { + if( !cReturnTargetType.isAnon() && + cReturnTargetType.asCompound().getNumFields() > 0 ) + { + // fully declared non-anonymous struct pointer: pass content + if ( cReturnTargetType.getSize() == null ) { + throw new GlueGenException( + "Error emitting code for compound return type "+ + "for function \"" + binding + "\": " + + "Structs to be emitted should have been laid out by this point " + + "(type " + cReturnTargetType.getCName() + " / " + + cReturnTargetType.getDebugString() + " was not) for "+binding.getCSymbol(), + binding.getCSymbol().getASTLocusTag() + ); + } + writer.println("sizeof(" + cReturnTargetType.getCName() + ") );"); + mode = 10; + } else if( cReturnTargetType.asCompound().getNumFields() == 0 ) { + // anonymous struct pointer: pass pointer + writer.println("sizeof(" + cReturnType.getCName() + ") );"); + mode = 11; + } + } + if( 0 == mode ) { + if( cReturnTargetType.isPrimitive() ) { + // primitive pointer: pass primitive + writer.println("sizeof(" + cReturnTargetType.getCName() + ") );"); + mode = 20; + } else if( cReturnTargetType.isVoid() ) { + // void pointer: pass pointer + writer.println("sizeof(" + cReturnType.getCName() + ") );"); + mode = 21; + } + } + } + if( 0 == mode ) { + if( null != cfg.typeInfo(cReturnType) ) { // javaReturnType.isOpaqued() covered above via isPrimitive() + // Opaque + writer.println("sizeof(" + cReturnType.getCName() + ") );"); + mode = 88; + } else { + final String wmsg = "Assumed return size of equivalent C return type"; + writer.println("sizeof(" + cReturnType.getCName() + ") ); // WARNING: "+wmsg); + mode = 99; + LOG.warning(binding.getCSymbol().getASTLocusTag(), + "No capacity specified for java.nio.Buffer return " + + "value for function \"" + binding.getName() + "\". " + wmsg + " (sizeof(" + cReturnType.getCName() + ")): " + binding); } } - writer.print("sizeof(" + cReturnType.getName() + ")"); - LOG.warning( - "No capacity specified for java.nio.Buffer return " + - "value for function \"" + binding.getName() + "\"" + - " assuming size of equivalent C return type (sizeof(" + cReturnType.getName() + ")): " + binding); + writer.println(" /** "); + writer.println(" * mode: "+mode); + writer.println(" * cReturnType: "+cReturnType.getDebugString()); + writer.println(" * cReturnTargetType: "+cReturnTargetType.getDebugString()); + writer.println(" * javaReturnType: "+javaReturnType.getDebugString()); + writer.println(" */"); } - writer.println(");"); } else if (javaReturnType.isString()) { writer.println(" if (NULL == _res) return NULL;"); - writer.println(" return (*env)->NewStringUTF(env, _res);"); + writer.println(" return (*env)->NewStringUTF(env, (const char *)_res);"); } else if (javaReturnType.isArrayOfCompoundTypeWrappers() || (javaReturnType.isArray() && javaReturnType.isNIOByteBufferArray())) { writer.println(" if (NULL == _res) return NULL;"); if (returnValueLengthExpression == null) { - throw new RuntimeException("Error while generating C code: no length specified for array returned from function " + - binding); + throw new GlueGenException("Error while generating C code: no length specified for array returned from function " + + binding, binding.getCSymbol().getASTLocusTag()); } writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNameArray()) + ";"); writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); @@ -1071,7 +1124,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { pointerType = retType.asArray().getBaseElementType(); } writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + - ", (*env)->NewDirectByteBuffer(env, (void *)_res[" + arrayIdx + "], sizeof(" + pointerType.getName() + ")));"); + ", (*env)->NewDirectByteBuffer(env, (void *)_res[" + arrayIdx + "], sizeof(" + pointerType.getCName() + ")));"); writer.println(" }"); writer.println(" return " + arrayRes + ";"); } else if (javaReturnType.isArray()) { @@ -1080,9 +1133,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { // expression which computes the array size (already present // as ReturnValueCapacity, not yet implemented / tested here) - throw new RuntimeException( + throw new GlueGenException( "Could not emit native code for function \"" + binding + - "\": array return values for non-char types not implemented yet, for "+binding); + "\": array return values for non-char types not implemented yet, for "+binding, + binding.getCSymbol().getASTLocusTag()); // FIXME: This is approximately what will be required here // @@ -1104,8 +1158,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { //writer.print(arrayRes); //writer.println(";"); } else { - System.err.print("Unhandled return type: "+javaReturnType.getDebugString()); - throw new RuntimeException("Unhandled return type: "+javaReturnType.getDebugString()+" for "+binding); + throw new GlueGenException("Unhandled return type: "+javaReturnType.getDebugString()+" for "+binding, + binding.getCSymbol().getReturnType().getASTLocusTag()); } } } @@ -1116,7 +1170,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { protected String jniMangle(final MethodBinding binding) { final StringBuilder buf = new StringBuilder(); - buf.append(JavaEmitter.jniMangle(getName())); + buf.append(JavaEmitter.jniMangle(getImplName())); buf.append(getImplSuffix()); buf.append("__"); if (binding.hasContainingType()) { @@ -1132,7 +1186,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { // We should only see "void" as the first argument of a 1-argument function // FIXME: should normalize this in the parser if ((i != 0) || (binding.getNumArguments() > 1)) { - throw new RuntimeException("Saw illegal \"void\" argument while emitting \"" + getName() + "\""); + throw new GlueGenException("Saw illegal \"void\" argument while emitting arg "+i+" of "+binding, + binding.getCArgumentType(i).getASTLocusTag()); } } else { Class<?> c = type.getJavaClass(); @@ -1164,7 +1219,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { // These are not exposed at the Java level } else { // FIXME: add support for char* -> String conversion - throw new RuntimeException("Unknown kind of JavaType: name="+type.getName()); + throw new GlueGenException("Unknown kind of JavaType: arg "+i+", name="+type.getName()+" of "+binding, + binding.getCArgumentType(i).getASTLocusTag()); } } } @@ -1225,7 +1281,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/OutOfMemoryError\"),"); writer.print(" \"" + errorMessage); writer.print(" in native dispatcher for \\\""); - writer.print(getName()); + writer.print(getInterfaceName()); writer.println("\\\"\");"); writer.print(" return"); if (!binding.getJavaReturnType().isVoid()) { @@ -1386,33 +1442,34 @@ public class CMethodBindingEmitter extends FunctionEmitter { // Note that we don't need to obey const/volatile for outgoing arguments // if (javaType.isNIOBuffer()) { - ptrTypeString = cType.getName(); + // primitive NIO object + ptrTypeString = cType.getCName(); } else if (javaType.isArray() || javaType.isArrayOfCompoundTypeWrappers()) { needsDataCopy = javaArgTypeNeedsDataCopy(javaType); if (javaType.isPrimitiveArray() || javaType.isNIOBufferArray() || javaType.isArrayOfCompoundTypeWrappers()) { - ptrTypeString = cType.getName(); + ptrTypeString = cType.getCName(); } else if (!javaType.isStringArray()) { final Class<?> elementType = javaType.getJavaClass().getComponentType(); if (elementType.isArray()) { final Class<?> subElementType = elementType.getComponentType(); if (subElementType.isPrimitive()) { // type is pointer to pointer to primitive - ptrTypeString = cType.getName(); + ptrTypeString = cType.getCName(); } else { // type is pointer to pointer of some type we don't support (maybe // it's an array of pointers to structs?) - throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); + throw new GlueGenException("Unsupported pointer type: \"" + cType.getDebugString() + "\"", cType.getASTLocusTag()); } } else { // type is pointer to pointer of some type we don't support (maybe // it's an array of pointers to structs?) - throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); + throw new GlueGenException("Unsupported pointer type: \"" + cType.getDebugString() + "\"", cType.getASTLocusTag()); } } } else { - ptrTypeString = cType.getName(); + ptrTypeString = cType.getCName(); } writer.print(" "); @@ -1434,14 +1491,14 @@ public class CMethodBindingEmitter extends FunctionEmitter { String cElementTypeName = "char *"; final PointerType cPtrType = cType.asPointer(); if (cPtrType != null) { - cElementTypeName = cPtrType.getTargetType().asPointer().getName(); + cElementTypeName = cPtrType.getTargetType().asPointer().getCName(); } - if (isBaseTypeConst(cType)) { + if (cType.isBaseTypeConst()) { writer.print("const "); } writer.print(cElementTypeName+" *"); } else { - if (isBaseTypeConst(cType)) { + if (cType.isBaseTypeConst()) { writer.print("const "); } writer.print(ptrTypeString); @@ -1470,9 +1527,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { final String cVariableType; if( !cType.isPointer() && type.isCompoundTypeWrapper() ) { // FIXME: Compound call-by-value - cVariableType = cType.getName()+" *"; + cVariableType = cType.getCName()+" *"; } else { - cVariableType = cType.getName(); + cVariableType = cType.getCName(); } emitGetDirectBufferAddress(writer, incomingArgumentName, diff --git a/src/java/com/jogamp/gluegen/ConstantDefinition.java b/src/java/com/jogamp/gluegen/ConstantDefinition.java index ca67001..675c6d7 100644 --- a/src/java/com/jogamp/gluegen/ConstantDefinition.java +++ b/src/java/com/jogamp/gluegen/ConstantDefinition.java @@ -1,71 +1,481 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. +/** + * Copyright 2015 JogAmp Community. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. */ - package com.jogamp.gluegen; -import java.util.*; +import java.math.BigInteger; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.cgram.types.AliasedSymbol.AliasedSymbolImpl; +import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; + +/** + * Represents a [native] constant expression, + * comprises the [native] expression, see {@link #getNativeExpr()} + * and the optional {@link CNumber} representation, see {@link #getNumber()}. + * <p> + * The representation of the equivalent java expression including + * the result type is covered by {@link JavaExpr}, + * which can be computed via {@link #computeJavaExpr(Map)}. + * </p> + * <p> + * This class and its sub-classes define and convert all native expressions + * to Java space. + * </p> + */ +public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSemanticSymbol, ASTLocusTagProvider { + public static final long UNSIGNED_INT_MAX_VALUE = 0xffffffffL; + public static final BigInteger UNSIGNED_LONG_MAX_VALUE = new BigInteger("ffffffffffffffff", 16); + + /** + * A Number, either integer, optionally [long, unsigned], + * or floating point, optionally [double]. + */ + public static class CNumber { + /** + * {@code true} if number is integer and value stored in {@link #i}, + * otherwise {@code false} for floating point and value stored in {@link #f}. + */ + public final boolean isInteger; + /** {@code true} if number is a {@code long} {@link #isInteger}. */ + public final boolean isLong; + /** {@code true} if number is an {@code unsigned} {@link #isInteger}. */ + public final boolean isUnsigned; + /** The value if {@link #isInteger} */ + public final long i; + + /** {@code true} if number is a {@code double precision} {@code floating point}, i.e. !{@link #isInteger}. */ + public final boolean isDouble; + /** The value if !{@link #isInteger} */ + public final double f; + + /** ctor for integer number */ + public CNumber(final boolean isLong, final boolean isUnsigned, final long value) { + this.isInteger = true; + this.isLong = isLong; + this.isUnsigned = isUnsigned; + this.i = value; + this.isDouble = false; + this.f = 0.0; + } + /** ctor for floating point number */ + public CNumber(final boolean isDouble, final double value) { + this.isInteger = false; + this.isLong = false; + this.isUnsigned = false; + this.i = 0; + this.isDouble = isDouble; + this.f = value; + } + @Override + public int hashCode() { + return isInteger ? Long.valueOf(i).hashCode() : Double.valueOf(f).hashCode(); + } + @Override + public boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof CNumber) ) { + return false; + } + final CNumber t = (CNumber) arg; + return isInteger == t.isInteger && + ( isInteger ? i == t.i : f == t.f ); + } + public final String toJavaString() { + if( isInteger ) { + if( i >= 0 || isUnsigned ) { + if( isLong ) { + return "0x"+Long.toHexString(i)+"L"; + } else { + return "0x"+Integer.toHexString((int)i); + } + } else { + if( isLong ) { + return String.valueOf(i)+"L"; + } else { + return String.valueOf((int)i); + } + } + } else { + return String.valueOf(f) + ( !isDouble ? "f" : ""); + } + } + public final String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("["); + if( isInteger ) { + if( isUnsigned ) { + sb.append("unsigned "); + } + if( isLong) { + sb.append("long: "); + } else { + sb.append("int: "); + } + sb.append(i); + } else { + if( isDouble ) { + sb.append("double: "); + } else { + sb.append("float: "); + } + sb.append(f); + } + sb.append("]"); + return sb.toString(); + } + } + + /** + * A valid java expression, including its result type, + * usually generated from a native [C] expression, + * see {@link JavaExpr#create(ConstantDefinition)}. + */ + public static class JavaExpr { + public final String javaExpression; + public final CNumber resultType; + public final Number resultJavaType; + public final String resultJavaTypeName; + public JavaExpr(final String javaExpression, final CNumber resultType) { + this.javaExpression = javaExpression; + this.resultType = resultType; + if( resultType.isDouble ) { + resultJavaTypeName = "double"; + resultJavaType = Double.valueOf(resultType.f); + } else if( !resultType.isInteger ) { + resultJavaTypeName = "float"; + resultJavaType = Double.valueOf(resultType.f).floatValue(); + } else if( resultType.isLong ) { + resultJavaTypeName = "long"; + resultJavaType = Long.valueOf(resultType.i); + } else /* if( resultType.isInteger ) */ { + resultJavaTypeName = "int"; + resultJavaType = Long.valueOf(resultType.i).intValue(); + } + } + /** + * Computes a valid {@link JavaExpr java expression} based on the given {@link ConstantDefinition}, + * which may either be a single {@link CNumber}, see {@link ConstantDefinition#getNumber()}, + * or represents a native expression, see {@link ConstantDefinition#getExpr()}. + */ + public static JavaExpr compute(final ConstantDefinition constDef, + final Map<String, ConstantDefinition.JavaExpr> constMap) { + final boolean debug = GlueGen.debug(); + if( debug ) { + System.err.println("ConstJavaExpr.create: "+constDef); + } + if( constDef.hasNumber() ) { + // Already parsed as CNumber completely! + if( debug ) { + System.err.printf("V %s (isCNumber)%n", constDef); + } + return new JavaExpr(constDef.getNumber().toJavaString(), constDef.getNumber()); + } + final StringBuilder javaExpr = new StringBuilder(); + final String nativeExpr = constDef.getNativeExpr(); -/** Represents the definition of a constant which was provided either - via a #define statement or through an enum definition. */ -public class ConstantDefinition { + // "calculates" the result type of a simple expression + // example: (2+3)-(2.0f-3.0) -> Double + // example: (1 << 2) -> Integer + CNumber resultType = null; + final Matcher matcher = patternCPPOperand.matcher(nativeExpr); + int preStartIdx = 0; + int opEndIdx = 0; + while ( matcher.find() ) { + final int opStartIdx = matcher.start(); + if( opStartIdx > preStartIdx ) { + final String sValue = nativeExpr.substring(preStartIdx, opStartIdx).trim(); + if( sValue.length() > 0 ) { + if( debug ) { + System.err.printf("V %03d-%03d: %s%n", preStartIdx, opStartIdx, sValue); + } + resultType = processValue(constDef, sValue, constMap, resultType, javaExpr); + javaExpr.append(" "); + } + } + opEndIdx = matcher.end(); + final String op = nativeExpr.substring(opStartIdx, opEndIdx); + if( debug ) { + System.err.printf("O %03d-%03d: %s%n", opStartIdx, opEndIdx, op); + } + javaExpr.append(op).append(" "); + preStartIdx = opEndIdx; + } + if( opEndIdx < nativeExpr.length() ) { + // tail .. + final String sValue = nativeExpr.substring(opEndIdx).trim(); + if( sValue.length() > 0 ) { + if( debug ) { + System.err.printf("V %03d %03d-%03d: %s (tail)%n", preStartIdx, opEndIdx, nativeExpr.length(), sValue); + } + resultType = processValue(constDef, sValue, constMap, resultType, javaExpr); + } + } + final String javaExprS = javaExpr.toString().trim(); + if( null == resultType ) { + throw new GlueGenException("Cannot emit const \""+constDef.getName()+"\": value \""+nativeExpr+ + "\", parsed \""+javaExprS+"\" does not contain a constant number", constDef.getASTLocusTag()); + } + return new JavaExpr(javaExprS, resultType); + } + private static CNumber processValue(final ConstantDefinition constDef, + final String sValue, + final Map<String, ConstantDefinition.JavaExpr> constMap, + CNumber resultType, + final StringBuilder javaExpr) { + final CNumber nValue = getANumber(constDef, sValue); + if( null != nValue ) { + resultType = evalType(resultType , nValue); + javaExpr.append(nValue.toJavaString()); + } else { + // Lookup CNumber type in const-map, to evaluate this result type + final JavaExpr cje = constMap.get(sValue); + if( null != cje ) { + resultType = evalType(resultType , cje.resultType); + } + javaExpr.append(sValue); + } + return resultType; + } + private static CNumber getANumber(final ConstantDefinition constDef, final String value) { + try { + final CNumber number = decodeANumber(value); + if( null != number ) { + return number; + } + } catch( final Throwable _t ) { + final String msg = "Cannot emit const \""+constDef.getName()+"\": value \""+value+ + "\" cannot be assigned to a int, long, float, or double"; + throw new GlueGenException(msg, constDef.getASTLocusTag(), _t); + } + return null; + } + private static CNumber evalType(final CNumber resultType, final CNumber type) { + //fast path + if( type.isDouble ) { + return type; + } + if( null != resultType ) { + if( resultType.isInteger ) { + if( resultType.isLong ) { + /* resultType is Long */ + if( !type.isInteger ) { + /* resultType: Long -> [ Float || Double ] */ + return type; + } + } else if( type.isLong || !type.isInteger ) { + /* resultType: Integer -> [ Long || Float || Double ] */ + return type; + } + } else if( !resultType.isInteger && !resultType.isDouble ) { + if( type.isDouble ) { + /* resultType: Float -> Double */ + return type; + } + } + } else { + return type; + } + return resultType; + } + } - private final String origName; - private final HashSet<String> aliasedNames; - private String name; - private final String value; + private final boolean relaxedEqSem; + private final String nativeExpr; + private final CNumber number; private final boolean isEnum; private final String enumName; - private Set<String> aliases; + private final ASTLocusTag astLocus; + /** + * Constructor for plain const-values, non-enumerates. + * @param name unique name of this constant expression + * @param nativeExpr original [native] expression + * @param number optional {@link CNumber} representing this constant. + * If {@code null}, implementation attempts to derive a {@link CNumber} + * of the given {@code nativeExpr}. + * @param astLocus AST location of the represented constant. + */ + public ConstantDefinition(final String name, + final String nativeExpr, + final CNumber number, + final ASTLocusTag astLocus) { + this(name, nativeExpr, number, false, null, astLocus); + } + /** + * Constructor for enumerates + * @param name unique name of this constant expression + * @param nativeExpr original [native] expression + * @param number optional {@link CNumber} representing this constant. + * If {@code null}, implementation attempts to derive a {@link CNumber} + * of the given {@code nativeExpr}. + * @param enumName optional name of the represented enumeration + * @param astLocus AST location of the represented constant. + */ public ConstantDefinition(final String name, - final String value, - final boolean isEnum, - final String enumName) { - this.origName = name; - this.name = name; - this.value = value; + final String nativeExpr, + final CNumber number, + final String enumName, final ASTLocusTag astLocus) { + this(name, nativeExpr, number, true, enumName, astLocus); + } + /** + * @param name unique name of this constant expression + * @param nativeExpr original [native] expression + * @param number optional {@link CNumber} representing this constant. + * If {@code null}, implementation attempts to derive a {@link CNumber} + * of the given {@code nativeExpr}. + * @param isEnum {@code true} if this constant is an enumerate, otherwise {@code false}. + * @param enumName optional name of the represented enumeration + * @param astLocus AST location of the represented constant. + */ + private ConstantDefinition(final String name, + final String nativeExpr, + final CNumber number, + final boolean isEnum, final String enumName, final ASTLocusTag astLocus) { + super(name); + this.nativeExpr = nativeExpr; + this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest(); + if( null != number ) { + this.number = number; + } else { + // Attempt to parse define string as number + final CNumber iNum = decodeIntegerNumber(nativeExpr); + if( null != iNum ) { + this.number = iNum; + } else { + final CNumber fNum = decodeDecimalNumber(nativeExpr); + if( null != fNum ) { + this.number = fNum; + } else { + this.number = null; + } + } + } this.isEnum = isEnum; this.enumName = enumName; - this.aliasedNames=new HashSet<String>(); + this.astLocus = astLocus; } - public boolean equals(final ConstantDefinition other) { - return (equals(name, other.name) && - equals(value, other.value) && - equals(enumName, other.enumName)); + @Override + public ASTLocusTag getASTLocusTag() { return astLocus; } + + /** + * Hash by its given {@link #getName() name}. + */ + @Override + public final int hashCode() { + return getName().hashCode(); } - private boolean equals(final String s1, final String s2) { + /** + * Equality test by its given {@link #getName() name}. + */ + @Override + public final boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof ConstantDefinition) ) { + return false; + } else { + final ConstantDefinition t = (ConstantDefinition)arg; + return equals(getName(), t.getName()); + } + } + + @Override + public final int hashCodeSemantics() { + // 31 * x == (x << 5) - x + int hash = 31 + ( null != getName() ? getName().hashCode() : 0 ); + hash = ((hash << 5) - hash) + ( isEnum ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( null != enumName ? enumName.hashCode() : 0 ); + hash = ((hash << 5) - hash) + ( null != number ? number.hashCode() : 0 ); + return ((hash << 5) - hash) + ( !relaxedEqSem && null != nativeExpr ? nativeExpr.hashCode() : 0 ); + } + + @Override + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof ConstantDefinition) ) { + return false; + } else { + final ConstantDefinition t = (ConstantDefinition) arg; + if( !equals(getName(), t.getName()) || + isEnum != t.isEnum || + !equals(enumName, t.enumName) ) { + return false; + } + if( null != number ) { + if( number.isInteger ) { + return number.i == t.number.i; + } else { + return number.f == t.number.f; + } + } else { + // define's string value may be semantical equal .. but formatted differently! + return relaxedEqSem || equals(nativeExpr, t.nativeExpr); + } + } + } + + /** Returns the original [native] expression. */ + public String getNativeExpr() { return nativeExpr; } + /** + * Returns the parsed {@link CNumber} of the {@link #getNativeExpr() native expression}, + * or {@code null} if the latter does not comprise a single number, + * i.e. is a complex expression. + */ + public CNumber getNumber() { return number; } + /** + * Returns {@code true} if this instance represents has a {@link #getNumber() number}, + * otherwise {@code false}. + */ + public boolean hasNumber() { return null != number; } + + /** Returns {@code null} if this definition was not part of an + enumeration, or if the enumeration is anonymous. */ + public String getEnumName() { return enumName; } + + public boolean isEnum() { return isEnum; } + + @Override + public String toString() { + return "ConstantDefinition [name \"" + getName() + + "\", expression \"" + nativeExpr + + "\", number "+number + + "], enum[is " + isEnum + ", name \"" + enumName + "\"]]"; + } + + private static boolean equals(final String s1, final String s2) { if (s1 == null || s2 == null) { if (s1 == null && s2 == null) { return true; @@ -76,57 +486,469 @@ public class ConstantDefinition { return s1.equals(s2); } - @Override - public int hashCode() { - return name.hashCode(); + /** + * Computes the {@link JavaExpr java expression} based on this instance, + * see {@link JavaExpr#create(ConstantDefinition)}. + */ + public final JavaExpr computeJavaExpr(final Map<String, ConstantDefinition.JavaExpr> constMap) { + return JavaExpr.compute(this, constMap); } - /** Supports renaming in Java binding. */ - public void rename(final String name) { - if(null!=name) { - this.name = name; - aliasedNames.add(origName); - } + // + // Static utility functions for type detection + // + + public static boolean isConstantExpression(final String value) { + if( null != value && value.length() > 0 ) { + // Single numeric value + if ( isNumber(value) ) { + return true; + } + // Find constant expressions like (1 << 3) + // if found just pass them through, they will most likely work in java too + // expressions containing identifiers are currently ignored (casts too) + final String[] values = value.split("[\\s\\(\\)]"); // [ whitespace '(' ')' ] + int numberCount = 0; + for (final String s : values) { + if( s.length() > 0 ) { + if( isCPPOperand(s) ) { + // OK + } else if ( isNumber(s) ) { + // OK + numberCount++; + } else { + return false; + } + } + } + final boolean res = numberCount > 0; + return res; + } + return false; + } + + public static boolean isIdentifier(final String value) { + boolean identifier = false; + + final char[] chars = value.toCharArray(); + + for (int i = 0; i < chars.length; i++) { + final char c = chars[i]; + if (i == 0) { + if (Character.isJavaIdentifierStart(c)) { + identifier = true; + } + } else { + if (!Character.isJavaIdentifierPart(c)) { + identifier = false; + break; + } + } + } + return identifier; + } + + /** + * Returns either {@link #decodeIntegerNumber(String)}, + * {@link #decodeDecimalNumber(String)} or {@code null}. + * @param v + */ + public static CNumber decodeANumber(final String v) { + final CNumber iNumber = ConstantDefinition.decodeIntegerNumber(v); + if( null != iNumber ) { + return iNumber; + } + return ConstantDefinition.decodeDecimalNumber(v); } - public void addAliasedName(final String name) { - aliasedNames.add(name); + /** + * If the given string {@link #isIntegerNumber(String)}, + * return the decoded integer value, represented as a {@code ANumber}, + * otherwise returns {@code null}. + * <p> + * Method strips off sign prefix {@code +} + * and integer modifier suffixes {@code [uUlL]} + * before utilizing {@link Long#decode(String)}. + * </p> + * @param v + */ + public static CNumber decodeIntegerNumber(final String v) { + if( null == v || !isIntegerNumber(v) ) { + return null; + } + String s0 = v.trim(); + if( 0 == s0.length() ) { + return null; + } + if (s0.startsWith("+")) { + s0 = s0.substring(1, s0.length()).trim(); + if( 0 == s0.length() ) { + return null; + } + } + final boolean neg; + if (s0.startsWith("-")) { + s0 = s0.substring(1, s0.length()).trim(); + if( 0 == s0.length() ) { + return null; + } + neg = true; + } else { + neg = false; + } + + // Test last two chars for [lL] and [uU] modifiers! + boolean isUnsigned = false; + boolean isLong = false; + final int j = s0.length() - 2; + for(int i = s0.length() - 1; i >= 0 && i >= j; i--) { + final char lastChar = s0.charAt(s0.length()-1); + if( lastChar == 'u' || lastChar == 'U' ) { + s0 = s0.substring(0, s0.length()-1); + isUnsigned = true; + } else if( lastChar == 'l' || lastChar == 'L' ) { + s0 = s0.substring(0, s0.length()-1); + isLong = true; + } else { + // early out, no modifier match! + break; + } + } + if( 0 == s0.length() ) { + return null; + } + final long res; + if( isLong && isUnsigned ) { + res = decodeULong(s0, neg); + } else { + if( neg ) { + s0 = "-" + s0; + } + res = Long.decode(s0).longValue(); + } + final boolean isLong2 = isLong || + ( !isUnsigned && ( Integer.MIN_VALUE > res || res > Integer.MAX_VALUE ) ) || + ( isUnsigned && res > UNSIGNED_INT_MAX_VALUE ); + return new CNumber(isLong2, isUnsigned, res); } - public Collection<String> getAliasedNames() { - return aliasedNames; + private static long decodeULong(final String v, final boolean neg) throws NumberFormatException { + final int radix; + final int idx; + if (v.startsWith("0x") || v.startsWith("0X")) { + idx = 2; + radix = 16; + } else if (v.startsWith("#")) { + idx = 1; + radix = 16; + } else if (v.startsWith("0") && v.length() > 1) { + idx = 1; + radix = 8; + } else { + idx = 0; + radix = 10; + } + final String s0 = ( neg ? "-" : "" ) + v.substring(idx); + final BigInteger res = new BigInteger(s0, radix); + if( res.compareTo(UNSIGNED_LONG_MAX_VALUE) > 0 ) { + throw new NumberFormatException("Value \""+v+"\" is > UNSIGNED_LONG_MAX"); + } + return res.longValue(); } - public String getOrigName() { - return origName; + /** + * If the given string {@link #isDecimalNumber(String)}, + * return the decoded floating-point value, represented as a {@code ANumber} object, + * otherwise returns {@code null}. + * <p> + * Method utilizes {@link Double#valueOf(String)}. + * </p> + * @param v + * @param isDouble return value for {@code double} flag + */ + public static CNumber decodeDecimalNumber(final String v) { + if( null == v || !isDecimalNumber(v) ) { + return null; + } + final String s0 = v.trim(); + if( 0 == s0.length() ) { + return null; + } + boolean _isDouble = false; + final char lastChar = s0.charAt(s0.length()-1); + if( lastChar == 'd' || lastChar == 'D' ) { + _isDouble = true; + } + final double res = Double.valueOf(s0).doubleValue(); + final double ares = Math.abs(res); + return new CNumber(_isDouble || Float.MIN_VALUE > ares || ares > Float.MAX_VALUE, res); } - public String getName() { - return name; + /** + * Matches {@link #isHexNumber(String)} or {@link #isDecimalOrIntNumber(String)}. + */ + public static boolean isNumber(final String s) { + if( isHexNumber(s) ) { + return true; + } else { + return isDecimalOrIntNumber(s); + } } - public String getValue() { return value; } - /** Returns null if this definition was not part of an - enumeration, or if the enum was anonymous. */ - public String getEnumName() { return enumName; } + /** + * Matches {@link #isHexNumber(String)} or {@link #patternIntegerNumber}. + */ + public static boolean isIntegerNumber(final String s) { + if( isHexNumber(s) ) { + return true; + } else { + return patternIntegerNumber.matcher(s).matches(); + } + } - public boolean isEnum() { return isEnum; } + /** + * Matches {@link #patternHexNumber}. + */ + public static boolean isHexNumber(final String s) { + return patternHexNumber.matcher(s).matches(); + } - public Set<String> getAliases() { - return aliases; + /** + * Matches pattern for <code>floating point</code> number, + * compatible and described in {@link Double#valueOf(String)}. + */ + public static boolean isDecimalNumber(final String s) { + return patternDecimalNumber.matcher(s).matches(); } - public void addAlias(final String alias) { - if (aliases == null) { - aliases = new LinkedHashSet<String>(); - } - aliases.add(alias); + /** + * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number, + * covering {@link #patternDecimalNumber} <i>and</i> {@link #patternIntegerNumber}. + */ + public static boolean isDecimalOrIntNumber(final String s) { + return patternDecimalOrIntNumber.matcher(s).matches(); } - @Override - public String toString() { - return "ConstantDefinition [name " + name + " origName " + origName + " value " + value - + " aliasedNames " + aliasedNames + " aliases " + aliases - + " enumName " + enumName + " isEnum " + isEnum + "]"; + /** + * Matches pattern for valid CPP operands, see {@link #patternCPPOperand}. + */ + public static boolean isCPPOperand(final String s) { + return patternCPPOperand.matcher(s).matches(); } + /** + * Complete pattern for <code>hexadecimal</code> number, + * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}. + */ + public static Pattern patternHexNumber; + + /** + * Complete pattern for <code>floating point</code> number, + * compatible and described in {@link Double#valueOf(String)}. + */ + public final static Pattern patternDecimalNumber; + + /** + * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number, + * covering {@link #patternDecimalNumber} <i>and</i> {@link #patternIntegerNumber}. + */ + public final static Pattern patternDecimalOrIntNumber; + + /** + * Complete pattern for <code>integer</code> number, + * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}. + */ + public final static Pattern patternIntegerNumber; + + /** + * One of: {@code +} {@code -} {@code *} {@code /} {@code |} {@code &} {@code (} {@code )} {@code <<} {@code >>} + * <p> + * Expression excludes {@link #patternDecimalOrIntNumber}. + * </p> + */ + public static Pattern patternCPPOperand; + + static { + final String WhiteSpace = "[\\x00-\\x20]*"; + final String Digits = "(\\p{Digit}+)"; + final String HexDigits = "(\\p{XDigit}+)"; + final String IntTypeSuffix = + "(" + + "[uU]|" + + "([uU][lL])|" + + "[lL]|" + + "([lL][uU])" + + ")"; + + final String hexRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + // HexDigits IntTypeSuffix_opt + "0[xX]" + HexDigits + IntTypeSuffix + "?" + + WhiteSpace // Optional trailing "whitespace" + ; + patternHexNumber = Pattern.compile(hexRegex); + + final String intRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + // Digits IntTypeSuffix_opt + Digits + IntTypeSuffix + "?" + + WhiteSpace // Optional trailing "whitespace" + ; + patternIntegerNumber = Pattern.compile(intRegex); + + // an exponent is 'e' or 'E' followed by an optionally + // signed decimal integer. + final String Exp = "[eE][+-]?"+Digits; + final String fpRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + "("+ + "NaN|" + // "NaN" string + "Infinity|" + // "Infinity" string + + // A decimal floating-point string representing a finite positive + // number without a leading sign has at most five basic pieces: + // Digits . Digits ExponentPart FloatTypeSuffix + // + // Since this method allows integer-only strings as input + // in addition to strings of floating-point literals, the + // two sub-patterns below are simplifications of the grammar + // productions from the Java Language Specification, 2nd + // edition, section 3.10.2. + + "("+ + "("+ + // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt + "("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ + + // . Digits ExponentPart_opt FloatTypeSuffix_opt + "(\\.("+Digits+")("+Exp+")?)|"+ + + // Hexadecimal w/ binary exponent + "(" + + "(" + + // Hexadecimal strings + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "(\\.)?)|" + + + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + + ")" + + + // binary exponent + "[pP][+-]?" + Digits + + ")" + + ")" + + "[fFdD]?"+ + ")"+ + ")" + + WhiteSpace // Optional trailing "whitespace" + ; + patternDecimalNumber = Pattern.compile(fpRegex); + + final String fpOrIntRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + "("+ + "NaN|" + // "NaN" string + "Infinity|" + // "Infinity" string + + // Matching integers w/ IntTypeSuffix, + // which are otherwise not matched by the below floating point matcher! + // Digits IntTypeSuffix + "(" + Digits + IntTypeSuffix +")|" + + + // A decimal floating-point string representing a finite positive + // number without a leading sign has at most five basic pieces: + // Digits . Digits ExponentPart FloatTypeSuffix + // + // Since this method allows integer-only strings as input + // in addition to strings of floating-point literals, the + // two sub-patterns below are simplifications of the grammar + // productions from the Java Language Specification, 2nd + // edition, section 3.10.2. + + "("+ + "("+ + // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt + "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + + + // . Digits ExponentPart_opt FloatTypeSuffix_opt + "(\\.(" + Digits + ")(" + Exp + ")?)|" + + + // Hexadecimal w/ binary exponent + "(" + + "(" + + // Hexadecimal strings + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "(\\.)?)|" + + + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + + ")" + + + // binary exponent + "[pP][+-]?" + Digits + + ")" + + ")" + + "[fFdD]?"+ + ")"+ + ")" + + WhiteSpace // Optional trailing "whitespace" + ; + patternDecimalOrIntNumber = Pattern.compile(fpOrIntRegex); + + final String fpOrIntRegex2 = + WhiteSpace + // Optional leading "whitespace" + // "[+-]?" + // Optional sign character + "("+ + "NaN|" + // "NaN" string + "Infinity|" + // "Infinity" string + + // Matching integers w/ IntTypeSuffix, + // which are otherwise not matched by the below floating point matcher! + // Digits IntTypeSuffix + "(" + Digits + IntTypeSuffix +")|" + + + // A decimal floating-point string representing a finite positive + // number without a leading sign has at most five basic pieces: + // Digits . Digits ExponentPart FloatTypeSuffix + // + // Since this method allows integer-only strings as input + // in addition to strings of floating-point literals, the + // two sub-patterns below are simplifications of the grammar + // productions from the Java Language Specification, 2nd + // edition, section 3.10.2. + + "("+ + "("+ + // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt + "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + + + // . Digits ExponentPart_opt FloatTypeSuffix_opt + "(\\.(" + Digits + ")(" + Exp + ")?)|" + + + // Hexadecimal w/ binary exponent + "(" + + "(" + + // Hexadecimal strings + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "(\\.)?)|" + + + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + + ")" + + + // binary exponent + "[pP][+-]?" + Digits + + ")" + + ")" + + "[fFdD]?"+ + ")"+ + ")" + + WhiteSpace // Optional trailing "whitespace" + ; + patternCPPOperand = Pattern.compile("(?!"+fpOrIntRegex2+")[\\+\\-\\*\\/\\|\\&\\(\\)]|(\\<\\<)|(\\>\\>)"); + } } diff --git a/src/java/com/jogamp/gluegen/DebugEmitter.java b/src/java/com/jogamp/gluegen/DebugEmitter.java index 6381c8c..046c2b6 100644 --- a/src/java/com/jogamp/gluegen/DebugEmitter.java +++ b/src/java/com/jogamp/gluegen/DebugEmitter.java @@ -39,6 +39,7 @@ package com.jogamp.gluegen; +import java.io.IOException; import java.util.*; import com.jogamp.gluegen.cgram.types.*; @@ -46,9 +47,16 @@ import com.jogamp.gluegen.cgram.types.*; /** Debug emitter which prints the parsing results to standard output. */ public class DebugEmitter implements GlueEmitter { + protected JavaConfiguration cfg; @Override - public void readConfigurationFile(final String filename) {} + public void readConfigurationFile(final String filename) throws IOException { + cfg = createConfig(); + cfg.read(filename); + } + + @Override + public JavaConfiguration getConfiguration() { return cfg; } @Override public void beginEmission(final GlueEmitterControls controls) { @@ -66,7 +74,7 @@ public class DebugEmitter implements GlueEmitter { @Override public void emitDefine(final ConstantDefinition def, final String optionalComment) { final String name = def.getName(); - final String value = def.getValue(); + final String value = def.getNativeExpr(); System.out.println("#define " + name + " " + value + (optionalComment != null ? ("// " + optionalComment) : "")); } @@ -110,10 +118,10 @@ public class DebugEmitter implements GlueEmitter { } @Override - public void emitStruct(final CompoundType t, final String alternateName) { + public void emitStruct(final CompoundType t, final Type typedefType) { String name = t.getName(); - if (name == null && alternateName != null) { - name = alternateName; + if (name == null && typedefType != null) { + name = typedefType.getName(); } System.out.println("Referenced type \"" + name + "\""); @@ -121,4 +129,13 @@ public class DebugEmitter implements GlueEmitter { @Override public void endStructs() {} + + /** + * Create the object that will read and store configuration information for + * this JavaEmitter. + */ + protected JavaConfiguration createConfig() { + return new JavaConfiguration(); + } + } diff --git a/src/java/com/jogamp/gluegen/FunctionEmitter.java b/src/java/com/jogamp/gluegen/FunctionEmitter.java index 8e9d306..bfbb73b 100644 --- a/src/java/com/jogamp/gluegen/FunctionEmitter.java +++ b/src/java/com/jogamp/gluegen/FunctionEmitter.java @@ -42,57 +42,43 @@ package com.jogamp.gluegen; import java.util.*; import java.io.*; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; public abstract class FunctionEmitter { public static final EmissionModifier STATIC = new EmissionModifier("static"); - private final boolean isInterfaceVal; + private final boolean isInterface; private final ArrayList<EmissionModifier> modifiers; private CommentEmitter commentEmitter = null; private final PrintWriter defaultOutput; + // Only present to provide more clear comments + protected final JavaConfiguration cfg; /** * Constructs the FunctionEmitter with a CommentEmitter that emits nothing. */ - public FunctionEmitter(final PrintWriter defaultOutput, final boolean isInterface) { + public FunctionEmitter(final PrintWriter defaultOutput, final boolean isInterface, final JavaConfiguration configuration) { assert(defaultOutput != null); + this.isInterface = isInterface; this.modifiers = new ArrayList<EmissionModifier>(); this.defaultOutput = defaultOutput; - this.isInterfaceVal = isInterface; + this.cfg = configuration; } /** * Makes this FunctionEmitter a copy of the passed one. */ public FunctionEmitter(final FunctionEmitter arg) { + isInterface = arg.isInterface; modifiers = new ArrayList<EmissionModifier>(arg.modifiers); commentEmitter = arg.commentEmitter; defaultOutput = arg.defaultOutput; - isInterfaceVal = arg.isInterfaceVal; + cfg = arg.cfg; } - public boolean isInterface() { return isInterfaceVal; } - - /** - * Checks the base type of pointer-to-pointer, pointer, array or plain for const-ness. - * <p> - * Note: Implementation walks down to the base type and returns it's const-ness. - * Intermediate 'const' qualifier are not considered, e.g. const pointer. - * </p> - */ - protected final boolean isBaseTypeConst(final Type type) { - if ( 2 == type.pointerDepth() ) { - return type.asPointer().getTargetType().asPointer().getTargetType().isConst(); - } else if ( 1 == type.pointerDepth() ) { - return type.asPointer().getTargetType().isConst(); - } else if( type.isArray() ) { - return type.asArray().getBaseElementType().isConst(); - } else { - return type.isConst(); - } - } + public boolean isInterface() { return isInterface; } public PrintWriter getDefaultOutput() { return defaultOutput; } @@ -111,7 +97,11 @@ public abstract class FunctionEmitter { public Iterator<EmissionModifier> getModifiers() { return modifiers.iterator(); } - public abstract String getName(); + public abstract String getInterfaceName(); + public abstract String getImplName(); + public abstract String getNativeName(); + + public abstract FunctionSymbol getCSymbol(); /** * Emit the function to the specified output (instead of the default diff --git a/src/java/com/jogamp/gluegen/GenericCPP.java b/src/java/com/jogamp/gluegen/GenericCPP.java new file mode 100644 index 0000000..db414d9 --- /dev/null +++ b/src/java/com/jogamp/gluegen/GenericCPP.java @@ -0,0 +1,63 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen; + +import java.io.OutputStream; +import java.io.Reader; +import java.util.List; + +import com.jogamp.gluegen.jcpp.LexerException; + +/** + * Generic C preprocessor interface for GlueGen + */ +public interface GenericCPP { + + public void addDefine(String name, String value) throws LexerException; + + public String findFile(String filename); + + public OutputStream out(); + public void setOut(OutputStream out); + + public void run(Reader reader, String filename) throws GlueGenException; + + /** + * Returns a list of {@link ConstantDefinition}, i.e. + * <i>non-function-like</i> and <i>non-empty</i> macros w/ <i>constant-value</i>, + * as derived during parsing. + * <p> + * May return an empty list, in case this preprocessor does not + * store {@link ConstantDefinition}s. + * </p> + * @throws GlueGenException + */ + public List<ConstantDefinition> getConstantDefinitions() throws GlueGenException; + + +}
\ No newline at end of file diff --git a/src/java/com/jogamp/gluegen/GlueEmitter.java b/src/java/com/jogamp/gluegen/GlueEmitter.java index bb46cf5..0e8d61f 100644 --- a/src/java/com/jogamp/gluegen/GlueEmitter.java +++ b/src/java/com/jogamp/gluegen/GlueEmitter.java @@ -50,6 +50,7 @@ import com.jogamp.gluegen.cgram.types.*; public interface GlueEmitter { public void readConfigurationFile(String filename) throws Exception; + public JavaConfiguration getConfiguration(); /** * Begin the emission of glue code. This might include opening files, @@ -91,11 +92,11 @@ public interface GlueEmitter { public void beginStructs(TypeDictionary typedefDictionary, TypeDictionary structDictionary, Map<Type, Type> canonMap) throws Exception; - /** Emit glue code for the given CompoundType. alternateName is + /** Emit glue code for the given CompoundType. typedefType is provided when the CompoundType (e.g. "struct foo_t") has not been typedefed to anything but the type of "pointer to struct foo_t" has (e.g. "typedef struct foo_t {} *Foo"); in this case - alternateName would be set to Foo. */ - public void emitStruct(CompoundType t, String alternateName) throws Exception; + typedefType would be set to pointer type Foo. */ + public void emitStruct(CompoundType t, Type typedefType) throws Exception; public void endStructs() throws Exception; } diff --git a/src/java/com/jogamp/gluegen/GlueGen.java b/src/java/com/jogamp/gluegen/GlueGen.java index e123910..6dee6f0 100644 --- a/src/java/com/jogamp/gluegen/GlueGen.java +++ b/src/java/com/jogamp/gluegen/GlueGen.java @@ -43,11 +43,13 @@ import com.jogamp.common.GlueGenVersion; import java.io.*; import java.util.*; +import java.util.logging.Level; import antlr.*; + import com.jogamp.gluegen.cgram.*; import com.jogamp.gluegen.cgram.types.*; -import com.jogamp.gluegen.pcpp.*; +import com.jogamp.gluegen.jcpp.JCPP; import static java.lang.System.*; @@ -61,14 +63,18 @@ public class GlueGen implements GlueEmitterControls { } private final List<String> forcedStructNames = new ArrayList<String>(); - private PCPP preprocessor; + private GenericCPP preprocessor; // State for SymbolFilters - private List<ConstantDefinition> constants; - private List<FunctionSymbol> functions; + private List<ConstantDefinition> allConstants; + private List<FunctionSymbol> allFunctions; private static boolean debug = false; + private static Level logLevel = null; + + public static void setDebug(final boolean v) { debug=v; } + public static void setLogLevel(final Level l) { logLevel=l; } public static boolean debug() { return debug; } @Override @@ -83,38 +89,66 @@ public class GlueGen implements GlueEmitterControls { @Override public void runSymbolFilter(final SymbolFilter filter) { - filter.filterSymbols(constants, functions); + filter.filterSymbols(allConstants, allFunctions); final List<ConstantDefinition> newConstants = filter.getConstants(); final List<FunctionSymbol> newFunctions = filter.getFunctions(); if (newConstants != null) { - constants = newConstants; + allConstants = newConstants; } if (newFunctions != null) { - functions = newFunctions; + allFunctions = newFunctions; } } + /** GlueGen's build in macro name {@value}, when compiling w/ GlueGen. */ + public static final String __GLUEGEN__ = "__GLUEGEN__"; @SuppressWarnings("unchecked") - public void run(final Reader reader, final String filename, final Class<?> emitterClass, final List<String> includePaths, final List<String> cfgFiles, final String outputRootDir, final boolean copyPCPPOutput2Stderr) { + public void run(final Reader reader, final String filename, final Class<?> emitterClass, final List<String> includePaths, final List<String> cfgFiles, final String outputRootDir, final boolean copyCPPOutput2Stderr) { try { - final File out = File.createTempFile("PCPPTemp", ".pcpp"); + if(debug) { + Logging.getLogger().setLevel(Level.ALL); + } else if( null != logLevel ) { + Logging.getLogger().setLevel(logLevel); + } + final GlueEmitter emit; + if (emitterClass == null) { + emit = new JavaEmitter(); + } else { + try { + emit = (GlueEmitter) emitterClass.newInstance(); + } catch (final Exception e) { + throw new RuntimeException("Exception occurred while instantiating emitter class.", e); + } + } + + for (final String config : cfgFiles) { + emit.readConfigurationFile(config); + } + final JavaConfiguration cfg = emit.getConfiguration(); + + final File out = File.createTempFile("CPPTemp", ".cpp"); final FileOutputStream outStream = new FileOutputStream(out); + // preprocessor = new PCPP(includePaths, debug, copyCPPOutput2Stderr); + preprocessor = new JCPP(includePaths, debug, copyCPPOutput2Stderr); + final String cppName = preprocessor.getClass().getSimpleName(); if(debug) { - System.err.println("PCPP output at (persistent): " + out.getAbsolutePath()); + System.err.println("CPP <"+cppName+"> output at (persistent): " + out.getAbsolutePath()); } else { out.deleteOnExit(); } - preprocessor = new PCPP(includePaths, debug, copyPCPPOutput2Stderr); - preprocessor.addDefine("__GLUEGEN__", "2"); + preprocessor.addDefine(__GLUEGEN__, "2"); preprocessor.setOut(outStream); preprocessor.run(reader, filename); outStream.flush(); outStream.close(); + if(debug) { + System.err.println("CPP <"+cppName+"> done"); + } final FileInputStream inStream = new FileInputStream(out); final DataInputStream dis = new DataInputStream(inStream); @@ -140,6 +174,7 @@ public class GlueGen implements GlueEmitterControls { final HeaderParser headerParser = new HeaderParser(); headerParser.setDebug(debug); + headerParser.setJavaConfiguration(cfg); final TypeDictionary td = new TypeDictionary(); headerParser.setTypedefDictionary(td); final TypeDictionary sd = new TypeDictionary(); @@ -162,21 +197,6 @@ public class GlueGen implements GlueEmitterControls { // generate glue code: the #defines to constants, the set of // typedefs, and the set of functions. - GlueEmitter emit = null; - if (emitterClass == null) { - emit = new JavaEmitter(); - } else { - try { - emit = (GlueEmitter) emitterClass.newInstance(); - } catch (final Exception e) { - throw new RuntimeException("Exception occurred while instantiating emitter class.", e); - } - } - - for (final String config : cfgFiles) { - emit.readConfigurationFile(config); - } - if (null != outputRootDir && outputRootDir.trim().length() > 0) { if (emit instanceof JavaEmitter) { // FIXME: hack to interfere with the *Configuration setting via commandlines @@ -189,7 +209,7 @@ public class GlueGen implements GlueEmitterControls { // Repackage the enum and #define statements from the parser into a common format // so that SymbolFilters can operate upon both identically - constants = new ArrayList<ConstantDefinition>(); + allConstants = new ArrayList<ConstantDefinition>(); for (final EnumType enumeration : headerParser.getEnums()) { String enumName = enumeration.getName(); if (enumName.equals("<anonymous>")) { @@ -197,63 +217,93 @@ public class GlueGen implements GlueEmitterControls { } // iterate over all values in the enumeration for (int i = 0; i < enumeration.getNumEnumerates(); ++i) { - final String enumElementName = enumeration.getEnumName(i); - final String value = String.valueOf(enumeration.getEnumValue(i)); - constants.add(new ConstantDefinition(enumElementName, value, true, enumName)); + final EnumType.Enumerator enumerate = enumeration.getEnum(i); + final ConstantDefinition def = + new ConstantDefinition(enumerate.getName(), enumerate.getExpr(), + enumerate.getNumber(), + enumName, enumeration.getASTLocusTag()); + allConstants.add(def); } } for (final Object elem : lexer.getDefines()) { final Define def = (Define) elem; - constants.add(new ConstantDefinition(def.getName(), def.getValue(), false, null)); + allConstants.add(new ConstantDefinition(def.getName(), def.getValue(), null, def.getASTLocusTag())); } + allConstants.addAll(preprocessor.getConstantDefinitions()); - functions = headerParser.getParsedFunctions(); + allFunctions = headerParser.getParsedFunctions(); - // begin emission of glue code + // begin emission of glue code, + // incl. firing up 'runSymbolFilter(SymbolFilter)' calls, which: + // - filters all ConstantDefinition + // - filters all FunctionSymbol emit.beginEmission(this); - emit.beginDefines(); - final Set<String> emittedDefines = new HashSet<String>(100); - // emit java equivalent of enum { ... } statements - final StringBuilder comment = new StringBuilder(); - for (final ConstantDefinition def : constants) { - if (!emittedDefines.contains(def.getName())) { - emittedDefines.add(def.getName()); - final Set<String> aliases = def.getAliases(); - if (aliases != null) { - comment.append("Alias for: <code>"); - for (final String alias : aliases) { - comment.append(" ").append(alias); - } - comment.append("</code>"); + if( debug() ) { + int i=0; + System.err.println("Filtered Constants: "+allConstants.size()); + for (final ConstantDefinition def : allConstants) { + if( debug() ) { + System.err.println("Filtered ["+i+"]: "+def.getAliasedString()); + i++; } - if (def.getEnumName() != null) { - if (comment.length() > 0) - comment.append("<br>\n"); + } + i=0; + System.err.println("Filtered Functions: "+allFunctions.size()); + for (final FunctionSymbol cFunc : allFunctions) { + System.err.println("Filtered ["+i+"]: "+cFunc.getAliasedString()); + i++; + } + } - comment.append("Defined as part of enum type \""); - comment.append(def.getEnumName()); - comment.append("\""); - } - if (comment.length() > 0) { - emit.emitDefine(def, comment.toString()); - comment.setLength(0); - } - else { - emit.emitDefine(def, null); + if ( !cfg.structsOnly() ) { + emit.beginDefines(); + final Set<String> emittedDefines = new HashSet<String>(100); + // emit java equivalent of enum { ... } statements + final StringBuilder comment = new StringBuilder(); + for (final ConstantDefinition def : allConstants) { + if (!emittedDefines.contains(def.getName())) { + emittedDefines.add(def.getName()); + final Set<String> aliases = cfg.getAliasedDocNames(def); + if (aliases != null && aliases.size() > 0 ) { + int i=0; + comment.append("Alias for: <code>"); + for (final String alias : aliases) { + if(0 < i) { + comment.append("</code>, <code>"); + } + comment.append(alias); + i++; + } + comment.append("</code>"); + } + if (def.getEnumName() != null) { + if (comment.length() > 0) + comment.append("<br>\n"); + + comment.append("Defined as part of enum type \""); + comment.append(def.getEnumName()); + comment.append("\""); + } + if (comment.length() > 0) { + emit.emitDefine(def, comment.toString()); + comment.setLength(0); + } + else { + emit.emitDefine(def, null); + } } } + emit.endDefines(); } - emit.endDefines(); // Iterate through the functions finding structs that are referenced in // the function signatures; these will be remembered for later emission final ReferencedStructs referencedStructs = new ReferencedStructs(); - for (final FunctionSymbol sym : functions) { + for (final FunctionSymbol sym : allFunctions) { // FIXME: this doesn't take into account the possibility that some of // the functions we send to emitMethodBindings() might not actually be - // emitted (e.g., if an Ignore directive in the JavaEmitter causes it - // to be skipped). + // emitted (e.g., if an Ignore directive in the JavaEmitter causes it to be skipped). sym.getType().visit(referencedStructs); } @@ -273,13 +323,9 @@ public class GlueGen implements GlueEmitterControls { // Lay out structs emit.beginStructLayout(); - for (final Iterator<Type> iter = referencedStructs.results(); iter.hasNext();) { - final Type t = iter.next(); - if (t.isCompound()) { - emit.layoutStruct(t.asCompound()); - } else if (t.isPointer()) { - final PointerType p = t.asPointer(); - final CompoundType c = p.getTargetType().asCompound(); + for (final Iterator<CompoundType> iter = referencedStructs.layouts(); iter.hasNext();) { + final CompoundType c = iter.next(); + if( !c.isLayouted() ) { emit.layoutStruct(c); } } @@ -290,20 +336,23 @@ public class GlueGen implements GlueEmitterControls { for (final Iterator<Type> iter = referencedStructs.results(); iter.hasNext();) { final Type t = iter.next(); if (t.isCompound()) { + assert t.isTypedef() && t.getName() == null : "ReferencedStructs incorrectly recorded compound type " + t; emit.emitStruct(t.asCompound(), null); } else if (t.isPointer()) { final PointerType p = t.asPointer(); final CompoundType c = p.getTargetType().asCompound(); - assert p.hasTypedefedName() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; - emit.emitStruct(c, p.getName()); + assert p.isTypedef() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; + emit.emitStruct(c, p); } } emit.endStructs(); - // emit java and C code to interface with the native functions - emit.beginFunctions(td, sd, headerParser.getCanonMap()); - emit.emitFunctions(functions); - emit.endFunctions(); + if ( !cfg.structsOnly() ) { + // emit java and C code to interface with the native functions + emit.beginFunctions(td, sd, headerParser.getCanonMap()); + emit.emitFunctions(allFunctions); + emit.endFunctions(); + } // end emission of glue code emit.endEmission(); @@ -340,6 +389,9 @@ public class GlueGen implements GlueEmitterControls { emitterFQN = arg.substring(2); } else if (arg.startsWith("-C")) { cfgFiles.add(arg.substring(2)); + } else if (arg.equals("--logLevel")) { + i++; + logLevel = Level.parse(args[i]); } else if (arg.equals("--debug")) { debug=true; } else if (arg.equals("--dumpCPP")) { @@ -392,7 +444,7 @@ public class GlueGen implements GlueEmitterControls { out.println("file or files can be specified with -C option; e.g,"); out.println("-Cjava-emitter.cfg."); out.println(" --debug enables debug mode"); - out.println(" --dumpCPP directs PCPP to dump all output to stderr as well"); + out.println(" --dumpCPP directs CPP to dump all output to stderr as well"); exit(1); } } diff --git a/src/java/com/jogamp/gluegen/GlueGenException.java b/src/java/com/jogamp/gluegen/GlueGenException.java new file mode 100644 index 0000000..b6713e1 --- /dev/null +++ b/src/java/com/jogamp/gluegen/GlueGenException.java @@ -0,0 +1,92 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.gluegen; + +import com.jogamp.common.JogampRuntimeException; + +/** A generic exception for Jogamp errors used throughout the binding + as a substitute for {@link RuntimeException}. */ + +@SuppressWarnings("serial") +public class GlueGenException extends JogampRuntimeException { + final ASTLocusTag locus; + + public ASTLocusTag getASTLocusTag() { return locus; } + + /** Constructs a GlueGenException object. */ + public GlueGenException() { + super(); + locus = null; + } + + /** Constructs a GlueGenException object with the specified detail + message. */ + public GlueGenException(final String message) { + super(message); + locus = null; + } + + /** Constructs a GlueGenException object with the specified detail + message and root cause. */ + public GlueGenException(final String message, final Throwable cause) { + super(message, cause); + locus = null; + } + + /** Constructs a GlueGenException object with the specified root + cause. */ + public GlueGenException(final Throwable cause) { + super(cause); + locus = null; + } + + /** Constructs a GlueGenException object with the specified detail + message and root cause. */ + public GlueGenException(final String message, final ASTLocusTag locusTag) { + super(message); + this.locus = locusTag; + } + + /** Constructs a GlueGenException object with the specified detail + message and root cause. */ + public GlueGenException(final String message, final ASTLocusTag locusTag, final Throwable cause) { + super(message, cause); + this.locus = locusTag; + } + + public String toString() { + final StringBuilder sb = new StringBuilder(256); + if (null != locus) { + locus.toString(sb, "error", true).append(": "); + } + sb.append(getClass().getSimpleName()).append(": ").append(getLocalizedMessage()); + return sb.toString(); + } + +} diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java index 346920d..019ca2d 100644 --- a/src/java/com/jogamp/gluegen/JavaConfiguration.java +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -40,21 +40,19 @@ package com.jogamp.gluegen; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; import com.jogamp.gluegen.JavaEmitter.EmissionStyle; import com.jogamp.gluegen.JavaEmitter.MethodAccess; +import com.jogamp.gluegen.Logging.LoggerIf; import java.io.*; import java.lang.reflect.Array; import java.util.*; -import java.util.Map.Entry; import java.util.regex.*; import com.jogamp.gluegen.jgram.*; import com.jogamp.gluegen.cgram.types.*; -import java.util.logging.Logger; - -import jogamp.common.os.MachineDataInfoRuntime; import static java.util.logging.Level.*; import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; import static com.jogamp.gluegen.JavaEmitter.EmissionStyle.*; @@ -63,17 +61,13 @@ import static com.jogamp.gluegen.JavaEmitter.EmissionStyle.*; JavaEmitter. */ public class JavaConfiguration { - - public static final boolean DEBUG_IGNORES = GlueGen.debug() || false; - public static final boolean DEBUG_RENAMES = GlueGen.debug() || false; - private int nestedReads; private String packageName; private String implPackageName; private String className; private String implClassName; - protected static final Logger LOG = Logger.getLogger(JavaConfiguration.class.getPackage().getName()); + protected final LoggerIf LOG; public static String NEWLINE = System.getProperty("line.separator"); @@ -108,6 +102,13 @@ public class JavaConfiguration { private boolean tagNativeBinding; /** + * If true, {@link TypeConfig.SemanticEqualityOp#equalSemantics(TypeConfig.SemanticEqualityOp)} + * will attempt to perform a relaxed semantic equality test, e.g. skip the {@code const} and {@code volatile} qualifiers. + * Otherwise a full semantic equality test will be performed. + */ + private boolean relaxedEqualSemanticsTest; + + /** * Style of code emission. Can emit everything into one class * (AllStatic), separate interface and implementing classes * (InterfaceAndImpl), only the interface (InterfaceOnly), or only @@ -137,6 +138,7 @@ public class JavaConfiguration { private final Map<String, MethodAccess> accessControl = new HashMap<String, MethodAccess>(); private final Map<String, TypeInfo> typeInfoMap = new HashMap<String, TypeInfo>(); private final Set<String> returnsString = new HashSet<String>(); + private final Map<String, JavaType> returnsOpaqueJType = new HashMap<String, JavaType>(); private final Map<String, String> returnedArrayLengths = new HashMap<String, String>(); /** @@ -158,6 +160,7 @@ public class JavaConfiguration { private boolean forceUseNIODirectOnly4All = false; private final Set<String> useNIODirectOnly = new HashSet<String>(); private final Set<String> manuallyImplement = new HashSet<String>(); + private final Map<String, String> delegatedImplementation = new HashMap<String, String>(); private final Set<String> manualStaticInitCall = new HashSet<String>(); private final Set<String> forceStaticInitCode = new HashSet<String>(); private final Map<String, List<String>> customJavaCode = new HashMap<String, List<String>>(); @@ -180,6 +183,10 @@ public class JavaConfiguration { private final Map<String, List<String>> javaPrologues = new HashMap<String, List<String>>(); private final Map<String, List<String>> javaEpilogues = new HashMap<String, List<String>>(); + public JavaConfiguration() { + LOG = Logging.getLogger(JavaConfiguration.class.getPackage().getName(), JavaConfiguration.class.getSimpleName()); + } + /** Reads the configuration file. @param filename path to file that should be read */ @@ -317,6 +324,15 @@ public class JavaConfiguration { return tagNativeBinding; } + /** + * Returns whether {@link TypeConfig.SemanticEqualityOp#equalSemantics(TypeConfig.SemanticEqualityOp)} + * shall attempt to perform a relaxed semantic equality test, e.g. skip the {@code const} and {@code volatile} qualifier + * - or not. + */ + public boolean relaxedEqualSemanticsTest() { + return relaxedEqualSemanticsTest; + } + /** Returns the code emission style (constants in JavaEmitter) parsed from the configuration file. */ public EmissionStyle emissionStyle() { return emissionStyle; @@ -333,7 +349,7 @@ public class JavaConfiguration { } // Default access control is public return PUBLIC; - } + } /** Returns the package in which the generated glue code expects to find its run-time helper classes (Buffers, Platform, @@ -358,15 +374,31 @@ public class JavaConfiguration { } private static final boolean DEBUG_TYPE_INFO = false; + + /** + * If the given {@code canonicalName} should be considered opaque, + * returns the TypeInfo describing the replacement type. + * <p> + * Returns null if this type should not be considered opaque. + * </p> + * <p> + * If symbol references a struct fields, see {@link #canonicalStructFieldSymbol(String, String)}, + * it describes field's array-length or element-count referenced by a pointer. + * </p> + */ + public TypeInfo canonicalNameOpaque(final String canonicalName) { + return typeInfoMap.get(canonicalName); + } + /** If this type should be considered opaque, returns the TypeInfo describing the replacement type. Returns null if this type should not be considered opaque. */ - public TypeInfo typeInfo(Type type, final TypeDictionary typedefDictionary) { + public TypeInfo typeInfo(Type type) { // Because typedefs of pointer types can show up at any point, // walk the pointer chain looking for a typedef name that is in // the TypeInfo map. if (DEBUG_TYPE_INFO) - System.err.println("Incoming type = " + type); + System.err.println("Incoming type = " + type + ", " + type.getDebugString()); final int pointerDepth = type.pointerDepth(); for (int i = 0; i <= pointerDepth; i++) { String name = type.getName(); @@ -377,12 +409,13 @@ public class JavaConfiguration { if (name != null) { final TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); if (info != null) { + final TypeInfo res = promoteTypeInfo(info, i); if (DEBUG_TYPE_INFO) { - System.err.println(" info.name=" + info.name() + ", name=" + name + + System.err.println(" [1] info.name=" + info.name() + ", name=" + name + ", info.pointerDepth=" + info.pointerDepth() + - ", type.pointerDepth=" + type.pointerDepth()); + ", type.pointerDepth=" + type.pointerDepth() + " -> "+res); } - return promoteTypeInfo(info, i); + return res; } } @@ -392,33 +425,13 @@ public class JavaConfiguration { if (name != null) { final TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); if (info != null) { + final TypeInfo res = promoteTypeInfo(info, i); if (DEBUG_TYPE_INFO) { - System.err.println(" info.name=" + info.name() + ", name=" + name + - ", info.pointerDepth=" + info.pointerDepth() + - ", type.pointerDepth=" + type.pointerDepth()); - } - return promoteTypeInfo(info, i); - } - } - } - - // Try all typedef names that map to this type - final Set<Entry<String, Type>> entrySet = typedefDictionary.entrySet(); - for (final Map.Entry<String, Type> entry : entrySet) { - // "eq" equality is OK to use here since all types have been canonicalized - if (entry.getValue() == type) { - name = entry.getKey(); - if (DEBUG_TYPE_INFO) { - System.err.println("Looking under typedef name " + name); - } - final TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); - if (info != null) { - if (DEBUG_TYPE_INFO) { - System.err.println(" info.name=" + info.name() + ", name=" + name + + System.err.println(" [2] info.name=" + info.name() + ", name=" + name + ", info.pointerDepth=" + info.pointerDepth() + - ", type.pointerDepth=" + type.pointerDepth()); + ", type.pointerDepth=" + type.pointerDepth() + " -> "+res); } - return promoteTypeInfo(info, i); + return res; } } } @@ -427,7 +440,9 @@ public class JavaConfiguration { type = type.asPointer().getTargetType(); } } - + if (DEBUG_TYPE_INFO) { + System.err.println(" [X] NULL"); + } return null; } @@ -497,6 +512,13 @@ public class JavaConfiguration { public boolean returnsString(final String functionName) { return returnsString.contains(functionName); } + /** Indicates whether the given function (which returns a + <code>char*</code> in C) should be translated as returning a + <code>java.lang.String</code>. */ + public boolean returnsString(final AliasedSymbol symbol) { + return returnsString.contains( symbol.getName() ) || + oneInSet(returnsString, symbol.getAliasedNames()); + } /** * Returns a MessageFormat string of the Java expression calculating @@ -549,17 +571,6 @@ public class JavaConfiguration { return forceUseNIODirectOnly4All || useNIODirectOnly.contains(functionName); } - /** Returns true if the glue code for the given function will be - manually implemented by the end user. - * <p> - * If symbol references a struct field or method, see {@link #canonicalStructFieldSymbol(String, String)}, - * it describes field's array-length or element-count referenced by a pointer. - * </p> - */ - public boolean manuallyImplement(final String functionName) { - return manuallyImplement.contains(functionName); - } - /** * Returns true if the static initialization java code calling <code>initializeImpl()</code> * for the given class will be manually implemented by the end user @@ -727,52 +738,94 @@ public class JavaConfiguration { return parentClass.get(className); } - public void dumpIgnoresOnce() { - if(!dumpedIgnores) { - dumpedIgnores = true; - dumpIgnores(); + public void logIgnoresOnce() { + if(!loggedIgnores) { + loggedIgnores = true; + logIgnores(); } } - private static boolean dumpedIgnores = false; + private static boolean loggedIgnores = false; - public void dumpIgnores() { - System.err.println("Extended Intf: "); + public void logIgnores() { + LOG.log(INFO, "Extended Intf: {0}", extendedIntfSymbolsIgnore.size()); for (final String str : extendedIntfSymbolsIgnore) { - System.err.println("\t"+str); + LOG.log(INFO, "\t{0}", str); } - System.err.println("Extended Impl: "); + LOG.log(INFO, "Extended Impl: {0}", extendedImplSymbolsIgnore.size()); for (final String str : extendedImplSymbolsIgnore) { - System.err.println("\t"+str); + LOG.log(INFO, "\t{0}", str); } - System.err.println("Ignores (All): "); + LOG.log(INFO, "Ignores (All): {0}", ignores.size()); for (final Pattern pattern : ignores) { - System.err.println("\t"+pattern); + LOG.log(INFO, "\t{0}", pattern); } } - public void dumpRenamesOnce() { - if(!dumpedRenames) { - dumpedRenames = true; - dumpRenames(); + public void logRenamesOnce() { + if(!loggedRenames) { + loggedRenames = true; + logRenames(); } } - private static boolean dumpedRenames = false; + private static boolean loggedRenames = false; - public void dumpRenames() { - System.err.println("Symbol Renames: "); + public void logRenames() { + LOG.log(INFO, "Symbol Renames: {0}", javaSymbolRenames.size()); for (final String key : javaSymbolRenames.keySet()) { - System.err.println("\t"+key+" -> "+javaSymbolRenames.get(key)); + LOG.log(INFO, "\t{0} -> {1}", key, javaSymbolRenames.get(key)); } - System.err.println("Symbol Aliasing (through renaming): "); + LOG.log(INFO, "Symbol Aliasing (through renaming): {0}", javaSymbolRenames.size()); for(final String newName : javaSymbolRenames.values()) { final Set<String> origNames = javaRenamedSymbols.get(newName); if(null!=origNames) { - System.err.println("\t"+newName+" <- "+origNames); + LOG.log(INFO, "\t{0} <- {1}", newName, origNames); } } } + public static <K,V> V oneInMap(final Map<K, V> map, final Set<K> symbols) { + if( null != map && map.size() > 0 && + null != symbols && symbols.size() > 0 ) { + for(final K sym : symbols) { + final V v = map.get(sym); + if( null != v ) { + return v; + } + } + } + return null; + } + public static <K> boolean oneInSet(final Set<K> set1, final Set<K> set2) { + if( null != set1 && set1.size() > 0 && + null != set2 && set2.size() > 0 ) { + for(final K sym : set2) { + if( set1.contains( sym ) ) { + return true; + } + } + } + return false; + } + private static boolean onePatternMatch(final Pattern ignoreRegexp, final Set<String> set) { + if( null != ignoreRegexp && null != set && set.size() > 0 ) { + for(final String sym : set) { + final Matcher matcher = ignoreRegexp.matcher(sym); + if (matcher.matches()) { + return true; + } + } + } + return false; + } + protected static ASTLocusTag getASTLocusTag(final AliasedSymbol s) { + if( s instanceof ASTLocusTagProvider ) { + return ((ASTLocusTagProvider)s).getASTLocusTag(); + } else { + return null; + } + } + /** * Returns the canonical configuration name for a struct field name, * i.e. 'struct-name'.'field-name' @@ -782,136 +835,286 @@ public class JavaConfiguration { } /** - * Returns true if this #define, function, struct, or field within - * a struct should be ignored during glue code generation of interfaces and implementation. + * Variant of {@link #manuallyImplement(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public boolean manuallyImplement(final String functionName) { + if( manuallyImplement.contains(functionName) ) { + LOG.log(INFO, "ManuallyImplement: \"{0}\"", functionName); + return true; + } else { + return false; + } + } + + /** + * Returns true if the glue code for the given aliased function will be + * manually implemented by the end user. * <p> - * For struct fields see {@link #canonicalStructFieldSymbol(String, String)}. + * Both, the {@link AliasedSymbol#getName() current-name} + * and all {@link AliasedSymbol#getAliasedNames() aliases} shall be considered. * </p> + * <p> + * If symbol references a struct field or method, see {@link #canonicalStructFieldSymbol(String, String)}, + * it describes field's array-length or element-count referenced by a pointer. + * </p> + * @see #manuallyImplement(String) */ - public boolean shouldIgnoreInInterface(final String symbol) { - if(DEBUG_IGNORES) { - dumpIgnoresOnce(); - } - // Simple case-1; the entire symbol (orig or renamed) is in the interface ignore table - final String renamedSymbol = getJavaSymbolRename(symbol); - if ( extendedIntfSymbolsIgnore.contains( symbol ) || - extendedIntfSymbolsIgnore.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Intf ignore : "+symbol); + public boolean manuallyImplement(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + + if ( manuallyImplement.contains( name ) || + oneInSet(manuallyImplement, aliases) + ) + { + LOG.log(INFO, getASTLocusTag(symbol), "ManuallyImplement: {0}", symbol); + return true; + } else { + return false; } - return true; - } - // Simple case-2; the entire symbol (orig or renamed) is _not_ in the not-empty interface only table - if ( !extendedIntfSymbolsOnly.isEmpty() && - !extendedIntfSymbolsOnly.contains( symbol ) && - !extendedIntfSymbolsOnly.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Intf !extended: " + symbol); + } + + /** + * Variant of {@link #getDelegatedImplementation(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public String getDelegatedImplementation(final String functionName) { + final String res = delegatedImplementation.get(functionName); + if( null == res ) { + return null; + } + LOG.log(INFO, "DelegatedImplementation: {0} -> {1}", functionName, res); + return res; + } + + /** + * Returns the {@code RENAMED-IMPL-SYMBOL} if the implementation of the glue code + * of the given function shall be manually delegated by the end user. + * <p> + * {@code DelegateImplementation <ORIG-SYMBOL> <RENAMED-IMPL-SYMBOL>} + * </p> + * <p> + * The interface is emitted unchanged. + * </p> + * <p> + * The Java and native-code implementation is renamed to {@code RENAMED-IMPL-SYMBOL}. + * The user's manual implementation of {@code ORIG-SYMBOL} + * may delegate to {@code RENAMED-IMPL-SYMBOL}. + * </p> + * <p> + * If symbol references a struct field or method, see {@link #canonicalStructFieldSymbol(String, String)}, + * it describes field's array-length or element-count referenced by a pointer. + * </p> + */ + public String getDelegatedImplementation(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + + String res = delegatedImplementation.get(name); + if( null == res ) { + res = oneInMap(delegatedImplementation, aliases); + if( null == res ) { + return null; } - return true; - } - return shouldIgnoreInImpl_Int(symbol); + } + LOG.log(INFO, getASTLocusTag(symbol), "DelegatedImplementation: {0} -> {1}", symbol, res); + return res; + } + + /** + * Variant of {@link #getOpaqueReturnType(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public JavaType getOpaqueReturnType(final String functionName) { + final JavaType res = returnsOpaqueJType.get(functionName); + if( null == res ) { + return null; + } + LOG.log(INFO, "ReturnsOpaque: {0} -> {1}", functionName, res); + return res; } /** - * Returns true if this #define, function, struct, or field within - * a struct should be ignored during glue code generation of implementation only. + * Returns the opaque {@link JavaType} for the given function {@link AliasedSymbol} + * or {@code null} if not opaque. * <p> - * For struct fields see {@link #canonicalStructFieldSymbol(String, String)}. + * {@code ReturnsOpaque <Primitive Java Type> <Function Name>} * </p> */ - public boolean shouldIgnoreInImpl(final String symbol) { - return shouldIgnoreInImpl_Int(symbol); + public JavaType getOpaqueReturnType(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + JavaType res = returnsOpaqueJType.get(name); + if( null == res ) { + res = oneInMap(returnsOpaqueJType, aliases); + if( null == res ) { + return null; + } + } + LOG.log(INFO, getASTLocusTag(symbol), "ReturnsOpaque: {0} -> {1}", symbol, res); + return res; } - private boolean shouldIgnoreInImpl_Int(final String symbol) { + /** + * Variant of {@link #shouldIgnoreInInterface(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public final boolean shouldIgnoreInInterface(final String symbol) { + return shouldIgnoreInInterface( new AliasedSymbol.NoneAliasedSymbol(symbol) ); + } + /** + * Returns true if this aliased symbol should be ignored + * during glue code generation of interfaces and implementation. + * <p> + * Both, the {@link AliasedSymbol#getName() current-name} + * and all {@link AliasedSymbol#getAliasedNames() aliases} shall be considered. + * </p> + * <p> + * Implementation calls {@link #shouldIgnoreInInterface_Int(AliasedSymbol)} + * and overriding implementations shall ensure its being called as well! + * </p> + * @param symbol the symbolic aliased name to check for exclusion + */ + public boolean shouldIgnoreInInterface(final AliasedSymbol symbol) { + return shouldIgnoreInInterface_Int(symbol); + } - if(DEBUG_IGNORES) { - dumpIgnoresOnce(); - } + protected final boolean shouldIgnoreInInterface_Int(final AliasedSymbol symbol) { + if( GlueGen.debug() ) { + logIgnoresOnce(); + } + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); - // Simple case-1; the entire symbol (orig or renamed) is in the implementation ignore table - final String renamedSymbol = getJavaSymbolRename(symbol); - if ( extendedImplSymbolsIgnore.contains( symbol ) || - extendedImplSymbolsIgnore.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl ignore : "+symbol); + // Simple case-1; the symbol (orig or renamed) is in the interface ignore table + if ( extendedIntfSymbolsIgnore.contains( name ) || + oneInSet(extendedIntfSymbolsIgnore, aliases) + ) + { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Intf ignore (one): {0}", symbol); + return true; } - return true; - } - // Simple case-2; the entire symbol (orig or renamed) is _not_ in the not-empty implementation only table - if ( !extendedImplSymbolsOnly.isEmpty() && - !extendedImplSymbolsOnly.contains( symbol ) && - !extendedImplSymbolsOnly.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl !extended: " + symbol); - } + // Simple case-2; the entire symbol (orig and renamed) is _not_ in the not-empty interface only table + if ( !extendedIntfSymbolsOnly.isEmpty() && + !extendedIntfSymbolsOnly.contains( name ) && + !oneInSet(extendedIntfSymbolsOnly, aliases) ) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Intf !extended (all): {0}", symbol); return true; - } + } + return shouldIgnoreInImpl_Int(symbol); + } - // Ok, the slow case. We need to check the entire table, in case the table - // contains an regular expression that matches the symbol. - for (final Pattern regexp : ignores) { - final Matcher matcher = regexp.matcher(symbol); - if (matcher.matches()) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl RegEx: "+symbol); - } - return true; + /** + * Returns true if this aliased symbol should be ignored + * during glue code generation of implementation only. + * <p> + * Both, the {@link AliasedSymbol#getName() current-name} + * and all {@link AliasedSymbol#getAliasedNames() aliases} shall be considered. + * </p> + * <p> + * Implementation calls {@link #shouldIgnoreInImpl_Int(AliasedSymbol)} + * and overriding implementations shall ensure its being called as well! + * </p> + * @param symbol the symbolic aliased name to check for exclusion + */ + public boolean shouldIgnoreInImpl(final AliasedSymbol symbol) { + return shouldIgnoreInImpl_Int(symbol); + } + + protected final boolean shouldIgnoreInImpl_Int(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + + // Simple case-1; the symbol (orig or renamed) is in the interface ignore table + if ( extendedImplSymbolsIgnore.contains( name ) || + oneInSet(extendedImplSymbolsIgnore, aliases) + ) + { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl ignore (one): {0}", symbol); + return true; + } + // Simple case-2; the entire symbol (orig and renamed) is _not_ in the not-empty interface only table + if ( !extendedImplSymbolsOnly.isEmpty() && + !extendedImplSymbolsOnly.contains( name ) && + !oneInSet(extendedImplSymbolsOnly, aliases) ) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl !extended (all): {0}", symbol); + return true; } - } - // Check negated ignore table if not empty - if (ignoreNots.size() > 0) { // Ok, the slow case. We need to check the entire table, in case the table // contains an regular expression that matches the symbol. - for (final Pattern regexp : ignoreNots) { - final Matcher matcher = regexp.matcher(symbol); - if (!matcher.matches()) { - // Special case as this is most often likely to be the case. - // Unignores are not used very often. - if(unignores.isEmpty()) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl unignores==0: "+symbol); - } - return true; + for (final Pattern ignoreRegexp : ignores) { + final Matcher matcher = ignoreRegexp.matcher(name); + if ( matcher.matches() || onePatternMatch(ignoreRegexp, aliases) ) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl RegEx: {0}", symbol); + return true; } + } - boolean unignoreFound = false; - for (final Pattern unignoreRegexp : unignores) { - final Matcher unignoreMatcher = unignoreRegexp.matcher(symbol); - if (unignoreMatcher.matches()) { - unignoreFound = true; - break; - } + // Check negated ignore table if not empty + if (ignoreNots.size() > 0) { + // Ok, the slow case. We need to check the entire table, in case the table + // contains an regular expression that matches the symbol. + for (final Pattern ignoreNotRegexp : ignoreNots) { + final Matcher matcher = ignoreNotRegexp.matcher(name); + if ( !matcher.matches() && !onePatternMatch(ignoreNotRegexp, aliases) ) { + // Special case as this is most often likely to be the case. + // Unignores are not used very often. + if(unignores.isEmpty()) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl unignores==0: {0} -> {1}", symbol, name); + return true; + } + boolean unignoreFound = false; + for (final Pattern unignoreRegexp : unignores) { + final Matcher unignoreMatcher = unignoreRegexp.matcher(name); + if ( unignoreMatcher.matches() || onePatternMatch(unignoreRegexp, aliases) ) { + unignoreFound = true; + break; + } + } + + if (!unignoreFound) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl !unignore: {0} -> {1}", symbol, name); + return true; + } + } } - - if (!unignoreFound) - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl !unignore: "+symbol); - } - return true; - } } - } - - return false; + return false; } /** Returns true if this function should be given a body which throws a run-time exception with an "unimplemented" message during glue code generation. */ - public boolean isUnimplemented(final String symbol) { - // Ok, the slow case. We need to check the entire table, in case the table - // contains an regular expression that matches the symbol. - for (final Pattern regexp : unimplemented) { - final Matcher matcher = regexp.matcher(symbol); - if (matcher.matches()) { - return true; + public boolean isUnimplemented(final AliasedSymbol symbol) { + // Ok, the slow case. We need to check the entire table, in case the table + // contains an regular expression that matches the symbol. + for (final Pattern unimplRegexp : unimplemented) { + final Matcher matcher = unimplRegexp.matcher(symbol.getName()); + if ( matcher.matches() || onePatternMatch(unimplRegexp, symbol.getAliasedNames()) ) { + return true; + } } - } + return false; + } - return false; + + /** + * Return a set of aliased-name for comment in docs. + * <p> + * This is usually {@link AliasedSymbol#addAliasedName(String)}, + * however an implementation may choose otherwise. + * </p> + * @param symbol the aliased symbol to retrieve the aliases + * @return set of aliased-names or {@code null}. + */ + public Set<String> getAliasedDocNames(final AliasedSymbol symbol) { + return symbol.getAliasedNames(); } /** Returns a replacement name for this type, which should be the @@ -932,8 +1135,8 @@ public class JavaConfiguration { function under the hood. Returns null if this symbol has not been explicitly renamed. */ public String getJavaSymbolRename(final String origName) { - if(DEBUG_RENAMES) { - dumpRenamesOnce(); + if( LOG.isLoggable(INFO) ) { + logRenamesOnce(); } return javaSymbolRenames.get(origName); } @@ -945,18 +1148,12 @@ public class JavaConfiguration { /** Programmatically adds a rename directive for the given symbol. */ public void addJavaSymbolRename(final String origName, final String newName) { - if(DEBUG_RENAMES) { - System.err.print("\tRename "+origName+" -> "+newName); - } + LOG.log(INFO, "\tRename {0} -> {1}", origName, newName); final String prevValue = javaSymbolRenames.put(origName, newName); if(null != prevValue && !prevValue.equals(newName)) { throw new RuntimeException("Rename-Override Attampt: "+origName+" -> "+newName+ ", but "+origName+" -> "+prevValue+" already exist. Run in 'debug' mode to analyze!"); } - if(DEBUG_RENAMES) { - System.err.println(); - } - Set<String> origNames = javaRenamedSymbols.get(newName); if(null == origNames) { origNames = new HashSet<String>(); @@ -965,6 +1162,16 @@ public class JavaConfiguration { origNames.add(origName); } + /** Programmatically adds a delegate implementation directive for the given symbol. */ + public void addDelegateImplementation(final String origName, final String renamedImpl) { + LOG.log(INFO, "\tDelegateImplementation {0} -> {1}", origName, renamedImpl); + final String prevValue = delegatedImplementation.put(origName, renamedImpl); + if(null != prevValue && !prevValue.equals(renamedImpl)) { + throw new RuntimeException("Rename-Override Attampt: "+origName+" -> "+renamedImpl+ + ", but "+origName+" -> "+prevValue+" already exist. Run in 'debug' mode to analyze!"); + } + } + /** Returns true if the emission style is AllStatic. */ public boolean allStatic() { return emissionStyle == AllStatic; @@ -1033,11 +1240,14 @@ public class JavaConfiguration { nativeOutputUsesJavaHierarchy = Boolean.valueOf(tmp).booleanValue(); } else if (cmd.equalsIgnoreCase("TagNativeBinding")) { tagNativeBinding = readBoolean("TagNativeBinding", tok, filename, lineNo).booleanValue(); + } else if (cmd.equalsIgnoreCase("RelaxedEqualSemanticsTest")) { + relaxedEqualSemanticsTest = readBoolean("RelaxedEqualSemanticsTest", tok, filename, lineNo).booleanValue(); + TypeConfig.setRelaxedEqualSemanticsTest(relaxedEqualSemanticsTest); // propagate .. } else if (cmd.equalsIgnoreCase("Style")) { try{ emissionStyle = EmissionStyle.valueOf(readString("Style", tok, filename, lineNo)); }catch(final IllegalArgumentException ex) { - LOG.log(WARNING, "Error parsing \"style\" command at line {0} in file \"{1}\"", new Object[]{lineNo, filename}); + LOG.log(WARNING, "Error parsing \"style\" command at line {0} in file \"{1}\"", lineNo, filename); } } else if (cmd.equalsIgnoreCase("AccessControl")) { readAccessControl(tok, filename, lineNo); @@ -1047,6 +1257,8 @@ public class JavaConfiguration { readOpaque(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("ReturnsString")) { readReturnsString(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ReturnsOpaque")) { + readReturnsOpaque(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("ReturnedArrayLength")) { readReturnedArrayLength(tok, filename, lineNo); // Warning: make sure delimiters are reset at the top of this loop @@ -1147,10 +1359,10 @@ public class JavaConfiguration { readParentClass(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("RenameJavaType")) { readRenameJavaType(tok, filename, lineNo); - } else if (cmd.equalsIgnoreCase("RenameJavaSymbol") || - // Backward compatibility - cmd.equalsIgnoreCase("RenameJavaMethod")) { + } else if (cmd.equalsIgnoreCase("RenameJavaSymbol")) { readRenameJavaSymbol(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("DelegateImplementation")) { + readDelegateImplementation(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("RuntimeExceptionType")) { runtimeExceptionType = readString("RuntimeExceptionType", tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("UnsupportedExceptionType")) { @@ -1222,7 +1434,7 @@ public class JavaConfiguration { protected void readOpaque(final StringTokenizer tok, final String filename, final int lineNo) { try { - final JavaType javaType = JavaType.createForClass(stringToPrimitiveType(tok.nextToken())); + final JavaType javaType = JavaType.createForOpaqueClass(stringToPrimitiveType(tok.nextToken())); String cType = null; while (tok.hasMoreTokens()) { if (cType == null) { @@ -1243,6 +1455,17 @@ public class JavaConfiguration { } } + protected void readReturnsOpaque(final StringTokenizer tok, final String filename, final int lineNo) { + try { + final JavaType javaType = JavaType.createForOpaqueClass(stringToPrimitiveType(tok.nextToken())); + final String funcName = tok.nextToken(); + returnsOpaqueJType.put(funcName, javaType); + } catch (final Exception e) { + throw new RuntimeException("Error parsing \"ReturnsOpaque\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + protected void readReturnsString(final StringTokenizer tok, final String filename, final int lineNo) { try { final String name = tok.nextToken(); @@ -1684,6 +1907,17 @@ public class JavaConfiguration { } } + public void readDelegateImplementation(final StringTokenizer tok, final String filename, final int lineNo) { + try { + final String fromName = tok.nextToken(); + final String toName = tok.nextToken(); + addDelegateImplementation(fromName, toName); + } catch (final NoSuchElementException e) { + throw new RuntimeException("Error parsing \"DelegateImplementation\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + protected void readJavaPrologueOrEpilogue(final StringTokenizer tok, final String filename, final int lineNo, final boolean prologue) { try { String methodName = tok.nextToken(); @@ -1755,6 +1989,16 @@ public class JavaConfiguration { return new TypeInfo(typeName, pointerDepth, javaType); } + public TypeInfo addTypeInfo(final String alias, final Type superType) { + final TypeInfo superInfo = typeInfo(superType); + if( null != superInfo ) { + final TypeInfo res = new TypeInfo(alias, superInfo.pointerDepth(), superInfo.javaType()); + addTypeInfo(res); + return res; + } else { + return null; + } + } protected void addTypeInfo(final TypeInfo info) { TypeInfo tmp = typeInfoMap.get(info.name()); if (tmp == null) { diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java index d2dc4ba..02e56a4 100644 --- a/src/java/com/jogamp/gluegen/JavaEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -40,22 +40,56 @@ package com.jogamp.gluegen; -import com.jogamp.common.nio.Buffers; -import com.jogamp.common.os.DynamicLookupHelper; -import com.jogamp.common.os.MachineDataInfo; - -import java.io.*; -import java.util.*; -import java.text.MessageFormat; - -import com.jogamp.gluegen.cgram.types.*; - +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PACKAGE_PRIVATE; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PRIVATE; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PROTECTED; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC_ABSTRACT; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.Buffer; -import java.util.logging.Logger; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; import jogamp.common.os.MachineDataInfoRuntime; -import static java.util.logging.Level.*; -import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.DynamicLookupHelper; +import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.common.util.ArrayHashMap; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.Logging.LoggerIf; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; +import com.jogamp.gluegen.cgram.types.ArrayType; +import com.jogamp.gluegen.cgram.types.CVAttributes; +import com.jogamp.gluegen.cgram.types.CompoundType; +import com.jogamp.gluegen.cgram.types.Field; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; +import com.jogamp.gluegen.cgram.types.FunctionType; +import com.jogamp.gluegen.cgram.types.IntType; +import com.jogamp.gluegen.cgram.types.PointerType; +import com.jogamp.gluegen.cgram.types.SizeThunk; +import com.jogamp.gluegen.cgram.types.StructLayout; +import com.jogamp.gluegen.cgram.types.Type; +import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol; +import com.jogamp.gluegen.cgram.types.TypeDictionary; // PROBLEMS: // - what if something returns 'const int *'? Could we @@ -70,7 +104,6 @@ import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; public class JavaEmitter implements GlueEmitter { private StructLayout layout; - private TypeDictionary typedefDictionary; private Map<Type, Type> canonMap; protected JavaConfiguration cfg; private boolean requiresStaticInitialization = false; @@ -97,13 +130,19 @@ public class JavaEmitter implements GlueEmitter { private final String javaName; } - private PrintWriter javaWriter; // Emits either interface or, in AllStatic mode, everything + private String javaFileName; // of javaWriter or javaImplWriter + private PrintWriter javaWriter; // Emits either interface or, in AllStatic mode, everything private PrintWriter javaImplWriter; // Only used in non-AllStatic modes for impl class + private String cFileName; // of cWriter private PrintWriter cWriter; private final MachineDataInfo machDescJava = MachineDataInfo.StaticConfig.LP64_UNIX.md; private final MachineDataInfo.StaticConfig[] machDescTargetConfigs = MachineDataInfo.StaticConfig.values(); - protected final static Logger LOG = Logger.getLogger(JavaEmitter.class.getPackage().getName()); + protected final LoggerIf LOG; + + public JavaEmitter() { + LOG = Logging.getLogger(JavaEmitter.class.getPackage().getName(), JavaEmitter.class.getSimpleName()); + } @Override public void readConfigurationFile(final String filename) throws Exception { @@ -111,61 +150,129 @@ public class JavaEmitter implements GlueEmitter { cfg.read(filename); } - class ConstantRenamer implements SymbolFilter { + @Override + public JavaConfiguration getConfiguration() { return cfg; } + class ConstFuncRenamer implements SymbolFilter { private List<ConstantDefinition> constants; - - @Override - public void filterSymbols(final List<ConstantDefinition> constants, final List<FunctionSymbol> functions) { - this.constants = constants; - doWork(); - } + private List<FunctionSymbol> functions; @Override public List<ConstantDefinition> getConstants() { return constants; } - @Override public List<FunctionSymbol> getFunctions() { - return null; + return functions; + } + + private <T extends AliasedSemanticSymbol> List<T> filterSymbolsInt(final List<T> inList, + final boolean preserveOrder, + final List<T> outList) { + final JavaConfiguration cfg = getConfig(); + final ArrayHashMap<String, T> symMap = + new ArrayHashMap<String, T>(false, 100, ArrayHashMap.DEFAULT_LOAD_FACTOR); + for (final T sym : inList) { + final String origName = sym.getName(); + final String newName = cfg.getJavaSymbolRename(origName); + final T dupSym; + if( null != newName ) { + // Alias Name + dupSym = symMap.get(newName); + if( null != dupSym ) { + // only rename to allow 'equalSemantics' to not care .. + sym.rename(newName); + } + } else { + // Original Name + dupSym = symMap.get(origName); + } + if( null != dupSym ) { + // Duplicate alias .. check + if( !dupSym.equalSemantics(sym) ) { + final ASTLocusTag loc; + final String preLoc; + if( sym instanceof ASTLocusTagProvider ) { + loc = ((ASTLocusTagProvider)sym).getASTLocusTag(); + } else { + loc = null; + } + if( dupSym instanceof ASTLocusTagProvider ) { + preLoc = String.format(",%n %s: previous definition is here", + ((ASTLocusTagProvider)dupSym).getASTLocusTag().toString(new StringBuilder(), "note", true)); + } else { + preLoc = ""; + } + final String mode = null != newName ? "alias" : "orig"; + final String message = + String.format("Duplicate Name (%s) w/ incompatible value:%n this '%s',%n have '%s'%s", + mode, sym.getAliasedString(), dupSym.getAliasedString(), preLoc); + throw new GlueGenException(message, loc); + } + } + if( null != newName ) { + // Alias Name + if( null != dupSym ) { + // Duplicate alias .. add aliased name + dupSym.addAliasedName(origName); + } else { + // No duplicate .. rename and add + sym.rename(newName); + symMap.put(newName, sym); + } + } else { + // Original Name + if( null != dupSym ) { + // Duplicate orig .. drop + } else { + // No duplicate orig .. add + symMap.put(origName, sym); + } + } + } + outList.addAll(symMap.getData()); + if( !preserveOrder ) { + // sort constants to make them easier to find in native code + Collections.sort(outList, new Comparator<T>() { + @Override + public int compare(final T o1, final T o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + } + return outList; } - private void doWork() { - final List<ConstantDefinition> newConstants = new ArrayList<ConstantDefinition>(); - final JavaConfiguration cfg = getConfig(); - for (final ConstantDefinition def : constants) { - def.rename(cfg.getJavaSymbolRename(def.getName())); - newConstants.add(def); - } - constants = newConstants; + @Override + public void filterSymbols(final List<ConstantDefinition> inConstList, final List<FunctionSymbol> inFuncList) { + constants = filterSymbolsInt(inConstList, true, new ArrayList<ConstantDefinition>(100)); + functions = filterSymbolsInt(inFuncList, true, new ArrayList<FunctionSymbol>(100)); } } @Override public void beginEmission(final GlueEmitterControls controls) throws IOException { + // Handle renaming of constants and functions + controls.runSymbolFilter(new ConstFuncRenamer()); // Request emission of any structs requested for (final String structs : cfg.forcedStructs()) { controls.forceStructEmission(structs); } - if (!cfg.structsOnly()) { + if ( !cfg.structsOnly() ) { try { openWriters(); } catch (final Exception e) { throw new RuntimeException("Unable to open files for writing", e); } emitAllFileHeaders(); - - // Handle renaming of constants - controls.runSymbolFilter(new ConstantRenamer()); } } @Override public void endEmission() { - if (!cfg.structsOnly()) { + if ( !cfg.structsOnly() ) { emitAllFileFooters(); try { @@ -178,177 +285,11 @@ public class JavaEmitter implements GlueEmitter { @Override public void beginDefines() throws Exception { - if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) { + if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) { javaWriter().println(); } } - protected static int getJavaRadix(final String name, final String value) { - // FIXME: need to handle when type specifier is in last char (e.g., - // "1.0d or 2759L", because parseXXX() methods don't allow the type - // specifier character in the string. - // - //char lastChar = value.charAt(value.length()-1); - - try { - // see if it's a long or int - int radix; - String parseValue; - // FIXME: are you allowed to specify hex/octal constants with - // negation, e.g. "-0xFF" or "-056"? If so, need to modify the - // following "if(..)" checks and parseValue computation - if (value.startsWith("0x") || value.startsWith("0X")) { - radix = 16; - parseValue = value.substring(2); - } - else if (value.startsWith("0") && value.length() > 1) { - // TODO: is "0" the prefix in C to indicate octal??? - radix = 8; - parseValue = value.substring(1); - } - else { - radix = 10; - parseValue = value; - } - //System.err.println("parsing " + value + " as long w/ radix " + radix); - Long.parseLong(parseValue, radix); - return radix; - } catch (final NumberFormatException e) { - try { - // see if it's a double or float - Double.parseDouble(value); - return 10; - } catch (final NumberFormatException e2) { - throw new RuntimeException( - "Cannot emit define \""+name+"\": value \""+value+ - "\" cannot be assigned to a int, long, float, or double", e2); - } - } - } - - protected static Object getJavaValue(final String name, final String value) { - - // "calculates" the result type of a simple expression - // example: (2+3)-(2.0f-3.0) -> Double - // example: (1 << 2) -> Integer - - final Scanner scanner = new Scanner(value).useDelimiter("[+-/*/></(/)]"); - - Object resultType = null; - - while (scanner.hasNext()) { - - final String t = scanner.next().trim(); - - if(0<t.length()) { - final Object type = getJavaValue2(name, t); - - //fast path - if(type instanceof Double) - return type; - - if(resultType != null) { - - if(resultType instanceof Integer) { - if(type instanceof Long || type instanceof Float || type instanceof Double) - resultType = type; - }else if(resultType instanceof Long) { - if(type instanceof Float || type instanceof Double) - resultType = type; - }else if(resultType instanceof Float) { - if(type instanceof Float) - resultType = type; - } - }else{ - resultType = type; - } - - //fast path - if(resultType instanceof Double) - return type; - } - } - - return resultType; - } - - private static Object getJavaValue2(final String name, final String value) { - // FIXME: need to handle when type specifier is in last char (e.g., - // "1.0d or 2759L", because parseXXX() methods don't allow the type - // specifier character in the string. - // - final char lastChar = value.charAt(value.length()-1); - - try { - // see if it's a long or int - int radix; - String parseValue; - // FIXME: are you allowed to specify hex/octal constants with - // negation, e.g. "-0xFF" or "-056"? If so, need to modify the - // following "if(..)" checks and parseValue computation - if (value.startsWith("0x") || value.startsWith("0X")) { - radix = 16; - parseValue = value.substring(2); - } else if (value.startsWith("0") && value.length() > 1) { - // TODO: is "0" the prefix in C to indicate octal??? - radix = 8; - parseValue = value.substring(1); - } else { - radix = 10; - parseValue = value; - } - if(lastChar == 'u' || lastChar == 'U') { - parseValue = parseValue.substring(0, parseValue.length()-1); - } - - //System.err.println("parsing " + value + " as long w/ radix " + radix); - final long longVal = Long.parseLong(parseValue, radix); - // if constant is small enough, store it as an int instead of a long - if (longVal > Integer.MIN_VALUE && longVal < Integer.MAX_VALUE) { - return (int)longVal; - } - return longVal; - - } catch (final NumberFormatException e) { - try { - // see if it's a double or float - final double dVal = Double.parseDouble(value); - final double absVal = Math.abs(dVal); - // if constant is small enough, store it as a float instead of a double - if (absVal < Float.MIN_VALUE || absVal > Float.MAX_VALUE) { - return new Double(dVal); - } - return new Float((float) dVal); - } catch (final NumberFormatException e2) { - throw new RuntimeException( - "Cannot emit define \""+name+"\": value \""+value+ - "\" cannot be assigned to a int, long, float, or double", e2); - } - } - } - - - protected static String getJavaType(final String name, final String value) { - final Object oval = getJavaValue(name, value); - return getJavaType(name, oval); - } - - protected static String getJavaType(final String name, final Object oval) { - if(oval instanceof Integer) { - return "int"; - } else if(oval instanceof Long) { - return "long"; - } else if(oval instanceof Float) { - return "float"; - } else if(oval instanceof Double) { - return "double"; - } - - throw new RuntimeException( - "Cannot emit define (2) \""+name+"\": value \""+oval+ - "\" cannot be assigned to a int, long, float, or double"); - } - /** Mangle a class, package or function name for JNI usage, i.e. replace all '.' w/ '_' */ protected static String jniMangle(final String name) { return name.replaceAll("_", "_1").replace('.', '_'); @@ -358,10 +299,12 @@ public class JavaEmitter implements GlueEmitter { return "Java_"+jniMangle(javaPackageName)+"_"+jniMangle(javaClassName); } + private final Map<String, ConstantDefinition.JavaExpr> constMap = + new HashMap<String, ConstantDefinition.JavaExpr>(); + @Override public void emitDefine(final ConstantDefinition def, final String optionalComment) throws Exception { - - if (cfg.allStatic() || cfg.emitInterface()) { + if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) { // TODO: Some defines (e.g., GL_DOUBLE_EXT in gl.h) are defined in terms // of other defines -- should we emit them as references to the original // define (not even sure if the lexer supports this)? Right now they're @@ -371,24 +314,22 @@ public class JavaEmitter implements GlueEmitter { // currently only emits only numeric defines -- if it handled #define'd // objects it would make a bigger difference. - final String name = def.getName(); - String value = def.getValue(); - - if (!cfg.shouldIgnoreInInterface(name)) { - final String type = getJavaType(name, value); + if ( !cfg.shouldIgnoreInInterface(def) ) { + final ConstantDefinition.JavaExpr constExpr = def.computeJavaExpr(constMap); + constMap.put(def.getName(), constExpr); + javaWriter().print(" /** "); if (optionalComment != null && optionalComment.length() != 0) { - javaWriter().println(" /** " + optionalComment + " */"); + javaWriter().print(optionalComment); + javaWriter().print(" - "); } - String suffix = ""; - if(!value.endsWith(")")) { - if (type.equals("float") && !value.endsWith("f")) { - suffix = "f"; - }else if(value.endsWith("u") || value.endsWith("U")) { - value = value.substring(0, value.length()-1); - } + javaWriter().print("CType: "); + if( constExpr.resultType.isUnsigned ) { + javaWriter().print("unsigned "); } - - javaWriter().println(" public static final " + type + " " + name + " = " + value + suffix + ";"); + javaWriter().print(constExpr.resultJavaTypeName); + javaWriter().println(" */"); + javaWriter().println(" public static final " + constExpr.resultJavaTypeName + + " " + def.getName() + " = " + constExpr.javaExpression + ";"); } } } @@ -402,67 +343,51 @@ public class JavaEmitter implements GlueEmitter { final TypeDictionary structDictionary, final Map<Type, Type> canonMap) throws Exception { - this.typedefDictionary = typedefDictionary; + // this.typedefDictionary = typedefDictionary; this.canonMap = canonMap; this.requiresStaticInitialization = false; // reset - if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) { + if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) { javaWriter().println(); } } @Override - public Iterator<FunctionSymbol> emitFunctions(final List<FunctionSymbol> originalCFunctions) throws Exception { - - // Sometimes headers will have the same function prototype twice, once - // with the argument names and once without. We'll remember the signatures - // we've already processed we don't generate duplicate bindings. - // - // Note: this code assumes that on the equals() method in FunctionSymbol - // only considers function name and argument types (i.e., it does not - // consider argument *names*) when comparing FunctionSymbols for equality - final Set<FunctionSymbol> funcsToBindSet = new HashSet<FunctionSymbol>(100); - for (final FunctionSymbol cFunc : originalCFunctions) { - if (!funcsToBindSet.contains(cFunc)) { - funcsToBindSet.add(cFunc); - } - } - - // validateFunctionsToBind(funcsToBindSet); + public Iterator<FunctionSymbol> emitFunctions(final List<FunctionSymbol> funcsToBind) throws Exception { + if ( !cfg.structsOnly() ) { + // Bind all the C funcs to Java methods + final ArrayList<FunctionEmitter> methodBindingEmitters = new ArrayList<FunctionEmitter>(2*funcsToBind.size()); + { + int i=0; + for (final FunctionSymbol cFunc : funcsToBind) { + // Check to see whether this function should be ignored + if ( !cfg.shouldIgnoreInImpl(cFunc) ) { + methodBindingEmitters.addAll(generateMethodBindingEmitters(cFunc)); + LOG.log(INFO, cFunc.getASTLocusTag(), "Non-Ignored Impl[{0}]: {1}", i++, cFunc); + } - final ArrayList<FunctionSymbol> funcsToBind = new ArrayList<FunctionSymbol>(funcsToBindSet); - // sort functions to make them easier to find in native code - Collections.sort(funcsToBind, new Comparator<FunctionSymbol>() { - @Override - public int compare(final FunctionSymbol o1, final FunctionSymbol o2) { - return o1.getName().compareTo(o2.getName()); } - }); - - // Bind all the C funcs to Java methods - final HashSet<MethodBinding> methodBindingSet = new HashSet<MethodBinding>(); - final ArrayList<FunctionEmitter> methodBindingEmitters = new ArrayList<FunctionEmitter>(2*funcsToBind.size()); - for (final FunctionSymbol cFunc : funcsToBind) { - // Check to see whether this function should be ignored - if (!cfg.shouldIgnoreInImpl(cFunc.getName())) { - methodBindingEmitters.addAll(generateMethodBindingEmitters(methodBindingSet, cFunc)); - } - - } + } - // Emit all the methods - for (final FunctionEmitter emitter : methodBindingEmitters) { - try { - if (!emitter.isInterface() || !cfg.shouldIgnoreInInterface(emitter.getName())) { - emitter.emit(); - emitter.getDefaultOutput().println(); // put newline after method body + // Emit all the methods + { + int i=0; + for (final FunctionEmitter emitter : methodBindingEmitters) { + try { + final FunctionSymbol cFunc = emitter.getCSymbol(); + if ( !emitter.isInterface() || !cfg.shouldIgnoreInInterface(cFunc) ) { + emitter.emit(); + emitter.getDefaultOutput().println(); // put newline after method body + LOG.log(INFO, cFunc.getASTLocusTag(), "Non-Ignored Intf[{0}]: {1}", i++, cFunc); + } + } catch (final Exception e) { + throw new GlueGenException( + "Error while emitting binding for \"" + emitter.getCSymbol().getAliasedString() + "\"", + emitter.getCSymbol().getASTLocusTag(), e); + } + } } - } catch (final Exception e) { - throw new RuntimeException( - "Error while emitting binding for \"" + emitter.getName() + "\"", e); - } } - // Return the list of FunctionSymbols that we generated gluecode for return funcsToBind.iterator(); } @@ -506,25 +431,33 @@ public class JavaEmitter implements GlueEmitter { * native code because it doesn't need any processing of the * outgoing arguments). */ - protected void generatePublicEmitters(final MethodBinding binding, final List<FunctionEmitter> allEmitters, final boolean signatureOnly) { - if (cfg.manuallyImplement(binding.getName()) && !signatureOnly) { + protected void generatePublicEmitters(final MethodBinding binding, final List<FunctionEmitter> allEmitters, + final boolean signatureOnly) { + final FunctionSymbol cSymbol = binding.getCSymbol(); + if ( !signatureOnly && cfg.manuallyImplement(cSymbol) ) { // We only generate signatures for manually-implemented methods; // user provides the implementation return; } - final MethodAccess accessControl = cfg.accessControl(binding.getName()); + final MethodAccess accessControl; + + if ( !signatureOnly && null != binding.getDelegationImplName() ) { + // private access for delegation implementation methods + accessControl = PRIVATE; + } else { + accessControl = cfg.accessControl(binding.getName()); + } + // We should not emit anything except public APIs into interfaces - if (signatureOnly && (accessControl != PUBLIC)) { + if ( signatureOnly && PUBLIC != accessControl ) { return; } - final PrintWriter writer = ((signatureOnly || cfg.allStatic()) ? javaWriter() : javaImplWriter()); - // It's possible we may not need a body even if signatureOnly is // set to false; for example, if the routine doesn't take any // arrays or buffers as arguments - final boolean isUnimplemented = cfg.isUnimplemented(binding.getName()); + final boolean isUnimplemented = cfg.isUnimplemented(cSymbol); final List<String> prologue = cfg.javaPrologueForMethod(binding, false, false); final List<String> epilogue = cfg.javaEpilogueForMethod(binding, false, false); final boolean needsBody = isUnimplemented || @@ -536,25 +469,31 @@ public class JavaEmitter implements GlueEmitter { if( !requiresStaticInitialization ) { requiresStaticInitialization = binding.signatureRequiresStaticInitialization(); if( requiresStaticInitialization ) { - LOG.log(INFO, "StaticInit Trigger.1 \"{0}\"", binding); + LOG.log(INFO, cSymbol.getASTLocusTag(), "StaticInit Trigger.1 \"{0}\"", binding); } } + final boolean emitBody = !signatureOnly && needsBody; + final boolean isNativeMethod = !isUnimplemented && !needsBody && !signatureOnly; + + final PrintWriter writer = ((signatureOnly || cfg.allStatic()) ? javaWriter() : javaImplWriter()); + final JavaMethodBindingEmitter emitter = new JavaMethodBindingEmitter(binding, writer, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - !signatureOnly && needsBody, + emitBody, // emitBody cfg.tagNativeBinding(), - false, // eraseBufferAndArrayTypes + false, // eraseBufferAndArrayTypes cfg.useNIOOnly(binding.getName()), cfg.useNIODirectOnly(binding.getName()), - false, - false, - false, - isUnimplemented, - signatureOnly, + false, // forDirectBufferImplementation + false, // forIndirectBufferAndArrayImplementation + isUnimplemented, // isUnimplemented + signatureOnly, // isInterface + isNativeMethod, // isNativeMethod + false, // isPrivateNativeMethod cfg); switch (accessControl) { case PUBLIC: emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); break; @@ -565,7 +504,7 @@ public class JavaEmitter implements GlueEmitter { if (cfg.allStatic()) { emitter.addModifier(FunctionEmitter.STATIC); } - if (!isUnimplemented && !needsBody && !signatureOnly) { + if (isNativeMethod) { emitter.addModifier(JavaMethodBindingEmitter.NATIVE); } emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); @@ -585,7 +524,8 @@ public class JavaEmitter implements GlueEmitter { */ protected void generatePrivateEmitters(final MethodBinding binding, final List<FunctionEmitter> allEmitters) { - if (cfg.manuallyImplement(binding.getName())) { + final FunctionSymbol cSymbol = binding.getCSymbol(); + if (cfg.manuallyImplement(cSymbol)) { // Don't produce emitters for the implementation class return; } @@ -594,11 +534,11 @@ public class JavaEmitter implements GlueEmitter { cfg.javaPrologueForMethod(binding, false, false) != null || cfg.javaEpilogueForMethod(binding, false, false) != null ; - if ( !cfg.isUnimplemented( binding.getName() ) ) { + if ( !cfg.isUnimplemented( cSymbol ) ) { if( !requiresStaticInitialization ) { requiresStaticInitialization = binding.signatureRequiresStaticInitialization(); if( requiresStaticInitialization ) { - LOG.log(INFO, "StaticInit Trigger.2 \"{0}\"", binding); + LOG.log(INFO, cSymbol.getASTLocusTag(), "StaticInit Trigger.2 \"{0}\"", binding); } } @@ -621,16 +561,17 @@ public class JavaEmitter implements GlueEmitter { writer, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - false, + false, // emitBody cfg.tagNativeBinding(), - true, // eraseBufferAndArrayTypes + true, // eraseBufferAndArrayTypes cfg.useNIOOnly(binding.getName()), cfg.useNIODirectOnly(binding.getName()), - true, - true, - false, - false, - false, + true, // forDirectBufferImplementation + false, // forIndirectBufferAndArrayImplementation + false, // isUnimplemented + false, // isInterface + true, // isNativeMethod + true, // isPrivateNativeMethod cfg); emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); if (cfg.allStatic()) { @@ -658,7 +599,7 @@ public class JavaEmitter implements GlueEmitter { cfg.allStatic(), (binding.needsNIOWrappingOrUnwrapping() || hasPrologueOrEpilogue), !cfg.useNIODirectOnly(binding.getName()), - machDescJava); + machDescJava, getConfiguration()); prepCEmitter(binding.getName(), binding.getJavaReturnType(), cEmitter); allEmitters.add(cEmitter); } @@ -700,18 +641,34 @@ public class JavaEmitter implements GlueEmitter { * Generate all appropriate Java bindings for the specified C function * symbols. */ - protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final Set<MethodBinding> methodBindingSet, final FunctionSymbol sym) throws Exception { - + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final FunctionSymbol sym) throws Exception { final ArrayList<FunctionEmitter> allEmitters = new ArrayList<FunctionEmitter>(); - try { + if( cfg.emitInterface() ) { + generateMethodBindingEmittersImpl(allEmitters, sym, true); + } + if( cfg.emitImpl() ) { + generateMethodBindingEmittersImpl(allEmitters, sym, false); + } + } catch (final Exception e) { + throw new GlueGenException("Error while generating bindings for \"" + sym + "\"", sym.getASTLocusTag(), e); + } + + return allEmitters; + } + private void generateMethodBindingEmittersImpl(final ArrayList<FunctionEmitter> allEmitters, + final FunctionSymbol sym, + final boolean forInterface) throws Exception + { // Get Java binding for the function - final MethodBinding mb = bindFunction(sym, null, null, machDescJava); + final MethodBinding mb = bindFunction(sym, forInterface, machDescJava, null, null); // JavaTypes representing C pointers in the initial // MethodBinding have not been lowered yet to concrete types final List<MethodBinding> bindings = expandMethodBinding(mb); + final HashSet<MethodBinding> methodBindingSet = new HashSet<MethodBinding>(); + for (final MethodBinding binding : bindings) { if(!methodBindingSet.add(binding)) { @@ -772,25 +729,19 @@ public class JavaEmitter implements GlueEmitter { // Note in particular that the public entry point taking an // array is merely a special case of the indirect buffer case. - if (cfg.emitInterface()) { + if ( forInterface ) { generatePublicEmitters(binding, allEmitters, true); - } - if (cfg.emitImpl()) { + } else { generatePublicEmitters(binding, allEmitters, false); generatePrivateEmitters(binding, allEmitters); } } // end iteration over expanded bindings - } catch (final Exception e) { - throw new RuntimeException("Error while generating bindings for \"" + sym + "\"", e); } - return allEmitters; - } - @Override public void endFunctions() throws Exception { - if (!cfg.structsOnly()) { + if ( !cfg.structsOnly() ) { if (cfg.allStatic() || cfg.emitInterface()) { emitCustomJavaCode(javaWriter(), cfg.className()); } @@ -821,40 +772,89 @@ public class JavaEmitter implements GlueEmitter { public void beginStructs(final TypeDictionary typedefDictionary, final TypeDictionary structDictionary, final Map<Type, Type> canonMap) throws Exception { - this.typedefDictionary = typedefDictionary; + // this.typedefDictionary = typedefDictionary; this.canonMap = canonMap; } @Override - public void emitStruct(final CompoundType structCType, final String alternateName) throws Exception { - final String structCTypeName; + public void emitStruct(final CompoundType structCType, final Type structCTypedefPtr) throws Exception { + final String structCTypeName, typedefedName; { - String _name = structCType.getName(); - if (_name == null && alternateName != null) { - _name = alternateName; + final String _name = structCType.getName(); + if ( null != structCTypedefPtr && null != structCTypedefPtr.getName() ) { + // always use typedef'ed name if available + typedefedName = structCTypedefPtr.getName(); + structCTypeName = typedefedName; + } else { + // fall back to actual struct type name + typedefedName = null; + structCTypeName = _name; } - structCTypeName = _name; - } - - if (structCTypeName == null) { - final String structName = structCType.getStructName(); - if ( null != structName && cfg.shouldIgnoreInInterface(structName) ) { + LOG.log(INFO, structCType.getASTLocusTag(), "Struct emission of structCType {0}", structCType); + LOG.log(INFO, structCType.getASTLocusTag()," structCTypedefPtr {0}", structCTypedefPtr); + LOG.log(INFO, structCType.getASTLocusTag()," : structCTypeName \"{0}\" -> typedefedName \"{1}\" -> \"{2}\"", + _name, typedefedName, structCTypeName); + if ( null == structCTypeName ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of unnamed struct {0} w/o typedef", structCType); + return; + } + final AliasedSymbol.AliasedSymbolImpl aliases = new AliasedSymbol.AliasedSymbolImpl(structCTypeName); + aliases.addAliasedName(_name); + aliases.addAliasedName(typedefedName); + if ( cfg.shouldIgnoreInInterface(aliases) ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of ignored \"{0}\": {1}", aliases, structCType); return; } - LOG.log(WARNING, "skipping emission of unnamed struct \"{0}\"", structCType); - return; } - if (cfg.shouldIgnoreInInterface(structCTypeName)) { - return; + if( null != structCTypedefPtr && isOpaque(structCTypedefPtr) ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of opaque typedef {0}", structCTypedefPtr); + return; + } + if( isOpaque(structCType) ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of opaque c-struct {0}", structCType); + return; } - final Type containingCType = canonicalize(new PointerType(SizeThunk.POINTER, structCType, 0)); + final Type containingCType; + { + // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser) + final Type aptr; + int mode; + if( null != typedefedName ) { + aptr = structCTypedefPtr; + mode = 1; + } else { + aptr = new PointerType(SizeThunk.POINTER, structCType, 0); + aptr.setTypedefName(typedefedName); + mode = 2; + } + containingCType = canonicalize(aptr); + LOG.log(INFO, structCType.getASTLocusTag(), "containingCType[{0}]: {1} -canon-> {2}", mode, aptr, containingCType); + } final JavaType containingJType = typeToJavaType(containingCType, null); - if (!containingJType.isCompoundTypeWrapper()) { - return; + if( containingJType.isOpaqued() ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of opaque {0}, {1}", containingJType, structCType); + return; + } + if( !containingJType.isCompoundTypeWrapper() ) { + LOG.log(WARNING, structCType.getASTLocusTag(), + "skipping emission of non-compound {0}, {1}", containingJType, structCType); + return; } final String containingJTypeName = containingJType.getName(); + LOG.log(INFO, structCType.getASTLocusTag(), + "perform emission of \"{0}\" -> \"{1}\": {2}", structCTypeName, containingJTypeName, structCType); + + if( 0 == structCType.getNumFields() ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "emission of \"{0}\" with zero fields {1}", containingJTypeName, structCType); + } this.requiresStaticInitialization = false; // reset @@ -884,13 +884,13 @@ public class JavaEmitter implements GlueEmitter { final Field field = structCType.getField(i); final Type fieldType = field.getType(); - final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, field.getName()); + final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName()); if (!cfg.shouldIgnoreInInterface(cfgFieldName0)) { final String renamed = cfg.getJavaSymbolRename(cfgFieldName0); final String fieldName = renamed==null ? field.getName() : renamed; - final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, fieldName); + final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName); if ( fieldType.isFunctionPointer() || fieldType.isPointer() || requiresGetCStringLength(fieldType, cfgFieldName1) ) { needsNativeCode = true; @@ -978,11 +978,11 @@ public class JavaEmitter implements GlueEmitter { for (int i = 0; i < structCType.getNumFields(); i++) { final Field field = structCType.getField(i); final Type fieldType = field.getType(); - final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, field.getName()); + final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName()); if ( !cfg.shouldIgnoreInInterface(cfgFieldName0) ) { final String renamed = cfg.getJavaSymbolRename(cfgFieldName0); final String fieldName = null==renamed ? field.getName() : renamed; - final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, fieldName); + final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName); if (fieldType.isFunctionPointer()) { // no offset/size for function pointer .. if( GlueGen.debug() ) { @@ -993,8 +993,8 @@ public class JavaEmitter implements GlueEmitter { // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate // a name?) if (fieldType.getName() == null) { - throw new RuntimeException("Anonymous structs as fields not supported yet, field \"" + - cfgFieldName1 + "\", "+fieldType.getDebugString()); + throw new GlueGenException("Anonymous structs as fields not supported yet, field \"" + + cfgFieldName1 + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag()); } if( GlueGen.debug() ) { System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "compound"); @@ -1012,11 +1012,11 @@ public class JavaEmitter implements GlueEmitter { try { externalJavaType = typeToJavaType(fieldType, machDescJava); } catch (final Exception e) { - throw new RuntimeException("Error occurred while creating accessor for field \"" + - cfgFieldName1 + "\", "+fieldType.getDebugString(), e); + throw new GlueGenException("Error occurred while creating accessor for field \"" + + cfgFieldName1 + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e); } if( GlueGen.debug() ) { - System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, fieldName, fieldType.getDebugString(), "MISC"); + System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "MISC"); System.err.printf("SE.os.%02d: javaType %s%n", (i+1), externalJavaType.getDebugString()); } if (externalJavaType.isPrimitive()) { @@ -1040,6 +1040,7 @@ public class JavaEmitter implements GlueEmitter { } } javaWriter.println(); + // getDelegatedImplementation if( !cfg.manuallyImplement(JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, "size")) ) { javaWriter.println(" public static int size() {"); javaWriter.println(" return "+containingJTypeName+"_size[mdIdx];"); @@ -1073,52 +1074,62 @@ public class JavaEmitter implements GlueEmitter { final Field field = structCType.getField(i); final Type fieldType = field.getType(); - final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, field.getName()); + final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName()); if (!cfg.shouldIgnoreInInterface(cfgFieldName0)) { final String renamed = cfg.getJavaSymbolRename(cfgFieldName0); final String fieldName = renamed==null ? field.getName() : renamed; - final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, fieldName); + final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName); + final TypeInfo opaqueFieldType = cfg.typeInfo(fieldType); + final boolean isOpaqueFieldType = null != opaqueFieldType; + final TypeInfo opaqueField = cfg.canonicalNameOpaque(cfgFieldName1); + final boolean isOpaqueField = null != opaqueField; if( GlueGen.debug() ) { - System.err.printf("SE.ac.%02d: %s / %s, %s%n", (i+1), field, cfgFieldName1, fieldType.getDebugString()); + System.err.printf("SE.ac.%02d: %s / %s (opaque %b), %s (opaque %b)%n", (i+1), + (i+1), field, cfgFieldName1, isOpaqueField, fieldType.getDebugString(), isOpaqueFieldType); } - if (fieldType.isFunctionPointer()) { + if ( fieldType.isFunctionPointer() && !isOpaqueField ) { + final FunctionSymbol func = new FunctionSymbol(field.getName(), fieldType.asPointer().getTargetType().asFunction()); + func.rename(renamed); // null is OK generateFunctionPointerCode(methodBindingSet, javaWriter, jniWriter, structCTypeName, structClassPkgName, containingCType, containingJType, i, - new FunctionSymbol(fieldName, fieldType.asPointer().getTargetType().asFunction()), cfgFieldName1); - } else if (fieldType.isCompound()) { + func, cfgFieldName1); + } else if ( fieldType.isCompound() && !isOpaqueField ) { // FIXME: will need to support this at least in order to // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate a name?) if (fieldType.getName() == null) { - throw new RuntimeException("Anonymous structs as fields not supported yet (field \"" + - field + "\" in type \"" + structCTypeName + "\")"); + throw new GlueGenException("Anonymous structs as fields not supported yet (field \"" + + field + "\" in type \"" + structCTypeName + "\")", + fieldType.getASTLocusTag()); } javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, fieldType.getName(), capitalizeString(fieldName), null, null); + generateGetterSignature(javaWriter, fieldType, false, false, fieldType.getName(), fieldName, capitalizeString(fieldName), null, null); javaWriter.println(" {"); javaWriter.println(" return " + fieldType.getName() + ".create( accessor.slice( " + fieldName+"_offset[mdIdx], "+fieldName+"_size[mdIdx] ) );"); javaWriter.println(" }"); - } else if ( fieldType.isArray() || fieldType.isPointer() ) { - generateArrayGetterSetterCode(methodBindingSet, javaWriter, jniWriter, structCTypeName, structClassPkgName, - containingCType, containingJType, - i, field, fieldName, cfgFieldName1); + } else if ( ( fieldType.isArray() || fieldType.isPointer() ) && !isOpaqueField ) { + generateArrayGetterSetterCode(methodBindingSet, javaWriter, jniWriter, structCType, structCTypeName, + structClassPkgName, containingCType, + containingJType, i, field, fieldName, cfgFieldName1); } else { final JavaType javaType; try { javaType = typeToJavaType(fieldType, machDescJava); } catch (final Exception e) { - System.err.println("Error occurred while creating accessor for field \"" + - field.getName() + "\", "+fieldType.getDebugString()); - throw(e); + throw new GlueGenException("Error occurred while creating accessor for field \"" + + field.getName() + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e); } - if (javaType.isPrimitive()) { + if ( isOpaqueFieldType || isOpaqueField || javaType.isPrimitive()) { // Primitive type final boolean fieldTypeNativeSizeFixed = fieldType.getSize().hasFixedNativeSize(); final String javaTypeName; - if ( isOpaque(fieldType) ) { - javaTypeName = compatiblePrimitiveJavaTypeName(fieldType, javaType, machDescJava); + if ( isOpaqueFieldType ) { + javaTypeName = opaqueFieldType.javaType().getName(); + } else if ( isOpaqueField ) { + javaTypeName = opaqueField.javaType().getName(); + // javaTypeName = compatiblePrimitiveJavaTypeName(fieldType, javaType, machDescJava); } else { javaTypeName = javaType.getName(); } @@ -1126,15 +1137,14 @@ public class JavaEmitter implements GlueEmitter { final String capFieldName = capitalizeString(fieldName); final String sizeDenominator = fieldType.isPointer() ? "pointer" : javaTypeName ; - if(GlueGen.debug()) { - System.err.println("Java.StructEmitter.Primitive: "+field.getName()+", "+fieldType.getDebugString()+", "+javaTypeName+", "+ - ", fixedSize "+fieldTypeNativeSizeFixed+", opaque "+isOpaque(fieldType)+", sizeDenominator "+sizeDenominator); - } + LOG.log(FINE, structCType.getASTLocusTag(), + "Java.StructEmitter.Primitive: "+field.getName()+", "+fieldType+", "+javaTypeName+", "+ + ", fixedSize "+fieldTypeNativeSizeFixed+", opaque[t "+isOpaqueFieldType+", f "+isOpaqueField+"], sizeDenominator "+sizeDenominator); if( !fieldType.isConst() ) { // Setter javaWriter.println(); - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capFieldName, null, javaTypeName, null, null); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capFieldName, null, javaTypeName, null, null); javaWriter.println(" {"); if( fieldTypeNativeSizeFixed ) { javaWriter.println(" accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], val);"); @@ -1147,7 +1157,7 @@ public class JavaEmitter implements GlueEmitter { // Getter javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, javaTypeName, capFieldName, null, null); + generateGetterSignature(javaWriter, fieldType, false, false, javaTypeName, fieldName, capFieldName, null, null); javaWriter.println(" {"); javaWriter.print (" return "); if( fieldTypeNativeSizeFixed ) { @@ -1216,9 +1226,9 @@ public class JavaEmitter implements GlueEmitter { private void generateGetterSignature(final PrintWriter writer, final Type origFieldType, final boolean staticMethod, final boolean abstractMethod, - final String returnTypeName, final String capitalizedFieldName, - final String customArgs, final String arrayLengthExpr) { - writer.print(" /** Getter for native field: "+origFieldType.getDebugString()); + final String returnTypeName, final String fieldName, + final String capitalizedFieldName, final String customArgs, final String arrayLengthExpr) { + writer.print(" /** Getter for native field <code>"+fieldName+"</code>: "+origFieldType.getDebugString()); if( null != arrayLengthExpr ) { writer.print(", with array length of <code>"+arrayLengthExpr+"</code>"); } @@ -1231,10 +1241,10 @@ public class JavaEmitter implements GlueEmitter { } private void generateSetterSignature(final PrintWriter writer, final Type origFieldType, final boolean abstractMethod, - final String returnTypeName, final String capitalizedFieldName, - final String customArgsPre, final String paramTypeName, final String customArgsPost, - final String arrayLengthExpr) { - writer.print(" /** Setter for native field: "+origFieldType.getDebugString()); + final String returnTypeName, final String fieldName, + final String capitalizedFieldName, final String customArgsPre, final String paramTypeName, + final String customArgsPost, final String arrayLengthExpr) { + writer.print(" /** Setter for native field <code>"+fieldName+"</code>: "+origFieldType.getDebugString()); if( null != arrayLengthExpr ) { writer.print(", with array length of <code>"+arrayLengthExpr+"</code>"); } @@ -1288,7 +1298,7 @@ public class JavaEmitter implements GlueEmitter { final Type containingCType, final JavaType containingJType, final int i, final FunctionSymbol funcSym, final String returnSizeLookupName) { // Emit method call and associated native code - final MethodBinding mb = bindFunction(funcSym, containingJType, containingCType, machDescJava); + final MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, containingJType, containingCType); mb.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis // JavaTypes representing C pointers in the initial @@ -1310,16 +1320,17 @@ public class JavaEmitter implements GlueEmitter { javaWriter, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - true, + true, // emitBody cfg.tagNativeBinding(), false, // eraseBufferAndArrayTypes useNIOOnly, useNIODirectOnly, - false, - false, // FIXME: should unify this with the general emission code + false, // forDirectBufferImplementation false, // forIndirectBufferAndArrayImplementation - false, // FIXME: should unify this with the general emission code - false, + false, // isUnimplemented + false, // isInterface + false, // isNativeMethod + false, // isPrivateNativeMethod cfg); emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); emitter.emit(); @@ -1330,17 +1341,17 @@ public class JavaEmitter implements GlueEmitter { javaWriter, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - false, + false, // emitBody cfg.tagNativeBinding(), - true, // eraseBufferAndArrayTypes + true, // eraseBufferAndArrayTypes useNIOOnly, useNIODirectOnly, - true, - true, // FIXME: should unify this with the general emission code + true, // forDirectBufferImplementation false, // forIndirectBufferAndArrayImplementation - false, // FIXME: should unify this with the general emission code - false, - cfg); + false, // isUnimplemented + false, // isInterface + false, // isNativeMethod + true, cfg); emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); emitter.addModifier(JavaMethodBindingEmitter.NATIVE); emitter.emit(); @@ -1355,7 +1366,7 @@ public class JavaEmitter implements GlueEmitter { false, true, false, // forIndirectBufferAndArrayImplementation - machDescJava); + machDescJava, getConfiguration()); cEmitter.setIsCStructFunctionPointer(true); prepCEmitter(returnSizeLookupName, binding.getJavaReturnType(), cEmitter); cEmitter.emit(); @@ -1369,7 +1380,7 @@ public class JavaEmitter implements GlueEmitter { final int i, final FunctionSymbol funcSym, final String returnSizeLookupName, final String docArrayLenExpr, final String nativeArrayLenExpr) { // Emit method call and associated native code - final MethodBinding mb = bindFunction(funcSym, containingJType, containingCType, machDescJava); + final MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, containingJType, containingCType); mb.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis // JavaTypes representing C pointers in the initial @@ -1392,16 +1403,17 @@ public class JavaEmitter implements GlueEmitter { javaWriter, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - false, - cfg.tagNativeBinding(), - true, // eraseBufferAndArrayTypes + false, // emitBody + cfg.tagNativeBinding(), // tagNativeBinding + true, // eraseBufferAndArrayTypes useNIOOnly, useNIODirectOnly, - true, - true, // FIXME: should unify this with the general emission code - false, // forIndirectBufferAndArrayImplementation - false, // FIXME: should unify this with the general emission code - false, + false, // forDirectBufferImplementation + false, // forIndirectBufferAndArrayImplementation + false, // isUnimplemented + true, // isInterface + true, // isNativeMethod + true, // isPrivateNativeMethod cfg); if( null != docArrayLenExpr ) { emitter.setReturnedArrayLengthExpression(docArrayLenExpr, true); @@ -1420,7 +1432,7 @@ public class JavaEmitter implements GlueEmitter { false, true, false, // forIndirectBufferAndArrayImplementation - machDescJava); + machDescJava, getConfiguration()); cEmitter.setIsCStructFunctionPointer(false); final String lenExprSet; if( null != nativeArrayLenExpr ) { @@ -1470,14 +1482,16 @@ public class JavaEmitter implements GlueEmitter { final String cfgVal = cfg.returnedArrayLength(returnSizeLookupName); if( null != cfgVal ) { if( hasFixedTypeLen[0] ) { - System.err.println("WARNING: struct array field '"+returnSizeLookupName+"' of '"+type+"' length '"+Arrays.toString(length)+"' overwritten by cfg-expression: "+cfgVal); + LOG.log(WARNING, type.getASTLocusTag(), + "struct array field '"+returnSizeLookupName+"' of '"+type+"' length '"+Arrays.toString(length)+"' overwritten by cfg-expression: "+cfgVal); } return cfgVal; } if( hasFixedTypeLen[0] ) { return lengthExpr.toString(); } else { - System.err.println("WARNING: struct array field '"+returnSizeLookupName+"' length '"+Arrays.toString(length)+"' without fixed- nor configured-size: "+type.getDebugString()); + LOG.log(WARNING, type.getASTLocusTag(), + "struct array field '"+returnSizeLookupName+"' length '"+Arrays.toString(length)+"' without fixed- nor configured-size: {0}", type); return null; } } @@ -1510,16 +1524,18 @@ public class JavaEmitter implements GlueEmitter { private void generateArrayGetterSetterCode(final Set<MethodBinding> methodBindingSet, final PrintWriter javaWriter, final PrintWriter jniWriter, + final CompoundType structCType, final String structCTypeName, final String structClassPkgName, final Type containingCType, final JavaType containingJType, - final int i, final Field field, final String fieldName, final String returnSizeLookupName) throws Exception { + final int i, final Field field, final String fieldName, + final String returnSizeLookupName) throws Exception { final Type fieldType = field.getType(); final JavaType javaType; try { javaType = typeToJavaType(fieldType, machDescJava); } catch (final Exception e) { - throw new RuntimeException("Error occurred while creating array/pointer accessor for field \"" + - returnSizeLookupName + "\", "+fieldType.getDebugString(), e); + throw new GlueGenException("Error occurred while creating array/pointer accessor for field \"" + + returnSizeLookupName + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e); } if( GlueGen.debug() ) { System.err.printf("SE.ac.%02d: javaType %s%n", (i+1), javaType.getDebugString()); @@ -1586,7 +1602,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(); final String msg = "SKIP ptr-ptr (depth "+pointerType.pointerDepth()+"): "+returnSizeLookupName +": "+fieldType; javaWriter.println(" // "+msg); - System.err.println("WARNING: "+msg); + LOG.log(WARNING, structCType.getASTLocusTag(), msg); return; } } @@ -1598,8 +1614,9 @@ public class JavaEmitter implements GlueEmitter { try { baseJElemType = typeToJavaType(baseCElemType, machDescJava); } catch (final Exception e ) { - throw new RuntimeException("Error occurred while creating array/pointer accessor for field \"" + - returnSizeLookupName + "\", baseType "+baseCElemType.getDebugString()+", topType "+fieldType.getDebugString(), e); + throw new GlueGenException("Error occurred while creating array/pointer accessor for field \"" + + returnSizeLookupName + "\", baseType "+baseCElemType.getDebugString()+", topType "+fieldType.getDebugString(), + fieldType.getASTLocusTag(), e); } baseJElemTypeName = baseJElemType.getName(); baseCElemNativeSizeFixed = baseCElemType.isPrimitive() ? baseCElemType.getSize().hasFixedNativeSize() : true; @@ -1609,7 +1626,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(); final String msg = "SKIP primitive w/ platform dependent sized type in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("WARNING: "+msg); + LOG.log(WARNING, structCType.getASTLocusTag(), msg); return; } } @@ -1625,7 +1642,7 @@ public class JavaEmitter implements GlueEmitter { _arrayLengthExpr = "getCStringLengthImpl(pString)+1"; _arrayLengthExprIsConst = false; this.requiresStaticInitialization = true; - LOG.log(INFO, "StaticInit Trigger.3 \"{0}\"", returnSizeLookupName); + LOG.log(INFO, structCType.getASTLocusTag(), "StaticInit Trigger.3 \"{0}\"", returnSizeLookupName); } else { useGetCStringLength = false; } @@ -1635,7 +1652,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(); final String msg = "SKIP unsized array in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("WARNING: "+msg); + LOG.log(WARNING, structCType.getASTLocusTag(), msg); return; } boolean _hasSingleElement=false; @@ -1657,7 +1674,7 @@ public class JavaEmitter implements GlueEmitter { // if( !hasSingleElement && useFixedTypeLen[0] ) { javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, arrayLengthExprIsConst, false, "final int", capitalFieldName+"ArrayLength", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, arrayLengthExprIsConst, false, "final int", fieldName, capitalFieldName+"ArrayLength", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" return "+arrayLengthExpr+";"); javaWriter.println(" }"); @@ -1671,11 +1688,11 @@ public class JavaEmitter implements GlueEmitter { // Setter Primitive Pointer final String msg = "SKIP setter for primitive-pointer type in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("INFO: "+msg); + LOG.log(INFO, structCType.getASTLocusTag(), msg); } else { // Setter Primitive Array if( hasSingleElement ) { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); javaWriter.println(" {"); if( baseCElemNativeSizeFixed ) { javaWriter.println(" accessor.set" + baseJElemTypeNameC + "At(" + fieldName+"_offset[mdIdx], val);"); @@ -1685,7 +1702,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return this;"); javaWriter.println(" }"); } else { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + val.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + val.length \"+val.length+\" > array-length \"+arrayLength); };"); @@ -1708,11 +1725,11 @@ public class JavaEmitter implements GlueEmitter { // Setter Struct Pointer final String msg = "SKIP setter for complex-pointer type in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("INFO: "+msg); + LOG.log(INFO, structCType.getASTLocusTag(), msg); } else { // Setter Struct Array if( hasSingleElement ) { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int elemSize = "+baseJElemTypeName+".size();"); javaWriter.println(" final ByteBuffer destB = getBuffer();"); @@ -1728,7 +1745,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return this;"); javaWriter.println(" }"); } else { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + val.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + val.length \"+val.length+\" > array-length \"+arrayLength); };"); @@ -1750,7 +1767,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return this;"); javaWriter.println(" }"); javaWriter.println(); - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, "final int index", baseJElemTypeName, null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, "final int index", baseJElemTypeName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" final int elemSize = "+baseJElemTypeName+".size();"); @@ -1779,12 +1796,12 @@ public class JavaEmitter implements GlueEmitter { if( isPointer ) { // Getter Primitive Pointer final FunctionType ft = new FunctionType(dummyFuncTypeName, SizeThunk.POINTER, fieldType, 0); - ft.addArgument(containingCType.getCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), + ft.addArgument(containingCType.newCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), CMethodBindingEmitter.cThisArgumentName()); ft.addArgument(int32Type, nativeArrayLengthArg); final FunctionSymbol fs = new FunctionSymbol("get"+capitalFieldName, ft); jniWriter.println(); - jniWriter.print("static "+fs.toString()); + jniWriter.print("static "+fs.toString(false)); jniWriter.println("{"); jniWriter.println(" return "+CMethodBindingEmitter.cThisArgumentName()+"->"+field.getName()+";"); jniWriter.println("}"); @@ -1792,7 +1809,7 @@ public class JavaEmitter implements GlueEmitter { generateArrayPointerCode(methodBindingSet, javaWriter, jniWriter, structCTypeName, structClassPkgName, containingCType, containingJType, i, fs, returnSizeLookupName, arrayLengthExpr, nativeArrayLengthArg); javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); if( useGetCStringLength ) { javaWriter.println(" final int arrayLength = get"+capitalFieldName+"ArrayLength();"); @@ -1809,7 +1826,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" }"); if( isString && isByteBuffer ) { javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, "String", capitalFieldName+"AsString", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, "String", fieldName, capitalFieldName+"AsString", null, arrayLengthExpr); javaWriter.println(" {"); if( useGetCStringLength ) { javaWriter.println(" final int arrayLength = get"+capitalFieldName+"ArrayLength();"); @@ -1829,7 +1846,7 @@ public class JavaEmitter implements GlueEmitter { } if( useGetCStringLength ) { javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, "final int", capitalFieldName+"ArrayLength", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, "final int", fieldName, capitalFieldName+"ArrayLength", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final long pString = PointerBuffer.wrap( accessor.slice(" + fieldName+"_offset[mdIdx], PointerBuffer.ELEMENT_SIZE) ).get(0);"); javaWriter.println(" return "+arrayLengthExpr+";"); @@ -1838,7 +1855,7 @@ public class JavaEmitter implements GlueEmitter { } else { // Getter Primitive Array if( hasSingleElement ) { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); if( baseCElemNativeSizeFixed ) { javaWriter.println(" return accessor.get" + baseJElemTypeNameC + "At(" + fieldName+"_offset[mdIdx]);"); @@ -1848,7 +1865,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" }"); javaWriter.println(); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.print(" return accessor.slice(" + fieldName+"_offset[mdIdx], Buffers.SIZEOF_"+baseJElemTypeNameU+" * "+arrayLengthExpr+")"); if( !isByteBuffer ) { @@ -1858,7 +1875,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" }"); javaWriter.println(); if( isString && isByteBuffer ) { - generateGetterSignature(javaWriter, fieldType, false, false, "String", capitalFieldName+"AsString", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, "String", fieldName, capitalFieldName+"AsString", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int offset = " + fieldName+"_offset[mdIdx];"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); @@ -1872,7 +1889,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return new String(ba, 0, i);"); javaWriter.println(" }"); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", fieldName, capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + result.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + result.length \"+result.length+\" > array-length \"+arrayLength); };"); @@ -1887,12 +1904,12 @@ public class JavaEmitter implements GlueEmitter { if( isPointer ) { // Getter Struct Pointer final FunctionType ft = new FunctionType(dummyFuncTypeName, SizeThunk.POINTER, fieldType, 0); - ft.addArgument(containingCType.getCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), + ft.addArgument(containingCType.newCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), CMethodBindingEmitter.cThisArgumentName()); ft.addArgument(int32Type, nativeArrayElemOffsetArg); final FunctionSymbol fs = new FunctionSymbol("get"+capitalFieldName, ft); jniWriter.println(); - jniWriter.print("static "+fs.toString()); + jniWriter.print("static "+fs.toString(false)); jniWriter.println("{"); jniWriter.println(" return "+CMethodBindingEmitter.cThisArgumentName()+"->"+field.getName()+"+"+nativeArrayElemOffsetArg+";"); jniWriter.println("}"); @@ -1901,7 +1918,7 @@ public class JavaEmitter implements GlueEmitter { containingCType, containingJType, i, fs, returnSizeLookupName, arrayLengthExpr, nativeArrayLengthONE); javaWriter.println(); if( hasSingleElement ) { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final ByteBuffer source = getBuffer();"); javaWriter.println(" final ByteBuffer _res = get"+capitalFieldName+"0(source, 0);"); @@ -1909,7 +1926,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return "+baseJElemTypeName+".create(_res);"); javaWriter.println(" }"); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", fieldName, capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + result.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + result.length \"+result.length+\" > array-length \"+arrayLength); };"); @@ -1925,12 +1942,12 @@ public class JavaEmitter implements GlueEmitter { } else { // Getter Struct Array if( hasSingleElement ) { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" return "+baseJElemTypeName+".create(accessor.slice("+fieldName+"_offset[mdIdx], "+baseJElemTypeName+".size()));"); javaWriter.println(" }"); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", fieldName, capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + result.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + result.length \"+result.length+\" > array-length \"+arrayLength); };"); @@ -1947,12 +1964,9 @@ public class JavaEmitter implements GlueEmitter { } } - private static final boolean DEBUG_TYPEC2JAVA = false; private JavaType typeToJavaType(final Type cType, final MachineDataInfo curMachDesc) { final JavaType jt = typeToJavaTypeImpl(cType, curMachDesc); - if( DEBUG_TYPEC2JAVA ) { - System.err.println("typeToJavaType: "+cType.getDebugString()+" -> "+jt.getDebugString()); - } + LOG.log(FINE, cType.getASTLocusTag(), "typeToJavaType: {0} -> {1}", cType, jt); return jt; } private boolean isJNIEnvPointer(final Type cType) { @@ -1968,7 +1982,7 @@ public class JavaEmitter implements GlueEmitter { } // Opaque specifications override automatic conversions // in case the identity is being used .. not if ptr-ptr - final TypeInfo info = cfg.typeInfo(cType, typedefDictionary); + final TypeInfo info = cfg.typeInfo(cType); if (info != null) { boolean isPointerPointer = false; if (cType.pointerDepth() > 0 || cType.arrayDimension() > 0) { @@ -1986,16 +2000,16 @@ public class JavaEmitter implements GlueEmitter { // target type) if (targetType.isPointer()) { isPointerPointer = true; - - // t is<type>**, targetType is <type>*, we need to get <type> - final Type bottomType = targetType.asPointer().getTargetType(); if( GlueGen.debug() ) { - LOG.log(INFO, "Opaque Type: {0}, targetType: {1}, bottomType: {2} is ptr-ptr", new Object[]{cType.getDebugString(), targetType, bottomType}); + // t is<type>**, targetType is <type>*, we need to get <type> + final Type bottomType = targetType.asPointer().getTargetType(); + LOG.log(INFO, cType.getASTLocusTag(), "Opaque Type: {0}, targetType: {1}, bottomType: {2} is ptr-ptr", + cType, targetType, bottomType); } } } } - if(!isPointerPointer) { + if( !isPointerPointer ) { return info.javaType(); } } @@ -2006,8 +2020,9 @@ public class JavaEmitter implements GlueEmitter { case 2: return javaType(Short.TYPE); case 4: return javaType(Integer.TYPE); case 8: return javaType(Long.TYPE); - default: throw new RuntimeException("Unknown integer type of size " + - cType.getSize(curMachDesc) + " and name " + cType.getName()); + default: throw new GlueGenException("Unknown integer type of size " + + cType.getSize(curMachDesc) + " and name " + cType.getName(), + cType.getASTLocusTag()); } } else if (cType.isFloat()) { return javaType(Float.TYPE); @@ -2042,8 +2057,9 @@ public class JavaEmitter implements GlueEmitter { case 2: return JavaType.createForCShortPointer(); case 4: return JavaType.createForCInt32Pointer(); case 8: return JavaType.createForCInt64Pointer(); - default: throw new RuntimeException("Unknown integer array type of size " + - cType.getSize(curMachDesc) + " and name " + cType.getName()+", "+cType.getDebugString()); + default: throw new GlueGenException("Unknown integer array type of size " + + cType.getSize(curMachDesc) + " and name " + cType.getName()+", "+cType.getDebugString(), + cType.getASTLocusTag()); } } else if (targetType.isFloat()) { return JavaType.createForCFloatPointer(); @@ -2058,19 +2074,28 @@ public class JavaEmitter implements GlueEmitter { cType.getName().equals("jobject")) { return javaType(java.lang.Object.class); } - String name = targetType.getName(); - if (name == null) { - // Try containing pointer type for any typedefs - name = cType.getName(); - if (name == null) { - throw new RuntimeException("Couldn't find a proper type name for pointer type " + cType.getDebugString()); - } + // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser) + String name; + if( !targetType.isTypedef() && cType.isTypedef() ) { + // If compound is not a typedef _and_ containing pointer is typedef, use the latter. + name = cType.getName(); + } else { + // .. otherwise try compound name + name = targetType.getName(); + if( null == name ) { + // .. fall back to pointer type name + name = cType.getName(); + if (name == null) { + throw new GlueGenException("Couldn't find a proper type name for pointer type " + cType.getDebugString(), + cType.getASTLocusTag()); + } + } } - return JavaType.createForCStruct(cfg.renameJavaType(name)); } else { - throw new RuntimeException("Don't know how to convert pointer/array type \"" + - cType.getDebugString() + "\""); + throw new GlueGenException("Don't know how to convert pointer/array type \"" + + cType.getDebugString() + "\"", + cType.getASTLocusTag()); } } // Handle Types of form pointer-to-pointer-to-type or @@ -2084,19 +2109,22 @@ public class JavaEmitter implements GlueEmitter { // t is<type>**, targetType is <type>*, we need to get <type> bottomType = targetType.asPointer().getTargetType(); if( GlueGen.debug() ) { - LOG.log(INFO, "typeToJavaType(ptr-ptr): {0}, targetType: {1}, bottomType: {2}", new Object[]{cType.getDebugString(), targetType, bottomType}); + LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr): {0}, targetType: {1}, bottomType: {2}", + cType.getDebugString(), targetType, bottomType); } return JavaType.forNIOPointerBufferClass(); } else if(targetType.isArray()) { // t is<type>[][], targetType is <type>[], we need to get <type> bottomType = targetType.asArray().getBaseElementType(); if( GlueGen.debug() ) { - LOG.log(INFO, "typeToJavaType(ptr-ptr.array): {0}, targetType: {1}, bottomType: {2}", new Object[]{cType.getDebugString(), targetType, bottomType}); + LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr.array): {0}, targetType: {1}, bottomType: {2}", + cType.getDebugString(), targetType, bottomType); } } else { bottomType = targetType; if( GlueGen.debug() ) { - LOG.log(INFO, "typeToJavaType(ptr-ptr.primitive): {0}, targetType: {1}, bottomType: {2}", new Object[]{cType.getDebugString(), targetType, bottomType}); + LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr.primitive): {0}, targetType: {1}, bottomType: {2}", + cType.getDebugString(), targetType, bottomType); } } @@ -2110,16 +2138,17 @@ public class JavaEmitter implements GlueEmitter { case 2: return javaType(ArrayTypes.shortBufferArrayClass); case 4: return javaType(ArrayTypes.intBufferArrayClass); case 8: return javaType(ArrayTypes.longBufferArrayClass); - default: throw new RuntimeException("Unknown two-dimensional integer array type of element size " + - bottomType.getSize(curMachDesc) + " and name " + bottomType.getName()+", "+bottomType.getDebugString()); + default: throw new GlueGenException("Unknown two-dimensional integer array type of element size " + + bottomType.getSize(curMachDesc) + " and name " + bottomType.getName()+", "+bottomType.getDebugString(), + bottomType.getASTLocusTag()); } } else if (bottomType.isFloat()) { return javaType(ArrayTypes.floatBufferArrayClass); } else if (bottomType.isDouble()) { return javaType(ArrayTypes.doubleBufferArrayClass); } else { - throw new RuntimeException("Unexpected primitive type " + bottomType.getDebugString() + - " in two-dimensional array"); + throw new GlueGenException("Unexpected primitive type " + bottomType.getDebugString() + + " in two-dimensional array", bottomType.getASTLocusTag()); } } else if (bottomType.isVoid()) { return javaType(ArrayTypes.bufferArrayClass); @@ -2128,32 +2157,38 @@ public class JavaEmitter implements GlueEmitter { // Array of pointers; convert as array of StructAccessors return JavaType.createForCArray(bottomType); } else { - throw new RuntimeException( + throw new GlueGenException( "Could not convert C type \"" + cType.getDebugString() + "\" " + "to appropriate Java type; need to add more support for " + "depth=2 pointer/array types [debug info: targetType=\"" + - targetType + "\"]"); + targetType + "\"]", cType.getASTLocusTag()); } } else { // can't handle this type of pointer/array argument - throw new RuntimeException( + throw new GlueGenException( "Could not convert C pointer/array \"" + cType.getDebugString() + "\" to " + "appropriate Java type; types with pointer/array depth " + "greater than 2 are not yet supported [debug info: " + "pointerDepth=" + cType.pointerDepth() + " arrayDimension=" + - cType.arrayDimension() + " targetType=\"" + targetType + "\"]"); + cType.arrayDimension() + " targetType=\"" + targetType + "\"]", + cType.getASTLocusTag()); } - } else if(cType.isCompound() ) { // FIXME: Compound and Compound-Arrays - final String name = cType.getName(); + } else if( cType.isCompound() ) { // FIXME: Compound and Compound-Arrays + String name = cType.getName(); if (name == null) { - throw new RuntimeException("Couldn't find a proper type name for pointer type " + cType.getDebugString()); + name = cType.asCompound().getStructName(); + if (name == null) { + throw new GlueGenException("Couldn't find a proper type name for pointer type " + cType.getDebugString(), + cType.getASTLocusTag()); + } } return JavaType.createForCStruct(cfg.renameJavaType(name)); } else { - throw new RuntimeException( + throw new GlueGenException( "Could not convert C type \"" + cType.getDebugString() + "\" (class " + - cType.getClass().getName() + ") to appropriate Java type"); + cType.getClass().getName() + ") to appropriate Java type", + cType.getASTLocusTag()); } } @@ -2189,7 +2224,7 @@ public class JavaEmitter implements GlueEmitter { } private boolean isOpaque(final Type type) { - return (cfg.typeInfo(type, typedefDictionary) != null); + return null != cfg.typeInfo(type); } private String compatiblePrimitiveJavaTypeName(final Type fieldType, @@ -2198,14 +2233,16 @@ public class JavaEmitter implements GlueEmitter { final Class<?> c = javaType.getJavaClass(); if (!isIntegerType(c)) { // FIXME - throw new RuntimeException("Can't yet handle opaque definitions of structs' fields to non-integer types (byte, short, int, long, etc.): type: "+fieldType+", javaType "+javaType+", javaClass "+c); + throw new GlueGenException("Can't yet handle opaque definitions of structs' fields to non-integer types (byte, short, int, long, etc.): type: "+fieldType+", javaType "+javaType+", javaClass "+c, + fieldType.getASTLocusTag()); } switch ((int) fieldType.getSize(curMachDesc)) { case 1: return "byte"; case 2: return "short"; case 4: return "int"; case 8: return "long"; - default: throw new RuntimeException("Can't handle opaque definitions if the starting type isn't compatible with integral types"); + default: throw new GlueGenException("Can't handle opaque definitions if the starting type isn't compatible with integral types", + fieldType.getASTLocusTag()); } } @@ -2229,13 +2266,16 @@ public class JavaEmitter implements GlueEmitter { } if (cfg.allStatic() || cfg.emitInterface()) { - javaWriter = openFile(jRoot + File.separator + cfg.className() + ".java", cfg.className()); + javaFileName = jRoot + File.separator + cfg.className() + ".java"; + javaWriter = openFile(javaFileName, cfg.className()); } if (!cfg.allStatic() && cfg.emitImpl()) { - javaImplWriter = openFile(jImplRoot + File.separator + cfg.implClassName() + ".java", cfg.implClassName()); + javaFileName = jImplRoot + File.separator + cfg.implClassName() + ".java"; + javaImplWriter = openFile(javaFileName, cfg.implClassName()); } if (cfg.emitImpl()) { - cWriter = openFile(nRoot + File.separator + cfg.implClassName() + "_JNI.c", cfg.implClassName()); + cFileName = nRoot + File.separator + cfg.implClassName() + "_JNI.c"; + cWriter = openFile(cFileName, cfg.implClassName()); } if (javaWriter != null) { @@ -2249,6 +2289,9 @@ public class JavaEmitter implements GlueEmitter { } } + /** For {@link #javaWriter} or {@link #javaImplWriter} */ + protected String javaFileName() { return javaFileName; } + protected PrintWriter javaWriter() { if (!cfg.allStatic() && !cfg.emitInterface()) { throw new InternalError("Should not call this"); @@ -2263,6 +2306,9 @@ public class JavaEmitter implements GlueEmitter { return javaImplWriter; } + /** For {@link #cImplWriter} */ + protected String cFileName() { return cFileName; } + protected PrintWriter cWriter() { if (!cfg.emitImpl()) { throw new InternalError("Should not call this"); @@ -2454,8 +2500,9 @@ public class JavaEmitter implements GlueEmitter { if (getConfig().emitImpl()) { cWriter.println("#include <assert.h>"); + cWriter.println("#include <stddef.h>"); cWriter.println(); - cWriter.println("static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity); /* forward decl. */"); + cWriter.println("static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, size_t capacity); /* forward decl. */"); cWriter.println(); } for (final String code : cfg.customCCode()) { @@ -2468,6 +2515,7 @@ public class JavaEmitter implements GlueEmitter { "static const char * clazzNameBuffers = \"com/jogamp/common/nio/Buffers\";\n"+ "static const char * clazzNameBuffersStaticNewCstrName = \"newDirectByteBuffer\";\n"+ "static const char * clazzNameBuffersStaticNewCstrSignature = \"(I)Ljava/nio/ByteBuffer;\";\n"+ + "static const char * sFatalError = \"FatalError:\";\n"+ "static jclass clazzBuffers = NULL;\n"+ "static jmethodID cstrBuffersNew = NULL;\n"+ "static jboolean _initClazzAccessDone = JNI_FALSE;\n"+ @@ -2479,13 +2527,13 @@ public class JavaEmitter implements GlueEmitter { "\n"+ " c = (*env)->FindClass(env, clazzNameBuffers);\n"+ " if(NULL==c) {\n"+ - " fprintf(stderr, \"FatalError: Can't find %s\\n\", clazzNameBuffers);\n"+ + " fprintf(stderr, \"%s Can't find %s\\n\", sFatalError, clazzNameBuffers);\n"+ " (*env)->FatalError(env, clazzNameBuffers);\n"+ " return JNI_FALSE;\n"+ " }\n"+ " clazzBuffers = (jclass)(*env)->NewGlobalRef(env, c);\n"+ " if(NULL==clazzBuffers) {\n"+ - " fprintf(stderr, \"FatalError: Can't use %s\\n\", clazzNameBuffers);\n"+ + " fprintf(stderr, \"%s Can't use %s\\n\", sFatalError, clazzNameBuffers);\n"+ " (*env)->FatalError(env, clazzNameBuffers);\n"+ " return JNI_FALSE;\n"+ " }\n"+ @@ -2493,7 +2541,7 @@ public class JavaEmitter implements GlueEmitter { " cstrBuffersNew = (*env)->GetStaticMethodID(env, clazzBuffers,\n"+ " clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+ " if(NULL==cstrBuffersNew) {\n"+ - " fprintf(stderr, \"FatalError: can't create %s.%s %s\\n\",\n"+ + " fprintf(stderr, \"%s can't create %s.%s %s\\n\", sFatalError,\n"+ " clazzNameBuffers,\n"+ " clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+ " (*env)->FatalError(env, clazzNameBuffersStaticNewCstrName);\n"+ @@ -2503,18 +2551,35 @@ public class JavaEmitter implements GlueEmitter { " return JNI_TRUE;\n"+ "}\n"+ "\n"+ - "static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity) {\n"+ + "#define JINT_MAX_VALUE ((size_t)0x7fffffffU)\n"+ + "static const char * sNewBufferImplNotCalled = \"initializeImpl() not called\";\n"+ + "static const char * sNewBufferMAX_INT = \"capacity > MAX_INT\";\n"+ + "static const char * sNewBufferNULL = \"New direct ByteBuffer is NULL\";\n"+ + "\n"+ + "static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, size_t capacity) {\n"+ " jobject jbyteBuffer;\n"+ " void * byteBufferPtr;\n"+ "\n"+ " if( JNI_FALSE == _initClazzAccessDone ) {\n"+ - " fprintf(stderr, \"FatalError: initializeImpl() not called\\n\");\n"+ - " (*env)->FatalError(env, \"initializeImpl() not called\");\n"+ + " fprintf(stderr, \"%s %s\\n\", sFatalError, sNewBufferImplNotCalled);\n"+ + " (*env)->FatalError(env, sNewBufferImplNotCalled);\n"+ " return NULL;\n"+ " }\n"+ - " jbyteBuffer = (*env)->CallStaticObjectMethod(env, clazzBuffers, cstrBuffersNew, capacity);\n"+ - " byteBufferPtr = (*env)->GetDirectBufferAddress(env, jbyteBuffer);\n"+ - " memcpy(byteBufferPtr, source_address, capacity);\n"+ + " if( JINT_MAX_VALUE < capacity ) {\n"+ + " fprintf(stderr, \"%s %s: %lu\\n\", sFatalError, sNewBufferMAX_INT, (unsigned long)capacity);\n"+ + " (*env)->FatalError(env, sNewBufferMAX_INT);\n"+ + " return NULL;\n"+ + " }\n"+ + " jbyteBuffer = (*env)->CallStaticObjectMethod(env, clazzBuffers, cstrBuffersNew, (jint)capacity);\n"+ + " if( NULL == jbyteBuffer ) {\n"+ + " fprintf(stderr, \"%s %s: size %lu\\n\", sFatalError, sNewBufferNULL, (unsigned long)capacity);\n"+ + " (*env)->FatalError(env, sNewBufferNULL);\n"+ + " return NULL;\n"+ + " }\n"+ + " if( 0 < capacity ) {\n"+ + " byteBufferPtr = (*env)->GetDirectBufferAddress(env, jbyteBuffer);\n"+ + " memcpy(byteBufferPtr, source_address, capacity);\n"+ + " }\n"+ " return jbyteBuffer;\n"+ "}\n"+ "\n"; @@ -2580,36 +2645,46 @@ public class JavaEmitter implements GlueEmitter { potentially representing C pointers rather than true Java types) and must be lowered to concrete Java types before creating emitters for them. */ - private MethodBinding bindFunction(final FunctionSymbol sym, - final JavaType containingType, - final Type containingCType, - final MachineDataInfo curMachDesc) { - - final MethodBinding binding = new MethodBinding(sym, containingType, containingCType); - - binding.renameMethodName(cfg.getJavaSymbolRename(sym.getName())); - - // System.out.println("bindFunction(0) "+sym.getReturnType()); - - if (cfg.returnsString(binding.getName())) { + private MethodBinding bindFunction(FunctionSymbol sym, + final boolean forInterface, + final MachineDataInfo curMachDesc, + final JavaType containingType, final Type containingCType) { + + final String delegationImplName = null == containingType && null == containingCType ? + cfg.getDelegatedImplementation(sym) : null; + if( !forInterface && null != delegationImplName ) { + // We need to reflect the 'delegationImplName' for implementations + // to allow all subsequent type/cfg checks to hit on AliasedSymbol! + sym = FunctionSymbol.cloneWithDeepAliases(sym); + sym.addAliasedName(delegationImplName); + } + final String name = sym.getName(); + final JavaType javaReturnType; + + if (cfg.returnsString(sym)) { final PointerType prt = sym.getReturnType().asPointer(); if (prt == null || prt.getTargetType().asInt() == null || prt.getTargetType().getSize(curMachDesc) != 1) { - throw new RuntimeException( + throw new GlueGenException( "Cannot apply ReturnsString configuration directive to \"" + sym + - "\". ReturnsString requires native method to have return type \"char *\""); + "\". ReturnsString requires native method to have return type \"char *\"", + sym.getASTLocusTag()); } - binding.setJavaReturnType(javaType(java.lang.String.class)); + javaReturnType = javaType(java.lang.String.class); } else { - binding.setJavaReturnType(typeToJavaType(sym.getReturnType(), curMachDesc)); + final JavaType r = cfg.getOpaqueReturnType(sym); + if( null != r ) { + javaReturnType = r; + } else { + javaReturnType = typeToJavaType(sym.getReturnType(), curMachDesc); + } } - // System.out.println("bindFunction(1) "+binding.getJavaReturnType()); - // List of the indices of the arguments in this function that should be // converted from byte[] or short[] to String - final List<Integer> stringArgIndices = cfg.stringArguments(binding.getName()); + final List<JavaType> javaArgumentTypes = new ArrayList<JavaType>(); + final List<Integer> stringArgIndices = cfg.stringArguments(name); for (int i = 0; i < sym.getNumArguments(); i++) { final Type cArgType = sym.getArgumentType(i); @@ -2637,20 +2712,21 @@ public class JavaEmitter implements GlueEmitter { } } else { - throw new RuntimeException( + throw new GlueGenException( "Cannot apply ArgumentIsString configuration directive to " + "argument " + i + " of \"" + sym + "\": argument type is not " + - "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent"); + "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent", + sym.getASTLocusTag()); } } - binding.addJavaArgumentType(mappedType); + javaArgumentTypes.add(mappedType); //System.out.println("During binding of [" + sym + "], added mapping from C type: " + cArgType + " to Java type: " + mappedType); } - - // System.out.println("---> " + binding); - // System.out.println(" ---> " + binding.getCSymbol()); - // System.out.println("bindFunction(3) "+binding); - return binding; + final MethodBinding mb = new MethodBinding(sym, delegationImplName, + javaReturnType, javaArgumentTypes, + containingType, containingCType); + mangleBinding(mb); + return mb; } private MethodBinding lowerMethodBindingPointerTypes(final MethodBinding inputBinding, @@ -2710,7 +2786,7 @@ public class JavaEmitter implements GlueEmitter { result = result.replaceJavaArgumentType(i, JavaType.forNIODoubleBufferClass()); } } else { - throw new RuntimeException("Unknown C pointer type " + t); + throw new GlueGenException("Unknown C pointer type " + t); } } } @@ -2735,7 +2811,7 @@ public class JavaEmitter implements GlueEmitter { } else if (t.isCDoublePointerType()) { result = result.replaceJavaArgumentType(-1, JavaType.forNIODoubleBufferClass()); } else { - throw new RuntimeException("Unknown C pointer type " + t); + throw new GlueGenException("Unknown C pointer type " + t, result.getCReturnType().getASTLocusTag()); } } @@ -2748,6 +2824,14 @@ public class JavaEmitter implements GlueEmitter { return result; } + /** + * Allow specializations to modify the given {@link MethodBinding} + * before {@link #expandMethodBinding(MethodBinding) expanding} and emission. + */ + protected void mangleBinding(final MethodBinding binding) { + // NOP + } + // Expands a MethodBinding containing C primitive pointer types into // multiple variants taking Java primitive arrays and NIO buffers, subject // to the per-function "NIO only" rule in the configuration file @@ -2779,10 +2863,11 @@ public class JavaEmitter implements GlueEmitter { private Type canonicalize(final Type t) { final Type res = canonMap.get(t); if (res != null) { - return res; + return res; + } else { + canonMap.put(t, t); + return t; } - canonMap.put(t, t); - return t; } /** diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java index 6966315..d3fca14 100644 --- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -40,14 +40,17 @@ package com.jogamp.gluegen; import com.jogamp.gluegen.cgram.HeaderParser; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; import com.jogamp.gluegen.cgram.types.ArrayType; import com.jogamp.gluegen.cgram.types.EnumType; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; import java.io.PrintWriter; import java.text.MessageFormat; import java.util.Iterator; import java.util.List; +import java.util.Set; /** * An emitter that emits only the interface for a Java<->C JNI binding. @@ -64,22 +67,22 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { protected final CommentEmitter defaultJavaCommentEmitter = new DefaultCommentEmitter(); protected final CommentEmitter defaultInterfaceCommentEmitter = new InterfaceCommentEmitter(); + protected final boolean tagNativeBinding; + protected final boolean useNIODirectOnly; + protected final MethodBinding binding; // Exception type raised in the generated code if runtime checks fail private final String runtimeExceptionType; private final String unsupportedExceptionType; + private final boolean useNIOOnly; + private final boolean isNativeMethod; + private final boolean isUnimplemented; - protected boolean emitBody; - protected boolean eraseBufferAndArrayTypes; - protected boolean useNIOOnly; - protected boolean useNIODirectOnly; - protected boolean forImplementingMethodCall; - protected boolean forDirectBufferImplementation; - protected boolean forIndirectBufferAndArrayImplementation; - protected boolean isUnimplemented; - protected boolean tagNativeBinding; - - protected MethodBinding binding; + private boolean emitBody; + private boolean eraseBufferAndArrayTypes; + private boolean isPrivateNativeMethod; + private boolean forDirectBufferImplementation; + private boolean forIndirectBufferAndArrayImplementation; // Manually-specified prologue and epilogue code protected List<String> prologue; @@ -97,9 +100,6 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { // represent an array of compound type wrappers private static final String COMPOUND_ARRAY_SUFFIX = "_buf_array_copy"; - // Only present to provide more clear comments - private final JavaConfiguration cfg; - public JavaMethodBindingEmitter(final MethodBinding binding, final PrintWriter output, final String runtimeExceptionType, @@ -109,13 +109,13 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { final boolean eraseBufferAndArrayTypes, final boolean useNIOOnly, final boolean useNIODirectOnly, - final boolean forImplementingMethodCall, final boolean forDirectBufferImplementation, final boolean forIndirectBufferAndArrayImplementation, final boolean isUnimplemented, final boolean isInterface, - final JavaConfiguration configuration) { - super(output, isInterface); + final boolean isNativeMethod, + final boolean isPrivateNativeMethod, final JavaConfiguration configuration) { + super(output, isInterface, configuration); this.binding = binding; this.runtimeExceptionType = runtimeExceptionType; this.unsupportedExceptionType = unsupportedExceptionType; @@ -124,16 +124,17 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { this.eraseBufferAndArrayTypes = eraseBufferAndArrayTypes; this.useNIOOnly = useNIOOnly; this.useNIODirectOnly = useNIODirectOnly; - this.forImplementingMethodCall = forImplementingMethodCall; this.forDirectBufferImplementation = forDirectBufferImplementation; this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; this.isUnimplemented = isUnimplemented; - if (forImplementingMethodCall) { + this.isNativeMethod = isNativeMethod; + this.isPrivateNativeMethod = isPrivateNativeMethod; + if (isPrivateNativeMethod) { setCommentEmitter(defaultJavaCommentEmitter); } else { setCommentEmitter(defaultInterfaceCommentEmitter); } - cfg = configuration; + // !forImplementingMethodCall && !isInterface } public JavaMethodBindingEmitter(final JavaMethodBindingEmitter arg) { @@ -146,7 +147,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { eraseBufferAndArrayTypes = arg.eraseBufferAndArrayTypes; useNIOOnly = arg.useNIOOnly; useNIODirectOnly = arg.useNIODirectOnly; - forImplementingMethodCall = arg.forImplementingMethodCall; + isNativeMethod = arg.isNativeMethod; + isPrivateNativeMethod = arg.isPrivateNativeMethod; forDirectBufferImplementation = arg.forDirectBufferImplementation; forIndirectBufferAndArrayImplementation = arg.forIndirectBufferAndArrayImplementation; isUnimplemented = arg.isUnimplemented; @@ -154,18 +156,31 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { epilogue = arg.epilogue; returnedArrayLengthExpression = arg.returnedArrayLengthExpression; returnedArrayLengthExpressionOnlyForComments = arg.returnedArrayLengthExpressionOnlyForComments; - cfg = arg.cfg; } public final MethodBinding getBinding() { return binding; } - public boolean isForImplementingMethodCall() { return forImplementingMethodCall; } + public boolean isNativeMethod() { return isNativeMethod; } + public boolean isPrivateNativeMethod() { return isPrivateNativeMethod; } public boolean isForDirectBufferImplementation() { return forDirectBufferImplementation; } public boolean isForIndirectBufferAndArrayImplementation() { return forIndirectBufferAndArrayImplementation; } @Override - public String getName() { - return binding.getName(); + public String getInterfaceName() { + return binding.getInterfaceName(); + } + @Override + public String getImplName() { + return binding.getImplName(); + } + @Override + public String getNativeName() { + return binding.getNativeName(); + } + + @Override + public FunctionSymbol getCSymbol() { + return binding.getCSymbol(); } protected String getArgumentName(final int i) { @@ -233,8 +248,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } /** Accessor for subclasses. */ - public void setForImplementingMethodCall(final boolean impl) { - this.forImplementingMethodCall = impl; + public void setPrivateNativeMethod(final boolean v) { + this.isPrivateNativeMethod = v; } /** Accessor for subclasses. */ @@ -322,10 +337,12 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { @Override protected void emitName(final PrintWriter writer) { - if (forImplementingMethodCall) { - writer.print(getImplMethodName()); + if (isPrivateNativeMethod) { + writer.print(getNativeImplMethodName()); + } else if( isInterface()) { + writer.print(getInterfaceName()); } else { - writer.print(getName()); + writer.print(getImplName()); } } @@ -334,7 +351,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { boolean needComma = false; int numEmitted = 0; - if (forImplementingMethodCall && binding.hasContainingType()) { + if (isPrivateNativeMethod && binding.hasContainingType()) { // Always emit outgoing "this" argument writer.print("ByteBuffer "); writer.print(javaThisArgumentName()); @@ -395,8 +412,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } - protected String getImplMethodName() { - return binding.getName() + ( useNIODirectOnly ? "0" : "1" ); + protected String getNativeImplMethodName() { + return binding.getImplName() + ( useNIODirectOnly ? "0" : "1" ); } protected String byteOffsetArgName(final int i) { @@ -544,7 +561,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } protected void emitCall(final MethodBinding binding, final PrintWriter writer) { - writer.print(getImplMethodName()); + writer.print(getNativeImplMethodName()); writer.print("("); emitCallArguments(binding, writer); writer.print(");"); @@ -675,9 +692,10 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } else if(type.isIntArray()) { writer.print(", Buffers.SIZEOF_INT * "); } else { - throw new RuntimeException("Unsupported type for calculating array offset argument for " + + throw new GlueGenException("Unsupported type for calculating array offset argument for " + getArgumentName(i) + - " -- error occurred while processing Java glue code for " + getName()); + " -- error occurred while processing Java glue code for " + getCSymbol().getAliasedString(), + getCSymbol().getASTLocusTag()); } writer.print(offsetArgName(i)); } @@ -688,7 +706,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } } else if (type.isPrimitiveArray()) { if (useNIOOnly) { - throw new RuntimeException("NIO[Direct]Only "+binding+" is set, but "+getArgumentName(i)+" is a primitive array"); + throw new GlueGenException("NIO[Direct]Only "+binding+" is set, but "+getArgumentName(i)+" is a primitive array", + getCSymbol().getASTLocusTag()); } writer.print( ", false"); } @@ -706,7 +725,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { // ByteBuffers back into the wrapper types for (int i = 0; i < binding.getNumArguments(); i++) { final JavaType javaArgType = binding.getJavaArgumentType(i); - if ( javaArgType.isArrayOfCompoundTypeWrappers() && !isBaseTypeConst(javaArgType.getElementCType()) ) { + if ( javaArgType.isArrayOfCompoundTypeWrappers() && !javaArgType.getElementCType().isBaseTypeConst() ) { final String argName = binding.getArgumentName(i); writer.println(" for (int _ctr = 0; _ctr < " + argName + ".length; _ctr++) {"); writer.println(" if ((" + argName + "[_ctr] == null && " + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr] == null) ||"); @@ -743,8 +762,9 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } else if (returnType.isNIOLongBuffer()) { writer.println(" return _res.asLongBuffer();"); } else { - throw new RuntimeException("While emitting glue code for " + getName() + - ": can not legally make pointers opaque to anything but PointerBuffer or LongBuffer/long"); + throw new GlueGenException("While emitting glue code for " + getCSymbol().getAliasedString() + + ": can not legally make pointers opaque to anything but PointerBuffer or LongBuffer/long", + getCSymbol().getASTLocusTag()); } } else if (getBinding().getCReturnType().pointerDepth() == 1 && returnType.isNIOLongBuffer()) { writer.println(" return _res.asLongBuffer();"); @@ -812,6 +832,26 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { * emitter java method. */ protected class DefaultCommentEmitter implements CommentEmitter { + protected void emitAliasedDocNamesComment(final AliasedSymbol sym, final PrintWriter writer) { + writer.print(emitAliasedDocNamesComment(sym, new StringBuilder()).toString()); + } + protected StringBuilder emitAliasedDocNamesComment(final AliasedSymbol sym, final StringBuilder sb) { + final Set<String> aliases = cfg.getAliasedDocNames(sym); + if (aliases != null && aliases.size() > 0 ) { + int i=0; + sb.append("Alias for: <code>"); + for (final String alias : aliases) { + if(0 < i) { + sb.append("</code>, <code>"); + } + sb.append(alias); + i++; + } + sb.append("</code>"); + } + return sb; + } + @Override public void emit(final FunctionEmitter emitter, final PrintWriter writer) { emitBeginning(emitter, writer); @@ -826,9 +866,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { writer.print("Entry point to C language function: "); } protected void emitBindingCSignature(final MethodBinding binding, final PrintWriter writer) { - writer.print("<code> "); - writer.print(binding.getCSymbol().toString(tagNativeBinding)); - writer.print(" </code> "); + final FunctionSymbol funcSym = binding.getCSymbol(); + writer.print("<code>"); + writer.print(funcSym.toString(tagNativeBinding)); + writer.print("</code><br>"); + emitAliasedDocNamesComment(funcSym, writer); } protected void emitEnding(final FunctionEmitter emitter, final PrintWriter writer) { // If argument type is a named enum, then emit a comment detailing the @@ -852,7 +894,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { writer.print(" valid values are: <code>"); for (int j = 0; j < enumType.getNumEnumerates(); ++j) { if (j>0) writer.print(", "); - writer.print(enumType.getEnumName(j)); + writer.print(enumType.getEnum(j).getName()); } writer.println("</code>"); } else if (javaType.isNIOBuffer()) { diff --git a/src/java/com/jogamp/gluegen/JavaType.java b/src/java/com/jogamp/gluegen/JavaType.java index 87804bd..9bcd663 100644 --- a/src/java/com/jogamp/gluegen/JavaType.java +++ b/src/java/com/jogamp/gluegen/JavaType.java @@ -63,6 +63,7 @@ public class JavaType { private final String structName; // Types we're generating glue code for (i.e., C structs) private final Type elementType; // Element type if this JavaType represents a C array private final C_PTR primitivePointerType; + private final boolean opaqued; private static JavaType nioBufferType; private static JavaType nioByteBufferType; @@ -107,12 +108,20 @@ public class JavaType { return elementType; } + /** Creates a JavaType corresponding to the given opaque Java type. This + can be used to represent arrays of primitive values or Strings; + the emitters understand how to perform proper conversion from + the corresponding C type. */ + public static JavaType createForOpaqueClass(final Class<?> clazz) { + return new JavaType(clazz, true); + } + /** Creates a JavaType corresponding to the given Java type. This can be used to represent arrays of primitive values or Strings; the emitters understand how to perform proper conversion from the corresponding C type. */ public static JavaType createForClass(final Class<?> clazz) { - return new JavaType(clazz); + return new JavaType(clazz, false); } /** Creates a JavaType corresponding to the specified C CompoundType @@ -336,6 +345,8 @@ public class JavaType { return "jobject"; } + public boolean isOpaqued() { return opaqued; } + public boolean isNIOBuffer() { return clazz != null && ( java.nio.Buffer.class.isAssignableFrom(clazz) || com.jogamp.common.nio.NativeBuffer.class.isAssignableFrom(clazz)) ; @@ -528,34 +539,39 @@ public class JavaType { append(sb, "primitivePointerType = "+primitivePointerType, prepComma); prepComma=true; } append(sb, "is[", prepComma); prepComma=false; - if( isArray() ) { - append(sb, "array", prepComma); prepComma=true; - } - if( isArrayOfCompoundTypeWrappers() ) { - append(sb, "compoundArray", prepComma); prepComma=true; - } - if( isCompoundTypeWrapper() ) { - append(sb, "compound", prepComma); prepComma=true; - } - if( isArray() ) { - append(sb, "array", prepComma); prepComma=true; - } - if( isPrimitive() ) { - append(sb, "primitive", prepComma); prepComma=true; - } - if( isPrimitiveArray() ) { - append(sb, "primitiveArray", prepComma); prepComma=true; - } - if( isNIOBuffer() ) { - append(sb, "nioBuffer", prepComma); prepComma=true; - } - if( isNIOBufferArray() ) { - append(sb, "nioBufferArray", prepComma); prepComma=true; - } - if( isCPrimitivePointerType() ) { - append(sb, "C-Primitive-Pointer", prepComma); prepComma=true; + { + if( isOpaqued() ) { + append(sb, "opaque", prepComma); prepComma=true; + } + if( isArray() ) { + append(sb, "array", prepComma); prepComma=true; + } + if( isArrayOfCompoundTypeWrappers() ) { + append(sb, "compoundArray", prepComma); prepComma=true; + } + if( isCompoundTypeWrapper() ) { + append(sb, "compound", prepComma); prepComma=true; + } + if( isArray() ) { + append(sb, "array", prepComma); prepComma=true; + } + if( isPrimitive() ) { + append(sb, "primitive", prepComma); prepComma=true; + } + if( isPrimitiveArray() ) { + append(sb, "primitiveArray", prepComma); prepComma=true; + } + if( isNIOBuffer() ) { + append(sb, "nioBuffer", prepComma); prepComma=true; + } + if( isNIOBufferArray() ) { + append(sb, "nioBufferArray", prepComma); prepComma=true; + } + if( isCPrimitivePointerType() ) { + append(sb, "C-Primitive-Pointer", prepComma); prepComma=true; + } } - append(sb, "descriptor '"+getDescriptor()+"'", prepComma); prepComma=true; + append(sb, "], descriptor '"+getDescriptor()+"']", prepComma); prepComma=true; return sb.toString(); } @@ -563,11 +579,12 @@ public class JavaType { * Constructs a representation for a type corresponding to the given Class * argument. */ - private JavaType(final Class<?> clazz) { + private JavaType(final Class<?> clazz, final boolean opaqued) { this.primitivePointerType = null; this.clazz = clazz; this.structName = null; this.elementType = null; + this.opaqued = opaqued; } /** Constructs a type representing a named C struct. */ @@ -576,6 +593,7 @@ public class JavaType { this.clazz = null; this.structName = structName; this.elementType = null; + this.opaqued = false; } /** Constructs a type representing a pointer to a C primitive @@ -585,6 +603,7 @@ public class JavaType { this.clazz = null; this.structName = null; this.elementType = null; + this.opaqued = false; } /** Constructs a type representing an array of C pointers. */ @@ -593,6 +612,7 @@ public class JavaType { this.clazz = null; this.structName = null; this.elementType = elementType; + this.opaqued = false; } /** clone only */ @@ -601,6 +621,7 @@ public class JavaType { this.clazz = clazz; this.structName = name; this.elementType = elementType; + this.opaqued = false; } private String arrayName(Class<?> clazz) { diff --git a/src/java/com/jogamp/gluegen/Logging.java b/src/java/com/jogamp/gluegen/Logging.java index 40eadcb..c057db4 100644 --- a/src/java/com/jogamp/gluegen/Logging.java +++ b/src/java/com/jogamp/gluegen/Logging.java @@ -31,57 +31,352 @@ */ package com.jogamp.gluegen; +import java.util.HashMap; +import java.util.Map; import java.util.logging.ConsoleHandler; import java.util.logging.Formatter; +import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; +import jogamp.common.Debug; + import com.jogamp.common.util.PropertyAccess; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; +import com.jogamp.gluegen.cgram.types.Type; /** * - * @author Michael Bien + * @author Michael Bien, et.al. */ public class Logging { + public static final boolean DEBUG = Debug.debug("Logging"); + + /** + * An interface for {@link Logger}. + */ + public static interface LoggerIf { + /** + * See {@link Logger#info(String)} + */ + void info(final String msg); + /** + * See {@link Logger#info(String)} + */ + void info(final ASTLocusTag loc, final String msg); + + /** + * See {@link Logger#warning(String)} + */ + void warning(final String msg); + /** + * See {@link Logger#warning(String)} + */ + void warning(final ASTLocusTag loc, final String msg); + + /** + * Calls {@link #log(Level, String)} w/ {@link Level#FINE}. + */ + void debug(final String msg); + /** + * Calls {@link #log(Level, ASTLocusTag, String)} w/ {@link Level#FINE}. + */ + void debug(final ASTLocusTag loc, final String msg); + + /** + * See {@link Logger#log(Level, String)} + */ + void log(final Level level, final String msg); + /** + * See {@link Logger#log(Level, String, Object)} + */ + void log(final Level level, final String msg, final Object param); + /** + * See {@link Logger#log(Level, String, Object[])} + */ + void log(final Level level, final String msg, final Object ... params); + + /** + * See {@link Logger#log(Level, String)} + */ + void log(final Level level, final ASTLocusTag loc, final String msg); + /** + * See {@link Logger#log(Level, String, Object)} + */ + void log(final Level level, final ASTLocusTag loc, final String msg, final Object param); + /** + * See {@link Logger#log(Level, String, Object[])} + */ + void log(final Level level, final ASTLocusTag loc, final String msg, final Object ... params); + + /** + * See {@link Logger#setLevel(Level)} + */ + void setLevel(final Level newLevel) throws SecurityException; + /** + * See {@link Handler#setLevel(Level)} + */ + void setLevelOfAllHandler(final Level newLevel) throws SecurityException; + /** + * See {@link Logger#getLevel()} + */ + Level getLevel(); + /** + * See {@link Logger#isLoggable(Level)} + */ + boolean isLoggable(Level level); + /** + * See {@link Logger#getName()} + */ + String getName(); + /** + * See {@link Logger#getHandlers()} + */ + Handler[] getHandlers(); + /** + * See {@link LogRecord#getSourceClassName()} + */ + String getSourceClassName(); + } + /* pp */ static class FQNLogger implements LoggerIf { + public final Logger impl; + public final PlainLogConsoleHandler handler; + /* pp */ FQNLogger(final String fqnClassName, final String simpleClassName, final Level level) { + this.impl = Logger.getLogger(fqnClassName); + this.handler = new PlainLogConsoleHandler(new PlainLogFormatter(simpleClassName), Level.ALL); + this.impl.setUseParentHandlers(false); + this.impl.setLevel(level); + this.impl.addHandler(this.handler); + this.impl.log(Level.INFO, "Logging.new: "+impl.getName()+": level "+level+ + ": obj 0x"+Integer.toHexString(impl.hashCode())); + } + @Override + public void info(final String msg) { + impl.info(msg); + } + @Override + public void info(final ASTLocusTag loc, final String msg) { + handler.plf.setASTLocusTag(loc); + try { + impl.info(msg); + } finally { + handler.plf.setASTLocusTag(null); + } + } + + @Override + public void warning(final String msg) { + impl.warning(msg); + } + @Override + public void warning(final ASTLocusTag loc, final String msg) { + handler.plf.setASTLocusTag(loc); + try { + impl.warning(msg); + } finally { + handler.plf.setASTLocusTag(null); + } + } + + @Override + public void debug(final String msg) { + log(Level.FINE, msg); + } + @Override + public void debug(final ASTLocusTag loc, final String msg) { + log(Level.FINE, loc, msg); + } + + @Override + public void log(final Level level, final String msg) { + impl.log(level, msg); + } + @Override + public void log(final Level level, final String msg, final Object param) { + impl.log(level, msg, param); + } + @Override + public void log(final Level level, final String msg, final Object ... params) { + impl.log(level, msg, params); + } - static void init() { + @Override + public void log(final Level level, final ASTLocusTag loc, final String msg) { + handler.plf.setASTLocusTag(loc); + try { + impl.log(level, msg); + } finally { + handler.plf.setASTLocusTag(null); + } + } + @Override + public void log(final Level level, final ASTLocusTag loc, final String msg, final Object param) { + handler.plf.setASTLocusTag(loc); + try { + impl.log(level, msg, param); + } finally { + handler.plf.setASTLocusTag(null); + } + } + @Override + public void log(final Level level, final ASTLocusTag loc, final String msg, final Object ... params) { + handler.plf.setASTLocusTag(loc); + try { + impl.log(level, msg, params); + } finally { + handler.plf.setASTLocusTag(null); + } + } + + @Override + public void setLevel(final Level newLevel) throws SecurityException { + impl.setLevel(newLevel); + } + @Override + public void setLevelOfAllHandler(final Level newLevel) throws SecurityException { + final Handler[] hs = getHandlers(); + for(final Handler h:hs) { + h.setLevel(newLevel); + } + } + @Override + public Level getLevel() { + return impl.getLevel(); + } + @Override + public boolean isLoggable(final Level level) { + return impl.isLoggable(level); + } + @Override + public String getName() { + return impl.getName(); + } + @Override + public synchronized Handler[] getHandlers() { + return impl.getHandlers(); + } + @Override + public String getSourceClassName() { + return handler.plf.simpleClassName; + } + } + static class PlainLogConsoleHandler extends ConsoleHandler { + final PlainLogFormatter plf; + PlainLogConsoleHandler(final PlainLogFormatter plf, final Level level) { + this.plf = plf; + setFormatter(plf); + setLevel(level); + } + @Override + public java.util.logging.Formatter getFormatter() { + return plf; + } + } + static class PlainLogFormatter extends Formatter { + final String simpleClassName; + ASTLocusTag astLocus; + PlainLogFormatter(final String simpleClassName) { + this.simpleClassName = simpleClassName; + } + public void setASTLocusTag(final ASTLocusTag loc) { astLocus = loc; } + @Override + public String format(final LogRecord record) { + // Replace [Type, JavaType] -> its debug string! + final Object[] params = record.getParameters(); + if( null != params ) { + for(int i=params.length-1; 0<=i; i--) { + final Object o = params[i]; + if( o instanceof Type ) { + params[i] = ((Type)o).getDebugString(); + } else if( o instanceof JavaType ) { + params[i] = ((JavaType)o).getDebugString(); + } else if( o instanceof AliasedSymbol ) { + params[i] = ((AliasedSymbol)o).getAliasedString(); + } + } + } + final StringBuilder sb = new StringBuilder(256); + if( null != astLocus ) { + astLocus.toString(sb, getCanonicalName(record.getLevel()), GlueGen.debug()).append(": "); + } + if( GlueGen.debug() ) { + sb.append(simpleClassName).append(": "); + } + sb.append(formatMessage(record)).append("\n"); + return sb.toString(); + } + } + + private final static Map<String, LoggerIf> loggers; + private final static FQNLogger rootPackageLogger; + static { + loggers = new HashMap<String, LoggerIf>(); final String packageName = Logging.class.getPackage().getName(); final String property = PropertyAccess.getProperty(packageName+".level", true); Level level; if(property != null) { level = Level.parse(property); } else { - level = Level.WARNING; + if( DEBUG || GlueGen.debug() ) { + level = Level.ALL; + } else { + level = Level.WARNING; + } } + final String simpleClassName = Logging.class.getSimpleName(); + final String fqnClassName = packageName+"."+simpleClassName; + rootPackageLogger = new FQNLogger(fqnClassName, simpleClassName, level); + loggers.put(fqnClassName, rootPackageLogger); + } - final ConsoleHandler handler = new ConsoleHandler() { - @Override - public java.util.logging.Formatter getFormatter() { - return new PlainLogFormatter(); - } - }; - handler.setFormatter(new PlainLogFormatter()); - handler.setLevel(level); - - final Logger rootPackageLogger = Logger.getLogger(packageName); - rootPackageLogger.setUseParentHandlers(false); - rootPackageLogger.setLevel(level); - rootPackageLogger.addHandler(handler); + /** provokes static initialization */ + static void init() { } + + public static String getCanonicalName(final Level level) { + if( Level.CONFIG == level ) { + return "config"; + } else if( Level.FINER == level ) { + return "verbose"; + } else if( Level.FINE == level ) { + return "debug"; + } else if( Level.INFO == level ) { + return "info"; + } else if( Level.WARNING == level ) { + return "warning"; + } else if( Level.SEVERE == level ) { + return "error"; + } else { + return level.getName().toLowerCase(); + } } - /** - * This log formatter needs usually one line per log record. - * @author Michael Bien - */ - private static class PlainLogFormatter extends Formatter { + /** Returns the <i>root package logger</i>. */ + public static LoggerIf getLogger() { + return rootPackageLogger; + } + /** Returns the demanded logger, while aligning its log-level to the root logger's level. */ + public static synchronized LoggerIf getLogger(final Class<?> clazz) { + return getLogger(clazz.getPackage().getName(), clazz.getSimpleName()); + } - @Override - public String format(final LogRecord record) { - final StringBuilder sb = new StringBuilder(128); - sb.append("[").append(record.getLevel()).append(' ').append(record.getSourceClassName()).append("]: "); - sb.append(formatMessage(record)).append("\n"); - return sb.toString(); + /** Returns the demanded logger, while aligning its log-level to the root logger's level. */ + public static synchronized LoggerIf getLogger(final String packageName, final String simpleClassName) { + final String fqnClassName = packageName+"."+simpleClassName; + LoggerIf res = loggers.get(fqnClassName); + if( null == res ) { + res = new FQNLogger(fqnClassName, simpleClassName, rootPackageLogger.getLevel()); + loggers.put(fqnClassName, res); } + return res; + } + /** Align log-level of given logger to the root logger's level. */ + public static void alignLevel(final LoggerIf l) { + alignLevel(l, rootPackageLogger.getLevel()); + } + /** Align log-level of given logger and all its handlers to the given level. */ + public static void alignLevel(final LoggerIf l, final Level level) { + l.setLevel(level); + l.setLevelOfAllHandler(level); } } diff --git a/src/java/com/jogamp/gluegen/MethodBinding.java b/src/java/com/jogamp/gluegen/MethodBinding.java index 93c55d5..95a10c6 100644 --- a/src/java/com/jogamp/gluegen/MethodBinding.java +++ b/src/java/com/jogamp/gluegen/MethodBinding.java @@ -43,8 +43,6 @@ import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; import java.util.List; /** Represents the binding of a C function to a Java method. Also used @@ -54,8 +52,10 @@ import java.util.List; public class MethodBinding { private final FunctionSymbol sym; - private String renamedMethodName; - private final HashSet<String> aliasedNames; + private final String delegationImplName; + private final JavaType containingType; + private final Type containingCType; + private String nativeName; private JavaType javaReturnType; private List<JavaType> javaArgumentTypes; private boolean computedSignatureProperties; @@ -69,8 +69,6 @@ public class MethodBinding { private boolean signatureUsesCArrays; private boolean signatureUsesJavaPrimitiveArrays; private boolean signatureRequiresStaticInitialization; - private JavaType containingType; - private Type containingCType; private int thisPointerIndex = -1; /** @@ -79,12 +77,12 @@ public class MethodBinding { * types. It's safe to modify this binding after construction. */ public MethodBinding(final MethodBinding bindingToCopy) { - this.sym = bindingToCopy.sym; - - this.renamedMethodName = bindingToCopy.renamedMethodName; - this.aliasedNames = new HashSet<String>(bindingToCopy.aliasedNames); + this.sym = bindingToCopy.sym; + this.delegationImplName = bindingToCopy.delegationImplName; this.containingType = bindingToCopy.containingType; this.containingCType = bindingToCopy.containingCType; + + this.nativeName = bindingToCopy.nativeName; this.javaReturnType = bindingToCopy.javaReturnType; this.javaArgumentTypes = ( null != bindingToCopy.javaArgumentTypes ) ? new ArrayList<JavaType>(bindingToCopy.javaArgumentTypes) : null; this.computedSignatureProperties = bindingToCopy.computedSignatureProperties; @@ -101,19 +99,27 @@ public class MethodBinding { this.thisPointerIndex = bindingToCopy.thisPointerIndex; } - /** Constructor for calling a C function. */ - public MethodBinding(final FunctionSymbol sym) { - this.sym = sym; - this.aliasedNames = new HashSet<String>(); - } - - /** Constructor for calling a function pointer contained in a - struct. */ - public MethodBinding(final FunctionSymbol sym, final JavaType containingType, final Type containingCType) { + /** + * Constructor for calling a C function or a function pointer contained in a struct. + * <p> + * In case of the latter, a struct function pointer, + * the arguments {@code containingType} and {@code containingCType} must not be {@code null}! + * </p> + */ + public MethodBinding(final FunctionSymbol sym, + final String delegationImplName, + final JavaType javaReturnType, + final List<JavaType> javaArgumentTypes, + final JavaType containingType, + final Type containingCType) { this.sym = sym; + this.delegationImplName = delegationImplName; this.containingType = containingType; this.containingCType = containingCType; - this.aliasedNames = new HashSet<String>(); + + this.nativeName = null; + this.javaReturnType = javaReturnType; + this.javaArgumentTypes = javaArgumentTypes; } public void setJavaReturnType(final JavaType type) { @@ -149,6 +155,7 @@ public class MethodBinding { return sym.getArgumentType(i); } + /** Returns the {@link FunctionSymbol}. */ public FunctionSymbol getCSymbol() { return sym; } @@ -166,33 +173,42 @@ public class MethodBinding { return "arg" + i; } - public String getOrigName() { - return sym.getName(); - } - + /** Returns the {@link FunctionSymbol}'s current {@link FunctionSymbol#getName() aliased} API name. */ public String getName() { - // Defaults to same as C symbol unless renamed - if (renamedMethodName != null) { - return renamedMethodName; - } return sym.getName(); } - - /** Supports renaming C function in Java binding. */ - public void renameMethodName(final String name) { - if (null != name) { - renamedMethodName = name; - aliasedNames.add(sym.getName()); - } - } - - public void addAliasedName(final String name) { - aliasedNames.add(name); + /** + * The + * {@link JavaConfiguration#getDelegatedImplementation(com.jogamp.gluegen.cgram.types.AliasedSymbol) implementation delegation} + * name, or {@code null} for no delegation. + * @see #getImplName() + */ + public String getDelegationImplName() { + return delegationImplName; } - public Collection<String> getAliasedNames() { - return aliasedNames; + /** Returns the {@link FunctionSymbol}'s current {@link FunctionSymbol#getName() aliased} API name for the interface. */ + public String getInterfaceName() { + return sym.getName(); } + /** + * Returns the {@link FunctionSymbol}'s name for the implementation, + * which is the current {@link FunctionSymbol#getName() aliased} API name per default, + * or the {@link #getDelegationImplName() delegation} name. + * @see #getDelegationImplName() + */ + public String getImplName() { + return null != delegationImplName ? delegationImplName : sym.getName(); + } + /** + * Returns the {@link FunctionSymbol}'s name for the native function + * which is the {@link FunctionSymbol#getOrigName() original} C API name per default, + * but may be overridden via {@link #setNativeName(String)}. + */ + public String getNativeName() { + return null != nativeName ? nativeName : sym.getOrigName(); + } + public void setNativeName(final String s) { nativeName = s; } /** Creates a new MethodBinding replacing the specified Java argument type with a new argument type. If argumentNumber is diff --git a/src/java/com/jogamp/gluegen/ReferencedStructs.java b/src/java/com/jogamp/gluegen/ReferencedStructs.java index d06d47f..26deb3a 100644 --- a/src/java/com/jogamp/gluegen/ReferencedStructs.java +++ b/src/java/com/jogamp/gluegen/ReferencedStructs.java @@ -44,31 +44,43 @@ import com.jogamp.gluegen.cgram.types.*; public class ReferencedStructs implements TypeVisitor { - private final Set<Type> results = new HashSet<Type>(); + private final Map<String, Type> resultMap = new HashMap<String, Type>(); + private final Set<CompoundType> layoutSet = new HashSet<CompoundType>(); + private final Set<Type> skip = new HashSet<Type>(); - public void clear() { - results.clear(); - } + public void clear() { + resultMap.clear(); + } - public Iterator<Type> results() { - return results.iterator(); - } + public Iterator<Type> results() { + return resultMap.values().iterator(); + } + public Iterator<CompoundType> layouts() { + return layoutSet.iterator(); + } - @Override - public void visitType(final Type t) { - if (t.isPointer()) { - final PointerType p = t.asPointer(); - if (p.hasTypedefedName()) { - final CompoundType c = p.getTargetType().asCompound(); - if (c != null && c.getName() == null) { - // This otherwise-unnamed CompoundType is referred to by a - // PointerType that has a typedef name. Assume that it is - // referred to in the glue code and emit it. - results.add(p); + @Override + public void visitType(final Type t) { + if( skip.contains(t) ) { + return; + } + if ( t.isPointer() ) { + final PointerType p = t.asPointer(); + final CompoundType c = p.getTargetType().asCompound(); + if( p.isTypedef() && null != c ) { + // If containing pointer is typedef, use it (preferred) + skip.add(c); // earmark to skip the compound! + resultMap.put(c.getName(), p); + layoutSet.add(c); + } else { + // .. otherwise skip pointer and use followup compound + } + } else if( t.isCompound() ) { + // Use compound if not yet mapped, e.g. by typedef'ed (preferred) + if( !resultMap.containsKey(t.getName()) ) { + resultMap.put(t.getName(), t); + } + layoutSet.add(t.asCompound()); // always: could be const/volatile variants .. } - } - } else if (t.isCompound()) { - results.add(t); } - } } diff --git a/src/java/com/jogamp/gluegen/TypeConfig.java b/src/java/com/jogamp/gluegen/TypeConfig.java new file mode 100644 index 0000000..5f389f4 --- /dev/null +++ b/src/java/com/jogamp/gluegen/TypeConfig.java @@ -0,0 +1,52 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen; + +import com.jogamp.gluegen.cgram.types.SizeThunk; +import com.jogamp.gluegen.cgram.types.Type; + +/** + * Static {@link Type} config helper + * binding {@link JavaConfiguration#relaxedEqualSemanticsTest()} system wide. + */ +public class TypeConfig { + private static boolean relaxedEqualSemanticsTest = false; + + /** + * Returns whether {@link TypeConfig.SemanticEqualityOp#equalSemantics(TypeConfig.SemanticEqualityOp)} + * shall attempt to perform a relaxed semantic equality test, e.g. skip the {@code const} and {@code volatile} qualifier + * - or not. + */ + public static boolean relaxedEqualSemanticsTest() { + return relaxedEqualSemanticsTest; + } + /* pp */ static void setRelaxedEqualSemanticsTest(final boolean v) { + relaxedEqualSemanticsTest = v; + SizeThunk.setRelaxedEqualSemanticsTest(v); + } +} diff --git a/src/java/com/jogamp/gluegen/TypeInfo.java b/src/java/com/jogamp/gluegen/TypeInfo.java index d89ac79..52fdc04 100644 --- a/src/java/com/jogamp/gluegen/TypeInfo.java +++ b/src/java/com/jogamp/gluegen/TypeInfo.java @@ -66,7 +66,7 @@ public class TypeInfo { buf.append(name); buf.append(" pointerDepth "); buf.append(pointerDepth); - buf.append(" JavaType " + javaType); + buf.append(" JavaType " + javaType.getDebugString()); return buf.toString(); } } diff --git a/src/java/com/jogamp/gluegen/ant/GlueGenTask.java b/src/java/com/jogamp/gluegen/ant/GlueGenTask.java index dd57365..2b11d3f 100644 --- a/src/java/com/jogamp/gluegen/ant/GlueGenTask.java +++ b/src/java/com/jogamp/gluegen/ant/GlueGenTask.java @@ -73,7 +73,8 @@ import org.apache.tools.ant.util.JavaEnvUtils; emitter="[emitter class name]" config="[configuration file]" dumpCPP="[optional boolean]" - debug="[optional boolean]" /> + debug="[optional boolean]" + logLevel="[optional string]" /> * </pre> * * @author Rob Grzywinski <a href="mailto:[email protected]">[email protected]</a> @@ -101,6 +102,11 @@ public class GlueGenTask extends Task private boolean debug=false; /** + * <p>The optional logLevel.</p> + */ + private String logLevel = null; + + /** * <p>The optional dumpCPP flag.</p> */ private boolean dumpCPP=false; @@ -182,6 +188,15 @@ public class GlueGenTask extends Task } /** + * <p>Set the logLevel (optional). This is called by ANT.</p> + */ + public void setLogLevel(final String logLevel) + { + log( ("Setting logLevel: " + logLevel), Project.MSG_VERBOSE); + this.logLevel=logLevel; + } + + /** * <p>Set the dumpCPP flag (optional). This is called by ANT.</p> */ public void setDumpCPP(final boolean dumpCPP) @@ -456,6 +471,12 @@ public void setIncludeRefid(final Reference reference) { gluegenCommandline.createArgument().setValue("--debug"); } + // add the logLevel if enabled + if(null != logLevel) { + gluegenCommandline.createArgument().setValue("--logLevel"); + gluegenCommandline.createArgument().setValue(logLevel); + } + // add the debug flag if enabled if(dumpCPP) { gluegenCommandline.createArgument().setValue("--dumpCPP"); diff --git a/src/java/com/jogamp/gluegen/cgram/Define.java b/src/java/com/jogamp/gluegen/cgram/Define.java index 797cf6f..23caabd 100644 --- a/src/java/com/jogamp/gluegen/cgram/Define.java +++ b/src/java/com/jogamp/gluegen/cgram/Define.java @@ -39,18 +39,32 @@ package com.jogamp.gluegen.cgram; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; + /** Represents a #define of a literal to a value (a number represented in string form.) */ -public class Define { +public class Define implements ASTLocusTagProvider { private final String name; private final String value; + private final ASTLocusTag astLocus; public Define(final String name, final String value) { this.name = name; this.value = value; + this.astLocus = null; + } + + public Define(final String name, final String value, final ASTLocusTag astLocus) { + this.name = name; + this.value = value; + this.astLocus = astLocus; } public String getName() { return name; } public String getValue() { return value; } + + @Override + public ASTLocusTag getASTLocusTag() { return astLocus; } } diff --git a/src/java/com/jogamp/gluegen/cgram/TNode.java b/src/java/com/jogamp/gluegen/cgram/TNode.java index a564c54..5a36945 100644 --- a/src/java/com/jogamp/gluegen/cgram/TNode.java +++ b/src/java/com/jogamp/gluegen/cgram/TNode.java @@ -3,10 +3,15 @@ package com.jogamp.gluegen.cgram; import antlr.collections.AST; import antlr.CommonAST; import antlr.Token; + import java.lang.reflect.*; import java.util.Hashtable; import java.util.Enumeration; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.GlueGen; + /** Class TNode is an implementation of the AST interface and adds many useful features: @@ -29,7 +34,8 @@ import java.util.Enumeration; */ -public class TNode extends CommonAST { +@SuppressWarnings("serial") +public class TNode extends CommonAST implements ASTLocusTagProvider { protected int ttype; protected String text; protected int lineNum = 0; @@ -40,7 +46,22 @@ public class TNode extends CommonAST { protected Hashtable<String, Object> attributes = null; static String tokenVocabulary; - + /** + * {@inheritDoc} + * <p> + * If <i>source</i> is not available, + * implementation returns {@code null}. + * </p> + */ + @Override + public ASTLocusTag getASTLocusTag() { + final Object s = getAttribute("source"); + if( null != s ) { + return new ASTLocusTag(s, getLineNum(), -1, getText()); + } else { + return null; + } + } /** Set the token vocabulary to a tokentypes class @@ -159,15 +180,87 @@ public void initialize(final AST tr) { text = text_; } - /** Returns the text for this node and all children */ - public String getAllChildrenText() { + static class DebugASTVisitor { + protected int level; + private String tabs(final StringBuilder sb) { + sb.setLength(0); + for (int i = 0; i < level; i++) { + sb.append(" "); + } + return sb.toString(); + } + DebugASTVisitor(final int level) { + this.level = level; + } + void visit(final AST node) { + final StringBuilder sb = new StringBuilder(); + AST node2; + for (node2 = node; node2 != null; node2 = node2.getNextSibling()) { + if (node2.getText() == null) { + System.err.printf("%03d: %snil [%d]%n", level, tabs(sb), node2.getType()); + } else { + System.err.printf("%03d: %s%s [%d]%n", level, tabs(sb), node2.getText(), node2.getType()); + } + if (node2.getFirstChild() != null) { + level++; + visit(node2.getFirstChild()); + level--; + } + } + } + } + + /** + * Returns the text for this node, its children and siblings. + * <p> + * Implementation converts the AST LISP notation to serialized form. + * </p> + */ + public String getAllChildrenText(final String name) { + if( GlueGen.debug() ) { + System.err.println("TNode.XXX: "+name); + new DebugASTVisitor(1).visit(getFirstChild()); + } final StringBuilder buf = new StringBuilder(); - buf.append(getText()); - for (TNode node = (TNode) getFirstChild(); node != null; node = (TNode) node.getNextSibling()) { - buf.append(node.getText()); + final TNode down = (TNode) this.getFirstChild(); + if( null == down ) { + buf.append(this.getText()); + } else { + getAllChildrenText(buf, this, down); } return buf.toString(); } + private static void getAllChildrenText(final StringBuilder buf, + final TNode upNode, TNode thisNode) { + boolean first = true; + while( null != thisNode ) { + final boolean isClosing = HeaderParserTokenTypes.RPAREN == thisNode.getType(); + final boolean isGroupStart = HeaderParserTokenTypes.NExpressionGroup == thisNode.getType(); + + final TNode nextNode = (TNode) thisNode.getNextSibling(); + final TNode downNode = (TNode) thisNode.getFirstChild(); + if( !isClosing && + ( null == downNode && null == nextNode || // unary + !first // binary + ) + ) { + buf.append(" ").append(upNode.getText()); + } + if( null != downNode ) { + if( !isGroupStart ) { + buf.append(" ("); + } + getAllChildrenText(buf, thisNode, downNode); + if( !isGroupStart ) { + buf.append(" )"); + } + } else if( !isClosing ) { + buf.append(" ").append(thisNode.getText()); + } + thisNode = nextNode; + first = false; + } + } /** return the last child of this node, or null if there is none */ public TNode getLastChild() { diff --git a/src/java/com/jogamp/gluegen/cgram/types/AliasedSymbol.java b/src/java/com/jogamp/gluegen/cgram/types/AliasedSymbol.java new file mode 100644 index 0000000..869c658 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/AliasedSymbol.java @@ -0,0 +1,185 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen.cgram.types; + +import java.util.HashSet; +import java.util.Set; + +/** + * Supports symbol aliasing, i.e. renaming, + * while preserving all its original names, i.e. aliases. + */ +public interface AliasedSymbol { + /** + * Rename this symbol with the given {@code newName} if not equal {@link #getName() current-name}. + * <p> + * Before renaming the {@link #getName() current-name} will be added + * to the list of {@link #getAliasedNames() aliases}. + * while the given {@code newName} will be removed. + * </p> + * <p> + * Operation will be ignored if {@code newName} is {@code null}. + * </p> + * @param newName the new {@link #getName() current-name}, maybe {@code null} + */ + void rename(final String newName); + /** + * Add the given {@code origName} to the list of {@link #getAliasedNames() aliases} + * if not equal {@link #getName() current-name}. + * <p> + * Operation will be ignored if {@code newName} is {@code null}. + * </p> + * @param origName the new alias to be added, maybe {@code null} + */ + void addAliasedName(final String origName); + /** + * + * Returns {@code true} if this symbol has aliases, i.e. either being {@link #rename(String) renamed} + * or {@link #addAliasedName(String) aliases-added}. + * <p> + * Otherwise {@code false} is being returned. + * </p> + */ + boolean hasAliases(); + /** + * Return all aliases for this symbol, i.e. original names, for this symbol. + * <p> + * Inclusive {@link #getOrigName() original-name}, if {@link #rename(String) renamed}, + * </p> + * <p> + * Exclusive {@link #getName() current-name}. + * </p> + * <p> + * May return {@code null} or a zero sized {@link Set} for no aliases. + * </p> + */ + Set<String> getAliasedNames(); + /** + * Return the original-name as set at creation. + */ + String getOrigName(); + /** + * Return the current-name, which is the last {@link #rename(String) renamed-name} if issued, + * or the {@link #getOrigName() original-name}. + */ + String getName(); + /** + * Return this object's {@link #toString()} wrapped w/ the {@link #getName() current-name} + * and all {@link #getAliasedNames() aliases}. + */ + String getAliasedString(); + + public static class AliasedSymbolImpl implements AliasedSymbol { + private final String origName; + private final HashSet<String> aliasedNames; + private String name; + + public AliasedSymbolImpl(final String origName) { + if( null == origName ) { + throw new IllegalArgumentException("Null origName not allowed"); + } + this.origName = origName; + this.aliasedNames=new HashSet<String>(); + this.name = origName; + } + public AliasedSymbolImpl(final AliasedSymbolImpl o) { + this.origName = o.origName; + this.aliasedNames = new HashSet<String>(o.aliasedNames); + this.name = o.name; + } + @Override + public void rename(final String newName) { + if( null != newName && !name.equals(newName) ) { + aliasedNames.add(name); + aliasedNames.remove(newName); + name = newName; + } + } + @Override + public void addAliasedName(final String origName) { + if( null != origName && !name.equals(origName) ) { + aliasedNames.add(origName); + } + } + @Override + public boolean hasAliases() { + return aliasedNames.size() > 0; + } + @Override + public Set<String> getAliasedNames() { + return aliasedNames; + } + @Override + public String getOrigName() { + return origName; + } + @Override + public String getName() { + return name; + } + @Override + public String getAliasedString() { + return "["+name+", aliases "+aliasedNames.toString()+", "+toString()+"]"; + } + } + public static class NoneAliasedSymbol implements AliasedSymbol { + private final String name; + + public NoneAliasedSymbol(final String origName) { + this.name = origName; + } + @Override + public void rename(final String newName) { + throw new UnsupportedOperationException(); + } + @Override + public void addAliasedName(final String origName) { + throw new UnsupportedOperationException(); + } + @Override + public boolean hasAliases() { + return false; + } + @Override + public Set<String> getAliasedNames() { + return null; + } + @Override + public String getOrigName() { + return name; + } + @Override + public String getName() { + return name; + } + @Override + public String getAliasedString() { + return toString(); + } + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java b/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java index d867b40..ada34f7 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java @@ -40,6 +40,8 @@ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents an array type. This differs from a pointer type in C syntax by the use of "[]" rather than "*". The length may or may not be known; if the length is unknown then a negative number @@ -48,50 +50,79 @@ package com.jogamp.gluegen.cgram.types; public class ArrayType extends MemoryLayoutType implements Cloneable { private final Type elementType; private final int length; - private String computedName; - public ArrayType(final Type elementType, final SizeThunk sizeInBytes, final int length, final int cvAttributes) { - super(elementType.getName() + " *", sizeInBytes, cvAttributes); + public ArrayType(final Type elementType, final SizeThunk sizeInBytes, final int length, + final int cvAttributes) { + this(elementType, sizeInBytes, length, cvAttributes, null); + } + public ArrayType(final Type elementType, final SizeThunk sizeInBytes, final int length, + final int cvAttributes, final ASTLocusTag astLocus) { + super(elementType.getName() + " *", sizeInBytes, cvAttributes, astLocus); this.elementType = elementType; this.length = length; } + private ArrayType(final ArrayType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + elementType = o.elementType; + length = o.length; + } @Override - public boolean equals(final Object arg) { - if (arg == this) return true; - if (arg == null || (!(arg instanceof ArrayType))) { - return false; - } + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new ArrayType(this, cvAttributes, astLocus); + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = elementType.hashCode(); + return ((hash << 5) - hash) + length; + } + + @Override + protected boolean equalsImpl(final Type arg) { final ArrayType t = (ArrayType) arg; - return (super.equals(arg) && elementType.equals(t.elementType) && (length == t.length)); + return elementType.equals(t.elementType) && + length == t.length; } @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + final int hash = elementType.hashCodeSemantics(); + return ((hash << 5) - hash) + length; + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final ArrayType t = (ArrayType) arg; + return elementType.equalSemantics(t.elementType) && + length == t.length; + } + + @Override + public boolean isAnon() { return elementType.isAnon(); } + + @Override public String getName(final boolean includeCVAttrs) { - // Lazy computation of name due to lazy setting of compound type - // names during parsing - // Note: don't think cvAttributes can be set for array types (unlike pointer types) - if (computedName == null) { - computedName = (elementType.getName() + " *").intern(); - } - return computedName; + return elementType.getName() + " *"; } @Override - public ArrayType asArray() { return this; } + public final ArrayType asArray() { return this; } public Type getElementType() { return elementType; } public int getLength() { return length; } public boolean hasLength() { return length >= 0; } @Override - public Type getBaseElementType() { - ArrayType t = this; - while (t.getElementType().isArray()) { - t = t.getElementType().asArray(); - } - return t.getElementType(); - // return elementType.getBaseElementType(); + public final Type getBaseElementType() { + return elementType.getBaseElementType(); + } + + @Override + public final int arrayDimension() { + return 1 + elementType.arrayDimension(); } /** Recompute the size of this array if necessary. This needs to be @@ -114,7 +145,7 @@ public class ArrayType extends MemoryLayoutType implements Cloneable { if(elementType.isConst()) { buf.append("const "); } - buf.append(elementType.getName()); + buf.append(elementType.getCName()); if (variableName != null) { buf.append(" "); buf.append(variableName); @@ -130,9 +161,4 @@ public class ArrayType extends MemoryLayoutType implements Cloneable { super.visit(arg); elementType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - return new ArrayType(elementType, getSize(), length, cvAttributes); - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/BitType.java b/src/java/com/jogamp/gluegen/cgram/types/BitType.java index 2644551..834ff95 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/BitType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/BitType.java @@ -40,6 +40,8 @@ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents a bitfield in a struct. */ public class BitType extends IntType implements Cloneable { @@ -47,22 +49,60 @@ public class BitType extends IntType implements Cloneable { private final int sizeInBits; private final int offset; - public BitType(final IntType underlyingType, final int sizeInBits, final int lsbOffset, final int cvAttributes) { - super(underlyingType.getName(), underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes); + public BitType(final IntType underlyingType, final int sizeInBits, final int lsbOffset, + final int cvAttributes, final ASTLocusTag astLocus) { + super(underlyingType.getName(), underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes, astLocus); this.underlyingType = underlyingType; this.sizeInBits = sizeInBits; this.offset = lsbOffset; } + private BitType(final BitType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + underlyingType = o.underlyingType; + sizeInBits = o.sizeInBits; + offset = o.offset; + } + + @Override + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new BitType(this, cvAttributes, astLocus); + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCode(); + hash = ((hash << 5) - hash) + sizeInBits; + return ((hash << 5) - hash) + offset; + } + @Override - public boolean equals(final Object arg) { - if (arg == this) return true; - if (arg == null || (!(arg instanceof BitType))) { - return false; - } - final BitType t = (BitType) arg; - return (super.equals(arg) && underlyingType.equals(t.underlyingType) && - (sizeInBits == t.sizeInBits) && (offset == t.offset)); + protected boolean equalsImpl(final Type arg) { + final BitType t = (BitType) arg; + return super.equalsImpl(arg) && + underlyingType.equals(t.underlyingType) && + sizeInBits == t.sizeInBits && + offset == t.offset; + } + + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeSemanticsImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCodeSemantics(); + hash = ((hash << 5) - hash) + sizeInBits; + return ((hash << 5) - hash) + offset; + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final BitType t = (BitType) arg; + return super.equalSemanticsImpl(arg) && + underlyingType.equalSemantics(t.underlyingType) && + sizeInBits == t.sizeInBits && + offset == t.offset; } @Override @@ -84,9 +124,4 @@ public class BitType extends IntType implements Cloneable { super.visit(arg); underlyingType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - return new BitType(underlyingType, sizeInBits, offset, cvAttributes); - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java b/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java index 9716f54..56bcdda 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java @@ -42,73 +42,110 @@ package com.jogamp.gluegen.cgram.types; import java.util.*; +import com.jogamp.gluegen.ASTLocusTag; + /** Models all compound types, i.e., those containing fields: structs and unions. The boolean type accessors indicate how the type is really defined. */ -public abstract class CompoundType extends MemoryLayoutType implements Cloneable { +public abstract class CompoundType extends MemoryLayoutType implements Cloneable, AliasedSymbol { // The name "foo" in the construct "struct foo { ... }"; - private String structName; + private final String structName; private ArrayList<Field> fields; private boolean visiting; private boolean bodyParsed; - private boolean computedHashcode; - private int hashcode; - CompoundType(final String name, final SizeThunk size, final int cvAttributes, final String structName) { - super(name, size, cvAttributes); - this.structName = structName; + @Override + public void rename(final String newName) { + throw new UnsupportedOperationException(); } - - public static CompoundType create(final String name, final SizeThunk size, final CompoundTypeKind kind, final int cvAttributes) { + @Override + public void addAliasedName(final String origName) { + throw new UnsupportedOperationException(); + } + @Override + public boolean hasAliases() { + return false; + } + @Override + public Set<String> getAliasedNames() { + return null; + } + @Override + public String getAliasedString() { + return toString(); + } + @Override + public String getOrigName() { + return getName(); + } + /** + * @param structName struct name of this CompoundType, i.e. the "foo" in the + construct {@code struct foo { int a, ... };} or {@code struct foo;} <i>even</i> for anonymous structs. + * @param size + * @param kind + * @param cvAttributes + * @return + */ + public static CompoundType create(final String structName, final SizeThunk size, + final CompoundTypeKind kind, final int cvAttributes, + final ASTLocusTag astLocus) + { + final CompoundType res; switch (kind) { case STRUCT: - return new StructType(name, size, cvAttributes); + res = new StructType(null, size, cvAttributes, structName, astLocus); + break; case UNION: - return new UnionType(name, size, cvAttributes); + res = new UnionType(null, size, cvAttributes, structName, astLocus); + break; default: throw new RuntimeException("OO relation "+kind+" / Compount not yet supported"); } + return res; } - @Override - public Object clone() { - final CompoundType n = (CompoundType) super.clone(); - if(null!=this.fields) { - n.fields = new ArrayList<Field>(this.fields); + CompoundType(final String name, final SizeThunk size, final int cvAttributes, + final String structName, final ASTLocusTag astLocus) { + super(null == name ? structName : name, size, cvAttributes, astLocus); + this.structName = structName; + } + + CompoundType(final CompoundType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + this.structName = o.structName; + if(null != o.fields) { + fields = new ArrayList<Field>(o.fields); } - return n; + bodyParsed = o.bodyParsed; } @Override - public int hashCode() { - if (computedHashcode) { - return hashcode; - } + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = 31 + ( null != structName ? structName.hashCode() : 0 ); + return ((hash << 5) - hash) + TypeComparator.listsHashCode(fields); + } - if (structName != null) { - hashcode = structName.hashCode(); - } else if (getName() != null) { - hashcode = getName().hashCode(); - } else { - hashcode = 0; - } + @Override + protected boolean equalsImpl(final Type arg) { + final CompoundType ct = (CompoundType) arg; + return ( (structName == null ? ct.structName == null : structName.equals(ct.structName)) || + (structName != null && structName.equals(ct.structName)) + ) && + TypeComparator.listsEqual(fields, ct.fields); + } - computedHashcode = true; - return hashcode; + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + return TypeComparator.listsHashCodeSemantics(fields); } @Override - public boolean equals(final Object arg) { - if (arg == this) return true; - if (arg == null || !(arg instanceof CompoundType)) { - return false; - } - final CompoundType t = (CompoundType) arg; - return super.equals(arg) && - ((structName == null ? t.structName == null : structName.equals(t.structName)) || - (structName != null && structName.equals(t.structName))) && - listsEqual(fields, t.fields); + protected boolean equalSemanticsImpl(final Type arg) { + final CompoundType ct = (CompoundType) arg; + return TypeComparator.listsEqualSemantics(fields, ct.fields); } /** Returns the struct name of this CompoundType, i.e. the "foo" in @@ -117,22 +154,20 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable return structName; } - /** Sets the struct name of this CompoundType, i.e. the "foo" in the - construct "struct foo { ... };". */ - public void setStructName(final String structName) { - this.structName = structName; - } - @Override - public void setSize(final SizeThunk size) { - super.setSize(size); - } + public CompoundType asCompound() { return this; } @Override - public CompoundType asCompound() { return this; } + public String getCName(final boolean includeCVAttrs) { + if( isTypedef() ) { + return getName(includeCVAttrs); + } else { + return (isStruct() ? "struct " : "union ")+getName(includeCVAttrs); + } + } ArrayList<Field> getFields() { return fields; } - void setFields(final ArrayList<Field> fields) { this.fields = fields; } + void setFields(final ArrayList<Field> fields) { this.fields = fields; clearCache(); } /** Returns the number of fields in this type. */ public int getNumFields() { @@ -147,17 +182,24 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable /** Adds a field to this type. */ public void addField(final Field f) { if (bodyParsed) { - throw new RuntimeException("Body of this CompoundType has already been parsed; should not be adding more fields"); + throw new IllegalStateException("Body of this CompoundType has been already closed"); } if (fields == null) { fields = new ArrayList<Field>(); } fields.add(f); + clearCache(); } - /** Indicates to this CompoundType that its body has been parsed and - that no more {@link #addField} operations will be made. */ - public void setBodyParsed() { + /** + * Indicates to this CompoundType that its body has been parsed and + * that no more {@link #addField} operations will be made. + * @throws IllegalStateException If called twice. + */ + public void setBodyParsed() throws IllegalStateException { + if (bodyParsed) { + throw new IllegalStateException("Body of this CompoundType has been already closed"); + } bodyParsed = true; } @@ -169,8 +211,9 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable @Override public String toString() { final String cvAttributesString = getCVAttributesString(); - if (getName() != null) { - return cvAttributesString + getName(); + final String cname = getCName(); + if ( null != cname ) { + return cvAttributesString + cname; } else if (getStructName() != null) { return cvAttributesString + "struct " + getStructName(); } else { @@ -188,12 +231,12 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable super.visit(arg); final int n = getNumFields(); for (int i = 0; i < n; i++) { - final Field f = getField(i); - f.getType().visit(arg); + getField(i).getType().visit(arg); } } finally { visiting = false; } + return; } public String getStructString() { diff --git a/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java b/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java index de42522..133a322 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java @@ -39,22 +39,22 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents a double-word floating-point type (C type "double".) */ public class DoubleType extends PrimitiveType implements Cloneable { - public DoubleType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + public DoubleType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + } + + private DoubleType(final DoubleType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof DoubleType))) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new DoubleType(this, cvAttributes, astLocus); } @Override @@ -63,7 +63,22 @@ public class DoubleType extends PrimitiveType implements Cloneable { } @Override - Type newCVVariant(final int cvAttributes) { - return new DoubleType(getName(), getSize(), cvAttributes); + protected int hashCodeImpl() { + return 0; + } + + @Override + protected boolean equalsImpl(final Type t) { + return true; + } + + @Override + protected int hashCodeSemanticsImpl() { + return 0; + } + + @Override + protected boolean equalSemanticsImpl(final Type t) { + return true; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/EnumType.java b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java index 0b1193b..7c2fa73 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/EnumType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java @@ -42,73 +42,132 @@ package com.jogamp.gluegen.cgram.types; import java.util.ArrayList; import java.util.NoSuchElementException; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ConstantDefinition; +import com.jogamp.gluegen.ConstantDefinition.CNumber; +import com.jogamp.gluegen.GlueGenException; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; + /** Describes enumerated types. Enumerations are like ints except that they have a set of named values. */ public class EnumType extends IntType implements Cloneable { - private IntType underlyingType; + public static class Enumerator implements TypeComparator.SemanticEqualityOp { + private final String name; + private final String expr; + private final CNumber number; - private static class Enum { + public Enumerator(final String name, final long value) { + this.name = name; + this.number = new CNumber(false, false, value); + this.expr = this.number.toJavaString(); + } + public Enumerator(final String name, final CNumber number) { + this.name = name; + this.number = number; + this.expr = this.number.toJavaString(); + } + public Enumerator(final String name, final String value) { + this.name = name; + this.expr = value; + this.number = ConstantDefinition.decodeIntegerNumber(value); + } - String name; - long value; + public String getName() { return name; } + public String getExpr() { return expr; } + public CNumber getNumber() { return number; } + public boolean hasNumber() { return null != number; } - Enum(final String name, final long value) { - this.name = name; - this.value = value; + @Override + public int hashCode() { + // 31 * x == (x << 5) - x + final int hash = name.hashCode(); + return ((hash << 5) - hash) + expr.hashCode(); } - String getName() { - return name; + @Override + public boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof Enumerator) ) { + return false; + } + final Enumerator t = (Enumerator) arg; + return name.equals(t.name) && + expr.equals(t.expr); } - long getValue() { - return value; + @Override + public int hashCodeSemantics() { + return hashCode(); } + + @Override + public boolean equalSemantics(final SemanticEqualityOp arg) { + return equals(arg); + } + + @Override + public String toString() { return "["+name+" = ["+expr+", "+number+"]"; } } - private ArrayList<Enum> enums; + private final IntType underlyingType; + private ArrayList<Enumerator> enums; public EnumType(final String name) { super(name, SizeThunk.LONG, false, CVAttributes.CONST); this.underlyingType = new IntType(name, SizeThunk.LONG, false, CVAttributes.CONST); } - public EnumType(final String name, final SizeThunk enumSizeInBytes) { - super(name, enumSizeInBytes, false, CVAttributes.CONST); - this.underlyingType = new IntType(name, enumSizeInBytes, false, CVAttributes.CONST); + public EnumType(final String name, final SizeThunk enumSizeInBytes, final ASTLocusTag astLocus) { + super(name, enumSizeInBytes, false, CVAttributes.CONST, astLocus); + this.underlyingType = new IntType(name, enumSizeInBytes, false, CVAttributes.CONST, astLocus); } - protected EnumType(final String name, final IntType underlyingType, final int cvAttributes) { - super(name, underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes); - this.underlyingType = underlyingType; + private EnumType(final EnumType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + underlyingType = o.underlyingType; + if(null != o.enums) { + enums = new ArrayList<Enumerator>(o.enums); + } } @Override - public Object clone() { - final EnumType n = (EnumType) super.clone(); - if(null!=this.underlyingType) { - n.underlyingType = (IntType) this.underlyingType.clone(); - } - if(null!=this.enums) { - n.enums = new ArrayList<Enum>(this.enums); - } - return n; + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new EnumType(this, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof EnumType))) { - return false; - } + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCode(); + return ((hash << 5) - hash) + TypeComparator.listsHashCode(enums); + } + + @Override + protected boolean equalsImpl(final Type arg) { + final EnumType t = (EnumType) arg; + return super.equalsImpl(arg) && + underlyingType.equals(t.underlyingType) && + TypeComparator.listsEqual(enums, t.enums); + } + + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeSemanticsImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCodeSemantics(); + return ((hash << 5) - hash) + TypeComparator.listsHashCodeSemantics(enums); + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { final EnumType t = (EnumType) arg; - return (super.equals(arg) - && underlyingType.equals(t.underlyingType) - && listsEqual(enums, t.enums)); + return super.equalSemanticsImpl(arg) && + underlyingType.equalSemantics(t.underlyingType) && + TypeComparator.listsEqualSemantics(enums, t.enums); } @Override @@ -116,11 +175,14 @@ public class EnumType extends IntType implements Cloneable { return this; } - public void addEnum(final String name, final long val) { + public Type getUnderlyingType() { return this.underlyingType; } + + public void addEnum(final String name, final Enumerator newEnum) { if (enums == null) { - enums = new ArrayList<Enum>(); + enums = new ArrayList<Enumerator>(); } - enums.add(new Enum(name, val)); + enums.add(newEnum); + clearCache(); } /** Number of enumerates defined in this enum. */ @@ -128,22 +190,17 @@ public class EnumType extends IntType implements Cloneable { return enums.size(); } - /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) name */ - public String getEnumName(final int i) { - return (enums.get(i)).getName(); + /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) {@link Enumerator} */ + public Enumerator getEnum(final int i) { + return enums.get(i); } - /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) value */ - public long getEnumValue(final int i) { - return (enums.get(i)).getValue(); - } - - /** Fetch the value of the enumerate with the given name. */ - public long getEnumValue(final String name) { + /** Fetch the enumerate with the given name. */ + public Enumerator getEnum(final String name) { for (int i = 0; i < enums.size(); ++i) { - final Enum n = (enums.get(i)); + final Enumerator n = (enums.get(i)); if (n.getName().equals(name)) { - return n.getValue(); + return n; } } throw new NoSuchElementException( @@ -166,25 +223,30 @@ public class EnumType extends IntType implements Cloneable { */ public boolean removeEnumerate(final String name) { for (int i = 0; i < enums.size(); ++i) { - final Enum e = enums.get(i); + final Enumerator e = enums.get(i); if (e.getName().equals(name)) { enums.remove(e); + clearCache(); return true; } } return false; } + public StringBuilder appendEnums(final StringBuilder sb, final boolean cr) { + for(int i=0; i<enums.size(); i++) { + sb.append(enums.get(i)).append(", "); + if( cr ) { + sb.append(String.format("%n")); + } + } + sb.append("}"); + return sb; + } + @Override public void visit(final TypeVisitor arg) { super.visit(arg); underlyingType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - final EnumType t = new EnumType(getName(), underlyingType, cvAttributes); - t.enums = enums; - return t; - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/Field.java b/src/java/com/jogamp/gluegen/cgram/types/Field.java index 858d81a..a8fc599 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/Field.java +++ b/src/java/com/jogamp/gluegen/cgram/types/Field.java @@ -40,10 +40,11 @@ package com.jogamp.gluegen.cgram.types; import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; /** Represents a field in a struct or union. */ -public class Field { +public class Field implements SemanticEqualityOp { private final String name; private final Type type; private SizeThunk offset; @@ -56,21 +57,41 @@ public class Field { @Override public int hashCode() { - return name.hashCode(); + // 31 * x == (x << 5) - x + final int hash = 31 + ( null != name ? name.hashCode() : 0 ); + return ((hash << 5) - hash) + type.hashCode(); } @Override public boolean equals(final Object arg) { - if (arg == null || (!(arg instanceof Field))) { + if ( !(arg instanceof Field) ) { return false; } final Field f = (Field) arg; // Note: don't know how to examine offset any more since it's // implemented in terms of code and they're not canonicalized - return (((name != null && name.equals(f.name)) || - (name == null && f.name == null)) && - type.equals(f.type)); + return ( ( name != null && name.equals(f.name) ) || + ( name == null && f.name == null ) + ) && + type.equals(f.type); + } + + @Override + public int hashCodeSemantics() { + return type.hashCodeSemantics(); + } + + @Override + public boolean equalSemantics(final SemanticEqualityOp arg) { + if ( !(arg instanceof Field) ) { + return false; + } + + final Field f = (Field) arg; + // Note: don't know how to examine offset any more since it's + // implemented in terms of code and they're not canonicalized + return type.equalSemantics(f.type); } /** Name of this field in the containing data structure. */ diff --git a/src/java/com/jogamp/gluegen/cgram/types/FloatType.java b/src/java/com/jogamp/gluegen/cgram/types/FloatType.java index d8b0b13..2e7a2cf 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/FloatType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/FloatType.java @@ -40,29 +40,44 @@ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents a single-word floating-point type (C type "float".) */ public class FloatType extends PrimitiveType implements Cloneable { - public FloatType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + public FloatType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + } + + private FloatType(final FloatType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof FloatType))) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new FloatType(this, cvAttributes, astLocus); } @Override public FloatType asFloat() { return this; } @Override - Type newCVVariant(final int cvAttributes) { - return new FloatType(getName(), getSize(), cvAttributes); + protected int hashCodeImpl() { + return 0; + } + + @Override + protected boolean equalsImpl(final Type t) { + return true; + } + + @Override + protected int hashCodeSemanticsImpl() { + return 0; + } + + @Override + protected boolean equalSemanticsImpl(final Type t) { + return true; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java b/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java index d41f2fd..91a0a5a 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java +++ b/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java @@ -38,6 +38,14 @@ */ package com.jogamp.gluegen.cgram.types; +import java.util.List; + +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.cgram.types.AliasedSymbol.AliasedSymbolImpl; +import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; + /** * Describes a function symbol, which includes the name and @@ -51,20 +59,37 @@ package com.jogamp.gluegen.cgram.types; * Deep comparison can be performed via {@link #isCompletelyEqual(Object o)}; * </p> **/ -public class FunctionSymbol { +public class FunctionSymbol extends AliasedSymbolImpl implements AliasedSemanticSymbol, ASTLocusTagProvider { - private final String name; private final FunctionType type; + private final ASTLocusTag astLocus; public FunctionSymbol(final String name, final FunctionType type) { - this.name = name; + super(name); + this.type = type; + this.astLocus = null; + } + + public FunctionSymbol(final String name, final FunctionType type, final ASTLocusTag locus) { + super(name); this.type = type; + this.astLocus = locus; } - public String getName() { - return name; + /** Shallow'ish copy, only aliased names are re-created. */ + public static FunctionSymbol cloneWithDeepAliases(final FunctionSymbol o) { + return new FunctionSymbol(o); + } + /** Warning: Shallow'ish copy, only aliased names are re-created. */ + private FunctionSymbol(final FunctionSymbol o) { + super(o); + this.type = o.type; + this.astLocus = o.astLocus; } + @Override + public ASTLocusTag getASTLocusTag() { return astLocus; } + /** Returns the type of this function. Do not add arguments to it directly; use addArgument instead. */ public FunctionType getType() { @@ -99,7 +124,7 @@ public class FunctionSymbol { @Override public String toString() { - return getType().toString(getName()); + return getType().toString(getName(), false); } /** Helper routine for emitting native javadoc tags */ @@ -109,10 +134,10 @@ public class FunctionSymbol { @Override public int hashCode() { - if (name == null) { + if (getName() == null) { return 0; } - return name.hashCode(); + return getName().hashCode(); } @Override @@ -120,25 +145,54 @@ public class FunctionSymbol { if (arg == this) { return true; } - - if (arg == null || (!(arg instanceof FunctionSymbol))) { + if ( !(arg instanceof FunctionSymbol) ) { return false; } - final FunctionSymbol other = (FunctionSymbol) arg; - if (getName() == null && other.getName() != null) { return false; } - return getName().equals(other.getName()); } + @Override + public int hashCodeSemantics() { + return type.hashCodeSemantics(); + } + @Override + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } + if ( !(arg instanceof FunctionSymbol) ) { + return false; + } + final FunctionSymbol other = (FunctionSymbol) arg; + return type.equalSemantics(other.type); + } + + + public static boolean containsExactly(final List<FunctionSymbol> l, final FunctionSymbol s) { + return exactIndexOf(l, s) >= 0; + } + + public static int exactIndexOf(final List<FunctionSymbol> l, final FunctionSymbol s) { + final int size = l.size(); + for (int i = 0; i < size; i++) { + final FunctionSymbol e = l.get(i); + if( null == s && null == e || + s.equals( e ) && s.type.equals(e.type) ) { + return i; + } + } + return -1; + } + /** * Compares the function type as well, since {@link #equals(Object)} * and {@link #hashCode()} won't. */ - public boolean isCompletelyEqual(final Object arg) { + public boolean exactlyEqual(final Object arg) { if( !this.equals(arg) ) { return false; } diff --git a/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java b/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java index 4b39a34..2b9dec7 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java @@ -41,6 +41,8 @@ package com.jogamp.gluegen.cgram.types; import java.util.*; +import com.jogamp.gluegen.ASTLocusTag; + /** Describes a function type, used to model both function declarations and (via PointerType) function pointers. */ public class FunctionType extends Type implements Cloneable { @@ -49,35 +51,63 @@ public class FunctionType extends Type implements Cloneable { private ArrayList<Type> argumentTypes; private ArrayList<String> argumentNames; - public FunctionType(final String name, final SizeThunk size, final Type returnType, final int cvAttributes) { - super(name, size, cvAttributes); + public FunctionType(final String name, final SizeThunk size, final Type returnType, + final int cvAttributes) { + this(name, size, returnType, cvAttributes, null); + } + public FunctionType(final String name, final SizeThunk size, final Type returnType, + final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); this.returnType = returnType; } - @Override - public Object clone() { - final FunctionType n = (FunctionType) super.clone(); - if(null!=this.argumentTypes) { - n.argumentTypes = new ArrayList<Type>(this.argumentTypes); + private FunctionType(final FunctionType o, final ASTLocusTag astLocus) { + super(o, o.getCVAttributes(), astLocus); + returnType = o.returnType; + if(null != o.argumentTypes) { + argumentTypes = new ArrayList<Type>(o.argumentTypes); } - if(null!=this.argumentNames) { - n.argumentNames = new ArrayList<String>(this.argumentNames); + if(null != o.argumentNames) { + argumentNames = new ArrayList<String>(o.argumentNames); } - return n; } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof FunctionType))) { - return false; + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + if( newCVVariant ) { + // Functions don't have const/volatile attributes + return this; + } else { + return new FunctionType(this, astLocus); } + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = returnType.hashCode(); + return ((hash << 5) - hash) + TypeComparator.listsHashCode(argumentTypes); + } + + @Override + protected boolean equalsImpl(final Type arg) { final FunctionType t = (FunctionType) arg; - return (super.equals(arg) - && returnType.equals(t.returnType) - && listsEqual(argumentTypes, t.argumentTypes)); + return returnType.equals(t.returnType) && + TypeComparator.listsEqual(argumentTypes, t.argumentTypes); + } + + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + final int hash = returnType.hashCodeSemantics(); + return ((hash << 5) - hash) + TypeComparator.listsHashCodeSemantics(argumentTypes); + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final FunctionType t = (FunctionType) arg; + return returnType.equalSemantics(t.returnType) && + TypeComparator.listsEqualSemantics(argumentTypes, t.argumentTypes); } @Override @@ -115,28 +145,27 @@ public class FunctionType extends Type implements Cloneable { } argumentTypes.add(argumentType); argumentNames.add(argumentName); + clearCache(); } public void setArgumentName(final int i, final String name) { argumentNames.set(i, name); + clearCache(); } @Override public String toString() { - return toString(null); - } - - public String toString(final String functionName) { - return toString(functionName, false); + return toString(null, false); } public String toString(final String functionName, final boolean emitNativeTag) { return toString(functionName, null, emitNativeTag, false); } - String toString(final String functionName, final String callingConvention, final boolean emitNativeTag, final boolean isPointer) { + String toString(final String functionName, final String callingConvention, + final boolean emitNativeTag, final boolean isPointer) { final StringBuilder res = new StringBuilder(); - res.append(getReturnType()); + res.append(getReturnType().getCName(true)); res.append(" "); if (isPointer) { res.append("("); @@ -169,7 +198,7 @@ public class FunctionType extends Type implements Cloneable { } else if (t.isArray()) { res.append(t.asArray().toString(getArgumentName(i))); } else { - res.append(t); + res.append(t.getCName(true)); final String argumentName = getArgumentName(i); if (argumentName != null) { res.append(" "); @@ -193,10 +222,4 @@ public class FunctionType extends Type implements Cloneable { getArgumentType(i).visit(arg); } } - - @Override - Type newCVVariant(final int cvAttributes) { - // Functions don't have const/volatile attributes - return this; - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/IntType.java b/src/java/com/jogamp/gluegen/cgram/types/IntType.java index 3f8dddc..2433fc6 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/IntType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/IntType.java @@ -39,37 +39,95 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class IntType extends PrimitiveType implements Cloneable { private final boolean unsigned; - private boolean typedefedUnsigned; + private boolean typedefUnsigned; public IntType(final String name, final SizeThunk size, final boolean unsigned, final int cvAttributes) { - this(name, size, unsigned, cvAttributes, false); + this(name, size, unsigned, cvAttributes, null); } - public IntType(final String name, final SizeThunk size, final boolean unsigned, final int cvAttributes, final boolean typedefedUnsigned) { - super(name, size, cvAttributes); + public IntType(final String name, final SizeThunk size, + final boolean unsigned, final int cvAttributes, + final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); this.unsigned = unsigned; - this.typedefedUnsigned = typedefedUnsigned; + this.typedefUnsigned = false; } - @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof IntType))) { - return false; + /** + * Only for HeaderParser! + * + * @param name the name + * @param size the size + * @param unsigned true if this instance is unsigned, not the <i>typedef</i>! + * @param cvAttributes the cvAttributes for this instance, not for the <i>typedef</i>! + * @param isTypedef true if this instance is a <i>typedef</i> variant + * @param typedefUnsigned true if the <i>typedef</i> itself is unsigned + * @param astLocus the location in source code + */ + public IntType(final String name, final SizeThunk size, + final boolean unsigned, final int cvAttributes, + final boolean isTypedef, final boolean typedefUnsigned, + final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + this.unsigned = unsigned; + if( isTypedef ) { + // the 'cvAttributes' are intended for this instance, not the 'typedef cvAttributes'! + setTypedef(0); + this.typedefUnsigned = typedefUnsigned; + } else { + this.typedefUnsigned = false; } + } + + IntType(final IntType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + this.unsigned = o.unsigned; + this.typedefUnsigned = o.typedefUnsigned; + } + + @Override + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new IntType(this, cvAttributes, astLocus); + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 1; + hash = ((hash << 5) - hash) + ( unsigned ? 1 : 0 ); + return ((hash << 5) - hash) + ( typedefUnsigned ? 1 : 0 ); + } + + @Override + protected boolean equalsImpl(final Type arg) { final IntType t = (IntType) arg; - return (super.equals(arg) && (unsigned == t.unsigned)); + return unsigned == t.unsigned && + typedefUnsigned == t.typedefUnsigned; } @Override - public void setName(final String name) { - super.setName(name); - typedefedUnsigned = unsigned; + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + int hash = 1; + if( !relaxedEqSem ) { + hash = ((hash << 5) - hash) + ( unsigned ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( typedefUnsigned ? 1 : 0 ); + } + return hash; + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final IntType t = (IntType) arg; + return relaxedEqSem || + ( unsigned == t.unsigned && + typedefUnsigned == t.typedefUnsigned + ); } @Override @@ -82,18 +140,27 @@ public class IntType extends PrimitiveType implements Cloneable { return unsigned; } - /** Indicates whether this type is an unsigned primitive type, as opposed to a typedef type that's unsigned. */ - public boolean isPrimitiveUnsigned() { - return unsigned && !typedefedUnsigned; + @Override + public String getCName(final boolean includeCVAttrs) { + if ( !unsigned || typedefUnsigned ) { + return super.getCName(includeCVAttrs); + } else { + return "unsigned "+super.getCName(includeCVAttrs); + } } @Override public String toString() { - return getCVAttributesString() + ((isUnsigned() & (!typedefedUnsigned)) ? "unsigned " : "") + getName(); + return getCVAttributesString() + ( unsigned && !typedefUnsigned ? "unsigned " : "") + getCName(); } @Override - Type newCVVariant(final int cvAttributes) { - return new IntType(getName(), getSize(), isUnsigned(), cvAttributes, typedefedUnsigned); + public boolean setTypedefName(final String name) { + if( super.setTypedefName(name) ) { + typedefUnsigned = unsigned; + return true; + } else { + return false; + } } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java b/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java index 25d2d1d..8b06a7e 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java @@ -27,15 +27,23 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public abstract class MemoryLayoutType extends Type { private boolean isLayouted; - protected MemoryLayoutType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + protected MemoryLayoutType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); isLayouted = false; } + MemoryLayoutType(final MemoryLayoutType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + isLayouted = o.isLayouted; + } public boolean isLayouted() { return isLayouted; } - public void setLayouted() { isLayouted = true; } + public void setLayouted() { + isLayouted = true; + } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/PointerType.java b/src/java/com/jogamp/gluegen/cgram/types/PointerType.java index d1dfb17..5707b5c 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/PointerType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/PointerType.java @@ -39,111 +39,124 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class PointerType extends Type implements Cloneable { private final Type targetType; - private String computedName; - private boolean hasTypedefedName; public PointerType(final SizeThunk size, final Type targetType, final int cvAttributes) { + this(size, targetType, cvAttributes, null); + } + public PointerType(final SizeThunk size, final Type targetType, final int cvAttributes, final ASTLocusTag astLocus) { // can pass null for the final name parameter because the PointerType's getName() // completely replaces superclass behavior - this(size, targetType, cvAttributes, false, null); + super(targetType.getName() + " *", size, cvAttributes, astLocus); + this.targetType = targetType; } - private PointerType(final SizeThunk size, final Type targetType, final int cvAttributes, final boolean hasTypedefedName, final String typedefedName) { - super(targetType.getName() + " *", size, cvAttributes); - this.hasTypedefedName = false; - this.targetType = targetType; - if (hasTypedefedName) { - setName(typedefedName); - } + private PointerType(final PointerType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + targetType = o.targetType; } @Override - public int hashCode() { - return targetType.hashCode(); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new PointerType(this, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof PointerType))) { - return false; - } + protected int hashCodeImpl() { + return targetType.hashCode(); + } + + @Override + protected boolean equalsImpl(final Type arg) { final PointerType t = (PointerType) arg; - // Note we ignore the name of this type (which might be a typedef - // name) for comparison purposes because this is what allows - // e.g. a newly-fabricated type "PIXELFORMATDESCRIPTOR *" to be - // canonicalized to e.g. "LPPIXELFORMATDESCRIPTOR" - return ((getSize() == t.getSize()) - && (getCVAttributes() == t.getCVAttributes()) - && targetType.equals(t.targetType)); + return targetType.equals(t.targetType); + } + + @Override + protected int hashCodeSemanticsImpl() { + return targetType.hashCodeSemantics(); } @Override - public void setName(final String name) { - super.setName(name); - hasTypedefedName = true; + protected boolean equalSemanticsImpl(final Type arg) { + final PointerType pt = (PointerType) arg; + return targetType.equalSemantics(pt.targetType); + } + + @Override + public boolean isAnon() { + if ( isTypedef() ) { + return super.isAnon(); + } else { + return targetType.isAnon(); + } } @Override public String getName(final boolean includeCVAttrs) { - if (hasTypedefedName) { + if ( isTypedef() ) { return super.getName(includeCVAttrs); + } else if (!includeCVAttrs) { + return targetType.getName(includeCVAttrs) + " *"; } else { - // Lazy computation of name due to lazy setting of compound type - // names during parsing - if (computedName == null) { - computedName = (targetType.getName(includeCVAttrs) + " *").intern(); - } - if (!includeCVAttrs) { - return computedName; - } return targetType.getName(includeCVAttrs) + " * " + getCVAttributesString(); } } - public boolean hasTypedefedName() { - return hasTypedefedName; + @Override + public String getCName(final boolean includeCVAttrs) { + if ( isTypedef() ) { + return super.getCName(includeCVAttrs); + } else if (!includeCVAttrs) { + return targetType.getCName(includeCVAttrs) + " *"; + } else { + return targetType.getCName(includeCVAttrs) + " * " + getCVAttributesString(); + } } @Override - public PointerType asPointer() { + public final PointerType asPointer() { return this; } - public Type getTargetType() { + @Override + public final Type getTargetType() { return targetType; } @Override - public Type getBaseElementType() { - /** - if(targetType.isPointer()) { - return ((PointerType)targetType).getBaseElementType(); - } else { - return targetType; - } */ + public final Type getBaseElementType() { return targetType.getBaseElementType(); } @Override - public boolean isFunctionPointer() { + public final boolean isFunctionPointer() { return targetType.isFunction(); } @Override + public final int pointerDepth() { + return 1 + targetType.pointerDepth(); + } + + @Override public String toString() { - if (hasTypedefedName) { - return super.getName(true); + if ( isTypedef() ) { + return super.getCName(true); + } else { + return toStringInt(); + } + } + private String toStringInt() { + if (!targetType.isFunction()) { + return targetType.getCName(true) + " * " + getCVAttributesString(); } else { - if (!targetType.isFunction()) { - return targetType.toString() + " * " + getCVAttributesString(); - } - return toString(null, null); // this is a pointer to an unnamed function + // return toString(null, null); // this is a pointer to an unnamed function + return ((FunctionType) targetType).toString(null /* functionName */, null /* callingConvention */, false, true); } } @@ -162,9 +175,4 @@ public class PointerType extends Type implements Cloneable { super.visit(arg); targetType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - return new PointerType(getSize(), targetType, cvAttributes, hasTypedefedName, (hasTypedefedName ? getName() : null)); - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java b/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java index 8a86337..76f3ff1 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java @@ -39,10 +39,16 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public abstract class PrimitiveType extends Type implements Cloneable { - protected PrimitiveType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + protected PrimitiveType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + } + + PrimitiveType(final PrimitiveType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override diff --git a/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java b/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java index 9843d6b..7a9c62a 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java +++ b/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java @@ -41,17 +41,25 @@ package com.jogamp.gluegen.cgram.types; import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; /** Provides a level of indirection between the definition of a type's size and the absolute value of this size. Necessary when generating glue code for two different CPU architectures (e.g., 32-bit and 64-bit) from the same internal representation of the various types involved. */ -public abstract class SizeThunk implements Cloneable { +public abstract class SizeThunk implements Cloneable, SemanticEqualityOp { + /* pp */ static boolean relaxedEqSem = false; private final boolean fixedNativeSize; + public static void setRelaxedEqualSemanticsTest(final boolean v) { + relaxedEqSem = v; + } + // Private constructor because there are only a few of these - private SizeThunk(final boolean fixedNativeSize) { this.fixedNativeSize = fixedNativeSize; } + private SizeThunk(final boolean fixedNativeSize) { + this.fixedNativeSize = fixedNativeSize; + } @Override public Object clone() { @@ -67,6 +75,55 @@ public abstract class SizeThunk implements Cloneable { public abstract long computeSize(MachineDataInfo machDesc); public abstract long computeAlignment(MachineDataInfo machDesc); + @Override + public final int hashCode() { + final int hash = 0x02DEAD6F; // magic hash start + return ((hash << 5) - hash) + hashCodeImpl(); + } + /* pp */ abstract int hashCodeImpl(); + + @Override + public final boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof SizeThunk) ) { + return false; + } else { + final SizeThunk t = (SizeThunk) arg; + return hashCodeImpl() == t.hashCodeImpl(); + } + } + + @Override + public final int hashCodeSemantics() { + final int hash = 0x01DEAD5F; // magic hash start + return ((hash << 5) - hash) + hashCodeSemanticsImpl(); + } + /* pp */ abstract int hashCodeSemanticsImpl(); + + @Override + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof SizeThunk) ) { + return false; + } else { + final SizeThunk t = (SizeThunk) arg; + return hashCodeSemanticsImpl() == t.hashCodeSemanticsImpl(); + } + } + + static final int magic_int08 = 0x00000010; + static final int magic_int16 = 0x00000012; + static final int magic_int32 = 0x00000014; + static final int magic_intxx = 0x00000016; + static final int magic_long64 = 0x00000020; + static final int magic_longxx = 0x00000022; + static final int magic_float32 = 0x00000030; + static final int magic_float64 = 0x00000032; + static final int magic_aptr64 = 0x00000040; + static final int magic_ops = 0x00010000; + public static final SizeThunk INT8 = new SizeThunk(true) { @Override public long computeSize(final MachineDataInfo machDesc) { @@ -76,6 +133,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int8AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 1; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_int32 : magic_int08; } }; public static final SizeThunk INT16 = new SizeThunk(true) { @@ -87,6 +148,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int16AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 2; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_int32 : magic_int16; } }; public static final SizeThunk INT32 = new SizeThunk(true) { @@ -98,6 +163,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int32AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 3; } + @Override + protected int hashCodeSemanticsImpl() { return magic_int32; } }; public static final SizeThunk INTxx = new SizeThunk(false) { @@ -109,6 +178,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.intAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 4; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_int32 : magic_intxx; } }; public static final SizeThunk LONG = new SizeThunk(false) { @@ -120,6 +193,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.longAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 5; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_long64 : magic_longxx; } }; public static final SizeThunk INT64 = new SizeThunk(true) { @@ -131,6 +208,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int64AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 6; } + @Override + protected int hashCodeSemanticsImpl() { return magic_long64; } }; public static final SizeThunk FLOAT = new SizeThunk(true) { @@ -142,6 +223,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.floatAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 7; } + @Override + protected int hashCodeSemanticsImpl() { return magic_float32; } }; public static final SizeThunk DOUBLE = new SizeThunk(true) { @@ -153,6 +238,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.doubleAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 8; } + @Override + protected int hashCodeSemanticsImpl() { return magic_float64; } }; public static final SizeThunk POINTER = new SizeThunk(false) { @@ -164,6 +253,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.pointerAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 9; } + @Override + protected int hashCodeSemanticsImpl() { return magic_aptr64; } }; // Factory methods for performing certain limited kinds of @@ -181,6 +274,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = thunk2.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 10; + hash = ((hash << 5) - hash) + ( null != thunk1 ? thunk1.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != thunk2 ? thunk2.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 1; } }; } @@ -197,6 +299,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = thunk2.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 11; + hash = ((hash << 5) - hash) + ( null != thunk1 ? thunk1.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != thunk2 ? thunk2.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 2; } }; } @@ -239,6 +350,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = alignmentThunk.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 12; + hash = ((hash << 5) - hash) + ( null != offsetThunk ? offsetThunk.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != alignmentThunk ? alignmentThunk.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 3; } }; } @@ -255,6 +375,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = thunk2.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 13; + hash = ((hash << 5) - hash) + ( null != thunk1 ? thunk1.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != thunk2 ? thunk2.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 4; } }; } @@ -268,6 +397,14 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return 1; // no alignment for constants } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = 31 + 14; + return ((hash << 5) - hash) + constant; + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 5; } }; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/StructType.java b/src/java/com/jogamp/gluegen/cgram/types/StructType.java index 27099e9..fa78006 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/StructType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/StructType.java @@ -27,34 +27,25 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class StructType extends CompoundType { - public StructType(final String name, final SizeThunk size, final int cvAttributes) { - this(name, size, cvAttributes, null); + StructType(final String name, final SizeThunk size, final int cvAttributes, final String structName, final ASTLocusTag astLocus) { + super (name, size, cvAttributes, structName, astLocus); } - StructType(final String name, final SizeThunk size, final int cvAttributes, final String structName) { - super (name, size, cvAttributes, structName); + private StructType(final StructType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == null || !(arg instanceof StructType)) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new StructType(this, cvAttributes, astLocus); } @Override public final boolean isStruct() { return true; } @Override public final boolean isUnion() { return false; } - - @Override - Type newCVVariant(final int cvAttributes) { - final StructType t = new StructType(getName(), getSize(), cvAttributes, getStructName()); - t.setFields(getFields()); - return t; - } - } diff --git a/src/java/com/jogamp/gluegen/cgram/types/Type.java b/src/java/com/jogamp/gluegen/cgram/types/Type.java index 28ba6b4..04c46af 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/Type.java +++ b/src/java/com/jogamp/gluegen/cgram/types/Type.java @@ -40,46 +40,110 @@ package com.jogamp.gluegen.cgram.types; -import java.util.List; - import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.GlueGen; +import com.jogamp.gluegen.TypeConfig; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; /** Models a C type. Primitive types include int, float, and double. All types have an associated name. Structs and unions are modeled as "compound" types -- composed of fields of primitive or other types. */ -public abstract class Type implements Cloneable { - +public abstract class Type implements SemanticEqualityOp, ASTLocusTagProvider { + public final boolean relaxedEqSem; + private final int cvAttributes; + final ASTLocusTag astLocus; private String name; private SizeThunk size; - private final int cvAttributes; - private int typedefedCVAttributes; - private boolean hasTypedefName; - - protected Type(final String name, final SizeThunk size, final int cvAttributes) { - setName(name); + private int typedefCVAttributes; + private boolean isTypedef; + private boolean hasCachedHash; + private int cachedHash; + private boolean hasCachedSemanticHash; + private int cachedSemanticHash; + + protected Type(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + setName(name); // -> clearCache() + this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest(); + this.cvAttributes = cvAttributes; + this.astLocus = astLocus; this.size = size; + this.typedefCVAttributes = 0; + this.isTypedef = false; + } + Type(final Type o, final int cvAttributes, final ASTLocusTag astLocus) { + this.relaxedEqSem = o.relaxedEqSem; this.cvAttributes = cvAttributes; - hasTypedefName = false; + this.astLocus = astLocus; + this.name = o.name; + this.size = o.size; + this.typedefCVAttributes = o.typedefCVAttributes; + this.isTypedef = o.isTypedef; + clearCache(); } - @Override - public Object clone() { - try { - return super.clone(); - } catch (final CloneNotSupportedException ex) { - throw new InternalError(); + protected final void clearCache() { + hasCachedHash = false; + cachedHash = 0; + hasCachedSemanticHash = false; + cachedSemanticHash = 0; + } + + /** + * Return a variant of this type matching the given const/volatile + * attributes. May return this object if the attributes match. + */ + public final Type newCVVariant(final int cvAttributes) { + if (this.cvAttributes == cvAttributes) { + return this; + } else { + return newVariantImpl(true, cvAttributes, astLocus); } } + /** + * Clones this instance using a new {@link ASTLocusTag}. + */ + public Type clone(final ASTLocusTag newLoc) { + return newVariantImpl(true, cvAttributes, newLoc); + } + + /** + * Create a new variant of this type matching the given parameter + * <p> + * Implementation <i>must</i> use {@link Type}'s copy-ctor: {@link #Type(Type, int, ASTLocusTag)}! + * </p> + * @param newCVVariant true if new variant is intended to have new <i>cvAttributes</i> + * @param cvAttributes the <i>cvAttributes</i> to be used + * @param astLocus the {@link ASTLocusTag} to be used + */ + abstract Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus); + + @Override + public final ASTLocusTag getASTLocusTag() { return astLocus; } + + public boolean isAnon() { return null == name; } + /** Returns the name of this type. The returned string is suitable - for use as a type specifier. Does not include any const/volatile + for use as a type specifier for native C. Does not include any const/volatile + attributes. */ + public final String getCName() { return getCName(false); } + + /** Returns the name of this type, optionally including + const/volatile attributes. The returned string is suitable for + use as a type specifier for native C. */ + public String getCName(final boolean includeCVAttrs) { return getName(includeCVAttrs); } + + /** Returns the name of this type. The returned string is suitable + for use as a type specifier for Java. Does not include any const/volatile attributes. */ public final String getName() { return getName(false); } /** Returns the name of this type, optionally including const/volatile attributes. The returned string is suitable for - use as a type specifier. */ + use as a type specifier for Java. */ public String getName(final boolean includeCVAttrs) { if (!includeCVAttrs) { return name; @@ -87,26 +151,53 @@ public abstract class Type implements Cloneable { return getCVAttributesString() + name; } - private void append(final StringBuilder sb, final String val, final boolean prepComma) { + /** + * Returns a string representation of this type. + * The returned string is suitable for use as a type specifier for native C. + * It does contain an expanded description of structs/unions, + * hence may not be suitable for type declarations. + */ + @Override + public String toString() { + return getCName(true); + } + + + private static StringBuilder append(final StringBuilder sb, final String val, final boolean prepComma) { if( prepComma ) { sb.append(", "); } sb.append(val); + return sb; } // For debugging - public String getDebugString() { + public final String getDebugString() { final StringBuilder sb = new StringBuilder(); boolean prepComma = false; sb.append("CType["); + sb.append("(").append(getClass().getSimpleName()).append(") "); + if( isTypedef() ) { + sb.append("typedef "); + } if( null != name ) { - append(sb, "'"+name+"'", prepComma); prepComma=true; + sb.append("'").append(name).append("'"); } else { - append(sb, "ANON", prepComma); prepComma=true; + sb.append("ANON"); + } + final Type targetType = getTargetType(); + if( null != targetType && this != targetType ) { + sb.append(" -> "); + if (!targetType.isFunction()) { + sb.append("(" + targetType.toString() + ") * " + getCVAttributesString()); + } else { + sb.append(((FunctionType) targetType).toString(null /* functionName */, null /* callingConvention */, false, true)); + } } - if( hasTypedefName() ) { - sb.append(" (typedef)"); + if( GlueGen.debug() ) { + sb.append(", o=0x"+Integer.toHexString(objHash())); } - append(sb, "size ", prepComma); prepComma=true; + sb.append(", size "); + prepComma=true; if( null != size ) { final long mdSize; { @@ -121,67 +212,135 @@ public abstract class Type implements Cloneable { sb.append(" ZERO"); } append(sb, "[", prepComma); prepComma=false; - if( isConst() ) { - append(sb, "const ", false); - } - if( isVolatile() ) { - append(sb, "volatile ", false); - } - if( isPointer() ) { - append(sb, "pointer*"+pointerDepth(), prepComma); prepComma=true; - } - if( isArray() ) { - append(sb, "array*"+arrayDimension(), prepComma); prepComma=true; - } - if( isBit() ) { - append(sb, "bit", prepComma); prepComma=true; - } - if( isCompound() ) { - sb.append("struct{").append(asCompound().getNumFields()); - append(sb, "}", prepComma); prepComma=true; - } - if( isDouble() ) { - append(sb, "double", prepComma); prepComma=true; - } - if( isEnum() ) { - append(sb, "enum", prepComma); prepComma=true; - } - if( isFloat() ) { - append(sb, "float", prepComma); prepComma=true; - } - if( isFunction() ) { - append(sb, "function", prepComma); prepComma=true; - } - if( isFunctionPointer() ) { - append(sb, "funcPointer", prepComma); prepComma=true; - } - if( isInt() ) { - append(sb, "int", prepComma); prepComma=true; - } - if( isVoid() ) { - append(sb, "void", prepComma); prepComma=true; + { + append(sb, "const[", prepComma); prepComma=false; + { + if( isConstTypedef() ) { + append(sb, "type ", prepComma); prepComma=true; + } + if( isConstRaw() ) { + append(sb, "inst -> ", prepComma); prepComma=false; + } + if( isConst() ) { + append(sb, "true]", prepComma); + } else { + append(sb, "false]", prepComma); + } + prepComma=true; + } + if( isVolatile() ) { + append(sb, "volatile ", prepComma); prepComma=true; + } + if( isPointer() ) { + append(sb, "pointer*"+pointerDepth(), prepComma); prepComma=true; + } + if( isArray() ) { + append(sb, "array*"+arrayDimension(), prepComma); prepComma=true; + } + if( isBit() ) { + append(sb, "bit", prepComma); prepComma=true; + } + if( isCompound() ) { + append(sb, "struct{", prepComma).append(asCompound().getStructName()).append(": ").append(asCompound().getNumFields()); + append(sb, "}", prepComma); prepComma=true; + } + if( isDouble() ) { + append(sb, "double", prepComma); prepComma=true; + } + if( isEnum() ) { + final EnumType eT = asEnum(); + append(sb, "enum ", prepComma).append(" [").append(eT.getUnderlyingType()).append("] {").append(eT.getNumEnumerates()).append(": "); + eT.appendEnums(sb, false); + prepComma=true; + } + if( isFloat() ) { + append(sb, "float", prepComma); prepComma=true; + } + if( isFunction() ) { + append(sb, "function", prepComma); prepComma=true; + } + if( isFunctionPointer() ) { + append(sb, "funcPointer", prepComma); prepComma=true; + } + if( isInt() ) { + append(sb, "int", prepComma); prepComma=true; + } + if( isVoid() ) { + append(sb, "void", prepComma); prepComma=true; + } + sb.append("]"); } - sb.append("]]"); + sb.append("]"); return sb.toString(); } + private final int objHash() { return super.hashCode(); } + - /** Set the name of this type; used for handling typedefs. */ - public void setName(final String name) { - if (name == null) { + /** + * Returns {@code true} if given {@code name} is not {@code null} + * and has a length > 0. In this case this instance's names will + * be set to the internalized version. + * <p> + * Otherwise method returns {@code false} + * and this instance's name will be set to {@code null}. + * </p> + * <p> + * Method issues {@link #clearCache()}, to force re-evaluation + * of hashes. + * </p> + */ + private final boolean setName(final String name) { + clearCache(); + if( null == name || 0 == name.length() ) { this.name = name; + return false; } else { this.name = name.intern(); + return true; + } + } + + /** + * Set the typedef name of this type and renders this type a typedef, + * if given {@code name} has a length. + * <p> + * Method issues {@link #clearCache()}, to force re-evaluation + * of hashes. + * </p> + */ + public boolean setTypedefName(final String name) { + if( setName(name) ) { + // Capture the const/volatile attributes at the time of typedef so + // we don't redundantly repeat them in the CV attributes string + typedefCVAttributes = cvAttributes; + isTypedef = true; + return true; + } else { + return false; } - // Capture the const/volatile attributes at the time of typedef so - // we don't redundantly repeat them in the CV attributes string - typedefedCVAttributes = cvAttributes; - hasTypedefName = true; + } + final void setTypedef(final int typedefedCVAttributes) { + this.name = this.name.intern(); // just make sure .. + this.typedefCVAttributes = typedefedCVAttributes; + this.isTypedef = true; + clearCache(); + } + final int getTypedefCVAttributes() { + return typedefCVAttributes; + } + + /** + * Indicates whether this type is a typedef type, + * i.e. declared via {@link #setTypedefName(String)}. + */ + public final boolean isTypedef() { + return isTypedef; } /** SizeThunk which computes size of this type in bytes. */ - public SizeThunk getSize() { return size; } + public final SizeThunk getSize() { return size; } /** Size of this type in bytes according to the given MachineDataInfo. */ - public long getSize(final MachineDataInfo machDesc) { + public final long getSize(final MachineDataInfo machDesc) { final SizeThunk thunk = getSize(); if (thunk == null) { throw new RuntimeException("No size set for type \"" + getName() + "\""); @@ -189,7 +348,10 @@ public abstract class Type implements Cloneable { return thunk.computeSize(machDesc); } /** Set the size of this type; only available for CompoundTypes. */ - void setSize(final SizeThunk size) { this.size = size; } + final void setSize(final SizeThunk size) { + this.size = size; + clearCache(); + } /** Casts this to a BitType or returns null if not a BitType. */ public BitType asBit() { return null; } @@ -213,82 +375,153 @@ public abstract class Type implements Cloneable { public VoidType asVoid() { return null; } /** Indicates whether this is a BitType. */ - public boolean isBit() { return (asBit() != null); } + public final boolean isBit() { return (asBit() != null); } /** Indicates whether this is an IntType. */ - public boolean isInt() { return (asInt() != null); } + public final boolean isInt() { return (asInt() != null); } /** Indicates whether this is an EnumType. */ - public boolean isEnum() { return (asEnum() != null); } + public final boolean isEnum() { return (asEnum() != null); } /** Indicates whether this is a FloatType. */ - public boolean isFloat() { return (asFloat() != null); } + public final boolean isFloat() { return (asFloat() != null); } /** Indicates whether this is a DoubleType. */ - public boolean isDouble() { return (asDouble() != null); } + public final boolean isDouble() { return (asDouble() != null); } /** Indicates whether this is a PointerType. */ - public boolean isPointer() { return (asPointer() != null); } + public final boolean isPointer() { return (asPointer() != null); } /** Indicates whether this is an ArrayType. */ - public boolean isArray() { return (asArray() != null); } + public final boolean isArray() { return (asArray() != null); } /** Indicates whether this is a CompoundType. */ - public boolean isCompound() { return (asCompound() != null); } + public final boolean isCompound() { return (asCompound() != null); } /** Indicates whether this is a FunctionType. */ - public boolean isFunction() { return (asFunction() != null); } + public final boolean isFunction() { return (asFunction() != null); } /** Indicates whether this is a VoidType. */ - public boolean isVoid() { return (asVoid() != null); } + public final boolean isVoid() { return (asVoid() != null); } - /** Indicates whether this type is const. */ - public boolean isConst() { return (((cvAttributes & ~typedefedCVAttributes) & CVAttributes.CONST) != 0); } /** Indicates whether this type is volatile. */ - public boolean isVolatile() { return (((cvAttributes & ~typedefedCVAttributes) & CVAttributes.VOLATILE) != 0); } + public final boolean isVolatile() { return 0 != ( ( cvAttributes & ~typedefCVAttributes ) & CVAttributes.VOLATILE ); } + /** Indicates whether this type is const. */ + public final boolean isConst() { return 0 != ( ( cvAttributes & ~typedefCVAttributes ) & CVAttributes.CONST ); } + + private final boolean isConstTypedef() { return 0 != ( typedefCVAttributes & CVAttributes.CONST ); } + private final boolean isConstRaw() { return 0 != ( cvAttributes & CVAttributes.CONST ); } /** Indicates whether this type is a primitive type. */ - public boolean isPrimitive(){ return false; } + public boolean isPrimitive(){ return false; } /** Convenience routine indicating whether this Type is a pointer to a function. */ public boolean isFunctionPointer() { - return (isPointer() && asPointer().getTargetType().isFunction()); + return false; + } + + /** + * Checks the base type of pointer-to-pointer, pointer, array or plain for const-ness. + * <p> + * Note: Intermediate 'const' qualifier are not considered, e.g. const pointer. + * </p> + */ + public final boolean isBaseTypeConst() { + return getBaseElementType().isConst(); } /** Hashcode for Types. */ @Override - public int hashCode() { - if (name == null) { - return 0; - } - - if (cvAttributes != 0) { - final String nameWithAttribs = name + cvAttributes; - return nameWithAttribs.hashCode(); + public final int hashCode() { + if( !hasCachedHash ) { + // 31 * x == (x << 5) - x + int hash = 31 + ( isTypedef ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( null != size ? size.hashCode() : 0 ); + hash = ((hash << 5) - hash) + cvAttributes; + hash = ((hash << 5) - hash) + typedefCVAttributes; + hash = ((hash << 5) - hash) + ( null != name ? name.hashCode() : 0 ); + if( !isTypedef ) { + hash = ((hash << 5) - hash) + hashCodeImpl(); + } + cachedHash = hash; + hasCachedHash = true; } - return name.hashCode(); + return cachedHash; } + protected abstract int hashCodeImpl(); /** - * Equality test for Types. + * Equality test for Types inclusive its given {@link #getName() name}. */ @Override - public boolean equals(final Object arg) { + public final boolean equals(final Object arg) { if (arg == this) { - return true; + return true; + } else if ( !getClass().isInstance(arg) ) { // implies null == arg || !(arg instanceof Type) + return false; + } else { + final Type t = (Type)arg; + if( isTypedef == t.isTypedef && + ( ( null != size && size.equals(t.size) ) || + ( null == size && null == t.size ) + ) && + cvAttributes == t.cvAttributes && + typedefCVAttributes == t.typedefCVAttributes && + ( null == name ? null == t.name : name.equals(t.name) ) + ) + { + if( !isTypedef ) { + return equalsImpl(t); + } else { + return true; + } + } else { + return false; + } } + } + protected abstract boolean equalsImpl(final Type t); - if ( !(arg instanceof Type) ) { - return false; + @Override + public final int hashCodeSemantics() { + if( !hasCachedSemanticHash ) { + // 31 * x == (x << 5) - x + int hash = 31 + ( null != size ? size.hashCodeSemantics() : 0 ); + if( !relaxedEqSem ) { + hash = ((hash << 5) - hash) + cvAttributes; + hash = ((hash << 5) - hash) + typedefCVAttributes; + } + hash = ((hash << 5) - hash) + hashCodeSemanticsImpl(); + cachedSemanticHash = hash; + hasCachedSemanticHash = true; } - - final Type t = (Type)arg; - return size == t.size && cvAttributes == t.cvAttributes && - ( null == name ? null == t.name : name.equals(t.name) ) ; + return cachedSemanticHash; } + protected abstract int hashCodeSemanticsImpl(); - /** Returns a string representation of this type. This string is not - necessarily suitable for use as a type specifier; for example, - it will contain an expanded description of structs/unions. */ @Override - public String toString() { - return getName(true); + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof Type) || + !getClass().isInstance(arg) ) { // implies null == arg + return false; + } else { + final Type t = (Type) arg; + if( ( ( null != size && size.equalSemantics(t.size) ) || + ( null == size && null == t.size ) + ) && + ( relaxedEqSem || + ( cvAttributes == t.cvAttributes && + typedefCVAttributes == t.typedefCVAttributes + ) + ) + ) + { + return equalSemanticsImpl(t); + } else { + return false; + } + } } + protected abstract boolean equalSemanticsImpl(final Type t); - /** Visit this type and all of the component types of this one; for - example, the return type and argument types of a FunctionType. */ + /** + * Traverse this {@link Type} and all of its component types; for + * example, the return type and argument types of a FunctionType. + */ public void visit(final TypeVisitor visitor) { visitor.visitType(this); } @@ -306,45 +539,18 @@ public abstract class Type implements Cloneable { return ""; } - /** Return a variant of this type matching the given const/volatile - attributes. May return this object if the attributes match. */ - public final Type getCVVariant(final int cvAttributes) { - if (this.cvAttributes == cvAttributes) { - return this; - } - return newCVVariant(cvAttributes); - } - - /** Create a new variant of this type matching the given - const/volatile attributes. */ - abstract Type newCVVariant(int cvAttributes); - - /** Indicates whether setName() has been called on this type, - indicating that it already has a typedef name. */ - public boolean hasTypedefName() { - return hasTypedefName; - } - /** Helper method for determining how many pointer indirections this type represents (i.e., "void **" returns 2). Returns 0 if this type is not a pointer type. */ public int pointerDepth() { - final PointerType pt = asPointer(); - if (pt == null) { - return 0; - } - return 1 + pt.getTargetType().pointerDepth(); + return 0; } /** Helper method for determining how many array dimentions this type represents (i.e., "char[][]" returns 2). Returns 0 if this type is not an array type. */ public int arrayDimension() { - final ArrayType arrayType = asArray(); - if (arrayType == null) { - return 0; - } - return 1 + arrayType.getElementType().arrayDimension(); + return 0; } /** @@ -358,8 +564,10 @@ public abstract class Type implements Cloneable { return this; } - /** Helper routine for list equality comparison */ - static <C> boolean listsEqual(final List<C> a, final List<C> b) { - return ((a == null && b == null) || (a != null && b != null && a.equals(b))); + /** + * Helper method to returns the target type of this type, in case another type is being referenced. + */ + public Type getTargetType() { + return this; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeComparator.java b/src/java/com/jogamp/gluegen/cgram/types/TypeComparator.java new file mode 100644 index 0000000..850d953 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeComparator.java @@ -0,0 +1,143 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen.cgram.types; + +import java.util.List; + +public class TypeComparator { + /** + * Supports semantic equality and hash functions for types. + */ + public static interface SemanticEqualityOp { + /** + * Semantic hashcode for Types exclusive its given {@link #getName() name}. + * @see #equalSemantics(SemanticEqualityOp) + */ + int hashCodeSemantics(); + + /** + * Semantic equality test for Types exclusive its given {@link #getName() name}. + * @see #hashCodeSemantics() + */ + boolean equalSemantics(final SemanticEqualityOp arg); + } + /** + * Supports common interface for {@link SemanticEqualityOp} and {@link AliasedSymbol}. + */ + public static interface AliasedSemanticSymbol extends AliasedSymbol, SemanticEqualityOp { }; + + /** Helper routine for list equality comparison*/ + static <C> boolean listsEqual(final List<C> a, final List<C> b) { + if( a == null ) { + if( null != b ) { + return false; + } else { + return true; // elements equal, i.e. both null + } + } + if( b != null && a.size() == b.size() ) { + final int count = a.size(); + for(int i=0; i<count; i++) { + final C ac = a.get(i); + final C bc = b.get(i); + if( null == ac ) { + if( null != bc ) { + return false; + } else { + continue; // elements equal, i.e. both null + } + } + if( !ac.equals(bc) ) { + return false; + } + } + return true; + } + return false; + } + + /** Helper routine for list hashCode */ + static <C extends SemanticEqualityOp> int listsHashCode(final List<C> a) { + if( a == null ) { + return 0; + } else { + final int count = a.size(); + int hash = 31; + for(int i=0; i<count; i++) { + final C ac = a.get(i); + hash = ((hash << 5) - hash) + ( null != ac ? ac.hashCode() : 0 ); + } + return hash; + } + } + + /** Helper routine for list semantic equality comparison*/ + static <C extends SemanticEqualityOp> boolean listsEqualSemantics(final List<C> a, final List<C> b) { + if( a == null ) { + if( null != b ) { + return false; + } else { + return true; // elements equal, i.e. both null + } + } + if( b != null && a.size() == b.size() ) { + final int count = a.size(); + for(int i=0; i<count; i++) { + final C ac = a.get(i); + final C bc = b.get(i); + if( null == ac ) { + if( null != bc ) { + return false; + } else { + continue; // elements equal, i.e. both null + } + } + if( !ac.equalSemantics(bc) ) { + return false; + } + } + return true; + } + return false; + } + + /** Helper routine for list hashCode */ + static <C extends SemanticEqualityOp> int listsHashCodeSemantics(final List<C> a) { + if( a == null ) { + return 0; + } else { + final int count = a.size(); + int hash = 31; + for(int i=0; i<count; i++) { + final C ac = a.get(i); + hash = ((hash << 5) - hash) + ( null != ac ? ac.hashCodeSemantics() : 0 ); + } + return hash; + } + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java b/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java index cd03388..c1cfcdf 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java @@ -41,6 +41,9 @@ package com.jogamp.gluegen.cgram.types; import java.util.*; +import com.jogamp.gluegen.GlueGen; +import com.jogamp.gluegen.JavaConfiguration; + /** Utility class for recording names of typedefs and structs. */ @@ -63,6 +66,38 @@ public class TypeDictionary { return map.get(name); } + public List<Type> getEqualSemantics(final Type s, final JavaConfiguration cfg, final boolean skipOpaque) { + final List<Type> res = new ArrayList<Type>(); + if( !skipOpaque || null == cfg.typeInfo(s) ) { + final Set<Map.Entry<String, Type>> entries = entrySet(); + for(final Iterator<Map.Entry<String, Type>> iter = entries.iterator(); iter.hasNext(); ) { + final Map.Entry<String, Type> entry = iter.next(); + final Type t = entry.getValue(); + if( s.equalSemantics(t) ) { + if( !skipOpaque || null == cfg.typeInfo(t) ) { + if( GlueGen.debug() ) { + System.err.println(" tls["+res.size()+"]: -> "+entry.getKey()+" -> "+t.getDebugString()); + } + res.add(t); + } + } + } + } + return res; + } + public Type getEqualSemantics1(final Type s, final JavaConfiguration cfg, final boolean skipOpaque) { + final List<Type> tls = getEqualSemantics(s, cfg, skipOpaque); + if( tls.size() > 0 ) { + final Type res = tls.get(0); + if( GlueGen.debug() ) { + System.err.println(" tls.0: "+res.getDebugString()); + } + return res; + } else { + return null; + } + } + //this method is broken /** * Get the names that correspond to the given type. There will be more than diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java b/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java index 89c014b..ed5cfa9 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java @@ -39,6 +39,12 @@ package com.jogamp.gluegen.cgram.types; +/** + * A visitor for {@link Type}'s visitor model. + */ public interface TypeVisitor { + /** + * Visiting the given {@link Type}. + */ public void visitType(Type t); } diff --git a/src/java/com/jogamp/gluegen/cgram/types/UnionType.java b/src/java/com/jogamp/gluegen/cgram/types/UnionType.java index 99d2fed..8c6d9dd 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/UnionType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/UnionType.java @@ -27,34 +27,25 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class UnionType extends CompoundType { - public UnionType(final String name, final SizeThunk size, final int cvAttributes) { - this(name, size, cvAttributes, null); + UnionType(final String name, final SizeThunk size, final int cvAttributes, final String structName, final ASTLocusTag astLocus) { + super (name, size, cvAttributes, structName, astLocus); } - UnionType(final String name, final SizeThunk size, final int cvAttributes, final String structName) { - super (name, size, cvAttributes, structName); + private UnionType(final UnionType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == null || !(arg instanceof UnionType)) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new UnionType(this, cvAttributes, astLocus); } @Override public final boolean isStruct() { return false; } @Override public final boolean isUnion() { return true; } - - @Override - Type newCVVariant(final int cvAttributes) { - final UnionType t = new UnionType(getName(), getSize(), cvAttributes, getStructName()); - t.setFields(getFields()); - return t; - } - } diff --git a/src/java/com/jogamp/gluegen/cgram/types/VoidType.java b/src/java/com/jogamp/gluegen/cgram/types/VoidType.java index 2e1f069..bf51523 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/VoidType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/VoidType.java @@ -39,14 +39,25 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class VoidType extends Type implements Cloneable { - public VoidType(final int cvAttributes) { - this("void", cvAttributes); + public VoidType(final int cvAttributes, final ASTLocusTag astLocus) { + this("void", cvAttributes, astLocus); + } + + private VoidType(final String name, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, null, cvAttributes, astLocus); + } + + private VoidType(final VoidType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } - private VoidType(final String name, final int cvAttributes) { - super(name, null, cvAttributes); + @Override + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new VoidType(this, cvAttributes, astLocus); } @Override @@ -55,7 +66,22 @@ public class VoidType extends Type implements Cloneable { } @Override - Type newCVVariant(final int cvAttributes) { - return new VoidType(getName(), cvAttributes); + protected int hashCodeImpl() { + return 0; + } + + @Override + protected boolean equalsImpl(final Type t) { + return true; + } + + @Override + protected int hashCodeSemanticsImpl() { + return 0; + } + + @Override + protected boolean equalSemanticsImpl(final Type t) { + return true; } } diff --git a/src/java/com/jogamp/gluegen/pcpp/PCPP.java b/src/java/com/jogamp/gluegen/pcpp/PCPP.java index aca7b14..c766634 100644 --- a/src/java/com/jogamp/gluegen/pcpp/PCPP.java +++ b/src/java/com/jogamp/gluegen/pcpp/PCPP.java @@ -56,16 +56,24 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.logging.Logger; +import java.util.logging.Level; + +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ConstantDefinition; +import com.jogamp.gluegen.GenericCPP; +import com.jogamp.gluegen.GlueGenException; +import com.jogamp.gluegen.Logging; +import com.jogamp.gluegen.Logging.LoggerIf; + import static java.util.logging.Level.*; /** A minimal pseudo-C-preprocessor designed in particular to preserve #define statements defining constants so they can be observed by a glue code generator. */ -public class PCPP { +public class PCPP implements GenericCPP { - private static final Logger LOG = Logger.getLogger(PCPP.class.getPackage().getName()); + private final LoggerIf LOG; /** Map containing the results of #define statements. We must evaluate certain very simple definitions (to properly handle @@ -86,13 +94,15 @@ public class PCPP { private final boolean enableCopyOutput2Stderr; public PCPP(final List<String> includePaths, final boolean debug, final boolean copyOutput2Stderr) { + LOG = Logging.getLogger(PCPP.class.getPackage().getName(), PCPP.class.getSimpleName()); this.includePaths = includePaths; setOut(System.out); enableDebugPrint = debug; enableCopyOutput2Stderr = copyOutput2Stderr; } - public void run(final Reader reader, final String filename) throws IOException { + @Override + public void run(final Reader reader, final String filename) throws GlueGenException { StreamTokenizer tok = null; BufferedReader bufReader = null; if (reader instanceof BufferedReader) { @@ -108,13 +118,29 @@ public class PCPP { final ParseState oldState = state; state = curState; lineDirective(); - parse(); + try { + parse(); + } catch (final Exception e) { + final StringBuilder buf = new StringBuilder("Preprocessor failed"); + LOG.log(Level.SEVERE, buf.toString(), e); + if( e instanceof GlueGenException ) { + throw (GlueGenException)e; + } else { + throw new GlueGenException("Preprocessor failed", + new ASTLocusTag(filename(), lineNumber(), -1, null), e); + } + } state = oldState; if (state != null) { lineDirective(); } } + @Override + public List<ConstantDefinition> getConstantDefinitions() throws GlueGenException { + return new ArrayList<ConstantDefinition>(); // NOP + } + private void initTokenizer(final StreamTokenizer tok) { tok.resetSyntax(); tok.wordChars('a', 'z'); @@ -131,6 +157,7 @@ public class PCPP { tok.slashStarComments(true); } + @Override public String findFile(final String filename) { final String sep = File.separator; for (final String inclPath : includePaths) { @@ -143,10 +170,12 @@ public class PCPP { return null; } + @Override public OutputStream out() { return out; } + @Override public void setOut(final OutputStream out) { this.out = out; writer = new PrintWriter(out); @@ -375,7 +404,7 @@ public class PCPP { } } - if(isIdentifier(value)) { + if(ConstantDefinition.isIdentifier(value)) { newS +=" "; } @@ -459,28 +488,30 @@ public class PCPP { if (enabled()) { final String oldDef = defineMap.remove(name); if (oldDef == null) { - LOG.log(WARNING, "ignoring redundant \"#undef {0}\", at \"{1}\" line {2}: \"{3}\" was not previously defined", - new Object[]{name, filename(), lineNumber(), name}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, name), + "ignoring redundant \"#undef {0}\" - was not previously defined", + name); } else { // System.err.println("UNDEFINED: '" + name + "' (line " + lineNumber() + " file " + filename() + ")"); } nonConstantDefines.remove(name); } else { - LOG.log(WARNING, "FAILED TO UNDEFINE: ''{0}'' (line {1} file {2})", new Object[]{name, lineNumber(), filename()}); + LOG.log(INFO, new ASTLocusTag(filename(), lineNumber(), -1, name), + "DISABLED UNDEFINE: ''{0}''", name); } } private void handleWarning() throws IOException { final String msg = nextWordOrString(); if (enabled()) { - LOG.log(WARNING, "#warning {0} at \"{1}\" line \"{2}\"", new Object[]{msg, filename(), lineNumber()}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), msg); } } - private void handleError() throws IOException { + private void handleError() throws IOException, GlueGenException { final String msg = nextWordOrString(); if (enabled()) { - throw new RuntimeException("#error "+msg+" at \""+filename()+"\" line "+lineNumber()); + throw new GlueGenException(msg, new ASTLocusTag(filename(), lineNumber(), -1, null)); } } @@ -520,6 +551,7 @@ public class PCPP { addDefine(name, macroDefinition, values); } + @Override public void addDefine(final String name, final String value) { final List<String> values = new ArrayList<String>(); values.add(value); @@ -541,7 +573,8 @@ public class PCPP { final String value = ""; final String oldDef = defineMap.put(name, value); if (oldDef != null && !oldDef.equals(value)) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"\"", new Object[]{name, oldDef}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"\"", name, oldDef); } // We don't want to emit the define, because it would serve no purpose // and cause GlueGen errors (confuse the GnuCParser) @@ -551,12 +584,13 @@ public class PCPP { // See whether the value is a constant final String value = values.get(0); - if (isConstant(value)) { + if (ConstantDefinition.isNumber(value)) { // Value is numeric constant like "#define FOO 5". // Put it in the #define map final String oldDef = defineMap.put(name, value); if (oldDef != null && !oldDef.equals(value)) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"{2}\"", new Object[]{name, oldDef, value}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"{2}\"", name, oldDef, value); } debugPrint(true, "DEFINE " + name + " ["+oldDef+" ] -> "+value + " CONST"); //System.err.println("//---DEFINED: " + name + " to \"" + value + "\""); @@ -606,7 +640,8 @@ public class PCPP { final Macro macro = new Macro(params, values); final Macro oldDef = macroMap.put(name, macro); if (oldDef != null) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"{2}\"", new Object[]{name, oldDef, macro}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"{2}\"", name, oldDef, macro); } emitDefine = false; @@ -618,7 +653,7 @@ public class PCPP { boolean containsIdentifier = false; for (final String value : values) { - if(isIdentifier(value)) { + if(ConstantDefinition.isIdentifier(value)) { containsIdentifier = true; break; } @@ -657,7 +692,8 @@ public class PCPP { final String oldDef = defineMap.put(name, value); if (oldDef != null && !oldDef.equals(value)) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"{2}\"", new Object[]{name, oldDef, value}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"{2}\"", name, oldDef, value); } debugPrint(true, "DEFINE " + name + " ["+oldDef+" ] -> "+value + " CONST"); // System.err.println("#define " + name +" "+value + " CONST EXPRESSION"); @@ -681,68 +717,6 @@ public class PCPP { //System.err.println("OUT HANDLE_DEFINE: " + name); } - private boolean isIdentifier(final String value) { - - boolean identifier = false; - - final char[] chars = value.toCharArray(); - - for (int i = 0; i < chars.length; i++) { - final char c = chars[i]; - if (i == 0) { - if (Character.isJavaIdentifierStart(c)) { - identifier = true; - } - } else { - if (!Character.isJavaIdentifierPart(c)) { - identifier = false; - break; - } - } - } - return identifier; - } - - private boolean isConstant(final String s) { - if (s.startsWith("0x") || s.startsWith("0X")) { - return checkHex(s); - } else { - return checkDecimal(s); - } - } - - private boolean checkHex(final String s) { - char c='\0'; - int i; - for (i = 2; i < s.length(); i++) { - c = s.charAt(i); - if (!((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'))) { - break; - } - } - if(i==s.length()) { - return true; - } else if(i==s.length()-1) { - // Const qualifier .. - return c == 'l' || c == 'L' || - c == 'f' || c == 'F' || - c == 'u' || c == 'U' ; - } - return false; - } - - private boolean checkDecimal(final String s) { - try { - Float.valueOf(s); - } catch (final NumberFormatException e) { - // not parsable as a number - return false; - } - return true; - } - private String resolveDefine(final String word, final boolean returnNullIfNotFound) { String lastWord = defineMap.get(word); if (lastWord == null) { @@ -920,6 +894,27 @@ public class PCPP { ifValue = false; } break; + case '*': + { + // NOTE: we don't handle expressions like this properly + final boolean rhs = handleIfRecursive(false); + ifValue = false; + } + break; + case '+': + { + // NOTE: we don't handle expressions like this properly + final boolean rhs = handleIfRecursive(false); + ifValue = false; + } + break; + case '-': + { + // NOTE: we don't handle expressions like this properly + final boolean rhs = handleIfRecursive(false); + ifValue = false; + } + break; case '=': { // NOTE: we don't handle expressions like this properly @@ -1008,7 +1003,8 @@ public class PCPP { buf.append(curTokenAsString()); } if (t == StreamTokenizer.TT_EOF) { - LOG.warning("unexpected EOF while processing #include directive"); + LOG.warning(new ASTLocusTag(filename(), lineNumber(), -1, null), + "unexpected EOF while processing #include directive"); } filename = buf.toString(); } diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java index 5c059c9..37a39e1 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java @@ -42,30 +42,35 @@ package com.jogamp.gluegen.procaddress; import com.jogamp.gluegen.CMethodBindingEmitter; import com.jogamp.gluegen.MethodBinding; import com.jogamp.gluegen.JavaType; + import java.io.*; + import com.jogamp.gluegen.cgram.types.*; public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { private boolean callThroughProcAddress; - private boolean needsLocalTypedef; + private boolean hasProcAddrTypedef; private String localTypedefCallingConvention; private static final String procAddressJavaTypeName = JavaType.createForClass(Long.TYPE).jniTypeName(); private ProcAddressEmitter emitter; - public ProcAddressCMethodBindingEmitter(final CMethodBindingEmitter methodToWrap, final boolean callThroughProcAddress, - final boolean needsLocalTypedef, final String localTypedefCallingConvention, final ProcAddressEmitter emitter) { + public ProcAddressCMethodBindingEmitter(final CMethodBindingEmitter methodToWrap, + final boolean callThroughProcAddress, + final boolean hasProcAddrTypedef, + final String localTypedefCallingConvention, + final ProcAddressEmitter emitter) { super( new MethodBinding(methodToWrap.getBinding()) { @Override - public String getName() { + public String getImplName() { if (callThroughProcAddress) { - return ProcAddressEmitter.WRAP_PREFIX + super.getName(); + return ProcAddressEmitter.WRAP_PREFIX + super.getImplName(); } else { - return super.getName(); + return super.getImplName(); } } }, @@ -76,9 +81,9 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { methodToWrap.getIsJavaMethodStatic(), true, methodToWrap.forIndirectBufferAndArrayImplementation(), - methodToWrap.getMachineDataInfo() + methodToWrap.getMachineDataInfo(), + emitter.getConfiguration() ); - if (methodToWrap.getReturnValueCapacityExpression() != null) { setReturnValueCapacityExpression(methodToWrap.getReturnValueCapacityExpression()); } @@ -91,7 +96,7 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { setCommentEmitter(defaultCommentEmitter); this.callThroughProcAddress = callThroughProcAddress; - this.needsLocalTypedef = needsLocalTypedef; + this.hasProcAddrTypedef = hasProcAddrTypedef; this.localTypedefCallingConvention = localTypedefCallingConvention; this.emitter = emitter; } @@ -116,28 +121,31 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { if (callThroughProcAddress) { // create variable for the function pointer with the right type, and set // it to the value of the passed-in proc address - final FunctionSymbol cSym = getBinding().getCSymbol(); - String funcPointerTypedefName = - emitter.getFunctionPointerTypedefName(cSym); - - if (needsLocalTypedef) { - // We (probably) didn't get a typedef for this function - // pointer type in the header file; the user requested that we - // forcibly generate one. Here we force the emission of one. - final PointerType funcPtrType = new PointerType(null, cSym.getType(), 0); - // Just for safety, emit this name slightly differently than - // the mangling would otherwise produce - funcPointerTypedefName = "_local_" + funcPointerTypedefName; - - writer.print(" typedef "); - writer.print(funcPtrType.toString(funcPointerTypedefName, localTypedefCallingConvention)); - writer.println(";"); + final FunctionSymbol cSym = binding.getCSymbol(); + + // Always emit the local typedef, based on our parsing results. + // In case we do have the public typedef from the original header, + // we use it for the local var and assign our proc-handle to it, + // cast to the local typedef. + // This allows the native C compiler to validate our types! + final String funcPointerTypedefBaseName = emitter.getFunctionPointerTypedefName(cSym); + final String funcPointerTypedefLocalName = "_local_" + funcPointerTypedefBaseName; + final String funcPointerTypedefName; + if (hasProcAddrTypedef) { + funcPointerTypedefName = funcPointerTypedefBaseName; + } else { + funcPointerTypedefName = funcPointerTypedefLocalName; } + final PointerType funcPtrType = new PointerType(null, cSym.getType(), 0); + + writer.print(" typedef "); + writer.print(funcPtrType.toString(funcPointerTypedefLocalName, localTypedefCallingConvention)); + writer.println(";"); writer.print(" "); - writer.print(funcPointerTypedefName); + writer.print(funcPointerTypedefName); // Uses public typedef if available! writer.print(" ptr_"); - writer.print(cSym.getName()); + writer.print(getNativeName()); writer.println(";"); } @@ -150,18 +158,25 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { if (callThroughProcAddress) { // set the function pointer to the value of the passed-in procAddress - final FunctionSymbol cSym = getBinding().getCSymbol(); - String funcPointerTypedefName = emitter.getFunctionPointerTypedefName(cSym); - if (needsLocalTypedef) { - funcPointerTypedefName = "_local_" + funcPointerTypedefName; + // See above notes in emitBodyVariableDeclarations(..)! + final String funcPointerTypedefBaseName = emitter.getFunctionPointerTypedefName(binding.getCSymbol()); + final String funcPointerTypedefLocalName = "_local_" + funcPointerTypedefBaseName; + final String funcPointerTypedefName; + if (hasProcAddrTypedef) { + funcPointerTypedefName = funcPointerTypedefBaseName; + } else { + funcPointerTypedefName = funcPointerTypedefLocalName; } - final String ptrVarName = "ptr_" + cSym.getName(); + final String ptrVarName = "ptr_" + getNativeName(); + if (hasProcAddrTypedef) { + writer.println(" // implicit type validation of "+funcPointerTypedefLocalName+" -> "+funcPointerTypedefName); + } writer.print(" "); writer.print(ptrVarName); writer.print(" = ("); - writer.print(funcPointerTypedefName); + writer.print(funcPointerTypedefLocalName); writer.println(") (intptr_t) procAddress;"); writer.println(" assert(" + ptrVarName + " != NULL);"); @@ -181,7 +196,12 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { final Type cReturnType = binding.getCReturnType(); if (!cReturnType.isVoid()) { - writer.print("_res = "); + // Note we respect const/volatile in the function return type. + // However, we cannot have it 'const' for our local variable. + // See return type in CMethodBindingEmitter.emitBodyVariableDeclarations(..)! + writer.print("_res = ("); + writer.print(cReturnType.getCName(false)); + writer.print(") "); } final MethodBinding mBinding = getBinding(); if (mBinding.hasContainingType()) { @@ -192,7 +212,7 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { // call throught the run-time function pointer writer.print("(* ptr_"); - writer.print(mBinding.getCSymbol().getName()); + writer.print(getNativeName()); writer.print(") "); writer.print("("); emitBodyPassCArguments(writer); diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java index 0c5692b..36d433a 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java @@ -38,7 +38,12 @@ */ package com.jogamp.gluegen.procaddress; +import static java.util.logging.Level.INFO; + import com.jogamp.gluegen.JavaConfiguration; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; + import java.io.*; import java.text.*; import java.util.*; @@ -269,8 +274,15 @@ public class ProcAddressConfiguration extends JavaConfiguration { return tableClassName; } - public boolean skipProcAddressGen(final String name) { - return skipProcAddressGen.contains(name); + public boolean skipProcAddressGen(final FunctionSymbol symbol) { + if ( skipProcAddressGen.contains( symbol.getName() ) || + oneInSet(skipProcAddressGen, symbol.getAliasedNames()) + ) + { + LOG.log(INFO, symbol.getASTLocusTag(), "Skip ProcAddress: {0}", symbol); + return true; + } + return false; } public boolean isForceProcAddressGen4All() { @@ -298,9 +310,25 @@ public class ProcAddressConfiguration extends JavaConfiguration { return procAddressNameConverter.convert(funcName); } - public boolean forceProcAddressGen(final String funcName) { - return forceProcAddressGen4All || forceProcAddressGenSet.contains(funcName); + public boolean forceProcAddressGen(final FunctionSymbol symbol) { + if( forceProcAddressGen4All ) { + if(!forceProcAddressGen4AllOnce) { + forceProcAddressGen4AllOnce = true; + LOG.log(INFO, symbol.getASTLocusTag(), "Force ALL ProcAddress"); + } + return true; + } + + if ( forceProcAddressGenSet.contains( symbol.getName() ) || + oneInSet(forceProcAddressGenSet, symbol.getAliasedNames()) + ) + { + LOG.log(INFO, symbol.getASTLocusTag(), "Force ProcAddress: {0}", symbol); + return true; + } + return false; } + private static boolean forceProcAddressGen4AllOnce = false; public void addForceProcAddressGen(final String funcName) { forceProcAddressGen.add(funcName); @@ -311,11 +339,15 @@ public class ProcAddressConfiguration extends JavaConfiguration { localProcAddressCallingConventionMap.put(funcName, callingConvention); } - public String getLocalProcAddressCallingConvention(final String funcName) { - if (isLocalProcAddressCallingConvention4All()) { + public String getLocalProcAddressCallingConvention(final FunctionSymbol symbol) { + if ( isLocalProcAddressCallingConvention4All() ) { return getLocalProcAddressCallingConvention4All(); } - return localProcAddressCallingConventionMap.get(funcName); + final String res = localProcAddressCallingConventionMap.get(symbol.getName()); + if( null != res ) { + return res; + } + return oneInMap(localProcAddressCallingConventionMap, symbol.getAliasedNames()); } public boolean isLocalProcAddressCallingConvention4All() { diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java index 4145cc4..ec29b08 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java @@ -47,6 +47,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.logging.Level; import com.jogamp.gluegen.CMethodBindingEmitter; import com.jogamp.gluegen.CodeGenUtils; @@ -54,7 +55,6 @@ import com.jogamp.gluegen.FunctionEmitter; import com.jogamp.gluegen.JavaConfiguration; import com.jogamp.gluegen.JavaEmitter; import com.jogamp.gluegen.JavaMethodBindingEmitter; -import com.jogamp.gluegen.MethodBinding; import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; import com.jogamp.gluegen.cgram.types.TypeDictionary; @@ -114,40 +114,45 @@ public class ProcAddressEmitter extends JavaEmitter { } @Override - protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final Set<MethodBinding> methodBindingSet, final FunctionSymbol sym) throws Exception { - return generateMethodBindingEmittersImpl(methodBindingSet, sym); + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final FunctionSymbol sym) throws Exception { + return generateMethodBindingEmittersImpl(sym); } protected boolean needsModifiedEmitters(final FunctionSymbol sym) { - if (!needsProcAddressWrapper(sym) || getConfig().isUnimplemented(getAliasedSymName(sym))) { + if ( !callThroughProcAddress(sym) || getConfig().isUnimplemented(sym) ) { return false; + } else { + return true; } - - return true; } - private List<? extends FunctionEmitter> generateMethodBindingEmittersImpl(final Set<MethodBinding> methodBindingSet, final FunctionSymbol sym) throws Exception { - final List<? extends FunctionEmitter> defaultEmitters = super.generateMethodBindingEmitters(methodBindingSet, sym); + private List<? extends FunctionEmitter> generateMethodBindingEmittersImpl(final FunctionSymbol sym) throws Exception { + final List<? extends FunctionEmitter> defaultEmitters = super.generateMethodBindingEmitters(sym); // if the superclass didn't generate any bindings for the symbol, let's // honor that (for example, the superclass might have caught an Ignore // direction that matched the symbol's name). if (defaultEmitters.isEmpty()) { + LOG.log(Level.INFO, sym.getASTLocusTag(), "genModProcAddrEmitter: SKIP, empty binding set: {0}", sym); return defaultEmitters; } - // Don't do anything special if this symbol doesn't require - // modifications - if (!needsModifiedEmitters(sym)) { + final boolean callThroughProcAddress = callThroughProcAddress(sym); + final boolean isUnimplemented = getConfig().isUnimplemented(sym); + + // Don't do anything special if this symbol doesn't require modifications + if( !callThroughProcAddress || isUnimplemented ) { + LOG.log(Level.INFO, sym.getASTLocusTag(), "genModProcAddrEmitter: SKIP, not needed: callThrough {0}, isUnimplemented {1}: {2}", + callThroughProcAddress, isUnimplemented, sym); return defaultEmitters; } final ArrayList<FunctionEmitter> modifiedEmitters = new ArrayList<FunctionEmitter>(defaultEmitters.size()); - if (needsProcAddressWrapper(sym)) { + if ( callThroughProcAddress ) { if (getProcAddressConfig().emitProcAddressTable()) { // emit an entry in the GL proc address table for this method. - emitProcAddressTableEntryForString(getAliasedSymName(sym)); + emitProcAddressTableEntryForString(sym.getName()); } } for (final FunctionEmitter emitter : defaultEmitters) { @@ -172,7 +177,7 @@ public class ProcAddressEmitter extends JavaEmitter { * whether or not the typedef is actually defined. */ protected String getFunctionPointerTypedefName(final FunctionSymbol sym) { - return getProcAddressConfig().convertToFunctionPointerName(sym.getName()); + return getProcAddressConfig().convertToFunctionPointerName(sym.getOrigName()); } //---------------------------------------------------------------------- @@ -194,20 +199,14 @@ public class ProcAddressEmitter extends JavaEmitter { protected void generateModifiedEmitters(final JavaMethodBindingEmitter baseJavaEmitter, final List<FunctionEmitter> emitters) { // See whether we need a proc address entry for this one - final boolean callThroughProcAddress = needsProcAddressWrapper(baseJavaEmitter.getBinding().getCSymbol()); + final boolean callThroughProcAddress = callThroughProcAddress(baseJavaEmitter.getBinding().getCSymbol()); // If this emitter doesn't have a body (i.e., is a direct native // call with no intervening argument processing), we need to force - // it to emit a body, and produce another one to act as the entry - // point - // FIXME: the negative test against the PRIVATE modifier is a - // nasty hack to prevent the ProcAddressJavaMethodBindingEmitter - // from incorrectly introducing method bodies to the private - // native implementing methods; want this to work at least for - // public and package-private methods + // it to emit a body, and produce another one to act as the entry point final boolean needsJavaWrapper = baseJavaEmitter.signatureOnly() && - !baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.PRIVATE) && - baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.NATIVE) && + baseJavaEmitter.isNativeMethod() && + !baseJavaEmitter.isPrivateNativeMethod() && callThroughProcAddress; @@ -215,7 +214,7 @@ public class ProcAddressEmitter extends JavaEmitter { final ProcAddressJavaMethodBindingEmitter emitter = new ProcAddressJavaMethodBindingEmitter(baseJavaEmitter, callThroughProcAddress, getProcAddressConfig().getProcAddressTableExpr(), - baseJavaEmitter.isForImplementingMethodCall(), + baseJavaEmitter.isPrivateNativeMethod(), this); if( needsJavaWrapper ) { emitter.setEmitBody(true); @@ -232,7 +231,7 @@ public class ProcAddressEmitter extends JavaEmitter { getProcAddressConfig().getProcAddressTableExpr(), true, this); - emitter.setForImplementingMethodCall(true); + emitter.setPrivateNativeMethod(true); fixSecurityModifiers(emitter); emitters.add(emitter); } @@ -243,13 +242,13 @@ public class ProcAddressEmitter extends JavaEmitter { final FunctionSymbol cSymbol = baseCEmitter.getBinding().getCSymbol(); // See whether we need a proc address entry for this one - final boolean callThroughProcAddress = needsProcAddressWrapper(cSymbol); - final boolean forceProcAddress = getProcAddressConfig().forceProcAddressGen(cSymbol.getName()); + final boolean hasProcAddrTypedef = hasFunctionPointerTypedef(cSymbol); + final boolean callThroughProcAddress = hasProcAddrTypedef || callThroughProcAddress(cSymbol); + final String localProcCallingConvention = getProcAddressConfig().getLocalProcAddressCallingConvention(cSymbol); + + LOG.log(Level.INFO, cSymbol.getASTLocusTag(), "genModProcAddrEmitter: callThrough {0}, hasTypedef {1}, localCallConv {2}: {3}", + callThroughProcAddress, hasProcAddrTypedef, localProcCallingConvention, cSymbol); - String forcedCallingConvention = null; - if (forceProcAddress) { - forcedCallingConvention = getProcAddressConfig().getLocalProcAddressCallingConvention(cSymbol.getName()); - } // Note that we don't care much about the naming of the C argument // variables so to keep things simple we ignore the buffer object // property for the binding @@ -258,7 +257,7 @@ public class ProcAddressEmitter extends JavaEmitter { // extra final argument, which is the address (the OpenGL procedure // address) of the function it needs to call final ProcAddressCMethodBindingEmitter res = new ProcAddressCMethodBindingEmitter( - baseCEmitter, callThroughProcAddress, forceProcAddress, forcedCallingConvention, this); + baseCEmitter, callThroughProcAddress, hasProcAddrTypedef, localProcCallingConvention, this); final MessageFormat exp = baseCEmitter.getReturnValueCapacityExpression(); if (exp != null) { @@ -267,34 +266,30 @@ public class ProcAddressEmitter extends JavaEmitter { emitters.add(res); } - private String getAliasedSymName(final FunctionSymbol sym) { - String symName = getConfig().getJavaSymbolRename(sym.getName()); - if (null == symName) { - symName = sym.getName(); + protected boolean callThroughProcAddress(final FunctionSymbol sym) { + final ProcAddressConfiguration cfg = getProcAddressConfig(); + boolean res = false; + int mode = 0; + if (cfg.forceProcAddressGen(sym)) { + res = true; + mode = 1; + } else { + if (cfg.skipProcAddressGen(sym)) { + res = false; + mode = 2; + } else { + res = hasFunctionPointerTypedef(sym); + mode = 3; + } } - return symName; + LOG.log(Level.INFO, sym.getASTLocusTag(), "callThroughProcAddress: {0} [m {1}]: {2}", res, mode, sym); + return res; } - - protected boolean needsProcAddressWrapper(final FunctionSymbol sym) { - final String symName = getAliasedSymName(sym); - - final ProcAddressConfiguration config = getProcAddressConfig(); - - // We should only generate code to call through a function pointer - // if the symbol has an associated function pointer typedef. + protected boolean hasFunctionPointerTypedef(final FunctionSymbol sym) { final String funcPointerTypedefName = getFunctionPointerTypedefName(sym); - boolean shouldWrap = typedefDictionary.containsKey(funcPointerTypedefName); - //System.err.println(funcPointerTypedefName + " defined: " + shouldWrap); - - if (config.skipProcAddressGen(symName)) { - shouldWrap = false; - } - - if (config.forceProcAddressGen(symName)) { - shouldWrap = true; - } - - return shouldWrap; + final boolean res = typedefDictionary.containsKey(funcPointerTypedefName); + LOG.log(Level.INFO, sym.getASTLocusTag(), "hasFunctionPointerTypedef: {0}: {1}", res, sym); + return res; } protected void beginProcAddressTable() throws Exception { diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java index a70c18d..5298a8d 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java @@ -41,6 +41,7 @@ package com.jogamp.gluegen.procaddress; import com.jogamp.gluegen.MethodBinding; import com.jogamp.gluegen.FunctionEmitter; import com.jogamp.gluegen.JavaMethodBindingEmitter; + import java.io.*; /** A specialization of JavaMethodBindingEmitter with knowledge of how @@ -76,12 +77,12 @@ public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitte public ProcAddressJavaMethodBindingEmitter(final ProcAddressJavaMethodBindingEmitter methodToWrap) { this(methodToWrap, methodToWrap.callThroughProcAddress, methodToWrap.getProcAddressTableExpr, - methodToWrap.changeNameAndArguments, methodToWrap.emitter); + methodToWrap.changeNameAndArguments, methodToWrap.emitter); } @Override - public String getName() { - final String res = super.getName(); + public String getImplName() { + final String res = super.getImplName(); if (changeNameAndArguments) { return ProcAddressEmitter.WRAP_PREFIX + res; } @@ -106,8 +107,8 @@ public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitte } @Override - protected String getImplMethodName() { - final String name = super.getImplMethodName(); + protected String getNativeImplMethodName() { + final String name = super.getNativeImplMethodName(); if (callThroughProcAddress) { return ProcAddressEmitter.WRAP_PREFIX + name; } @@ -119,7 +120,7 @@ public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitte super.emitPreCallSetup(binding, writer); if (callThroughProcAddress) { - final String procAddressVariable = ProcAddressEmitter.PROCADDRESS_VAR_PREFIX + binding.getName(); + final String procAddressVariable = ProcAddressEmitter.PROCADDRESS_VAR_PREFIX + binding.getNativeName(); writer.println(" final long __addr_ = " + getProcAddressTableExpr + "." + procAddressVariable + ";"); writer.println(" if (__addr_ == 0) {"); writer.format(" throw new %s(String.format(\"Method \\\"%%s\\\" not available\", \"%s\"));%n", diff --git a/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java index c4dedb7..7ccfd1b 100644 --- a/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java +++ b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java @@ -131,7 +131,11 @@ public class CStructAnnotationProcessor extends AbstractProcessor { if( f.exists() ) { return f; } - } catch (final IOException e) { if(DEBUG) { System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage()); /* e.printStackTrace(); */ } } + } catch (final IOException e) { + if(DEBUG) { + System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage()); /* e.printStackTrace(); */ + } + } return null; } @@ -263,6 +267,9 @@ public class CStructAnnotationProcessor extends AbstractProcessor { } catch (final FileNotFoundException ex) { throw new RuntimeException("input file not found", ex); } + if( DEBUG ) { + GlueGen.setDebug(true); + } new GlueGen().run(reader, filename, AnnotationProcessorJavaStructEmitter.class, includePaths, cfgFiles, outputPath1, false /* copyCPPOutput2Stderr */); diff --git a/src/java/jogamp/android/launcher/MainLauncher.java b/src/java/jogamp/android/launcher/MainLauncher.java index 0dc6b4a..e0eff7d 100644 --- a/src/java/jogamp/android/launcher/MainLauncher.java +++ b/src/java/jogamp/android/launcher/MainLauncher.java @@ -33,6 +33,8 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import com.jogamp.common.util.InterruptSource; + import android.app.Activity; import android.net.Uri; import android.os.Bundle; @@ -117,17 +119,17 @@ public class MainLauncher extends Activity { public void onResume() { Log.d(TAG, "onResume - S - "+Thread.currentThread().getName()); super.onResume(); - final Thread mainThread = new Thread("Main") { + final Thread mainThread = new InterruptSource.Thread(null, null, "Main") { public void run() { try { - Log.d(TAG, "onResume - main.0 - "+Thread.currentThread().getName()); + Log.d(TAG, "onResume - main.0 - "+java.lang.Thread.currentThread().getName()); mainClazzMain.invoke(null, new Object[] { mainClassArgs } ); } catch (final InvocationTargetException ite) { ite.getTargetException().printStackTrace(); } catch (final Throwable t) { t.printStackTrace(); } - Log.d(TAG, "onResume - main.X -> finish() - "+Thread.currentThread().getName()); + Log.d(TAG, "onResume - main.X -> finish() - "+java.lang.Thread.currentThread().getName()); finish(); } }; mainThread.start(); diff --git a/src/java/jogamp/common/os/PlatformPropsImpl.java b/src/java/jogamp/common/os/PlatformPropsImpl.java index 2d8bdec..fdd6b7f 100644 --- a/src/java/jogamp/common/os/PlatformPropsImpl.java +++ b/src/java/jogamp/common/os/PlatformPropsImpl.java @@ -63,6 +63,10 @@ public abstract class PlatformPropsImpl { public static final VersionNumber Version16; /** Version 1.7. As a JVM version, it enables certain JVM 1.7 features. */ public static final VersionNumber Version17; + /** Version 1.8. As a JVM version, it enables certain JVM 1.8 features. */ + public static final VersionNumber Version18; + /** Version 1.9. As a JVM version, it enables certain JVM 1.9 features. */ + public static final VersionNumber Version19; public static final String OS; public static final String OS_lower; @@ -101,6 +105,8 @@ public abstract class PlatformPropsImpl { static { Version16 = new VersionNumber(1, 6, 0); Version17 = new VersionNumber(1, 7, 0); + Version18 = new VersionNumber(1, 8, 0); + Version19 = new VersionNumber(1, 9, 0); // We don't seem to need an AccessController.doPrivileged() block // here as these system properties are visible even to unsigned Applets. diff --git a/src/java/jogamp/common/util/Int32ArrayBitfield.java b/src/java/jogamp/common/util/Int32ArrayBitfield.java new file mode 100644 index 0000000..5bc95eb --- /dev/null +++ b/src/java/jogamp/common/util/Int32ArrayBitfield.java @@ -0,0 +1,207 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.util; + +import com.jogamp.common.util.Bitfield; + +/** + * Simple bitfield interface for efficient storage access in O(1). + * <p> + * Implementation uses a 32bit integer array for storage. + * </p> + */ +public class Int32ArrayBitfield implements Bitfield { + private static final int UNIT_SHIFT = 5; + private final int[] storage; + private final int bitSize; + + /** + * @param storageBitSize + */ + public Int32ArrayBitfield(final int storageBitSize) { + final int units = Math.max(1, ( storageBitSize + 31 ) >>> UNIT_SHIFT); + this.storage = new int[units]; // initialized w/ default '0' + this.bitSize = units << UNIT_SHIFT; + } + + @Override + public int size() { + return bitSize; + } + + @Override + public final void clearField(final boolean bit) { + final int v; + if( bit ) { + v = Bitfield.UNSIGNED_INT_MAX_VALUE; + } else { + v = 0; + } + for(int i=storage.length-1; i>=0; i--) { + storage[i] = v; + } + } + + private static final void check(final int size, final int bitnum) throws IndexOutOfBoundsException { + if( 0 > bitnum || bitnum >= size ) { + throw new IndexOutOfBoundsException("Bitnum should be within [0.."+(size-1)+"], but is "+bitnum); + } + } + + @Override + public final int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(bitSize-length+1, lowBitnum); + final int u = lowBitnum >>> UNIT_SHIFT; + final int left = 32 - ( lowBitnum - ( u << UNIT_SHIFT ) ); // remaining bits of first chunk storage + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + return m & storage[u]; + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + final int d = m & ( storage[u] >>> lowBitnum ); + final int l2 = length - l; // length of last chunk < 32 + if( l2 > 0 ) { + final int m2 = ( 1 << l2 ) - 1; // mask of last chunk + return d | ( ( m2 & storage[u+1] ) << l ); + } else { + return d; + } + } + } + @Override + public final void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(bitSize-length+1, lowBitnum); + final int u = lowBitnum >>> UNIT_SHIFT; + final int left = 32 - ( lowBitnum - ( u << UNIT_SHIFT ) ); // remaining bits of first chunk storage + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + storage[u] = ( ( ~m ) & storage[u] ) // keep non-written storage bits + | ( m & data ); // overwrite storage w/ used data bits + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + storage[u] = ( ( ~( m << lowBitnum ) ) & storage[u] ) // keep non-written storage bits + | ( ( m & data ) << lowBitnum ); // overwrite storage w/ used data bits + final int l2 = length - l; // length of last chunk < 32 + if( l2 > 0 ) { + final int m2 = ( 1 << l2 ) - 1; // mask of last chunk + storage[u+1] = ( ( ~m2 ) & storage[u+1] ) // keep non-written storage bits + | ( m2 & ( data >>> l ) ); // overwrite storage w/ used data bits + } + } + } + @Override + public final int copy32(final int srcBitnum, final int dstBitnum, final int length) throws IndexOutOfBoundsException { + final int data = get32(srcBitnum, length); + put32(dstBitnum, length, data); + return data; + } + + @Override + public final boolean get(final int bitnum) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + return 0 != ( storage[u] & ( 1 << b ) ) ; + } + + @Override + public final boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + final boolean prev = 0 != ( storage[u] & m ) ; + if( prev != bit ) { + if( bit ) { + storage[u] |= m; + } else { + storage[u] &= ~m; + } + } + return prev; + } + @Override + public final void set(final int bitnum) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + storage[u] |= m; + } + @Override + public final void clear(final int bitnum) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + storage[u] &= ~m; + } + @Override + public final boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException { + check(bitSize, srcBitnum); + check(bitSize, dstBitnum); + final boolean bit; + // get + { + final int u = srcBitnum >>> UNIT_SHIFT; + final int b = srcBitnum - ( u << UNIT_SHIFT ); + bit = 0 != ( storage[u] & ( 1 << b ) ) ; + } + // put + final int u = dstBitnum >>> UNIT_SHIFT; + final int b = dstBitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + if( bit ) { + storage[u] |= m; + } else { + storage[u] &= ~m; + } + return bit; + } + + @Override + public int bitCount() { + int c = 0; + for(int i = storage.length-1; i>=0; i--) { + c += Bitfield.Util.bitCount(storage[i]); + } + return c; + } +} diff --git a/src/java/jogamp/common/util/Int32Bitfield.java b/src/java/jogamp/common/util/Int32Bitfield.java new file mode 100644 index 0000000..7b55a59 --- /dev/null +++ b/src/java/jogamp/common/util/Int32Bitfield.java @@ -0,0 +1,163 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.util; + +import com.jogamp.common.util.Bitfield; + +/** + * Simple bitfield interface for efficient storage access in O(1). + * <p> + * Implementation uses one 32bit integer field for storage. + * </p> + */ +public class Int32Bitfield implements Bitfield { + /** Unit size in bits, here 32 bits for one int unit. */ + private static final int UNIT_SIZE = 32; + private int storage; + + public Int32Bitfield() { + this.storage = 0; + } + + @Override + public int size() { + return UNIT_SIZE; + } + + @Override + public final void clearField(final boolean bit) { + if( bit ) { + storage = Bitfield.UNSIGNED_INT_MAX_VALUE; + } else { + storage = 0; + } + } + + private static final void check(final int size, final int bitnum) throws IndexOutOfBoundsException { + if( 0 > bitnum || bitnum >= size ) { + throw new IndexOutOfBoundsException("Bitnum should be within [0.."+(size-1)+"], but is "+bitnum); + } + } + + @Override + public final int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(UNIT_SIZE-length+1, lowBitnum); + final int left = 32 - lowBitnum; // remaining bits of first chunk + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + return m & storage; + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + return m & ( storage >>> lowBitnum ); + } + } + @Override + public final void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(UNIT_SIZE-length+1, lowBitnum); + final int left = 32 - lowBitnum; // remaining bits of first chunk storage + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + storage = ( ( ~m ) & storage ) // keep non-written storage bits + | ( m & data ); // overwrite storage w/ used data bits + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + storage = ( ( ~( m << lowBitnum ) ) & storage ) // keep non-written storage bits + | ( ( m & data ) << lowBitnum ); // overwrite storage w/ used data bits + } + } + @Override + public final int copy32(final int srcBitnum, final int dstBitnum, final int length) throws IndexOutOfBoundsException { + final int data = get32(srcBitnum, length); + put32(dstBitnum, length, data); + return data; + } + + @Override + public final boolean get(final int bitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + return 0 != ( storage & ( 1 << bitnum ) ) ; + } + @Override + public final boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + final int m = 1 << bitnum; + final boolean prev = 0 != ( storage & m ) ; + if( prev != bit ) { + if( bit ) { + storage |= m; + } else { + storage &= ~m; + } + } + return prev; + } + @Override + public final void set(final int bitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + final int m = 1 << bitnum; + storage |= m; + } + @Override + public final void clear (final int bitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + final int m = 1 << bitnum; + storage &= ~m; + } + @Override + public final boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, srcBitnum); + check(UNIT_SIZE, dstBitnum); + // get + final boolean bit = 0 != ( storage & ( 1 << srcBitnum ) ) ; + // put + final int m = 1 << dstBitnum; + if( bit ) { + storage |= m; + } else { + storage &= ~m; + } + return bit; + } + + @Override + public int bitCount() { + return Bitfield.Util.bitCount(storage); + } +} diff --git a/src/java/jogamp/common/util/SyncedBitfield.java b/src/java/jogamp/common/util/SyncedBitfield.java new file mode 100644 index 0000000..49c27b0 --- /dev/null +++ b/src/java/jogamp/common/util/SyncedBitfield.java @@ -0,0 +1,96 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.util; + +import com.jogamp.common.util.Bitfield; + +/** + * Simple synchronized {@link Bitfield} by wrapping an existing {@link Bitfield}. + */ +public class SyncedBitfield implements Bitfield { + private final Bitfield impl; + + public SyncedBitfield(final Bitfield impl) { + this.impl = impl; + } + + @Override + public final synchronized int size() { + return impl.size(); + } + + @Override + public final synchronized void clearField(final boolean bit) { + impl.clearField(bit); + } + + @Override + public final synchronized int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException { + return impl.get32(lowBitnum, length); + } + + @Override + public final synchronized void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException { + impl.put32(lowBitnum, length, data); + } + + @Override + public final synchronized int copy32(final int srcLowBitnum, final int dstLowBitnum, final int length) throws IndexOutOfBoundsException { + return impl.copy32(srcLowBitnum, dstLowBitnum, length); + } + + @Override + public final synchronized boolean get(final int bitnum) throws IndexOutOfBoundsException { + return impl.get(bitnum); + } + + @Override + public final synchronized boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException { + return impl.put(bitnum, bit); + } + + @Override + public final synchronized void set(final int bitnum) throws IndexOutOfBoundsException { + impl.set(bitnum); + } + + @Override + public final synchronized void clear(final int bitnum) throws IndexOutOfBoundsException { + impl.clear(bitnum); + } + + @Override + public final synchronized boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException { + return impl.copy(srcBitnum, dstBitnum); + } + + @Override + public final synchronized int bitCount() { + return impl.bitCount(); + } +}
\ No newline at end of file diff --git a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java index c930dff..1286924 100644 --- a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java +++ b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.AbstractOwnableSynchronizer; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.common.util.locks.RecursiveLock; /** @@ -197,7 +198,7 @@ public class RecursiveLockImpl01CompleteFair implements RecursiveLock { } catch (final InterruptedException e) { if( !wCur.signaledByUnlock ) { sync.queue.remove(wCur); // O(n) - throw e; // propagate interruption not send by unlock + throw SourcedInterruptedException.wrap(e); // propagate interruption not send by unlock } else if( cur != sync.getOwner() ) { // Issued by unlock, but still locked by other thread // @@ -215,6 +216,7 @@ public class RecursiveLockImpl01CompleteFair implements RecursiveLock { } // else: Issued by unlock, owning lock .. expected! } } while ( cur != sync.getOwner() && 0 < timeout ) ; + Thread.interrupted(); // clear slipped interrupt if( 0 >= timeout && cur != sync.getOwner() ) { // timed out diff --git a/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java index 77f73d8..fc5f739 100644 --- a/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java +++ b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java @@ -42,6 +42,7 @@ public class RecursiveThreadGroupLockImpl01Unfairish threadNum = 0; threads = null; holdCountAdditionOwner = 0; + waitingOrigOwner = null; } @Override public final void incrHoldCount(final Thread t) { @@ -64,6 +65,12 @@ public class RecursiveThreadGroupLockImpl01Unfairish public final boolean isOriginalOwner(final Thread t) { return super.isOwner(t); } + public final void setWaitingOrigOwner(final Thread origOwner) { + waitingOrigOwner = origOwner; + } + public final Thread getWaitingOrigOwner() { + return waitingOrigOwner; + } @Override public final boolean isOwner(final Thread t) { if(getExclusiveOwnerThread()==t) { @@ -133,6 +140,7 @@ public class RecursiveThreadGroupLockImpl01Unfairish private int holdCountAdditionOwner; private Thread[] threads; private int threadNum; + private Thread waitingOrigOwner; } public RecursiveThreadGroupLockImpl01Unfairish() { @@ -157,10 +165,10 @@ public class RecursiveThreadGroupLockImpl01Unfairish final Thread cur = Thread.currentThread(); final ThreadGroupSync tgSync = (ThreadGroupSync)sync; if(!tgSync.isOriginalOwner(cur)) { - throw new IllegalArgumentException("Current thread is not the original owner: orig-owner: "+tgSync.getOwner()+", current "+cur); + throw new IllegalArgumentException("Current thread is not the original owner: orig-owner: "+tgSync.getOwner()+", current "+cur+": "+toString()); } if(tgSync.isOriginalOwner(t)) { - throw new IllegalArgumentException("Passed thread is original owner: "+t); + throw new IllegalArgumentException("Passed thread is original owner: "+t+", "+toString()); } tgSync.addOwner(t); } @@ -179,19 +187,25 @@ public class RecursiveThreadGroupLockImpl01Unfairish // original locking owner thread if( tgSync.getHoldCount() - tgSync.getAdditionalOwnerHoldCount() == 1 ) { // release orig. lock - while ( tgSync.getAdditionalOwnerHoldCount() > 0 ) { - try { - sync.wait(); - } catch (final InterruptedException e) { - // regular wake up! + tgSync.setWaitingOrigOwner(cur); + try { + while ( tgSync.getAdditionalOwnerHoldCount() > 0 ) { + try { + sync.wait(); + } catch (final InterruptedException e) { + // regular wake up! + } } + } finally { + tgSync.setWaitingOrigOwner(null); + Thread.interrupted(); // clear slipped interrupt } tgSync.removeAllOwners(); } } else if( tgSync.getAdditionalOwnerHoldCount() == 1 ) { - // last additional owner thread wakes up original owner - final Thread originalOwner = tgSync.getOwner(); - if(originalOwner.getState() == Thread.State.WAITING) { + // last additional owner thread wakes up original owner if waiting in unlock(..) + final Thread originalOwner = tgSync.getWaitingOrigOwner(); + if( null != originalOwner ) { originalOwner.interrupt(); } } diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java index 44a5d28..9fe7966 100644 --- a/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java +++ b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java @@ -32,6 +32,8 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileLock; + +import com.jogamp.common.util.InterruptSource; import com.jogamp.common.util.locks.SingletonInstance; public class SingletonInstanceFileLock extends SingletonInstance { @@ -76,7 +78,7 @@ public class SingletonInstanceFileLock extends SingletonInstance { private void setupFileCleanup() { file.deleteOnExit(); - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { @Override public void run() { if(isLocked()) { diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java index b1b42c3..6219b5c 100644 --- a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java +++ b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java @@ -33,10 +33,16 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.common.util.locks.SingletonInstance; public class SingletonInstanceServerSocket extends SingletonInstance { + private static int serverInstanceCount = 0; private final Server singletonServer; private final String fullName; @@ -71,7 +77,7 @@ public class SingletonInstanceServerSocket extends SingletonInstance { fullName = ilh.toString()+":"+portNumber; singletonServer = new Server(ilh, portNumber); - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { @Override public void run() { singletonServer.kill(); @@ -139,38 +145,58 @@ public class SingletonInstanceServerSocket extends SingletonInstance { public final boolean start() { if(alive) return true; + final String sname; + synchronized (Server.class) { + serverInstanceCount++; + sname = "SingletonServerSocket"+serverInstanceCount+"-"+fullName; + } synchronized (syncOnStartStop) { - serverThread = new Thread(this); + shallQuit = false; + serverThread = new InterruptSource.Thread(null, this, sname); serverThread.setDaemon(true); // be a daemon, don't keep the JVM running serverThread.start(); try { - syncOnStartStop.wait(); + while( !alive && !shallQuit ) { + syncOnStartStop.wait(); + } } catch (final InterruptedException ie) { - ie.printStackTrace(); + final InterruptedException ie2 = SourcedInterruptedException.wrap(ie); + shutdown(false); + throw new InterruptedRuntimeException(ie2); } } final boolean ok = isBound(); if(!ok) { - shutdown(); + shutdown(true); } return ok; } public final boolean shutdown() { + return shutdown(true); + } + private final boolean shutdown(final boolean wait) { if(!alive) return true; - synchronized (syncOnStartStop) { - shallQuit = true; - connect(); - try { - syncOnStartStop.wait(); - } catch (final InterruptedException ie) { - ie.printStackTrace(); + try { + synchronized (syncOnStartStop) { + shallQuit = true; + connect(); + if( wait ) { + try { + while( alive ) { + syncOnStartStop.wait(); + } + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + } + } + } finally { + if(alive) { + System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?"); + kill(); } - } - if(alive) { - System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?"); - kill(); } return true; } @@ -185,7 +211,8 @@ public class SingletonInstanceServerSocket extends SingletonInstance { System.err.println(infoPrefix()+" XXX "+getName()+" - Kill @ JVM Shutdown"); } alive = false; - if(null != serverThread) { + shallQuit = false; + if(null != serverThread && serverThread.isAlive() ) { try { serverThread.stop(); } catch(final Throwable t) { } @@ -214,47 +241,49 @@ public class SingletonInstanceServerSocket extends SingletonInstance { @Override public void run() { - { - final Thread currentThread = Thread.currentThread(); - currentThread.setName(currentThread.getName() + " - SISock: "+getName()); - if(DEBUG) { - System.err.println(currentThread.getName()+" - started"); - } + if(DEBUG) { + System.err.println(infoPrefix()+" III - Start"); } - alive = false; - synchronized (syncOnStartStop) { - try { - serverSocket = new ServerSocket(portNumber, 1, localInetAddress); - serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed - alive = true; - } catch (final IOException e) { - System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage()); - shallQuit = true; - } finally { - syncOnStartStop.notifyAll(); + try { + synchronized (syncOnStartStop) { + try { + serverSocket = new ServerSocket(portNumber, 1, localInetAddress); + serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed + alive = true; + } catch (final IOException e) { + System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage()); + shallQuit = true; + } finally { + syncOnStartStop.notifyAll(); + } } - } - while (!shallQuit) { - try { - final Socket clientSocket = serverSocket.accept(); - clientSocket.close(); - } catch (final IOException ioe) { - System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage()); + while (!shallQuit) { + try { + final Socket clientSocket = serverSocket.accept(); + clientSocket.close(); + } catch (final IOException ioe) { + System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage()); + } } - } - - synchronized (syncOnStartStop) { - try { + } catch(final ThreadDeath td) { + if( DEBUG ) { + ExceptionUtils.dumpThrowable("", td); + } + } finally { + synchronized (syncOnStartStop) { + if(DEBUG) { + System.err.println(infoPrefix()+" III - Stopping: alive "+alive+", shallQuit "+shallQuit+", hasSocket "+(null!=serverSocket)); + } if(null != serverSocket) { - serverSocket.close(); + try { + serverSocket.close(); + } catch (final IOException e) { + System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage()); + } } - } catch (final IOException e) { - System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage()); - } finally { serverSocket = null; alive = false; - shallQuit = false; syncOnStartStop.notifyAll(); } } diff --git a/src/junit/com/jogamp/common/net/AssetURLConnectionUnregisteredTest.java b/src/junit/com/jogamp/common/net/AssetURLConnectionUnregisteredTest.java index 5167abb..1bb88c5 100644 --- a/src/junit/com/jogamp/common/net/AssetURLConnectionUnregisteredTest.java +++ b/src/junit/com/jogamp/common/net/AssetURLConnectionUnregisteredTest.java @@ -38,7 +38,7 @@ public class AssetURLConnectionUnregisteredTest extends AssetURLConnectionBase { @Test public void assetUnregisteredIOUtilGetResourceRel0_RT() throws IOException, URISyntaxException { - final URLConnection urlConn0 = IOUtil.getResource(this.getClass(), test_asset_test2_rel.get()); + final URLConnection urlConn0 = IOUtil.getResource(test_asset_test2_rel.get(), this.getClass().getClassLoader(), this.getClass()); testAssetConnection(urlConn0, test_asset_test2_entry); final Uri uri1 = Uri.valueOf(urlConn0.getURL()).getRelativeOf(test_asset_test3_rel); diff --git a/src/junit/com/jogamp/common/net/TestUri01.java b/src/junit/com/jogamp/common/net/TestUri01.java index 1173610..4205de1 100644 --- a/src/junit/com/jogamp/common/net/TestUri01.java +++ b/src/junit/com/jogamp/common/net/TestUri01.java @@ -248,6 +248,20 @@ public class TestUri01 extends SingletonJunitCase { @Test public void test08NormalizedHierarchy() throws IOException, URISyntaxException { { + final Uri input = Uri.cast("./dummy/nop/../a.txt"); + final Uri expected = Uri.cast("dummy/a.txt"); + URIDumpUtil.showUri(input); + final Uri normal = input.getNormalized(); + Assert.assertEquals(expected, normal); + } + { + final Uri input = Uri.cast("../dummy/nop/../a.txt"); + final Uri expected = Uri.cast("../dummy/a.txt"); + URIDumpUtil.showUri(input); + final Uri normal = input.getNormalized(); + Assert.assertEquals(expected, normal); + } + { final Uri input = Uri.cast("http://localhost/dummy/../"); final Uri expected = Uri.cast("http://localhost/"); URIDumpUtil.showUri(input); @@ -255,7 +269,21 @@ public class TestUri01 extends SingletonJunitCase { Assert.assertEquals(expected, normal); } { - final Uri input = Uri.cast("http://localhost/test/dummy/../text.txt"); + final Uri input = Uri.cast("http://localhost/dummy/./../"); + final Uri expected = Uri.cast("http://localhost/"); + URIDumpUtil.showUri(input); + final Uri normal = input.getNormalized(); + Assert.assertEquals(expected, normal); + } + { + final Uri input = Uri.cast("http://localhost/dummy/../aa/././../"); + final Uri expected = Uri.cast("http://localhost/"); + URIDumpUtil.showUri(input); + final Uri normal = input.getNormalized(); + Assert.assertEquals(expected, normal); + } + { + final Uri input = Uri.cast("http://localhost/test/dummy/./../text.txt"); final Uri expected = Uri.cast("http://localhost/test/text.txt"); URIDumpUtil.showUri(input); final Uri normal = input.getNormalized(); @@ -280,7 +308,7 @@ public class TestUri01 extends SingletonJunitCase { Assert.assertEquals(expected, normal); } { - final Uri input = Uri.cast("jar:http://localhost/test/dummy/../abc.jar!/"); + final Uri input = Uri.cast("jar:http://localhost/test/./dummy/../abc.jar!/"); final Uri expected = Uri.cast("jar:http://localhost/test/abc.jar!/"); URIDumpUtil.showUri(input); final Uri normal = input.getNormalized(); diff --git a/src/junit/com/jogamp/common/nio/BuffersTest.java b/src/junit/com/jogamp/common/nio/BuffersTest.java index c6a89f1..c267100 100644 --- a/src/junit/com/jogamp/common/nio/BuffersTest.java +++ b/src/junit/com/jogamp/common/nio/BuffersTest.java @@ -31,7 +31,15 @@ */ package com.jogamp.common.nio; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.nio.ShortBuffer; + import org.junit.Test; import com.jogamp.junit.util.SingletonJunitCase; @@ -48,7 +56,66 @@ import org.junit.runners.MethodSorters; public class BuffersTest extends SingletonJunitCase { @Test - public void slice() { + public void test01PositionLimitCapacityAfterArrayAllocation() { + final byte[] byteData = { 1, 2, 3, 4, 5, 6, 7, 8 }; + final ByteBuffer byteBuffer = Buffers.newDirectByteBuffer(byteData); + assertEquals(0, byteBuffer.position()); + assertEquals(8, byteBuffer.limit()); + assertEquals(8, byteBuffer.capacity()); + assertEquals(8, byteBuffer.remaining()); + assertEquals(5, byteBuffer.get(4)); + + final double[] doubleData = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 }; + final DoubleBuffer doubleBuffer = Buffers.newDirectDoubleBuffer(doubleData); + assertEquals(0, doubleBuffer.position()); + assertEquals(8, doubleBuffer.limit()); + assertEquals(8, doubleBuffer.capacity()); + assertEquals(8, doubleBuffer.remaining()); + assertEquals(5.0, doubleBuffer.get(4), 0.1); + + final float[] floatData = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f }; + final FloatBuffer floatBuffer = Buffers.newDirectFloatBuffer(floatData); + assertEquals(0, floatBuffer.position()); + assertEquals(8, floatBuffer.limit()); + assertEquals(8, floatBuffer.capacity()); + assertEquals(8, floatBuffer.remaining()); + assertEquals(5.0f, floatBuffer.get(4), 0.1f); + + final int[] intData = { 1, 2, 3, 4, 5, 6, 7, 8 }; + final IntBuffer intBuffer = Buffers.newDirectIntBuffer(intData); + assertEquals(0, intBuffer.position()); + assertEquals(8, intBuffer.limit()); + assertEquals(8, intBuffer.capacity()); + assertEquals(8, intBuffer.remaining()); + assertEquals(5, intBuffer.get(4)); + + final long[] longData = { 1, 2, 3, 4, 5, 6, 7, 8 }; + final LongBuffer longBuffer = Buffers.newDirectLongBuffer(longData); + assertEquals(0, longBuffer.position()); + assertEquals(8, longBuffer.limit()); + assertEquals(8, longBuffer.capacity()); + assertEquals(8, longBuffer.remaining()); + assertEquals(5, longBuffer.get(4)); + + final short[] shortData = { 1, 2, 3, 4, 5, 6, 7, 8 }; + final ShortBuffer shortBuffer = Buffers.newDirectShortBuffer(shortData); + assertEquals(0, shortBuffer.position()); + assertEquals(8, shortBuffer.limit()); + assertEquals(8, shortBuffer.capacity()); + assertEquals(8, shortBuffer.remaining()); + assertEquals(5, shortBuffer.get(4)); + + final char[] charData = { 1, 2, 3, 4, 5, 6, 7, 8 }; + final CharBuffer charBuffer = Buffers.newDirectCharBuffer(charData); + assertEquals(0, charBuffer.position()); + assertEquals(8, charBuffer.limit()); + assertEquals(8, charBuffer.capacity()); + assertEquals(8, charBuffer.remaining()); + assertEquals(5, charBuffer.get(4)); + } + + @Test + public void test10Slice() { final IntBuffer buffer = Buffers.newDirectIntBuffer(6); buffer.put(new int[]{1,2,3,4,5,6}).rewind(); @@ -87,8 +154,10 @@ public class BuffersTest extends SingletonJunitCase { assertEquals(42, buffer.get(2)); assertEquals(42, onetwothree.get(2)); - - } + public static void main(final String args[]) throws IOException { + final String tstname = BuffersTest.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } } diff --git a/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java b/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java index bef813b..9524e91 100644 --- a/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java +++ b/src/junit/com/jogamp/common/nio/TestByteBufferCopyStream.java @@ -35,6 +35,7 @@ import java.nio.channels.FileChannel; import org.junit.Assert; import org.junit.Test; +import com.jogamp.common.os.Platform; import com.jogamp.junit.util.SingletonJunitCase; import org.junit.FixMethodOrder; @@ -171,27 +172,42 @@ public class TestByteBufferCopyStream extends SingletonJunitCase { @Test public void test00() throws IOException { + final long size; + if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { + size = quaterGiB; + } else { + size = twoPlusGiB; + } final int srcSliceShift = MappedByteBufferInputStream.DEFAULT_SLICE_SHIFT; final int dstSliceShift = MappedByteBufferInputStream.DEFAULT_SLICE_SHIFT; - final long size = twoPlusGiB; testImpl(getSimpleTestName(".")+"_In.bin", size, MappedByteBufferInputStream.CacheMode.FLUSH_PRE_HARD, srcSliceShift, - getSimpleTestName(".")+"_Out.bin", MappedByteBufferInputStream.CacheMode.FLUSH_PRE_HARD, dstSliceShift ); + getSimpleTestName(".")+"_Out.bin", MappedByteBufferInputStream.CacheMode.FLUSH_PRE_HARD, dstSliceShift ); } @Test public void test01() throws IOException { + final long size; + if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { + size = quaterGiB; + } else { + size = twoPlusGiB; + } final int srcSliceShift = MappedByteBufferInputStream.DEFAULT_SLICE_SHIFT; final int dstSliceShift = MappedByteBufferInputStream.DEFAULT_SLICE_SHIFT; - final long size = twoPlusGiB; testImpl(getSimpleTestName(".")+"_In.bin", size, MappedByteBufferInputStream.CacheMode.FLUSH_PRE_SOFT, srcSliceShift, getSimpleTestName(".")+"_Out.bin", MappedByteBufferInputStream.CacheMode.FLUSH_PRE_SOFT, dstSliceShift ); } @Test public void test02() throws IOException { + final long size; + if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { + size = quaterPlusGiB; + } else { + size = halfPlusGiB; + } final int srcSliceShift = 27; // 125M bytes per slice final int dstSliceShift = 27; // 125M bytes per slice - final long size = halfPlusGiB; testImpl(getSimpleTestName(".")+"_In.bin", size, MappedByteBufferInputStream.CacheMode.FLUSH_PRE_SOFT, srcSliceShift, getSimpleTestName(".")+"_Out.bin", MappedByteBufferInputStream.CacheMode.FLUSH_PRE_SOFT, dstSliceShift ); } @@ -214,7 +230,14 @@ public class TestByteBufferCopyStream extends SingletonJunitCase { getSimpleTestName(".")+"_Out.bin", MappedByteBufferInputStream.CacheMode.FLUSH_PRE_SOFT, dstSliceShift ); } + static boolean manualTest = false; + public static void main(final String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-manual")) { + manualTest = true; + } + } final String tstname = TestByteBufferCopyStream.class.getName(); org.junit.runner.JUnitCore.main(tstname); } diff --git a/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java b/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java index 53ebac9..90a954b 100644 --- a/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java +++ b/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java @@ -41,6 +41,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import com.jogamp.common.os.Platform; import com.jogamp.common.util.IOUtil; import com.jogamp.junit.util.SingletonJunitCase; @@ -144,20 +145,32 @@ public class TestByteBufferInputStream extends SingletonJunitCase { @Test public void test11MMap1GiBFlushNone() throws IOException { - testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileOneGiB, oneGiB); - // testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileTwoPlusGiB, twoPlusGiB); + if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { + testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileOneMiB, oneMiB); + } else { + testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileOneGiB, oneGiB); + // testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileTwoPlusGiB, twoPlusGiB); + } } @Test public void test12MMap1GiBFlushSoft() throws IOException { - testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileOneGiB, oneGiB); - // testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileTwoPlusGiB, twoPlusGiB); + if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { + testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileOneMiB, oneMiB); + } else { + testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileOneGiB, oneGiB); + // testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileTwoPlusGiB, twoPlusGiB); + } } @Test public void test13MMap2GiBFlushHard() throws IOException { - // testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileOneGiB, oneGiB); - testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileTwoPlusGiB, twoPlusGiB); + if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { + testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileOneMiB, oneMiB); + } else { + // testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileOneGiB, oneGiB); + testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileTwoPlusGiB, twoPlusGiB); + } } void testCopyIntSize1Impl(final String testFileName, final long expSize) throws IOException { @@ -336,7 +349,14 @@ public class TestByteBufferInputStream extends SingletonJunitCase { System.err.println(" MiB"); */ } + static boolean manualTest = false; + public static void main(final String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-manual")) { + manualTest = true; + } + } final String tstname = TestByteBufferInputStream.class.getName(); org.junit.runner.JUnitCore.main(tstname); } diff --git a/src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java b/src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java index 8686b46..b0d7baf 100644 --- a/src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java +++ b/src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java @@ -284,7 +284,14 @@ public class TestByteBufferOutputStream extends SingletonJunitCase { testImpl(getSimpleTestName(".")+".bin", payLoad, 3021L, 6301L, "EOF".getBytes(), sliceShift); } + static boolean manualTest = false; + public static void main(final String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-manual")) { + manualTest = true; + } + } final String tstname = TestByteBufferOutputStream.class.getName(); org.junit.runner.JUnitCore.main(tstname); } diff --git a/src/junit/com/jogamp/common/util/BitstreamData.java b/src/junit/com/jogamp/common/util/BitDemoData.java index a5a0bd9..9d605fc 100644 --- a/src/junit/com/jogamp/common/util/BitstreamData.java +++ b/src/junit/com/jogamp/common/util/BitDemoData.java @@ -30,11 +30,49 @@ package com.jogamp.common.util; import java.nio.ByteBuffer; -public class BitstreamData { +public class BitDemoData { + public static final long UNSIGNED_INT_MAX_VALUE = 0xffffffffL; + + public static final String[] pyramid32bit_one = { + "00000000000000000000000000000001", + "00000000000000000000000000000010", + "00000000000000000000000000000100", + "00000000000000000000000000001000", + "00000000000000000000000000010000", + "00000000000000000000000000100000", + "00000000000000000000000001000000", + "00000000000000000000000010000000", + "00000000000000000000000100000000", + "00000000000000000000001000000000", + "00000000000000000000010000000000", + "00000000000000000000100000000000", + "00000000000000000001000000000000", + "00000000000000000010000000000000", + "00000000000000000100000000000000", + "00000000000000001000000000000000", + "00000000000000010000000000000000", + "00000000000000100000000000000000", + "00000000000001000000000000000000", + "00000000000010000000000000000000", + "00000000000100000000000000000000", + "00000000001000000000000000000000", + "00000000010000000000000000000000", + "00000000100000000000000000000000", + "00000001000000000000000000000000", + "00000010000000000000000000000000", + "00000100000000000000000000000000", + "00001000000000000000000000000000", + "00010000000000000000000000000000", + "00100000000000000000000000000000", + "01000000000000000000000000000000", + "10000000000000000000000000000000" + }; + // // MSB -> LSB over whole data // public static final byte[] testBytesMSB = new byte[] { (byte)0xde, (byte)0xaf, (byte)0xca, (byte)0xfe }; + public static final int testIntMSB = 0xdeafcafe; // 11011110 10101111 11001010 11111110 public static final String[] testStringsMSB = new String[] { "11011110", "10101111", "11001010", "11111110" }; public static final String testStringMSB = testStringsMSB[0]+testStringsMSB[1]+testStringsMSB[2]+testStringsMSB[3]; @@ -42,6 +80,7 @@ public class BitstreamData { // MSB -> LSB, reverse bit-order over each byte of testBytesLSB // public static final byte[] testBytesMSB_rev = new byte[] { (byte)0xfe, (byte)0xca, (byte)0xaf, (byte)0xde }; + public static final int testIntMSB_rev = 0xfecaafde; public static final String[] testStringsMSB_rev = new String[] { "11111110", "11001010", "10101111", "11011110" }; public static final String testStringMSB_rev = testStringsMSB_rev[0]+testStringsMSB_rev[1]+testStringsMSB_rev[2]+testStringsMSB_rev[3]; @@ -49,6 +88,7 @@ public class BitstreamData { // LSB -> MSB over whole data // public static final byte[] testBytesLSB = new byte[] { (byte)0x7f, (byte)0x53, (byte)0xf5, (byte)0x7b }; + public static final int testIntLSB = 0x7f53f57b; public static final String[] testStringsLSB = new String[] { "01111111", "01010011", "11110101", "01111011" }; public static final String testStringLSB = testStringsLSB[0]+testStringsLSB[1]+testStringsLSB[2]+testStringsLSB[3]; @@ -56,6 +96,7 @@ public class BitstreamData { // LSB -> MSB, reverse bit-order over each byte of testBytesMSB // public static final byte[] testBytesLSB_revByte = new byte[] { (byte)0x7b, (byte)0xf5, (byte)0x53, (byte)0x7f }; + public static final int testIntLSB_revByte = 0x7bf5537f; public static final String[] testStringsLSB_revByte = new String[] { "01111011", "11110101", "01010011", "01111111" }; public static final String testStringLSB_revByte = testStringsLSB_revByte[0]+testStringsLSB_revByte[1]+testStringsLSB_revByte[2]+testStringsLSB_revByte[3]; @@ -80,6 +121,26 @@ public class BitstreamData { } } + public static int getOneBitCount(final String pattern) { + int c=0; + for(int i=0; i<pattern.length(); i++) { + if( '1' == pattern.charAt(i) ) { + c++; + } + } + return c; + } + public static long toLong(final String bitPattern) { + return Long.valueOf(bitPattern, 2).longValue(); + } + public static int toInteger(final String bitPattern) { + final long res = Long.valueOf(bitPattern, 2).longValue(); + if( res > UNSIGNED_INT_MAX_VALUE ) { + throw new NumberFormatException("Exceeds "+toHexString(UNSIGNED_INT_MAX_VALUE)+": "+toHexString(res)+" - source "+bitPattern); + } + return (int)res; + } + public static String toHexString(final int v) { return "0x"+Integer.toHexString(v); } diff --git a/src/junit/com/jogamp/common/util/CustomDeflate.java b/src/junit/com/jogamp/common/util/CustomDeflate.java new file mode 100644 index 0000000..d4682e8 --- /dev/null +++ b/src/junit/com/jogamp/common/util/CustomDeflate.java @@ -0,0 +1,115 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class CustomDeflate { + public static void main(final String[] args) { + if (args.length != 2) { + System.err.println("Usage: java "+CustomDeflate.class.getName()+" file-in file-out"); + } else { + final File fileIn = new File(args[0]); + final File fileOut = new File(args[1]); + final int inSize; + { + final long _inSize = fileIn.length(); + if( 0 >= _inSize || _inSize > Integer.MAX_VALUE ) { + throw new IllegalArgumentException(""); + } + inSize = (int) _inSize; + } + final byte[] input = new byte[inSize]; + InputStream in = null; + OutputStream out = null; + try { + in = new FileInputStream(fileIn); + int numBytes = 0; + try { + while (true) { + final int remBytes = inSize - numBytes; + int count; + if ( 0 >= remBytes || (count = in.read(input, numBytes, remBytes)) == -1 ) { + break; + } + numBytes += count; + } + } finally { + in.close(); + } + if( inSize != numBytes ) { + throw new IOException("Got "+numBytes+" bytes != expected "+inSize); + } + out = new FileOutputStream(fileOut); + CustomCompress.deflateToStream(input, 0, inSize, 9, out); + } catch (final IOException ioe) { + ioe.printStackTrace(); + } finally { + if( null != in ) { + try { in.close(); } catch (final IOException e) { } + } + if( null != out ) { + try { out.close(); } catch (final IOException e) { } + } + } + + // + // Test + // + in = null; + out = null; + try { + in = new FileInputStream(fileOut); + final byte[] compare = CustomCompress.inflateFromStream(in); + if( compare.length != inSize ) { + throw new InternalError("Inflated Size Mismatch: Has "+compare.length+", expected "+inSize); + } + for(int i=0; i<inSize; i++) { + if( input[i] != compare[i] ) { + throw new InternalError("Inflated Bytes Mismatch at "+i+"/"+inSize+": Has "+Integer.toHexString(compare[i])+", expected "+Integer.toHexString(input[i])); + } + } + } catch (final IOException ioe) { + ioe.printStackTrace(); + } finally { + if( null != in ) { + try { in.close(); } catch (final IOException e) { } + } + if( null != out ) { + try { out.close(); } catch (final IOException e) { } + } + } + } + } + +} diff --git a/src/junit/com/jogamp/common/util/CustomInflate.java b/src/junit/com/jogamp/common/util/CustomInflate.java new file mode 100644 index 0000000..477439e --- /dev/null +++ b/src/junit/com/jogamp/common/util/CustomInflate.java @@ -0,0 +1,68 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class CustomInflate { + public static final int magic = 0xDEF1A7E0; + + public static void main(final String[] args) { + if (args.length != 2) { + System.err.println("Usage: java "+CustomCompress.class.getName()+" file-in file-out"); + } else { + InputStream in = null; + OutputStream out = null; + try { + final File fileIn = new File(args[0]); + in = new FileInputStream(fileIn); + + final byte[] output = CustomCompress.inflateFromStream(in); + + final File fileOut = new File(args[1]); + out = new FileOutputStream(fileOut); + out.write(output, 0, output.length); + out.flush(); + } catch (final IOException ioe) { + ioe.printStackTrace(); + } finally { + if( null != in ) { + try { in.close(); } catch (final IOException e) { } + } + if( null != out ) { + try { out.close(); } catch (final IOException e) { } + } + } + } + } +} diff --git a/src/junit/com/jogamp/common/util/TestArrayHashMap01.java b/src/junit/com/jogamp/common/util/TestArrayHashMap01.java new file mode 100644 index 0000000..529612c --- /dev/null +++ b/src/junit/com/jogamp/common/util/TestArrayHashMap01.java @@ -0,0 +1,186 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.util.*; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + +import com.jogamp.junit.util.SingletonJunitCase; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestArrayHashMap01 extends SingletonJunitCase { + + public static class Dummy { + int i1, i2, i3; + + public Dummy(final int i1, final int i2, final int i3) { + this.i1 = i1; + this.i2 = i2; + this.i3 = i3; + } + + public boolean equals(final Object o) { + if(o instanceof Dummy) { + final Dummy d = (Dummy)o; + return this.i1 == d.i1 && + this.i2 == d.i2 && + this.i3 == d.i3 ; + } + return false; + } + + public final int hashCode() { + // 31 * x == (x << 5) - x + int hash = 31 + i1; + hash = ((hash << 5) - hash) + i2; + hash = ((hash << 5) - hash) + i3; + return hash; + } + + public String toString() { + return "Dummy["+super.toString()+": "+i1+", "+i2+", "+i3+"]"; + } + } + + void populate(final Map<Integer, Dummy> l, final int start, final int len, + final int i2, final int i3, final int expectedPlusSize) { + final int oldSize = l.size(); + for(int pos = start+len-1; pos>=start; pos--) { + l.put(pos, new Dummy(pos, i2, i3)); + } + Assert.assertEquals(expectedPlusSize, l.size() - oldSize); + } + boolean checkOrder(final List<Dummy> l, final int startIdx, final int start, final int len) { + for(int i=0; i<len; i++) { + final Dummy d = l.get(startIdx+i); + final int i1 = start+len-1-i; + if( d.i1 != i1 ) { + return false; + } + } + return true; + } + + @Test + public void test01ArrayHashMapWithNullValue() { + testArrayHashMapImpl(true); + } + @Test + public void test02ArrayHashSetWithoutNullValue() { + testArrayHashMapImpl(false); + } + void testArrayHashMapImpl(final boolean supportNullValue) { + final ArrayHashMap<Integer, Dummy> l = + new ArrayHashMap<Integer, Dummy>(supportNullValue, + ArrayHashSet.DEFAULT_INITIAL_CAPACITY, + ArrayHashSet.DEFAULT_LOAD_FACTOR); + Assert.assertEquals(supportNullValue, l.supportsNullValue()); + final int p7_22_34_key, p7_22_34_idx; + final Dummy p7_22_34_orig; + final int p6_22_34_key, p6_22_34_idx; + final Dummy p6_22_34_orig; + { + populate(l, 10, 100, 22, 34, 100); // [109 .. 10] + Assert.assertTrue(checkOrder(l.getData(), 0, 10, 100)); + populate(l, 10, 100, 22, 34, 0); // [109 .. 10] + Assert.assertTrue(checkOrder(l.getData(), 0, 10, 100)); + populate(l, 6, 5, 22, 34, 4); // [ 9 .. 6], 10 already exists + Assert.assertTrue(checkOrder(l.getData(), 100, 6, 4)); + p7_22_34_idx = l.size() - 2; + p7_22_34_key = 7; + p7_22_34_orig = l.get(p7_22_34_key); + p6_22_34_idx = l.size() - 1; + p6_22_34_key = 6; + p6_22_34_orig = l.get(p6_22_34_key); + } + Assert.assertNotNull(p7_22_34_orig); + Assert.assertEquals(7, p7_22_34_orig.i1); + Assert.assertEquals(l.getData().get(p7_22_34_idx), p7_22_34_orig); + Assert.assertNotNull(p6_22_34_orig); + Assert.assertEquals(6, p6_22_34_orig.i1); + Assert.assertEquals(l.getData().get(p6_22_34_idx), p6_22_34_orig); + + final Dummy p7_22_34_other = new Dummy(7, 22, 34); + Assert.assertEquals(p7_22_34_other, p7_22_34_orig); + Assert.assertTrue(p7_22_34_other.hashCode() == p7_22_34_orig.hashCode()); + Assert.assertTrue(p7_22_34_other != p7_22_34_orig); // diff reference + final Dummy p6_22_34_other = new Dummy(6, 22, 34); + Assert.assertEquals(p6_22_34_other, p6_22_34_orig); + Assert.assertTrue(p6_22_34_other.hashCode() == p6_22_34_orig.hashCode()); + Assert.assertTrue(p6_22_34_other != p6_22_34_orig); // diff reference + + // fast identity .. + Dummy q = l.get(p6_22_34_key); + Assert.assertNotNull(q); + Assert.assertEquals(p6_22_34_other, q); + Assert.assertTrue(p6_22_34_other.hashCode() == q.hashCode()); + Assert.assertTrue(p6_22_34_other != q); // diff reference + Assert.assertTrue(p6_22_34_orig == q); // same reference + + Assert.assertTrue(l.containsValue(q)); + Assert.assertTrue(l.containsValue(p6_22_34_other)); // add equivalent + + q = l.put(p6_22_34_key, p6_22_34_other); // override w/ diff hash-obj + Assert.assertNotNull(q); + Assert.assertEquals(p6_22_34_other, q); + Assert.assertTrue(p6_22_34_other.hashCode() == q.hashCode()); + Assert.assertTrue(p6_22_34_other != q); // diff reference new != old (q) + Assert.assertTrue(p6_22_34_orig == q); // same reference orig == old (q) + Assert.assertTrue(checkOrder(l.getData(), 0, 10, 100)); + Assert.assertTrue(checkOrder(l.getData(), 100, 6, 4)); + + final Dummy p1_2_3 = new Dummy(1, 2, 3); // a new one .. + q = l.put(1, p1_2_3); // added test + Assert.assertNull(q); + + final Dummy pNull = null; + NullPointerException npe = null; + try { + q = l.put(0, pNull); + Assert.assertNull(q); + } catch (final NullPointerException _npe) { npe = _npe; } + if( l.supportsNullValue() ) { + Assert.assertNull(npe); + } else { + Assert.assertNotNull(npe); + } + } + + public static void main(final String args[]) throws IOException { + final String tstname = TestArrayHashMap01.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/junit/com/jogamp/common/util/TestArrayHashSet01.java b/src/junit/com/jogamp/common/util/TestArrayHashSet01.java index 51db2c7..3e8fbc0 100644 --- a/src/junit/com/jogamp/common/util/TestArrayHashSet01.java +++ b/src/junit/com/jogamp/common/util/TestArrayHashSet01.java @@ -74,47 +74,99 @@ public class TestArrayHashSet01 extends SingletonJunitCase { } } - public void populate(final List<Dummy> l, final int start, final int len, final int i2, final int i3, final int expectedPlusSize) { + void populate(final List<Dummy> l, final int start, final int len, + final int i2, final int i3, final int expectedPlusSize) { final int oldSize = l.size(); - int pos = start+len-1; - while(pos>=start) { - l.add(new Dummy(pos--, i2, i3)); + for(int pos = start+len-1; pos>=start; pos--) { + l.add(new Dummy(pos, i2, i3)); } Assert.assertEquals(expectedPlusSize, l.size() - oldSize); } + boolean checkOrder(final List<Dummy> l, final int startIdx, final int start, final int len) { + for(int i=0; i<len; i++) { + final Dummy d = l.get(startIdx+i); + final int i1 = start+len-1-i; + if( d.i1 != i1 ) { + return false; + } + } + return true; + } @Test - public void test01ArrayHashSet() { - final ArrayHashSet<Dummy> l = new ArrayHashSet<Dummy>(); - populate(l, 10, 100, 22, 34, 100); // [10 .. 109] - populate(l, 10, 100, 22, 34, 0); // [10 .. 109] - populate(l, 6, 5, 22, 34, 4); // [ 6 .. 9], 10 already exists - - final Dummy p6_22_34 = new Dummy(6, 22, 34); + public void test01ArrayHashSetWithNullValue() { + testArrayHashSetImpl(true); + } + @Test + public void test02ArrayHashSetWithoutNullValue() { + testArrayHashSetImpl(false); + } + void testArrayHashSetImpl(final boolean supportNullValue) { + final ArrayHashSet<Dummy> l = + new ArrayHashSet<Dummy>(supportNullValue, + ArrayHashSet.DEFAULT_INITIAL_CAPACITY, + ArrayHashSet.DEFAULT_LOAD_FACTOR); + Assert.assertEquals(supportNullValue, l.supportsNullValue()); + final int p7_22_34_idx; + final Dummy p7_22_34_orig; + final int p6_22_34_idx; + final Dummy p6_22_34_orig; + { + populate(l, 10, 100, 22, 34, 100); // [109 .. 10] + Assert.assertTrue(checkOrder(l, 0, 10, 100)); + populate(l, 10, 100, 22, 34, 0); // [109 .. 10] + Assert.assertTrue(checkOrder(l, 0, 10, 100)); + populate(l, 6, 5, 22, 34, 4); // [ 9 .. 6], 10 already exists + Assert.assertTrue(checkOrder(l, 100, 6, 4)); + p7_22_34_idx = l.size() - 2; + p7_22_34_orig = l.get(p7_22_34_idx); + p6_22_34_idx = l.size() - 1; + p6_22_34_orig = l.get(p6_22_34_idx); + } + Assert.assertNotNull(p7_22_34_orig); + Assert.assertEquals(7, p7_22_34_orig.i1); + Assert.assertEquals(l.getData().get(p7_22_34_idx), p7_22_34_orig); + Assert.assertNotNull(p6_22_34_orig); + Assert.assertEquals(6, p6_22_34_orig.i1); + Assert.assertEquals(l.getData().get(p6_22_34_idx), p6_22_34_orig); + + final Dummy p7_22_34_other = new Dummy(7, 22, 34); + Assert.assertEquals(p7_22_34_other, p7_22_34_orig); + Assert.assertTrue(p7_22_34_other.hashCode() == p7_22_34_orig.hashCode()); + Assert.assertTrue(p7_22_34_other != p7_22_34_orig); // diff reference + final Dummy p6_22_34_other = new Dummy(6, 22, 34); + Assert.assertEquals(p6_22_34_other, p6_22_34_orig); + Assert.assertTrue(p6_22_34_other.hashCode() == p6_22_34_orig.hashCode()); + Assert.assertTrue(p6_22_34_other != p6_22_34_orig); // diff reference // slow get on position .. - final int i = l.indexOf(p6_22_34); + final int i = l.indexOf(p6_22_34_other); Dummy q = l.get(i); Assert.assertNotNull(q); - Assert.assertEquals(p6_22_34, q); - Assert.assertTrue(p6_22_34.hashCode() == q.hashCode()); - Assert.assertTrue(p6_22_34 != q); // diff reference + Assert.assertEquals(p6_22_34_other, q); + Assert.assertTrue(p6_22_34_other.hashCode() == q.hashCode()); + Assert.assertTrue(p6_22_34_other != q); // diff reference + Assert.assertTrue(p6_22_34_orig == q); // same reference // fast identity .. - q = l.get(p6_22_34); + q = l.get(p6_22_34_other); Assert.assertNotNull(q); - Assert.assertEquals(p6_22_34, q); - Assert.assertTrue(p6_22_34.hashCode() == q.hashCode()); - Assert.assertTrue(p6_22_34 != q); // diff reference + Assert.assertEquals(p6_22_34_other, q); + Assert.assertTrue(p6_22_34_other.hashCode() == q.hashCode()); + Assert.assertTrue(p6_22_34_other != q); // diff reference + Assert.assertTrue(p6_22_34_orig == q); // same reference Assert.assertTrue(!l.add(q)); // add same - Assert.assertTrue(!l.add(p6_22_34)); // add equivalent + Assert.assertTrue(!l.add(p6_22_34_other)); // add equivalent - q = l.getOrAdd(p6_22_34); // not added test + q = l.getOrAdd(p6_22_34_other); // not added test w/ diff hash-obj Assert.assertNotNull(q); - Assert.assertEquals(p6_22_34, q); - Assert.assertTrue(p6_22_34.hashCode() == q.hashCode()); - Assert.assertTrue(p6_22_34 != q); // diff reference + Assert.assertEquals(p6_22_34_other, q); + Assert.assertTrue(p6_22_34_other.hashCode() == q.hashCode()); + Assert.assertTrue(p6_22_34_other != q); // diff reference + Assert.assertTrue(p6_22_34_orig == q); // same reference + Assert.assertTrue(checkOrder(l, 0, 10, 100)); + Assert.assertTrue(checkOrder(l, 100, 6, 4)); final Dummy p1_2_3 = new Dummy(1, 2, 3); // a new one .. q = l.getOrAdd(p1_2_3); // added test @@ -122,6 +174,29 @@ public class TestArrayHashSet01 extends SingletonJunitCase { Assert.assertEquals(p1_2_3, q); Assert.assertTrue(p1_2_3.hashCode() == q.hashCode()); Assert.assertTrue(p1_2_3 == q); // _same_ reference, since getOrAdd added it + Assert.assertTrue(checkOrder(l, 0, 10, 100)); + Assert.assertTrue(checkOrder(l, 100, 6, 4)); + + final Dummy pNull = null; + NullPointerException npe = null; + try { + q = l.getOrAdd(pNull); + Assert.assertNull(q); + } catch (final NullPointerException _npe) { npe = _npe; } + if( l.supportsNullValue() ) { + Assert.assertNull(npe); + } else { + Assert.assertNotNull(npe); + } + + try { + Assert.assertTrue(l.remove(pNull)); + } catch (final NullPointerException _npe) { npe = _npe; } + if( l.supportsNullValue() ) { + Assert.assertNull(npe); + } else { + Assert.assertNotNull(npe); + } } public static void main(final String args[]) throws IOException { diff --git a/src/junit/com/jogamp/common/util/TestBitfield00.java b/src/junit/com/jogamp/common/util/TestBitfield00.java new file mode 100644 index 0000000..9ace743 --- /dev/null +++ b/src/junit/com/jogamp/common/util/TestBitfield00.java @@ -0,0 +1,431 @@ +/** + * Copyright 2015 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.IOException; + +import org.junit.Test; +import org.junit.Assert; + +import com.jogamp.junit.util.SingletonJunitCase; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test basic bitfield operations for {@link Bitfield} + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestBitfield00 extends SingletonJunitCase { + + @Test + public void test01_BitCount32_One() { + final String[] pyramid32bit_one = BitDemoData.pyramid32bit_one; + for(int i=0; i<pyramid32bit_one.length; i++) { + final int val0 = 1 << i; + final int oneBitCountI = Integer.bitCount(val0); + final String pattern0 = pyramid32bit_one[i]; + final int val1 = BitDemoData.toInteger(pattern0); + final String pattern1 = BitDemoData.toBinaryString(val0, 32); + final int oneBitCount0 = BitDemoData.getOneBitCount(pattern0); + final int oneBitCount1 = Bitfield.Util.bitCount(val0); + final String msg = String.format("Round %02d: 0x%08x %s, c %d / %d%n : 0x%08x %s, c %d%n", + i, val0, pattern0, oneBitCount0, oneBitCountI, val1, pattern1, oneBitCount1); + + Assert.assertEquals(msg, val0, val1); + Assert.assertEquals(msg, pattern0, pattern1); + + Assert.assertEquals(msg, oneBitCount0, oneBitCountI); + Assert.assertEquals(msg, oneBitCount0, oneBitCount1); + } + } + + @SuppressWarnings("unused") + @Test + public void test02_BitCount32_Samples() { + final long MAX = BitDemoData.UNSIGNED_INT_MAX_VALUE; + final long MAX_minus = MAX-0x1FF; + final long MAX_half = MAX/2; + final long MAX_half_minus = MAX_half-0x1FF; + final long MAX_half_plus = MAX_half+0x1FF; + + if( false ) { + for(long l=0; l<=MAX; l++) { + test_BitCount32_Samples(l); + } + } + for(long l=0; l>=0x1FF; l++) { + test_BitCount32_Samples(l); + } + for(long l=MAX_half_minus; l<=MAX_half_plus; l++) { + test_BitCount32_Samples(l); + } + for(long l=MAX_minus; l<=MAX; l++) { + test_BitCount32_Samples(l); + } + } + static void test_BitCount32_Samples(final long l) { + final int oneBitCountL = Long.bitCount(l); + final int val0 = (int)l; + final int oneBitCountI = Integer.bitCount(val0); + final int oneBitCount1 = Bitfield.Util.bitCount(val0); + final String msg = String.format("Round 0x%08x, c %d / %d / %d", val0, + oneBitCountL, oneBitCountI, oneBitCount1); + Assert.assertEquals(msg, oneBitCountI, oneBitCountL); + Assert.assertEquals(msg, oneBitCountI, oneBitCount1); + } + + static int[] testDataOneBit = new int[] { + 0,0, 1,1, 2,1, 3,2, 4,1, 5,2, 6,2, 7,3, + 8,1, 9,2, 10,2, 11,3, 12,2, 13,3, 14,3, 15,4, 16,1, 17,2, + 0x3F,6, 0x40,1, 0x41,2, 0x7f,7, 0x80,1, 0x81,2, 0xfe,7, 0xff,8, + 0x4000,1, 0x4001,2, 0x7000,3, 0x7fff,15, + 0x0FFFFFF0,24, + 0x55555555,16, + 0x7F53F57B,23, 0xFEA7EAF6,23, /* << 1 */ + 0x80000000, 1, + 0xAAAAAAAA,16, + 0xC0C0C0C0, 8, + 0xFF000000, 8, + 0xFFFFFFFF,32 + }; + @Test + public void test03_BitCount32_Data() { + for(int i = 0; i<testDataOneBit.length; i+=2) { + test_BitCount32_Data(testDataOneBit[i], testDataOneBit[i+1]); + } + } + static void test_BitCount32_Data(final int i, final int expOneBits) { + final int oneBitCountI = Integer.bitCount(i); + final int oneBitCount1 = Bitfield.Util.bitCount(i); + final String msg = String.format("Round 0x%08x, c %d / %d", i, + oneBitCountI, oneBitCount1); + Assert.assertEquals(msg, oneBitCount1, expOneBits); + Assert.assertEquals(msg, oneBitCountI, oneBitCount1); + } + + @Test + public void test10_Setup() { + final int bitSize32 = 32; + final int bitSize128 = 128; + final Bitfield bf1 = Bitfield.Factory.create(bitSize32); + Assert.assertEquals(bitSize32, bf1.size()); + Assert.assertTrue(bf1 instanceof jogamp.common.util.Int32Bitfield); + final Bitfield bf2 = Bitfield.Factory.create(bitSize128); + Assert.assertEquals(bitSize128, bf2.size()); + Assert.assertTrue(bf2 instanceof jogamp.common.util.Int32ArrayBitfield); + + // verify no bit is set + Assert.assertEquals(0, bf1.bitCount()); + Assert.assertEquals(0, bf2.bitCount()); + + bf1.clearField(true); + bf2.clearField(true); + Assert.assertEquals(bf1.size(), bf1.bitCount()); + Assert.assertEquals(bf2.size(), bf2.bitCount()); + + bf1.clearField(false); + bf2.clearField(false); + Assert.assertEquals(0, bf1.bitCount()); + Assert.assertEquals(0, bf2.bitCount()); + } + static class TestDataBF { + final int bitSize; + final int val; + final String pattern; + public TestDataBF(final int bitSize, final int value, final String pattern) { + this.bitSize = bitSize; + this.val = value; + this.pattern = pattern; + } + } + static TestDataBF[] testDataBF32Bit = { + new TestDataBF(32, BitDemoData.testIntMSB, BitDemoData.testStringMSB), + new TestDataBF(32, BitDemoData.testIntMSB_rev, BitDemoData.testStringMSB_rev), + new TestDataBF(32, BitDemoData.testIntLSB, BitDemoData.testStringLSB), + new TestDataBF(32, BitDemoData.testIntLSB_revByte, BitDemoData.testStringLSB_revByte), + + // H->L : 0x04030201: 00000100 00000011 00000010 00000001 + new TestDataBF(32, 0x04030201, "00000100000000110000001000000001"), + + // H->L : 0xAFFECAFE: 10101111 11111110 11001010 11111110 + new TestDataBF(32, 0xAFFECAFE, "10101111111111101100101011111110"), + // H->L : 0xDEADBEEF: 11011110 10101101 10111110 11101111 + new TestDataBF(32, 0xDEADBEEF, "11011110101011011011111011101111") + }; + static TestDataBF[] testDataBF16Bit = { + // H->L : 0x0201: 00000100 00000011 00000010 00000001 + new TestDataBF(16, 0x0201, "0000001000000001"), + // H->L : 0x0403: 00000100 00000011 + new TestDataBF(16, 0x0403, "0000010000000011"), + + // H->L : 0xAFFE: 10101111 11111110 + new TestDataBF(16, 0xAFFE, "1010111111111110"), + // H->L : 0xCAFE: 11001010 11111110 + new TestDataBF(16, 0xCAFE, "1100101011111110"), + + // H->L : 0xDEADBEEF: 11011110 10101101 10111110 11101111 + new TestDataBF(16, 0xDEAD, "1101111010101101"), + new TestDataBF(16, 0xBEEF, "1011111011101111") + }; + static TestDataBF[] testDataBF3Bit = { + new TestDataBF(3, 0x01, "001"), + new TestDataBF(3, 0x02, "010"), + new TestDataBF(3, 0x05, "101") + }; + + @Test + public void test20_ValidateTestData() { + for(int i=0; i<testDataBF32Bit.length; i++) { + test_ValidateTestData( testDataBF32Bit[i] ); + } + for(int i=0; i<testDataBF16Bit.length; i++) { + test_ValidateTestData( testDataBF16Bit[i] ); + } + for(int i=0; i<testDataBF3Bit.length; i++) { + test_ValidateTestData( testDataBF3Bit[i] ); + } + } + static void test_ValidateTestData(final TestDataBF d) { + final int oneBitCount0 = Bitfield.Util.bitCount(d.val); + final int oneBitCount1 = BitDemoData.getOneBitCount(d.pattern); + Assert.assertEquals(oneBitCount1, oneBitCount0); + final String pattern0 = BitDemoData.toBinaryString(d.val, d.bitSize); + Assert.assertEquals(d.pattern, pattern0); + final int val1 = BitDemoData.toInteger(d.pattern); + Assert.assertEquals(d.val, val1); + Assert.assertEquals(d.bitSize, pattern0.length()); + } + + static void assertEquals(final Bitfield bf, final int bf_off, final int v, final String pattern, final int oneBitCount) { + final int len = pattern.length(); + for(int i=0; i<len; i++) { + final boolean exp0 = 0 != ( v & ( 1 << i ) ); + final boolean exp1 = '1' == pattern.charAt(len-1-i); + final boolean has = bf.get(i+bf_off); + final String msg = String.format("Pos %04d: Value 0x%08x / %s, c %d", i, v, pattern, oneBitCount); + Assert.assertEquals(msg, exp0, has); + Assert.assertEquals(msg, exp1, has); + } + } + + @Test + public void test21_Aligned32bit() { + for(int i=0; i<testDataBF32Bit.length; i++) { + test_Aligned32bit( testDataBF32Bit[i] ); + } + for(int i=0; i<testDataBF16Bit.length; i++) { + test_Aligned32bit( testDataBF16Bit[i] ); + } + for(int i=0; i<testDataBF3Bit.length; i++) { + test_Aligned32bit( testDataBF3Bit[i] ); + } + } + static int get32BitStorageSize(final int bits) { + final int units = Math.max(1, ( bits + 31 ) >>> 5); + return units << 5; + } + static void test_Aligned32bit(final TestDataBF d) { + final int oneBitCount = Bitfield.Util.bitCount(d.val); + + final Bitfield bf1 = Bitfield.Factory.create(d.bitSize); + Assert.assertEquals(get32BitStorageSize(d.bitSize), bf1.size()); + final Bitfield bf2 = Bitfield.Factory.create(d.bitSize+128); + Assert.assertEquals(get32BitStorageSize(d.bitSize+128), bf2.size()); + + bf1.put32( 0, d.bitSize, d.val); + Assert.assertEquals(d.val, bf1.get32( 0, d.bitSize)); + Assert.assertEquals(oneBitCount, bf1.bitCount()); + assertEquals(bf1, 0, d.val, d.pattern, oneBitCount); + + bf2.put32( 0, d.bitSize, d.val); + Assert.assertEquals(d.val, bf2.get32( 0, d.bitSize)); + Assert.assertEquals(oneBitCount*1, bf2.bitCount()); + assertEquals(bf2, 0, d.val, d.pattern, oneBitCount); + bf2.put32(64, d.bitSize, d.val); + Assert.assertEquals(d.val, bf2.get32(64, d.bitSize)); + Assert.assertEquals(oneBitCount*2, bf2.bitCount()); + assertEquals(bf2, 64, d.val, d.pattern, oneBitCount); + + Assert.assertEquals(d.val, bf2.copy32(0, 96, d.bitSize)); + Assert.assertEquals(d.val, bf2.get32(96, d.bitSize)); + Assert.assertEquals(oneBitCount*3, bf2.bitCount()); + assertEquals(bf2, 96, d.val, d.pattern, oneBitCount); + } + + @Test + public void test21_Unaligned() { + for(int i=0; i<testDataBF32Bit.length; i++) { + test_Unaligned(testDataBF32Bit[i]); + } + for(int i=0; i<testDataBF16Bit.length; i++) { + test_Unaligned(testDataBF16Bit[i]); + } + for(int i=0; i<testDataBF3Bit.length; i++) { + test_Unaligned( testDataBF3Bit[i] ); + } + } + static void test_Unaligned(final TestDataBF d) { + final Bitfield bf1 = Bitfield.Factory.create(d.bitSize); + final Bitfield bf2 = Bitfield.Factory.create(d.bitSize+128); + Assert.assertEquals(get32BitStorageSize(d.bitSize), bf1.size()); + Assert.assertEquals(get32BitStorageSize(d.bitSize+128), bf2.size()); + test_Unaligned( d, bf1 ); + test_Unaligned( d, bf2 ); + } + static void test_Unaligned(final TestDataBF d, final Bitfield bf) { + final int maxBitpos = bf.size()-d.bitSize; + for(int i=0; i<=maxBitpos; i++) { + bf.clearField(false); + test_Unaligned(d, bf, i); + } + } + static void test_Unaligned(final TestDataBF d, final Bitfield bf, final int lowBitnum) { + final int maxBitpos = bf.size()-d.bitSize; + final int oneBitCount = Bitfield.Util.bitCount(d.val); + + final String msg = String.format("Value 0x%08x / %s, l %d/%d, c %d, lbPos %d -> %d", + d.val, d.pattern, d.bitSize, bf.size(), oneBitCount, lowBitnum, maxBitpos); + + // + // via put32 + // + bf.put32( lowBitnum, d.bitSize, d.val); + for(int i=0; i<d.bitSize; i++) { + Assert.assertEquals(msg+", bitpos "+i, 0 != ( d.val & ( 1 << i ) ), bf.get(lowBitnum+i)); + } + Assert.assertEquals(msg, d.val, bf.get32( lowBitnum, d.bitSize)); + Assert.assertEquals(msg, oneBitCount, bf.bitCount()); + assertEquals(bf, lowBitnum, d.val, d.pattern, oneBitCount); + + // + // via copy32 + // + if( lowBitnum < maxBitpos ) { + // copy bits 1 forward + // clear trailing orig bit + Assert.assertEquals(msg, d.val, bf.copy32(lowBitnum, lowBitnum+1, d.bitSize)); + bf.clear(lowBitnum); + Assert.assertEquals(msg, d.val, bf.get32( lowBitnum+1, d.bitSize)); + Assert.assertEquals(msg, oneBitCount, bf.bitCount()); + assertEquals(bf, lowBitnum+1, d.val, d.pattern, oneBitCount); + } + + // test put() return value (previous value) + bf.clearField(false); + Assert.assertEquals(msg+", bitpos "+0, false, bf.put(lowBitnum+0, true)); + Assert.assertEquals(msg+", bitpos "+0, true, bf.put(lowBitnum+0, false)); + + // + // via put + // + for(int i=0; i<d.bitSize; i++) { + Assert.assertEquals(msg+", bitpos "+i, false, bf.put(lowBitnum+i, 0 != ( d.val & ( 1 << i ) ))); + } + Assert.assertEquals(msg, d.val, bf.get32(lowBitnum, d.bitSize)); + for(int i=0; i<d.bitSize; i++) { + Assert.assertEquals(msg+", bitpos "+i, 0 != ( d.val & ( 1 << i ) ), bf.get(lowBitnum+i)); + } + Assert.assertEquals(msg, oneBitCount, bf.bitCount()); + assertEquals(bf, lowBitnum, d.val, d.pattern, oneBitCount); + + // + // via copy + // + if( lowBitnum < maxBitpos ) { + // copy bits 1 forward + // clear trailing orig bit + for(int i=d.bitSize-1; i>=0; i--) { + Assert.assertEquals(msg+", bitpos "+i, 0 != ( d.val & ( 1 << i ) ), + bf.copy(lowBitnum+i, lowBitnum+1+i) ); + } + bf.clear(lowBitnum); + Assert.assertEquals(msg, d.val, bf.get32( lowBitnum+1, d.bitSize)); + for(int i=0; i<d.bitSize; i++) { + Assert.assertEquals(msg+", bitpos "+i, 0 != ( d.val & ( 1 << i ) ), bf.get(lowBitnum+1+i)); + } + Assert.assertEquals(msg, oneBitCount, bf.bitCount()); + assertEquals(bf, lowBitnum+1, d.val, d.pattern, oneBitCount); + } + + // + // via set/clear + // + bf.clearField(false); + for(int i=0; i<d.bitSize; i++) { + if( 0 != ( d.val & ( 1 << i ) ) ) { + bf.set(lowBitnum+i); + } else { + bf.clear(lowBitnum+i); + } + } + Assert.assertEquals(msg, d.val, bf.get32(lowBitnum, d.bitSize)); + for(int i=0; i<d.bitSize; i++) { + Assert.assertEquals(msg+", bitpos "+i, 0 != ( d.val & ( 1 << i ) ), bf.get(lowBitnum+i)); + } + Assert.assertEquals(msg, oneBitCount, bf.bitCount()); + assertEquals(bf, lowBitnum, d.val, d.pattern, oneBitCount); + + // + // Validate 'other bits' put32/get32 + // + bf.clearField(false); + bf.put32( lowBitnum, d.bitSize, d.val); + checkOtherBits(d, bf, lowBitnum, msg, 0); + + bf.clearField(true); + bf.put32( lowBitnum, d.bitSize, d.val); + checkOtherBits(d, bf, lowBitnum, msg, Bitfield.UNSIGNED_INT_MAX_VALUE); + } + + static void checkOtherBits(final TestDataBF d, final Bitfield bf, final int lowBitnum, final String msg, final int expBits) { + final int highBitnum = lowBitnum + d.bitSize - 1; + // System.err.println(msg+": [0"+".."+"("+lowBitnum+".."+highBitnum+").."+(bf.size()-1)+"]"); + for(int i=0; i<lowBitnum; i+=32) { + final int len = Math.min(32, lowBitnum-i); + final int val = bf.get32(i, len); + final int exp = expBits & Bitfield.Util.getBitMask(len); + // System.err.println(" <"+i+".."+(i+len-1)+">, exp "+BitDemoData.toHexString(exp)); + Assert.assertEquals(msg+", bitpos "+i, exp, val); + } + for(int i=highBitnum+1; i<bf.size(); i+=32) { + final int len = Math.min(32, bf.size() - i); + final int val = bf.get32(i, len); + final int exp = expBits & Bitfield.Util.getBitMask(len); + // System.err.println(" <"+i+".."+(i+len-1)+">, exp "+BitDemoData.toHexString(exp)); + Assert.assertEquals(msg+", bitpos "+i, exp, val); + } + } + + public static void main(final String args[]) throws IOException { + final String tstname = TestBitfield00.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/junit/com/jogamp/common/util/TestBitstream00.java b/src/junit/com/jogamp/common/util/TestBitstream00.java index 920363b..a2fd095 100644 --- a/src/junit/com/jogamp/common/util/TestBitstream00.java +++ b/src/junit/com/jogamp/common/util/TestBitstream00.java @@ -40,7 +40,7 @@ import org.junit.Assert; import com.jogamp.common.nio.Buffers; import com.jogamp.common.os.Platform; -import static com.jogamp.common.util.BitstreamData.*; +import static com.jogamp.common.util.BitDemoData.*; import com.jogamp.junit.util.SingletonJunitCase; diff --git a/src/junit/com/jogamp/common/util/TestBitstream01.java b/src/junit/com/jogamp/common/util/TestBitstream01.java index 49931a3..bff37e3 100644 --- a/src/junit/com/jogamp/common/util/TestBitstream01.java +++ b/src/junit/com/jogamp/common/util/TestBitstream01.java @@ -35,7 +35,7 @@ import org.junit.Assert; import org.junit.Test; import com.jogamp.junit.util.SingletonJunitCase; -import static com.jogamp.common.util.BitstreamData.*; +import static com.jogamp.common.util.BitDemoData.*; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; diff --git a/src/junit/com/jogamp/common/util/TestBitstream02.java b/src/junit/com/jogamp/common/util/TestBitstream02.java index 16904a2..5e6da1c 100644 --- a/src/junit/com/jogamp/common/util/TestBitstream02.java +++ b/src/junit/com/jogamp/common/util/TestBitstream02.java @@ -37,7 +37,7 @@ import org.junit.Test; import com.jogamp.common.nio.Buffers; import com.jogamp.junit.util.SingletonJunitCase; -import static com.jogamp.common.util.BitstreamData.*; +import static com.jogamp.common.util.BitDemoData.*; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; diff --git a/src/junit/com/jogamp/common/util/TestBitstream03.java b/src/junit/com/jogamp/common/util/TestBitstream03.java index a6129d8..bd09d4a 100644 --- a/src/junit/com/jogamp/common/util/TestBitstream03.java +++ b/src/junit/com/jogamp/common/util/TestBitstream03.java @@ -38,7 +38,7 @@ import org.junit.Test; import com.jogamp.common.nio.Buffers; import com.jogamp.junit.util.SingletonJunitCase; -import static com.jogamp.common.util.BitstreamData.*; +import static com.jogamp.common.util.BitDemoData.*; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; diff --git a/src/junit/com/jogamp/common/util/TestBitstream04.java b/src/junit/com/jogamp/common/util/TestBitstream04.java index 47be38d..2302e2e 100644 --- a/src/junit/com/jogamp/common/util/TestBitstream04.java +++ b/src/junit/com/jogamp/common/util/TestBitstream04.java @@ -38,7 +38,7 @@ import org.junit.Test; import com.jogamp.common.nio.Buffers; import com.jogamp.junit.util.SingletonJunitCase; -import static com.jogamp.common.util.BitstreamData.*; +import static com.jogamp.common.util.BitDemoData.*; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; diff --git a/src/junit/com/jogamp/common/util/TestIOUtil01.java b/src/junit/com/jogamp/common/util/TestIOUtil01.java index e85aa37..19bc8b0 100644 --- a/src/junit/com/jogamp/common/util/TestIOUtil01.java +++ b/src/junit/com/jogamp/common/util/TestIOUtil01.java @@ -35,6 +35,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.net.URISyntaxException; import java.net.URLConnection; import java.nio.ByteBuffer; @@ -43,6 +44,9 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.net.URIDumpUtil; +import com.jogamp.common.net.Uri; import com.jogamp.common.os.MachineDataInfo; import com.jogamp.common.os.Platform; import com.jogamp.junit.util.SingletonJunitCase; @@ -78,8 +82,86 @@ public class TestIOUtil01 extends SingletonJunitCase { } @Test - public void testCopyStream01Array() throws IOException { - final URLConnection urlConn = IOUtil.getResource(this.getClass(), tfilename); + public void test01CleanPathString() throws IOException, URISyntaxException { + { + final String input = "./dummy/nop/../a.txt"; + final String expected = "dummy/a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + final String input = "../dummy/nop/../a.txt"; + final String expected = "../dummy/a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + final String input = ".././dummy/nop/../a.txt"; + final String expected = "../dummy/a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + final String input = "./../dummy/nop/../a.txt"; + final String expected = "../dummy/a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + final String input = "../dummy/./nop/../a.txt"; + final String expected = "../dummy/a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + final String input = "/dummy/nop/./../a.txt"; + final String expected = "/dummy/a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + final String input = "dummy/../nop/./.././aaa/bbb/../../a.txt"; + final String expected = "a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + final String input = "/dummy/../nop/./.././aaa/bbb/././ccc/../../../a.txt"; + final String expected = "/a.txt"; + Assert.assertEquals(expected, IOUtil.cleanPathString(input)); + } + { + URISyntaxException use = null; + try { + // Error case! + final String input = "../../error.txt"; + final String expected = "error.txt"; + final String result = IOUtil.cleanPathString(input); // URISyntaxException + System.err.println("input : "+input); + System.err.println("expected: "+expected); + System.err.println("result : "+result); + Assert.assertEquals(expected, result); + } catch (final URISyntaxException _use) { + use = _use; + ExceptionUtils.dumpThrowable("", _use, 0, 3); + } + Assert.assertNotNull("URISyntaxException expected", use); + } + { + URISyntaxException use = null; + try { + // Error case! + final String input = ".././a/../../error.txt"; + final String expected = "error.txt"; + final String result = IOUtil.cleanPathString(input); // URISyntaxException + System.err.println("input : "+input); + System.err.println("expected: "+expected); + System.err.println("result : "+result); + Assert.assertEquals(expected, result); + } catch (final URISyntaxException _use) { + use = _use; + ExceptionUtils.dumpThrowable("", _use, 0, 3); + } + Assert.assertNotNull("URISyntaxException expected", use); + } + } + + @Test + public void test11CopyStream01Array() throws IOException { + final URLConnection urlConn = IOUtil.getResource(tfilename, this.getClass().getClassLoader(), this.getClass()); Assert.assertNotNull(urlConn); final BufferedInputStream bis = new BufferedInputStream( urlConn.getInputStream() ); final byte[] bb; @@ -94,8 +176,8 @@ public class TestIOUtil01 extends SingletonJunitCase { } @Test - public void testCopyStream02Buffer() throws IOException { - final URLConnection urlConn = IOUtil.getResource(this.getClass(), tfilename); + public void test12CopyStream02Buffer() throws IOException { + final URLConnection urlConn = IOUtil.getResource(tfilename, this.getClass().getClassLoader(), this.getClass()); Assert.assertNotNull(urlConn); final BufferedInputStream bis = new BufferedInputStream( urlConn.getInputStream() ); final ByteBuffer bb; @@ -111,16 +193,16 @@ public class TestIOUtil01 extends SingletonJunitCase { } @Test - public void testCopyStream03Buffer() throws IOException { + public void test13CopyStream03Buffer() throws IOException { final String tfilename2 = "./test2.bin" ; - final URLConnection urlConn1 = IOUtil.getResource(this.getClass(), tfilename); + final URLConnection urlConn1 = IOUtil.getResource(tfilename, this.getClass().getClassLoader(), this.getClass()); Assert.assertNotNull(urlConn1); final File file2 = new File(tfilename2); file2.deleteOnExit(); try { IOUtil.copyURLConn2File(urlConn1, file2); - final URLConnection urlConn2 = IOUtil.getResource(this.getClass(), tfilename2); + final URLConnection urlConn2 = IOUtil.getResource(tfilename2, this.getClass().getClassLoader(), this.getClass()); Assert.assertNotNull(urlConn2); final BufferedInputStream bis = new BufferedInputStream( urlConn2.getInputStream() ); diff --git a/src/junit/com/jogamp/common/util/TestPlatform01.java b/src/junit/com/jogamp/common/util/TestPlatform01.java index 6f1fe0e..bf3e79d 100644 --- a/src/junit/com/jogamp/common/util/TestPlatform01.java +++ b/src/junit/com/jogamp/common/util/TestPlatform01.java @@ -44,7 +44,7 @@ public class TestPlatform01 extends SingletonJunitCase { @Test public void testInfo00() { System.err.println(); - System.err.println(); + System.err.print(Platform.getNewline()); System.err.println("OS name/type: "+Platform.getOSName()+", "+Platform.getOSType()); System.err.println("OS version: "+Platform.getOSVersion()+", "+Platform.getOSVersionNumber()); System.err.println(); diff --git a/src/junit/com/jogamp/common/util/TestRunnableTask01.java b/src/junit/com/jogamp/common/util/TestRunnableTask01.java index 76c2d2a..6fd8f19 100644 --- a/src/junit/com/jogamp/common/util/TestRunnableTask01.java +++ b/src/junit/com/jogamp/common/util/TestRunnableTask01.java @@ -60,7 +60,7 @@ public class TestRunnableTask01 extends SingletonJunitCase { System.err.println("BB.0: "+syncObject); synchronized (syncObject) { System.err.println("BB.1: "+syncObject); - new Thread(clientAction, Thread.currentThread().getName()+"-clientAction").start(); + new InterruptSource.Thread(null, clientAction, Thread.currentThread().getName()+"-clientAction").start(); try { System.err.println("BB.2"); syncObject.wait(); @@ -88,7 +88,7 @@ public class TestRunnableTask01 extends SingletonJunitCase { System.err.println("BB.0: "+rTask.getSyncObject()); synchronized (rTask.getSyncObject()) { System.err.println("BB.1: "+rTask.getSyncObject()); - new Thread(rTask, Thread.currentThread().getName()+"-clientAction").start(); + new InterruptSource.Thread(null, rTask, Thread.currentThread().getName()+"-clientAction").start(); try { System.err.println("BB.2"); rTask.getSyncObject().wait(); diff --git a/src/junit/com/jogamp/common/util/TestVersionSemantics.java b/src/junit/com/jogamp/common/util/TestVersionSemantics.java index 6faa9a6..8e36684 100644 --- a/src/junit/com/jogamp/common/util/TestVersionSemantics.java +++ b/src/junit/com/jogamp/common/util/TestVersionSemantics.java @@ -66,9 +66,6 @@ import com.jogamp.junit.util.VersionSemanticsUtil; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestVersionSemantics extends SingletonJunitCase { static final String jarFile = "gluegen-rt.jar"; - static final VersionNumberString preVersionNumber = new VersionNumberString("2.2.0"); - static final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.NON_BACKWARD_COMPATIBLE; - // static final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER; static final DiffCriteria diffCriteria = new SimpleDiffCriteria(); // static final DiffCriteria diffCriteria = new PublicDiffCriteria(); @@ -76,22 +73,55 @@ public class TestVersionSemantics extends SingletonJunitCase { static final JogampVersion curVersion = GlueGenVersion.getInstance(); static final VersionNumberString curVersionNumber = new VersionNumberString(curVersion.getImplementationVersion()); - static final Set<String> excludes; + static final Set<String> excludesDefault; static { - excludes = new HashSet<String>(); - excludes.add("^\\Qjogamp/\\E.*$"); + excludesDefault = new HashSet<String>(); + excludesDefault.add("^\\Qjogamp/\\E.*$"); } @Test - public void testVersionLatest() throws IllegalArgumentException, IOException, URISyntaxException { + public void testVersionV220V221() throws IllegalArgumentException, IOException, URISyntaxException { + testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.2.0", "2.2.1", excludesDefault); + } + + @Test + public void testVersionV221V230() throws IllegalArgumentException, IOException, URISyntaxException { + testVersions(diffCriteria, Delta.CompatibilityType.NON_BACKWARD_COMPATIBLE, "2.2.1", "2.3.0", excludesDefault); + } + + @Test + public void testVersionV230V232() throws IllegalArgumentException, IOException, URISyntaxException { + testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_BINARY, "2.3.0", "2.3.2", excludesDefault); + } + + void testVersions(final DiffCriteria diffCriteria, final Delta.CompatibilityType expectedCompatibilityType, + final String v1, final String v2, final Set<String> excludes) + throws IllegalArgumentException, IOException, URISyntaxException { + final VersionNumberString preVersionNumber = new VersionNumberString(v1); + final File previousJar = new File("lib/v"+v1+"/"+jarFile); + + final VersionNumberString curVersionNumber = new VersionNumberString(v2); + final File currentJar = new File("lib/v"+v2+"/"+jarFile); + + VersionSemanticsUtil.testVersion(diffCriteria, expectedCompatibilityType, + previousJar, preVersionNumber, + currentJar, curVersionNumber, excludes); + } + + @Test + public void testVersionV232V24x() throws IllegalArgumentException, IOException, URISyntaxException { + final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.NON_BACKWARD_COMPATIBLE; + // final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER; + // final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.BACKWARD_COMPATIBLE_BINARY; + final VersionNumberString preVersionNumber = new VersionNumberString("2.3.2"); final File previousJar = new File("lib/v"+preVersionNumber.getVersionString()+"/"+jarFile); final ClassLoader currentCL = TestVersionSemantics.class.getClassLoader(); VersionSemanticsUtil.testVersion(diffCriteria, expectedCompatibilityType, previousJar, preVersionNumber, - curVersion.getClass(), currentCL, curVersionNumber, excludes); + curVersion.getClass(), currentCL, curVersionNumber, excludesDefault); } public static void main(final String args[]) throws IOException { diff --git a/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java b/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java index 4508f94..7455618 100644 --- a/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java +++ b/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java @@ -38,6 +38,7 @@ import org.junit.Assert; import org.junit.Test; import com.jogamp.common.os.Platform; +import com.jogamp.common.util.InterruptSource; import com.jogamp.junit.util.SingletonJunitCase; import org.junit.FixMethodOrder; @@ -170,7 +171,7 @@ public class TestRecursiveLock01 extends SingletonJunitCase { public final void action2Deferred(final int l, final YieldMode yieldMode) { final Action2 action2 = new Action2(l, yieldMode); - new Thread(action2, Thread.currentThread().getName()+"-deferred").start(); + new InterruptSource.Thread(null, action2, Thread.currentThread().getName()+"-deferred").start(); } public final void lock() { @@ -296,14 +297,14 @@ public class TestRecursiveLock01 extends SingletonJunitCase { final long t0 = System.currentTimeMillis(); final LockedObject lo = new LockedObject(implType, fair); final LockedObjectRunner[] runners = new LockedObjectRunner[threadNum]; - final Thread[] threads = new Thread[threadNum]; + final InterruptSource.Thread[] threads = new InterruptSource.Thread[threadNum]; int i; for(i=0; i<threadNum; i++) { runners[i] = new LockedObjectRunner1(lo, loops, iloops, yieldMode); // String name = Thread.currentThread().getName()+"-ActionThread-"+i+"_of_"+threadNum; final String name = "ActionThread-"+i+"_of_"+threadNum; - threads[i] = new Thread( runners[i], name ); + threads[i] = new InterruptSource.Thread( null, runners[i], name ); threads[i].start(); } diff --git a/src/junit/com/jogamp/common/util/locks/TestRecursiveThreadGroupLock01.java b/src/junit/com/jogamp/common/util/locks/TestRecursiveThreadGroupLock01.java index e35d146..30a0274 100644 --- a/src/junit/com/jogamp/common/util/locks/TestRecursiveThreadGroupLock01.java +++ b/src/junit/com/jogamp/common/util/locks/TestRecursiveThreadGroupLock01.java @@ -34,6 +34,7 @@ import org.junit.Assert; import org.junit.Test; import com.jogamp.common.os.Platform; +import com.jogamp.common.util.InterruptSource; import com.jogamp.junit.util.SingletonJunitCase; import org.junit.FixMethodOrder; @@ -239,15 +240,15 @@ public class TestRecursiveThreadGroupLock01 extends SingletonJunitCase { final LockedObject lo = new LockedObject(); final LockedObjectRunner[] concurrentRunners = new LockedObjectRunner[concurrentThreadNum]; final LockedObjectRunner[] slaveRunners = new LockedObjectRunner[slaveThreadNum]; - final Thread[] concurrentThreads = new Thread[concurrentThreadNum]; - final Thread[] slaveThreads = new Thread[slaveThreadNum]; - final Thread[] noCoOwnerThreads = new Thread[0]; + final InterruptSource.Thread[] concurrentThreads = new InterruptSource.Thread[concurrentThreadNum]; + final InterruptSource.Thread[] slaveThreads = new InterruptSource.Thread[slaveThreadNum]; + final InterruptSource.Thread[] noCoOwnerThreads = new InterruptSource.Thread[0]; int i; for(i=0; i<slaveThreadNum; i++) { slaveRunners[i] = new LockedObjectRunner1(" ", "s"+i, lo, loops, mark, yieldMode); final String name = "ActionThread-Slaves-"+i+"_of_"+slaveThreadNum; - slaveThreads[i] = new Thread( slaveRunners[i], name ); + slaveThreads[i] = new InterruptSource.Thread( null, slaveRunners[i], name ); } for(i=0; i<concurrentThreadNum; i++) { String name; @@ -258,7 +259,7 @@ public class TestRecursiveThreadGroupLock01 extends SingletonJunitCase { concurrentRunners[i] = new LockedObjectRunner1(" ", "O"+i, lo, noCoOwnerThreads, loops, mark, yieldMode); name = "ActionThread-Others-"+i+"_of_"+concurrentThreadNum; } - concurrentThreads[i] = new Thread( concurrentRunners[i], name ); + concurrentThreads[i] = new InterruptSource.Thread( null, concurrentRunners[i], name ); concurrentThreads[i].start(); if(i==0) { // master thread w/ slaves shall start first diff --git a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java index b018a79..ce0087a 100644 --- a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java +++ b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java @@ -35,10 +35,12 @@ import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.junit.util.JunitTracer; import com.jogamp.junit.util.SingletonJunitCase; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestSingletonServerSocket00 { +public class TestSingletonServerSocket00 extends JunitTracer { public static final long SINGLE_INSTANCE_LOCK_TO = SingletonJunitCase.SINGLE_INSTANCE_LOCK_TO; public static final long SINGLE_INSTANCE_LOCK_POLL = 100; // poll every 100ms @@ -69,7 +71,7 @@ public class TestSingletonServerSocket00 { } private Thread startLockUnlockOffThread(final int i) { - final Thread t = new Thread(new Runnable() { + final Thread t = new InterruptSource.Thread(null, new Runnable() { public void run() { final SingletonInstance myLock = SingletonInstance.createServerSocket(10, SingletonJunitCase.SINGLE_INSTANCE_LOCK_PORT); System.err.println(Thread.currentThread().getName()+" LOCK try .."); diff --git a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java index b37e600..c637865 100644 --- a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java +++ b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java @@ -35,10 +35,11 @@ import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.junit.util.JunitTracer; import com.jogamp.junit.util.SingletonJunitCase; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestSingletonServerSocket01 { +public class TestSingletonServerSocket01 extends JunitTracer { private static volatile SingletonInstance singletonInstance; @BeforeClass diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java index 3b1857d..db8c157 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java @@ -65,21 +65,25 @@ public class BaseClass extends SingletonJunitCase { Assert.assertNotNull(ifName+" does not exist", clazzIf); Assert.assertNotNull(implName+" does not exist", clazzImpl); - Assert.assertEquals(1, clazzIf.getDeclaredField("CONSTANT_ONE").get(null)); + Assert.assertNotNull(clazzImpl.getDeclaredMethod("nopTest")); final Object obj = clazzImpl.newInstance(); Assert.assertTrue("Not of type "+ifName, clazzIf.isAssignableFrom(obj.getClass())); - Assert.assertTrue("Not of type com.jogamp.gluegen.test.junit.generation.Bindingtest1", (obj instanceof com.jogamp.gluegen.test.junit.generation.Bindingtest1)); + Assert.assertTrue("Not of type com.jogamp.gluegen.test.junit.generation.Bindingtest1", + (obj instanceof com.jogamp.gluegen.test.junit.generation.Bindingtest1)); } + public static final float EPSILON = 1.1920929E-7f; // Float.MIN_VALUE == 1.4e-45f ; double EPSILON 2.220446049250313E-16d + /** * Verifies if all generated method signatures are completed, * ie a compilation only coverage test without functional tests. */ public void chapter__TestCoverageSignature(final Bindingtest1 binding) throws Exception { - int i; + int i = 0; final long context = 0; LongBuffer lb=null; + ByteBuffer bb=null; final IntBuffer ib=null; final long[] larray = null; final int larray_offset = 0; @@ -89,10 +93,42 @@ public class BaseClass extends SingletonJunitCase { final int iarray_offset = 0; long result = 0; long l = result; + ShortBlob sb = null; + Int32Struct i32s = null; + AnonBlob ab = null; + PointerBuffer pb=null; { - final ByteBuffer bb = binding.createAPtrBlob(); - PointerBuffer pb = safeByteBuffer2PointerBuffer(bb, 1); + l = binding.testXID(l); + l = binding.testXID_2(l); + + bb = binding.testAnonBuffer(bb); + + sb = binding.testShortBlob(sb); + sb = binding.testLPShortBlob0(sb); + sb = binding.testLPShortBlob1(sb); + sb = binding.testLPShortBlob2(sb); + sb = binding.testLPShortBlob3(sb); + sb = binding.testShortBlobL1(sb); + sb = binding.testShortBlobL2(sb); + + i32s = binding.testInt32Struct(i32s); + + ab = binding.testCreateAnonBlob(); + binding.testDestroyAnonBlob(ab); + + l = binding.testCreateAnonBlob2(); + binding.testDestroyAnonBlob2(l); + + lb = binding.testFooPtr(larray, 0); + lb = binding.testFooPtr(lb); + + i = binding.testDelegate(i); + } + + { + bb = binding.createAPtrBlob(); + pb = safeByteBuffer2PointerBuffer(bb, 1); long bb2A = binding.getAPtrAddress(bb); bb2A = bb2A - 0; // avoid warning @@ -119,9 +155,6 @@ public class BaseClass extends SingletonJunitCase { binding.releaseAPtrBlob(bb); } - final ByteBuffer bb=null; - PointerBuffer pb=null; - result = binding.arrayTestInt32(context, ib); result = binding.arrayTestInt32(context, iarray, iarray_offset); @@ -177,6 +210,185 @@ public class BaseClass extends SingletonJunitCase { l = binding.typeTestUIntPtrT(l, l); } + /** + * Verifies if all generated static constant values are completed, + * and whether their value is as expected! + * <p> + * Covers all enumerates and defines. + * </p> + */ + public void chapter01TestStaticConstants(final Bindingtest1 binding) throws Exception { + // Plain vanilla CPP constants + Assert.assertEquals( 1, Bindingtest1.CONSTANT_ONE); + Assert.assertEquals( 8, Bindingtest1.ARRAY_SIZE); + Assert.assertEquals(1234, Bindingtest1.DEFINE_01); + + // Enums + Assert.assertEquals( 1, Bindingtest1.LI); + Assert.assertEquals( 3, Bindingtest1.LO); + Assert.assertEquals( 2, Bindingtest1.LU); + Assert.assertEquals( 1, Bindingtest1.MI); + Assert.assertEquals( 3, Bindingtest1.MO); + Assert.assertEquals( 2, Bindingtest1.MU); + Assert.assertEquals( 0, Bindingtest1.ZERO); + Assert.assertEquals( 1, Bindingtest1.ONE); + Assert.assertEquals( 2, Bindingtest1.TWO); + Assert.assertEquals( 3, Bindingtest1.THREE); + + // CPP Macro Expansion! + Assert.assertEquals( 1, Bindingtest1.NUMBER_ONE); + Assert.assertEquals( 2, Bindingtest1.NUMBER_TWO); + Assert.assertEquals( 4, Bindingtest1.NUMBER_FOUR); + Assert.assertEquals( 5, Bindingtest1.NUMBER_FIVE); + Assert.assertEquals( 8, Bindingtest1.NUMBER_EIGHT); + Assert.assertEquals( 9, Bindingtest1.NUMBER_NINE); + Assert.assertEquals( 10, Bindingtest1.NUMBER_TEN); + + // Enum Constant Expressions! + Assert.assertEquals( 1, Bindingtest1.ENUM_NUM_ONE); + Assert.assertEquals( 2, Bindingtest1.ENUM_NUM_TWO); + Assert.assertEquals( 3, Bindingtest1.ENUM_NUM_THREE); + Assert.assertEquals( 4, Bindingtest1.ENUM_NUM_FOUR); + Assert.assertEquals( 5, Bindingtest1.ENUM_NUM_FIVE); + Assert.assertEquals( 8, Bindingtest1.ENUM_NUM_EIGHT); + Assert.assertEquals( 9, Bindingtest1.ENUM_NUM_NINE); + Assert.assertEquals( 10, Bindingtest1.ENUM_NUM_TEN); + + // Integer 32bit (int / enum) + final int ENUM_I0 = Bindingtest1.ENUM_I0; + final int ENUM_I1 = Bindingtest1.ENUM_I1; + final int ENUM_I2 = Bindingtest1.ENUM_I2; + final int ENUM_I3 = Bindingtest1.ENUM_I3; + final int ENUM_I4 = -Bindingtest1.ENUM_I4; + final int ENUM_I5 = -Bindingtest1.ENUM_I5; + final int ENUM_I6 = -Bindingtest1.ENUM_I6; + final int ENUM_I7 = Bindingtest1.ENUM_I7; + final int ENUM_I8 = Bindingtest1.ENUM_I8; + final int ENUM_I9 = Bindingtest1.ENUM_I9; + final int ENUM_IA = Bindingtest1.ENUM_IA; + final int ENUM_IB = Bindingtest1.ENUM_IB; + final int ENUM_IX = Bindingtest1.ENUM_IX; + int iexp = 10; + Assert.assertEquals(iexp++, ENUM_I0); + Assert.assertEquals(iexp++, ENUM_I1); + Assert.assertEquals(iexp++, ENUM_I2); + Assert.assertEquals(iexp++, ENUM_I3); + Assert.assertEquals(iexp++, ENUM_I4); + Assert.assertEquals(iexp++, ENUM_I5); + Assert.assertEquals(iexp++, ENUM_I6); + Assert.assertEquals(iexp++, ENUM_I7); + Assert.assertEquals(iexp++, ENUM_I8); + Assert.assertEquals(iexp++, ENUM_I9); + Assert.assertEquals(iexp++, ENUM_IA); + Assert.assertEquals(iexp++, ENUM_IB); + Assert.assertEquals(0xffffffff, ENUM_IX); + + // Integer 32bit (int / define) + final int CL_INT_I0 = Bindingtest1.CL_INT_I0; + final int CL_INT_I1 = Bindingtest1.CL_INT_I1; + final int CL_INT_I2 = Bindingtest1.CL_INT_I2; + final int CL_INT_I3 = Bindingtest1.CL_INT_I3; + final int CL_INT_I4 = -Bindingtest1.CL_INT_I4; + final int CL_INT_I5 = -Bindingtest1.CL_INT_I5; + final int CL_INT_I6 = -Bindingtest1.CL_INT_I6; + final int CL_INT_I7 = -Bindingtest1.CL_INT_I7; + final int CL_INT_I8 = Bindingtest1.CL_INT_I8; + final int CL_INT_I9 = Bindingtest1.CL_INT_I9; + final int CL_INT_IA = Bindingtest1.CL_INT_IA; + final int CL_INT_IB = Bindingtest1.CL_INT_IB; + final int CL_INT_IX = Bindingtest1.CL_INT_IX; + iexp = 10; + Assert.assertEquals(iexp++, CL_INT_I0); + Assert.assertEquals(iexp++, CL_INT_I1); + Assert.assertEquals(iexp++, CL_INT_I2); + Assert.assertEquals(iexp++, CL_INT_I3); + Assert.assertEquals(iexp++, CL_INT_I4); + Assert.assertEquals(iexp++, CL_INT_I5); + Assert.assertEquals(iexp++, CL_INT_I6); + Assert.assertEquals(iexp++, CL_INT_I7); + Assert.assertEquals(iexp++, CL_INT_I8); + Assert.assertEquals(iexp++, CL_INT_I9); + Assert.assertEquals(iexp++, CL_INT_IA); + Assert.assertEquals(iexp++, CL_INT_IB); + Assert.assertEquals(0xffffffff, CL_INT_IX); + + // Integer 64bit (long / define ) + final long CL_LNG_L0 = Bindingtest1.CL_LNG_L0; + final long CL_LNG_L1 = Bindingtest1.CL_LNG_L1; + final long CL_LNG_L2 = Bindingtest1.CL_LNG_L2; + final long CL_LNG_L3 = Bindingtest1.CL_LNG_L3; + final long CL_LNG_L4 = -Bindingtest1.CL_LNG_L4; + final long CL_LNG_L5 = -Bindingtest1.CL_LNG_L5; + final long CL_LNG_L6 = -Bindingtest1.CL_LNG_L6; + final long CL_LNG_L7 = -Bindingtest1.CL_LNG_L7; + final long CL_LNG_L8 = Bindingtest1.CL_LNG_L8; + final long CL_LNG_L9 = Bindingtest1.CL_LNG_L9; + final long CL_LNG_LA = Bindingtest1.CL_LNG_LA; + final long CL_LNG_LB = Bindingtest1.CL_LNG_LB; + final long CL_LNG_LX = Bindingtest1.CL_LNG_LX; + long lexp = 2147483648L; + Assert.assertEquals(lexp++, CL_LNG_L0); + Assert.assertEquals(lexp++, CL_LNG_L1); + Assert.assertEquals(lexp++, CL_LNG_L2); + Assert.assertEquals(lexp++, CL_LNG_L3); + Assert.assertEquals(lexp++, CL_LNG_L4); + Assert.assertEquals(lexp++, CL_LNG_L5); + Assert.assertEquals(lexp++, CL_LNG_L6); + Assert.assertEquals(lexp++, CL_LNG_L7); + Assert.assertEquals(lexp++, CL_LNG_L8); + Assert.assertEquals(lexp++, CL_LNG_L9); + Assert.assertEquals(lexp++, CL_LNG_LA); + Assert.assertEquals(lexp++, CL_LNG_LB); + Assert.assertEquals(0xffffffffffffffffL, CL_LNG_LX); + + // Floating point hexadecimals + final float CL_FLT_A0 = Bindingtest1.CL_FLT_A0; + final float CL_FLT_A1 = Bindingtest1.CL_FLT_A1; + final float CL_FLT_A2 = Bindingtest1.CL_FLT_A2; + final float CL_FLT_A3 = Bindingtest1.CL_FLT_A3; + final float CL_FLT_A4 = Bindingtest1.CL_FLT_A4; + final float CL_FLT_A5 = Bindingtest1.CL_FLT_A5; + final float CL_FLT_A6 = Bindingtest1.CL_FLT_A6; + final float CL_FLT_A7 = Bindingtest1.CL_FLT_A7; + Assert.assertEquals( 0x1.p127f, CL_FLT_A0, EPSILON); + Assert.assertEquals( 0x1.p+127F, CL_FLT_A1, EPSILON); + Assert.assertEquals( 0x1.p-127f, CL_FLT_A2, EPSILON); + Assert.assertEquals( -0.1f, CL_FLT_A3, EPSILON); + Assert.assertEquals( 0.2f, CL_FLT_A4, EPSILON); + Assert.assertEquals( 0.3f, CL_FLT_A5, EPSILON); + Assert.assertEquals( 0.4f, CL_FLT_A6, EPSILON); + Assert.assertEquals( 1.0f, CL_FLT_A7, EPSILON); + + final float CL_FLT_EPSILON = Bindingtest1.CL_FLT_EPSILON; + final double CL_FLT_MAX= Bindingtest1.CL_FLT_MAX; + final double CL_FLT_MIN = Bindingtest1.CL_FLT_MIN; + Assert.assertEquals( 0x1.0p-23f, CL_FLT_EPSILON, EPSILON); + Assert.assertEquals( 0x1.fffffep127f, CL_FLT_MAX, EPSILON); + Assert.assertEquals( 0x1.0p-126f, CL_FLT_MIN, EPSILON); + + final double CL_DBL_B0 = Bindingtest1.CL_DBL_B0; + final double CL_DBL_B1 = Bindingtest1.CL_DBL_B1; + final double CL_DBL_B2 = Bindingtest1.CL_DBL_B2; + final double CL_DBL_B3 = Bindingtest1.CL_DBL_B3; + final double CL_DBL_B4 = Bindingtest1.CL_DBL_B4; + final double CL_DBL_B5 = Bindingtest1.CL_DBL_B5; + final double CL_DBL_B6 = Bindingtest1.CL_DBL_B6; + Assert.assertEquals( 0x1.p127d, CL_DBL_B0, EPSILON); + Assert.assertEquals( 0x1.p+127D, CL_DBL_B1, EPSILON); + Assert.assertEquals( 0x1.p-127d, CL_DBL_B2, EPSILON); + Assert.assertEquals( -0.1, CL_DBL_B3, EPSILON); + Assert.assertEquals( 0.2, CL_DBL_B4, EPSILON); + Assert.assertEquals( 0.3, CL_DBL_B5, EPSILON); + Assert.assertEquals( 3.5e+38, CL_DBL_B6, EPSILON); + + final float CL_DBL_EPSILON = Bindingtest1.CL_DBL_EPSILON; + final double CL_DBL_MAX= Bindingtest1.CL_DBL_MAX; + final double CL_DBL_MIN = Bindingtest1.CL_DBL_MIN; + Assert.assertEquals( 0x1.0p-52f, CL_DBL_EPSILON, EPSILON); + Assert.assertEquals( 0x1.fffffffffffffp1023, CL_DBL_MAX, EPSILON); + Assert.assertEquals( 0x1.0p-1022, CL_DBL_MIN, EPSILON); + } + ByteBuffer newByteBuffer(final int size, final boolean direct) { if(direct) { final ByteBuffer bb = Buffers.newDirectByteBuffer(size); @@ -629,6 +841,66 @@ public class BaseClass extends SingletonJunitCase { Assert.assertTrue("Wrong result: 0x"+Long.toHexString(pb.get(i))+"+1 != 0x"+Long.toHexString(pb2.get(i)), (pb.get(i)+1)==pb2.get(i)); } } + + { + final long l0 = 0xAAFFEE; + final long l1 = binding.testXID(l0); + final long l2 = binding.testXID_2(l0); + Assert.assertEquals(l0, l1); + Assert.assertEquals(l0, l2); + + final ByteBuffer bb = Buffers.newDirectByteBuffer(PointerBuffer.ELEMENT_SIZE); + for(int j=0; j<bb.limit(); j++) { + bb.put(j, (byte)(0xAA+j)); + } + final ByteBuffer bbOut = binding.testAnonBuffer(bb); + Assert.assertEquals(bb, bbOut); + + final ShortBlob sb = ShortBlob.create(); + sb.setB1((byte)0xAA); + sb.setB2((byte)0xEE); + final ShortBlob sb_ = binding.testShortBlob(sb); + final ShortBlob sb0 = binding.testLPShortBlob0(sb); + final ShortBlob sb1 = binding.testLPShortBlob1(sb); + final ShortBlob sb2 = binding.testLPShortBlob2(sb); + final ShortBlob sb3 = binding.testLPShortBlob3(sb); + final ShortBlob sb4 = binding.testShortBlobL1(sb); + final ShortBlob sb5 = binding.testShortBlobL2(sb); + Assert.assertEquals(sb.getBuffer(), sb_.getBuffer()); + Assert.assertEquals(sb.getBuffer(), sb0.getBuffer()); + Assert.assertEquals(sb.getBuffer(), sb1.getBuffer()); + Assert.assertEquals(sb.getBuffer(), sb2.getBuffer()); + Assert.assertEquals(sb.getBuffer(), sb3.getBuffer()); + Assert.assertEquals(sb.getBuffer(), sb4.getBuffer()); + Assert.assertEquals(sb.getBuffer(), sb5.getBuffer()); + + final Int32Struct i32s = Int32Struct.create(); + i32s.setB1((byte)0x02); + i32s.setB2((byte)0x12); + i32s.setB3((byte)0x22); + i32s.setB4((byte)0x32); + final Int32Struct i32s0 = binding.testInt32Struct(i32s); + Assert.assertEquals(i32s.getBuffer(), i32s0.getBuffer()); + + final AnonBlob ab = binding.testCreateAnonBlob(); + binding.testDestroyAnonBlob(ab); + + final long ab2 = binding.testCreateAnonBlob2(); + binding.testDestroyAnonBlob2(ab2); + + final long[] foo = new long[] { 0x1122334455667788L }; + final LongBuffer fooLB = Buffers.newDirectLongBuffer(foo); + final LongBuffer foo1Out = binding.testFooPtr(fooLB); + Assert.assertEquals(fooLB, foo1Out); + final LongBuffer foo2Out = binding.testFooPtr(foo, 0); + Assert.assertEquals(fooLB, foo2Out); + } + + { + i=41; + final int iRes = binding.testDelegate(i); + Assert.assertEquals(i+1, iRes); + } } public void chapter04TestPointerBuffer(final Bindingtest1 binding) throws Exception { @@ -1115,22 +1387,20 @@ public class BaseClass extends SingletonJunitCase { } } - public static final float EPSILON = 1.1920929E-7f; // Float.MIN_VALUE == 1.4e-45f ; double EPSILON 2.220446049250313E-16d - /** Test array and pointer bindings of structs */ public void chapter12TestStructArrayModelConst(final Bindingtest1 binding) throws Exception { final TK_ModelConst model = binding.createModelConst(); Assert.assertEquals(3, model.getIntxxPointerCustomLenVal()); Assert.assertEquals(3, model.getInt32PointerCustomLenVal()); - Assert.assertEquals(3, model.getInt32ArrayFixedLenArrayLength()); - Assert.assertEquals(3, model.getStructArrayFixedLenArrayLength()); + Assert.assertEquals(3, TK_ModelConst.getInt32ArrayFixedLenArrayLength()); + Assert.assertEquals(3, TK_ModelConst.getStructArrayFixedLenArrayLength()); Assert.assertEquals(3, model.getStructPointerCustomLenVal()); // field: int32ArrayFixedLen // CType['int32_t *', size [fixed false, lnx64 12], [array*1]], with array length of 3 { - final int size = model.getInt32ArrayFixedLenArrayLength(); + final int size = TK_ModelConst.getInt32ArrayFixedLenArrayLength(); final int[] all = model.getInt32ArrayFixedLen(0, new int[size]); final IntBuffer allB = model.getInt32ArrayFixedLen(); Assert.assertEquals(size, allB.limit()); @@ -1170,7 +1440,7 @@ public class BaseClass extends SingletonJunitCase { // field: mat4x4 // CType['float * *', size [fixed false, lnx64 64], [array*2]], with array length of <code>4*4</code> */ { - Assert.assertEquals(4*4, model.getMat4x4ArrayLength()); + Assert.assertEquals(4*4, TK_ModelConst.getMat4x4ArrayLength()); final FloatBuffer mat4x4 = model.getMat4x4(); Assert.assertEquals(4*4, mat4x4.limit()); for(int i=0; i<4; i++) { @@ -1185,7 +1455,7 @@ public class BaseClass extends SingletonJunitCase { // field: structArrayFixedLen // field: CType['TK_Dimension *', size [fixed false, lnx64 48], [array*1]], with array length of 3 { - final int size = model.getStructArrayFixedLenArrayLength(); + final int size = TK_ModelConst.getStructArrayFixedLenArrayLength(); final TK_Dimension[] all = model.getStructArrayFixedLen(0, new TK_Dimension[size]); for(int i=0; i<size; i++) { Assert.assertEquals(51 + i * 10, all[i].getX()); @@ -1236,7 +1506,7 @@ public class BaseClass extends SingletonJunitCase { assertAPTR(surfaceContext, model.getCtx()); { - Assert.assertEquals(12, model.getModelNameArrayFixedLenArrayLength()); + Assert.assertEquals(12, TK_ModelConst.getModelNameArrayFixedLenArrayLength()); final ByteBuffer bb = model.getModelNameArrayFixedLen(); Assert.assertEquals(12, bb.limit()); @@ -1296,14 +1566,14 @@ public class BaseClass extends SingletonJunitCase { Assert.assertEquals(3, model.getIntxxPointerCustomLenVal()); Assert.assertEquals(3, model.getInt32PointerCustomLenVal()); - Assert.assertEquals(3, model.getInt32ArrayFixedLenArrayLength()); - Assert.assertEquals(3, model.getStructArrayFixedLenArrayLength()); + Assert.assertEquals(3, TK_ModelMutable.getInt32ArrayFixedLenArrayLength()); + Assert.assertEquals(3, TK_ModelMutable.getStructArrayFixedLenArrayLength()); Assert.assertEquals(3, model.getStructPointerCustomLenVal()); // field: int32ArrayFixedLen // CType['int32_t *', size [fixed false, lnx64 12], [array*1]], with array length of 3 { - final int size = model.getInt32ArrayFixedLenArrayLength(); + final int size = TK_ModelMutable.getInt32ArrayFixedLenArrayLength(); { final int[] values = new int[] { 1, 2, 3 }; model.setInt32ArrayFixedLen(0, values); @@ -1385,7 +1655,7 @@ public class BaseClass extends SingletonJunitCase { model.setMat4x4(2*4, new float[] { 31, 32, 33, 34 } ); model.setMat4x4(3*4, new float[] { 41, 42, 43, 44 } ); - Assert.assertEquals(4*4, model.getMat4x4ArrayLength()); + Assert.assertEquals(4*4, TK_ModelMutable.getMat4x4ArrayLength()); final FloatBuffer mat4x4 = model.getMat4x4(); Assert.assertEquals(4*4, mat4x4.limit()); for(int i=0; i<4; i++) { @@ -1400,7 +1670,7 @@ public class BaseClass extends SingletonJunitCase { // field: structArrayFixedLen // field: CType['TK_Dimension *', size [fixed false, lnx64 48], [array*1]], with array length of 3 { - final int size = model.getStructArrayFixedLenArrayLength(); + final int size = TK_ModelMutable.getStructArrayFixedLenArrayLength(); { for(int i=0; i<size; i++) { final TK_Dimension d = TK_Dimension.create(); diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java index 9e961cb..5809acf 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java @@ -50,7 +50,7 @@ public class Test1p1JavaEmitter extends BaseClass { * Verifies loading of the new library. */ @BeforeClass - public static void chapter01TestLoadLibrary() throws Exception { + public static void chapter__TestLoadLibrary() throws Exception { BindingJNILibLoader.loadBindingtest1p1(); } @@ -58,7 +58,7 @@ public class Test1p1JavaEmitter extends BaseClass { * Verifies the existence and creation of the generated class. */ @Test - public void chapter02TestClassExist() throws Exception { + public void chapter00TestClassExist() throws Exception { testClassExist("test1p1"); } @@ -71,6 +71,18 @@ public class Test1p1JavaEmitter extends BaseClass { } /** + * Verifies if all generated static constant values are completed, + * and whether their value is as expected! + * <p> + * Covers all enumerates and defines. + * </p> + */ + @Test + public void chapter01TestStaticConstants() throws Exception { + chapter01TestStaticConstants(new Bindingtest1p1Impl()); + } + + /** * Verifies if all methods / signatures are properly generated, * can be invoked and functions. * This is a compilation (coverage) and runtime time (semantic) test. diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2LoadJNIAndImplLib.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2LoadJNIAndImplLib.java index b8adab0..e61c600 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2LoadJNIAndImplLib.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2LoadJNIAndImplLib.java @@ -46,9 +46,9 @@ public class Test1p2LoadJNIAndImplLib extends BaseClass { * Verifies loading of the new library. */ @BeforeClass - public static void chapter01TestLoadLibrary() throws Exception { + public static void chapter__TestLoadLibrary() throws Exception { BindingJNILibLoader.loadBindingtest1p2(); - dynamicLookupHelper = NativeLibrary.open("test1", Test1p2LoadJNIAndImplLib.class.getClassLoader(), true); + dynamicLookupHelper = NativeLibrary.open("test1", true, true, Test1p2LoadJNIAndImplLib.class.getClassLoader(), true); Assert.assertNotNull("NativeLibrary.open(test1) failed", dynamicLookupHelper); Bindingtest1p2Impl.resetProcAddressTable(dynamicLookupHelper); @@ -58,17 +58,16 @@ public class Test1p2LoadJNIAndImplLib extends BaseClass { * Verifies the existence and creation of the generated class. */ @Test - public void chapter02TestClassExist() throws Exception { + public void chapter00TestClassExist() throws Exception { testClassExist("test1p2"); } - @SuppressWarnings("unused") public static void main(final String args[]) throws Exception { if( true ) { - chapter01TestLoadLibrary(); + chapter__TestLoadLibrary(); final Test1p2LoadJNIAndImplLib tst = new Test1p2LoadJNIAndImplLib(); - tst.chapter02TestClassExist(); + tst.chapter00TestClassExist(); } else { final String tstname = Test1p2LoadJNIAndImplLib.class.getName(); org.junit.runner.JUnitCore.main(tstname); diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java index 49a1851..fa99915 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java @@ -30,6 +30,7 @@ package com.jogamp.gluegen.test.junit.generation; import java.io.IOException; +import com.jogamp.gluegen.test.junit.generation.impl.Bindingtest1p1Impl; import com.jogamp.gluegen.test.junit.generation.impl.Bindingtest1p2Impl; import com.jogamp.common.os.NativeLibrary; @@ -54,9 +55,9 @@ public class Test1p2ProcAddressEmitter extends BaseClass { * Verifies loading of the new library. */ @BeforeClass - public static void chapter01TestLoadLibrary() throws Exception { + public static void chapter__TestLoadLibrary() throws Exception { BindingJNILibLoader.loadBindingtest1p2(); - dynamicLookupHelper = NativeLibrary.open("test1", Test1p2ProcAddressEmitter.class.getClassLoader(), true); + dynamicLookupHelper = NativeLibrary.open("test1", false, false, Test1p2ProcAddressEmitter.class.getClassLoader(), true); Assert.assertNotNull("NativeLibrary.open(test1) failed", dynamicLookupHelper); Bindingtest1p2Impl.resetProcAddressTable(dynamicLookupHelper); @@ -66,7 +67,7 @@ public class Test1p2ProcAddressEmitter extends BaseClass { * Verifies the existence and creation of the generated class. */ @Test - public void chapter02TestClassExist() throws Exception { + public void chapter00TestClassExist() throws Exception { testClassExist("test1p2"); } @@ -79,6 +80,18 @@ public class Test1p2ProcAddressEmitter extends BaseClass { } /** + * Verifies if all generated static constant values are completed, + * and whether their value is as expected! + * <p> + * Covers all enumerates and defines. + * </p> + */ + @Test + public void chapter01TestStaticConstants() throws Exception { + chapter01TestStaticConstants(new Bindingtest1p2Impl()); + } + + /** * Verifies if all methods / signatures are properly generated, * can be invoked and functions. * This is a compilation (coverage) and runtime time (semantic) test. diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1-CustomJavaCode.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test1-CustomJavaCode.cfg new file mode 100644 index 0000000..8d8f650 --- /dev/null +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1-CustomJavaCode.cfg @@ -0,0 +1,7 @@ + +@Override +public int testDelegate(int v) { + return testDelegateOrigImpl(v) + 1; +} + + diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg index 8e41aae..6c516c2 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg @@ -32,10 +32,24 @@ ReturnValueCapacity typeTestAnonPointer ARRAY_SIZE * sizeof(MYAPIConfig) Opaque long MYAPIConfig Opaque boolean Bool +Opaque long XID +# For 'struct _AnonBlob2*', we need to drop 'struct' +Opaque long _AnonBlob2* + +Opaque long _Crazy*; +Opaque long ShortBlob.Cool + CustomCCode #include "test1.h" Opaque long TK_Context +RenameJavaSymbol DEFINE_01_EXT DEFINE_01 +RenameJavaSymbol testXID_EXT testXID + +DelegateImplementation testDelegate testDelegateOrigImpl +IncludeAs CustomJavaCode Bindingtest1p1Impl test1-CustomJavaCode.cfg +IncludeAs CustomJavaCode Bindingtest1p2Impl test1-CustomJavaCode.cfg + StructPackage TK_Dimension com.jogamp.gluegen.test.junit.generation EmitStruct TK_Dimension StructPackage TK_DimensionPair com.jogamp.gluegen.test.junit.generation @@ -96,6 +110,9 @@ Import java.nio.* Import java.util.* Import com.jogamp.common.os.* Import com.jogamp.common.nio.* +Import com.jogamp.gluegen.test.junit.generation.ShortBlob +Import com.jogamp.gluegen.test.junit.generation.Int32Struct +Import com.jogamp.gluegen.test.junit.generation.AnonBlob Import com.jogamp.gluegen.test.junit.generation.TK_Surface Import com.jogamp.gluegen.test.junit.generation.TK_Dimension Import com.jogamp.gluegen.test.junit.generation.TK_DimensionPair diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c index 9999274..0683600 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c @@ -8,10 +8,67 @@ #define DEBUG 1 +MYAPI XID MYAPIENTRY testXID(XID v) { + return v; +} +MYAPI XID_2 MYAPIENTRY testXID_2(XID_2 v) { + return v; +} +MYAPI AnonBuffer MYAPIENTRY testAnonBuffer(AnonBuffer v) { + return v; +} + +MYAPI const ShortBlob * MYAPIENTRY testShortBlob(const ShortBlob *v) { + return v; +} +MYAPI const LPShortBlob0 MYAPIENTRY testLPShortBlob0(const LPShortBlob0 v) { + return v; +} +MYAPI LPShortBlob1 MYAPIENTRY testLPShortBlob1(LPShortBlob1 v) { + return v; +} +MYAPI const LPShortBlob2 MYAPIENTRY testLPShortBlob2(const LPShortBlob2 v) { + return v; +} +MYAPI LPShortBlob3 MYAPIENTRY testLPShortBlob3(LPShortBlob3 v) { + return v; +} +MYAPI const ShortBlobL1 * MYAPIENTRY testShortBlobL1(const ShortBlobL1 * v) { + return v; +} +MYAPI ShortBlobL2 * MYAPIENTRY testShortBlobL2(ShortBlobL2 * v) { + return v; +} +MYAPI struct Int32Struct * MYAPIENTRY testInt32Struct(struct Int32Struct * v) { + return v; +} + +MYAPI AnonBlob MYAPIENTRY testCreateAnonBlob() { + return (AnonBlob) calloc(1, sizeof(char)); +} +MYAPI void MYAPIENTRY testDestroyAnonBlob(AnonBlob v) { + free(v); +} + +MYAPI struct _AnonBlob2 * MYAPIENTRY testCreateAnonBlob2() { + return (struct _AnonBlob2 *) calloc(1, sizeof(char)); +} +MYAPI void MYAPIENTRY testDestroyAnonBlob2(struct _AnonBlob2 * v) { + free(v); +} + +MYAPI foo_ptr MYAPIENTRY testFooPtr(foo_ptr v) { + return v; +} + MYAPI foo MYAPIENTRY nopTest() { return 42; } +MYAPI int32_t MYAPIENTRY testDelegate(int32_t v) { + return v; +} + /** * new blob sizeof(void*) filled w/ 0xDEADBEEF */ diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h index 67a8050..4896165 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h @@ -28,12 +28,166 @@ typedef int Bool; typedef uint64_t foo; +typedef foo * foo_ptr; typedef void * APtr1Type; typedef intptr_t APtr2Type; +typedef void * XID; // Opaque +typedef XID XID_2; // Opaque, due to XID +typedef void * AnonBuffer; // Non Opaque + +// typedef XID XID_2; // Duplicate w/ compatible type (ignored) - OpenSolaris: Native gcc error +// typedef int XID_2; // Duplicate w/ incompatible type ERROR + +#define CL_INT_I0 10 +#define CL_INT_I1 11u +#define CL_INT_I2 12U +#define CL_INT_I3 0x0d +#define CL_INT_I4 -14 +#define CL_INT_I5 -15u +#define CL_INT_I6 -16U +#define CL_INT_I7 -0x11U +#define CL_INT_I8 +18 +#define CL_INT_I9 +19u +#define CL_INT_IA +20U +#define CL_INT_IB +0x15u +#define CL_INT_IX 0xffffffffU + +enum CL_INT { ENUM_I0=10, ENUM_I1, ENUM_I2=+12U, ENUM_I3=0x0d, ENUM_I4=-14, ENUM_I5=-15u, ENUM_I6=-16U, ENUM_I7=0x11U, + ENUM_I8=+18, ENUM_I9=+19u, ENUM_IA, ENUM_IB=+0x15u, ENUM_IX=0xffffffffU }; + +#define CL_LNG_L0 2147483648 +#define CL_LNG_L1 0x80000001ul +#define CL_LNG_L2 2147483650UL +#define CL_LNG_L3 0x80000003l +#define CL_LNG_L4 -2147483652L +#define CL_LNG_L5 -2147483653ul +#define CL_LNG_L6 -2147483654lu +#define CL_LNG_L7 -0x80000007UL +#define CL_LNG_L8 +2147483656LU +#define CL_LNG_L9 +2147483657uL +#define CL_LNG_LA +2147483658lU +#define CL_LNG_LB +0x8000000BLu +#define CL_LNG_LX 0xffffffffffffffffUL + +#define CL_FLT_A0 0x1p127 +#define CL_FLT_A1 0x1p+127F +#define CL_FLT_A2 +0x1p-127f +#define CL_FLT_A3 -0.1 +#define CL_FLT_A4 0.2f +#define CL_FLT_A5 0.3F +#define CL_FLT_A6 .4 +#define CL_FLT_A7 1.0 + +#define CL_DBL_B0 0x1.p127d +#define CL_DBL_B1 0x1.p+127D +#define CL_DBL_B2 +0x1.p-127d +#define CL_DBL_B3 -0.1d +#define CL_DBL_B4 0.2D +#define CL_DBL_B5 .3D +#define CL_DBL_B6 3.5e+38 + +#define CL_FLT_MAX 0x1.fffffep127f +#define CL_FLT_MIN 0x1.0p-126f +#define CL_FLT_EPSILON 0x1.0p-23f + +#define CL_DBL_MAX 0x1.fffffffffffffp1023 +#define CL_DBL_MIN 0x1.0p-1022 +#define CL_DBL_EPSILON 0x1.0p-52 + +#define DEFINE_01 1234 +#define DEFINE_01 1234 // Duplicate w/ same value (ignored) +// #define DEFINE_01 1235 // Duplicate w/ diff value ERROR +#define DEFINE_01_EXT 1234 // Renamed Duplicate w/ same value (ignored) +// #define DEFINE_01_EXT 1235 // Renamed Duplicate w/ diff value ERROR +// #define DEFINE_01 1235 // Duplicate w/ diff value ERROR + +#define DEFINE_02 ( (int ) 3 ) +// #define DEFINE_02 ( (int ) 3 ) // Duplicate w/ same value ERROR (PCPP redefine) +// #define DEFINE_02 ( (int) 3 ) // Duplicate w/ diff value ERROR (PCPP redefine, then GlueGen) + +#define NUMBER_ONE CONSTANT_ONE +#define NUMBER_TWO ( NUMBER_ONE + NUMBER_ONE ) +#define NUMBER_FOUR ( NUMBER_ONE << NUMBER_TWO ) +#define NUMBER_FIVE ( ( CONSTANT_ONE << NUMBER_TWO ) + NUMBER_ONE ) +#define NUMBER_EIGHT ( NUMBER_TWO * NUMBER_TWO + ( NUMBER_ONE << NUMBER_TWO ) ) +#define NUMBER_NINE ( 2 * 2 + ( 1 << 2 ) + 1 ) +#define NUMBER_TEN ( NUMBER_EIGHT | NUMBER_TWO ) + +enum NumberOps { ENUM_NUM_ONE = CONSTANT_ONE, + ENUM_NUM_TWO = 1+1, + ENUM_NUM_THREE, + ENUM_NUM_FOUR = ( ENUM_NUM_ONE << ENUM_NUM_TWO ), + ENUM_NUM_FIVE = ( CONSTANT_ONE << ENUM_NUM_TWO ) + ENUM_NUM_ONE, + ENUM_NUM_EIGHT = ( ENUM_NUM_TWO * ENUM_NUM_TWO + ( ENUM_NUM_ONE << ENUM_NUM_TWO ) ), + ENUM_NUM_NINE = ( 2 * 2 + ( 1 << 2 ) + 1 ), + ENUM_NUM_TEN = ENUM_NUM_EIGHT | + ENUM_NUM_TWO + }; + +enum Lala { LI=1, LU, LO }; +// enum Lala { LI=1, LU, LO }; // Duplicate w/ same value (ignored, ERROR in native compilation) +// enum Lala { LI=1, LU=3, LO }; // Duplicate w/ diff value ERROR +// enum Lala { LI=1, LU, LO, LERROR }; // Duplicate w/ diff value ERROR + +typedef enum { MI=1, MU, MO } Momo; +// typedef enum { MI=1, MU, MO } Momo; // Duplicate w/ same value (ignored, ERROR in native compilation) +// typedef enum { MI=1, MU=3, MO } Momo; // Duplicate w/ diff value ERROR +// typedef enum { MI=1, MU, MO, MERR } Momo; // Duplicate w/ diff value ERROR + +struct _Crazy; + +typedef struct _ShortBlob { + uint8_t b1; + uint8_t b2; + struct _Crazy * Cool; // Opaque field! +} ShortBlob, ShortBlob2, *LPShortBlob0; // Aliased to 'ShortBlob' +typedef ShortBlob * LPShortBlob1; // Aliased to 'ShortBlob' +typedef ShortBlob2 * LPShortBlob2; // Aliased to 'ShortBlob' +typedef LPShortBlob1 LPShortBlob3; // Aliased to 'ShortBlob' +typedef ShortBlob ShortBlobL1; // Aliased to 'ShortBlob' +typedef ShortBlob2 ShortBlobL2; // Aliased to 'ShortBlob' + +struct Int32Struct { + uint8_t b1; + uint8_t b2; + uint8_t b3; + uint8_t b4; +}; + +typedef struct _AnonBlob * AnonBlob; // Anonymous-Struct, Non Opaque + +struct _AnonBlob2; // opaque: struct _AnonBlob2* + +MYAPI XID MYAPIENTRY testXID(XID v); +MYAPI XID MYAPIENTRY testXID(XID_2 v); // duplicate: shall be dropped +// MYAPI XID MYAPIENTRY testXID(int v); // duplicate w/ diff value ERROR +MYAPI XID MYAPIENTRY testXID_EXT(XID v); // renamed duplicate w/ compat value: shall be dropped +// MYAPI XID MYAPIENTRY testXID_EXT(int v); // renamed duplicate w/ diff value ERROR +MYAPI XID_2 MYAPIENTRY testXID_2(XID_2 v); +MYAPI AnonBuffer MYAPIENTRY testAnonBuffer(AnonBuffer v); +MYAPI const ShortBlob * MYAPIENTRY testShortBlob(const ShortBlob *v); +MYAPI const LPShortBlob0 MYAPIENTRY testLPShortBlob0(const LPShortBlob0 v); +MYAPI LPShortBlob1 MYAPIENTRY testLPShortBlob1(LPShortBlob1 v); +MYAPI const LPShortBlob2 MYAPIENTRY testLPShortBlob2(const LPShortBlob2 v); +MYAPI LPShortBlob3 MYAPIENTRY testLPShortBlob3(LPShortBlob3 v); +MYAPI const ShortBlobL1 * MYAPIENTRY testShortBlobL1(const ShortBlobL1 *v); +MYAPI ShortBlobL2 * MYAPIENTRY testShortBlobL2(ShortBlobL2 *v); +MYAPI struct Int32Struct * MYAPIENTRY testInt32Struct(struct Int32Struct * v); + +MYAPI AnonBlob MYAPIENTRY testCreateAnonBlob(); +MYAPI void MYAPIENTRY testDestroyAnonBlob(AnonBlob v); + +MYAPI struct _AnonBlob2 * MYAPIENTRY testCreateAnonBlob2(); +MYAPI void MYAPIENTRY testDestroyAnonBlob2(struct _AnonBlob2 * v); + +MYAPI foo_ptr MYAPIENTRY testFooPtr(foo_ptr v); + /** Returns 42 */ MYAPI foo MYAPIENTRY nopTest(); +MYAPI int32_t MYAPIENTRY testDelegate(int32_t v); + // // Different pointer type tests .. // @@ -131,7 +285,7 @@ MYAPI int MYAPIENTRY intArrayCopy(int * dest, const int * src, int num); /** Increases the elements by 1, and returns the sum MYAPI int MYAPIENTRY intArrayWrite(int * * ints, int num); */ -typedef struct __MYAPIConfig * MYAPIConfig; +typedef struct __MYAPIConfig * MYAPIConfig; // anonymous-struct opaque /** Returns the passed MYAPIConfig incremented by 1 */ MYAPI MYAPIConfig MYAPIENTRY typeTestAnonSingle(const MYAPIConfig a); @@ -172,7 +326,7 @@ typedef struct { int32_t height; } TK_Dimension; -typedef struct _TK_Context * TK_Context; // anonymous +typedef struct _TK_Context * TK_Context; // anonymous-struct opaque typedef struct { TK_Context ctx; @@ -246,9 +400,12 @@ typedef struct { int32_t i2; } TK_DimensionPair; +// some implicity _local_ typedef -> public typedef checks +typedef TK_Surface * (MYAPIENTRY* PFNCREATESURFACEPROC)(); +typedef void (MYAPIENTRY* PFNDESTROYSURFACEPROC)(TK_Surface * surface); + MYAPI TK_Surface * MYAPIENTRY createSurface(); MYAPI void MYAPIENTRY destroySurface(TK_Surface * surface); - MYAPI TK_ComplicatedSuperSet * MYAPIENTRY createComplicatedSuperSet(); MYAPI Bool MYAPIENTRY hasInitValues(TK_ComplicatedSuperSet * s); MYAPI void MYAPIENTRY destroyComplicatedSuperSet(TK_ComplicatedSuperSet * s); @@ -266,6 +423,13 @@ MYAPI TK_Dimension MYAPIENTRY addDimensions(const TK_Dimension s[TWO]); MYAPI TK_Dimension MYAPIENTRY addDimensionPair(const TK_DimensionPair s); MYAPI void MYAPIENTRY zeroDimensions(TK_Dimension s[2]); + +// some implicity _local_ typedef -> public typedef checks +typedef void (MYAPIENTRY* PFNCOPYPRIMTODIMENSIONSPROC)(const int pos[2], const int size[2], TK_Dimension dest[1]); +typedef int (MYAPIENTRY* PFNRGBATOINTPROC)(const char rgba[4]); +typedef void (MYAPIENTRY* PFNINTTORGBAPROC)(int irgba, char rgbaSink[4]); +typedef void (MYAPIENTRY* PFNADDBYTEPROC)(const char summands[2], char result[1]); + MYAPI void MYAPIENTRY copyPrimToDimensions(const int pos[2], const int size[2], TK_Dimension dest[1]); MYAPI void MYAPIENTRY copyDimensionsToPrim(TK_Dimension dim, int dpos[2], int dsize[2]); MYAPI int MYAPIENTRY rgbaToInt(const char rgba[4]); @@ -296,7 +460,7 @@ typedef struct { const int32_t structPointerCustomLenVal; const TK_Dimension * structPointerOneElem; - const TK_Context ctx; + TK_Context ctx; const char modelNameArrayFixedLen[12]; /* 'Hello Array' len=11+1 */ const char * modelNamePointerCString; /* 'Hello CString' len=13+1 */ diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1p1-gluegen.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test1p1-gluegen.cfg index 6ef171b..b8582a8 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1p1-gluegen.cfg +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1p1-gluegen.cfg @@ -6,6 +6,8 @@ NativeOutputDir native Extends Bindingtest1p1 Bindingtest1 +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/gluegen/test/junit/generation/Bindingtest1.java + Include test1-common.cfg Import com.jogamp.gluegen.test.junit.generation.Bindingtest1 diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1p2-gluegen.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test1p2-gluegen.cfg index 708bd26..eb9fdcf 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1p2-gluegen.cfg +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1p2-gluegen.cfg @@ -6,6 +6,8 @@ NativeOutputDir native Extends Bindingtest1p2 Bindingtest1 +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/gluegen/test/junit/generation/Bindingtest1.java + # Use a ProcAddressTable so we dynamically look up the routines EmitProcAddressTable true ProcAddressTableClassName Bindingtest1p2ProcAddressTable diff --git a/src/junit/com/jogamp/gluegen/test/junit/internals/TestType.java b/src/junit/com/jogamp/gluegen/test/junit/internals/TestType.java new file mode 100644 index 0000000..84fc463 --- /dev/null +++ b/src/junit/com/jogamp/gluegen/test/junit/internals/TestType.java @@ -0,0 +1,85 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.gluegen.test.junit.internals; + +import org.junit.Assert; +import org.junit.Test; + +import com.jogamp.gluegen.cgram.types.FloatType; +import com.jogamp.gluegen.cgram.types.IntType; +import com.jogamp.junit.util.SingletonJunitCase; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestType extends SingletonJunitCase { + + @Test + public void test01Equals() { + final FloatType f1 = new FloatType("GLfloat", null, 0, null); + final FloatType f2 = new FloatType("float", null, 0, null); + final IntType i1 = new IntType("GLint", null, false, 0, null); + final IntType i2 = new IntType("int", null, false, 0, null); + final int f1H = f1.hashCode(); + final int f2H = f2.hashCode(); + final int i1H = i1.hashCode(); + final int i2H = i2.hashCode(); + + final int f1HS = f1.hashCodeSemantics(); + final int f2HS = f2.hashCodeSemantics(); + final int i1HS = i1.hashCodeSemantics(); + final int i2HS = i2.hashCodeSemantics(); + + Assert.assertFalse(f1.getClass().isInstance(null)); + Assert.assertTrue(f1.getClass().isInstance(f2)); + Assert.assertTrue(i1.getClass().isInstance(i2)); + Assert.assertFalse(f1.getClass().isInstance(i2)); + + Assert.assertFalse(f1.equals(f2)); + Assert.assertFalse(i1.equals(i2)); + Assert.assertFalse(f1.equals(i2)); + Assert.assertNotEquals(f1H, f2H); + Assert.assertNotEquals(i1H, i2H); + Assert.assertNotEquals(f1H, i2H); + + Assert.assertTrue(f1.equalSemantics(f2)); + Assert.assertTrue(i1.equalSemantics(i2)); + Assert.assertFalse(f1.equalSemantics(i2)); + Assert.assertEquals(f1HS, f2HS); + Assert.assertEquals(i1HS, i2HS); + Assert.assertNotEquals(f1HS, i2HS); + } + + public static void main(final String args[]) { + final String tstname = TestType.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/junit/com/jogamp/junit/sec/Applet01.java b/src/junit/com/jogamp/junit/sec/Applet01.java index f028d7c..fd13207 100644 --- a/src/junit/com/jogamp/junit/sec/Applet01.java +++ b/src/junit/com/jogamp/junit/sec/Applet01.java @@ -201,7 +201,7 @@ public class Applet01 extends Applet { final Uri absLib = libDir1.concat(Uri.Encoded.cast("natives/" + libBaseName)); Exception sec01 = null; try { - final NativeLibrary nlib = NativeLibrary.open(absLib.toFile().getPath(), cl); + final NativeLibrary nlib = NativeLibrary.open(absLib.toFile().getPath(), true, true, cl, true); System.err.println("NativeLibrary: "+nlib); } catch (final SecurityException e) { sec01 = e; diff --git a/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java b/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java index b3a1877..27f8d0b 100644 --- a/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java +++ b/src/junit/com/jogamp/junit/sec/TestSecIOUtil01.java @@ -183,7 +183,7 @@ public class TestSecIOUtil01 extends SingletonJunitCase { Exception se0 = null; NativeLibrary nlib = null; try { - nlib = NativeLibrary.open(absLib.toFile().getPath(), cl); + nlib = NativeLibrary.open(absLib.toFile().getPath(), true, true, cl, true); System.err.println("NativeLibrary: "+nlib); } catch (final SecurityException e) { se0 = e; diff --git a/src/junit/com/jogamp/junit/util/SingletonJunitCase.java b/src/junit/com/jogamp/junit/util/SingletonJunitCase.java index 7fb5fea..ad80bde 100644 --- a/src/junit/com/jogamp/junit/util/SingletonJunitCase.java +++ b/src/junit/com/jogamp/junit/util/SingletonJunitCase.java @@ -45,19 +45,29 @@ public abstract class SingletonJunitCase extends JunitTracer { private static SingletonInstance singletonInstance = null; // system wide lock via port locking private static final Object singletonSync = new Object(); // classloader wide lock + private static boolean enabled = true; + + /** + * Default is {@code true}. + */ + public static final void enableSingletonLock(final boolean v) { + enabled = v; + } @BeforeClass public static final void oneTimeSetUpSingleton() { // one-time initialization code synchronized( singletonSync ) { - if( null == singletonInstance ) { - System.err.println("++++ Test Singleton.ctor()"); - // singletonInstance = SingletonInstance.createFileLock(SINGLE_INSTANCE_LOCK_POLL, SINGLE_INSTANCE_LOCK_FILE); - singletonInstance = SingletonInstance.createServerSocket(SINGLE_INSTANCE_LOCK_POLL, SINGLE_INSTANCE_LOCK_PORT); - } - System.err.println("++++ Test Singleton.lock()"); - if(!singletonInstance.tryLock(SINGLE_INSTANCE_LOCK_TO)) { - throw new RuntimeException("Fatal: Could not lock single instance: "+singletonInstance.getName()); + if( enabled ) { + if( null == singletonInstance ) { + System.err.println("++++ Test Singleton.ctor()"); + // singletonInstance = SingletonInstance.createFileLock(SINGLE_INSTANCE_LOCK_POLL, SINGLE_INSTANCE_LOCK_FILE); + singletonInstance = SingletonInstance.createServerSocket(SINGLE_INSTANCE_LOCK_POLL, SINGLE_INSTANCE_LOCK_PORT); + } + System.err.println("++++ Test Singleton.lock()"); + if(!singletonInstance.tryLock(SINGLE_INSTANCE_LOCK_TO)) { + throw new RuntimeException("Fatal: Could not lock single instance: "+singletonInstance.getName()); + } } } } @@ -67,12 +77,14 @@ public abstract class SingletonJunitCase extends JunitTracer { // one-time cleanup code synchronized( singletonSync ) { System.gc(); // force cleanup - System.err.println("++++ Test Singleton.unlock()"); - singletonInstance.unlock(); - try { - // allowing other JVM instances to pick-up socket - Thread.sleep( SINGLE_INSTANCE_LOCK_POLL ); - } catch (final InterruptedException e) { } + if( enabled ) { + System.err.println("++++ Test Singleton.unlock()"); + singletonInstance.unlock(); + try { + // allowing other JVM instances to pick-up socket + Thread.sleep( SINGLE_INSTANCE_LOCK_POLL ); + } catch (final InterruptedException e) { } + } } } } diff --git a/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java b/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java index 78f4460..de850d0 100644 --- a/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java +++ b/src/junit/com/jogamp/junit/util/VersionSemanticsUtil.java @@ -86,16 +86,18 @@ public class VersionSemanticsUtil { } System.err.println("Semantic Version Test"); - System.err.println("Previous version: "+preVersionNumber+" - "+previousJar.toString()); - System.err.println("Current version: "+curVersionNumber+" - "+currentJar.toString()); - System.err.println("Compat. expected: "+expectedCompatibilityType); - System.err.println("Compat. detected: "+detectedCompatibilityType); - System.err.println("Compat. result: detected "+compS+" expected -> "+(compOK ? "OK" : "ERROR")); + System.err.println(" criteria: "+diffCriteria); + System.err.println(" Previous version: "+preVersionNumber+" - "+previousJar.toString()); + System.err.println(" Current version: "+curVersionNumber+" - "+currentJar.toString()); + System.err.println(" Field values changed: "+delta.fieldCompatChanged()); + System.err.println(" Compat. expected: "+expectedCompatibilityType); + System.err.println(" Compat. detected: "+detectedCompatibilityType); + System.err.println(" Compat. result: detected "+compS+" expected -> "+(compOK ? "OK" : "ERROR")); final String resS; if( compOK ) { - resS = "Current version "+curVersionNumber+" is "+expectedCompatibilityType+" to previous version "+preVersionNumber+", actually "+detectedCompatibilityType; + resS = " Current version "+curVersionNumber+" is "+expectedCompatibilityType+" to previous version "+preVersionNumber+", actually "+detectedCompatibilityType; } else { - resS = "Current version "+curVersionNumber+" is not "+expectedCompatibilityType+" to previous version "+preVersionNumber+", but "+detectedCompatibilityType; + resS = " Current version "+curVersionNumber+" is not "+expectedCompatibilityType+" to previous version "+preVersionNumber+", but "+detectedCompatibilityType; } System.err.println(resS); System.err.printf("%n%n"); diff --git a/src/native/tinype/make.sh b/src/native/tinype/make.sh new file mode 100755 index 0000000..b141066 --- /dev/null +++ b/src/native/tinype/make.sh @@ -0,0 +1,15 @@ +# /cygdrive/c/mingw/bin/gcc -nodefaultlibs -nostdlib -s -Os -mconsole -o tiny-conso-i386.exe tiny.c + +jardir=../../../build-win64 + +/cygdrive/c/mingw/bin/gcc -nodefaultlibs -nostdlib -s -Os -mwindows -o tiny2-win32-i386.exe tiny2.c -lUser32 +java -cp "$jardir/test/build/gluegen-test.jar;$jardir/gluegen-rt.jar" com.jogamp.common.util.CustomDeflate tiny2-win32-i386.exe exe2-windows-i386.defl + +/cygdrive/c/mingw64/bin/gcc -nodefaultlibs -nostdlib -s -Os -mwindows -o tiny2-win32-x86_64.exe tiny2.c -lUser32 +java -cp "$jardir/test/build/gluegen-test.jar;$jardir/gluegen-rt.jar" com.jogamp.common.util.CustomDeflate tiny2-win32-x86_64.exe exe2-windows-x86_64.defl + +/cygdrive/c/mingw/bin/gcc -nodefaultlibs -nostdlib -s -Os -mwindows -o tiny-win32-i386.exe tiny.c +java -cp "$jardir/test/build/gluegen-test.jar;$jardir/gluegen-rt.jar" com.jogamp.common.util.CustomDeflate tiny-win32-i386.exe exe-windows-i386.defl + +/cygdrive/c/mingw64/bin/gcc -nodefaultlibs -nostdlib -s -Os -mwindows -o tiny-win32-x86_64.exe tiny.c +java -cp "$jardir/test/build/gluegen-test.jar;$jardir/gluegen-rt.jar" com.jogamp.common.util.CustomDeflate tiny-win32-x86_64.exe exe-windows-x86_64.defl diff --git a/src/native/tinype/tiny.c b/src/native/tinype/tiny.c new file mode 100644 index 0000000..6d2de3a --- /dev/null +++ b/src/native/tinype/tiny.c @@ -0,0 +1,13 @@ +#undef UNICODE +#define UNICODE +#include <windows.h> + +// const char * id = "JogAmp Windows Universal Test PE Executable"; + +// int __main() +// int main() +// int main( int argc, char* argv[] ) +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + return 0; +} diff --git a/src/native/tinype/tiny2.c b/src/native/tinype/tiny2.c new file mode 100644 index 0000000..6330911 --- /dev/null +++ b/src/native/tinype/tiny2.c @@ -0,0 +1,15 @@ +#undef UNICODE +#define UNICODE +#include <windows.h> + +const wchar_t * id = L"JogAmp Windows Universal Test PE Executable"; +const wchar_t * cap = L"JogAmp"; + +// int __main() +// int main() +// int main( int argc, char* argv[] ) +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + MessageBox(0, id, cap, MB_SETFOREGROUND | MB_OK); + return 0; +} |