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