1 /*
  2  * Copyright (c) 2022, 2024, 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 package jdk.internal.classfile.impl;
 26 
 27 import java.lang.classfile.*;
 28 import java.lang.classfile.attribute.*;
 29 import java.lang.classfile.constantpool.ClassEntry;
 30 import java.lang.classfile.constantpool.ConstantPool;
 31 import java.lang.reflect.AccessFlag;
 32 import java.util.List;
 33 import java.util.Optional;
 34 import java.util.function.Consumer;
 35 
 36 import jdk.internal.access.SharedSecrets;
 37 
 38 public final class ClassImpl
 39         extends AbstractElement
 40         implements ClassModel {
 41     final ClassReaderImpl reader;
 42     private final int attributesPos;
 43     private final List<MethodModel> methods;
 44     private final List<FieldModel> fields;
 45     private List<Attribute<?>> attributes;
 46     private List<ClassEntry> interfaces;
 47 
 48     public ClassImpl(byte[] cfbytes, ClassFileImpl context) {
 49         this.reader = new ClassReaderImpl(cfbytes, context);
 50         int p = reader.interfacesPos;
 51         int icnt = reader.readU2(p);
 52         p += 2 + icnt * 2;
 53         int fcnt = reader.readU2(p);
 54         FieldImpl[] fields = new FieldImpl[fcnt];
 55         p += 2;
 56         for (int i = 0; i < fcnt; ++i) {
 57             int startPos = p;
 58             int attrStart = p + 6;
 59             p = reader.skipAttributeHolder(attrStart);
 60             fields[i] = new FieldImpl(reader, startPos, p, attrStart);
 61         }
 62         this.fields = List.of(fields);
 63         int mcnt = reader.readU2(p);
 64         MethodImpl[] methods = new MethodImpl[mcnt];
 65         p += 2;
 66         for (int i = 0; i < mcnt; ++i) {
 67             int startPos = p;
 68             int attrStart = p + 6;
 69             p = reader.skipAttributeHolder(attrStart);
 70             methods[i] = new MethodImpl(reader, startPos, p, attrStart);
 71         }
 72         this.methods = List.of(methods);
 73         this.attributesPos = p;
 74         reader.setContainedClass(this);
 75     }
 76 
 77     public int classfileLength() {
 78         return reader.classfileLength();
 79     }
 80 
 81     @Override
 82     public AccessFlags flags() {
 83         return new AccessFlagsImpl(AccessFlag.Location.CLASS, reader.flags());
 84     }
 85 
 86     @Override
 87     public int majorVersion() {
 88         return reader.readU2(6);
 89     }
 90 
 91     @Override
 92     public int minorVersion() {
 93         return reader.readU2(4);
 94     }
 95 
 96     @Override
 97     public ConstantPool constantPool() {
 98         return reader;
 99     }
100 
101     @Override
102     public ClassEntry thisClass() {
103         return reader.thisClassEntry();
104     }
105 
106     @Override
107     public Optional<ClassEntry> superclass() {
108         return reader.superclassEntry();
109     }
110 
111     @Override
112     public List<ClassEntry> interfaces() {
113         if (interfaces == null) {
114             int pos = reader.thisClassPos() + 4;
115             int cnt = reader.readU2(pos);
116             pos += 2;
117             var arr = new Object[cnt];
118             for (int i = 0; i < cnt; ++i) {
119                 arr[i] = reader.readEntry(pos, ClassEntry.class);
120                 pos += 2;
121             }
122             this.interfaces = SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(arr);
123         }
124         return interfaces;
125     }
126 
127     @Override
128     public List<Attribute<?>> attributes() {
129         if (attributes == null) {
130             attributes = BoundAttribute.readAttributes(this, reader, attributesPos, reader.customAttributes());
131         }
132         return attributes;
133     }
134 
135     // ClassModel
136 
137     @Override
138     public void forEach(Consumer<? super ClassElement> consumer) {
139         consumer.accept(flags());
140         consumer.accept(ClassFileVersion.of(majorVersion(), minorVersion()));
141         superclass().ifPresent(new Consumer<ClassEntry>() {
142             @Override
143             public void accept(ClassEntry entry) {
144                 consumer.accept(Superclass.of(entry));
145             }
146         });
147         consumer.accept(Interfaces.of(interfaces()));
148         fields().forEach(consumer);
149         methods().forEach(consumer);
150         for (Attribute<?> attr : attributes()) {
151             if (attr instanceof ClassElement e)
152                 consumer.accept(e);
153         }
154     }
155 
156     @Override
157     public List<FieldModel> fields() {
158         return fields;
159     }
160 
161     @Override
162     public List<MethodModel> methods() {
163         return methods;
164     }
165 
166     @Override
167     public boolean isModuleInfo() {
168         AccessFlags flags = flags();
169         // move to where?
170         return flags.has(AccessFlag.MODULE)
171                && majorVersion() >= ClassFile.JAVA_9_VERSION
172                && thisClass().asInternalName().equals("module-info")
173                && (superclass().isEmpty())
174                && interfaces().isEmpty()
175                && fields().isEmpty()
176                && methods().isEmpty()
177                && verifyModuleAttributes();
178     }
179 
180     @Override
181     public String toString() {
182         return String.format("ClassModel[thisClass=%s, flags=%d]", thisClass().name().stringValue(), flags().flagsMask());
183     }
184 
185     private boolean verifyModuleAttributes() {
186         if (findAttribute(Attributes.module()).isEmpty())
187             return false;
188 
189         return attributes().stream().allMatch(a ->
190                 a instanceof ModuleAttribute
191              || a instanceof ModulePackagesAttribute
192              || a instanceof ModuleHashesAttribute
193              || a instanceof ModuleMainClassAttribute
194              || a instanceof ModuleResolutionAttribute
195              || a instanceof ModuleTargetAttribute
196              || a instanceof InnerClassesAttribute
197              || a instanceof SourceFileAttribute
198              || a instanceof SourceDebugExtensionAttribute
199              || a instanceof RuntimeVisibleAnnotationsAttribute
200              || a instanceof RuntimeInvisibleAnnotationsAttribute
201              || a instanceof CustomAttribute);
202     }
203 }