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