
org.jetbrains.jet.codegen.ImplementationBodyCodegen Maven / Gradle / Ivy
/*
* Copyright 2010-2014 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.jet.codegen;
import com.google.common.collect.Lists;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.util.ArrayUtil;
import kotlin.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.backend.common.DataClassMethodGenerator;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.binding.MutableClosure;
import org.jetbrains.jet.codegen.bridges.BridgesPackage;
import org.jetbrains.jet.codegen.context.ClassContext;
import org.jetbrains.jet.codegen.context.ConstructorContext;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.codegen.signature.*;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.descriptors.serialization.BitEncoding;
import org.jetbrains.jet.descriptors.serialization.ClassData;
import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer;
import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmClassSignature;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import java.util.*;
import static org.jetbrains.jet.codegen.AsmUtil.*;
import static org.jetbrains.jet.codegen.JvmCodegenUtil.*;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class ImplementationBodyCodegen extends ClassBodyCodegen {
private static final String VALUES = "$VALUES";
private JetDelegationSpecifier superCall;
private Type superClassAsmType;
@Nullable // null means java/lang/Object
private JetType superClassType;
private final Type classAsmType;
private final FunctionCodegen functionCodegen;
private final PropertyCodegen propertyCodegen;
private List classObjectPropertiesToCopy;
public ImplementationBodyCodegen(
@NotNull JetClassOrObject aClass,
@NotNull ClassContext context,
@NotNull ClassBuilder v,
@NotNull GenerationState state,
@Nullable MemberCodegen> parentCodegen
) {
super(aClass, context, v, state, parentCodegen);
this.classAsmType = typeMapper.mapClass(descriptor);
this.functionCodegen = new FunctionCodegen(context, v, state, this);
this.propertyCodegen = new PropertyCodegen(context, v, this.functionCodegen, this);
}
@Override
protected void generateDeclaration() {
getSuperClass();
JvmClassSignature signature = signature();
boolean isAbstract = false;
boolean isInterface = false;
boolean isFinal = false;
boolean isStatic;
boolean isAnnotation = false;
boolean isEnum = false;
if (myClass instanceof JetClass) {
JetClass jetClass = (JetClass) myClass;
if (jetClass.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
isAbstract = true;
}
if (jetClass.isTrait()) {
isAbstract = true;
isInterface = true;
}
else if (jetClass.isAnnotation()) {
isAbstract = true;
isInterface = true;
isAnnotation = true;
signature.getInterfaces().add("java/lang/annotation/Annotation");
}
else if (jetClass.isEnum()) {
isAbstract = hasAbstractMembers(descriptor);
isEnum = true;
}
if (descriptor.getKind() == ClassKind.OBJECT || descriptor.getKind() == ClassKind.CLASS_OBJECT) {
isFinal = true;
}
if (!jetClass.hasModifier(JetTokens.OPEN_KEYWORD) && !isAbstract) {
isFinal = true;
}
isStatic = !jetClass.isInner();
}
else {
isStatic = myClass.getParent() instanceof JetClassObject;
isFinal = true;
}
int access = 0;
if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES && !DescriptorUtils.isTopLevelDeclaration(descriptor)) {
// ClassBuilderMode.LIGHT_CLASSES means we are generating light classes & looking at a nested or inner class
// Light class generation is implemented so that Cls-classes only read bare code of classes,
// without knowing whether these classes are inner or not (see ClassStubBuilder.EMPTY_STRATEGY)
// Thus we must write full accessibility flags on inner classes in this mode
access |= getVisibilityAccessFlag(descriptor);
// Same for STATIC
if (isStatic) {
access |= ACC_STATIC;
}
}
else {
access |= getVisibilityAccessFlagForClass(descriptor);
}
if (isAbstract) {
access |= ACC_ABSTRACT;
}
if (isInterface) {
access |= ACC_INTERFACE; // ACC_SUPER
}
else {
access |= ACC_SUPER;
}
if (isFinal) {
access |= ACC_FINAL;
}
if (isAnnotation) {
access |= ACC_ANNOTATION;
}
if (KotlinBuiltIns.getInstance().isDeprecated(descriptor)) {
access |= ACC_DEPRECATED;
}
if (isEnum) {
for (JetDeclaration declaration : myClass.getDeclarations()) {
if (declaration instanceof JetEnumEntry) {
if (enumEntryNeedSubclass(state.getBindingContext(), (JetEnumEntry) declaration)) {
access &= ~ACC_FINAL;
}
}
}
access |= ACC_ENUM;
}
List interfaces = signature.getInterfaces();
v.defineClass(myClass, V1_6,
access,
signature.getName(),
signature.getJavaGenericSignature(),
signature.getSuperclassName(),
ArrayUtil.toStringArray(interfaces)
);
v.visitSource(myClass.getContainingFile().getName(), null);
writeEnclosingMethod();
writeOuterClasses();
writeInnerClasses();
AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor);
}
@Override
protected void generateKotlinAnnotation() {
if (isAnonymousObject(descriptor)) {
writeKotlinSyntheticClassAnnotation(v, KotlinSyntheticClass.Kind.ANONYMOUS_OBJECT);
return;
}
if (!isTopLevelOrInnerClass(descriptor)) {
// LOCAL_CLASS is also written to inner classes of local classes
writeKotlinSyntheticClassAnnotation(v, KotlinSyntheticClass.Kind.LOCAL_CLASS);
return;
}
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(v.getSerializationBindings()));
ProtoBuf.Class classProto = serializer.classProto(descriptor).build();
ClassData data = new ClassData(createNameResolver(serializer.getNameTable()), classProto);
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CLASS), true);
//noinspection ConstantConditions
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(data.toBytes())) {
array.visit(null, string);
}
array.visitEnd();
av.visitEnd();
}
private void writeEnclosingMethod() {
//JVMS7: A class must have an EnclosingMethod attribute if and only if it is a local class or an anonymous class.
DeclarationDescriptor parentDescriptor = descriptor.getContainingDeclaration();
boolean isObjectLiteral = DescriptorUtils.isAnonymousObject(descriptor);
boolean isLocalOrAnonymousClass = isObjectLiteral ||
!(parentDescriptor instanceof PackageFragmentDescriptor || parentDescriptor instanceof ClassDescriptor);
// Do not emit enclosing method in "light-classes mode" since currently we generate local light classes as if they're top level
if (isLocalOrAnonymousClass && state.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) {
String outerClassName = getOuterClassName(descriptor, typeMapper);
FunctionDescriptor function = AsmUtil.isDeclarationInsideInlineFunction(descriptor)
? null
: DescriptorUtils.getParentOfType(descriptor, FunctionDescriptor.class);
if (function != null) {
Method method = typeMapper.mapSignature(function).getAsmMethod();
v.visitOuterClass(outerClassName, method.getName(), method.getDescriptor());
}
else {
v.visitOuterClass(outerClassName, null, null);
}
}
}
@NotNull
private static String getOuterClassName(@NotNull ClassDescriptor classDescriptor, @NotNull JetTypeMapper typeMapper) {
ClassDescriptor container = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class);
if (container != null) {
return typeMapper.mapClass(container).getInternalName();
}
JetFile containingFile = BindingContextUtils.getContainingFile(typeMapper.getBindingContext(), classDescriptor);
assert containingFile != null : "Containing file should be present for " + classDescriptor;
return PackageCodegen.getPackagePartInternalName(containingFile);
}
private void writeInnerClasses() {
Collection result = bindingContext.get(INNER_CLASSES, descriptor);
if (result != null) {
for (ClassDescriptor innerClass : result) {
writeInnerClass(innerClass);
}
}
}
private void writeOuterClasses() {
// JVMS7 (4.7.6): a nested class or interface member will have InnerClasses information
// for each enclosing class and for each immediate member
DeclarationDescriptor inner = descriptor;
while (true) {
if (inner == null || isTopLevelDeclaration(inner)) {
break;
}
if (inner instanceof ClassDescriptor) {
writeInnerClass((ClassDescriptor) inner);
}
inner = inner.getContainingDeclaration();
}
}
private void writeInnerClass(@NotNull ClassDescriptor innerClass) {
// TODO: proper access
int innerClassAccess = getVisibilityAccessFlag(innerClass);
if (innerClass.getModality() == Modality.FINAL) {
innerClassAccess |= ACC_FINAL;
}
else if (innerClass.getModality() == Modality.ABSTRACT) {
innerClassAccess |= ACC_ABSTRACT;
}
if (innerClass.getKind() == ClassKind.TRAIT) {
innerClassAccess |= ACC_INTERFACE;
}
else if (innerClass.getKind() == ClassKind.ENUM_CLASS) {
innerClassAccess |= ACC_ENUM;
}
if (!innerClass.isInner()) {
innerClassAccess |= ACC_STATIC;
}
// TODO: cache internal names
DeclarationDescriptor containing = innerClass.getContainingDeclaration();
String outerClassInternalName = containing instanceof ClassDescriptor ? getInternalNameForImpl((ClassDescriptor) containing) : null;
String innerClassInternalName;
String innerName;
if (isClassObject(innerClass)) {
innerName = JvmAbi.CLASS_OBJECT_CLASS_NAME;
innerClassInternalName = outerClassInternalName + JvmAbi.CLASS_OBJECT_SUFFIX;
}
else {
innerName = innerClass.getName().isSpecial() ? null : innerClass.getName().asString();
innerClassInternalName = getInternalNameForImpl(innerClass);
}
v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, innerClassAccess);
}
@NotNull
private String getInternalNameForImpl(@NotNull ClassDescriptor descriptor) {
return typeMapper.mapClass(descriptor).getInternalName();
}
@NotNull
private JvmClassSignature signature() {
BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS);
typeMapper.writeFormalTypeParameters(descriptor.getTypeConstructor().getParameters(), sw);
sw.writeSuperclass();
if (superClassType == null) {
sw.writeClassBegin(superClassAsmType);
sw.writeClassEnd();
}
else {
typeMapper.mapSupertype(superClassType, sw);
}
sw.writeSuperclassEnd();
List interfaceSupertypes = Lists.newArrayList();
boolean explicitKObject = false;
for (JetDelegationSpecifier specifier : myClass.getDelegationSpecifiers()) {
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
assert superType != null : "No supertype for class: " + myClass.getText();
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
if (isInterface(superClassDescriptor)) {
interfaceSupertypes.add(superType);
assert superClassDescriptor != null : "should be already checked by isInterface()";
if (JvmAbi.K_OBJECT.equalsTo(DescriptorUtils.getFqName(superClassDescriptor))) {
explicitKObject = true;
}
}
}
LinkedHashSet superInterfaces = new LinkedHashSet();
if (!explicitKObject) {
Type kObject = asmTypeByFqNameWithoutInnerClasses(JvmAbi.K_OBJECT);
sw.writeInterface();
sw.writeClassBegin(kObject);
sw.writeClassEnd();
sw.writeInterfaceEnd();
superInterfaces.add(kObject.getInternalName());
}
for (JetType supertype : interfaceSupertypes) {
sw.writeInterface();
Type jvmName = typeMapper.mapSupertype(supertype, sw);
sw.writeInterfaceEnd();
superInterfaces.add(jvmName.getInternalName());
}
return new JvmClassSignature(classAsmType.getInternalName(), superClassAsmType.getInternalName(),
new ArrayList(superInterfaces),
sw.makeJavaGenericSignature());
}
protected void getSuperClass() {
superClassAsmType = AsmTypeConstants.OBJECT_TYPE;
superClassType = null;
List delegationSpecifiers = myClass.getDelegationSpecifiers();
if (myClass instanceof JetClass && ((JetClass) myClass).isTrait()) {
return;
}
if (kind != OwnerKind.IMPLEMENTATION) {
throw new IllegalStateException("must be impl to reach this code: " + kind);
}
for (JetDelegationSpecifier specifier : delegationSpecifiers) {
if (specifier instanceof JetDelegatorToSuperClass || specifier instanceof JetDelegatorToSuperCall) {
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
assert superType != null :
String.format("No type recorded for \n---\n%s\n---\n", JetPsiUtil.getElementTextWithContext(specifier));
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
assert superClassDescriptor != null;
if (!isInterface(superClassDescriptor)) {
superClassType = superType;
superClassAsmType = typeMapper.mapClass(superClassDescriptor);
superCall = specifier;
}
}
}
if (superClassType == null) {
if (descriptor.getKind() == ClassKind.ENUM_CLASS) {
superClassType = KotlinBuiltIns.getInstance().getEnumType(descriptor.getDefaultType());
superClassAsmType = typeMapper.mapType(superClassType);
}
if (descriptor.getKind() == ClassKind.ENUM_ENTRY) {
superClassType = descriptor.getTypeConstructor().getSupertypes().iterator().next();
superClassAsmType = typeMapper.mapType(superClassType);
}
}
}
@Override
protected void generateSyntheticParts() {
generateDelegatedPropertyMetadataArray();
generateFieldForSingleton();
generateClassObjectBackingFieldCopies();
try {
generatePrimaryConstructor();
}
catch (CompilationException e) {
throw e;
}
catch (ProcessCanceledException e) {
throw e;
}
catch (RuntimeException e) {
throw new RuntimeException("Error generating primary constructor of class " + myClass.getName() + " with kind " + kind, e);
}
generateTraitMethods();
generateSyntheticAccessors();
generateEnumMethodsAndConstInitializers();
generateFunctionsForDataClasses();
generateBuiltinMethodStubs();
generateToArray();
genClosureFields(context.closure, v, state.getTypeMapper());
}
private void generateDelegatedPropertyMetadataArray() {
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
}
}
private boolean isGenericToArrayPresent() {
Collection functions = descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier("toArray"));
for (FunctionDescriptor function : functions) {
if (CallResolverUtil.isOrOverridesSynthesized(function)) {
continue;
}
if (function.getValueParameters().size() != 1 || function.getTypeParameters().size() != 1) {
continue;
}
JetType arrayType = KotlinBuiltIns.getInstance().getArrayType(function.getTypeParameters().get(0).getDefaultType());
JetType returnType = function.getReturnType();
assert returnType != null : function.toString();
JetType paramType = function.getValueParameters().get(0).getType();
if (JetTypeChecker.INSTANCE.equalTypes(arrayType, returnType) && JetTypeChecker.INSTANCE.equalTypes(arrayType, paramType)) {
return true;
}
}
return false;
}
private void generateToArray() {
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
if (!isSubclass(descriptor, builtIns.getCollection())) return;
int access = descriptor.getKind() == ClassKind.TRAIT ?
ACC_PUBLIC | ACC_ABSTRACT :
ACC_PUBLIC;
if (JvmCodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("toArray"), builtIns.getArray()) == null) {
MethodVisitor mv = v.getVisitor().visitMethod(access, "toArray", "()[Ljava/lang/Object;", null, null);
if (descriptor.getKind() != ClassKind.TRAIT) {
InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitCode();
iv.load(0, classAsmType);
iv.invokestatic("kotlin/jvm/internal/CollectionToArray", "toArray", "(Ljava/util/Collection;)[Ljava/lang/Object;");
iv.areturn(Type.getObjectType("[Ljava/lang/Object;"));
FunctionCodegen.endVisit(mv, "toArray", myClass);
}
}
if (!isGenericToArrayPresent()) {
MethodVisitor mv = v.getVisitor().visitMethod(access, "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;", null, null);
if (descriptor.getKind() != ClassKind.TRAIT) {
InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitCode();
iv.load(0, classAsmType);
iv.load(1, Type.getObjectType("[Ljava/lang/Object;"));
iv.invokestatic("kotlin/jvm/internal/CollectionToArray", "toArray", "(Ljava/util/Collection;[Ljava/lang/Object;)[Ljava/lang/Object;");
iv.areturn(Type.getObjectType("[Ljava/lang/Object;"));
FunctionCodegen.endVisit(mv, "toArray", myClass);
}
}
}
private void generateMethodStub(
@NotNull String name,
@NotNull String desc,
@NotNull ClassifierDescriptor returnedClassifier,
@NotNull ClassifierDescriptor... valueParameterClassifiers
) {
if (JvmCodegenUtil.getDeclaredFunctionByRawSignature(
descriptor, Name.identifier(name), returnedClassifier, valueParameterClassifiers) == null) {
int access = descriptor.getKind() == ClassKind.TRAIT ?
ACC_PUBLIC | ACC_ABSTRACT :
ACC_PUBLIC;
MethodVisitor mv = v.getVisitor().visitMethod(access, name, desc, null, null);
if (descriptor.getKind() != ClassKind.TRAIT) {
mv.visitCode();
genThrow(mv, "java/lang/UnsupportedOperationException", "Mutating immutable collection");
FunctionCodegen.endVisit(mv, "built-in stub for " + name + desc, null);
}
}
}
private void generateBuiltinMethodStubs() {
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
if (isSubclass(descriptor, builtIns.getCollection())) {
ClassifierDescriptor classifier = getSubstituteForTypeParameterOf(builtIns.getCollection(), 0);
generateMethodStub("add", "(Ljava/lang/Object;)Z", builtIns.getBoolean(), classifier);
generateMethodStub("remove", "(Ljava/lang/Object;)Z", builtIns.getBoolean(), builtIns.getAny());
generateMethodStub("addAll", "(Ljava/util/Collection;)Z", builtIns.getBoolean(), builtIns.getCollection());
generateMethodStub("removeAll", "(Ljava/util/Collection;)Z", builtIns.getBoolean(), builtIns.getCollection());
generateMethodStub("retainAll", "(Ljava/util/Collection;)Z", builtIns.getBoolean(), builtIns.getCollection());
generateMethodStub("clear", "()V", builtIns.getUnit());
}
if (isSubclass(descriptor, builtIns.getList())) {
ClassifierDescriptor classifier = getSubstituteForTypeParameterOf(builtIns.getList(), 0);
generateMethodStub("set", "(ILjava/lang/Object;)Ljava/lang/Object;", classifier, builtIns.getInt(), classifier);
generateMethodStub("add", "(ILjava/lang/Object;)V", builtIns.getUnit(), builtIns.getInt(), classifier);
generateMethodStub("remove", "(I)Ljava/lang/Object;", classifier, builtIns.getInt());
}
if (isSubclass(descriptor, builtIns.getMap())) {
ClassifierDescriptor keyClassifier = getSubstituteForTypeParameterOf(builtIns.getMap(), 0);
ClassifierDescriptor valueClassifier = getSubstituteForTypeParameterOf(builtIns.getMap(), 1);
generateMethodStub("put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", valueClassifier, keyClassifier,
valueClassifier);
generateMethodStub("remove", "(Ljava/lang/Object;)Ljava/lang/Object;", valueClassifier, builtIns.getAny());
generateMethodStub("putAll", "(Ljava/util/Map;)V", builtIns.getUnit(), builtIns.getMap());
generateMethodStub("clear", "()V", builtIns.getUnit());
}
if (isSubclass(descriptor, builtIns.getMapEntry())) {
ClassifierDescriptor valueClassifier = getSubstituteForTypeParameterOf(builtIns.getMapEntry(), 1);
generateMethodStub("setValue", "(Ljava/lang/Object;)Ljava/lang/Object;", valueClassifier, valueClassifier);
}
if (isSubclass(descriptor, builtIns.getIterator())) {
generateMethodStub("remove", "()V", builtIns.getUnit());
}
}
@NotNull
private ClassifierDescriptor getSubstituteForTypeParameterOf(@NotNull ClassDescriptor trait, int index) {
TypeParameterDescriptor listTypeParameter = trait.getTypeConstructor().getParameters().get(index);
TypeSubstitutor deepSubstitutor = SubstitutionUtils.buildDeepSubstitutor(descriptor.getDefaultType());
TypeProjection substitute = deepSubstitutor.substitute(new TypeProjectionImpl(listTypeParameter.getDefaultType()));
assert substitute != null : "Couldn't substitute: " + descriptor;
ClassifierDescriptor classifier = substitute.getType().getConstructor().getDeclarationDescriptor();
assert classifier != null : "No classifier: " + substitute.getType();
return classifier;
}
private void generateFunctionsForDataClasses() {
if (!KotlinBuiltIns.getInstance().isData(descriptor)) return;
new DataClassMethodGeneratorImpl(myClass, bindingContext).generate();
}
private class DataClassMethodGeneratorImpl extends DataClassMethodGenerator {
DataClassMethodGeneratorImpl(
JetClassOrObject klass,
BindingContext bindingContext
) {
super(klass, bindingContext);
}
@Override
public void generateEqualsMethod(@NotNull List properties) {
MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitCode();
Label eq = new Label();
Label ne = new Label();
iv.load(0, OBJECT_TYPE);
iv.load(1, AsmTypeConstants.OBJECT_TYPE);
iv.ifacmpeq(eq);
iv.load(1, AsmTypeConstants.OBJECT_TYPE);
iv.instanceOf(classAsmType);
iv.ifeq(ne);
iv.load(1, AsmTypeConstants.OBJECT_TYPE);
iv.checkcast(classAsmType);
iv.store(2, AsmTypeConstants.OBJECT_TYPE);
for (PropertyDescriptor propertyDescriptor : properties) {
Type asmType = typeMapper.mapType(propertyDescriptor);
genPropertyOnStack(iv, propertyDescriptor, 0);
genPropertyOnStack(iv, propertyDescriptor, 2);
if (asmType.getSort() == Type.ARRAY) {
Type elementType = correctElementType(asmType);
if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
iv.invokestatic("java/util/Arrays", "equals", "([Ljava/lang/Object;[Ljava/lang/Object;)Z");
}
else {
iv.invokestatic("java/util/Arrays", "equals", "([" + elementType.getDescriptor() + "[" + elementType.getDescriptor() + ")Z");
}
}
else {
StackValue value = genEqualsForExpressionsOnStack(iv, JetTokens.EQEQ, asmType, asmType);
value.put(Type.BOOLEAN_TYPE, iv);
}
iv.ifeq(ne);
}
iv.mark(eq);
iv.iconst(1);
iv.areturn(Type.INT_TYPE);
iv.mark(ne);
iv.iconst(0);
iv.areturn(Type.INT_TYPE);
FunctionCodegen.endVisit(mv, "equals", myClass);
}
@Override
public void generateHashCodeMethod(@NotNull List properties) {
MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null);
InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitCode();
boolean first = true;
for (PropertyDescriptor propertyDescriptor : properties) {
if (!first) {
iv.iconst(31);
iv.mul(Type.INT_TYPE);
}
genPropertyOnStack(iv, propertyDescriptor, 0);
Label ifNull = null;
Type asmType = typeMapper.mapType(propertyDescriptor);
if (!isPrimitive(asmType)) {
ifNull = new Label();
iv.dup();
iv.ifnull(ifNull);
}
genHashCode(mv, iv, asmType);
if (ifNull != null) {
Label end = new Label();
iv.goTo(end);
iv.mark(ifNull);
iv.pop();
iv.iconst(0);
iv.mark(end);
}
if (first) {
first = false;
}
else {
iv.add(Type.INT_TYPE);
}
}
mv.visitInsn(IRETURN);
FunctionCodegen.endVisit(mv, "hashCode", myClass);
}
@Override
public void generateToStringMethod(@NotNull List properties) {
MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitCode();
genStringBuilderConstructor(iv);
boolean first = true;
for (PropertyDescriptor propertyDescriptor : properties) {
if (first) {
iv.aconst(descriptor.getName() + "(" + propertyDescriptor.getName().asString()+"=");
first = false;
}
else {
iv.aconst(", " + propertyDescriptor.getName().asString()+"=");
}
genInvokeAppendMethod(iv, JAVA_STRING_TYPE);
Type type = genPropertyOnStack(iv, propertyDescriptor, 0);
if (type.getSort() == Type.ARRAY) {
Type elementType = correctElementType(type);
if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
iv.invokestatic("java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;");
type = JAVA_STRING_TYPE;
}
else {
if (elementType.getSort() != Type.CHAR) {
iv.invokestatic("java/util/Arrays", "toString", "(" + type.getDescriptor() + ")Ljava/lang/String;");
type = JAVA_STRING_TYPE;
}
}
}
genInvokeAppendMethod(iv, type);
}
iv.aconst(")");
genInvokeAppendMethod(iv, JAVA_STRING_TYPE);
iv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
iv.areturn(JAVA_STRING_TYPE);
FunctionCodegen.endVisit(mv, "toString", myClass);
}
private Type genPropertyOnStack(InstructionAdapter iv, PropertyDescriptor propertyDescriptor, int index) {
iv.load(index, classAsmType);
//noinspection ConstantConditions
Method method = typeMapper.mapSignature(propertyDescriptor.getGetter()).getAsmMethod();
iv.invokevirtual(classAsmType.getInternalName(), method.getName(), method.getDescriptor());
return method.getReturnType();
}
@Override
public void generateComponentFunction(@NotNull FunctionDescriptor function, @NotNull final ValueParameterDescriptor parameter) {
functionCodegen.generateMethod(myClass, typeMapper.mapSignature(function), function, new FunctionGenerationStrategy() {
@Override
public void generateBody(
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature signature,
@NotNull MethodContext context,
@Nullable MemberCodegen> parentCodegen
) {
Type componentType = signature.getReturnType();
InstructionAdapter iv = new InstructionAdapter(mv);
if (!componentType.equals(Type.VOID_TYPE)) {
iv.load(0, classAsmType);
String desc = "()" + componentType.getDescriptor();
iv.invokevirtual(classAsmType.getInternalName(), PropertyCodegen.getterName(parameter.getName()), desc);
}
iv.areturn(componentType);
}
});
}
@Override
public void generateCopyFunction(@NotNull final FunctionDescriptor function, @NotNull List constructorParameters) {
JvmMethodSignature methodSignature = typeMapper.mapSignature(function);
final Type thisDescriptorType = typeMapper.mapType(descriptor);
functionCodegen.generateMethod(myClass, methodSignature, function, new FunctionGenerationStrategy() {
@Override
public void generateBody(
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature signature,
@NotNull MethodContext context,
@Nullable MemberCodegen> parentCodegen
) {
InstructionAdapter iv = new InstructionAdapter(mv);
iv.anew(thisDescriptorType);
iv.dup();
ConstructorDescriptor constructor = DescriptorUtils.getConstructorOfDataClass(descriptor);
assert function.getValueParameters().size() == constructor.getValueParameters().size() :
"Number of parameters of copy function and constructor are different. " +
"Copy: " + function.getValueParameters().size() + ", " +
"constructor: " + constructor.getValueParameters().size();
MutableClosure closure = ImplementationBodyCodegen.this.context.closure;
if (closure != null && closure.getCaptureThis() != null) {
Type type = typeMapper.mapType(enclosingClassDescriptor(bindingContext, descriptor));
iv.load(0, classAsmType);
iv.getfield(classAsmType.getInternalName(), CAPTURED_THIS_FIELD, type.getDescriptor());
}
int parameterIndex = 1; // localVariable 0 = this
for (ValueParameterDescriptor parameterDescriptor : function.getValueParameters()) {
Type type = typeMapper.mapType(parameterDescriptor.getType());
iv.load(parameterIndex, type);
parameterIndex += type.getSize();
}
String constructorJvmDescriptor = typeMapper.mapToCallableMethod(constructor).getAsmMethod().getDescriptor();
iv.invokespecial(thisDescriptorType.getInternalName(), "", constructorJvmDescriptor);
iv.areturn(thisDescriptorType);
}
});
functionCodegen.generateDefaultIfNeeded(
context.intoFunction(function), methodSignature, function, OwnerKind.IMPLEMENTATION,
new DefaultParameterValueLoader() {
@Override
public void putValueOnStack(ValueParameterDescriptor valueParameter, ExpressionCodegen codegen) {
assert KotlinBuiltIns.getInstance().isData((ClassDescriptor) function.getContainingDeclaration())
: "Function container should be annotated with [data]: " + function;
PropertyDescriptor property = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameter);
assert property != null : "Copy function doesn't correspond to any property: " + function;
codegen.v.load(0, thisDescriptorType);
Type propertyType = typeMapper.mapType(property);
codegen.intermediateValueForProperty(property, false, null).put(propertyType, codegen.v);
}
},
null
);
}
}
private void generateEnumMethodsAndConstInitializers() {
if (isEnumClass(descriptor)) {
generateEnumValuesMethod();
generateEnumValueOfMethod();
initializeEnumConstants();
}
}
private void generateEnumValuesMethod() {
Type type = typeMapper.mapType(KotlinBuiltIns.getInstance().getArrayType(descriptor.getDefaultType()));
MethodVisitor mv = v.newMethod(myClass, ACC_PUBLIC | ACC_STATIC, "values", "()" + type.getDescriptor(), null, null);
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, classAsmType.getInternalName(), VALUES, type.getDescriptor());
mv.visitMethodInsn(INVOKEVIRTUAL, type.getInternalName(), "clone", "()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, type.getInternalName());
mv.visitInsn(ARETURN);
FunctionCodegen.endVisit(mv, "values()", myClass);
}
private void generateEnumValueOfMethod() {
MethodVisitor mv =
v.newMethod(myClass, ACC_PUBLIC | ACC_STATIC, "valueOf", "(Ljava/lang/String;)" + classAsmType.getDescriptor(), null, null);
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
mv.visitCode();
mv.visitLdcInsn(classAsmType);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false);
mv.visitTypeInsn(CHECKCAST, classAsmType.getInternalName());
mv.visitInsn(ARETURN);
FunctionCodegen.endVisit(mv, "valueOf()", myClass);
}
protected void generateSyntheticAccessors() {
Map accessors = context.getAccessors();
for (Map.Entry entry : accessors.entrySet()) {
generateSyntheticAccessor(entry);
}
}
private void generateSyntheticAccessor(Map.Entry entry) {
if (entry.getValue() instanceof FunctionDescriptor) {
FunctionDescriptor bridge = (FunctionDescriptor) entry.getValue();
final FunctionDescriptor original = (FunctionDescriptor) entry.getKey();
functionCodegen.generateMethod(
null, typeMapper.mapSignature(bridge), bridge,
new FunctionGenerationStrategy.CodegenBased(state, bridge) {
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
generateMethodCallTo(original, codegen.v);
codegen.v.areturn(signature.getReturnType());
}
}
);
}
else if (entry.getValue() instanceof PropertyDescriptor) {
final PropertyDescriptor bridge = (PropertyDescriptor) entry.getValue();
final PropertyDescriptor original = (PropertyDescriptor) entry.getKey();
PropertyGetterDescriptor getter = bridge.getGetter();
assert getter != null;
functionCodegen.generateMethod(null, typeMapper.mapSignature(getter), getter,
new FunctionGenerationStrategy.CodegenBased(state, getter) {
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
InstructionAdapter iv = codegen.v;
boolean forceField = AsmUtil.isPropertyWithBackingFieldInOuterClass(original) && !isClassObject(bridge.getContainingDeclaration());
StackValue property = codegen.intermediateValueForProperty(original, forceField, null, MethodKind.SYNTHETIC_ACCESSOR);
Type[] argTypes = signature.getAsmMethod().getArgumentTypes();
for (int i = 0, reg = 0; i < argTypes.length; i++) {
Type argType = argTypes[i];
iv.load(reg, argType);
//noinspection AssignmentToForLoopParameter
reg += argType.getSize();
}
property.put(property.type, iv);
iv.areturn(signature.getReturnType());
}
});
if (bridge.isVar()) {
PropertySetterDescriptor setter = bridge.getSetter();
assert setter != null;
functionCodegen.generateMethod(null, typeMapper.mapSignature(setter), setter,
new FunctionGenerationStrategy.CodegenBased(state, setter) {
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
boolean forceField = AsmUtil.isPropertyWithBackingFieldInOuterClass(original) && !isClassObject(bridge.getContainingDeclaration());
StackValue property = codegen.intermediateValueForProperty(original, forceField, null, MethodKind.SYNTHETIC_ACCESSOR);
InstructionAdapter iv = codegen.v;
Type[] argTypes = signature.getAsmMethod().getArgumentTypes();
for (int i = 0, reg = 0; i < argTypes.length; i++) {
Type argType = argTypes[i];
iv.load(reg, argType);
//noinspection AssignmentToForLoopParameter
reg += argType.getSize();
}
property.store(property.type, iv);
iv.areturn(signature.getReturnType());
}
});
}
}
else {
throw new UnsupportedOperationException();
}
}
private void generateMethodCallTo(FunctionDescriptor functionDescriptor, InstructionAdapter iv) {
boolean isConstructor = functionDescriptor instanceof ConstructorDescriptor;
boolean callFromAccessor = !JetTypeMapper.isAccessor(functionDescriptor);
CallableMethod callableMethod = isConstructor ?
typeMapper.mapToCallableMethod((ConstructorDescriptor) functionDescriptor) :
typeMapper.mapToCallableMethod(functionDescriptor, callFromAccessor, context);
int reg = 1;
if (isConstructor) {
iv.anew(callableMethod.getOwner());
iv.dup();
reg = 0;
}
else if (callFromAccessor) {
iv.load(0, OBJECT_TYPE);
}
for (Type argType : callableMethod.getAsmMethod().getArgumentTypes()) {
iv.load(reg, argType);
reg += argType.getSize();
}
callableMethod.invokeWithoutAssertions(iv);
}
private void generateFieldForSingleton() {
if (isEnumClass(descriptor) || isEnumEntry(descriptor)) return;
ClassDescriptor classObjectDescriptor = descriptor.getClassObjectDescriptor();
ClassDescriptor fieldTypeDescriptor;
JetClassOrObject original;
if (isObject(descriptor)) {
original = myClass;
fieldTypeDescriptor = descriptor;
}
else if (classObjectDescriptor != null) {
JetClassObject classObject = ((JetClass) myClass).getClassObject();
assert classObject != null : "Class object not found: " + myClass.getText();
original = classObject.getObjectDeclaration();
fieldTypeDescriptor = classObjectDescriptor;
}
else {
return;
}
StackValue.Field field = StackValue.singleton(fieldTypeDescriptor, typeMapper);
v.newField(original, ACC_PUBLIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null);
if (!AsmUtil.isClassObjectWithBackingFieldsInOuter(fieldTypeDescriptor)) {
genInitSingleton(fieldTypeDescriptor, field);
}
}
private void generateClassObjectBackingFieldCopies() {
if (classObjectPropertiesToCopy == null) return;
for (PropertyAndDefaultValue info : classObjectPropertiesToCopy) {
PropertyDescriptor property = info.descriptor;
FieldVisitor fv = v.newField(null, ACC_STATIC | ACC_FINAL | ACC_PUBLIC, context.getFieldName(property),
typeMapper.mapType(property).getDescriptor(), null, info.defaultValue);
AnnotationCodegen.forField(fv, typeMapper).genAnnotations(property);
//This field are always static and final so if it has constant initializer don't do anything in clinit,
//field would be initialized via default value in v.newField(...) - see JVM SPEC Ch.4
// TODO: test this code
if (state.getClassBuilderMode() == ClassBuilderMode.FULL && info.defaultValue == null) {
ExpressionCodegen codegen = createOrGetClInitCodegen();
int classObjectIndex = putClassObjectInLocalVar(codegen);
StackValue.local(classObjectIndex, OBJECT_TYPE).put(OBJECT_TYPE, codegen.v);
copyFieldFromClassObject(property);
}
}
}
private int putClassObjectInLocalVar(ExpressionCodegen codegen) {
FrameMap frameMap = codegen.myFrameMap;
ClassDescriptor classObjectDescriptor = descriptor.getClassObjectDescriptor();
int classObjectIndex = frameMap.getIndex(classObjectDescriptor);
if (classObjectIndex == -1) {
classObjectIndex = frameMap.enter(classObjectDescriptor, OBJECT_TYPE);
StackValue classObject = StackValue.singleton(classObjectDescriptor, typeMapper);
classObject.put(classObject.type, codegen.v);
StackValue.local(classObjectIndex, classObject.type).store(classObject.type, codegen.v);
}
return classObjectIndex;
}
private void copyFieldFromClassObject(PropertyDescriptor propertyDescriptor) {
ExpressionCodegen codegen = createOrGetClInitCodegen();
StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, false, null);
property.put(property.type, codegen.v);
StackValue.Field field = StackValue.field(property.type, classAsmType, propertyDescriptor.getName().asString(), true);
field.store(field.type, codegen.v);
}
protected void genInitSingleton(ClassDescriptor fieldTypeDescriptor, StackValue.Field field) {
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
ConstructorDescriptor constructorDescriptor = DescriptorUtils.getConstructorOfSingletonObject(fieldTypeDescriptor);
ExpressionCodegen codegen = createOrGetClInitCodegen();
FunctionDescriptor fd = codegen.accessibleFunctionDescriptor(constructorDescriptor);
generateMethodCallTo(fd, codegen.v);
field.store(field.type, codegen.v);
}
}
protected void generatePrimaryConstructor() {
if (ignoreIfTraitOrAnnotation()) return;
if (kind != OwnerKind.IMPLEMENTATION) {
throw new IllegalStateException("incorrect kind for primary constructor: " + kind);
}
final MutableClosure closure = context.closure;
ConstructorDescriptor constructorDescriptor = bindingContext.get(BindingContext.CONSTRUCTOR, myClass);
ConstructorContext constructorContext = context.intoConstructor(constructorDescriptor, closure);
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
lookupConstructorExpressionsInClosureIfPresent(constructorContext);
}
assert constructorDescriptor != null : "Constructor not found for class: " + descriptor;
final JvmMethodSignature constructorSignature = typeMapper.mapSignature(constructorDescriptor);
functionCodegen.generateMethod(myClass, constructorSignature, constructorDescriptor, constructorContext,
new FunctionGenerationStrategy.CodegenBased(state, constructorDescriptor) {
@NotNull
@Override
protected FrameMap createFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull MethodContext context) {
return new ConstructorFrameMap(constructorSignature);
}
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
generatePrimaryConstructorImpl(callableDescriptor, codegen, closure);
}
}
);
functionCodegen.generateDefaultIfNeeded(constructorContext, constructorSignature, constructorDescriptor,
OwnerKind.IMPLEMENTATION, DefaultParameterValueLoader.DEFAULT, null);
CallableMethod callableMethod = typeMapper.mapToCallableMethod(constructorDescriptor);
FunctionCodegen.generateConstructorWithoutParametersIfNeeded(state, callableMethod, constructorDescriptor, v);
if (isClassObject(descriptor)) {
context.recordSyntheticAccessorIfNeeded(constructorDescriptor, bindingContext);
}
}
private void generatePrimaryConstructorImpl(
@Nullable ConstructorDescriptor constructorDescriptor,
@NotNull final ExpressionCodegen codegen,
@Nullable MutableClosure closure
) {
List paramDescrs = constructorDescriptor != null
? constructorDescriptor.getValueParameters()
: Collections.emptyList();
InstructionAdapter iv = codegen.v;
if (closure != null) {
List argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, closure, classAsmType);
int k = 1;
for (FieldInfo info : argsFromClosure) {
k = AsmUtil.genAssignInstanceFieldFromParam(info, k, iv);
}
}
if (superCall == null) {
genSimpleSuperCall(iv);
}
else if (superCall instanceof JetDelegatorToSuperClass) {
genSuperCallToDelegatorToSuperClass(iv);
}
else {
generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor);
}
int n = 0;
for (JetDelegationSpecifier specifier : myClass.getDelegationSpecifiers()) {
if (specifier == superCall) {
continue;
}
if (specifier instanceof JetDelegatorByExpressionSpecifier) {
genCallToDelegatorByExpressionSpecifier(iv, codegen, n++, specifier);
}
}
int curParam = 0;
List constructorParameters = getPrimaryConstructorParameters();
for (JetParameter parameter : constructorParameters) {
if (parameter.getValOrVarNode() != null) {
VariableDescriptor descriptor = paramDescrs.get(curParam);
Type type = typeMapper.mapType(descriptor);
iv.load(0, classAsmType);
iv.load(codegen.myFrameMap.getIndex(descriptor), type);
PropertyDescriptor propertyDescriptor = bindingContext.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
assert propertyDescriptor != null : "Property descriptor is not found for primary constructor parameter: " + parameter;
iv.putfield(classAsmType.getInternalName(), context.getFieldName(propertyDescriptor), type.getDescriptor());
}
curParam++;
}
boolean generateInitializerInOuter = isClassObjectWithBackingFieldsInOuter(descriptor);
if (generateInitializerInOuter) {
final ImplementationBodyCodegen parentCodegen = getParentBodyCodegen(this);
//generate object$
parentCodegen.genInitSingleton(descriptor, StackValue.singleton(descriptor, typeMapper));
generateInitializers(new Function0() {
@Override
public ExpressionCodegen invoke() {
return parentCodegen.createOrGetClInitCodegen();
}
});
} else {
generateInitializers(new Function0() {
@Override
public ExpressionCodegen invoke() {
return codegen;
}
});
}
iv.visitInsn(RETURN);
}
private void genSuperCallToDelegatorToSuperClass(InstructionAdapter iv) {
iv.load(0, superClassAsmType);
JetType superType = bindingContext.get(BindingContext.TYPE, superCall.getTypeReference());
List parameterTypes = new ArrayList();
assert superType != null;
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
if (CodegenBinding.hasThis0(bindingContext, superClassDescriptor)) {
iv.load(1, OBJECT_TYPE);
parameterTypes.add(typeMapper.mapType(
enclosingClassDescriptor(bindingContext, descriptor)));
}
Method superCallMethod = new Method("", Type.VOID_TYPE, parameterTypes.toArray(new Type[parameterTypes.size()]));
//noinspection ConstantConditions
iv.invokespecial(typeMapper.mapType(superClassDescriptor).getInternalName(), "",
superCallMethod.getDescriptor());
}
private void genSimpleSuperCall(InstructionAdapter iv) {
iv.load(0, superClassAsmType);
if (descriptor.getKind() == ClassKind.ENUM_CLASS || descriptor.getKind() == ClassKind.ENUM_ENTRY) {
iv.load(1, JAVA_STRING_TYPE);
iv.load(2, Type.INT_TYPE);
iv.invokespecial(superClassAsmType.getInternalName(), "", "(Ljava/lang/String;I)V");
}
else {
iv.invokespecial(superClassAsmType.getInternalName(), "", "()V");
}
}
private void genCallToDelegatorByExpressionSpecifier(
InstructionAdapter iv,
ExpressionCodegen codegen,
int n,
JetDelegationSpecifier specifier
) {
JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
PropertyDescriptor propertyDescriptor = null;
if (expression instanceof JetSimpleNameExpression) {
ResolvedCall> call = bindingContext.get(BindingContext.RESOLVED_CALL, expression);
if (call != null) {
CallableDescriptor callResultingDescriptor = call.getResultingDescriptor();
if (callResultingDescriptor instanceof ValueParameterDescriptor) {
ValueParameterDescriptor valueParameterDescriptor = (ValueParameterDescriptor) callResultingDescriptor;
// constructor parameter
if (valueParameterDescriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
// constructor of my class
if (valueParameterDescriptor.getContainingDeclaration().getContainingDeclaration() == descriptor) {
propertyDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameterDescriptor);
}
}
}
// todo: when and if frontend will allow properties defined not as constructor parameters to be used in delegation specifier
}
}
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
assert superType != null;
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
assert superClassDescriptor != null;
StackValue field;
if (propertyDescriptor != null &&
!propertyDescriptor.isVar() &&
Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor))) {
// final property with backing field
field = StackValue.field(typeMapper.mapType(propertyDescriptor), classAsmType, propertyDescriptor.getName().asString(), false);
}
else {
iv.load(0, classAsmType);
codegen.genToJVMStack(expression);
String delegateField = "$delegate_" + n;
Type fieldType = typeMapper.mapType(superClassDescriptor);
String fieldDesc = fieldType.getDescriptor();
v.newField(specifier, ACC_PRIVATE | ACC_FINAL | ACC_SYNTHETIC, delegateField, fieldDesc, /*TODO*/null, null);
field = StackValue.field(fieldType, classAsmType, delegateField, false);
field.store(fieldType, iv);
}
generateDelegates(superClassDescriptor, field);
}
private void lookupConstructorExpressionsInClosureIfPresent(final ConstructorContext constructorContext) {
JetVisitorVoid visitor = new JetVisitorVoid() {
@Override
public void visitJetElement(@NotNull JetElement e) {
e.acceptChildren(this);
}
@Override
public void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expr) {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expr);
DeclarationDescriptor toLookup;
if (isLocalNamedFun(descriptor)) {
toLookup = descriptor;
}
else if (descriptor instanceof CallableMemberDescriptor) {
toLookup = descriptor.getContainingDeclaration();
}
else if (descriptor instanceof VariableDescriptor) {
ConstructorDescriptor constructorDescriptor = (ConstructorDescriptor) constructorContext.getContextDescriptor();
for (ValueParameterDescriptor parameterDescriptor : constructorDescriptor.getValueParameters()) {
if (descriptor.equals(parameterDescriptor)) return;
}
toLookup = descriptor;
}
else return;
constructorContext.lookupInContext(toLookup, null, state, true);
}
@Override
public void visitThisExpression(@NotNull JetThisExpression expression) {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expression.getInstanceReference());
assert descriptor instanceof CallableDescriptor ||
descriptor instanceof ClassDescriptor : "'This' reference target should be class or callable descriptor but was " + descriptor;
if (context.getCallableDescriptorWithReceiver() != descriptor) {
context.lookupInContext(descriptor, null, state, true);
}
}
};
for (JetDeclaration declaration : myClass.getDeclarations()) {
if (declaration instanceof JetProperty) {
JetProperty property = (JetProperty) declaration;
JetExpression initializer = property.getInitializer();
if (initializer != null) {
initializer.accept(visitor);
}
}
else if (declaration instanceof JetClassInitializer) {
JetClassInitializer initializer = (JetClassInitializer) declaration;
initializer.accept(visitor);
}
}
for (JetDelegationSpecifier specifier : myClass.getDelegationSpecifiers()) {
if (specifier != superCall) {
if (specifier instanceof JetDelegatorByExpressionSpecifier) {
JetExpression delegateExpression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
assert delegateExpression != null;
delegateExpression.accept(visitor);
}
}
else {
if (superCall instanceof JetDelegatorToSuperCall) {
JetValueArgumentList argumentList = ((JetDelegatorToSuperCall) superCall).getValueArgumentList();
if (argumentList != null) {
argumentList.accept(visitor);
}
}
}
}
}
private boolean ignoreIfTraitOrAnnotation() {
if (myClass instanceof JetClass) {
JetClass aClass = (JetClass) myClass;
if (aClass.isTrait()) {
return true;
}
if (aClass.isAnnotation()) {
return true;
}
}
return false;
}
private void generateTraitMethods() {
if (JetPsiUtil.isTrait(myClass)) return;
for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (!(declaration instanceof CallableMemberDescriptor)) continue;
CallableMemberDescriptor inheritedMember = (CallableMemberDescriptor) declaration;
CallableMemberDescriptor traitMember = BridgesPackage.findTraitImplementation(inheritedMember);
if (traitMember == null) continue;
assert traitMember.getModality() != Modality.ABSTRACT : "Cannot delegate to abstract trait method: " + inheritedMember;
// inheritedMember can be abstract here. In order for FunctionCodegen to generate the method body, we're creating a copy here
// with traitMember's modality
CallableMemberDescriptor copy =
inheritedMember.copy(inheritedMember.getContainingDeclaration(), traitMember.getModality(), Visibilities.PUBLIC,
CallableMemberDescriptor.Kind.DECLARATION, true);
if (traitMember instanceof SimpleFunctionDescriptor) {
generateDelegationToTraitImpl((FunctionDescriptor) traitMember, (FunctionDescriptor) copy);
}
else if (traitMember instanceof PropertyDescriptor) {
for (PropertyAccessorDescriptor traitAccessor : ((PropertyDescriptor) traitMember).getAccessors()) {
for (PropertyAccessorDescriptor inheritedAccessor : ((PropertyDescriptor) copy).getAccessors()) {
if (inheritedAccessor.getClass() == traitAccessor.getClass()) { // same accessor kind
generateDelegationToTraitImpl(traitAccessor, inheritedAccessor);
}
}
}
}
}
}
private void generateDelegationToTraitImpl(@NotNull final FunctionDescriptor traitFun, @NotNull FunctionDescriptor inheritedFun) {
functionCodegen.generateMethod(
descriptorToDeclaration(bindingContext, traitFun),
typeMapper.mapSignature(inheritedFun),
inheritedFun,
new FunctionGenerationStrategy.CodegenBased(state, inheritedFun) {
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
DeclarationDescriptor containingDeclaration = traitFun.getContainingDeclaration();
if (!(containingDeclaration instanceof ClassDescriptor)) return;
ClassDescriptor containingTrait = (ClassDescriptor) containingDeclaration;
if (containingTrait.getKind() != ClassKind.TRAIT) return;
Method traitMethod = typeMapper.mapSignature(traitFun.getOriginal()).getAsmMethod();
Type[] argTypes = signature.getAsmMethod().getArgumentTypes();
Type[] originalArgTypes = traitMethod.getArgumentTypes();
InstructionAdapter iv = codegen.v;
iv.load(0, OBJECT_TYPE);
for (int i = 0, reg = 1; i < argTypes.length; i++) {
StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv);
//noinspection AssignmentToForLoopParameter
reg += argTypes[i].getSize();
}
Type type = getTraitImplThisParameterType(containingTrait, typeMapper);
String desc = traitMethod.getDescriptor().replace("(", "(" + type.getDescriptor());
iv.invokestatic(typeMapper.mapTraitImpl(containingTrait).getInternalName(), traitMethod.getName(), desc);
Type returnType = signature.getReturnType();
StackValue.onStack(traitMethod.getReturnType()).put(returnType, iv);
iv.areturn(returnType);
}
}
);
}
private void generateDelegatorToConstructorCall(
InstructionAdapter iv, ExpressionCodegen codegen,
ConstructorDescriptor constructorDescriptor
) {
ClassDescriptor classDecl = constructorDescriptor.getContainingDeclaration();
iv.load(0, OBJECT_TYPE);
if (classDecl.getKind() == ClassKind.ENUM_CLASS || classDecl.getKind() == ClassKind.ENUM_ENTRY) {
iv.load(1, OBJECT_TYPE);
iv.load(2, Type.INT_TYPE);
}
CallableMethod method = typeMapper.mapToCallableMethod(constructorDescriptor);
ResolvedCall> resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, ((JetCallElement) superCall).getCalleeExpression());
assert resolvedCall != null;
ConstructorDescriptor superConstructor = (ConstructorDescriptor) resolvedCall.getResultingDescriptor();
//noinspection SuspiciousMethodCalls
CalculatedClosure closureForSuper = bindingContext.get(CLOSURE, superConstructor.getContainingDeclaration());
if (closureForSuper != null && closureForSuper.getCaptureThis() != null) {
iv.load(((ConstructorFrameMap) codegen.myFrameMap).getOuterThisIndex(), OBJECT_TYPE);
}
CallableMethod superCallable = typeMapper.mapToCallableMethod(superConstructor);
if (isAnonymousObject(descriptor) && superCall instanceof JetDelegatorToSuperCall) {
int nextVar = findFirstSuperArgument(method);
for (Type t : superCallable.getAsmMethod().getArgumentTypes()) {
iv.load(nextVar, t);
nextVar += t.getSize();
}
superCallable.invokeWithNotNullAssertion(codegen.v, state, resolvedCall);
}
else {
codegen.invokeMethodWithArguments(null, superCallable, resolvedCall, StackValue.none());
}
}
private static int findFirstSuperArgument(@NotNull CallableMethod method) {
int i = 0;
for (JvmMethodParameterSignature type : method.getValueParameters()) {
if (type.getKind() == JvmMethodParameterKind.SUPER_OF_ANONYMOUS_CALL_PARAM) {
return i + 1; // because of this
}
i += type.getAsmType().getSize();
}
return -1;
}
@Override
protected void generateDeclaration(PropertyCodegen propertyCodegen, JetDeclaration declaration) {
if (declaration instanceof JetEnumEntry) {
String name = declaration.getName();
assert name != null : "Enum entry has no name: " + declaration.getText();
String desc = "L" + classAsmType.getInternalName() + ";";
v.newField(declaration, ACC_PUBLIC | ACC_ENUM | ACC_STATIC | ACC_FINAL, name, desc, null, null);
myEnumConstants.add((JetEnumEntry) declaration);
}
super.generateDeclaration(propertyCodegen, declaration);
}
private final List myEnumConstants = new ArrayList();
private void initializeEnumConstants() {
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
ExpressionCodegen codegen = createOrGetClInitCodegen();
InstructionAdapter iv = codegen.v;
Type arrayAsmType = typeMapper.mapType(KotlinBuiltIns.getInstance().getArrayType(descriptor.getDefaultType()));
v.newField(myClass, ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, VALUES, arrayAsmType.getDescriptor(), null, null);
iv.iconst(myEnumConstants.size());
iv.newarray(classAsmType);
if (!myEnumConstants.isEmpty()) {
iv.dup();
for (int ordinal = 0, size = myEnumConstants.size(); ordinal < size; ordinal++) {
initializeEnumConstant(codegen, ordinal);
}
}
iv.putstatic(classAsmType.getInternalName(), VALUES, arrayAsmType.getDescriptor());
}
private void initializeEnumConstant(@NotNull ExpressionCodegen codegen, int ordinal) {
InstructionAdapter iv = codegen.v;
JetEnumEntry enumConstant = myEnumConstants.get(ordinal);
iv.dup();
iv.iconst(ordinal);
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, enumConstant);
assert classDescriptor != null;
Type implClass = typeMapper.mapClass(classDescriptor);
List delegationSpecifiers = enumConstant.getDelegationSpecifiers();
if (delegationSpecifiers.size() > 1) {
throw new UnsupportedOperationException("multiple delegation specifiers for enum constant not supported");
}
iv.anew(implClass);
iv.dup();
iv.aconst(enumConstant.getName());
iv.iconst(ordinal);
if (delegationSpecifiers.size() == 1 && !enumEntryNeedSubclass(state.getBindingContext(), enumConstant)) {
JetDelegationSpecifier specifier = delegationSpecifiers.get(0);
if (!(specifier instanceof JetDelegatorToSuperCall)) {
throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier);
}
ResolvedCall> resolvedCall =
bindingContext.get(BindingContext.RESOLVED_CALL, ((JetDelegatorToSuperCall) specifier).getCalleeExpression());
assert resolvedCall != null : "Enum entry delegation specifier is unresolved: " + specifier.getText();
CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor());
codegen.invokeMethodWithArguments(null, method, resolvedCall, StackValue.none());
}
else {
iv.invokespecial(implClass.getInternalName(), "", "(Ljava/lang/String;I)V");
}
iv.dup();
iv.putstatic(classAsmType.getInternalName(), enumConstant.getName(), classAsmType.getDescriptor());
iv.astore(OBJECT_TYPE);
}
protected void generateDelegates(ClassDescriptor toClass, StackValue field) {
for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (declaration instanceof CallableMemberDescriptor) {
CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declaration;
if (callableMemberDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION) {
Set extends CallableMemberDescriptor> overriddenDescriptors = callableMemberDescriptor.getOverriddenDescriptors();
for (CallableMemberDescriptor overriddenDescriptor : overriddenDescriptors) {
if (overriddenDescriptor.getContainingDeclaration() == toClass) {
if (declaration instanceof PropertyDescriptor) {
propertyCodegen
.genDelegate((PropertyDescriptor) declaration, (PropertyDescriptor) overriddenDescriptor, field);
}
else if (declaration instanceof FunctionDescriptor) {
functionCodegen
.genDelegate((FunctionDescriptor) declaration, (FunctionDescriptor) overriddenDescriptor, field);
}
}
}
}
}
}
}
public void addClassObjectPropertyToCopy(PropertyDescriptor descriptor, Object defaultValue) {
if (classObjectPropertiesToCopy == null) {
classObjectPropertiesToCopy = new ArrayList();
}
classObjectPropertiesToCopy.add(new PropertyAndDefaultValue(descriptor, defaultValue));
}
private static class PropertyAndDefaultValue {
public final PropertyDescriptor descriptor;
public final Object defaultValue;
public PropertyAndDefaultValue(PropertyDescriptor descriptor, Object defaultValue) {
this.descriptor = descriptor;
this.defaultValue = defaultValue;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy