1/*
2 * Copyright (C) 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.hit;
18
19import java.io.ByteArrayInputStream;
20import java.io.DataInputStream;
21import java.io.IOException;
22import java.util.Set;
23
24public class ClassInstance extends Instance {
25    private byte[] mFieldValues;
26
27    public ClassInstance(long id, StackTrace stack, long classId) {
28        mId = id;
29        mStack = stack;
30        mClassId = classId;
31    }
32
33    public final void loadFieldData(DataInputStream in, int numBytes)
34            throws IOException {
35        mFieldValues = new byte[numBytes];
36        in.readFully(mFieldValues);
37    }
38
39    @Override
40    public void resolveReferences(State state) {
41        ClassObj isa = mHeap.mState.findClass(mClassId);
42
43        resolve(state, isa, isa.mStaticFieldTypes, isa.mStaticFieldValues);
44        resolve(state, isa, isa.mFieldTypes, mFieldValues);
45    }
46
47    private void resolve(State state, ClassObj isa, int[] types,
48            byte[] values) {
49        ByteArrayInputStream bais = new ByteArrayInputStream(values);
50        DataInputStream dis = new DataInputStream(bais);
51        final int N = types.length;
52
53        /*
54         * Spin through the list of fields, find all object references,
55         * and list ourselves as a reference holder.
56         */
57        try {
58            for (int i = 0; i < N; i++) {
59                int type = types[i];
60                int size = Types.getTypeSize(type);
61
62                    if (type == Types.OBJECT) {
63                        long id;
64
65                        if (size == 4) {
66                            id = dis.readInt();
67                        } else {
68                            id = dis.readLong();
69                        }
70
71                        Instance instance = state.findReference(id);
72
73                        if (instance != null) {
74                            instance.addParent(this);
75                        }
76                    } else {
77                        dis.skipBytes(size);
78                    }
79            }
80        } catch (Exception e) {
81            e.printStackTrace();
82        }
83    }
84
85    @Override
86    public final int getSize() {
87        ClassObj isa = mHeap.mState.findClass(mClassId);
88
89        return isa.getSize();
90    }
91
92    @Override
93    public final void visit(Set<Instance> resultSet, Filter filter) {
94        if (resultSet.contains(this)) {
95            return;
96        }
97
98        if (filter != null) {
99            if (filter.accept(this)) {
100                resultSet.add(this);
101            }
102        } else {
103            resultSet.add(this);
104        }
105
106        State state = mHeap.mState;
107        ClassObj isa = state.findClass(mClassId);
108        int[] types = isa.mFieldTypes;
109        ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
110        DataInputStream dis = new DataInputStream(bais);
111        final int N = types.length;
112
113        /*
114         * Spin through the list of fields, find all object references,
115         * and list ourselves as a reference holder.
116         */
117        try {
118            for (int i = 0; i < N; i++) {
119                int type = types[i];
120                int size = Types.getTypeSize(type);
121
122                if (type == Types.OBJECT) {
123                    long id;
124
125                    if (size == 4) {
126                        id = dis.readInt();
127                    } else {
128                        id = dis.readLong();
129                    }
130
131                    Instance instance = state.findReference(id);
132
133                    if (instance != null) {
134                        instance.visit(resultSet, filter);
135                    }
136                } else {
137                    dis.skipBytes(size);
138                }
139            }
140        } catch (Exception e) {
141            e.printStackTrace();
142        }
143    }
144
145    @Override
146    public final String getTypeName() {
147        ClassObj theClass = mHeap.mState.findClass(mClassId);
148
149        return theClass.mClassName;
150    }
151
152    public final String toString() {
153        return String.format("%s@0x%08x", getTypeName(), mId);
154    }
155
156    @Override
157    public String describeReferenceTo(long referent) {
158        ClassObj isa = mHeap.mState.findClass(mClassId);
159        int[] types = isa.mFieldTypes;
160        String[] fieldNames = isa.mFieldNames;
161        ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
162        DataInputStream dis = new DataInputStream(bais);
163        final int N = types.length;
164        StringBuilder result = new StringBuilder("Referenced in field(s):");
165        int numReferences = 0;
166
167        /*
168         * Spin through the list of fields, add info about the field
169         * references to the output text.
170         */
171        try {
172            for (int i = 0; i < N; i++) {
173                int type = types[i];
174                int size = Types.getTypeSize(type);
175
176                if (type == Types.OBJECT) {
177                    long id;
178
179                    if (size == 4) {
180                        id = dis.readInt();
181                    } else {
182                        id = dis.readLong();
183                    }
184
185                    if (id == referent) {
186                        numReferences++;
187                        result.append("\n    ");
188                        result.append(fieldNames[i]);
189                    }
190                } else {
191                    dis.skipBytes(size);
192                }
193            }
194        } catch (Exception e) {
195            e.printStackTrace();
196        }
197
198        /*
199         *  TODO:  perform a similar loop over the static fields of isa
200         */
201
202        if (numReferences == 0) {
203            return super.describeReferenceTo(referent);
204        }
205
206        return result.toString();
207    }
208}
209