1/*
2 * Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.tools.java;
27
28import java.io.IOException;
29import java.io.DataInputStream;
30import java.io.OutputStream;
31import java.io.DataOutputStream;
32import java.io.ByteArrayInputStream;
33import java.util.Hashtable;
34import java.util.Vector;
35import java.util.Enumeration;
36
37/**
38 * WARNING: The contents of this source file are not part of any
39 * supported API.  Code that depends on them does so at its own risk:
40 * they are subject to change or removal without notice.
41 */
42@SuppressWarnings("deprecation")
43public final
44class BinaryClass extends ClassDefinition implements Constants {
45    BinaryConstantPool cpool;
46    BinaryAttribute atts;
47    Vector<ClassDeclaration> dependencies;
48    private boolean haveLoadedNested = false;
49
50    /**
51     * Constructor
52     */
53    public BinaryClass(Object source, ClassDeclaration declaration, int modifiers,
54                           ClassDeclaration superClass, ClassDeclaration interfaces[],
55                           Vector<ClassDeclaration> dependencies) {
56        super(source, 0, declaration, modifiers, null, null);
57        this.dependencies = dependencies;
58        this.superClass = superClass;
59        this.interfaces = interfaces;
60    }
61
62    /**
63     * Flags used by basicCheck() to avoid duplicate calls.
64     * (Part of fix for 4105911)
65     */
66    private boolean basicCheckDone = false;
67    private boolean basicChecking = false;
68
69    /**
70     * Ready a BinaryClass for further checking.  Note that, until recently,
71     * BinaryClass relied on the default basicCheck() provided by
72     * ClassDefinition.  The definition here has been added to ensure that
73     * the information generated by collectInheritedMethods is available
74     * for BinaryClasses.
75     */
76    protected void basicCheck(Environment env) throws ClassNotFound {
77        if (tracing) env.dtEnter("BinaryClass.basicCheck: " + getName());
78
79        // We need to guard against duplicate calls to basicCheck().  They
80        // can lead to calling collectInheritedMethods() for this class
81        // from within a previous call to collectInheritedMethods() for
82        // this class.  That is not allowed.
83        // (Part of fix for 4105911)
84        if (basicChecking || basicCheckDone) {
85            if (tracing) env.dtExit("BinaryClass.basicCheck: OK " + getName());
86            return;
87        }
88
89        if (tracing) env.dtEvent("BinaryClass.basicCheck: CHECKING " + getName());
90        basicChecking = true;
91
92        super.basicCheck(env);
93
94        // Collect inheritance information.
95        if (doInheritanceChecks) {
96            collectInheritedMethods(env);
97        }
98
99        basicCheckDone = true;
100        basicChecking = false;
101        if (tracing) env.dtExit("BinaryClass.basicCheck: " + getName());
102    }
103
104    /**
105     * Load a binary class
106     */
107    public static BinaryClass load(Environment env, DataInputStream in) throws IOException {
108        return load(env, in, ~(ATT_CODE|ATT_ALLCLASSES));
109    }
110
111    public static BinaryClass load(Environment env,
112                                   DataInputStream in, int mask) throws IOException {
113        // Read the header
114        int magic = in.readInt();                    // JVM 4.1 ClassFile.magic
115        if (magic != JAVA_MAGIC) {
116            throw new ClassFormatError("wrong magic: " + magic + ", expected " + JAVA_MAGIC);
117        }
118        int minor_version = in.readUnsignedShort();  // JVM 4.1 ClassFile.minor_version
119        int version = in.readUnsignedShort();        // JVM 4.1 ClassFile.major_version
120        if (version < JAVA_MIN_SUPPORTED_VERSION) {
121            throw new ClassFormatError(
122                           sun.tools.javac.Main.getText(
123                               "javac.err.version.too.old",
124                               String.valueOf(version)));
125        } else if ((version > JAVA_MAX_SUPPORTED_VERSION)
126                     || (version == JAVA_MAX_SUPPORTED_VERSION
127                  && minor_version > JAVA_MAX_SUPPORTED_MINOR_VERSION)) {
128            throw new ClassFormatError(
129                           sun.tools.javac.Main.getText(
130                               "javac.err.version.too.recent",
131                               version+"."+minor_version));
132        }
133
134        // Read the constant pool
135        BinaryConstantPool cpool = new BinaryConstantPool(in);
136
137        // The dependencies of this class
138        Vector<ClassDeclaration> dependencies = cpool.getDependencies(env);
139
140        // Read modifiers
141        int classMod = in.readUnsignedShort() & ACCM_CLASS;  // JVM 4.1 ClassFile.access_flags
142
143        // Read the class name - from JVM 4.1 ClassFile.this_class
144        ClassDeclaration classDecl = cpool.getDeclaration(env, in.readUnsignedShort());
145
146        // Read the super class name (may be null) - from JVM 4.1 ClassFile.super_class
147        ClassDeclaration superClassDecl = cpool.getDeclaration(env, in.readUnsignedShort());
148
149        // Read the interface names - from JVM 4.1 ClassFile.interfaces_count
150        ClassDeclaration interfaces[] = new ClassDeclaration[in.readUnsignedShort()];
151        for (int i = 0 ; i < interfaces.length ; i++) {
152            // JVM 4.1 ClassFile.interfaces[]
153            interfaces[i] = cpool.getDeclaration(env, in.readUnsignedShort());
154        }
155
156        // Allocate the class
157        BinaryClass c = new BinaryClass(null, classDecl, classMod, superClassDecl,
158                                        interfaces, dependencies);
159        c.cpool = cpool;
160
161        // Add any additional dependencies
162        c.addDependency(superClassDecl);
163
164        // Read the fields
165        int nfields = in.readUnsignedShort();  // JVM 4.1 ClassFile.fields_count
166        for (int i = 0 ; i < nfields ; i++) {
167            // JVM 4.5 field_info.access_flags
168            int fieldMod = in.readUnsignedShort() & ACCM_FIELD;
169            // JVM 4.5 field_info.name_index
170            Identifier fieldName = cpool.getIdentifier(in.readUnsignedShort());
171            // JVM 4.5 field_info.descriptor_index
172            Type fieldType = cpool.getType(in.readUnsignedShort());
173            BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
174            c.addMember(new BinaryMember(c, fieldMod, fieldType, fieldName, atts));
175        }
176
177        // Read the methods
178        int nmethods = in.readUnsignedShort();  // JVM 4.1 ClassFile.methods_count
179        for (int i = 0 ; i < nmethods ; i++) {
180            // JVM 4.6 method_info.access_flags
181            int methMod = in.readUnsignedShort() & ACCM_METHOD;
182            // JVM 4.6 method_info.name_index
183            Identifier methName = cpool.getIdentifier(in.readUnsignedShort());
184            // JVM 4.6 method_info.descriptor_index
185            Type methType = cpool.getType(in.readUnsignedShort());
186            BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
187            c.addMember(new BinaryMember(c, methMod, methType, methName, atts));
188        }
189
190        // Read the class attributes
191        c.atts = BinaryAttribute.load(in, cpool, mask);
192
193        // See if the SourceFile is known
194        byte data[] = c.getAttribute(idSourceFile);
195        if (data != null) {
196            DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(data));
197            // JVM 4.7.2 SourceFile_attribute.sourcefile_index
198            c.source = cpool.getString(dataStream.readUnsignedShort());
199        }
200
201        // See if the Documentation is know
202        data = c.getAttribute(idDocumentation);
203        if (data != null) {
204            c.documentation = new DataInputStream(new ByteArrayInputStream(data)).readUTF();
205        }
206
207        // Was it compiled as deprecated?
208        if (c.getAttribute(idDeprecated) != null) {
209            c.modifiers |= M_DEPRECATED;
210        }
211
212        // Was it synthesized by the compiler?
213        if (c.getAttribute(idSynthetic) != null) {
214            c.modifiers |= M_SYNTHETIC;
215        }
216
217        return c;
218    }
219
220    /**
221     * Called when an environment ties a binary definition to a declaration.
222     * At this point, auxiliary definitions may be loaded.
223     */
224
225    public void loadNested(Environment env) {
226        loadNested(env, 0);
227    }
228
229    public void loadNested(Environment env, int flags) {
230        // Sanity check.
231        if (haveLoadedNested) {
232            // Duplicate calls most likely should not occur, but they do
233            // in javap.  Be tolerant of them for the time being.
234            // throw new CompilerError("multiple loadNested");
235            if (tracing) env.dtEvent("loadNested: DUPLICATE CALL SKIPPED");
236            return;
237        }
238        haveLoadedNested = true;
239        // Read class-nesting information.
240        try {
241            byte data[];
242            data = getAttribute(idInnerClasses);
243            if (data != null) {
244                initInnerClasses(env, data, flags);
245            }
246        } catch (IOException ee) {
247            // The inner classes attribute is not well-formed.
248            // It may, for example, contain no data.  Report this.
249            // We used to throw a CompilerError here (bug 4095108).
250            env.error(0, "malformed.attribute", getClassDeclaration(),
251                      idInnerClasses);
252            if (tracing)
253                env.dtEvent("loadNested: MALFORMED ATTRIBUTE (InnerClasses)");
254        }
255    }
256
257    private void initInnerClasses(Environment env,
258                                  byte data[],
259                                  int flags) throws IOException {
260        DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data));
261        int nrec = ds.readUnsignedShort();  // InnerClasses_attribute.number_of_classes
262        for (int i = 0; i < nrec; i++) {
263            // For each inner class name transformation, we have a record
264            // with the following fields:
265            //
266            //    u2 inner_class_info_index;   // CONSTANT_Class_info index
267            //    u2 outer_class_info_index;   // CONSTANT_Class_info index
268            //    u2 inner_name_index;         // CONSTANT_Utf8_info index
269            //    u2 inner_class_access_flags; // access_flags bitmask
270            //
271            // The spec states that outer_class_info_index is 0 iff
272            // the inner class is not a member of its enclosing class (i.e.
273            // it is a local or anonymous class).  The spec also states
274            // that if a class is anonymous then inner_name_index should
275            // be 0.
276            //
277            // Prior to jdk1.2, javac did not implement the spec.  Instead
278            // it <em>always</em> set outer_class_info_index to the
279            // enclosing outer class and if the class was anonymous,
280            // it set inner_name_index to be the index of a CONSTANT_Utf8
281            // entry containing the null string "" (idNull).  This code is
282            // designed to handle either kind of class file.
283            //
284            // See also the compileClass() method in SourceClass.java.
285
286            // Read in the inner_class_info
287            // InnerClasses_attribute.classes.inner_class_info_index
288            int inner_index = ds.readUnsignedShort();
289            // could check for zero.
290            ClassDeclaration inner = cpool.getDeclaration(env, inner_index);
291
292            // Read in the outer_class_info.  Note that the index will be
293            // zero if the class is "not a member".
294            ClassDeclaration outer = null;
295            // InnerClasses_attribute.classes.outer_class_info_index
296            int outer_index = ds.readUnsignedShort();
297            if (outer_index != 0) {
298                outer = cpool.getDeclaration(env, outer_index);
299            }
300
301            // Read in the inner_name_index.  This may be zero.  An anonymous
302            // class will either have an inner_nm_index of zero (as the spec
303            // dictates) or it will have an inner_nm of idNull (for classes
304            // generated by pre-1.2 compilers).  Handle both.
305            Identifier inner_nm = idNull;
306            // InnerClasses_attribute.classes.inner_name_index
307            int inner_nm_index = ds.readUnsignedShort();
308            if (inner_nm_index != 0) {
309                inner_nm = Identifier.lookup(cpool.getString(inner_nm_index));
310            }
311
312            // Read in the modifiers for the inner class.
313            // InnerClasses_attribute.classes.inner_name_index
314            int mods = ds.readUnsignedShort();
315
316            // Is the class accessible?
317            // The old code checked for
318            //
319            //    (!inner_nm.equals(idNull) && (mods & M_PRIVATE) == 0)
320            //
321            // which we will preserve to keep it working for class files
322            // generated by 1.1 compilers.  In addition we check for
323            //
324            //    (outer != null)
325            //
326            // as an additional check that only makes sense with 1.2
327            // generated files.  Note that it is entirely possible that
328            // the M_PRIVATE bit is always enough.  We are being
329            // conservative here.
330            //
331            // The ATT_ALLCLASSES flag causes the M_PRIVATE modifier
332            // to be ignored, and is used by tools such as 'javap' that
333            // wish to examine all classes regardless of the normal access
334            // controls that apply during compilation.  Note that anonymous
335            // and local classes are still not considered accessible, though
336            // named local classes in jdk1.1 may slip through.  Note that
337            // this accessibility test is an optimization, and it is safe to
338            // err on the side of greater accessibility.
339            boolean accessible =
340                (outer != null) &&
341                (!inner_nm.equals(idNull)) &&
342                ((mods & M_PRIVATE) == 0 ||
343                 (flags & ATT_ALLCLASSES) != 0);
344
345            // The reader should note that there has been a significant change
346            // in the way that the InnerClasses attribute is being handled.
347            // In particular, previously the compiler called initInner() for
348            // <em>every</em> inner class.  Now the compiler does not call
349            // initInner() if the inner class is inaccessible.  This means
350            // that inaccessible inner classes don't have any of the processing
351            // from initInner() done for them: fixing the access flags,
352            // setting outerClass, setting outerMember in their outerClass,
353            // etc.  We believe this is fine: if the class is inaccessible
354            // and binary, then everyone who needs to see its internals
355            // has already been compiled.  Hopefully.
356
357            if (accessible) {
358                Identifier nm =
359                    Identifier.lookupInner(outer.getName(), inner_nm);
360
361                // Tell the type module about the nesting relation:
362                Type.tClass(nm);
363
364                if (inner.equals(getClassDeclaration())) {
365                    // The inner class in the record is this class.
366                    try {
367                        ClassDefinition outerClass = outer.getClassDefinition(env);
368                        initInner(outerClass, mods);
369                    } catch (ClassNotFound e) {
370                        // report the error elsewhere
371                    }
372                } else if (outer.equals(getClassDeclaration())) {
373                    // The outer class in the record is this class.
374                    try {
375                        ClassDefinition innerClass =
376                            inner.getClassDefinition(env);
377                        initOuter(innerClass, mods);
378                    } catch (ClassNotFound e) {
379                        // report the error elsewhere
380                    }
381                }
382            }
383        }
384    }
385
386    private void initInner(ClassDefinition outerClass, int mods) {
387        if (getOuterClass() != null)
388            return;             // already done
389        /******
390        // Maybe set static, protected, or private.
391        if ((modifiers & M_PUBLIC) != 0)
392            mods &= M_STATIC;
393        else
394            mods &= M_PRIVATE | M_PROTECTED | M_STATIC;
395        modifiers |= mods;
396        ******/
397        // For an inner class, the class access may have been weakened
398        // from that originally declared the source.  We must take the
399        // actual access permissions against which we check any source
400        // we are currently compiling from the InnerClasses attribute.
401        // We attempt to guard here against bogus combinations of modifiers.
402        if ((mods & M_PRIVATE) != 0) {
403            // Private cannot be combined with public or protected.
404            mods &= ~(M_PUBLIC | M_PROTECTED);
405        } else if ((mods & M_PROTECTED) != 0) {
406            // Protected cannot be combined with public.
407            mods &= ~M_PUBLIC;
408        }
409        if ((mods & M_INTERFACE) != 0) {
410            // All interfaces are implicitly abstract.
411            // All interfaces that are members of a type are implicitly static.
412            mods |= (M_ABSTRACT | M_STATIC);
413        }
414        if (outerClass.isInterface()) {
415            // All types that are members of interfaces are implicitly
416            // public and static.
417            mods |= (M_PUBLIC | M_STATIC);
418            mods &= ~(M_PRIVATE | M_PROTECTED);
419        }
420        modifiers = mods;
421
422        setOuterClass(outerClass);
423
424        for (MemberDefinition field = getFirstMember();
425             field != null;
426             field = field.getNextMember()) {
427            if (field.isUplevelValue()
428                    && outerClass.getType().equals(field.getType())
429                    && field.getName().toString().startsWith(prefixThis)) {
430                setOuterMember(field);
431            }
432        }
433    }
434
435    private void initOuter(ClassDefinition innerClass, int mods) {
436        if (innerClass instanceof BinaryClass)
437            ((BinaryClass)innerClass).initInner(this, mods);
438        addMember(new BinaryMember(innerClass));
439    }
440
441    /**
442     * Write the class out to a given stream.  This function mirrors the loader.
443     */
444    public void write(Environment env, OutputStream out) throws IOException {
445        DataOutputStream data = new DataOutputStream(out);
446
447        // write out the header
448        data.writeInt(JAVA_MAGIC);
449        data.writeShort(env.getMinorVersion());
450        data.writeShort(env.getMajorVersion());
451
452        // Write out the constant pool
453        cpool.write(data, env);
454
455        // Write class information
456        data.writeShort(getModifiers() & ACCM_CLASS);
457        data.writeShort(cpool.indexObject(getClassDeclaration(), env));
458        data.writeShort((getSuperClass() != null)
459                        ? cpool.indexObject(getSuperClass(), env) : 0);
460        data.writeShort(interfaces.length);
461        for (int i = 0 ; i < interfaces.length ; i++) {
462            data.writeShort(cpool.indexObject(interfaces[i], env));
463        }
464
465        // count the fields and the methods
466        int fieldCount = 0, methodCount = 0;
467        for (MemberDefinition f = firstMember; f != null; f = f.getNextMember())
468            if (f.isMethod()) methodCount++; else fieldCount++;
469
470        // write out each the field count, and then each field
471        data.writeShort(fieldCount);
472        for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
473            if (!f.isMethod()) {
474                data.writeShort(f.getModifiers() & ACCM_FIELD);
475                String name = f.getName().toString();
476                String signature = f.getType().getTypeSignature();
477                data.writeShort(cpool.indexString(name, env));
478                data.writeShort(cpool.indexString(signature, env));
479                BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
480            }
481        }
482
483        // write out each method count, and then each method
484        data.writeShort(methodCount);
485        for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
486            if (f.isMethod()) {
487                data.writeShort(f.getModifiers() & ACCM_METHOD);
488                String name = f.getName().toString();
489                String signature = f.getType().getTypeSignature();
490                data.writeShort(cpool.indexString(name, env));
491                data.writeShort(cpool.indexString(signature, env));
492                BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
493            }
494        }
495
496        // write out the class attributes
497        BinaryAttribute.write(atts, data, cpool, env);
498        data.flush();
499    }
500
501    /**
502     * Get the dependencies
503     */
504    public Enumeration<ClassDeclaration> getDependencies() {
505        return dependencies.elements();
506    }
507
508    /**
509     * Add a dependency
510     */
511    public void addDependency(ClassDeclaration c) {
512        if ((c != null) && !dependencies.contains(c)) {
513            dependencies.addElement(c);
514        }
515    }
516
517    /**
518     * Get the constant pool
519     */
520    public BinaryConstantPool getConstants() {
521        return cpool;
522    }
523
524    /**
525     * Get a class attribute
526     */
527    public byte getAttribute(Identifier name)[] {
528        for (BinaryAttribute att = atts ; att != null ; att = att.next) {
529            if (att.name.equals(name)) {
530                return att.data;
531            }
532        }
533        return null;
534    }
535}
536