Refactor property initialization codegen

Move a lot of the code from ImplementationBodyCodegen up to MemberCodegen, make
methods non-static (consequently get rid of a lot of parameters), reuse the
same code in PackagePartCodegen
This commit is contained in:
Alexander Udalov
2014-04-21 20:11:41 +04:00
parent 6803fec053
commit 678520364a
5 changed files with 170 additions and 169 deletions
@@ -19,6 +19,7 @@ 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.codegen.binding.CalculatedClosure;
@@ -43,7 +44,6 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.OverrideResolver;
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
@@ -1214,7 +1214,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private void generatePrimaryConstructorImpl(
@Nullable ConstructorDescriptor constructorDescriptor,
@NotNull ExpressionCodegen codegen,
@NotNull final ExpressionCodegen codegen,
@Nullable MutableClosure closure
) {
List<ValueParameterDescriptor> paramDescrs = constructorDescriptor != null
@@ -1269,12 +1269,22 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
boolean generateInitializerInOuter = isClassObjectWithBackingFieldsInOuter(descriptor);
if (generateInitializerInOuter) {
ImplementationBodyCodegen parentCodegen = getParentBodyCodegen(this);
final ImplementationBodyCodegen parentCodegen = getParentBodyCodegen(this);
//generate object$
parentCodegen.genInitSingleton(descriptor, StackValue.singleton(descriptor, typeMapper));
generateInitializers(parentCodegen.createOrGetClInitCodegen(), myClass.getDeclarations(), bindingContext, state);
generateInitializers(myClass.getDeclarations(), new Function0<ExpressionCodegen>() {
@Override
public ExpressionCodegen invoke() {
return parentCodegen.createOrGetClInitCodegen();
}
});
} else {
generateInitializers(codegen, myClass.getDeclarations(), bindingContext, state);
generateInitializers(myClass.getDeclarations(), new Function0<ExpressionCodegen>() {
@Override
public ExpressionCodegen invoke() {
return codegen;
}
});
}
iv.visitInsn(RETURN);
@@ -1658,131 +1668,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
iv.astore(OBJECT_TYPE);
}
public static void generateInitializers(
@NotNull ExpressionCodegen codegen, @NotNull List<JetDeclaration> declarations,
@NotNull BindingContext bindingContext, @NotNull GenerationState state
) {
JetTypeMapper typeMapper = state.getTypeMapper();
for (JetDeclaration declaration : declarations) {
if (declaration instanceof JetProperty) {
if (shouldInitializeProperty((JetProperty) declaration, typeMapper)) {
initializeProperty(codegen, bindingContext, (JetProperty) declaration);
}
}
else if (declaration instanceof JetClassInitializer) {
codegen.gen(((JetClassInitializer) declaration).getBody(), Type.VOID_TYPE);
}
}
}
public static void initializeProperty(
@NotNull ExpressionCodegen codegen,
@NotNull BindingContext bindingContext,
@NotNull JetProperty property
) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(BindingContext.VARIABLE, property);
assert propertyDescriptor != null;
JetExpression initializer = property.getDelegateExpressionOrInitializer();
assert initializer != null : "shouldInitializeProperty must return false if initializer is null";
JetType jetType = getPropertyOrDelegateType(bindingContext, property, propertyDescriptor);
StackValue.StackValueWithSimpleReceiver propValue = codegen.intermediateValueForProperty(propertyDescriptor, true, null, MethodKind.INITIALIZER);
if (!propValue.isStatic) {
codegen.v.load(0, OBJECT_TYPE);
}
Type type = codegen.expressionType(initializer);
if (jetType.isNullable()) {
type = boxType(type);
}
codegen.gen(initializer, type);
propValue.store(type, codegen.v);
}
public static boolean shouldWriteFieldInitializer(PropertyDescriptor descriptor, JetTypeMapper mapper) {
//final field of primitive or String type
if (!descriptor.isVar()) {
Type type = mapper.mapType(descriptor);
return AsmUtil.isPrimitive(type) || "java.lang.String".equals(type.getClassName());
}
return false;
}
public static boolean shouldInitializeProperty(
@NotNull JetProperty property,
@NotNull JetTypeMapper typeMapper
) {
JetExpression initializer = property.getDelegateExpressionOrInitializer();
if (initializer == null) return false;
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) typeMapper.getBindingContext().get(BindingContext.VARIABLE, property);
assert propertyDescriptor != null;
CompileTimeConstant<?> compileTimeValue = propertyDescriptor.getCompileTimeInitializer();
if (compileTimeValue == null) return true;
//TODO: OPTIMIZATION: don't initialize static final fields
Object value = compileTimeValue.getValue();
JetType jetType = getPropertyOrDelegateType(typeMapper.getBindingContext(), property, propertyDescriptor);
Type type = typeMapper.mapType(jetType);
return !skipDefaultValue(propertyDescriptor, value, type);
}
@NotNull
private static JetType getPropertyOrDelegateType(@NotNull BindingContext bindingContext, @NotNull JetProperty property, @NotNull PropertyDescriptor descriptor) {
JetExpression delegateExpression = property.getDelegateExpression();
if (delegateExpression != null) {
JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, delegateExpression);
assert delegateType != null : "Type of delegate expression should be recorded";
return delegateType;
}
return descriptor.getType();
}
private static boolean skipDefaultValue(@NotNull PropertyDescriptor propertyDescriptor, Object value, @NotNull Type type) {
if (isPrimitive(type)) {
if (!propertyDescriptor.getType().isNullable() && value instanceof Number) {
if (type == Type.INT_TYPE && ((Number) value).intValue() == 0) {
return true;
}
if (type == Type.BYTE_TYPE && ((Number) value).byteValue() == 0) {
return true;
}
if (type == Type.LONG_TYPE && ((Number) value).longValue() == 0L) {
return true;
}
if (type == Type.SHORT_TYPE && ((Number) value).shortValue() == 0) {
return true;
}
if (type == Type.DOUBLE_TYPE && ((Number) value).doubleValue() == 0d) {
return true;
}
if (type == Type.FLOAT_TYPE && ((Number) value).floatValue() == 0f) {
return true;
}
}
if (type == Type.BOOLEAN_TYPE && value instanceof Boolean && !((Boolean) value)) {
return true;
}
if (type == Type.CHAR_TYPE && value instanceof Character && ((Character) value) == 0) {
return true;
}
}
else {
if (value == null) {
return true;
}
}
return false;
}
protected void generateDelegates(ClassDescriptor toClass, StackValue field) {
for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (declaration instanceof CallableMemberDescriptor) {
@@ -17,6 +17,7 @@
package org.jetbrains.jet.codegen;
import com.intellij.openapi.progress.ProcessCanceledException;
import kotlin.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.context.ClassContext;
@@ -30,18 +31,26 @@ import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.name.SpecialNames;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.storage.LockBasedStorageManager;
import org.jetbrains.jet.storage.NotNullLazyValue;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.codegen.AsmUtil.boxType;
import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_STATIC;
public class MemberCodegen extends ParentCodegenAwareImpl {
public abstract class MemberCodegen extends ParentCodegenAwareImpl {
protected final FieldOwnerContext context;
protected final ClassBuilder v;
protected ExpressionCodegen clInit;
@@ -168,4 +177,108 @@ public class MemberCodegen extends ParentCodegenAwareImpl {
}
return clInit;
}
protected void generateInitializers(@NotNull List<JetDeclaration> declarations, @NotNull Function0<ExpressionCodegen> createCodegen) {
NotNullLazyValue<ExpressionCodegen> codegen = LockBasedStorageManager.NO_LOCKS.createLazyValue(createCodegen);
for (JetDeclaration declaration : declarations) {
if (declaration instanceof JetProperty) {
if (shouldInitializeProperty((JetProperty) declaration)) {
initializeProperty(codegen.invoke(), (JetProperty) declaration);
}
}
else if (declaration instanceof JetClassInitializer) {
codegen.invoke().gen(((JetClassInitializer) declaration).getBody(), Type.VOID_TYPE);
}
}
}
protected void initializeProperty(@NotNull ExpressionCodegen codegen, @NotNull JetProperty property) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(BindingContext.VARIABLE, property);
assert propertyDescriptor != null;
JetExpression initializer = property.getDelegateExpressionOrInitializer();
assert initializer != null : "shouldInitializeProperty must return false if initializer is null";
JetType jetType = getPropertyOrDelegateType(property, propertyDescriptor);
StackValue.Property propValue = codegen.intermediateValueForProperty(propertyDescriptor, true, null, MethodKind.INITIALIZER);
if (!propValue.isStatic) {
codegen.v.load(0, OBJECT_TYPE);
}
Type type = codegen.expressionType(initializer);
if (jetType.isNullable()) {
type = boxType(type);
}
codegen.gen(initializer, type);
propValue.store(type, codegen.v);
}
protected boolean shouldInitializeProperty(@NotNull JetProperty property) {
JetExpression initializer = property.getDelegateExpressionOrInitializer();
if (initializer == null) return false;
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(BindingContext.VARIABLE, property);
assert propertyDescriptor != null;
CompileTimeConstant<?> compileTimeValue = propertyDescriptor.getCompileTimeInitializer();
if (compileTimeValue == null) return true;
//TODO: OPTIMIZATION: don't initialize static final fields
Object value = compileTimeValue.getValue();
JetType jetType = getPropertyOrDelegateType(property, propertyDescriptor);
Type type = typeMapper.mapType(jetType);
return !skipDefaultValue(propertyDescriptor, value, type);
}
@NotNull
private JetType getPropertyOrDelegateType(@NotNull JetProperty property, @NotNull PropertyDescriptor descriptor) {
JetExpression delegateExpression = property.getDelegateExpression();
if (delegateExpression != null) {
JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, delegateExpression);
assert delegateType != null : "Type of delegate expression should be recorded";
return delegateType;
}
return descriptor.getType();
}
private static boolean skipDefaultValue(@NotNull PropertyDescriptor propertyDescriptor, Object value, @NotNull Type type) {
if (isPrimitive(type)) {
if (!propertyDescriptor.getType().isNullable() && value instanceof Number) {
if (type == Type.INT_TYPE && ((Number) value).intValue() == 0) {
return true;
}
if (type == Type.BYTE_TYPE && ((Number) value).byteValue() == 0) {
return true;
}
if (type == Type.LONG_TYPE && ((Number) value).longValue() == 0L) {
return true;
}
if (type == Type.SHORT_TYPE && ((Number) value).shortValue() == 0) {
return true;
}
if (type == Type.DOUBLE_TYPE && ((Number) value).doubleValue() == 0d) {
return true;
}
if (type == Type.FLOAT_TYPE && ((Number) value).floatValue() == 0f) {
return true;
}
}
if (type == Type.BOOLEAN_TYPE && value instanceof Boolean && !((Boolean) value)) {
return true;
}
if (type == Type.CHAR_TYPE && value instanceof Character && ((Character) value) == 0) {
return true;
}
}
else {
if (value == null) {
return true;
}
}
return false;
}
}
@@ -16,8 +16,8 @@
package org.jetbrains.jet.codegen;
import com.google.common.collect.Lists;
import com.intellij.util.ArrayUtil;
import kotlin.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.context.FieldOwnerContext;
import org.jetbrains.jet.codegen.state.GenerationState;
@@ -58,36 +58,28 @@ public class PackagePartCodegen extends MemberCodegen {
writeKotlinSyntheticClassAnnotation(v, KotlinSyntheticClass.Kind.PACKAGE_PART);
for (JetDeclaration declaration : jetFile.getDeclarations()) {
List<JetDeclaration> declarations = jetFile.getDeclarations();
for (JetDeclaration declaration : declarations) {
if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
genFunctionOrProperty((JetTypeParameterListOwner) declaration, v);
}
}
generateStaticInitializers();
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
generateInitializers(declarations, new Function0<ExpressionCodegen>() {
@Override
public ExpressionCodegen invoke() {
return createOrGetClInitCodegen();
}
});
if (clInit != null) {
clInit.v.visitInsn(RETURN);
FunctionCodegen.endVisit(clInit.v, "static initializer for package", jetFile);
}
}
v.done();
}
private void generateStaticInitializers() {
List<JetProperty> properties = Lists.newArrayList();
for (JetDeclaration declaration : jetFile.getDeclarations()) {
if (declaration instanceof JetProperty) {
JetProperty property = (JetProperty) declaration;
if (ImplementationBodyCodegen.shouldInitializeProperty(property, typeMapper)) {
properties.add(property);
}
}
}
if (properties.isEmpty()) return;
ExpressionCodegen codegen = createOrGetClInitCodegen();
for (JetProperty property : properties) {
ImplementationBodyCodegen.initializeProperty(codegen, bindingContext, property);
}
codegen.v.visitInsn(RETURN);
FunctionCodegen.endVisit(codegen.v, "static initializer for package", jetFile);
}
}
@@ -247,7 +247,7 @@ public class PropertyCodegen extends GenerationStateAware {
private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
Object value = null;
if (ImplementationBodyCodegen.shouldWriteFieldInitializer(propertyDescriptor, typeMapper)) {
if (shouldWriteFieldInitializer(propertyDescriptor)) {
CompileTimeConstant<?> initializer = propertyDescriptor.getCompileTimeInitializer();
if (initializer != null) {
value = initializer.getValue();
@@ -257,6 +257,15 @@ public class PropertyCodegen extends GenerationStateAware {
return generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value);
}
private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) {
//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(@Nullable JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor getter) {
generateAccessor(p, getter, descriptor.getGetter() != null
? descriptor.getGetter()
@@ -16,11 +16,9 @@
package org.jetbrains.jet.codegen;
import kotlin.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.FieldOwnerContext;
import org.jetbrains.jet.codegen.context.MethodContext;
@@ -34,13 +32,16 @@ import org.jetbrains.jet.lang.psi.JetDeclaration;
import org.jetbrains.jet.lang.psi.JetScript;
import org.jetbrains.jet.lang.psi.JetTypeParameterListOwner;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
// SCRIPT: script code generator
public class ScriptCodegen extends MemberCodegen {
@@ -113,7 +114,7 @@ public class ScriptCodegen extends MemberCodegen {
@NotNull ScriptDescriptor scriptDescriptor,
@NotNull ClassDescriptor classDescriptorForScript,
@NotNull ClassBuilder classBuilder,
@NotNull MethodContext context
@NotNull final MethodContext context
) {
Type blockType = typeMapper.mapType(scriptDescriptor.getScriptCodeDescriptor().getReturnType());
@@ -129,7 +130,7 @@ public class ScriptCodegen extends MemberCodegen {
mv.visitCode();
InstructionAdapter instructionAdapter = new InstructionAdapter(mv);
final InstructionAdapter instructionAdapter = new InstructionAdapter(mv);
Type classType = bindingContext.get(ASM_TYPE, classDescriptorForScript);
assert classType != null;
@@ -139,7 +140,7 @@ public class ScriptCodegen extends MemberCodegen {
instructionAdapter.load(0, classType);
FrameMap frameMap = context.prepareFrame(typeMapper);
final FrameMap frameMap = context.prepareFrame(typeMapper);
for (ScriptDescriptor importedScript : earlierScripts) {
frameMap.enter(importedScript, OBJECT_TYPE);
@@ -153,11 +154,12 @@ public class ScriptCodegen extends MemberCodegen {
frameMap.enter(parameter, argTypes[i + add]);
}
ImplementationBodyCodegen.generateInitializers(
new ExpressionCodegen(instructionAdapter, frameMap, Type.VOID_TYPE, context, state, this),
scriptDeclaration.getDeclarations(),
bindingContext,
state);
generateInitializers(scriptDeclaration.getDeclarations(), new Function0<ExpressionCodegen>() {
@Override
public ExpressionCodegen invoke() {
return new ExpressionCodegen(instructionAdapter, frameMap, Type.VOID_TYPE, context, state, ScriptCodegen.this);
}
});
int offset = 1;