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