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.Lists;
020 import com.google.common.collect.Ordering;
021 import com.intellij.openapi.application.ApplicationManager;
022 import com.intellij.openapi.progress.ProcessCanceledException;
023 import com.intellij.openapi.util.io.FileUtil;
024 import com.intellij.openapi.vfs.VirtualFile;
025 import com.intellij.psi.PsiFile;
026 import com.intellij.util.ArrayUtil;
027 import com.intellij.util.PathUtil;
028 import com.intellij.util.SmartList;
029 import org.jetbrains.annotations.NotNull;
030 import org.jetbrains.annotations.Nullable;
031 import org.jetbrains.jet.codegen.context.CodegenContext;
032 import org.jetbrains.jet.codegen.context.FieldOwnerContext;
033 import org.jetbrains.jet.codegen.context.MethodContext;
034 import org.jetbrains.jet.codegen.context.PackageContext;
035 import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
036 import org.jetbrains.jet.codegen.state.GenerationState;
037 import org.jetbrains.jet.config.IncrementalCompilation;
038 import org.jetbrains.jet.descriptors.serialization.BitEncoding;
039 import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer;
040 import org.jetbrains.jet.descriptors.serialization.PackageData;
041 import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
042 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor;
043 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPropertyDescriptor;
044 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
045 import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
046 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
047 import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor;
048 import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils;
049 import org.jetbrains.jet.lang.psi.*;
050 import org.jetbrains.jet.lang.resolve.BindingContext;
051 import org.jetbrains.jet.lang.resolve.MemberComparator;
052 import org.jetbrains.jet.lang.resolve.java.JvmAbi;
053 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
054 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
055 import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyJavaPackageFragmentScope;
056 import org.jetbrains.jet.lang.resolve.kotlin.BaseDescriptorDeserializer;
057 import org.jetbrains.jet.lang.resolve.name.FqName;
058 import org.jetbrains.jet.lang.resolve.name.Name;
059 import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
060 import org.jetbrains.org.objectweb.asm.MethodVisitor;
061 import org.jetbrains.org.objectweb.asm.Type;
062
063 import java.util.*;
064
065 import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
066 import static org.jetbrains.jet.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses;
067 import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
068 import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;
069 import static org.jetbrains.org.objectweb.asm.Opcodes.*;
070
071 public class PackageCodegen {
072 private final GenerationState state;
073 private final ClassBuilderOnDemand v;
074 private final Collection<JetFile> files;
075 private final PackageFragmentDescriptor packageFragment;
076 private final PackageFragmentDescriptor compiledPackageFragment;
077
078 public PackageCodegen(
079 @NotNull ClassBuilderOnDemand v,
080 @NotNull final FqName fqName,
081 @NotNull GenerationState state,
082 @NotNull Collection<JetFile> packageFiles
083 ) {
084 this.state = state;
085 this.v = v;
086 this.files = packageFiles;
087 this.packageFragment = getOnlyPackageFragment();
088 this.compiledPackageFragment = getCompiledPackageFragment();
089
090 final PsiFile sourceFile = packageFiles.size() == 1 && getPreviouslyCompiledCallables().isEmpty()
091 ? packageFiles.iterator().next().getContainingFile() : null;
092
093 v.addOptionalDeclaration(new ClassBuilderOnDemand.ClassBuilderCallback() {
094 @Override
095 public void doSomething(@NotNull ClassBuilder v) {
096 v.defineClass(sourceFile, V1_6,
097 ACC_PUBLIC | ACC_FINAL,
098 JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(fqName)).getInternalName(),
099 null,
100 "java/lang/Object",
101 ArrayUtil.EMPTY_STRING_ARRAY
102 );
103 //We don't generate any source information for package with multiple files
104 if (sourceFile != null) {
105 v.visitSource(sourceFile.getName(), null);
106 }
107 }
108 });
109 }
110
111 @Nullable
112 private PackageFragmentDescriptor getCompiledPackageFragment() {
113 if (!IncrementalCompilation.ENABLED) {
114 return null;
115 }
116
117 // TODO rewrite it to something more robust when module system is implemented
118 for (PackageFragmentDescriptor anotherFragment : packageFragment.getContainingDeclaration().getPackageFragmentProvider()
119 .getPackageFragments(packageFragment.getFqName())) {
120 if (anotherFragment.getMemberScope() instanceof LazyJavaPackageFragmentScope) {
121 return anotherFragment;
122 }
123 }
124 return null;
125 }
126
127 @NotNull
128 private List<DeserializedCallableMemberDescriptor> getPreviouslyCompiledCallables() {
129 List<DeserializedCallableMemberDescriptor> callables = Lists.newArrayList();
130 if (compiledPackageFragment != null) {
131 for (DeclarationDescriptor member : compiledPackageFragment.getMemberScope().getAllDescriptors()) {
132 if (member instanceof DeserializedCallableMemberDescriptor) {
133 callables.add((DeserializedCallableMemberDescriptor) member);
134 }
135 }
136 }
137 return callables;
138 }
139
140 private void generateDelegationsToPreviouslyCompiled(Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) {
141 for (final DeserializedCallableMemberDescriptor member : getPreviouslyCompiledCallables()) {
142 generateCallableMemberTasks.put(member, new Runnable() {
143 @Override
144 public void run() {
145 FieldOwnerContext context = CodegenContext.STATIC.intoPackageFacade(
146 Type.getObjectType(getPackagePartInternalName(member)),
147 compiledPackageFragment);
148
149 FunctionCodegen functionCodegen = new FunctionCodegen(
150 context,
151 v.getClassBuilder(),
152 state,
153 getMemberCodegen(context)
154 );
155
156 if (member instanceof DeserializedSimpleFunctionDescriptor) {
157 DeserializedSimpleFunctionDescriptor function = (DeserializedSimpleFunctionDescriptor) member;
158 JvmMethodSignature signature = state.getTypeMapper().mapSignature(function, OwnerKind.PACKAGE);
159 functionCodegen.generateMethod(null, signature, function,
160 new FunctionGenerationStrategy() {
161 @Override
162 public void generateBody(
163 @NotNull MethodVisitor mv,
164 @NotNull JvmMethodSignature signature,
165 @NotNull MethodContext context,
166 @Nullable MemberCodegen<?> parentCodegen
167 ) {
168 throw new IllegalStateException("shouldn't be called");
169 }
170 }
171 );
172 }
173 else if (member instanceof DeserializedPropertyDescriptor) {
174 PropertyCodegen propertyCodegen = new PropertyCodegen(
175 context, v.getClassBuilder(), functionCodegen, getMemberCodegen(context));
176 propertyCodegen.generateInPackageFacade((DeserializedPropertyDescriptor) member);
177 }
178 else {
179 throw new IllegalStateException("Unexpected member: " + member);
180 }
181 }
182 });
183 }
184 }
185
186 public void generate(@NotNull CompilationErrorHandler errorHandler) {
187 List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1);
188 boolean shouldGeneratePackageClass = shouldGeneratePackageClass(files);
189 if (shouldGeneratePackageClass) {
190 bindings.add(v.getClassBuilder().getSerializationBindings());
191 }
192
193 Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks = new HashMap<CallableMemberDescriptor, Runnable>();
194
195 for (JetFile file : files) {
196 try {
197 ClassBuilder builder = generate(file, generateCallableMemberTasks);
198 if (builder != null) {
199 bindings.add(builder.getSerializationBindings());
200 }
201 }
202 catch (ProcessCanceledException e) {
203 throw e;
204 }
205 catch (Throwable e) {
206 VirtualFile vFile = file.getVirtualFile();
207 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
208 DiagnosticUtils.throwIfRunningOnServer(e);
209 if (ApplicationManager.getApplication().isInternal()) {
210 //noinspection CallToPrintStackTrace
211 e.printStackTrace();
212 }
213 }
214 }
215
216 if (shouldGeneratePackageClass) {
217 // Shouldn't generate delegations to previously compiled if we compile only "classes" part of a package.
218 generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks);
219 }
220
221 for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(generateCallableMemberTasks.keySet())) {
222 generateCallableMemberTasks.get(member).run();
223 }
224
225 if (shouldGeneratePackageClass) {
226 writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings));
227 }
228
229 assert v.isActivated() == shouldGeneratePackageClass :
230 "Different algorithms for generating package class and for heuristics for: " + packageFragment;
231 }
232
233 private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) {
234 if (state.getClassBuilderMode() != ClassBuilderMode.FULL) {
235 return;
236 }
237
238 // SCRIPT: Do not write annotations for scripts (if any is??)
239 for (JetFile file : files) {
240 if (file.isScript()) return;
241 }
242
243 DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(bindings));
244 Collection<PackageFragmentDescriptor> packageFragments = compiledPackageFragment == null
245 ? Collections.singleton(packageFragment)
246 : Arrays.asList(packageFragment, compiledPackageFragment);
247 ProtoBuf.Package packageProto = serializer.packageProto(packageFragments).build();
248
249 if (packageProto.getMemberCount() == 0) return;
250
251 PackageData data = new PackageData(createNameResolver(serializer.getNameTable()), packageProto);
252
253 AnnotationVisitor av =
254 v.getClassBuilder().newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true);
255 av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
256 AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
257 for (String string : BitEncoding.encodeBytes(data.toBytes())) {
258 array.visit(null, string);
259 }
260 array.visitEnd();
261 av.visitEnd();
262 }
263
264 @Nullable
265 private ClassBuilder generate(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) {
266 boolean generateSrcClass = false;
267 Type packagePartType = getPackagePartType(getPackageClassFqName(packageFragment.getFqName()), file.getVirtualFile());
268 PackageContext packagePartContext = CodegenContext.STATIC.intoPackagePart(packageFragment, packagePartType);
269
270 for (JetDeclaration declaration : file.getDeclarations()) {
271 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
272 generateSrcClass = true;
273 }
274 else if (declaration instanceof JetClassOrObject) {
275 JetClassOrObject classOrObject = (JetClassOrObject) declaration;
276 if (state.getGenerateDeclaredClassFilter().shouldProcess(classOrObject)) {
277 generateClassOrObject(classOrObject);
278 }
279 }
280 else if (declaration instanceof JetScript) {
281 // SCRIPT: generate script code, should be separate execution branch
282 ScriptCodegen.createScriptCodegen((JetScript) declaration, state, packagePartContext).generate();
283 }
284 }
285
286 if (!generateSrcClass) return null;
287
288 ClassBuilder builder = state.getFactory().forPackagePart(packagePartType, file);
289
290 new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate();
291
292 FieldOwnerContext packageFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, packageFragment);
293
294 final MemberCodegen<?> memberCodegen = getMemberCodegen(packageFacade);
295
296 for (final JetDeclaration declaration : file.getDeclarations()) {
297 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
298 DeclarationDescriptor descriptor = state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration);
299 assert descriptor instanceof CallableMemberDescriptor :
300 "Expected callable member, was " + descriptor + " for " + declaration.getText();
301 generateCallableMemberTasks.put(
302 (CallableMemberDescriptor) descriptor,
303 new Runnable() {
304 @Override
305 public void run() {
306 memberCodegen.genFunctionOrProperty((JetTypeParameterListOwner) declaration, v.getClassBuilder());
307 }
308 }
309 );
310 }
311 }
312
313 return builder;
314 }
315
316 //TODO: FIX: Default method generated at facade without delegation
317 private MemberCodegen<?> getMemberCodegen(@NotNull FieldOwnerContext packageFacade) {
318 return new MemberCodegen<JetFile>(state, null, packageFacade, null, null) {
319 @Override
320 protected void generateDeclaration() {
321 throw new UnsupportedOperationException();
322 }
323
324 @Override
325 protected void generateBody() {
326 throw new UnsupportedOperationException();
327 }
328
329 @Override
330 protected void generateKotlinAnnotation() {
331 throw new UnsupportedOperationException();
332 }
333 };
334 }
335
336 @NotNull
337 private PackageFragmentDescriptor getOnlyPackageFragment() {
338 SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>();
339 for (JetFile file : files) {
340 PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
341 assert fragment != null : "package fragment is null for " + file;
342
343 if (!fragments.contains(fragment)) {
344 fragments.add(fragment);
345 }
346 }
347 if (fragments.size() != 1) {
348 throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments);
349 }
350 return fragments.get(0);
351 }
352
353 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) {
354 JetFile file = classOrObject.getContainingJetFile();
355 Type packagePartType = getPackagePartType(getPackageClassFqName(packageFragment.getFqName()), file.getVirtualFile());
356 CodegenContext context = CodegenContext.STATIC.intoPackagePart(packageFragment, packagePartType);
357 MemberCodegen.genClassOrObject(context, classOrObject, state, null);
358 }
359
360 /**
361 * @param packageFiles all files should have same package name
362 * @return
363 */
364 public static boolean shouldGeneratePackageClass(@NotNull Collection<JetFile> packageFiles) {
365 for (JetFile file : packageFiles) {
366 for (JetDeclaration declaration : file.getDeclarations()) {
367 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
368 return true;
369 }
370 }
371 }
372
373 return false;
374 }
375
376 public void done() {
377 v.done();
378 }
379
380 @NotNull
381 public static Type getPackagePartType(@NotNull FqName facadeFqName, @NotNull VirtualFile file) {
382 String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName()));
383
384 // path hashCode to prevent same name / different path collision
385 String srcName = facadeFqName.shortName().asString() + "-" + replaceSpecialSymbols(fileName) + "-" + Integer.toHexString(
386 JvmCodegenUtil.getPathHashCode(file));
387
388 return asmTypeByFqNameWithoutInnerClasses(facadeFqName.parent().child(Name.identifier(srcName)));
389 }
390
391 @NotNull
392 private static String replaceSpecialSymbols(@NotNull String str) {
393 return str.replace('.', '_');
394 }
395
396 @NotNull
397 public static String getPackagePartInternalName(@NotNull JetFile file) {
398 FqName packageFqName = file.getPackageFqName();
399 return getPackagePartType(getPackageClassFqName(packageFqName), file.getVirtualFile()).getInternalName();
400 }
401
402 @NotNull
403 public static String getPackagePartInternalName(@NotNull DeserializedCallableMemberDescriptor callable) {
404 FqName packageFqName = ((PackageFragmentDescriptor) callable.getContainingDeclaration()).getFqName();
405 FqName packagePartFqName = packageFqName.child(BaseDescriptorDeserializer.getPackagePartClassName(callable));
406 return JvmClassName.byFqNameWithoutInnerClasses(packagePartFqName).getInternalName();
407 }
408 }