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