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