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