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