extract code for generating parameterless constructor into a separate class and rewrite it in Kotlin

This commit is contained in:
Dmitry Jemerov
2015-03-27 17:08:54 +01:00
parent d78573059e
commit c1c9d3b090
3 changed files with 101 additions and 86 deletions
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2015 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.codegen
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
public class DefaultParameterValueSubstitutor(val state: GenerationState) {
fun generateDefaultConstructorIfNeeded(method: CallableMethod,
constructorDescriptor: ConstructorDescriptor,
classBuilder: ClassBuilder,
classOrObject: JetClassOrObject) {
if (!isEmptyConstructorNeeded(constructorDescriptor, classOrObject)) {
return
}
val flags = AsmUtil.getVisibilityAccessFlag(constructorDescriptor)
val mv = classBuilder.newMethod(OtherOrigin(constructorDescriptor), flags, "<init>", "()V", null,
FunctionCodegen.getThrownExceptions(constructorDescriptor, state.getTypeMapper()))
if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return
val v = InstructionAdapter(mv)
mv.visitCode()
val methodOwner = method.getOwner()
v.load(0, methodOwner) // Load this on stack
var mask = 0
val masks = arrayListOf<Int>()
for (parameterDescriptor in constructorDescriptor.getValueParameters()) {
val paramType = state.getTypeMapper().mapType(parameterDescriptor.getType())
AsmUtil.pushDefaultValueOnStack(paramType, v)
val i = parameterDescriptor.getIndex()
if (i != 0 && i % Integer.SIZE == 0) {
masks.add(mask)
mask = 0
}
mask = mask or (1 shl (i % Integer.SIZE))
}
masks.add(mask)
for (m in masks) {
v.iconst(m)
}
// constructors with default arguments has last synthetic argument of specific type
v.aconst(null)
val desc = JetTypeMapper.getDefaultDescriptor(method.getAsmMethod(), false)
v.invokespecial(methodOwner.getInternalName(), "<init>", desc, false)
v.areturn(Type.VOID_TYPE)
FunctionCodegen.endVisit(mv, "default constructor for " + methodOwner.getInternalName(), classOrObject)
}
private fun isEmptyConstructorNeeded(constructorDescriptor: ConstructorDescriptor, classOrObject: JetClassOrObject): Boolean {
val classDescriptor = constructorDescriptor.getContainingDeclaration()
if (classOrObject.isLocal()) return false
if (CodegenBinding.canHaveOuter(state.getBindingContext(), classDescriptor)) return false
if (Visibilities.isPrivate(classDescriptor.getVisibility()) || Visibilities.isPrivate(constructorDescriptor.getVisibility()))
return false
if (constructorDescriptor.getValueParameters().isEmpty()) return false
if (classOrObject is JetClass && hasSecondaryConstructorsWithNoParameters(classOrObject)) return false
return constructorDescriptor.getValueParameters().all { it.declaresDefaultValue() }
}
private fun hasSecondaryConstructorsWithNoParameters(klass: JetClass) =
klass.getSecondaryConstructors().any { it.getValueParameters().isEmpty() }
}
@@ -27,7 +27,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.bridges.Bridge;
import org.jetbrains.kotlin.backend.common.bridges.BridgesPackage;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.context.MethodContext;
import org.jetbrains.kotlin.codegen.context.PackageContext;
@@ -39,10 +38,7 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeDeclarationsPackage;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.JetClass;
import org.jetbrains.kotlin.psi.JetClassOrObject;
import org.jetbrains.kotlin.psi.JetNamedFunction;
import org.jetbrains.kotlin.psi.JetSecondaryConstructor;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
@@ -68,7 +64,10 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmSerializationBindings.*;
@@ -548,54 +547,6 @@ public class FunctionCodegen {
return ArrayUtil.toStringArray(strings);
}
static void generateConstructorWithoutParametersIfNeeded(
@NotNull GenerationState state,
@NotNull CallableMethod method,
@NotNull ConstructorDescriptor constructorDescriptor,
@NotNull ClassBuilder classBuilder,
@NotNull JetClassOrObject classOrObject
) {
if (!isEmptyConstructorNeeded(state.getBindingContext(), constructorDescriptor, classOrObject)) {
return;
}
int flags = getVisibilityAccessFlag(constructorDescriptor);
MethodVisitor mv = classBuilder.newMethod(OtherOrigin(constructorDescriptor), flags, "<init>", "()V", null,
getThrownExceptions(constructorDescriptor, state.getTypeMapper()));
if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return;
InstructionAdapter v = new InstructionAdapter(mv);
mv.visitCode();
Type methodOwner = method.getOwner();
v.load(0, methodOwner); // Load this on stack
int mask = 0;
List<Integer> masks = new ArrayList<Integer>(1);
for (ValueParameterDescriptor parameterDescriptor : constructorDescriptor.getValueParameters()) {
Type paramType = state.getTypeMapper().mapType(parameterDescriptor.getType());
pushDefaultValueOnStack(paramType, v);
int i = parameterDescriptor.getIndex();
if (i != 0 && i % Integer.SIZE == 0) {
masks.add(mask);
mask = 0;
}
mask |= (1 << (i % Integer.SIZE));
}
masks.add(mask);
for (int m : masks) {
v.iconst(m);
}
// constructors with default arguments has last synthetic argument of specific type
v.aconst(null);
String desc = JetTypeMapper.getDefaultDescriptor(method.getAsmMethod(), false);
v.invokespecial(methodOwner.getInternalName(), "<init>", desc, false);
v.areturn(Type.VOID_TYPE);
endVisit(mv, "default constructor for " + methodOwner.getInternalName(), classOrObject);
}
void generateDefaultIfNeeded(
@NotNull MethodContext owner,
@NotNull FunctionDescriptor functionDescriptor,
@@ -775,38 +726,6 @@ public class FunctionCodegen {
return needed;
}
private static boolean isEmptyConstructorNeeded(
@NotNull BindingContext context,
@NotNull ConstructorDescriptor constructorDescriptor,
@NotNull JetClassOrObject classOrObject
) {
ClassDescriptor classDescriptor = constructorDescriptor.getContainingDeclaration();
if (classOrObject.isLocal()) return false;
if (CodegenBinding.canHaveOuter(context, classDescriptor)) return false;
if (Visibilities.isPrivate(classDescriptor.getVisibility()) ||
Visibilities.isPrivate(constructorDescriptor.getVisibility())) return false;
if (constructorDescriptor.getValueParameters().isEmpty()) return false;
if (classOrObject instanceof JetClass && hasSecondaryConstructorsWithNoParameters((JetClass) classOrObject)) return false;
for (ValueParameterDescriptor parameterDescriptor : constructorDescriptor.getValueParameters()) {
if (!parameterDescriptor.declaresDefaultValue()) {
return false;
}
}
return true;
}
private static boolean hasSecondaryConstructorsWithNoParameters(@NotNull JetClass klass) {
for (JetSecondaryConstructor constructor : klass.getSecondaryConstructors()) {
if (constructor.getValueParameters().isEmpty()) return true;
}
return false;
}
private void generateBridge(
@Nullable PsiElement origin,
@NotNull FunctionDescriptor descriptor,
@@ -1077,7 +1077,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
DefaultParameterValueLoader.DEFAULT, null);
CallableMethod callableMethod = typeMapper.mapToCallableMethod(constructorDescriptor);
FunctionCodegen.generateConstructorWithoutParametersIfNeeded(state, callableMethod, constructorDescriptor, v, myClass);
new DefaultParameterValueSubstitutor(state).generateDefaultConstructorIfNeeded(callableMethod, constructorDescriptor, v, myClass);
if (isCompanionObject(descriptor)) {
context.recordSyntheticAccessorIfNeeded(constructorDescriptor, bindingContext);