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.TypeVariable;
034import java.lang.reflect.WildcardType;
035import org.bridj.CallIO.NativeObjectHandler;
036import org.bridj.util.*;
037import java.lang.reflect.ParameterizedType;
038import java.lang.reflect.Method;
039import java.lang.reflect.Modifier;
040import java.lang.reflect.Member;
041import java.lang.reflect.AnnotatedElement;
042import java.lang.reflect.Type;
043import java.nio.*;
044import java.util.ArrayList;
045import java.util.Collections;
046import java.util.Comparator;
047import java.util.HashMap;
048import java.util.LinkedHashMap;
049import java.util.HashSet;
050import java.util.List;
051import java.util.Map;
052import java.util.TreeMap;
053import java.util.Set;
054import java.util.concurrent.*;
055import org.bridj.ann.Virtual;
056import org.bridj.ann.Array;
057import org.bridj.ann.Union;
058import org.bridj.ann.Bits;
059import org.bridj.ann.Field;
060import org.bridj.ann.Struct;
061import org.bridj.ann.Alignment;
062import static org.bridj.Pointer.*;
063import static org.bridj.StructUtils.*;
064
065/**
066 * Representation of a C struct's memory layout, built thanks to the annotations
067 * found in the Java bindings.<br>
068 * End-users should not use this class, it's used by runtimes.<br>
069 * Annotations currently used are
070 * {@link org.bridj.ann.Virtual}, {@link org.bridj.ann.Array}, {@link org.bridj.ann.Bits}, {@link org.bridj.ann.Field}, {@link org.bridj.ann.Alignment}
071 * and {@link org.bridj.ann.Struct}
072 *
073 * @author ochafik
074 */
075public class StructDescription {
076
077    protected volatile StructFieldDescription[] fields;
078    protected long structSize = -1;
079    protected long structAlignment = -1;
080    protected final Class<?> structClass;
081    protected final Type structType;
082    protected boolean hasFieldFields;
083
084    public void prependBytes(long bytes) {
085        build();
086        for (StructFieldDescription field : fields) {
087            field.offset(bytes);
088        }
089        structSize += bytes;
090    }
091
092    public void appendBytes(long bytes) {
093        build();
094        structSize += bytes;
095    }
096
097    public void setFieldOffset(String fieldName, long fieldOffset, boolean propagateChanges) {
098        build();
099
100        long propagatedOffsetDelta = 0;
101        long originalOffset = 0;
102        for (StructFieldDescription field : fields) {
103            if (field.name.equals(fieldName)) {
104                originalOffset = field.byteOffset;
105                propagatedOffsetDelta = fieldOffset - field.byteOffset;
106                field.offset(propagatedOffsetDelta);
107                if (!propagateChanges) {
108                    long minSize = fieldOffset + field.byteLength;
109                    structSize = structSize < minSize ? minSize : structSize;
110                    return;
111                }
112            }
113        }
114        structSize += propagatedOffsetDelta;
115        for (StructFieldDescription field : fields) {
116            if (!field.name.equals(fieldName) && field.byteOffset > originalOffset) {
117                field.offset(propagatedOffsetDelta);
118            }
119        }
120    }
121    StructCustomizer customizer;
122
123    public StructDescription(Class<?> structClass, Type structType, StructCustomizer customizer) {
124        this.structClass = structClass;
125        this.structType = structType;
126        this.customizer = customizer;
127        if (Utils.containsTypeVariables(structType)) {
128            throw new RuntimeException("Type " + structType + " contains unresolved type variables!");
129        }
130        // Don't call build here, for recursive initialization cases.
131    }
132
133    boolean isVirtual() {
134        for (Method m : structClass.getMethods()) {
135            if (m.getAnnotation(Virtual.class) != null) {
136                return true;
137            }
138        }
139        return false;
140    }
141
142    public Class<?> getStructClass() {
143        return structClass;
144    }
145
146    public Type getStructType() {
147        return structType;
148    }
149
150    @Override
151    public String toString() {
152        return "StructDescription(" + Utils.toString(structType) + ")";
153    }
154
155    /// Call whenever an instanceof a struct that depends on that StructIO is created
156    void build() {
157        if (fields == null) {
158            synchronized (this) {
159                if (fields == null) {
160                    computeStructLayout(this, customizer);
161                    customizer.afterBuild(this);
162                    if (BridJ.debug) {
163                        BridJ.info(describe());
164                    }
165                }
166            }
167        }
168    }
169
170    public final long getStructSize() {
171        build();
172        return structSize;
173    }
174
175    public final long getStructAlignment() {
176        build();
177        return structAlignment;
178    }
179    private List<StructFieldDescription> aggregatedFields;
180
181    public void setAggregatedFields(List<StructFieldDescription> aggregatedFields) {
182        this.aggregatedFields = aggregatedFields;
183    }
184
185    public List<StructFieldDescription> getAggregatedFields() {
186        build();
187        return aggregatedFields;
188    }
189    SolidRanges solidRanges;
190
191    public SolidRanges getSolidRanges() {
192        build();
193        return solidRanges;
194    }
195
196    public final String describe(StructObject struct) {
197        return StructUtils.describe(struct, structType, fields);
198    }
199
200    public final String describe() {
201        StringBuilder b = new StringBuilder();
202        b.append("// ");
203        b.append("size = ").append(structSize).append(", ");
204        b.append("alignment = ").append(structAlignment);
205        b.append("\nstruct ");
206        b.append(StructUtils.describe(structType)).append(" { ");
207        for (int iField = 0, nFields = fields.length; iField < nFields; iField++) {
208            StructFieldDescription fd = fields[iField];
209            b.append("\n\t");
210            b.append("@Field(").append(iField).append(") ");
211            if (fd.isCLong) {
212                b.append("@CLong ");
213            } else if (fd.isSizeT) {
214                b.append("@Ptr ");
215            }
216            b.append(StructUtils.describe(fd.valueType)).append(" ").append(fd.name).append("; ");
217
218            b.append("// ");
219            b.append("offset = ").append(fd.byteOffset).append(", ");
220            b.append("length = ").append(fd.byteLength).append(", ");
221            if (fd.bitOffset != 0) {
222                b.append("bitOffset = ").append(fd.bitOffset).append(", ");
223            }
224            if (fd.bitLength != -1) {
225                b.append("bitLength = ").append(fd.bitLength).append(", ");
226            }
227            if (fd.arrayLength != 1) {
228                b.append("arrayLength = ").append(fd.arrayLength).append(", ");
229            }
230            if (fd.alignment != 1) {
231                b.append("alignment = ").append(fd.alignment);//.append(", ");
232            }
233        }
234        b.append("\n}");
235        return b.toString();
236    }
237}