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