001 /*
002 * Copyright 2010-2015 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.kotlin.codegen.context;
018
019 import kotlin.jvm.functions.Function0;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.codegen.*;
023 import org.jetbrains.kotlin.codegen.binding.MutableClosure;
024 import org.jetbrains.kotlin.codegen.state.GenerationState;
025 import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
026 import org.jetbrains.kotlin.descriptors.*;
027 import org.jetbrains.kotlin.psi.KtFile;
028 import org.jetbrains.kotlin.psi.KtSuperExpression;
029 import org.jetbrains.kotlin.resolve.BindingContext;
030 import org.jetbrains.kotlin.resolve.DescriptorUtils;
031 import org.jetbrains.kotlin.storage.LockBasedStorageManager;
032 import org.jetbrains.kotlin.storage.NullableLazyValue;
033 import org.jetbrains.kotlin.types.KotlinType;
034 import org.jetbrains.org.objectweb.asm.Type;
035
036 import java.util.*;
037
038 import static org.jetbrains.kotlin.codegen.AsmUtil.getVisibilityAccessFlag;
039 import static org.jetbrains.kotlin.resolve.BindingContext.NEED_SYNTHETIC_ACCESSOR;
040 import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE;
041 import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PROTECTED;
042
043 public abstract class CodegenContext<T extends DeclarationDescriptor> {
044 private final T contextDescriptor;
045 private final OwnerKind contextKind;
046 private final CodegenContext parentContext;
047 private final ClassDescriptor thisDescriptor;
048 public final MutableClosure closure;
049 private final LocalLookup enclosingLocalLookup;
050 private final NullableLazyValue<StackValue.Field> outerExpression;
051
052 private Map<DeclarationDescriptor, CodegenContext> childContexts;
053 private Map<AccessorKey, AccessorForCallableDescriptor<?>> accessors;
054
055 private static class AccessorKey {
056 public final DeclarationDescriptor descriptor;
057 public final ClassDescriptor superCallLabelTarget;
058
059 public AccessorKey(@NotNull DeclarationDescriptor descriptor, @Nullable ClassDescriptor superCallLabelTarget) {
060 this.descriptor = descriptor;
061 this.superCallLabelTarget = superCallLabelTarget;
062 }
063
064 @Override
065 public boolean equals(Object obj) {
066 if (!(obj instanceof AccessorKey)) return false;
067 AccessorKey other = (AccessorKey) obj;
068 return descriptor.equals(other.descriptor) &&
069 (superCallLabelTarget == null
070 ? other.superCallLabelTarget == null
071 : superCallLabelTarget.equals(other.superCallLabelTarget));
072 }
073
074 @Override
075 public int hashCode() {
076 return 31 * descriptor.hashCode() + (superCallLabelTarget == null ? 0 : superCallLabelTarget.hashCode());
077 }
078
079 @Override
080 public String toString() {
081 return descriptor.toString();
082 }
083 }
084
085 public CodegenContext(
086 @NotNull T contextDescriptor,
087 @NotNull OwnerKind contextKind,
088 @Nullable CodegenContext parentContext,
089 @Nullable MutableClosure closure,
090 @Nullable ClassDescriptor thisDescriptor,
091 @Nullable LocalLookup localLookup
092 ) {
093 this.contextDescriptor = contextDescriptor;
094 this.contextKind = contextKind;
095 this.parentContext = parentContext;
096 this.closure = closure;
097 this.thisDescriptor = thisDescriptor;
098 this.enclosingLocalLookup = localLookup;
099 this.outerExpression = LockBasedStorageManager.NO_LOCKS.createNullableLazyValue(new Function0<StackValue.Field>() {
100 @Override
101 public StackValue.Field invoke() {
102 return computeOuterExpression();
103 }
104 });
105
106 if (parentContext != null) {
107 parentContext.addChild(this);
108 }
109 }
110
111 @NotNull
112 public GenerationState getState() {
113 return parentContext.getState();
114 }
115
116 @NotNull
117 public final ClassDescriptor getThisDescriptor() {
118 if (thisDescriptor == null) {
119 throw new UnsupportedOperationException("Context doesn't have a \"this\": " + this);
120 }
121 return thisDescriptor;
122 }
123
124 public final boolean hasThisDescriptor() {
125 return thisDescriptor != null;
126 }
127
128 @NotNull
129 @SuppressWarnings("unchecked")
130 public CodegenContext<? extends ClassOrPackageFragmentDescriptor> getClassOrPackageParentContext() {
131 CodegenContext<?> context = this;
132 while (true) {
133 if (context.getContextDescriptor() instanceof ClassOrPackageFragmentDescriptor) {
134 return (CodegenContext) context;
135 }
136 context = context.getParentContext();
137 assert context != null : "Context which is not top-level has no parent: " + this;
138 }
139 }
140
141 /**
142 * This method returns not null only if context descriptor corresponds to method or function which has receiver
143 */
144 @Nullable
145 public final CallableDescriptor getCallableDescriptorWithReceiver() {
146 if (contextDescriptor instanceof CallableDescriptor) {
147 CallableDescriptor callableDescriptor = (CallableDescriptor) getContextDescriptor();
148 return callableDescriptor.getExtensionReceiverParameter() != null ? callableDescriptor : null;
149 }
150 return null;
151 }
152
153 public StackValue getOuterExpression(@Nullable StackValue prefix, boolean ignoreNoOuter) {
154 return getOuterExpression(prefix, ignoreNoOuter, true);
155 }
156
157 private StackValue getOuterExpression(@Nullable StackValue prefix, boolean ignoreNoOuter, boolean captureThis) {
158 if (outerExpression.invoke() == null) {
159 if (!ignoreNoOuter) {
160 throw new UnsupportedOperationException("Don't know how to generate outer expression for " + getContextDescriptor());
161 }
162 return null;
163 }
164 if (captureThis) {
165 if (closure == null) {
166 throw new IllegalStateException("Can't capture this for context without closure: " + getContextDescriptor());
167 }
168 closure.setCaptureThis();
169 }
170 return StackValue.changeReceiverForFieldAndSharedVar(outerExpression.invoke(), prefix);
171 }
172
173 @NotNull
174 public T getContextDescriptor() {
175 return contextDescriptor;
176 }
177
178 @NotNull
179 public OwnerKind getContextKind() {
180 return contextKind;
181 }
182
183 @NotNull
184 public PackageContext intoPackagePart(@NotNull PackageFragmentDescriptor descriptor, Type packagePartType, @Nullable KtFile sourceFile) {
185 return new PackageContext(descriptor, this, packagePartType, sourceFile);
186 }
187
188 @NotNull
189 public FieldOwnerContext<PackageFragmentDescriptor> intoMultifileClassPart(
190 @NotNull PackageFragmentDescriptor descriptor,
191 @NotNull Type multifileClassType,
192 @NotNull Type filePartType,
193 @NotNull KtFile sourceFile
194 ) {
195 return new MultifileClassPartContext(descriptor, this, multifileClassType, filePartType, sourceFile);
196 }
197
198 @NotNull
199 public FieldOwnerContext<PackageFragmentDescriptor> intoMultifileClass(
200 @NotNull PackageFragmentDescriptor descriptor,
201 @NotNull Type multifileClassType,
202 @NotNull Type filePartType
203 ) {
204 return new MultifileClassFacadeContext(descriptor, this, multifileClassType, filePartType);
205 }
206
207 @NotNull
208 public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind, GenerationState state) {
209 return new ClassContext(state.getTypeMapper(), descriptor, kind, this, null);
210 }
211
212 @NotNull
213 public ClassContext intoAnonymousClass(@NotNull ClassDescriptor descriptor, @NotNull ExpressionCodegen codegen, @NotNull OwnerKind ownerKind) {
214 return new AnonymousClassContext(codegen.getState().getTypeMapper(), descriptor, ownerKind, this, codegen);
215 }
216
217 @NotNull
218 public MethodContext intoFunction(FunctionDescriptor descriptor) {
219 return new MethodContext(descriptor, getContextKind(), this, null, false);
220 }
221
222 @NotNull
223 public MethodContext intoInlinedLambda(FunctionDescriptor descriptor) {
224 return new MethodContext(descriptor, getContextKind(), this, null, true);
225 }
226
227 @NotNull
228 public ConstructorContext intoConstructor(@NotNull ConstructorDescriptor descriptor) {
229 return new ConstructorContext(descriptor, getContextKind(), this, closure);
230 }
231
232 // SCRIPT: generate into script, move to ScriptingUtil
233 @NotNull
234 public ScriptContext intoScript(
235 @NotNull ScriptDescriptor script,
236 @NotNull List<ScriptDescriptor> earlierScripts,
237 @NotNull ClassDescriptor classDescriptor
238 ) {
239 return new ScriptContext(script, earlierScripts, classDescriptor, OwnerKind.IMPLEMENTATION, this, closure);
240 }
241
242 @NotNull
243 public ClosureContext intoClosure(
244 @NotNull FunctionDescriptor funDescriptor,
245 @NotNull LocalLookup localLookup,
246 @NotNull JetTypeMapper typeMapper
247 ) {
248 return new ClosureContext(typeMapper, funDescriptor, this, localLookup);
249 }
250
251 @Nullable
252 public CodegenContext getParentContext() {
253 return parentContext;
254 }
255
256 public ClassDescriptor getEnclosingClass() {
257 CodegenContext cur = getParentContext();
258 while (cur != null && !(cur.getContextDescriptor() instanceof ClassDescriptor)) {
259 cur = cur.getParentContext();
260 }
261
262 return cur == null ? null : (ClassDescriptor) cur.getContextDescriptor();
263 }
264
265 @Nullable
266 public CodegenContext findParentContextWithDescriptor(DeclarationDescriptor descriptor) {
267 CodegenContext c = this;
268 while (c != null) {
269 if (c.getContextDescriptor() == descriptor) break;
270 c = c.getParentContext();
271 }
272 return c;
273 }
274
275 @NotNull
276 public <D extends CallableMemberDescriptor> D getAccessor(@NotNull D descriptor, @Nullable KtSuperExpression superCallExpression) {
277 return getAccessor(descriptor, false, null, superCallExpression);
278 }
279
280 @SuppressWarnings("unchecked")
281 @NotNull
282 public <D extends CallableMemberDescriptor> D getAccessor(
283 @NotNull D possiblySubstitutedDescriptor,
284 boolean isForBackingFieldInOuterClass,
285 @Nullable KotlinType delegateType,
286 @Nullable KtSuperExpression superCallExpression
287 ) {
288 if (accessors == null) {
289 accessors = new LinkedHashMap<AccessorKey, AccessorForCallableDescriptor<?>>();
290 }
291
292 D descriptor = (D) possiblySubstitutedDescriptor.getOriginal();
293 AccessorKey key = new AccessorKey(
294 descriptor, superCallExpression == null ? null : ExpressionCodegen.getSuperCallLabelTarget(this, superCallExpression)
295 );
296
297 AccessorForCallableDescriptor<?> accessor = accessors.get(key);
298 if (accessor != null) {
299 assert !isForBackingFieldInOuterClass ||
300 accessor instanceof AccessorForPropertyBackingFieldInOuterClass : "There is already exists accessor with isForBackingFieldInOuterClass = false in this context";
301 return (D) accessor;
302 }
303 String nameSuffix = SyntheticAccessorUtilKt.getAccessorNameSuffix(descriptor, key.superCallLabelTarget, isForBackingFieldInOuterClass);
304 if (descriptor instanceof SimpleFunctionDescriptor) {
305 accessor = new AccessorForFunctionDescriptor(
306 (FunctionDescriptor) descriptor, contextDescriptor, superCallExpression, nameSuffix
307 );
308 }
309 else if (descriptor instanceof ConstructorDescriptor) {
310 accessor = new AccessorForConstructorDescriptor((ConstructorDescriptor) descriptor, contextDescriptor, superCallExpression);
311 }
312 else if (descriptor instanceof PropertyDescriptor) {
313 if (isForBackingFieldInOuterClass) {
314 accessor = new AccessorForPropertyBackingFieldInOuterClass((PropertyDescriptor) descriptor, contextDescriptor,
315 delegateType, nameSuffix);
316 }
317 else {
318 accessor = new AccessorForPropertyDescriptor((PropertyDescriptor) descriptor, contextDescriptor,
319 superCallExpression, nameSuffix);
320 }
321 }
322 else {
323 throw new UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor);
324 }
325
326 accessors.put(key, accessor);
327
328 return (D) accessor;
329 }
330
331 @Nullable
332 protected StackValue.Field computeOuterExpression() {
333 return null;
334 }
335
336 public StackValue lookupInContext(DeclarationDescriptor d, @Nullable StackValue result, GenerationState state, boolean ignoreNoOuter) {
337 StackValue myOuter = null;
338 if (closure != null) {
339 EnclosedValueDescriptor answer = closure.getCaptureVariables().get(d);
340 if (answer != null) {
341 return StackValue.changeReceiverForFieldAndSharedVar(answer.getInnerValue(), result);
342 }
343
344 for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
345 if (aCase.isCase(d)) {
346 Type classType = state.getTypeMapper().mapType(getThisDescriptor());
347 StackValue.StackValueWithSimpleReceiver innerValue = aCase.innerValue(d, enclosingLocalLookup, state, closure, classType);
348 if (innerValue == null) {
349 break;
350 }
351 else {
352 return StackValue.changeReceiverForFieldAndSharedVar(innerValue, result);
353 }
354 }
355 }
356
357 myOuter = getOuterExpression(result, ignoreNoOuter, false);
358 result = myOuter;
359 }
360
361 StackValue resultValue;
362 if (myOuter != null && getEnclosingClass() == d) {
363 resultValue = result;
364 } else {
365 resultValue = parentContext != null ? parentContext.lookupInContext(d, result, state, ignoreNoOuter) : null;
366 }
367
368 if (myOuter != null && resultValue != null && !isStaticField(resultValue)) {
369 closure.setCaptureThis();
370 }
371 return resultValue;
372 }
373
374 @NotNull
375 public Collection<? extends AccessorForCallableDescriptor<?>> getAccessors() {
376 return accessors == null ? Collections.<AccessorForCallableDescriptor<CallableMemberDescriptor>>emptySet() : accessors.values();
377 }
378
379 @NotNull
380 public <D extends CallableMemberDescriptor> D accessibleDescriptor(
381 @NotNull D descriptor,
382 @Nullable KtSuperExpression superCallExpression
383 ) {
384 DeclarationDescriptor enclosing = descriptor.getContainingDeclaration();
385 if (!isInlineMethodContext() && (
386 !hasThisDescriptor() ||
387 enclosing == getThisDescriptor() ||
388 enclosing == getClassOrPackageParentContext().getContextDescriptor())) {
389 return descriptor;
390 }
391
392 return accessibleDescriptorIfNeeded(descriptor, superCallExpression);
393 }
394
395 public void recordSyntheticAccessorIfNeeded(@NotNull CallableMemberDescriptor descriptor, @NotNull BindingContext bindingContext) {
396 if (hasThisDescriptor() && Boolean.TRUE.equals(bindingContext.get(NEED_SYNTHETIC_ACCESSOR, descriptor))) {
397 // Not a super call because neither constructors nor private members can be targets of super calls
398 accessibleDescriptorIfNeeded(descriptor, /* superCallExpression = */ null);
399 }
400 }
401
402 private static int getAccessFlags(@NotNull CallableMemberDescriptor descriptor) {
403 int flag = getVisibilityAccessFlag(descriptor);
404 if (descriptor instanceof PropertyDescriptor) {
405 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
406
407 PropertySetterDescriptor setter = propertyDescriptor.getSetter();
408 PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
409
410 flag |= (getter == null ? 0 : getVisibilityAccessFlag(getter)) |
411 (setter == null ? 0 : getVisibilityAccessFlag(setter));
412 }
413 return flag;
414 }
415
416 @SuppressWarnings("unchecked")
417 @NotNull
418 private <D extends CallableMemberDescriptor> D accessibleDescriptorIfNeeded(
419 @NotNull D descriptor,
420 @Nullable KtSuperExpression superCallExpression
421 ) {
422 CallableMemberDescriptor unwrappedDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor);
423 int flag = getAccessFlags(unwrappedDescriptor);
424 if ((flag & ACC_PRIVATE) == 0 && (flag & ACC_PROTECTED) == 0) {
425 return descriptor;
426 }
427
428 DeclarationDescriptor enclosed = descriptor.getContainingDeclaration();
429 CodegenContext descriptorContext = findParentContextWithDescriptor(enclosed);
430 if (descriptorContext == null && DescriptorUtils.isCompanionObject(enclosed)) {
431 CodegenContext classContext = findParentContextWithDescriptor(enclosed.getContainingDeclaration());
432 if (classContext instanceof ClassContext) {
433 descriptorContext = ((ClassContext) classContext).getCompanionObjectContext();
434 }
435 }
436
437 if (descriptorContext == null) {
438 return descriptor;
439 }
440
441 if ((flag & ACC_PROTECTED) != 0) {
442 PackageFragmentDescriptor unwrappedDescriptorPackage =
443 DescriptorUtils.getParentOfType(unwrappedDescriptor, PackageFragmentDescriptor.class, false);
444 PackageFragmentDescriptor contextDescriptorPackage =
445 DescriptorUtils.getParentOfType(descriptorContext.getContextDescriptor(), PackageFragmentDescriptor.class, false);
446
447 boolean inSamePackage = contextDescriptorPackage != null && unwrappedDescriptorPackage != null &&
448 unwrappedDescriptorPackage.getFqName().equals(contextDescriptorPackage.getFqName());
449 if (inSamePackage) {
450 return descriptor;
451 }
452 }
453
454 return (D) descriptorContext.getAccessor(descriptor, superCallExpression);
455 }
456
457 private void addChild(@NotNull CodegenContext child) {
458 if (shouldAddChild(child)) {
459 if (childContexts == null) {
460 childContexts = new HashMap<DeclarationDescriptor, CodegenContext>();
461 }
462 DeclarationDescriptor childContextDescriptor = child.getContextDescriptor();
463 childContexts.put(childContextDescriptor, child);
464 }
465 }
466
467 protected boolean shouldAddChild(@NotNull CodegenContext child) {
468 return DescriptorUtils.isCompanionObject(child.contextDescriptor);
469 }
470
471 @Nullable
472 public CodegenContext findChildContext(@NotNull DeclarationDescriptor child) {
473 return childContexts == null ? null : childContexts.get(child);
474 }
475
476 private static boolean isStaticField(@NotNull StackValue value) {
477 return value instanceof StackValue.Field && ((StackValue.Field) value).isStaticPut;
478 }
479
480 private boolean isInsideInliningContext() {
481 CodegenContext current = this;
482 while (current != null) {
483 if (current instanceof MethodContext && ((MethodContext) current).isInlineFunction()) {
484 return true;
485 }
486 current = current.getParentContext();
487 }
488 return false;
489 }
490
491 private boolean isInlineMethodContext() {
492 return this instanceof MethodContext && ((MethodContext) this).isInlineFunction();
493 }
494 }