summaryrefslogtreecommitdiff
path: root/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java
blob: 1c1ced500dbcfe3ba0295420e874f982fd9e1617 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/* FJBG -- Fast Java Bytecode Generator
 * Copyright 2002-2013 LAMP/EPFL
 * @author  Michel Schinz
 */

package ch.epfl.lamp.fjbg;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * InnerClasses attribute.
 *
 * The ClassFile structure of a class/interface C must have exactly one
 * InnerClasses attribute in its attributes table if the constant pool of C
 * contains a CONSTANT_Class_info entry which represents a class or interface
 * that is not a member of a package. See section 4.8.5 of the JVM Specification.
 *
 * @author Iulian Dragos, Stephane Micheloud
 * @version 1.1
 */
public class JInnerClassesAttribute extends JAttribute {
    /** Constant pool of the current classfile. */
    private JConstantPool pool;

    /** InnerClass entries */
    private Map/*<String, Entry>*/ entries = new LinkedHashMap();

    public JInnerClassesAttribute(FJBGContext context, JClass clazz) {
        super(context, clazz);
        this.pool = clazz.pool;
    }

    public JInnerClassesAttribute(FJBGContext context,
                                  JClass clazz,
                                  Object owner,
                                  String name,
                                  int size,
                                  DataInputStream stream)
        throws IOException {
        super(context, clazz, name);
        this.pool = clazz.pool;

        String inner = null;
        int count = stream.readShort();
        for (int i = 0; i < count; ++i) {
            int innerIdx = stream.readShort();
            int outerIdx = stream.readShort();
            int nameIdx = stream.readShort();
            int flags = stream.readShort();
            inner = pool.lookupClass(innerIdx);
            entries.put(inner, new Entry(innerIdx, outerIdx, nameIdx, flags));
        }

        assert name.equals(getName());
    }

    public void addEntry(String inner, String outer, String name, int flags) {
        int innerIdx = pool.addClass(inner);
        int outerIdx = 0;
        if (outer != null) outerIdx = pool.addClass(outer);
        int nameIdx = 0;
        if (name != null) nameIdx = pool.addUtf8(name);

        Entry e = new Entry(innerIdx, outerIdx, nameIdx, flags);

        if (entries.containsKey(inner)) {
            Entry other = (Entry) entries.get(inner);
            assert other.outerInfo == e.outerInfo && other.originalName == e.originalName && other.innerFlags == e.innerFlags
                : inner + " already declared as " + other;
        } else
            entries.put(inner, e);
    }

    public String getName() { return "InnerClasses"; }

    // Follows javap output format for the InnerClass attribute.
    /*@Override*/ public String toString() {
        // Here we intentionally use "InnerClass" as javap :-(
        StringBuffer buf = new StringBuffer("  InnerClass: ");
        for (Iterator it = entries.values().iterator(); it.hasNext(); ) {
            Entry e = (Entry)it.next();
            buf.append("\n   ");
            buf.append(e.innerFlagsToString());
            buf.append("#");
            if (e.originalName != 0) {
                buf.append(e.originalName);
                buf.append("= #");
            }
            buf.append(e.innerInfo);
            if (e.outerInfo != 0) {
                buf.append(" of #");
                buf.append(e.outerInfo);
            }
            buf.append("; //");
            if (e.originalName != 0) {
                buf.append(pool.lookupUtf8(e.originalName));
                buf.append("=");
            }
            buf.append("class ");
            buf.append(pool.lookupClass(e.innerInfo));
            if (e.outerInfo != 0) {
                buf.append(" of class ");
                buf.append(pool.lookupClass(e.outerInfo));
            }
        }
        buf.append("\n");
        return buf.toString();
    }

    protected int getSize() {
        return 2 + entries.size() * 8;
    }

    protected void writeContentsTo(DataOutputStream stream) throws IOException {
        stream.writeShort(entries.size());
        for (Iterator it = entries.values().iterator(); it.hasNext(); ) {
            Entry e = (Entry)it.next();
            stream.writeShort(e.innerInfo);
            stream.writeShort(e.outerInfo);
            stream.writeShort(e.originalName);
            stream.writeShort(e.innerFlags);
        }
    }

    /** An entry in the InnerClasses attribute, as defined by the JVM Spec. */
    private class Entry {
        /** CONSTANT_Class_info index in the pool for the inner class (mangled). */
        int innerInfo;

        /** CONSTANT_Class_info index in the pool for the outer class (mangled). */
        int outerInfo;

        /** CONSTANT_Utf8_info index in the pool for the original name of the inner class. */
        int originalName;

        /** Short int for modifier flags. */
        int innerFlags;

        public Entry(int iI, int oI, int oN, int f) {
            this.innerInfo = iI;
            this.outerInfo = oI;
            this.originalName = oN;
            this.innerFlags = f;
        }

        public Entry(String innerClass, String outerClass, String name, int flags) {
            this(pool.addClass(innerClass), pool.addClass(outerClass), pool.addUtf8(name), flags);
        }

        /** Two entries are equal if they refer to the same inner class.
         *  innerInfo represents a unique name (mangled).
         */
        public boolean equals(Object other) {
            if (other instanceof Entry) {
                Entry otherEntry = (Entry) other;
                return otherEntry.innerInfo == this.innerInfo;
            }
            return false;
        }

        public String innerFlagsToString() {
            StringBuffer buf = new StringBuffer();
            if (isPublic()) buf.append("public ");
            else if (isProtected()) buf.append("protected ");
            else if (isPrivate()) buf.append("private ");
            //if (isStatic()) buf.append("static "); // as javap
            if (isAbstract()) buf.append("abstract ");
            else if (isFinal()) buf.append("final ");
            return buf.toString();
        }

        private boolean isPublic() {
            return (innerFlags & JAccessFlags.ACC_PUBLIC) != 0;
        }

        private boolean isPrivate() {
            return (innerFlags & JAccessFlags.ACC_PRIVATE) != 0;
        }

        private boolean isProtected() {
            return (innerFlags & JAccessFlags.ACC_PROTECTED) != 0;
        }

        private boolean isStatic() {
            return (innerFlags & JAccessFlags.ACC_STATIC) != 0;
        }

        private boolean isFinal() {
            return (innerFlags & JAccessFlags.ACC_FINAL) != 0;
        }

        private boolean isAbstract() {
            return (innerFlags & JAccessFlags.ACC_ABSTRACT) != 0;
        }
    }
}