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.js.translate.reference;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.descriptors.*;
022    import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation;
023    import org.jetbrains.kotlin.js.backend.ast.JsExpression;
024    import org.jetbrains.kotlin.js.backend.ast.JsNameRef;
025    import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
026    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
027    import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
028    import org.jetbrains.kotlin.psi.KtReferenceExpression;
029    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
030    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
031    import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
032    import org.jetbrains.kotlin.resolve.inline.InlineUtil;
033    
034    import static org.jetbrains.kotlin.js.translate.utils.InlineUtils.setInlineCallMetadata;
035    
036    public class VariableAccessTranslator extends AbstractTranslator implements AccessTranslator {
037        public static VariableAccessTranslator newInstance(
038                @NotNull TranslationContext context,
039                @NotNull KtReferenceExpression referenceExpression,
040                @Nullable JsExpression receiver
041        ) {
042            ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(referenceExpression, context.bindingContext());
043            if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
044                resolvedCall = ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall();
045            }
046            assert resolvedCall.getResultingDescriptor() instanceof VariableDescriptor;
047            return new VariableAccessTranslator(context, referenceExpression, (ResolvedCall<? extends VariableDescriptor>) resolvedCall,
048                                                receiver);
049        }
050    
051    
052        private final ResolvedCall<? extends VariableDescriptor> resolvedCall;
053        private final KtReferenceExpression referenceExpression;
054        private final JsExpression receiver;
055    
056        private VariableAccessTranslator(
057                @NotNull TranslationContext context,
058                @NotNull KtReferenceExpression referenceExpression,
059                @NotNull ResolvedCall<? extends VariableDescriptor> resolvedCall,
060                @Nullable JsExpression receiver
061        ) {
062            super(context);
063            this.referenceExpression = referenceExpression;
064            this.receiver = receiver;
065            this.resolvedCall = resolvedCall;
066        }
067    
068        @NotNull
069        @Override
070        public JsExpression translateAsGet() {
071            JsExpression e = CallTranslator.INSTANCE.translateGet(context(), resolvedCall, receiver);
072            CallableDescriptor original = resolvedCall.getResultingDescriptor().getOriginal();
073            if (original instanceof PropertyDescriptor) {
074                PropertyGetterDescriptor getter = ((PropertyDescriptor) original).getGetter();
075                if (InlineUtil.isInline(getter)) {
076                    if (e instanceof JsNameRef) {
077                        // Get was translated as a name reference
078                        setInlineCallMetadata((JsNameRef) e, referenceExpression, getter);
079                    } else {
080                        setInlineCallMetadata(e, referenceExpression, getter, context());
081                    }
082                }
083            }
084            return e;
085        }
086    
087        @NotNull
088        @Override
089        public JsExpression translateAsSet(@NotNull JsExpression setTo) {
090            JsExpression e = CallTranslator.INSTANCE.translateSet(context(), resolvedCall, setTo, receiver);
091            CallableDescriptor original = resolvedCall.getResultingDescriptor().getOriginal();
092            if (original instanceof PropertyDescriptor) {
093                PropertySetterDescriptor setter = ((PropertyDescriptor)original).getSetter();
094                if (InlineUtil.isInline(setter)) {
095                    if (e instanceof JsBinaryOperation && ((JsBinaryOperation) e).getOperator().isAssignment()) {
096                        // Set was translated as an assignment
097                        setInlineCallMetadata((JsNameRef) (((JsBinaryOperation) e).getArg1()), referenceExpression, setter);
098                    } else {
099                        setInlineCallMetadata(e, referenceExpression, setter, context());
100                    }
101                }
102            }
103            return e;
104        }
105    
106        @NotNull
107        @Override
108        public AccessTranslator getCached() {
109            JsExpression cachedReceiver = receiver != null ? context().cacheExpressionIfNeeded(receiver) : null;
110            return new CachedVariableAccessTranslator(context(), referenceExpression, resolvedCall, cachedReceiver);
111        }
112    
113        private static class CachedVariableAccessTranslator extends VariableAccessTranslator implements AccessTranslator {
114            public CachedVariableAccessTranslator(
115                    @NotNull TranslationContext context,
116                    @NotNull KtReferenceExpression referenceExpression,
117                    @NotNull  ResolvedCall<? extends VariableDescriptor> resolvedCall,
118                    @Nullable JsExpression cachedReceiver
119            ) {
120                super(context, referenceExpression, resolvedCall, cachedReceiver);
121            }
122    
123            @NotNull
124            @Override
125            public AccessTranslator getCached() {
126                return this;
127            }
128        }
129    }