Extract record related parts into smaller bunch files for 201
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
|
||||
// This object should help compiling against different ASM versions in different bunch versions
|
||||
object VersionIndependentOpcodes {
|
||||
const val ACC_RECORD = Opcodes.ACC_RECORD
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
|
||||
// This object should help compiling against different ASM versions from different bunch versions
|
||||
object VersionIndependentOpcodes {
|
||||
const val ACC_RECORD = 0
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
fun ClassBuilder.addRecordComponent(name: String, desc: String, signature: String?) {
|
||||
newRecordComponent(name, desc, signature)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
fun ClassBuilder.addRecordComponent(name: String, desc: String, signature: String?) {
|
||||
// newRecordComponent(name, desc, signature)
|
||||
}
|
||||
@@ -223,7 +223,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
if (JvmAnnotationUtilKt.isJvmRecord(descriptor)) {
|
||||
access |= ACC_RECORD;
|
||||
access |= VersionIndependentOpcodes.ACC_RECORD;
|
||||
}
|
||||
|
||||
v.defineClass(
|
||||
|
||||
-1248
File diff suppressed because it is too large
Load Diff
@@ -454,7 +454,7 @@ public class PropertyCodegen {
|
||||
}
|
||||
|
||||
if (propertyDescriptor.getContainingDeclaration() instanceof ClassDescriptor && JvmAnnotationUtilKt.isJvmRecord((ClassDescriptor) propertyDescriptor.getContainingDeclaration())) {
|
||||
builder.newRecordComponent(name, type.getDescriptor(), signature);
|
||||
ClassBuilderRecordKt.addRecordComponent(builder, name, type.getDescriptor(), signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,680 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import kotlin.Pair;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.context.CodegenContextUtil;
|
||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
|
||||
import org.jetbrains.kotlin.codegen.context.MultifileClassFacadeContext;
|
||||
import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.load.java.DescriptorsJvmAbiUtil;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor;
|
||||
import org.jetbrains.kotlin.types.ErrorUtils;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.org.objectweb.asm.FieldVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.DescriptorAsmUtil.getDeprecatedAccessFlag;
|
||||
import static org.jetbrains.kotlin.codegen.DescriptorAsmUtil.getVisibilityForBackingField;
|
||||
import static org.jetbrains.kotlin.codegen.FunctionCodegen.processInterfaceMethod;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConstOrHasJvmFieldAnnotation;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
|
||||
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*;
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND;
|
||||
import static org.jetbrains.kotlin.fileClasses.JvmFileClassUtilKt.isTopLevelInJvmMultifileClass;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.K_PROPERTY_TYPE;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt.hasJvmFieldAnnotation;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt.hasJvmSyntheticAnnotation;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
public class PropertyCodegen {
|
||||
private final GenerationState state;
|
||||
private final ClassBuilder v;
|
||||
private final FunctionCodegen functionCodegen;
|
||||
private final KotlinTypeMapper typeMapper;
|
||||
private final BindingContext bindingContext;
|
||||
private final FieldOwnerContext context;
|
||||
private final MemberCodegen<?> memberCodegen;
|
||||
private final OwnerKind kind;
|
||||
|
||||
public PropertyCodegen(
|
||||
@NotNull FieldOwnerContext context,
|
||||
@NotNull ClassBuilder v,
|
||||
@NotNull FunctionCodegen functionCodegen,
|
||||
@NotNull MemberCodegen<?> memberCodegen
|
||||
) {
|
||||
this.state = functionCodegen.state;
|
||||
this.v = v;
|
||||
this.functionCodegen = functionCodegen;
|
||||
this.typeMapper = state.getTypeMapper();
|
||||
this.bindingContext = state.getBindingContext();
|
||||
this.context = context;
|
||||
this.memberCodegen = memberCodegen;
|
||||
this.kind = context.getContextKind();
|
||||
}
|
||||
|
||||
public void gen(@NotNull KtProperty property) {
|
||||
VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, property);
|
||||
if (!(variableDescriptor instanceof PropertyDescriptor)) {
|
||||
throw ExceptionLogger.logDescriptorNotFound(
|
||||
"Property " + property.getName() + " should have a property descriptor: " + variableDescriptor, property
|
||||
);
|
||||
}
|
||||
|
||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
|
||||
gen(property, propertyDescriptor, property.getGetter(), property.getSetter());
|
||||
}
|
||||
|
||||
public void genDestructuringDeclaration(@NotNull KtDestructuringDeclarationEntry entry) {
|
||||
VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, entry);
|
||||
if (!(variableDescriptor instanceof PropertyDescriptor)) {
|
||||
throw ExceptionLogger.logDescriptorNotFound(
|
||||
"Destructuring declaration entry" + entry.getName() + " should have a property descriptor: " + variableDescriptor, entry
|
||||
);
|
||||
}
|
||||
|
||||
if (!UnderscoreUtilKt.isSingleUnderscore(entry)) {
|
||||
genDestructuringDeclaration((PropertyDescriptor) variableDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public void generateInPackageFacade(@NotNull DeserializedPropertyDescriptor deserializedProperty) {
|
||||
assert context instanceof MultifileClassFacadeContext : "should be called only for generating facade: " + context;
|
||||
|
||||
genBackingFieldAndAnnotations(deserializedProperty);
|
||||
|
||||
if (!isConstOrHasJvmFieldAnnotation(deserializedProperty)) {
|
||||
generateGetter(deserializedProperty, null);
|
||||
generateSetter(deserializedProperty, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void gen(
|
||||
@NotNull KtProperty declaration,
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@Nullable KtPropertyAccessor getter,
|
||||
@Nullable KtPropertyAccessor setter
|
||||
) {
|
||||
assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION ||
|
||||
kind == OwnerKind.DEFAULT_IMPLS || kind == OwnerKind.ERASED_INLINE_CLASS
|
||||
: "Generating property with a wrong kind (" + kind + "): " + descriptor;
|
||||
|
||||
genBackingFieldAndAnnotations(descriptor);
|
||||
|
||||
boolean isDefaultGetterAndSetter = isDefaultAccessor(getter) && isDefaultAccessor(setter);
|
||||
|
||||
if (isAccessorNeeded(descriptor, getter, isDefaultGetterAndSetter)) {
|
||||
generateGetter(descriptor, getter);
|
||||
}
|
||||
if (isAccessorNeeded(descriptor, setter, isDefaultGetterAndSetter)) {
|
||||
generateSetter(descriptor, setter);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDefaultAccessor(@Nullable KtPropertyAccessor accessor) {
|
||||
return accessor == null || !accessor.hasBody();
|
||||
}
|
||||
|
||||
private void genDestructuringDeclaration(@NotNull PropertyDescriptor descriptor) {
|
||||
assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.DEFAULT_IMPLS
|
||||
: "Generating property with a wrong kind (" + kind + "): " + descriptor;
|
||||
|
||||
genBackingFieldAndAnnotations(descriptor);
|
||||
|
||||
generateGetter(descriptor, null);
|
||||
generateSetter(descriptor, null);
|
||||
}
|
||||
|
||||
private void genBackingFieldAndAnnotations(@NotNull PropertyDescriptor descriptor) {
|
||||
// Fields and '$annotations' methods for non-private const properties are generated in the multi-file facade
|
||||
boolean isBackingFieldOwner = descriptor.isConst() && !DescriptorVisibilities.isPrivate(descriptor.getVisibility())
|
||||
? !(context instanceof MultifileClassPartContext)
|
||||
: CodegenContextUtil.isImplementationOwner(context, descriptor);
|
||||
|
||||
generateBackingField(descriptor, isBackingFieldOwner);
|
||||
generateSyntheticMethodIfNeeded(descriptor, isBackingFieldOwner);
|
||||
}
|
||||
|
||||
private boolean isAccessorNeeded(
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@Nullable KtPropertyAccessor accessor,
|
||||
boolean isDefaultGetterAndSetter
|
||||
) {
|
||||
return isAccessorNeeded(descriptor, accessor, isDefaultGetterAndSetter, kind);
|
||||
}
|
||||
|
||||
public static boolean isReferenceablePropertyWithGetter(@NotNull PropertyDescriptor descriptor) {
|
||||
PsiElement psiElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
|
||||
KtDeclaration ktDeclaration = psiElement instanceof KtDeclaration ? (KtDeclaration) psiElement : null;
|
||||
if (ktDeclaration instanceof KtProperty) {
|
||||
KtProperty ktProperty = (KtProperty) ktDeclaration;
|
||||
boolean isDefaultGetterAndSetter =
|
||||
isDefaultAccessor(ktProperty.getGetter()) && isDefaultAccessor(ktProperty.getSetter());
|
||||
return isAccessorNeeded(descriptor, ktProperty.getGetter(), isDefaultGetterAndSetter, OwnerKind.IMPLEMENTATION);
|
||||
} else if (ktDeclaration instanceof KtParameter) {
|
||||
return isAccessorNeeded(descriptor, null, true, OwnerKind.IMPLEMENTATION);
|
||||
} else {
|
||||
return isAccessorNeeded(descriptor, null, false, OwnerKind.IMPLEMENTATION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if it's necessary to generate an accessor to the property, i.e. if this property can be referenced via getter/setter
|
||||
* for any reason
|
||||
*
|
||||
* @see JvmCodegenUtil#couldUseDirectAccessToProperty
|
||||
*/
|
||||
private static boolean isAccessorNeeded(
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@Nullable KtPropertyAccessor accessor,
|
||||
boolean isDefaultGetterAndSetter,
|
||||
OwnerKind kind
|
||||
) {
|
||||
if (isConstOrHasJvmFieldAnnotation(descriptor)) return false;
|
||||
|
||||
boolean isDefaultAccessor = isDefaultAccessor(accessor);
|
||||
|
||||
// Don't generate accessors for interface properties with default accessors in DefaultImpls
|
||||
if (kind == OwnerKind.DEFAULT_IMPLS && isDefaultAccessor) return false;
|
||||
|
||||
// Delegated or extension properties can only be referenced via accessors
|
||||
if (descriptor.isDelegated() || descriptor.getExtensionReceiverParameter() != null) return true;
|
||||
|
||||
// Companion object properties should have accessors for non-private properties because these properties can be referenced
|
||||
// via getter/setter. But these accessors getter/setter are not required for private properties that have a default getter
|
||||
// and setter, in this case, the property can always be accessed through the accessor 'access<property name>$cp' and avoid some
|
||||
// useless indirection by using others accessors.
|
||||
if (isCompanionObject(descriptor.getContainingDeclaration())) {
|
||||
if (DescriptorVisibilities.isPrivate(descriptor.getVisibility()) && isDefaultGetterAndSetter) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Non-const properties from multifile classes have accessors regardless of visibility
|
||||
if (isTopLevelInJvmMultifileClass(descriptor)) return true;
|
||||
|
||||
// Private class properties have accessors only in cases when those accessors are non-trivial
|
||||
if (DescriptorVisibilities.isPrivate(descriptor.getVisibility())) {
|
||||
return !isDefaultAccessor;
|
||||
}
|
||||
|
||||
// Non-private properties with private setter should not be generated for trivial properties
|
||||
// as the class will use direct field access instead
|
||||
//noinspection ConstantConditions
|
||||
if (accessor != null && accessor.isSetter() && DescriptorVisibilities.isPrivate(descriptor.getSetter().getVisibility())) {
|
||||
return !isDefaultAccessor;
|
||||
}
|
||||
|
||||
// Non-public API (private and internal) primary vals of inline classes don't have getter
|
||||
if (InlineClassesUtilsKt.isUnderlyingPropertyOfInlineClass(descriptor) && !descriptor.getVisibility().isPublicAPI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean areAccessorsNeededForPrimaryConstructorProperty(
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@NotNull OwnerKind kind
|
||||
) {
|
||||
if (hasJvmFieldAnnotation(descriptor)) return false;
|
||||
if (kind == OwnerKind.ERASED_INLINE_CLASS) return false;
|
||||
|
||||
DescriptorVisibility visibility = descriptor.getVisibility();
|
||||
if (InlineClassesUtilsKt.isInlineClass(descriptor.getContainingDeclaration())) {
|
||||
return visibility.isPublicAPI();
|
||||
}
|
||||
else {
|
||||
return !DescriptorVisibilities.isPrivate(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
public void generatePrimaryConstructorProperty(@NotNull PropertyDescriptor descriptor) {
|
||||
genBackingFieldAndAnnotations(descriptor);
|
||||
|
||||
if (areAccessorsNeededForPrimaryConstructorProperty(descriptor, context.getContextKind())) {
|
||||
generateGetter(descriptor, null);
|
||||
generateSetter(descriptor, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void generateConstructorPropertyAsMethodForAnnotationClass(
|
||||
@NotNull KtParameter parameter,
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@Nullable FunctionDescriptor expectedAnnotationConstructor
|
||||
) {
|
||||
JvmMethodGenericSignature signature = typeMapper.mapAnnotationParameterSignature(descriptor);
|
||||
Method asmMethod = signature.getAsmMethod();
|
||||
MethodVisitor mv = v.newMethod(
|
||||
JvmDeclarationOriginKt.OtherOrigin(parameter, descriptor),
|
||||
ACC_PUBLIC | ACC_ABSTRACT,
|
||||
asmMethod.getName(),
|
||||
asmMethod.getDescriptor(),
|
||||
signature.getGenericsSignature(),
|
||||
null
|
||||
);
|
||||
|
||||
PropertyGetterDescriptor getter = descriptor.getGetter();
|
||||
assert getter != null : "Annotation property should have a getter: " + descriptor;
|
||||
v.getSerializationBindings().put(METHOD_FOR_FUNCTION, getter, asmMethod);
|
||||
AnnotationCodegen.forMethod(mv, memberCodegen, state).genAnnotations(getter, asmMethod.getReturnType(), null);
|
||||
|
||||
KtExpression defaultValue = loadAnnotationArgumentDefaultValue(parameter, descriptor, expectedAnnotationConstructor);
|
||||
if (defaultValue != null) {
|
||||
ConstantValue<?> constant = ExpressionCodegen.getCompileTimeConstant(
|
||||
defaultValue, bindingContext, true, state.getShouldInlineConstVals());
|
||||
assert !state.getClassBuilderMode().generateBodies || constant != null
|
||||
: "Default value for annotation parameter should be compile time value: " + defaultValue.getText();
|
||||
if (constant != null) {
|
||||
AnnotationCodegen annotationCodegen = AnnotationCodegen.forAnnotationDefaultValue(mv, memberCodegen, state);
|
||||
annotationCodegen.generateAnnotationDefaultValue(constant, descriptor.getType());
|
||||
}
|
||||
}
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private KtExpression loadAnnotationArgumentDefaultValue(
|
||||
@NotNull KtParameter ktParameter,
|
||||
@NotNull PropertyDescriptor descriptor,
|
||||
@Nullable FunctionDescriptor expectedAnnotationConstructor
|
||||
) {
|
||||
KtExpression value = ktParameter.getDefaultValue();
|
||||
if (value != null) return value;
|
||||
|
||||
if (expectedAnnotationConstructor != null) {
|
||||
ValueParameterDescriptor expectedParameter = CollectionsKt.single(
|
||||
expectedAnnotationConstructor.getValueParameters(), parameter -> parameter.getName().equals(descriptor.getName())
|
||||
);
|
||||
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(expectedParameter);
|
||||
if (!(element instanceof KtParameter)) {
|
||||
state.getDiagnostics().report(EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND.on(ktParameter));
|
||||
return null;
|
||||
}
|
||||
return ((KtParameter) element).getDefaultValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void generateBackingField(@NotNull PropertyDescriptor descriptor, boolean isBackingFieldOwner) {
|
||||
if (isJvmInterface(descriptor.getContainingDeclaration()) || kind == OwnerKind.DEFAULT_IMPLS ||
|
||||
kind == OwnerKind.ERASED_INLINE_CLASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object defaultValue;
|
||||
if (descriptor.isDelegated()) {
|
||||
defaultValue = null;
|
||||
}
|
||||
else if (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) {
|
||||
if (shouldWriteFieldInitializer(descriptor)) {
|
||||
ConstantValue<?> initializer = descriptor.getCompileTimeInitializer();
|
||||
defaultValue = initializer == null ? null : initializer.getValue();
|
||||
}
|
||||
else {
|
||||
defaultValue = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
generateBackingField(descriptor, descriptor.isDelegated(), defaultValue, isBackingFieldOwner);
|
||||
}
|
||||
|
||||
// Annotations on properties are stored in bytecode on an empty synthetic method. This way they're still
|
||||
// accessible via reflection, and 'deprecated' and 'synthetic' flags prevent this method from being called accidentally
|
||||
private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor, boolean isBackingFieldOwner) {
|
||||
Annotations annotations = descriptor.getAnnotations();
|
||||
if (annotations.isEmpty()) return;
|
||||
|
||||
Method signature = typeMapper.mapSyntheticMethodForPropertyAnnotations(descriptor);
|
||||
if (kind != OwnerKind.DEFAULT_IMPLS && CodegenContextUtil.isImplementationOwner(context, descriptor)) {
|
||||
v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, signature);
|
||||
}
|
||||
|
||||
if (isBackingFieldOwner) {
|
||||
if (!isInterface(context.getContextDescriptor()) ||
|
||||
processInterfaceMethod(descriptor, kind, false, true, state.getJvmDefaultMode())) {
|
||||
memberCodegen.generateSyntheticAnnotationsMethod(descriptor, signature, annotations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateBackingField(
|
||||
@NotNull PropertyDescriptor propertyDescriptor,
|
||||
boolean isDelegate,
|
||||
@Nullable Object defaultValue,
|
||||
boolean isBackingFieldOwner
|
||||
) {
|
||||
FieldDescriptor annotatedField = isDelegate ? propertyDescriptor.getDelegateField() : propertyDescriptor.getBackingField();
|
||||
|
||||
int modifiers = getDeprecatedAccessFlag(propertyDescriptor);
|
||||
|
||||
for (AnnotationCodegen.JvmFlagAnnotation flagAnnotation : AnnotationCodegen.FIELD_FLAGS) {
|
||||
modifiers |= flagAnnotation.getJvmFlag(annotatedField);
|
||||
}
|
||||
|
||||
if (kind == OwnerKind.PACKAGE) {
|
||||
modifiers |= ACC_STATIC;
|
||||
}
|
||||
|
||||
if (!propertyDescriptor.isLateInit() && (!propertyDescriptor.isVar() || isDelegate)) {
|
||||
modifiers |= ACC_FINAL;
|
||||
}
|
||||
|
||||
if (hasJvmSyntheticAnnotation(propertyDescriptor)) {
|
||||
modifiers |= ACC_SYNTHETIC;
|
||||
}
|
||||
|
||||
KotlinType kotlinType = isDelegate ? getDelegateTypeForProperty(propertyDescriptor, bindingContext) : propertyDescriptor.getType();
|
||||
Type type = typeMapper.mapType(kotlinType);
|
||||
|
||||
ClassBuilder builder = v;
|
||||
|
||||
FieldOwnerContext backingFieldContext = context;
|
||||
List<String> additionalVisibleAnnotations = Collections.emptyList();
|
||||
if (DescriptorAsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor) ) {
|
||||
modifiers |= ACC_STATIC;
|
||||
|
||||
if (DescriptorsJvmAbiUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) {
|
||||
ImplementationBodyCodegen codegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
|
||||
builder = codegen.v;
|
||||
backingFieldContext = codegen.context;
|
||||
if (DescriptorVisibilities.isPrivate(((ClassDescriptor) propertyDescriptor.getContainingDeclaration()).getVisibility())) {
|
||||
modifiers |= ACC_DEPRECATED;
|
||||
additionalVisibleAnnotations = Collections.singletonList(CodegenUtilKt.JAVA_LANG_DEPRECATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
modifiers |= getVisibilityForBackingField(propertyDescriptor, isDelegate);
|
||||
|
||||
if (DescriptorAsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
|
||||
ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
|
||||
parentBodyCodegen.addCompanionObjectPropertyToCopy(propertyDescriptor, defaultValue);
|
||||
}
|
||||
|
||||
String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
|
||||
|
||||
v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, new Pair<>(type, name));
|
||||
|
||||
if (isBackingFieldOwner) {
|
||||
String signature = isDelegate ? null : typeMapper.mapFieldSignature(kotlinType, propertyDescriptor);
|
||||
FieldVisitor fv = builder.newField(
|
||||
JvmDeclarationOriginKt.OtherOrigin(propertyDescriptor), modifiers, name, type.getDescriptor(),
|
||||
signature, defaultValue
|
||||
);
|
||||
|
||||
if (annotatedField != null) {
|
||||
// Don't emit nullability annotations for backing field if:
|
||||
// - backing field is synthetic;
|
||||
// - property is lateinit (since corresponding field is actually nullable).
|
||||
boolean skipNullabilityAnnotations =
|
||||
(modifiers & ACC_SYNTHETIC) != 0 ||
|
||||
propertyDescriptor.isLateInit();
|
||||
AnnotationCodegen.forField(fv, memberCodegen, state, skipNullabilityAnnotations)
|
||||
.genAnnotations(annotatedField, type, propertyDescriptor.getType(), null, additionalVisibleAnnotations);
|
||||
}
|
||||
|
||||
if (propertyDescriptor.getContainingDeclaration() instanceof ClassDescriptor && JvmAnnotationUtilKt.isJvmRecord((ClassDescriptor) propertyDescriptor.getContainingDeclaration())) {
|
||||
// builder.newRecordComponent(name, type.getDescriptor(), signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KotlinType getDelegateTypeForProperty(
|
||||
@NotNull PropertyDescriptor propertyDescriptor,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
ResolvedCall<FunctionDescriptor> provideDelegateResolvedCall =
|
||||
bindingContext.get(BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, propertyDescriptor);
|
||||
|
||||
KtProperty property = (KtProperty) DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
|
||||
KtExpression delegateExpression = property != null ? property.getDelegateExpression() : null;
|
||||
|
||||
KotlinType delegateType;
|
||||
if (provideDelegateResolvedCall != null) {
|
||||
delegateType = provideDelegateResolvedCall.getResultingDescriptor().getReturnType();
|
||||
}
|
||||
else if (delegateExpression != null) {
|
||||
delegateType = bindingContext.getType(delegateExpression);
|
||||
}
|
||||
else {
|
||||
delegateType = null;
|
||||
}
|
||||
|
||||
if (delegateType == null) {
|
||||
// Delegation convention is unresolved
|
||||
delegateType = ErrorUtils.createErrorType("Delegate type");
|
||||
}
|
||||
return delegateType;
|
||||
}
|
||||
|
||||
private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) {
|
||||
if (!descriptor.isConst() &&
|
||||
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.NoConstantValueAttributeForNonConstVals)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//final field of primitive or String type
|
||||
if (!descriptor.isVar()) {
|
||||
Type type = typeMapper.mapType(descriptor);
|
||||
return AsmUtil.isPrimitive(type) || "java.lang.String".equals(type.getClassName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void generateGetter(@NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor getter) {
|
||||
generateAccessor(
|
||||
getter,
|
||||
descriptor.getGetter() != null ? descriptor.getGetter() : DescriptorFactory.createDefaultGetter(
|
||||
descriptor, Annotations.Companion.getEMPTY()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void generateSetter(@NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor setter) {
|
||||
if (!descriptor.isVar()) return;
|
||||
|
||||
generateAccessor(
|
||||
setter,
|
||||
descriptor.getSetter() != null ? descriptor.getSetter() : DescriptorFactory.createDefaultSetter(
|
||||
descriptor, Annotations.Companion.getEMPTY(), Annotations.Companion.getEMPTY()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void generateAccessor(@Nullable KtPropertyAccessor accessor, @NotNull PropertyAccessorDescriptor descriptor) {
|
||||
if (context instanceof MultifileClassFacadeContext &&
|
||||
(DescriptorVisibilities.isPrivate(descriptor.getVisibility()) ||
|
||||
DescriptorAsmUtil.getVisibilityAccessFlag(descriptor) == Opcodes.ACC_PRIVATE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FunctionGenerationStrategy strategy;
|
||||
if (accessor == null || !accessor.hasBody()) {
|
||||
if (descriptor.getCorrespondingProperty().isDelegated()) {
|
||||
strategy = new DelegatedPropertyAccessorStrategy(state, descriptor);
|
||||
}
|
||||
else {
|
||||
strategy = new DefaultPropertyAccessorStrategy(state, descriptor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
strategy = new FunctionGenerationStrategy.FunctionDefault(state, accessor);
|
||||
}
|
||||
|
||||
functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), descriptor, strategy);
|
||||
}
|
||||
|
||||
private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased {
|
||||
private final PropertyAccessorDescriptor propertyAccessorDescriptor;
|
||||
|
||||
public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
|
||||
super(state);
|
||||
propertyAccessorDescriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||
InstructionAdapter v = codegen.v;
|
||||
PropertyDescriptor propertyDescriptor = propertyAccessorDescriptor.getCorrespondingProperty();
|
||||
StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, true, null, StackValue.LOCAL_0);
|
||||
|
||||
PsiElement jetProperty = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
|
||||
if (jetProperty instanceof KtProperty || jetProperty instanceof KtParameter) {
|
||||
codegen.markLineNumber((KtElement) jetProperty, false);
|
||||
}
|
||||
|
||||
if (propertyAccessorDescriptor instanceof PropertyGetterDescriptor) {
|
||||
Type type = signature.getReturnType();
|
||||
property.put(type, v);
|
||||
v.areturn(type);
|
||||
}
|
||||
else if (propertyAccessorDescriptor instanceof PropertySetterDescriptor) {
|
||||
List<ValueParameterDescriptor> valueParameters = propertyAccessorDescriptor.getValueParameters();
|
||||
assert valueParameters.size() == 1 : "Property setter should have only one value parameter but has " + propertyAccessorDescriptor;
|
||||
int parameterIndex = codegen.lookupLocalIndex(valueParameters.get(0));
|
||||
assert parameterIndex >= 0 : "Local index for setter parameter should be positive or zero: " + propertyAccessorDescriptor;
|
||||
Type type = codegen.typeMapper.mapType(propertyDescriptor);
|
||||
property.store(StackValue.local(parameterIndex, type), codegen.v);
|
||||
v.visitInsn(RETURN);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unknown property accessor: " + propertyAccessorDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static StackValue invokeDelegatedPropertyConventionMethod(
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ResolvedCall<FunctionDescriptor> resolvedCall,
|
||||
@NotNull StackValue receiver,
|
||||
@NotNull PropertyDescriptor propertyDescriptor
|
||||
) {
|
||||
codegen.tempVariables.put(
|
||||
resolvedCall.getCall().getValueArguments().get(1).asElement(),
|
||||
getDelegatedPropertyMetadata(propertyDescriptor, codegen.getBindingContext())
|
||||
);
|
||||
|
||||
return codegen.invokeFunction(resolvedCall, receiver);
|
||||
}
|
||||
|
||||
public static boolean isDelegatedPropertyWithOptimizedMetadata(
|
||||
@NotNull VariableDescriptorWithAccessors descriptor,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
return Boolean.TRUE == bindingContext.get(DELEGATED_PROPERTY_WITH_OPTIMIZED_METADATA, descriptor);
|
||||
}
|
||||
|
||||
public static @NotNull StackValue getOptimizedDelegatedPropertyMetadataValue() {
|
||||
return StackValue.constant(null, K_PROPERTY_TYPE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static StackValue getDelegatedPropertyMetadata(
|
||||
@NotNull VariableDescriptorWithAccessors descriptor,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
if (isDelegatedPropertyWithOptimizedMetadata(descriptor, bindingContext)) {
|
||||
return getOptimizedDelegatedPropertyMetadataValue();
|
||||
}
|
||||
|
||||
Type owner = bindingContext.get(DELEGATED_PROPERTY_METADATA_OWNER, descriptor);
|
||||
assert owner != null : "Delegated property owner not found: " + descriptor;
|
||||
|
||||
List<VariableDescriptorWithAccessors> allDelegatedProperties = bindingContext.get(DELEGATED_PROPERTIES_WITH_METADATA, owner);
|
||||
int index = allDelegatedProperties == null ? -1 : allDelegatedProperties.indexOf(descriptor);
|
||||
if (index < 0) {
|
||||
throw new AssertionError("Delegated property not found in " + owner + ": " + descriptor);
|
||||
}
|
||||
|
||||
StackValue.Field array = StackValue.field(
|
||||
Type.getType("[" + K_PROPERTY_TYPE), owner, JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME, true, StackValue.none()
|
||||
);
|
||||
return StackValue.arrayElement(K_PROPERTY_TYPE, null, array, StackValue.constant(index));
|
||||
}
|
||||
|
||||
private static class DelegatedPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased {
|
||||
private final PropertyAccessorDescriptor propertyAccessorDescriptor;
|
||||
|
||||
public DelegatedPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
|
||||
super(state);
|
||||
this.propertyAccessorDescriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||
InstructionAdapter v = codegen.v;
|
||||
|
||||
BindingContext bindingContext = state.getBindingContext();
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall =
|
||||
bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, propertyAccessorDescriptor);
|
||||
assert resolvedCall != null : "Resolve call should be recorded for delegate call " + signature.toString();
|
||||
|
||||
PropertyDescriptor propertyDescriptor = propertyAccessorDescriptor.getCorrespondingProperty();
|
||||
StackValue.Property property = codegen.intermediateValueForProperty(propertyDescriptor, true, null, StackValue.LOCAL_0);
|
||||
StackValue.Property delegate = property.getDelegateOrNull();
|
||||
assert delegate != null : "No delegate for delegated property: " + propertyDescriptor;
|
||||
StackValue lastValue = invokeDelegatedPropertyConventionMethod(codegen, resolvedCall, delegate, propertyDescriptor);
|
||||
Type asmType = signature.getReturnType();
|
||||
KotlinType kotlinReturnType = propertyAccessorDescriptor.getOriginal().getReturnType();
|
||||
lastValue.put(asmType, kotlinReturnType, v);
|
||||
v.areturn(asmType);
|
||||
}
|
||||
}
|
||||
|
||||
public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor delegateTo, @NotNull StackValue field) {
|
||||
ClassDescriptor toClass = (ClassDescriptor) delegateTo.getContainingDeclaration();
|
||||
|
||||
PropertyGetterDescriptor getter = delegate.getGetter();
|
||||
if (getter != null) {
|
||||
//noinspection ConstantConditions
|
||||
functionCodegen.genDelegate(getter, delegateTo.getGetter().getOriginal(), toClass, field);
|
||||
}
|
||||
|
||||
PropertySetterDescriptor setter = delegate.getSetter();
|
||||
if (setter != null) {
|
||||
//noinspection ConstantConditions
|
||||
functionCodegen.genDelegate(setter, delegateTo.getSetter().getOriginal(), toClass, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.LanguageLevelProjectExtension
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
|
||||
fun Project.setupHighestLanguageLevel() {
|
||||
LanguageLevelProjectExtension.getInstance(this).languageLevel = LanguageLevel.JDK_15_PREVIEW
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.LanguageLevelProjectExtension
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
|
||||
fun Project.setupHighestLanguageLevel() {
|
||||
// LanguageLevelProjectExtension.getInstance(this).languageLevel = LanguageLevel.JDK_15_PREVIEW
|
||||
}
|
||||
@@ -253,7 +253,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
updateClasspath(roots.map { JavaSourceRoot(it, null) })
|
||||
})
|
||||
|
||||
LanguageLevelProjectExtension.getInstance(project).languageLevel = LanguageLevel.JDK_15_PREVIEW
|
||||
project.setupHighestLanguageLevel()
|
||||
}
|
||||
|
||||
private fun collectAdditionalSources(project: MockProject) {
|
||||
|
||||
@@ -1,703 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager
|
||||
import com.intellij.codeInsight.InferredAnnotationsManager
|
||||
import com.intellij.core.CoreApplicationEnvironment
|
||||
import com.intellij.core.CoreJavaFileManager
|
||||
import com.intellij.core.JavaCoreProjectEnvironment
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.lang.java.JavaParserDefinition
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.TransactionGuard
|
||||
import com.intellij.openapi.application.TransactionGuardImpl
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.extensions.ExtensionsArea
|
||||
import com.intellij.openapi.fileTypes.PlainTextFileType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.LanguageLevelProjectExtension
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.*
|
||||
import com.intellij.openapi.vfs.impl.ZipHandler
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||
import com.intellij.psi.impl.JavaClassSupersImpl
|
||||
import com.intellij.psi.impl.PsiElementFinderImpl
|
||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
||||
import com.intellij.psi.impl.file.impl.JavaFileManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.JavaClassSupers
|
||||
import com.intellij.util.io.URLUtil
|
||||
import com.intellij.util.lang.UrlClassLoader
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.classes.FacadeCache
|
||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
|
||||
import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
|
||||
import org.jetbrains.kotlin.cli.common.config.ContentRoot
|
||||
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
|
||||
import org.jetbrains.kotlin.cli.common.extensions.ScriptEvaluationExtension
|
||||
import org.jetbrains.kotlin.cli.common.extensions.ShellExtension
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.cli.jvm.index.*
|
||||
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.extensions.*
|
||||
import org.jetbrains.kotlin.extensions.internal.CandidateInterceptor
|
||||
import org.jetbrains.kotlin.extensions.internal.TypeResolutionInterceptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
|
||||
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class KotlinCoreEnvironment private constructor(
|
||||
val projectEnvironment: JavaCoreProjectEnvironment,
|
||||
private val initialConfiguration: CompilerConfiguration,
|
||||
configFiles: EnvironmentConfigFiles
|
||||
) {
|
||||
|
||||
class ProjectEnvironment(
|
||||
disposable: Disposable,
|
||||
applicationEnvironment: KotlinCoreApplicationEnvironment
|
||||
) :
|
||||
KotlinCoreProjectEnvironment(disposable, applicationEnvironment) {
|
||||
|
||||
private var extensionRegistered = false
|
||||
|
||||
override fun preregisterServices() {
|
||||
registerProjectExtensionPoints(project.extensionArea)
|
||||
}
|
||||
|
||||
fun registerExtensionsFromPlugins(configuration: CompilerConfiguration) {
|
||||
if (!extensionRegistered) {
|
||||
registerPluginExtensionPoints(project)
|
||||
registerExtensionsFromPlugins(project, configuration)
|
||||
extensionRegistered = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerJavaPsiFacade() {
|
||||
with(project) {
|
||||
registerService(
|
||||
CoreJavaFileManager::class.java,
|
||||
ServiceManager.getService(this, JavaFileManager::class.java) as CoreJavaFileManager
|
||||
)
|
||||
|
||||
registerKotlinLightClassSupport(project)
|
||||
|
||||
registerService(ExternalAnnotationsManager::class.java, MockExternalAnnotationsManager())
|
||||
registerService(InferredAnnotationsManager::class.java, MockInferredAnnotationsManager())
|
||||
}
|
||||
|
||||
super.registerJavaPsiFacade()
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceFiles = mutableListOf<KtFile>()
|
||||
private val rootsIndex: JvmDependenciesDynamicCompoundIndex
|
||||
private val packagePartProviders = mutableListOf<JvmPackagePartProvider>()
|
||||
|
||||
private val classpathRootsResolver: ClasspathRootsResolver
|
||||
private val initialRoots = ArrayList<JavaRoot>()
|
||||
|
||||
val configuration: CompilerConfiguration = initialConfiguration.apply { setupJdkClasspathRoots(configFiles) }.copy()
|
||||
|
||||
init {
|
||||
PersistentFSConstants::class.java.getDeclaredField("ourMaxIntellisenseFileSize")
|
||||
.apply { isAccessible = true }
|
||||
.setInt(null, FileUtilRt.LARGE_FOR_CONTENT_LOADING)
|
||||
|
||||
val project = projectEnvironment.project
|
||||
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
(projectEnvironment as? ProjectEnvironment)?.registerExtensionsFromPlugins(configuration)
|
||||
// otherwise consider that project environment is properly configured before passing to the environment
|
||||
// TODO: consider some asserts to check important extension points
|
||||
|
||||
project.registerService(DeclarationProviderFactoryService::class.java, CliDeclarationProviderFactoryService(sourceFiles))
|
||||
|
||||
val isJvm = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
project.registerService(ModuleVisibilityManager::class.java, CliModuleVisibilityManagerImpl(isJvm))
|
||||
|
||||
registerProjectServicesForCLI(projectEnvironment)
|
||||
|
||||
registerProjectServices(projectEnvironment.project)
|
||||
|
||||
for (extension in CompilerConfigurationExtension.getInstances(project)) {
|
||||
extension.updateConfiguration(configuration)
|
||||
}
|
||||
|
||||
sourceFiles += createKtFiles(project)
|
||||
|
||||
collectAdditionalSources(project)
|
||||
|
||||
sourceFiles.sortBy { it.virtualFile.path }
|
||||
|
||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
||||
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
})
|
||||
|
||||
val outputDirectory =
|
||||
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
|
||||
?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)?.absolutePath
|
||||
|
||||
classpathRootsResolver = ClasspathRootsResolver(
|
||||
PsiManager.getInstance(project),
|
||||
messageCollector,
|
||||
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||
this::contentRootToVirtualFile,
|
||||
javaModuleFinder,
|
||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
|
||||
outputDirectory?.let(this::findLocalFile)
|
||||
)
|
||||
|
||||
val (initialRoots, javaModules) =
|
||||
classpathRootsResolver.convertClasspathRoots(configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS))
|
||||
this.initialRoots.addAll(initialRoots)
|
||||
|
||||
if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) {
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
}
|
||||
|
||||
val (roots, singleJavaFileRoots) =
|
||||
initialRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION }
|
||||
|
||||
// REPL and kapt2 update classpath dynamically
|
||||
rootsIndex = JvmDependenciesDynamicCompoundIndex().apply {
|
||||
addIndex(JvmDependenciesIndexImpl(roots))
|
||||
updateClasspathFromRootsIndex(this)
|
||||
}
|
||||
|
||||
(ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl).initialize(
|
||||
rootsIndex,
|
||||
packagePartProviders,
|
||||
SingleJavaFileRootsIndex(singleJavaFileRoots),
|
||||
configuration.getBoolean(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING)
|
||||
)
|
||||
|
||||
project.registerService(
|
||||
JavaModuleResolver::class.java,
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList())
|
||||
)
|
||||
|
||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||
project.registerService(MetadataFinderFactory::class.java, finderFactory)
|
||||
project.registerService(VirtualFileFinderFactory::class.java, finderFactory)
|
||||
|
||||
project.putUserData(APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY, fun(roots: List<File>) {
|
||||
updateClasspath(roots.map { JavaSourceRoot(it, null) })
|
||||
})
|
||||
}
|
||||
|
||||
private fun collectAdditionalSources(project: MockProject) {
|
||||
var unprocessedSources: Collection<KtFile> = sourceFiles
|
||||
val processedSources = HashSet<KtFile>()
|
||||
val processedSourcesByExtension = HashMap<CollectAdditionalSourcesExtension, Collection<KtFile>>()
|
||||
// repeat feeding extensions with sources while new sources a being added
|
||||
var sourceCollectionIterations = 0
|
||||
while (unprocessedSources.isNotEmpty()) {
|
||||
if (sourceCollectionIterations++ > 10) { // TODO: consider using some appropriate global constant
|
||||
throw IllegalStateException("Unable to collect additional sources in reasonable number of iterations")
|
||||
}
|
||||
processedSources.addAll(unprocessedSources)
|
||||
val allNewSources = ArrayList<KtFile>()
|
||||
for (extension in CollectAdditionalSourcesExtension.getInstances(project)) {
|
||||
// do not feed the extension with the sources it returned on the previous iteration
|
||||
val sourcesToProcess = unprocessedSources - (processedSourcesByExtension[extension] ?: emptyList())
|
||||
val newSources = extension.collectAdditionalSourcesAndUpdateConfiguration(sourcesToProcess, configuration, project)
|
||||
if (newSources.isNotEmpty()) {
|
||||
allNewSources.addAll(newSources)
|
||||
processedSourcesByExtension[extension] = newSources
|
||||
}
|
||||
}
|
||||
unprocessedSources = allNewSources.filterNot { processedSources.contains(it) }.distinct()
|
||||
sourceFiles += unprocessedSources
|
||||
}
|
||||
}
|
||||
|
||||
fun addKotlinSourceRoots(rootDirs: List<File>) {
|
||||
val roots = rootDirs.map { KotlinSourceRoot(it.absolutePath, isCommon = false) }
|
||||
sourceFiles += createSourceFilesFromSourceRoots(configuration, project, roots)
|
||||
}
|
||||
|
||||
fun createPackagePartProvider(scope: GlobalSearchScope): JvmPackagePartProvider {
|
||||
return JvmPackagePartProvider(configuration.languageVersionSettings, scope).apply {
|
||||
addRoots(initialRoots, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
|
||||
packagePartProviders += this
|
||||
(ModuleAnnotationsResolver.getInstance(project) as CliModuleAnnotationsResolver).addPackagePartProvider(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val VirtualFile.javaFiles: List<VirtualFile>
|
||||
get() = mutableListOf<VirtualFile>().apply {
|
||||
VfsUtilCore.processFilesRecursively(this@javaFiles) { file ->
|
||||
if (file.fileType == JavaFileType.INSTANCE) {
|
||||
add(file)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private val allJavaFiles: List<File>
|
||||
get() = configuration.javaSourceRoots
|
||||
.mapNotNull(this::findLocalFile)
|
||||
.flatMap { it.javaFiles }
|
||||
.map { File(it.canonicalPath) }
|
||||
|
||||
fun registerJavac(
|
||||
javaFiles: List<File> = allJavaFiles,
|
||||
kotlinFiles: List<KtFile> = sourceFiles,
|
||||
arguments: Array<String>? = null,
|
||||
bootClasspath: List<File>? = null,
|
||||
sourcePath: List<File>? = null
|
||||
): Boolean {
|
||||
return JavacWrapperRegistrar.registerJavac(
|
||||
projectEnvironment.project, configuration, javaFiles, kotlinFiles, arguments, bootClasspath, sourcePath,
|
||||
LightClassGenerationSupport.getInstance(project), packagePartProviders
|
||||
)
|
||||
}
|
||||
|
||||
private val applicationEnvironment: CoreApplicationEnvironment
|
||||
get() = projectEnvironment.environment
|
||||
|
||||
val project: Project
|
||||
get() = projectEnvironment.project
|
||||
|
||||
internal fun countLinesOfCode(sourceFiles: List<KtFile>): Int =
|
||||
sourceFiles.sumBy { sourceFile ->
|
||||
val text = sourceFile.text
|
||||
StringUtil.getLineBreakCount(text) + (if (StringUtil.endsWithLineBreak(text)) 0 else 1)
|
||||
}
|
||||
|
||||
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {
|
||||
index.indexedRoots.forEach {
|
||||
projectEnvironment.addSourcesToClasspath(it.file)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateClasspath(contentRoots: List<ContentRoot>): List<File>? {
|
||||
// TODO: add new Java modules to CliJavaModuleResolver
|
||||
val newRoots = classpathRootsResolver.convertClasspathRoots(contentRoots).roots
|
||||
|
||||
if (packagePartProviders.isEmpty()) {
|
||||
initialRoots.addAll(newRoots)
|
||||
} else {
|
||||
for (packagePartProvider in packagePartProviders) {
|
||||
packagePartProvider.addRoots(newRoots, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
|
||||
}
|
||||
}
|
||||
|
||||
return rootsIndex.addNewIndexForRoots(newRoots)?.let { newIndex ->
|
||||
updateClasspathFromRootsIndex(newIndex)
|
||||
newIndex.indexedRoots.mapNotNull { (file) ->
|
||||
VfsUtilCore.virtualToIoFile(VfsUtilCore.getVirtualFileForJar(file) ?: file)
|
||||
}.toList()
|
||||
}.orEmpty()
|
||||
}
|
||||
|
||||
private fun contentRootToVirtualFile(root: JvmContentRoot): VirtualFile? =
|
||||
when (root) {
|
||||
is JvmClasspathRoot ->
|
||||
if (root.file.isFile) findJarRoot(root.file) else findExistingRoot(root, "Classpath entry")
|
||||
is JvmModulePathRoot ->
|
||||
if (root.file.isFile) findJarRoot(root.file) else findExistingRoot(root, "Java module root")
|
||||
is JavaSourceRoot ->
|
||||
findExistingRoot(root, "Java source root")
|
||||
else ->
|
||||
throw IllegalStateException("Unexpected root: $root")
|
||||
}
|
||||
|
||||
internal fun findLocalFile(path: String): VirtualFile? =
|
||||
applicationEnvironment.localFileSystem.findFileByPath(path)
|
||||
|
||||
private fun findExistingRoot(root: JvmContentRoot, rootDescription: String): VirtualFile? {
|
||||
return findLocalFile(root.file.absolutePath).also {
|
||||
if (it == null) {
|
||||
report(STRONG_WARNING, "$rootDescription points to a non-existent location: ${root.file}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findJarRoot(file: File): VirtualFile? =
|
||||
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
|
||||
private fun getSourceRootsCheckingForDuplicates(): List<KotlinSourceRoot> {
|
||||
val uniqueSourceRoots = hashSetOf<String>()
|
||||
val result = mutableListOf<KotlinSourceRoot>()
|
||||
|
||||
for (root in configuration.kotlinSourceRoots) {
|
||||
if (!uniqueSourceRoots.add(root.path)) {
|
||||
report(STRONG_WARNING, "Duplicate source root: ${root.path}")
|
||||
}
|
||||
result.add(root)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun getSourceFiles(): List<KtFile> = sourceFiles
|
||||
|
||||
private fun createKtFiles(project: Project): List<KtFile> =
|
||||
createSourceFilesFromSourceRoots(configuration, project, getSourceRootsCheckingForDuplicates())
|
||||
|
||||
internal fun report(severity: CompilerMessageSeverity, message: String) = configuration.report(severity, message)
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinCoreEnvironment::class.java)
|
||||
|
||||
private val APPLICATION_LOCK = Object()
|
||||
private var ourApplicationEnvironment: KotlinCoreApplicationEnvironment? = null
|
||||
private var ourProjectCount = 0
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
setupIdeaStandaloneExecution()
|
||||
val appEnv = getOrCreateApplicationEnvironmentForProduction(parentDisposable, configuration)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
val environment = KotlinCoreEnvironment(projectEnv, configuration, configFiles)
|
||||
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourProjectCount++
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
projectEnvironment: JavaCoreProjectEnvironment, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val environment = KotlinCoreEnvironment(projectEnvironment, configuration, configFiles)
|
||||
|
||||
if (projectEnvironment.environment == applicationEnvironment) {
|
||||
// accounting for core environment disposing
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourProjectCount++
|
||||
}
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun createForTests(
|
||||
parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val configuration = initialConfiguration.copy()
|
||||
// Tests are supposed to create a single project and dispose it right after use
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
||||
}
|
||||
|
||||
// used in the daemon for jar cache cleanup
|
||||
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
||||
|
||||
fun getOrCreateApplicationEnvironmentForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (ourApplicationEnvironment == null) {
|
||||
val disposable = Disposer.newDisposable()
|
||||
ourApplicationEnvironment = createApplicationEnvironment(disposable, configuration, unitTestMode = false)
|
||||
ourProjectCount = 0
|
||||
Disposer.register(disposable, Disposable {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourApplicationEnvironment = null
|
||||
}
|
||||
})
|
||||
}
|
||||
// Disposing of the environment is unsafe in production then parallel builds are enabled, but turning it off universally
|
||||
// breaks a lot of tests, therefore it is disabled for production and enabled for tests
|
||||
if (System.getProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY).toBooleanLenient() != true) {
|
||||
// JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ)
|
||||
// All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well
|
||||
@Suppress("ObjectLiteralToLambda") // Disposer tree depends on identity of disposables.
|
||||
Disposer.register(parentDisposable, object : Disposable {
|
||||
override fun dispose() {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (--ourProjectCount <= 0) {
|
||||
disposeApplicationEnvironment()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return ourApplicationEnvironment!!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is also used in Gradle after configuration phase finished.
|
||||
*/
|
||||
fun disposeApplicationEnvironment() {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
val environment = ourApplicationEnvironment ?: return
|
||||
ourApplicationEnvironment = null
|
||||
Disposer.dispose(environment.parentDisposable)
|
||||
ZipHandler.clearFileAccessorCache()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createApplicationEnvironment(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, unitTestMode: Boolean
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
val applicationEnvironment = KotlinCoreApplicationEnvironment.create(parentDisposable, unitTestMode)
|
||||
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml")
|
||||
// FIX ME WHEN BUNCH 202 REMOVED: this code is required to support compiler bundled to both 202 and 203.
|
||||
// Please, remove "com.intellij.psi.classFileDecompiler" EP registration once 202 is no longer supported by the compiler
|
||||
if (!Extensions.getRootArea().hasExtensionPoint("com.intellij.psi.classFileDecompiler")) {
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/core.xml")
|
||||
}
|
||||
|
||||
registerApplicationServicesForCLI(applicationEnvironment)
|
||||
registerApplicationServices(applicationEnvironment)
|
||||
|
||||
return applicationEnvironment
|
||||
}
|
||||
|
||||
private fun registerApplicationExtensionPointsAndExtensionsFrom(configuration: CompilerConfiguration, configFilePath: String) {
|
||||
fun File.hasConfigFile(configFile: String): Boolean =
|
||||
if (isDirectory) File(this, "META-INF" + File.separator + configFile).exists()
|
||||
else try {
|
||||
ZipFile(this).use {
|
||||
it.getEntry("META-INF/$configFile") != null
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
val pluginRoot: File =
|
||||
configuration.get(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT)?.let(::File)
|
||||
?: PathUtil.getResourcePathForClass(this::class.java).takeIf { it.hasConfigFile(configFilePath) }
|
||||
// hack for load extensions when compiler run directly from project directory (e.g. in tests)
|
||||
?: File("compiler/cli/cli-common/resources").takeIf { it.hasConfigFile(configFilePath) }
|
||||
?: throw IllegalStateException(
|
||||
"Unable to find extension point configuration $configFilePath " +
|
||||
"(cp:\n ${(Thread.currentThread().contextClassLoader as? UrlClassLoader)?.urls?.joinToString("\n ") { it.file }})"
|
||||
)
|
||||
|
||||
CoreApplicationEnvironment.registerExtensionPointAndExtensions(
|
||||
FileSystems.getDefault().getPath(pluginRoot.path),
|
||||
configFilePath,
|
||||
Extensions.getRootArea()
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("MemberVisibilityCanPrivate") // made public for CLI Android Lint
|
||||
fun registerPluginExtensionPoints(project: MockProject) {
|
||||
ExpressionCodegenExtension.registerExtensionPoint(project)
|
||||
SyntheticResolveExtension.registerExtensionPoint(project)
|
||||
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
|
||||
AnalysisHandlerExtension.registerExtensionPoint(project)
|
||||
PackageFragmentProviderExtension.registerExtensionPoint(project)
|
||||
StorageComponentContainerContributor.registerExtensionPoint(project)
|
||||
DeclarationAttributeAltererExtension.registerExtensionPoint(project)
|
||||
PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project)
|
||||
JsSyntheticTranslateExtension.registerExtensionPoint(project)
|
||||
CompilerConfigurationExtension.registerExtensionPoint(project)
|
||||
CollectAdditionalSourcesExtension.registerExtensionPoint(project)
|
||||
ExtraImportsProviderExtension.registerExtensionPoint(project)
|
||||
IrGenerationExtension.registerExtensionPoint(project)
|
||||
ScriptEvaluationExtension.registerExtensionPoint(project)
|
||||
ShellExtension.registerExtensionPoint(project)
|
||||
TypeResolutionInterceptor.registerExtensionPoint(project)
|
||||
CandidateInterceptor.registerExtensionPoint(project)
|
||||
DescriptorSerializerPlugin.registerExtensionPoint(project)
|
||||
}
|
||||
|
||||
internal fun registerExtensionsFromPlugins(project: MockProject, configuration: CompilerConfiguration) {
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
||||
try {
|
||||
registrar.registerProjectComponents(project, configuration)
|
||||
} catch (e: AbstractMethodError) {
|
||||
val message = "The provided plugin ${registrar.javaClass.name} is not compatible with this version of compiler"
|
||||
// Since the scripting plugin is often discovered in the compiler environment, it is often taken from the incompatible
|
||||
// location, and in many cases this is not a fatal error, therefore strong warning is generated instead of exception
|
||||
if (registrar.javaClass.simpleName == "ScriptingCompilerConfigurationComponentRegistrar") {
|
||||
messageCollector?.report(STRONG_WARNING, "Default scripting plugin is disabled: $message")
|
||||
} else {
|
||||
throw IllegalStateException(message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun registerApplicationServicesForCLI(applicationEnvironment: KotlinCoreApplicationEnvironment) {
|
||||
// ability to get text from annotations xml files
|
||||
applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml")
|
||||
applicationEnvironment.registerParserDefinition(JavaParserDefinition())
|
||||
}
|
||||
|
||||
// made public for Upsource
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@JvmStatic
|
||||
fun registerApplicationServices(applicationEnvironment: KotlinCoreApplicationEnvironment) {
|
||||
with(applicationEnvironment) {
|
||||
registerFileType(KotlinFileType.INSTANCE, "kt")
|
||||
registerFileType(KotlinFileType.INSTANCE, KotlinParserDefinition.STD_SCRIPT_SUFFIX)
|
||||
registerParserDefinition(KotlinParserDefinition())
|
||||
application.registerService(KotlinBinaryClassCache::class.java, KotlinBinaryClassCache())
|
||||
application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java)
|
||||
application.registerService(TransactionGuard::class.java, TransactionGuardImpl::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun registerProjectExtensionPoints(area: ExtensionsArea) {
|
||||
CoreApplicationEnvironment.registerExtensionPoint(
|
||||
area, PsiTreeChangePreprocessor.EP.name, PsiTreeChangePreprocessor::class.java
|
||||
)
|
||||
CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP.name, PsiElementFinder::class.java)
|
||||
|
||||
IdeaExtensionPoints.registerVersionSpecificProjectExtensionPoints(area)
|
||||
}
|
||||
|
||||
// made public for Upsource
|
||||
@JvmStatic
|
||||
@Deprecated("Use registerProjectServices(project) instead.", ReplaceWith("registerProjectServices(projectEnvironment.project)"))
|
||||
fun registerProjectServices(
|
||||
projectEnvironment: JavaCoreProjectEnvironment,
|
||||
@Suppress("UNUSED_PARAMETER") messageCollector: MessageCollector?
|
||||
) {
|
||||
registerProjectServices(projectEnvironment.project)
|
||||
}
|
||||
|
||||
// made public for Android Lint
|
||||
@JvmStatic
|
||||
fun registerProjectServices(project: MockProject) {
|
||||
with(project) {
|
||||
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
|
||||
registerService(FacadeCache::class.java, FacadeCache(this))
|
||||
registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver())
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerProjectServicesForCLI(@Suppress("UNUSED_PARAMETER") projectEnvironment: JavaCoreProjectEnvironment) {
|
||||
/**
|
||||
* Note that Kapt may restart code analysis process, and CLI services should be aware of that.
|
||||
* Use PsiManager.getModificationTracker() to ensure that all the data you cached is still valid.
|
||||
*/
|
||||
}
|
||||
|
||||
// made public for Android Lint
|
||||
@JvmStatic
|
||||
fun registerKotlinLightClassSupport(project: MockProject) {
|
||||
with(project) {
|
||||
val traceHolder = CliTraceHolder()
|
||||
val cliLightClassGenerationSupport = CliLightClassGenerationSupport(traceHolder, project)
|
||||
val kotlinAsJavaSupport = CliKotlinAsJavaSupport(this, traceHolder)
|
||||
registerService(LightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(CliLightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(KotlinAsJavaSupport::class.java, kotlinAsJavaSupport)
|
||||
registerService(CodeAnalyzerInitializer::class.java, traceHolder)
|
||||
|
||||
// We don't pass Disposable because in some tests, we manually unregister these extensions, and that leads to LOG.error
|
||||
// exception from `ExtensionPointImpl.doRegisterExtension`, because the registered extension can no longer be found
|
||||
// when the project is being disposed.
|
||||
// For example, see the `unregisterExtension` call in `GenerationUtils.compileFilesUsingFrontendIR`.
|
||||
// TODO: refactor this to avoid registering unneeded extensions in the first place, and avoid using deprecated API.
|
||||
@Suppress("DEPRECATION")
|
||||
PsiElementFinder.EP.getPoint(project).registerExtension(JavaElementFinder(this, kotlinAsJavaSupport))
|
||||
@Suppress("DEPRECATION")
|
||||
PsiElementFinder.EP.getPoint(project).registerExtension(PsiElementFinderImpl(this))
|
||||
}
|
||||
}
|
||||
|
||||
private fun CompilerConfiguration.setupJdkClasspathRoots(configFiles: EnvironmentConfigFiles) {
|
||||
if (getBoolean(JVMConfigurationKeys.NO_JDK)) return
|
||||
|
||||
val jvmTarget = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
if (!jvmTarget) return
|
||||
|
||||
val jdkHome = get(JVMConfigurationKeys.JDK_HOME)
|
||||
val (javaRoot, classesRoots) = if (jdkHome == null) {
|
||||
val javaHome = File(System.getProperty("java.home"))
|
||||
put(JVMConfigurationKeys.JDK_HOME, javaHome)
|
||||
|
||||
javaHome to PathUtil.getJdkClassesRootsFromCurrentJre()
|
||||
} else {
|
||||
jdkHome to PathUtil.getJdkClassesRoots(jdkHome)
|
||||
}
|
||||
|
||||
if (!CoreJrtFileSystem.isModularJdk(javaRoot)) {
|
||||
if (classesRoots.isEmpty()) {
|
||||
report(ERROR, "No class roots are found in the JDK path: $javaRoot")
|
||||
} else {
|
||||
addJvmSdkRoots(classesRoots)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -10,9 +10,8 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.writeKotlinMetadata
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
@@ -309,7 +308,7 @@ class ClassCodegen private constructor(
|
||||
|
||||
if (irClass.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) && !field.isStatic) {
|
||||
// TODO: Write annotations to the component
|
||||
visitor.newRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
|
||||
visitor.addRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,7 +456,7 @@ private val IrClass.flags: Int
|
||||
isAnnotationClass -> Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
isInterface -> Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
isEnumClass -> Opcodes.ACC_ENUM or Opcodes.ACC_SUPER or modality.flags
|
||||
hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) -> Opcodes.ACC_RECORD or Opcodes.ACC_SUPER or modality.flags
|
||||
hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) -> VersionIndependentOpcodes.ACC_RECORD or Opcodes.ACC_SUPER or modality.flags
|
||||
else -> Opcodes.ACC_SUPER or modality.flags
|
||||
}
|
||||
|
||||
|
||||
-495
@@ -1,495 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.writeKotlinMetadata
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_RECORD_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.VOLATILE_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.jvm.checkers.JvmSimpleNameBacktickChecker
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.io.File
|
||||
|
||||
interface MetadataSerializer {
|
||||
fun serialize(metadata: MetadataSource): Pair<MessageLite, JvmStringTable>?
|
||||
fun bindMethodMetadata(metadata: MetadataSource.Property, signature: Method)
|
||||
fun bindMethodMetadata(metadata: MetadataSource.Function, signature: Method)
|
||||
fun bindFieldMetadata(metadata: MetadataSource.Property, signature: Pair<Type, String>)
|
||||
}
|
||||
|
||||
class ClassCodegen private constructor(
|
||||
val irClass: IrClass,
|
||||
val context: JvmBackendContext,
|
||||
private val parentFunction: IrFunction?,
|
||||
) : InnerClassConsumer {
|
||||
private val parentClassCodegen = (parentFunction?.parentAsClass ?: irClass.parent as? IrClass)?.let { getOrCreate(it, context) }
|
||||
private val withinInline: Boolean = parentClassCodegen?.withinInline == true || parentFunction?.isInline == true
|
||||
|
||||
private val state get() = context.state
|
||||
private val typeMapper get() = context.typeMapper
|
||||
|
||||
val type: Type = typeMapper.mapClass(irClass)
|
||||
|
||||
val reifiedTypeParametersUsages = ReifiedTypeParametersUsages()
|
||||
|
||||
private val jvmSignatureClashDetector = JvmSignatureClashDetector(irClass, type, context)
|
||||
|
||||
private val classOrigin = irClass.descriptorOrigin
|
||||
|
||||
private val visitor = state.factory.newVisitor(classOrigin, type, irClass.fileParent.loadSourceFilesInfo()).apply {
|
||||
val signature = typeMapper.mapClassSignature(irClass, type)
|
||||
// Ensure that the backend only produces class names that would be valid in the frontend for JVM.
|
||||
if (context.state.classBuilderMode.generateBodies && signature.hasInvalidName()) {
|
||||
throw IllegalStateException("Generating class with invalid name '${type.className}': ${irClass.dump()}")
|
||||
}
|
||||
defineClass(
|
||||
irClass.psiElement,
|
||||
state.classFileVersion,
|
||||
irClass.flags,
|
||||
signature.name,
|
||||
signature.javaGenericSignature,
|
||||
signature.superclassName,
|
||||
signature.interfaces.toTypedArray()
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: the order of entries in this set depends on the order in which methods are generated; this means it is unstable
|
||||
// under incremental compilation, as calls to `inline fun`s declared in this class cause them to be generated out of order.
|
||||
private val innerClasses = linkedSetOf<IrClass>()
|
||||
|
||||
// TODO: the names produced by generators in this map depend on the order in which methods are generated; see above.
|
||||
private val regeneratedObjectNameGenerators = mutableMapOf<String, NameGenerator>()
|
||||
|
||||
fun getRegeneratedObjectNameGenerator(function: IrFunction): NameGenerator {
|
||||
val name = if (function.name.isSpecial) "special" else function.name.asString()
|
||||
return regeneratedObjectNameGenerators.getOrPut(name) {
|
||||
NameGenerator("${type.internalName}\$$name\$\$inlined")
|
||||
}
|
||||
}
|
||||
|
||||
private var generated = false
|
||||
|
||||
fun generate() {
|
||||
// TODO: reject repeated generate() calls; currently, these can happen for objects in finally
|
||||
// blocks since they are `accept`ed once per each CFG edge out of the try-finally.
|
||||
if (generated) return
|
||||
generated = true
|
||||
|
||||
// We remove reads of `$$delegatedProperties` (and the field itself) if they are not in fact used for anything.
|
||||
val delegatedProperties = irClass.fields.singleOrNull { it.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE }
|
||||
val delegatedPropertyOptimizer = if (delegatedProperties != null) DelegatedPropertyOptimizer() else null
|
||||
// Generating a method node may cause the addition of a field with an initializer if an inline function
|
||||
// call uses `assert` and the JVM assertions mode is enabled. To avoid concurrent modification errors,
|
||||
// there is a very specific generation order.
|
||||
val smap = context.getSourceMapper(irClass)
|
||||
// 1. Any method other than `<clinit>` can add a field and a `<clinit>` statement:
|
||||
for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
|
||||
if (method.name.asString() != "<clinit>") {
|
||||
generateMethod(method, smap, delegatedPropertyOptimizer)
|
||||
}
|
||||
}
|
||||
// 2. `<clinit>` itself can add a field, but the statement is generated via the `return init` hack:
|
||||
irClass.functions.find { it.name.asString() == "<clinit>" }?.let { generateMethod(it, smap, delegatedPropertyOptimizer) }
|
||||
// 3. Now we have all the fields (`$$delegatedProperties` might be redundant if all reads were optimized out):
|
||||
for (field in irClass.fields) {
|
||||
if (field !== delegatedProperties || delegatedPropertyOptimizer?.needsDelegatedProperties == true) {
|
||||
generateField(field)
|
||||
}
|
||||
}
|
||||
// 4. Generate nested classes at the end, to ensure that when the companion's metadata is serialized
|
||||
// everything moved to the outer class has already been recorded in `globalSerializationBindings`.
|
||||
for (declaration in irClass.declarations) {
|
||||
if (declaration is IrClass) {
|
||||
getOrCreate(declaration, context).generate()
|
||||
}
|
||||
}
|
||||
|
||||
object : AnnotationCodegen(this@ClassCodegen, context) {
|
||||
override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor {
|
||||
return visitor.visitor.visitAnnotation(descr, visible)
|
||||
}
|
||||
}.genAnnotations(irClass, null, null)
|
||||
generateKotlinMetadataAnnotation()
|
||||
|
||||
generateInnerAndOuterClasses()
|
||||
|
||||
if (withinInline || !smap.isTrivial) {
|
||||
visitor.visitSMAP(smap, !context.state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
||||
} else {
|
||||
smap.sourceInfo!!.sourceFileName?.let {
|
||||
visitor.visitSource(it, null)
|
||||
}
|
||||
}
|
||||
|
||||
visitor.done()
|
||||
jvmSignatureClashDetector.reportErrors(classOrigin)
|
||||
}
|
||||
|
||||
fun generateAssertFieldIfNeeded(generatingClInit: Boolean): IrExpression? {
|
||||
if (irClass.hasAssertionsDisabledField(context))
|
||||
return null
|
||||
val topLevelClass = generateSequence(this) { it.parentClassCodegen }.last().irClass
|
||||
val field = irClass.buildAssertionsDisabledField(context, topLevelClass)
|
||||
generateField(field)
|
||||
// Normally, `InitializersLowering` would move the initializer to <clinit>, but
|
||||
// it's obviously too late for that.
|
||||
val init = IrSetFieldImpl(
|
||||
field.startOffset, field.endOffset, field.symbol, null,
|
||||
field.initializer!!.expression, context.irBuiltIns.unitType
|
||||
)
|
||||
if (generatingClInit) {
|
||||
// Too late to modify the IR; have to ask the currently active `ExpressionCodegen`
|
||||
// to generate this statement directly.
|
||||
return init
|
||||
}
|
||||
val classInitializer = irClass.functions.singleOrNull { it.name.asString() == "<clinit>" } ?: irClass.addFunction {
|
||||
name = Name.special("<clinit>")
|
||||
returnType = context.irBuiltIns.unitType
|
||||
}.apply {
|
||||
body = IrBlockBodyImpl(startOffset, endOffset)
|
||||
}
|
||||
(classInitializer.body as IrBlockBody).statements.add(0, init)
|
||||
return null
|
||||
}
|
||||
|
||||
private val metadataSerializer: MetadataSerializer =
|
||||
context.backendExtension.createSerializer(
|
||||
context, irClass, type, visitor.serializationBindings, parentClassCodegen?.metadataSerializer
|
||||
)
|
||||
|
||||
private fun generateKotlinMetadataAnnotation() {
|
||||
// TODO: if `-Xmultifile-parts-inherit` is enabled, write the corresponding flag for parts and facades to [Metadata.extraInt].
|
||||
val extraFlags = context.backendExtension.generateMetadataExtraFlags(state.abiStability)
|
||||
|
||||
val facadeClassName = context.multifileFacadeForPart[irClass.attributeOwnerId]
|
||||
val metadata = irClass.metadata
|
||||
val entry = irClass.fileParent.fileEntry
|
||||
val kind = when {
|
||||
facadeClassName != null -> KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
||||
metadata is MetadataSource.Class -> KotlinClassHeader.Kind.CLASS
|
||||
metadata is MetadataSource.File -> KotlinClassHeader.Kind.FILE_FACADE
|
||||
metadata is MetadataSource.Function -> KotlinClassHeader.Kind.SYNTHETIC_CLASS
|
||||
entry is MultifileFacadeFileEntry -> KotlinClassHeader.Kind.MULTIFILE_CLASS
|
||||
else -> KotlinClassHeader.Kind.SYNTHETIC_CLASS
|
||||
}
|
||||
writeKotlinMetadata(visitor, state, kind, extraFlags) {
|
||||
if (metadata != null) {
|
||||
metadataSerializer.serialize(metadata)?.let { (proto, stringTable) ->
|
||||
DescriptorAsmUtil.writeAnnotationData(it, proto, stringTable)
|
||||
}
|
||||
}
|
||||
|
||||
if (entry is MultifileFacadeFileEntry) {
|
||||
val arv = it.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME)
|
||||
for (partFile in entry.partFiles) {
|
||||
val fileClass = partFile.declarations.singleOrNull { it.isFileClass } as IrClass?
|
||||
if (fileClass != null) arv.visit(null, typeMapper.mapClass(fileClass).internalName)
|
||||
}
|
||||
arv.visitEnd()
|
||||
}
|
||||
|
||||
if (facadeClassName != null) {
|
||||
it.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassName.internalName)
|
||||
}
|
||||
|
||||
if (irClass in context.classNameOverride) {
|
||||
val isFileClass = kind == KotlinClassHeader.Kind.MULTIFILE_CLASS ||
|
||||
kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART ||
|
||||
kind == KotlinClassHeader.Kind.FILE_FACADE
|
||||
assert(isFileClass) { "JvmPackageName is not supported for classes: ${irClass.render()}" }
|
||||
it.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, irClass.fqNameWhenAvailable!!.parent().asString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFile.loadSourceFilesInfo(): List<File> {
|
||||
val entry = fileEntry
|
||||
if (entry is MultifileFacadeFileEntry) {
|
||||
return entry.partFiles.flatMap { it.loadSourceFilesInfo() }
|
||||
}
|
||||
return listOfNotNull(context.psiSourceManager.getFileEntry(this)?.let { File(it.name) })
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getOrCreate(
|
||||
irClass: IrClass,
|
||||
context: JvmBackendContext,
|
||||
// The `parentFunction` is only set for classes nested inside of functions. This is usually safe, since there is no
|
||||
// way to refer to (inline) members of such a class from outside of the function unless the function in question is
|
||||
// itself declared as inline. In that case, the function will be compiled before we can refer to the nested class.
|
||||
//
|
||||
// The one exception to this rule are anonymous objects defined as members of a class. These are nested inside of the
|
||||
// class initializer, but can be referred to from anywhere within the scope of the class. That's why we have to ensure
|
||||
// that all references to classes inside of <clinit> have a non-null `parentFunction`.
|
||||
parentFunction: IrFunction? = irClass.parent.safeAs<IrFunction>()?.takeIf {
|
||||
it.origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER
|
||||
},
|
||||
): ClassCodegen =
|
||||
context.classCodegens.getOrPut(irClass) { ClassCodegen(irClass, context, parentFunction) }.also {
|
||||
assert(parentFunction == null || it.parentFunction == parentFunction) {
|
||||
"inconsistent parent function for ${irClass.render()}:\n" +
|
||||
"New: ${parentFunction!!.render()}\n" +
|
||||
"Old: ${it.parentFunction?.render()}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun JvmClassSignature.hasInvalidName() =
|
||||
name.splitToSequence('/').any { identifier -> identifier.any { it in JvmSimpleNameBacktickChecker.INVALID_CHARS } }
|
||||
}
|
||||
|
||||
private fun generateField(field: IrField) {
|
||||
val fieldType = typeMapper.mapType(field)
|
||||
val fieldSignature =
|
||||
if (field.origin == IrDeclarationOrigin.PROPERTY_DELEGATE) null
|
||||
else context.methodSignatureMapper.mapFieldSignature(field)
|
||||
val fieldName = field.name.asString()
|
||||
val flags = field.computeFieldFlags(context, state.languageVersionSettings)
|
||||
val fv = visitor.newField(
|
||||
field.descriptorOrigin, flags, fieldName, fieldType.descriptor,
|
||||
fieldSignature, (field.initializer?.expression as? IrConst<*>)?.value
|
||||
)
|
||||
|
||||
jvmSignatureClashDetector.trackField(field, RawSignature(fieldName, fieldType.descriptor, MemberKind.FIELD))
|
||||
|
||||
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
|
||||
val skipNullabilityAnnotations =
|
||||
flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 ||
|
||||
field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
|
||||
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
|
||||
override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor {
|
||||
return fv.visitAnnotation(descr, visible)
|
||||
}
|
||||
|
||||
override fun visitTypeAnnotation(descr: String?, path: TypePath?, visible: Boolean): AnnotationVisitor {
|
||||
return fv.visitTypeAnnotation(TypeReference.newTypeReference(TypeReference.FIELD).value, path, descr, visible)
|
||||
}
|
||||
}.genAnnotations(field, fieldType, field.type)
|
||||
}
|
||||
|
||||
(field.metadata as? MetadataSource.Property)?.let {
|
||||
metadataSerializer.bindFieldMetadata(it, fieldType to fieldName)
|
||||
}
|
||||
|
||||
if (irClass.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) && !field.isStatic) {
|
||||
// TODO: Write annotations to the component
|
||||
// visitor.newRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
|
||||
}
|
||||
}
|
||||
|
||||
private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>()
|
||||
|
||||
fun generateMethodNode(method: IrFunction): SMAPAndMethodNode {
|
||||
if (!method.isInline && !method.isSuspendCapturingCrossinline()) {
|
||||
// Inline methods can be used multiple times by `IrSourceCompilerForInline`, suspend methods
|
||||
// are used twice (`f` and `f$$forInline`) if they capture crossinline lambdas, and everything
|
||||
// else is only generated by `generateMethod` below so does not need caching.
|
||||
// TODO: inline lambdas are not marked `isInline`, and are generally used once, but may be needed
|
||||
// multiple times if declared in a `finally` block - should they be cached?
|
||||
return FunctionCodegen(method, this).generate()
|
||||
}
|
||||
val (node, smap) = generatedInlineMethods.getOrPut(method) { FunctionCodegen(method, this).generate() }
|
||||
val copy = with(node) { MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions.toTypedArray()) }
|
||||
node.instructions.resetLabels()
|
||||
node.accept(copy)
|
||||
return SMAPAndMethodNode(copy, smap)
|
||||
}
|
||||
|
||||
private fun generateMethod(method: IrFunction, classSMAP: SourceMapper, delegatedPropertyOptimizer: DelegatedPropertyOptimizer?) {
|
||||
if (method.isFakeOverride) {
|
||||
jvmSignatureClashDetector.trackFakeOverrideMethod(method)
|
||||
return
|
||||
}
|
||||
|
||||
val (node, smap) = generateMethodNode(method)
|
||||
if (delegatedPropertyOptimizer != null) {
|
||||
delegatedPropertyOptimizer.transform(node)
|
||||
if (method.name.asString() == "<clinit>") {
|
||||
delegatedPropertyOptimizer.transformClassInitializer(node)
|
||||
}
|
||||
}
|
||||
node.preprocessSuspendMarkers(
|
||||
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE || method.isEffectivelyInlineOnly(),
|
||||
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||
)
|
||||
val mv = with(node) { visitor.newMethod(method.descriptorOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
|
||||
val smapCopier = SourceMapCopier(classSMAP, smap)
|
||||
val smapCopyingVisitor = object : MethodVisitor(Opcodes.API_VERSION, mv) {
|
||||
override fun visitLineNumber(line: Int, start: Label) =
|
||||
super.visitLineNumber(smapCopier.mapLineNumber(line), start)
|
||||
}
|
||||
if (method.hasContinuation()) {
|
||||
// Generate a state machine within this method. The continuation class for it should be generated
|
||||
// lazily so that if tail call optimization kicks in, the unused class will not be written to the output.
|
||||
val continuationClass = method.continuationClass() // null if `SuspendLambda.invokeSuspend` - `this` is continuation itself
|
||||
val continuationClassCodegen = lazy { if (continuationClass != null) getOrCreate(continuationClass, context, method) else this }
|
||||
node.acceptWithStateMachine(method, this, smapCopyingVisitor) { continuationClassCodegen.value.visitor }
|
||||
if (continuationClass != null && (continuationClassCodegen.isInitialized() || method.isSuspendCapturingCrossinline())) {
|
||||
continuationClassCodegen.value.generate()
|
||||
}
|
||||
} else {
|
||||
node.accept(smapCopyingVisitor)
|
||||
}
|
||||
jvmSignatureClashDetector.trackMethod(method, RawSignature(node.name, node.desc, MemberKind.METHOD))
|
||||
|
||||
when (val metadata = method.metadata) {
|
||||
is MetadataSource.Property -> {
|
||||
assert(method.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS) {
|
||||
"MetadataSource.Property on IrFunction should only be used for synthetic \$annotations methods: ${method.render()}"
|
||||
}
|
||||
metadataSerializer.bindMethodMetadata(metadata, Method(node.name, node.desc))
|
||||
}
|
||||
is MetadataSource.Function -> metadataSerializer.bindMethodMetadata(metadata, Method(node.name, node.desc))
|
||||
null -> Unit
|
||||
else -> error("Incorrect metadata source $metadata for:\n${method.dump()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateInnerAndOuterClasses() {
|
||||
// JVMS7 (4.7.6): a nested class or interface member will have InnerClasses information
|
||||
// for each enclosing class and for each immediate member
|
||||
parentClassCodegen?.innerClasses?.add(irClass)
|
||||
for (codegen in generateSequence(this) { it.parentClassCodegen }.takeWhile { it.parentClassCodegen != null }) {
|
||||
innerClasses.add(codegen.irClass)
|
||||
}
|
||||
|
||||
// JVMS7 (4.7.7): A class must have an EnclosingMethod attribute if and only if
|
||||
// it is a local class or an anonymous class.
|
||||
//
|
||||
// The attribute contains the innermost class that encloses the declaration of
|
||||
// the current class. If the current class is immediately enclosed by a method
|
||||
// or constructor, the name and type of the function is recorded as well.
|
||||
if (parentClassCodegen != null) {
|
||||
// In case there's no primary constructor, it's unclear which constructor should be the enclosing one, so we select the first.
|
||||
val enclosingFunction = if (irClass.attributeOwnerId in context.isEnclosedInConstructor) {
|
||||
val containerClass = parentClassCodegen.irClass
|
||||
containerClass.primaryConstructor
|
||||
?: containerClass.declarations.firstIsInstanceOrNull<IrConstructor>()
|
||||
?: error("Class in a non-static initializer found, but container has no constructors: ${containerClass.render()}")
|
||||
} else parentFunction
|
||||
if (enclosingFunction != null || irClass.isAnonymousObject) {
|
||||
val method = enclosingFunction?.let(context.methodSignatureMapper::mapAsmMethod)
|
||||
visitor.visitOuterClass(parentClassCodegen.type.internalName, method?.name, method?.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
for (klass in innerClasses) {
|
||||
val innerClass = typeMapper.classInternalName(klass)
|
||||
val outerClass =
|
||||
if (klass.attributeOwnerId in context.isEnclosedInConstructor) null
|
||||
else klass.parent.safeAs<IrClass>()?.let(typeMapper::classInternalName)
|
||||
val innerName = klass.name.takeUnless { it.isSpecial }?.asString()
|
||||
visitor.visitInnerClass(innerClass, outerClass, innerName, klass.calculateInnerClassAccessFlags(context))
|
||||
}
|
||||
}
|
||||
|
||||
override fun addInnerClassInfoFromAnnotation(innerClass: IrClass) {
|
||||
// It's necessary for proper recovering of classId by plain string JVM descriptor when loading annotations
|
||||
// See FileBasedKotlinClass.convertAnnotationVisitor
|
||||
generateSequence<IrDeclaration>(innerClass) { it.parent as? IrDeclaration }.takeWhile { !it.isTopLevelDeclaration }.forEach {
|
||||
if (it is IrClass) {
|
||||
innerClasses.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val IrDeclaration.descriptorOrigin: JvmDeclarationOrigin
|
||||
get() {
|
||||
val psiElement = context.psiSourceManager.findPsiElement(this)
|
||||
// For declarations inside lambdas, produce a descriptor which refers back to the original function.
|
||||
// This is needed for plugins which check for lambdas inside of inline functions using the descriptor
|
||||
// contained in JvmDeclarationOrigin. This matches the behavior of the JVM backend.
|
||||
// TODO: this is really not very useful, as this does nothing for other anonymous objects.
|
||||
val isLambda = irClass.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL ||
|
||||
irClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
|
||||
val descriptor = if (isLambda)
|
||||
irClass.attributeOwnerId.safeAs<IrFunctionReference>()?.symbol?.owner?.toIrBasedDescriptor() ?: toIrBasedDescriptor()
|
||||
else
|
||||
toIrBasedDescriptor()
|
||||
return if (origin == IrDeclarationOrigin.FILE_CLASS)
|
||||
JvmDeclarationOrigin(JvmDeclarationOriginKind.PACKAGE_PART, psiElement, descriptor)
|
||||
else
|
||||
OtherOrigin(psiElement, descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private val IrClass.flags: Int
|
||||
get() = origin.flags or getVisibilityAccessFlagForClass() or
|
||||
(if (isAnnotatedWithDeprecated) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) Opcodes.ACC_SYNTHETIC else 0) or
|
||||
when {
|
||||
isAnnotationClass -> Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
isInterface -> Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
isEnumClass -> Opcodes.ACC_ENUM or Opcodes.ACC_SUPER or modality.flags
|
||||
else -> Opcodes.ACC_SUPER or modality.flags
|
||||
}
|
||||
|
||||
private fun IrField.computeFieldFlags(context: JvmBackendContext, languageVersionSettings: LanguageVersionSettings): Int =
|
||||
origin.flags or visibility.flags or
|
||||
(if (isDeprecatedCallable(context) ||
|
||||
correspondingPropertySymbol?.owner?.isDeprecatedCallable(context) == true
|
||||
) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (isFinal) Opcodes.ACC_FINAL else 0) or
|
||||
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||
(if (hasAnnotation(VOLATILE_ANNOTATION_FQ_NAME)) Opcodes.ACC_VOLATILE else 0) or
|
||||
(if (hasAnnotation(TRANSIENT_ANNOTATION_FQ_NAME)) Opcodes.ACC_TRANSIENT else 0) or
|
||||
(if (hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) ||
|
||||
isPrivateCompanionFieldInInterface(languageVersionSettings)
|
||||
) Opcodes.ACC_SYNTHETIC else 0)
|
||||
|
||||
private fun IrField.isPrivateCompanionFieldInInterface(languageVersionSettings: LanguageVersionSettings): Boolean =
|
||||
origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE &&
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.ProperVisibilityForCompanionObjectInstanceField) &&
|
||||
parentAsClass.isJvmInterface &&
|
||||
DescriptorVisibilities.isPrivate(parentAsClass.companionObject()!!.visibility)
|
||||
|
||||
private val IrDeclarationOrigin.flags: Int
|
||||
get() = (if (isSynthetic) Opcodes.ACC_SYNTHETIC else 0) or
|
||||
(if (this == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) Opcodes.ACC_ENUM else 0)
|
||||
|
||||
private val Modality.flags: Int
|
||||
get() = when (this) {
|
||||
Modality.ABSTRACT, Modality.SEALED -> Opcodes.ACC_ABSTRACT
|
||||
Modality.FINAL -> Opcodes.ACC_FINAL
|
||||
Modality.OPEN -> 0
|
||||
else -> throw AssertionError("Unsupported modality $this")
|
||||
}
|
||||
|
||||
private val DescriptorVisibility.flags: Int
|
||||
get() = DescriptorAsmUtil.getVisibilityAccessFlag(this) ?: throw AssertionError("Unsupported visibility $this")
|
||||
Reference in New Issue
Block a user