001 /* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.jet.codegen; 018 019 import com.intellij.openapi.util.Pair; 020 import com.intellij.openapi.util.text.StringUtil; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.asm4.FieldVisitor; 024 import org.jetbrains.asm4.MethodVisitor; 025 import org.jetbrains.asm4.Opcodes; 026 import org.jetbrains.asm4.Type; 027 import org.jetbrains.asm4.commons.InstructionAdapter; 028 import org.jetbrains.asm4.commons.Method; 029 import org.jetbrains.jet.codegen.context.FieldOwnerContext; 030 import org.jetbrains.jet.codegen.context.PackageFacadeContext; 031 import org.jetbrains.jet.codegen.signature.JvmMethodSignature; 032 import org.jetbrains.jet.codegen.state.GenerationState; 033 import org.jetbrains.jet.codegen.state.GenerationStateAware; 034 import org.jetbrains.jet.lang.descriptors.*; 035 import org.jetbrains.jet.lang.psi.*; 036 import org.jetbrains.jet.lang.resolve.BindingContext; 037 import org.jetbrains.jet.lang.resolve.DescriptorFactory; 038 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 039 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; 040 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 041 import org.jetbrains.jet.lang.resolve.name.Name; 042 import org.jetbrains.jet.lang.types.ErrorUtils; 043 import org.jetbrains.jet.lang.types.JetType; 044 045 import static org.jetbrains.asm4.Opcodes.*; 046 import static org.jetbrains.jet.codegen.AsmUtil.*; 047 import static org.jetbrains.jet.codegen.CodegenUtil.getParentBodyCodegen; 048 import static org.jetbrains.jet.codegen.CodegenUtil.isInterface; 049 import static org.jetbrains.jet.codegen.JvmSerializationBindings.*; 050 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait; 051 import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; 052 053 public class PropertyCodegen extends GenerationStateAware { 054 @NotNull 055 private final FunctionCodegen functionCodegen; 056 057 @NotNull 058 private final ClassBuilder v; 059 060 @NotNull 061 private final FieldOwnerContext context; 062 063 @Nullable 064 private final MemberCodegen classBodyCodegen; 065 066 @NotNull 067 private final OwnerKind kind; 068 069 public PropertyCodegen( 070 @NotNull FieldOwnerContext context, 071 @NotNull ClassBuilder v, 072 @NotNull FunctionCodegen functionCodegen, 073 @Nullable MemberCodegen classBodyCodegen 074 ) { 075 super(functionCodegen.getState()); 076 this.v = v; 077 this.functionCodegen = functionCodegen; 078 this.context = context; 079 this.classBodyCodegen = classBodyCodegen; 080 this.kind = context.getContextKind(); 081 } 082 083 public void gen(JetProperty p) { 084 VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, p); 085 assert variableDescriptor instanceof PropertyDescriptor : "Property should have a property descriptor: " + variableDescriptor; 086 087 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor; 088 assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.TRAIT_IMPL 089 : "Generating property with a wrong kind (" + kind + "): " + propertyDescriptor; 090 091 092 if (context instanceof PackageFacadeContext) { 093 Type ownerType = ((PackageFacadeContext) context).getDelegateToClassType(); 094 v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, propertyDescriptor, shortNameByAsmType(ownerType)); 095 } 096 else if (!generateBackingField(p, propertyDescriptor)) { 097 generateSyntheticMethodIfNeeded(propertyDescriptor); 098 } 099 100 generateGetter(p, propertyDescriptor, p.getGetter()); 101 generateSetter(p, propertyDescriptor, p.getSetter()); 102 103 context.recordSyntheticAccessorIfNeeded(propertyDescriptor, bindingContext); 104 } 105 106 public void generatePrimaryConstructorProperty(JetParameter p, PropertyDescriptor descriptor) { 107 generateBackingField(p, descriptor); 108 generateGetter(p, descriptor, null); 109 if (descriptor.isVar()) { 110 generateSetter(p, descriptor, null); 111 } 112 } 113 114 public void generateConstructorPropertyAsMethodForAnnotationClass(JetParameter p, PropertyDescriptor descriptor) { 115 Type type = state.getTypeMapper().mapType(descriptor); 116 String name = p.getName(); 117 assert name != null : "Annotation parameter has no name: " + p.getText(); 118 MethodVisitor visitor = v.newMethod(p, ACC_PUBLIC | ACC_ABSTRACT, name, "()" + type.getDescriptor(), null, null); 119 JetExpression defaultValue = p.getDefaultValue(); 120 if (defaultValue != null) { 121 CompileTimeConstant<?> constant = ExpressionCodegen.getCompileTimeConstant(defaultValue, state.getBindingContext()); 122 assert constant != null : "Default value for annotation parameter should be compile time value: " + defaultValue.getText(); 123 AnnotationCodegen annotationCodegen = AnnotationCodegen.forAnnotationDefaultValue(visitor, typeMapper); 124 annotationCodegen.generateAnnotationDefaultValue(constant, descriptor.getType()); 125 } 126 } 127 128 private boolean generateBackingField(@NotNull JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor) { 129 if (isInterface(descriptor.getContainingDeclaration()) || kind == OwnerKind.TRAIT_IMPL) { 130 return false; 131 } 132 133 FieldVisitor fv; 134 if (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) { 135 fv = generateBackingFieldAccess(p, descriptor); 136 } 137 else if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) { 138 fv = generatePropertyDelegateAccess((JetProperty) p, descriptor); 139 } 140 else { 141 return false; 142 } 143 144 AnnotationCodegen.forField(fv, typeMapper).genAnnotations(descriptor); 145 return true; 146 } 147 148 // Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still 149 // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally 150 private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor) { 151 if (descriptor.getAnnotations().isEmpty()) return; 152 153 ReceiverParameterDescriptor receiver = descriptor.getReceiverParameter(); 154 Type receiverAsmType = receiver == null ? null : typeMapper.mapType(receiver.getType()); 155 Method method = JvmAbi.getSyntheticMethodSignatureForAnnotatedProperty(descriptor.getName(), receiverAsmType); 156 157 if (!isTrait(context.getContextDescriptor()) || kind == OwnerKind.TRAIT_IMPL) { 158 MethodVisitor mv = v.newMethod(null, 159 ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, 160 method.getName(), 161 method.getDescriptor(), 162 null, 163 null); 164 AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(descriptor); 165 mv.visitCode(); 166 mv.visitInsn(Opcodes.RETURN); 167 mv.visitEnd(); 168 } 169 else { 170 Type tImplType = typeMapper.mapTraitImpl((ClassDescriptor) context.getContextDescriptor()); 171 v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(tImplType)); 172 } 173 174 if (kind != OwnerKind.TRAIT_IMPL) { 175 v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, method); 176 } 177 } 178 179 private FieldVisitor generateBackingField(JetNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, JetType jetType, Object defaultValue) { 180 int modifiers = getDeprecatedAccessFlag(propertyDescriptor); 181 182 if (propertyDescriptor.getOriginal().getAnnotations().findAnnotation(AnnotationCodegen.VOLATILE_FQ_NAME) != null) { 183 modifiers |= ACC_VOLATILE; 184 } 185 186 if (kind == OwnerKind.PACKAGE) { 187 modifiers |= ACC_STATIC; 188 } 189 190 if (!propertyDescriptor.isVar() || isDelegate) { 191 modifiers |= ACC_FINAL; 192 } 193 194 Type type = typeMapper.mapType(jetType); 195 196 ClassBuilder builder = v; 197 198 FieldOwnerContext backingFieldContext = context; 199 if (AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) { 200 modifiers |= ACC_STATIC | getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate); 201 ImplementationBodyCodegen codegen = getParentBodyCodegen(classBodyCodegen); 202 builder = codegen.v; 203 backingFieldContext = codegen.context; 204 v.getSerializationBindings().put(STATIC_FIELD_IN_OUTER_CLASS, propertyDescriptor); 205 } else { 206 if (kind != OwnerKind.PACKAGE || isDelegate) { 207 modifiers |= ACC_PRIVATE; 208 } 209 } 210 211 if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) { 212 ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen); 213 parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue); 214 } 215 216 String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate); 217 218 v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name)); 219 220 return builder.newField(element, modifiers, name, type.getDescriptor(), 221 typeMapper.mapFieldSignature(jetType), defaultValue); 222 } 223 224 private FieldVisitor generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) { 225 JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression()); 226 if (delegateType == null) { 227 // If delegate expression is unresolved reference 228 delegateType = ErrorUtils.createErrorType("Delegate type"); 229 } 230 231 return generateBackingField(p, propertyDescriptor, true, delegateType, null); 232 } 233 234 private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) { 235 Object value = null; 236 237 if (ImplementationBodyCodegen.shouldWriteFieldInitializer(propertyDescriptor, typeMapper)) { 238 CompileTimeConstant<?> initializer = propertyDescriptor.getCompileTimeInitializer(); 239 if (initializer != null) { 240 value = initializer.getValue(); 241 } 242 } 243 244 return generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value); 245 } 246 247 private void generateGetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor getter) { 248 boolean defaultGetter = getter == null || getter.getBodyExpression() == null; 249 250 //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter 251 //if (!defaultGetter || isExternallyAccessible(propertyDescriptor)) { 252 PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter(); 253 getterDescriptor = getterDescriptor != null ? getterDescriptor : DescriptorFactory.createDefaultGetter(propertyDescriptor); 254 JvmMethodSignature signature = typeMapper.mapSignature(getterDescriptor, kind); 255 256 if (kind != OwnerKind.TRAIT_IMPL || !defaultGetter) { 257 FunctionGenerationStrategy strategy; 258 if (defaultGetter) { 259 if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) { 260 strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, getterDescriptor); 261 } 262 else { 263 strategy = new DefaultPropertyAccessorStrategy(state, getterDescriptor); 264 } 265 } 266 else { 267 strategy = new FunctionGenerationStrategy.FunctionDefault(state, getterDescriptor, getter); 268 } 269 functionCodegen.generateMethod(getter != null ? getter : p, 270 signature, 271 getterDescriptor, 272 strategy); 273 } 274 //} 275 } 276 277 private void generateSetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor setter) { 278 boolean defaultSetter = setter == null || setter.getBodyExpression() == null; 279 280 //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter 281 if (/*!defaultSetter || isExternallyAccessible(propertyDescriptor) &&*/ propertyDescriptor.isVar()) { 282 PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter(); 283 setterDescriptor = setterDescriptor != null ? setterDescriptor : DescriptorFactory.createDefaultSetter(propertyDescriptor); 284 JvmMethodSignature signature = typeMapper.mapSignature(setterDescriptor, kind); 285 286 if (kind != OwnerKind.TRAIT_IMPL || !defaultSetter) { 287 FunctionGenerationStrategy strategy; 288 if (defaultSetter) { 289 if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) { 290 strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, setterDescriptor); 291 } 292 else { 293 strategy = new DefaultPropertyAccessorStrategy(state, setterDescriptor); 294 } 295 } 296 else { 297 strategy = new FunctionGenerationStrategy.FunctionDefault(state, setterDescriptor, setter); 298 } 299 functionCodegen.generateMethod(setter != null ? setter : p, 300 signature, 301 setterDescriptor, 302 strategy); 303 } 304 } 305 } 306 307 308 private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> { 309 public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) { 310 super(state, descriptor); 311 } 312 313 @Override 314 public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { 315 InstructionAdapter v = codegen.v; 316 PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty(); 317 318 int paramCode = 0; 319 if (codegen.context.getContextKind() != OwnerKind.PACKAGE) { 320 v.load(0, OBJECT_TYPE); 321 paramCode = 1; 322 } 323 324 StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, true, null); 325 326 if (callableDescriptor instanceof PropertyGetterDescriptor) { 327 Type type = signature.getReturnType(); 328 property.put(type, v); 329 v.areturn(type); 330 } 331 else if (callableDescriptor instanceof PropertySetterDescriptor) { 332 ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter(); 333 if (receiverParameter != null) { 334 paramCode += codegen.typeMapper.mapType(receiverParameter.getType()).getSize(); 335 } 336 Type type = codegen.typeMapper.mapType(propertyDescriptor); 337 v.load(paramCode, type); 338 property.store(type, v); 339 v.visitInsn(RETURN); 340 } else { 341 throw new IllegalStateException("Unknown property accessor: " + callableDescriptor); 342 } 343 } 344 } 345 346 private static class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> { 347 public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) { 348 super(state, descriptor); 349 } 350 351 @Override 352 public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { 353 InstructionAdapter v = codegen.v; 354 355 BindingContext bindingContext = state.getBindingContext(); 356 ResolvedCall<FunctionDescriptor> resolvedCall = 357 bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor); 358 359 Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, callableDescriptor); 360 assert call != null : "Call should be recorded for delegate call " + signature.toString(); 361 362 if (codegen.context.getContextKind() != OwnerKind.PACKAGE) { 363 v.load(0, OBJECT_TYPE); 364 } 365 366 PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty(); 367 368 StackValue delegatedProperty = codegen.intermediateValueForProperty(propertyDescriptor, true, null); 369 StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall); 370 371 Type asmType = signature.getReturnType(); 372 lastValue.put(asmType, v); 373 v.areturn(asmType); 374 } 375 } 376 377 public static String getterName(Name propertyName) { 378 return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); 379 } 380 381 public static String setterName(Name propertyName) { 382 return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); 383 } 384 385 public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor overridden, @NotNull StackValue field) { 386 ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration(); 387 388 PropertyGetterDescriptor getter = delegate.getGetter(); 389 if (getter != null) { 390 //noinspection ConstantConditions 391 functionCodegen.genDelegate(getter, toClass, field, 392 typeMapper.mapSignature(getter), typeMapper.mapSignature(overridden.getGetter().getOriginal())); 393 } 394 395 PropertySetterDescriptor setter = delegate.getSetter(); 396 if (setter != null) { 397 //noinspection ConstantConditions 398 functionCodegen.genDelegate(setter, toClass, field, 399 typeMapper.mapSignature(setter), typeMapper.mapSignature(overridden.getSetter().getOriginal())); 400 } 401 } 402 }