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.jet.lang.resolve.scopes.DescriptorKindFilter;
058 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
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.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
068 import static org.jetbrains.jet.codegen.AsmUtil.method;
069 import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
070 import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.K_PACKAGE_IMPL_TYPE;
071 import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.getType;
072 import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;
073 import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.*;
074 import static org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
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 JavaSerializerExtension(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.getNameTable()), 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().shouldProcess(classOrObject)) {
308 generateClassOrObject(classOrObject);
309 }
310 }
311 else if (declaration instanceof JetScript) {
312 // SCRIPT: generate script code, should be separate execution branch
313 ScriptCodegen.createScriptCodegen((JetScript) declaration, state, packagePartContext).generate();
314 }
315 }
316
317 if (!generatePackagePart) return null;
318
319 ClassBuilder builder = state.getFactory().newVisitor(PackagePart(file, packageFragment), packagePartType, file);
320
321 new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate();
322
323 FieldOwnerContext packageFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, packageFragment);
324
325 final MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(packageFacade);
326
327 for (final JetDeclaration declaration : file.getDeclarations()) {
328 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
329 DeclarationDescriptor descriptor = state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration);
330 assert descriptor instanceof CallableMemberDescriptor :
331 "Expected callable member, was " + descriptor + " for " + declaration.getText();
332 generateCallableMemberTasks.put(
333 (CallableMemberDescriptor) descriptor,
334 new Runnable() {
335 @Override
336 public void run() {
337 memberCodegen.genFunctionOrProperty(declaration);
338 }
339 }
340 );
341 }
342 }
343
344 return builder;
345 }
346
347 private MemberCodegen<?> createCodegenForPartOfPackageFacade(@NotNull FieldOwnerContext packageFacade) {
348 return new MemberCodegen<JetFile>(state, null, packageFacade, null, v) {
349 @Override
350 protected void generateDeclaration() {
351 throw new UnsupportedOperationException();
352 }
353
354 @Override
355 protected void generateBody() {
356 throw new UnsupportedOperationException();
357 }
358
359 @Override
360 protected void generateKotlinAnnotation() {
361 throw new UnsupportedOperationException();
362 }
363 };
364 }
365
366 @Nullable
367 private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedFqName) {
368 SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>();
369 for (JetFile file : files) {
370 PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
371 assert fragment != null : "package fragment is null for " + file + "\n" + file.getText();
372
373 assert expectedFqName.equals(fragment.getFqName()) :
374 "expected package fq name: " + expectedFqName + ", actual: " + fragment.getFqName();
375
376 if (!fragments.contains(fragment)) {
377 fragments.add(fragment);
378 }
379 }
380 if (fragments.size() > 1) {
381 throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments);
382 }
383
384 if (fragments.isEmpty()) {
385 return null;
386 }
387 return fragments.get(0);
388 }
389
390 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) {
391 JetFile file = classOrObject.getContainingJetFile();
392 Type packagePartType = PackagePartClassUtils.getPackagePartType(file);
393 CodegenContext context = CodegenContext.STATIC.intoPackagePart(packageFragment, packagePartType);
394 MemberCodegen.genClassOrObject(context, classOrObject, state, null);
395 }
396
397 public void done() {
398 v.done();
399 }
400 }