001 /*
002 * Copyright 2010-2014 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.codegen;
018
019 import com.google.common.collect.Sets;
020 import com.intellij.openapi.application.ApplicationManager;
021 import com.intellij.openapi.progress.ProcessCanceledException;
022 import com.intellij.openapi.util.io.FileUtil;
023 import com.intellij.openapi.vfs.VirtualFile;
024 import com.intellij.psi.PsiFile;
025 import com.intellij.util.ArrayUtil;
026 import com.intellij.util.PathUtil;
027 import org.jetbrains.annotations.NotNull;
028 import org.jetbrains.annotations.Nullable;
029 import org.jetbrains.asm4.AnnotationVisitor;
030 import org.jetbrains.asm4.Type;
031 import org.jetbrains.jet.codegen.context.CodegenContext;
032 import org.jetbrains.jet.codegen.context.FieldOwnerContext;
033 import org.jetbrains.jet.codegen.context.PackageContext;
034 import org.jetbrains.jet.codegen.state.GenerationState;
035 import org.jetbrains.jet.codegen.state.GenerationStateAware;
036 import org.jetbrains.jet.descriptors.serialization.BitEncoding;
037 import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer;
038 import org.jetbrains.jet.descriptors.serialization.PackageData;
039 import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
040 import org.jetbrains.jet.lang.descriptors.*;
041 import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils;
042 import org.jetbrains.jet.lang.psi.*;
043 import org.jetbrains.jet.lang.resolve.BindingContext;
044 import org.jetbrains.jet.lang.resolve.java.JvmAbi;
045 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
046 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
047 import org.jetbrains.jet.lang.resolve.name.FqName;
048 import org.jetbrains.jet.lang.resolve.name.Name;
049
050 import java.util.*;
051
052 import static org.jetbrains.asm4.Opcodes.*;
053 import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
054 import static org.jetbrains.jet.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses;
055 import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
056 import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;
057
058 public class PackageCodegen extends GenerationStateAware {
059 @NotNull
060 private final ClassBuilderOnDemand v;
061
062 @NotNull
063 private final FqName name;
064
065 @NotNull
066 private final Collection<JetFile> files;
067 private final Set<PackageFragmentDescriptor> packageFragments;
068
069 public PackageCodegen(
070 @NotNull ClassBuilderOnDemand v,
071 @NotNull final FqName fqName,
072 @NotNull GenerationState state,
073 @NotNull Collection<JetFile> packageFiles
074 ) {
075 super(state);
076 checkAllFilesHaveSamePackage(packageFiles);
077
078 this.v = v;
079 name = fqName;
080 this.files = packageFiles;
081
082 packageFragments = Sets.newHashSet();
083 for (JetFile file : packageFiles) {
084 packageFragments.add(getPackageFragment(file));
085 }
086
087 final PsiFile sourceFile = packageFiles.size() == 1 ? packageFiles.iterator().next().getContainingFile() : null;
088
089 v.addOptionalDeclaration(new ClassBuilderOnDemand.ClassBuilderCallback() {
090 @Override
091 public void doSomething(@NotNull ClassBuilder v) {
092 v.defineClass(sourceFile, V1_6,
093 ACC_PUBLIC | ACC_FINAL,
094 JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(fqName)).getInternalName(),
095 null,
096 "java/lang/Object",
097 ArrayUtil.EMPTY_STRING_ARRAY
098 );
099 //We don't generate any source information for package with multiple files
100 if (sourceFile != null) {
101 v.visitSource(sourceFile.getName(), null);
102 }
103 }
104 });
105 }
106
107 public void generate(@NotNull CompilationErrorHandler errorHandler) {
108 List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1);
109 boolean shouldGeneratePackageClass = shouldGeneratePackageClass(files);
110 if (shouldGeneratePackageClass) {
111 bindings.add(v.getClassBuilder().getSerializationBindings());
112 }
113
114 for (JetFile file : files) {
115 try {
116 ClassBuilder builder = generate(file);
117 if (builder != null) {
118 bindings.add(builder.getSerializationBindings());
119 }
120 }
121 catch (ProcessCanceledException e) {
122 throw e;
123 }
124 catch (Throwable e) {
125 VirtualFile vFile = file.getVirtualFile();
126 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
127 DiagnosticUtils.throwIfRunningOnServer(e);
128 if (ApplicationManager.getApplication().isInternal()) {
129 //noinspection CallToPrintStackTrace
130 e.printStackTrace();
131 }
132 }
133 }
134
135 if (shouldGeneratePackageClass) {
136 writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings));
137 }
138
139 assert v.isActivated() == shouldGeneratePackageClass :
140 "Different algorithms for generating package class and for heuristics for: " + name.asString();
141 }
142
143 private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) {
144 if (state.getClassBuilderMode() != ClassBuilderMode.FULL) {
145 return;
146 }
147
148 for (JetFile file : files) {
149 if (file.isScript()) return;
150 }
151
152 DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(bindings));
153 ProtoBuf.Package packageProto = serializer.packageProto(packageFragments).build();
154
155 if (packageProto.getMemberCount() == 0) return;
156
157 PackageData data = new PackageData(createNameResolver(serializer.getNameTable()), packageProto);
158
159 AnnotationVisitor av =
160 v.getClassBuilder().newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true);
161 av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
162 AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
163 for (String string : BitEncoding.encodeBytes(data.toBytes())) {
164 array.visit(null, string);
165 }
166 array.visitEnd();
167 av.visitEnd();
168 }
169
170 @Nullable
171 private ClassBuilder generate(@NotNull JetFile file) {
172 boolean generateSrcClass = false;
173 Type packagePartType = getPackagePartType(getPackageClassFqName(name), file.getVirtualFile());
174 PackageContext packagePartContext = CodegenContext.STATIC.intoPackagePart(getPackageFragment(file), packagePartType);
175
176 for (JetDeclaration declaration : file.getDeclarations()) {
177 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
178 generateSrcClass = true;
179 }
180 else if (declaration instanceof JetClassOrObject) {
181 JetClassOrObject classOrObject = (JetClassOrObject) declaration;
182 if (state.getGenerateDeclaredClassFilter().shouldProcess(classOrObject)) {
183 generateClassOrObject(classOrObject);
184 }
185 }
186 else if (declaration instanceof JetScript) {
187 ScriptCodegen.createScriptCodegen((JetScript) declaration, state, packagePartContext).generate();
188 }
189 }
190
191 if (!generateSrcClass) return null;
192
193 ClassBuilder builder = state.getFactory().forPackagePart(packagePartType, file);
194
195 new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate();
196
197 FieldOwnerContext packageFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, getPackageFragment(file));
198 //TODO: FIX: Default method generated at facade without delegation
199 MemberCodegen memberCodegen = new MemberCodegen(state, null, packageFacade, null) {
200 @NotNull
201 @Override
202 public ClassBuilder getBuilder() {
203 return v.getClassBuilder();
204 }
205 };
206 for (JetDeclaration declaration : file.getDeclarations()) {
207 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
208 memberCodegen.genFunctionOrProperty(packageFacade, (JetTypeParameterListOwner) declaration, v.getClassBuilder());
209 }
210 }
211
212 return builder;
213 }
214
215 @NotNull
216 private PackageFragmentDescriptor getPackageFragment(@NotNull JetFile file) {
217 PackageFragmentDescriptor packageFragment = bindingContext.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
218 assert packageFragment != null : "package fragment is null for " + file;
219 return packageFragment;
220 }
221
222 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) {
223 JetFile file = (JetFile) classOrObject.getContainingFile();
224 Type packagePartType = getPackagePartType(getPackageClassFqName(name), file.getVirtualFile());
225 CodegenContext context = CodegenContext.STATIC.intoPackagePart(getPackageFragment(file), packagePartType);
226 MemberCodegen.genClassOrObject(context, classOrObject, state, null);
227 }
228
229 /**
230 * @param packageFiles all files should have same package name
231 * @return
232 */
233 public static boolean shouldGeneratePackageClass(@NotNull Collection<JetFile> packageFiles) {
234 checkAllFilesHaveSamePackage(packageFiles);
235
236 for (JetFile file : packageFiles) {
237 for (JetDeclaration declaration : file.getDeclarations()) {
238 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
239 return true;
240 }
241 }
242 }
243
244 return false;
245 }
246
247 private static void checkAllFilesHaveSamePackage(Collection<JetFile> packageFiles) {
248 FqName commonFqName = null;
249 for (JetFile file : packageFiles) {
250 FqName fqName = JetPsiUtil.getFQName(file);
251 if (commonFqName != null) {
252 if (!commonFqName.equals(fqName)) {
253 throw new IllegalArgumentException("All files should have same package name");
254 }
255 }
256 else {
257 commonFqName = JetPsiUtil.getFQName(file);
258 }
259 }
260 }
261
262 public void done() {
263 v.done();
264 }
265
266 @NotNull
267 public static Type getPackagePartType(@NotNull FqName facadeFqName, @NotNull VirtualFile file) {
268 String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName()));
269
270 // path hashCode to prevent same name / different path collision
271 String srcName = facadeFqName.shortName().asString() + "-" + replaceSpecialSymbols(fileName) + "-" + Integer.toHexString(
272 CodegenUtil.getPathHashCode(file));
273
274 FqName srcFqName = facadeFqName.parent().child(Name.identifier(srcName));
275
276 return asmTypeByFqNameWithoutInnerClasses(srcFqName);
277 }
278
279 @NotNull
280 private static String replaceSpecialSymbols(@NotNull String str) {
281 return str.replace('.', '_');
282 }
283
284 @NotNull
285 public static String getPackagePartInternalName(@NotNull JetFile file) {
286 FqName packageFqName = JetPsiUtil.getFQName(file);
287 return getPackagePartType(getPackageClassFqName(packageFqName), file.getVirtualFile()).getInternalName();
288 }
289 }