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.k2js.translate.reference;
018    
019    import com.google.dart.compiler.backend.js.ast.JsExpression;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
023    import org.jetbrains.jet.lang.psi.JetArrayAccessExpression;
024    import org.jetbrains.jet.lang.psi.JetExpression;
025    import org.jetbrains.jet.lang.psi.ValueArgument;
026    import org.jetbrains.jet.lang.resolve.calls.model.ExpressionValueArgument;
027    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
029    import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
030    import org.jetbrains.k2js.translate.context.TemporaryVariable;
031    import org.jetbrains.k2js.translate.context.TranslationContext;
032    import org.jetbrains.k2js.translate.general.AbstractTranslator;
033    import org.jetbrains.k2js.translate.general.Translation;
034    import org.jetbrains.k2js.translate.utils.BindingUtils;
035    
036    import java.util.*;
037    
038    public class ArrayAccessTranslator extends AbstractTranslator implements AccessTranslator {
039    
040        /*package*/
041        static ArrayAccessTranslator newInstance(@NotNull JetArrayAccessExpression expression,
042                                                 @NotNull TranslationContext context) {
043            return new ArrayAccessTranslator(expression, context);
044        }
045    
046        @NotNull
047        private final JetArrayAccessExpression expression;
048    
049        protected ArrayAccessTranslator(@NotNull JetArrayAccessExpression expression,
050                                        @NotNull TranslationContext context) {
051            super(context);
052            this.expression = expression;
053        }
054    
055        @NotNull
056        @Override
057        public JsExpression translateAsGet() {
058            return translateAsGet(getArrayExpression());
059        }
060    
061        @NotNull
062        protected JsExpression translateAsGet(@NotNull JsExpression arrayExpression) {
063            return translateAsMethodCall(arrayExpression,  null);
064        }
065    
066        @NotNull
067        @Override
068        public JsExpression translateAsSet(@NotNull JsExpression setTo) {
069            return translateAsSet(getArrayExpression(), setTo);
070        }
071    
072        @NotNull
073        protected JsExpression translateAsSet(@NotNull JsExpression arrayExpression, @NotNull JsExpression toSetTo) {
074            return translateAsMethodCall(arrayExpression,  toSetTo);
075        }
076    
077        @NotNull
078        private JsExpression translateAsMethodCall(@NotNull JsExpression arrayExpression, @Nullable JsExpression toSetTo) {
079            boolean isGetter = toSetTo == null;
080            TranslationContext context = context();
081            ResolvedCall<FunctionDescriptor> resolvedCall = BindingUtils.getResolvedCallForArrayAccess(bindingContext(), expression, isGetter);
082            if (!isGetter) {
083                context = contextWithValueParameterAliasInArrayGetAccess(toSetTo);
084            }
085            return CallTranslator.INSTANCE$.translate(context, resolvedCall, arrayExpression);
086        }
087    
088        @NotNull
089        protected JsExpression getArrayExpression() {
090            JetExpression arrayExpression = expression.getArrayExpression();
091            assert arrayExpression != null : "Code with parsing errors shouldn't be translated";
092            return Translation.translateAsExpression(arrayExpression, context());
093        }
094    
095        // this is hack for a[b]++ -> a.set(b, a.get(b) + 1). Frontend generate fake expression for a.get(b) + 1.
096        @NotNull
097        private TranslationContext contextWithValueParameterAliasInArrayGetAccess(@NotNull JsExpression toSetTo) {
098            ResolvedCall<FunctionDescriptor> resolvedCall =
099                    BindingUtils.getResolvedCallForArrayAccess(bindingContext(), expression,  /*isGetter = */ false);
100    
101            List<ResolvedValueArgument> arguments = resolvedCall.getValueArgumentsByIndex();
102            if (arguments == null) {
103                throw new IllegalStateException("Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor());
104            }
105            ResolvedValueArgument lastArgument = arguments.get(arguments.size() - 1);
106            assert lastArgument instanceof ExpressionValueArgument:
107                    "Last argument of array-like setter must be ExpressionValueArgument: " + lastArgument;
108    
109            ValueArgument valueArgument = ((ExpressionValueArgument) lastArgument).getValueArgument();
110            assert valueArgument != null;
111    
112            JetExpression element = valueArgument.getArgumentExpression();
113            return context().innerContextWithAliasesForExpressions(Collections.singletonMap(element, toSetTo));
114        }
115    
116        @NotNull
117        @Override
118        public CachedAccessTranslator getCached() {
119            List<TemporaryVariable> temporaries = new ArrayList<TemporaryVariable>();
120            Map<JetExpression, JsExpression> aliases = new HashMap<JetExpression, JsExpression>();
121    
122            TemporaryVariable temporaryArrayExpression = context().declareTemporary(getArrayExpression());
123            temporaries.add(temporaryArrayExpression);
124    
125            for (JetExpression jetExpression : expression.getIndexExpressions()) {
126                JsExpression jsExpression = Translation.translateAsExpression(jetExpression, context());
127                TemporaryVariable temporaryVariable = context().declareTemporary(jsExpression);
128                temporaries.add(temporaryVariable);
129                aliases.put(jetExpression, temporaryVariable.reference());
130            }
131            return new CachedArrayAccessTranslator(expression, context().innerContextWithAliasesForExpressions(aliases),
132                                                   temporaryArrayExpression, temporaries);
133        }
134    
135        private static class CachedArrayAccessTranslator extends ArrayAccessTranslator implements CachedAccessTranslator {
136            @NotNull
137            private final TemporaryVariable temporaryArrayExpression;
138            @NotNull
139            private final List<TemporaryVariable> declaredTemporaries;
140    
141            protected CachedArrayAccessTranslator(
142                    @NotNull JetArrayAccessExpression expression,
143                    @NotNull TranslationContext context,
144                    @NotNull TemporaryVariable temporaryArrayExpression,
145                    @NotNull List<TemporaryVariable> temporaries
146            ) {
147                super(expression, context);
148                this.temporaryArrayExpression = temporaryArrayExpression;
149                declaredTemporaries = temporaries;
150            }
151    
152            @NotNull
153            @Override
154            protected JsExpression getArrayExpression() {
155                return temporaryArrayExpression.reference();
156            }
157    
158            @NotNull
159            @Override
160            public List<TemporaryVariable> declaredTemporaries() {
161                return declaredTemporaries;
162            }
163        }
164    }