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