001 /* 002 * Copyright 2010-2015 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.kotlin.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.vfs.VirtualFile; 024 import com.intellij.util.ArrayUtil; 025 import com.intellij.util.SmartList; 026 import com.intellij.util.containers.ContainerUtil; 027 import kotlin.jvm.functions.Function0; 028 import kotlin.jvm.functions.Function1; 029 import org.jetbrains.annotations.Mutable; 030 import org.jetbrains.annotations.NotNull; 031 import org.jetbrains.annotations.Nullable; 032 import org.jetbrains.kotlin.codegen.context.CodegenContext; 033 import org.jetbrains.kotlin.codegen.context.FieldOwnerContext; 034 import org.jetbrains.kotlin.codegen.context.MethodContext; 035 import org.jetbrains.kotlin.codegen.context.PackageContext; 036 import org.jetbrains.kotlin.codegen.state.GenerationState; 037 import org.jetbrains.kotlin.config.IncrementalCompilation; 038 import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor; 039 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; 040 import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; 041 import org.jetbrains.kotlin.diagnostics.DiagnosticUtils; 042 import org.jetbrains.kotlin.fileClasses.FileClassesPackage; 043 import org.jetbrains.kotlin.load.java.JvmAbi; 044 import org.jetbrains.kotlin.load.java.JvmAnnotationNames; 045 import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; 046 import org.jetbrains.kotlin.load.kotlin.PackageParts; 047 import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider; 048 import org.jetbrains.kotlin.name.FqName; 049 import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; 050 import org.jetbrains.kotlin.psi.*; 051 import org.jetbrains.kotlin.resolve.BindingContext; 052 import org.jetbrains.kotlin.resolve.MemberComparator; 053 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; 054 import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter; 055 import org.jetbrains.kotlin.resolve.scopes.JetScope; 056 import org.jetbrains.kotlin.serialization.*; 057 import org.jetbrains.kotlin.serialization.deserialization.NameResolver; 058 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; 059 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor; 060 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor; 061 import org.jetbrains.kotlin.serialization.jvm.BitEncoding; 062 import org.jetbrains.org.objectweb.asm.AnnotationVisitor; 063 import org.jetbrains.org.objectweb.asm.MethodVisitor; 064 import org.jetbrains.org.objectweb.asm.Type; 065 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 066 import org.jetbrains.org.objectweb.asm.commons.Method; 067 068 import java.util.*; 069 070 import static org.jetbrains.kotlin.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses; 071 import static org.jetbrains.kotlin.codegen.AsmUtil.method; 072 import static org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassFqName; 073 import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.K_PACKAGE_TYPE; 074 import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.getType; 075 import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.*; 076 import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN; 077 import static org.jetbrains.org.objectweb.asm.Opcodes.*; 078 079 public class PackageCodegen { 080 private final ClassBuilderOnDemand v; 081 private final GenerationState state; 082 private final Collection<JetFile> files; 083 private final Type packageClassType; 084 private final PackageFragmentDescriptor packageFragment; 085 private final PackageFragmentDescriptor compiledPackageFragment; 086 private final List<DeserializedCallableMemberDescriptor> previouslyCompiledCallables; 087 088 private final PackageParts packageParts; 089 090 public PackageCodegen(@NotNull GenerationState state, @NotNull Collection<JetFile> files, @NotNull FqName fqName) { 091 this.state = state; 092 this.files = files; 093 this.packageFragment = getOnlyPackageFragment(fqName); 094 this.packageClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(getPackageClassFqName(fqName)); 095 this.compiledPackageFragment = getCompiledPackageFragment(fqName); 096 this.previouslyCompiledCallables = filterDeserializedCallables(compiledPackageFragment); 097 098 assert packageFragment != null || compiledPackageFragment != null : fqName.asString() + " " + files; 099 100 this.v = new ClassBuilderOnDemand(new Function0<ClassBuilder>() { 101 @Override 102 public ClassBuilder invoke() { 103 Collection<JetFile> files = PackageCodegen.this.files; 104 JetFile sourceFile = getRepresentativePackageFile(files); 105 106 ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor( 107 PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment), 108 packageClassType, PackagePartClassUtils.getFilesWithCallables(files) 109 ); 110 v.defineClass(sourceFile, V1_6, 111 ACC_PUBLIC | ACC_FINAL, 112 packageClassType.getInternalName(), 113 null, 114 "java/lang/Object", 115 ArrayUtil.EMPTY_STRING_ARRAY 116 ); 117 //We don't generate any source information for package with multiple files 118 if (sourceFile != null) { 119 v.visitSource(sourceFile.getName(), null); 120 } 121 return v; 122 } 123 }); 124 packageParts = new PackageParts(fqName.asString()); 125 } 126 127 // Returns null if file has callables in several files 128 @Nullable 129 private JetFile getRepresentativePackageFile(@NotNull Collection<JetFile> packageFiles) { 130 if (!previouslyCompiledCallables.isEmpty()) { 131 return null; 132 } 133 134 List<JetFile> packageFilesWithCallables = PackagePartClassUtils.getFilesWithCallables(packageFiles); 135 return packageFilesWithCallables.size() == 1 ? packageFilesWithCallables.get(0) : null; 136 } 137 138 @Nullable 139 private PackageFragmentDescriptor getCompiledPackageFragment(@NotNull FqName fqName) { 140 if (!IncrementalCompilation.ENABLED) { 141 return null; 142 } 143 144 // TODO rewrite it to something more robust when module system is implemented 145 for (PackageFragmentDescriptor fragment : state.getModule().getPackage(fqName).getFragments()) { 146 if (fragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment && 147 ((IncrementalPackageFragmentProvider.IncrementalPackageFragment) fragment).getTarget().equals(state.getTargetId())) { 148 return fragment; 149 } 150 } 151 return null; 152 } 153 154 @NotNull 155 private static List<DeserializedCallableMemberDescriptor> filterDeserializedCallables(@Nullable PackageFragmentDescriptor packageFragment) { 156 if (packageFragment == null) { 157 return Collections.emptyList(); 158 } 159 List<DeserializedCallableMemberDescriptor> callables = Lists.newArrayList(); 160 for (DeclarationDescriptor member : packageFragment.getMemberScope().getDescriptors(DescriptorKindFilter.CALLABLES, JetScope.ALL_NAME_FILTER)) { 161 if (member instanceof DeserializedCallableMemberDescriptor) { 162 callables.add((DeserializedCallableMemberDescriptor) member); 163 } 164 } 165 return callables; 166 } 167 168 private void generateDelegationsToPreviouslyCompiled(@NotNull @Mutable Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) { 169 for (final DeserializedCallableMemberDescriptor member : previouslyCompiledCallables) { 170 generateCallableMemberTasks.put(member, new Runnable() { 171 @Override 172 public void run() { 173 FieldOwnerContext context = state.getRootContext().intoPackageFacade( 174 AsmUtil.asmTypeByFqNameWithoutInnerClasses(PackagePartClassUtils.getPackagePartFqName(member)), 175 compiledPackageFragment 176 ); 177 178 MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(context); 179 180 if (member instanceof DeserializedSimpleFunctionDescriptor) { 181 DeserializedSimpleFunctionDescriptor function = (DeserializedSimpleFunctionDescriptor) member; 182 memberCodegen.functionCodegen.generateMethod(OtherOrigin(function), function, 183 new FunctionGenerationStrategy() { 184 @Override 185 public void generateBody( 186 @NotNull MethodVisitor mv, 187 @NotNull FrameMap frameMap, 188 @NotNull JvmMethodSignature signature, 189 @NotNull MethodContext context, 190 @NotNull MemberCodegen<?> parentCodegen 191 ) { 192 throw new IllegalStateException("shouldn't be called"); 193 } 194 } 195 ); 196 197 memberCodegen.functionCodegen.generateDefaultIfNeeded( 198 context.intoFunction(function), function, OwnerKind.PACKAGE, DefaultParameterValueLoader.DEFAULT, null 199 ); 200 201 memberCodegen.functionCodegen.generateOverloadsWithDefaultValues(null, function, function); 202 } 203 else if (member instanceof DeserializedPropertyDescriptor) { 204 memberCodegen.propertyCodegen.generateInPackageFacade((DeserializedPropertyDescriptor) member); 205 } 206 else { 207 throw new IllegalStateException("Unexpected member: " + member); 208 } 209 } 210 }); 211 } 212 } 213 214 public void generate(@NotNull CompilationErrorHandler errorHandler) { 215 List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1); 216 217 Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks = new HashMap<CallableMemberDescriptor, Runnable>(); 218 219 for (JetFile file : files) { 220 ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); 221 try { 222 ClassBuilder builder = generate(file, generateCallableMemberTasks); 223 if (builder != null) { 224 bindings.add(builder.getSerializationBindings()); 225 } 226 } 227 catch (ProcessCanceledException e) { 228 throw e; 229 } 230 catch (Throwable e) { 231 VirtualFile vFile = file.getVirtualFile(); 232 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl()); 233 DiagnosticUtils.throwIfRunningOnServer(e); 234 if (ApplicationManager.getApplication().isInternal()) { 235 //noinspection CallToPrintStackTrace 236 e.printStackTrace(); 237 } 238 } 239 } 240 241 generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks); 242 243 if (!generateCallableMemberTasks.isEmpty()) { 244 generatePackageFacadeClass(generateCallableMemberTasks, bindings); 245 } 246 } 247 248 private void generatePackageFacadeClass( 249 @NotNull Map<CallableMemberDescriptor, Runnable> tasks, 250 @NotNull List<JvmSerializationBindings> bindings 251 ) { 252 generateKotlinPackageReflectionField(); 253 MemberCodegen.generateModuleNameField(state, v); 254 255 for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(tasks.keySet())) { 256 tasks.get(member).run(); 257 } 258 259 bindings.add(v.getSerializationBindings()); 260 writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings), tasks.keySet()); 261 } 262 263 private void generateKotlinPackageReflectionField() { 264 MethodVisitor mv = v.newMethod(NO_ORIGIN, ACC_STATIC, "<clinit>", "()V", null, null); 265 Method method = method("createKotlinPackage", K_PACKAGE_TYPE, getType(Class.class), getType(String.class)); 266 InstructionAdapter iv = new InstructionAdapter(mv); 267 MemberCodegen.generateReflectionObjectField(state, packageClassType, v, method, JvmAbi.KOTLIN_PACKAGE_FIELD_NAME, iv); 268 iv.areturn(Type.VOID_TYPE); 269 FunctionCodegen.endVisit(mv, "package facade static initializer", null); 270 } 271 272 private void writeKotlinPackageAnnotationIfNeeded( 273 @NotNull JvmSerializationBindings bindings, 274 @NotNull final Collection<CallableMemberDescriptor> relevantCallables 275 ) { 276 if (state.getClassBuilderMode() != ClassBuilderMode.FULL) { 277 return; 278 } 279 280 // SCRIPT: Do not write annotations for scripts (if any is??) 281 for (JetFile file : files) { 282 if (file.isScript()) return; 283 } 284 285 DescriptorSerializer serializer = DescriptorSerializer.createTopLevel(new JvmSerializerExtension(bindings, state.getTypeMapper())); 286 Collection<PackageFragmentDescriptor> packageFragments = Lists.newArrayList(); 287 ContainerUtil.addIfNotNull(packageFragments, packageFragment); 288 ContainerUtil.addIfNotNull(packageFragments, compiledPackageFragment); 289 ProtoBuf.Package packageProto = serializer.packageProto(packageFragments, new Function1<DeclarationDescriptor, Boolean>() { 290 @Override 291 public Boolean invoke(DeclarationDescriptor descriptor) { 292 // return !(descriptor instanceof CallableMemberDescriptor && relevantCallables.contains(descriptor)); 293 return true; 294 } 295 }).build(); 296 297 //if (packageProto.getMemberCount() == 0) return; 298 299 StringTable strings = serializer.getStringTable(); 300 NameResolver nameResolver = new NameResolver(strings.serializeSimpleNames(), strings.serializeQualifiedNames()); 301 PackageData data = new PackageData(nameResolver, packageProto); 302 303 AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true); 304 JvmCodegenUtil.writeAbiVersion(av); 305 AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME); 306 for (String string : BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) { 307 array.visit(null, string); 308 } 309 array.visitEnd(); 310 av.visitEnd(); 311 } 312 313 @Nullable 314 private ClassBuilder generate(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) { 315 boolean generatePackagePart = false; 316 Type packagePartType = FileClassesPackage.getFileClassType(state.getFileClassesProvider(), file); 317 PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, packagePartType); 318 319 for (JetDeclaration declaration : file.getDeclarations()) { 320 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 321 generatePackagePart = true; 322 } 323 else if (declaration instanceof JetClassOrObject) { 324 JetClassOrObject classOrObject = (JetClassOrObject) declaration; 325 if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) { 326 generateClassOrObject(classOrObject); 327 } 328 } 329 else if (declaration instanceof JetScript) { 330 JetScript script = (JetScript) declaration; 331 332 // SCRIPT: generate script code, should be separate execution branch 333 if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) { 334 ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate(); 335 } 336 } 337 } 338 339 if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return null; 340 341 String name = packagePartType.getInternalName(); 342 packageParts.getParts().add(name.substring(name.lastIndexOf('/') + 1)); 343 344 ClassBuilder builder = state.getFactory().newVisitor(PackagePart(file, packageFragment), packagePartType, file); 345 346 new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate(); 347 348 FieldOwnerContext packageFacade = state.getRootContext().intoPackageFacade(packagePartType, packageFragment); 349 350 final MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(packageFacade); 351 352 for (final JetDeclaration declaration : file.getDeclarations()) { 353 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) { 354 DeclarationDescriptor descriptor = state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration); 355 assert descriptor instanceof CallableMemberDescriptor : 356 "Expected callable member, was " + descriptor + " for " + declaration.getText(); 357 generateCallableMemberTasks.put( 358 (CallableMemberDescriptor) descriptor, 359 new Runnable() { 360 @Override 361 public void run() { 362 memberCodegen.genFunctionOrProperty(declaration); 363 } 364 } 365 ); 366 } 367 } 368 369 return builder; 370 } 371 372 private MemberCodegen<?> createCodegenForPartOfPackageFacade(@NotNull FieldOwnerContext packageFacade) { 373 return new MemberCodegen<JetFile>(state, null, packageFacade, null, v) { 374 @Override 375 protected void generateDeclaration() { 376 throw new UnsupportedOperationException(); 377 } 378 379 @Override 380 protected void generateBody() { 381 throw new UnsupportedOperationException(); 382 } 383 384 @Override 385 protected void generateKotlinAnnotation() { 386 throw new UnsupportedOperationException(); 387 } 388 }; 389 } 390 391 @Nullable 392 private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedFqName) { 393 SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>(); 394 for (JetFile file : files) { 395 PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); 396 assert fragment != null : "package fragment is null for " + file + "\n" + file.getText(); 397 398 assert expectedFqName.equals(fragment.getFqName()) : 399 "expected package fq name: " + expectedFqName + ", actual: " + fragment.getFqName(); 400 401 if (!fragments.contains(fragment)) { 402 fragments.add(fragment); 403 } 404 } 405 if (fragments.size() > 1) { 406 throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments); 407 } 408 409 if (fragments.isEmpty()) { 410 return null; 411 } 412 return fragments.get(0); 413 } 414 415 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) { 416 JetFile file = classOrObject.getContainingJetFile(); 417 Type packagePartType = FileClassesPackage.getFileClassType(state.getFileClassesProvider(), file); 418 CodegenContext context = state.getRootContext().intoPackagePart(packageFragment, packagePartType); 419 MemberCodegen.genClassOrObject(context, classOrObject, state, null); 420 } 421 422 public void done() { 423 v.done(); 424 } 425 426 public PackageParts getPackageParts() { 427 return packageParts; 428 } 429 430 public Collection<JetFile> getFiles() { 431 return files; 432 } 433 }