001/*
002 * BridJ - Dynamic and blazing-fast native interop for Java.
003 * http://bridj.googlecode.com/
004 *
005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/)
006 * All rights reserved.
007 *
008 * Redistribution and use in source and binary forms, with or without
009 * modification, are permitted provided that the following conditions are met:
010 * 
011 *     * Redistributions of source code must retain the above copyright
012 *       notice, this list of conditions and the following disclaimer.
013 *     * Redistributions in binary form must reproduce the above copyright
014 *       notice, this list of conditions and the following disclaimer in the
015 *       documentation and/or other materials provided with the distribution.
016 *     * Neither the name of Olivier Chafik nor the
017 *       names of its contributors may be used to endorse or promote products
018 *       derived from this software without specific prior written permission.
019 * 
020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030 */
031package org.bridj;
032
033import java.lang.reflect.*;
034import java.nio.*;
035import java.util.*;
036import static org.bridj.StructUtils.*;
037import org.bridj.cpp.CPPObject;
038import org.bridj.cpp.CPPRuntime;
039import org.bridj.cpp.CPPType;
040import org.bridj.util.DefaultParameterizedType;
041import org.bridj.util.Utils;
042
043/**
044 * Internal metadata on a struct field
045 */
046public class StructFieldDescription {
047
048    public final List<StructFieldDeclaration> aggregatedFields = new ArrayList<StructFieldDeclaration>();
049    public long alignment = -1;
050    public long byteOffset = -1, byteLength = -1;
051    public long bitOffset = 0;
052    public long bitLength = -1;
053    public long arrayLength = 1;
054    public long bitMask = -1;
055    public boolean isArray, isNativeObject;
056    public Type nativeTypeOrPointerTargetType;
057    public java.lang.reflect.Field field;
058    Type valueType;
059    Method getter;
060    String name;
061    boolean isCLong, isSizeT;
062
063    public void offset(long bytes) {
064        byteOffset += bytes;
065    }
066
067    @Override
068    public String toString() {
069        return "Field(byteOffset = " + byteOffset + ", byteLength = " + byteLength + ", bitOffset = " + bitOffset + ", bitLength = " + bitLength + (nativeTypeOrPointerTargetType == null ? "" : ", ttype = " + nativeTypeOrPointerTargetType) + ")";
070    }
071
072    static Type resolveType(Type tpe, Type structType) {
073        if (tpe == null || (tpe instanceof WildcardType)) {
074            return null;
075        }
076
077        Type ret;
078        if (tpe instanceof ParameterizedType) {
079            ParameterizedType pt = (ParameterizedType) tpe;
080            Type[] actualTypeArguments = pt.getActualTypeArguments();
081            Type[] resolvedActualTypeArguments = new Type[actualTypeArguments.length];
082            for (int i = 0; i < actualTypeArguments.length; i++) {
083                resolvedActualTypeArguments[i] = resolveType(actualTypeArguments[i], structType);
084            }
085            Type resolvedOwnerType = resolveType(pt.getOwnerType(), structType);
086            Type rawType = pt.getRawType();
087            if ((tpe instanceof CPPType) || CPPObject.class.isAssignableFrom(Utils.getClass(rawType))) // TODO args
088            {
089                ret = new CPPType(resolvedOwnerType, rawType, resolvedActualTypeArguments);
090            } else {
091                ret = new DefaultParameterizedType(resolvedOwnerType, rawType, resolvedActualTypeArguments);
092            }
093        } else if (tpe instanceof TypeVariable) {
094            TypeVariable tv = (TypeVariable) tpe;
095            Class<?> structClass = Utils.getClass(structType);
096            TypeVariable[] typeParameters = structClass.getTypeParameters();
097            int i = Arrays.asList(typeParameters).indexOf(tv);
098            // TODO recurse on pt.getOwnerType() if i < 0.
099            if (i >= 0) {
100                if (structType instanceof ParameterizedType) {
101                    ParameterizedType pt = (ParameterizedType) structType;
102                    //Type[] typeParams = CPPRuntime.getTemplateTypeParameters(null, tpe)
103                    ret = pt.getActualTypeArguments()[i];
104                } else {
105                    throw new RuntimeException("Type " + structType + " does not have params, cannot resolve " + tpe);
106                }
107            } else {
108                throw new RuntimeException("Type param " + tpe + " not found in params of " + structType + " (" + Arrays.asList(typeParameters) + ")");
109            }
110        } else {
111            ret = tpe;
112        }
113
114        assert !Utils.containsTypeVariables(ret);
115//            throw new RuntimeException("Type " + ret + " cannot be resolved");
116
117        return ret;
118    }
119
120    static StructFieldDescription aggregateDeclarations(Type structType, List<StructFieldDeclaration> fieldGroup) {
121        StructFieldDescription aggregatedField = new StructFieldDescription();
122        boolean isMultiFields = fieldGroup.size() > 1;
123        aggregatedField.aggregatedFields.addAll(fieldGroup);
124        for (StructFieldDeclaration field : fieldGroup) {
125            if (field.valueClass.isArray()) {
126                throw new RuntimeException("Struct fields cannot be array types : please use a combination of Pointer and @Array (for instance, an int[10] is a @Array(10) Pointer<Integer>).");
127            }
128            if (field.valueClass.isPrimitive()) {
129                if (field.desc.isCLong) {
130                    field.desc.byteLength = CLong.SIZE;
131                } else if (field.desc.isSizeT) {
132                    field.desc.byteLength = SizeT.SIZE;
133                } else {
134                    field.desc.byteLength = primTypeLength(field.valueClass);
135                    if (field.desc.alignment < 0)
136                        field.desc.alignment = primTypeAlignment(field.valueClass, field.desc.byteLength);
137                }
138            } else if (field.valueClass == CLong.class) {
139                field.desc.byteLength = CLong.SIZE;
140            } else if (field.valueClass == SizeT.class) {
141                field.desc.byteLength = SizeT.SIZE;
142            } else if (StructObject.class.isAssignableFrom(field.valueClass)) {
143                field.desc.nativeTypeOrPointerTargetType = resolveType(field.desc.valueType, structType);
144                StructDescription desc = StructIO.getInstance(field.valueClass, field.desc.valueType).desc;
145                field.desc.byteLength = desc.getStructSize();
146                if (field.desc.alignment < 0)
147                    field.desc.alignment = desc.getStructAlignment();
148                field.desc.isNativeObject = true;
149            } else if (ValuedEnum.class.isAssignableFrom(field.valueClass)) {
150                field.desc.nativeTypeOrPointerTargetType = resolveType((field.desc.valueType instanceof ParameterizedType) ? PointerIO.getClass(((ParameterizedType) field.desc.valueType).getActualTypeArguments()[0]) : null, structType);
151                Class c = PointerIO.getClass(field.desc.nativeTypeOrPointerTargetType);
152                if (IntValuedEnum.class.isAssignableFrom(c)) {
153                    field.desc.byteLength = 4;
154                } else {
155                    throw new RuntimeException("Enum type unknown : " + c);
156                }
157                //field.callIO = CallIO.Utils.createPointerCallIO(field.valueClass, field.desc.valueType);
158            } else if (TypedPointer.class.isAssignableFrom(field.valueClass)) {
159                field.desc.nativeTypeOrPointerTargetType = resolveType(field.desc.valueType, structType);
160                if (field.desc.isArray) {
161                    throw new RuntimeException("Typed pointer field cannot be an array : " + field.desc.name);
162                }
163                field.desc.byteLength = Pointer.SIZE;
164                //field.callIO = CallIO.Utils.createPointerCallIO(field.valueClass, field.desc.valueType);
165            } else if (Pointer.class.isAssignableFrom(field.valueClass)) {
166                Type tpe = (field.desc.valueType instanceof ParameterizedType) ? ((ParameterizedType) field.desc.valueType).getActualTypeArguments()[0] : null;
167                field.desc.nativeTypeOrPointerTargetType = resolveType(tpe, structType);
168                if (field.desc.isArray) {
169                    field.desc.byteLength = BridJ.sizeOf(field.desc.nativeTypeOrPointerTargetType);
170                    if (field.desc.alignment < 0)
171                        field.desc.alignment = alignmentOf(field.desc.nativeTypeOrPointerTargetType);
172                } else {
173                    field.desc.byteLength = Pointer.SIZE;
174                }
175                //field.callIO = CallIO.Utils.createPointerCallIO(field.valueClass, field.desc.valueType);
176            } else if (Buffer.class.isAssignableFrom(field.valueClass)) {
177                if (field.valueClass == IntBuffer.class) {
178                    field.desc.byteLength = 4;
179                } else if (field.valueClass == LongBuffer.class) {
180                    field.desc.byteLength = 8;
181                } else if (field.valueClass == ShortBuffer.class) {
182                    field.desc.byteLength = 2;
183                } else if (field.valueClass == ByteBuffer.class) {
184                    field.desc.byteLength = 1;
185                } else if (field.valueClass == FloatBuffer.class) {
186                    field.desc.byteLength = 4;
187                } else if (field.valueClass == DoubleBuffer.class) {
188                    field.desc.byteLength = 8;
189                } else {
190                    throw new UnsupportedOperationException("Field array type " + field.valueClass.getName() + " not supported yet");
191                }
192            } else if (field.valueClass.isArray() && field.valueClass.getComponentType().isPrimitive()) {
193                field.desc.byteLength = primTypeLength(field.valueClass.getComponentType());
194                if (field.desc.alignment < 0)
195                    field.desc.alignment = primTypeAlignment(field.valueClass, field.desc.byteLength);
196            } else {
197                //throw new UnsupportedOperationException("Field type " + field.valueClass.getName() + " not supported yet");
198                StructDescription desc = StructIO.getInstance(field.valueClass, field.desc.valueType).desc;
199                long s = desc.getStructSize();
200                if (s > 0) {
201                    field.desc.byteLength = s;
202                } else {
203                    throw new UnsupportedOperationException("Field type " + field.valueClass.getName() + " not supported yet");
204                }
205            }
206
207            aggregatedField.alignment = Math.max(
208                    aggregatedField.alignment,
209                    field.desc.alignment >= 0
210                    ? field.desc.alignment
211                    : field.desc.byteLength);
212
213            long length = field.desc.arrayLength * field.desc.byteLength;
214            if (length >= aggregatedField.byteLength) {
215                aggregatedField.byteLength = length;
216            }
217
218            if (field.desc.bitLength >= 0) {
219                if (isMultiFields) {
220                    throw new RuntimeException("No support for bit fields unions yet !");
221                }
222                aggregatedField.bitLength = field.desc.bitLength;
223                aggregatedField.byteLength = (aggregatedField.bitLength >>> 3) + ((aggregatedField.bitLength & 7) != 0 ? 1 : 0);
224            }
225        }
226        return aggregatedField;
227    }
228
229    void computeBitMask() {
230        if (bitLength < 0) {
231            bitMask = -1;
232        } else {
233            bitMask = 0;
234            for (int i = 0; i < bitLength; i++) {
235                bitMask = bitMask << 1 | 1;
236            }
237            bitMask = bitMask << bitOffset;
238        }
239    }
240}