001 /*
002 * Copyright 2010-2014 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 com.intellij.psi.PsiElement;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.jet.codegen.context.*;
025 import org.jetbrains.jet.codegen.state.GenerationState;
026 import org.jetbrains.jet.codegen.state.JetTypeMapper;
027 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPropertyDescriptor;
028 import org.jetbrains.jet.lang.descriptors.*;
029 import org.jetbrains.jet.lang.psi.*;
030 import org.jetbrains.jet.lang.resolve.BindingContext;
031 import org.jetbrains.jet.lang.resolve.DescriptorFactory;
032 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
033 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
034 import org.jetbrains.jet.lang.resolve.java.JvmAbi;
035 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
036 import org.jetbrains.jet.lang.resolve.name.Name;
037 import org.jetbrains.jet.lang.types.ErrorUtils;
038 import org.jetbrains.jet.lang.types.JetType;
039 import org.jetbrains.org.objectweb.asm.FieldVisitor;
040 import org.jetbrains.org.objectweb.asm.MethodVisitor;
041 import org.jetbrains.org.objectweb.asm.Opcodes;
042 import org.jetbrains.org.objectweb.asm.Type;
043 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
044 import org.jetbrains.org.objectweb.asm.commons.Method;
045
046 import static org.jetbrains.jet.codegen.AsmUtil.*;
047 import static org.jetbrains.jet.codegen.JvmCodegenUtil.getParentBodyCodegen;
048 import static org.jetbrains.jet.codegen.JvmCodegenUtil.isInterface;
049 import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
050 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
051 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait;
052 import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
053 import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.PROPERTY_METADATA_TYPE;
054 import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
055 import static org.jetbrains.org.objectweb.asm.Opcodes.*;
056
057 public class PropertyCodegen {
058 private final GenerationState state;
059 private final ClassBuilder v;
060 private final FunctionCodegen functionCodegen;
061 private final JetTypeMapper typeMapper;
062 private final BindingContext bindingContext;
063 private final FieldOwnerContext context;
064 private final MemberCodegen<?> classBodyCodegen;
065 private final OwnerKind kind;
066
067 public PropertyCodegen(
068 @NotNull FieldOwnerContext context,
069 @NotNull ClassBuilder v,
070 @NotNull FunctionCodegen functionCodegen,
071 @Nullable MemberCodegen<?> classBodyCodegen
072 ) {
073 this.state = functionCodegen.state;
074 this.v = v;
075 this.functionCodegen = functionCodegen;
076 this.typeMapper = state.getTypeMapper();
077 this.bindingContext = state.getBindingContext();
078 this.context = context;
079 this.classBodyCodegen = classBodyCodegen;
080 this.kind = context.getContextKind();
081 }
082
083 public void gen(@NotNull JetProperty property) {
084 VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, property);
085 assert variableDescriptor instanceof PropertyDescriptor : "Property " + property.getText() + " should have a property descriptor: " + variableDescriptor;
086
087 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
088 gen(property, propertyDescriptor, property.getGetter(), property.getSetter());
089 }
090
091 public void generateInPackageFacade(@NotNull DeserializedPropertyDescriptor deserializedProperty) {
092 assert context instanceof PackageFacadeContext : "should be called only for generating package facade: " + context;
093 gen(null, deserializedProperty, null, null);
094 }
095
096 private void gen(
097 @Nullable JetProperty declaration,
098 @NotNull PropertyDescriptor descriptor,
099 @Nullable JetPropertyAccessor getter,
100 @Nullable JetPropertyAccessor setter
101 ) {
102 assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.TRAIT_IMPL
103 : "Generating property with a wrong kind (" + kind + "): " + descriptor;
104
105 if (context instanceof PackageFacadeContext) {
106 Type ownerType = ((PackageFacadeContext) context).getDelegateToClassType();
107 v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(ownerType));
108 }
109 else {
110 assert declaration != null : "Declaration is null for different context: " + context;
111 if (!generateBackingField(declaration, descriptor)) {
112 generateSyntheticMethodIfNeeded(descriptor);
113 }
114 }
115
116 if (isAccessorNeeded(declaration, descriptor, getter)) {
117 generateGetter(declaration, descriptor, getter);
118 }
119 if (isAccessorNeeded(declaration, descriptor, setter)) {
120 generateSetter(declaration, descriptor, setter);
121 }
122
123 context.recordSyntheticAccessorIfNeeded(descriptor, bindingContext);
124 }
125
126 /**
127 * Determines if it's necessary to generate an accessor to the property, i.e. if this property can be referenced via getter/setter
128 * for any reason
129 *
130 * @see JvmCodegenUtil#couldUseDirectAccessToProperty
131 */
132 private boolean isAccessorNeeded(
133 @Nullable JetProperty declaration,
134 @NotNull PropertyDescriptor descriptor,
135 @Nullable JetPropertyAccessor accessor
136 ) {
137 boolean isDefaultAccessor = accessor == null || !accessor.hasBody();
138
139 // Don't generate accessors for trait properties with default accessors in TRAIT_IMPL
140 if (kind == OwnerKind.TRAIT_IMPL && isDefaultAccessor) return false;
141
142 if (declaration == null) return true;
143
144 // Delegated or extension properties can only be referenced via accessors
145 if (declaration.hasDelegate() || declaration.getReceiverTypeRef() != null) return true;
146
147 // Class object properties always should have accessors, because their backing fields are moved/copied to the outer class
148 if (isClassObject(descriptor.getContainingDeclaration())) return true;
149
150 // Private class properties have accessors only in cases when those accessors are non-trivial
151 if (kind == OwnerKind.IMPLEMENTATION && descriptor.getVisibility() == Visibilities.PRIVATE) {
152 return !isDefaultAccessor;
153 }
154
155 return true;
156 }
157
158 public void generatePrimaryConstructorProperty(JetParameter p, PropertyDescriptor descriptor) {
159 generateBackingField(p, descriptor);
160 if (descriptor.getVisibility() != Visibilities.PRIVATE) {
161 generateGetter(p, descriptor, null);
162 if (descriptor.isVar()) {
163 generateSetter(p, descriptor, null);
164 }
165 }
166 }
167
168 public void generateConstructorPropertyAsMethodForAnnotationClass(JetParameter p, PropertyDescriptor descriptor) {
169 Type type = typeMapper.mapType(descriptor);
170 String name = p.getName();
171 assert name != null : "Annotation parameter has no name: " + p.getText();
172 MethodVisitor mv = v.newMethod(OtherOrigin(p, descriptor), ACC_PUBLIC | ACC_ABSTRACT, name, "()" + type.getDescriptor(), null, null);
173
174 if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
175 JetExpression defaultValue = p.getDefaultValue();
176 if (defaultValue != null) {
177 CompileTimeConstant<?> constant = ExpressionCodegen.getCompileTimeConstant(defaultValue, bindingContext);
178 assert constant != null : "Default value for annotation parameter should be compile time value: " + defaultValue.getText();
179 AnnotationCodegen annotationCodegen = AnnotationCodegen.forAnnotationDefaultValue(mv, typeMapper);
180 annotationCodegen.generateAnnotationDefaultValue(constant, descriptor.getType());
181 }
182 }
183
184 mv.visitEnd();
185 }
186
187 private boolean generateBackingField(@NotNull JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor) {
188 if (isInterface(descriptor.getContainingDeclaration()) || kind == OwnerKind.TRAIT_IMPL) {
189 return false;
190 }
191
192 if (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) {
193 generateBackingFieldAccess(p, descriptor);
194 }
195 else if (p instanceof JetProperty && ((JetProperty) p).hasDelegate()) {
196 generatePropertyDelegateAccess((JetProperty) p, descriptor);
197 }
198 else {
199 return false;
200 }
201 return true;
202 }
203
204 // Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still
205 // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally
206 private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor) {
207 if (descriptor.getAnnotations().isEmpty()) return;
208
209 ReceiverParameterDescriptor receiver = descriptor.getReceiverParameter();
210 String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
211 String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V";
212
213 if (!isTrait(context.getContextDescriptor()) || kind == OwnerKind.TRAIT_IMPL) {
214 int flags = ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
215 MethodVisitor mv = v.newMethod(OtherOrigin(descriptor), flags, name, desc, null, null);
216 AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(descriptor, Type.VOID_TYPE);
217 mv.visitCode();
218 mv.visitInsn(Opcodes.RETURN);
219 mv.visitEnd();
220 }
221 else {
222 Type tImplType = typeMapper.mapTraitImpl((ClassDescriptor) context.getContextDescriptor());
223 v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(tImplType));
224 }
225
226 if (kind != OwnerKind.TRAIT_IMPL) {
227 v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, new Method(name, desc));
228 }
229 }
230
231 private void generateBackingField(JetNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, JetType jetType, Object defaultValue) {
232 int modifiers = getDeprecatedAccessFlag(propertyDescriptor);
233
234 for (AnnotationCodegen.JvmFlagAnnotation flagAnnotation : AnnotationCodegen.FIELD_FLAGS) {
235 if (flagAnnotation.hasAnnotation(propertyDescriptor.getOriginal())) {
236 modifiers |= flagAnnotation.getJvmFlag();
237 }
238 }
239
240 if (kind == OwnerKind.PACKAGE) {
241 modifiers |= ACC_STATIC;
242 }
243
244 if (!propertyDescriptor.isVar() || isDelegate) {
245 modifiers |= ACC_FINAL;
246 }
247
248 Type type = typeMapper.mapType(jetType);
249
250 ClassBuilder builder = v;
251
252 FieldOwnerContext backingFieldContext = context;
253 if (AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) {
254 modifiers |= ACC_STATIC | getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate);
255 ImplementationBodyCodegen codegen = getParentBodyCodegen(classBodyCodegen);
256 builder = codegen.v;
257 backingFieldContext = codegen.context;
258 v.getSerializationBindings().put(STATIC_FIELD_IN_OUTER_CLASS, propertyDescriptor);
259 } else {
260 if (kind != OwnerKind.PACKAGE || isDelegate) {
261 modifiers |= ACC_PRIVATE;
262 }
263 }
264
265 if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
266 ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen);
267 parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue);
268 }
269
270 String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
271
272 v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name));
273
274 FieldVisitor fv = builder.newField(OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(),
275 typeMapper.mapFieldSignature(jetType), defaultValue);
276 AnnotationCodegen.forField(fv, typeMapper).genAnnotations(propertyDescriptor, type);
277 }
278
279 private void generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) {
280 JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression());
281 if (delegateType == null) {
282 // If delegate expression is unresolved reference
283 delegateType = ErrorUtils.createErrorType("Delegate type");
284 }
285
286 generateBackingField(p, propertyDescriptor, true, delegateType, null);
287 }
288
289 private void generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
290 Object value = null;
291
292 if (shouldWriteFieldInitializer(propertyDescriptor)) {
293 CompileTimeConstant<?> initializer = propertyDescriptor.getCompileTimeInitializer();
294 if (initializer != null) {
295 value = initializer.getValue();
296 }
297 }
298
299 generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value);
300 }
301
302 private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) {
303 //final field of primitive or String type
304 if (!descriptor.isVar()) {
305 Type type = typeMapper.mapType(descriptor);
306 return AsmUtil.isPrimitive(type) || "java.lang.String".equals(type.getClassName());
307 }
308 return false;
309 }
310
311 private void generateGetter(@Nullable JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor getter) {
312 generateAccessor(p, getter, descriptor.getGetter() != null
313 ? descriptor.getGetter()
314 : DescriptorFactory.createDefaultGetter(descriptor));
315 }
316
317 private void generateSetter(@Nullable JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor setter) {
318 if (!descriptor.isVar()) return;
319
320 generateAccessor(p, setter, descriptor.getSetter() != null
321 ? descriptor.getSetter()
322 : DescriptorFactory.createDefaultSetter(descriptor));
323 }
324
325 private void generateAccessor(
326 @Nullable JetNamedDeclaration p,
327 @Nullable JetPropertyAccessor accessor,
328 @NotNull PropertyAccessorDescriptor accessorDescriptor
329 ) {
330 FunctionGenerationStrategy strategy;
331 if (accessor == null || !accessor.hasBody()) {
332 if (p instanceof JetProperty && ((JetProperty) p).hasDelegate()) {
333 strategy = new DelegatedPropertyAccessorStrategy(state, accessorDescriptor, indexOfDelegatedProperty((JetProperty) p));
334 }
335 else {
336 strategy = new DefaultPropertyAccessorStrategy(state, accessorDescriptor);
337 }
338 }
339 else {
340 strategy = new FunctionGenerationStrategy.FunctionDefault(state, accessorDescriptor, accessor);
341 }
342
343 JvmMethodSignature signature = typeMapper.mapSignature(accessorDescriptor, kind);
344 functionCodegen.generateMethod(OtherOrigin(accessor != null ? accessor : p, accessorDescriptor), signature, accessorDescriptor, strategy);
345 }
346
347 private static int indexOfDelegatedProperty(@NotNull JetProperty property) {
348 PsiElement parent = property.getParent();
349 JetDeclarationContainer container;
350 if (parent instanceof JetClassBody) {
351 container = ((JetClassOrObject) parent.getParent());
352 }
353 else if (parent instanceof JetFile) {
354 container = (JetFile) parent;
355 }
356 else {
357 throw new UnsupportedOperationException("Unknown delegated property container: " + parent);
358 }
359
360 int index = 0;
361 for (JetDeclaration declaration : container.getDeclarations()) {
362 if (declaration instanceof JetProperty && ((JetProperty) declaration).hasDelegate()) {
363 if (declaration == property) {
364 return index;
365 }
366 index++;
367 }
368 }
369
370 throw new IllegalStateException("Delegated property not found in its parent: " + JetPsiUtil.getElementTextWithContext(property));
371 }
372
373
374 private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
375 public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
376 super(state, descriptor);
377 }
378
379 @Override
380 public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
381 InstructionAdapter v = codegen.v;
382 PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty();
383
384 int paramCode = 0;
385 if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) {
386 v.load(0, OBJECT_TYPE);
387 paramCode = 1;
388 }
389
390 StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, true, null);
391
392 if (callableDescriptor instanceof PropertyGetterDescriptor) {
393 Type type = signature.getReturnType();
394 property.put(type, v);
395 v.areturn(type);
396 }
397 else if (callableDescriptor instanceof PropertySetterDescriptor) {
398 ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
399 if (receiverParameter != null) {
400 paramCode += codegen.typeMapper.mapType(receiverParameter.getType()).getSize();
401 }
402 Type type = codegen.typeMapper.mapType(propertyDescriptor);
403 v.load(paramCode, type);
404 property.store(type, v);
405 v.visitInsn(RETURN);
406 } else {
407 throw new IllegalStateException("Unknown property accessor: " + callableDescriptor);
408 }
409 }
410 }
411
412 private static class DelegatedPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
413 private final int index;
414
415 public DelegatedPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor, int index) {
416 super(state, descriptor);
417 this.index = index;
418 }
419
420 @Override
421 public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
422 InstructionAdapter v = codegen.v;
423
424 BindingContext bindingContext = state.getBindingContext();
425 ResolvedCall<FunctionDescriptor> resolvedCall =
426 bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor);
427 assert resolvedCall != null : "Resolve call should be recorded for delegate call " + signature.toString();
428
429 if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) {
430 v.load(0, OBJECT_TYPE);
431 }
432
433 CodegenContext<? extends ClassOrPackageFragmentDescriptor> ownerContext = codegen.getContext().getClassOrPackageParentContext();
434 final Type owner;
435 if (ownerContext instanceof ClassContext) {
436 owner = state.getTypeMapper().mapClass(((ClassContext) ownerContext).getContextDescriptor());
437 }
438 else if (ownerContext instanceof PackageContext) {
439 owner = ((PackageContext) ownerContext).getPackagePartType();
440 }
441 else {
442 throw new UnsupportedOperationException("Unknown context: " + ownerContext);
443 }
444
445 codegen.tempVariables.put(
446 resolvedCall.getCall().getValueArguments().get(1).asElement(),
447 new StackValue(PROPERTY_METADATA_TYPE) {
448 @Override
449 public void put(Type type, InstructionAdapter v) {
450 v.getstatic(owner.getInternalName(), JvmAbi.PROPERTY_METADATA_ARRAY_NAME, "[" + PROPERTY_METADATA_TYPE);
451 v.iconst(index);
452 StackValue.arrayElement(PROPERTY_METADATA_TYPE).put(type, v);
453 }
454 }
455 );
456
457 StackValue delegatedProperty = codegen.intermediateValueForProperty(callableDescriptor.getCorrespondingProperty(), true, null);
458 StackValue lastValue = codegen.invokeFunction(resolvedCall, delegatedProperty);
459
460 Type asmType = signature.getReturnType();
461 lastValue.put(asmType, v);
462 v.areturn(asmType);
463 }
464 }
465
466 @NotNull
467 public static String getterName(Name propertyName) {
468 return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
469 }
470
471 @NotNull
472 public static String setterName(Name propertyName) {
473 return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
474 }
475
476 public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor overridden, @NotNull StackValue field) {
477 ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration();
478
479 PropertyGetterDescriptor getter = delegate.getGetter();
480 if (getter != null) {
481 //noinspection ConstantConditions
482 functionCodegen.genDelegate(getter, toClass, field,
483 typeMapper.mapSignature(getter), typeMapper.mapSignature(overridden.getGetter().getOriginal()));
484 }
485
486 PropertySetterDescriptor setter = delegate.getSetter();
487 if (setter != null) {
488 //noinspection ConstantConditions
489 functionCodegen.genDelegate(setter, toClass, field,
490 typeMapper.mapSignature(setter), typeMapper.mapSignature(overridden.getSetter().getOriginal()));
491 }
492 }
493 }