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 ResolvedValueArgument lastArgument = arguments.get(arguments.size() - 1);
103 assert lastArgument instanceof ExpressionValueArgument: "Last argument of array-like setter must be ExpressionValueArgument";
104
105 ValueArgument valueArgument = ((ExpressionValueArgument) lastArgument).getValueArgument();
106 assert valueArgument != null;
107
108 JetExpression element = valueArgument.getArgumentExpression();
109 return context().innerContextWithAliasesForExpressions(Collections.singletonMap(element, toSetTo));
110 }
111
112 @NotNull
113 @Override
114 public CachedAccessTranslator getCached() {
115 List<TemporaryVariable> temporaries = new ArrayList<TemporaryVariable>();
116 Map<JetExpression, JsExpression> aliases = new HashMap<JetExpression, JsExpression>();
117
118 TemporaryVariable temporaryArrayExpression = context().declareTemporary(getArrayExpression());
119 temporaries.add(temporaryArrayExpression);
120
121 for (JetExpression jetExpression : expression.getIndexExpressions()) {
122 JsExpression jsExpression = Translation.translateAsExpression(jetExpression, context());
123 TemporaryVariable temporaryVariable = context().declareTemporary(jsExpression);
124 temporaries.add(temporaryVariable);
125 aliases.put(jetExpression, temporaryVariable.reference());
126 }
127 return new CachedArrayAccessTranslator(expression, context().innerContextWithAliasesForExpressions(aliases),
128 temporaryArrayExpression, temporaries);
129 }
130
131 private static class CachedArrayAccessTranslator extends ArrayAccessTranslator implements CachedAccessTranslator {
132 @NotNull
133 private final TemporaryVariable temporaryArrayExpression;
134 @NotNull
135 private final List<TemporaryVariable> declaredTemporaries;
136
137 protected CachedArrayAccessTranslator(
138 @NotNull JetArrayAccessExpression expression,
139 @NotNull TranslationContext context,
140 @NotNull TemporaryVariable temporaryArrayExpression,
141 @NotNull List<TemporaryVariable> temporaries
142 ) {
143 super(expression, context);
144 this.temporaryArrayExpression = temporaryArrayExpression;
145 declaredTemporaries = temporaries;
146 }
147
148 @NotNull
149 @Override
150 protected JsExpression getArrayExpression() {
151 return temporaryArrayExpression.reference();
152 }
153
154 @NotNull
155 @Override
156 public List<TemporaryVariable> declaredTemporaries() {
157 return declaredTemporaries;
158 }
159 }
160 }