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