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