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 }