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