/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.translate.reference;
import org.jetbrains.kotlin.js.backend.ast.
JsExpression;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.descriptors.
FunctionDescriptor;
import org.jetbrains.kotlin.js.translate.callTranslator.
CallTranslator;
import org.jetbrains.kotlin.js.translate.context.
TranslationContext;
import org.jetbrains.kotlin.js.translate.general.
AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.
Translation;
import org.jetbrains.kotlin.js.translate.utils.
BindingUtils;
import org.jetbrains.kotlin.psi.
KtArrayAccessExpression;
import org.jetbrains.kotlin.psi.
KtExpression;
import org.jetbrains.kotlin.psi.
ValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.
ExpressionValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.
ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.
ResolvedValueArgument;
import java.util.
Collections;
import java.util.
HashMap;
import java.util.
List;
import java.util.
Map;
public class
ArrayAccessTranslator extends
AbstractTranslator implements
AccessTranslator {
/*package*/
static
ArrayAccessTranslator newInstance(@
NotNull KtArrayAccessExpression expression,
@
NotNull TranslationContext context) {
return new
ArrayAccessTranslator(
expression,
context);
}
@
NotNull
private final
KtArrayAccessExpression expression;
private
ArrayAccessTranslator(@
NotNull KtArrayAccessExpression expression, @
NotNull TranslationContext context) {
super(
context);
this.
expression =
expression;
}
@
NotNull
@
Override
public
JsExpression translateAsGet() {
return
translateAsGet(
getArrayExpression());
}
@
NotNull
protected
JsExpression translateAsGet(@
NotNull JsExpression arrayExpression) {
return
translateAsMethodCall(
arrayExpression, null);
}
@
NotNull
@
Override
public
JsExpression translateAsSet(@
NotNull JsExpression setTo) {
return
translateAsMethodCall(
getArrayExpression(),
setTo);
}
@
NotNull
private
JsExpression translateAsMethodCall(@
NotNull JsExpression arrayExpression, @
Nullable JsExpression toSetTo) {
boolean
isGetter =
toSetTo == null;
TranslationContext context =
context();
ResolvedCall<
FunctionDescriptor>
resolvedCall =
BindingUtils.
getResolvedCallForArrayAccess(
bindingContext(),
expression,
isGetter);
if (!
isGetter) {
context =
contextWithValueParameterAliasInArrayGetAccess(
toSetTo);
}
return
CallTranslator.
translate(
context,
resolvedCall,
arrayExpression);
}
@
NotNull
protected
JsExpression getArrayExpression() {
KtExpression arrayExpression =
expression.
getArrayExpression();
assert
arrayExpression != null : "Code with parsing errors shouldn't be translated";
return
Translation.
translateAsExpression(
arrayExpression,
context());
}
// this is hack for a[b]++ -> a.set(b, a.get(b) + 1). Frontend generate fake expression for a.get(b) + 1.
@
NotNull
private
TranslationContext contextWithValueParameterAliasInArrayGetAccess(@
NotNull JsExpression toSetTo) {
ResolvedCall<
FunctionDescriptor>
resolvedCall =
BindingUtils.
getResolvedCallForArrayAccess(
bindingContext(),
expression, /*isGetter = */ false);
List<
ResolvedValueArgument>
arguments =
resolvedCall.
getValueArgumentsByIndex();
if (
arguments == null) {
throw new
IllegalStateException("Failed to arrange value arguments by index: " +
resolvedCall.
getResultingDescriptor());
}
ResolvedValueArgument lastArgument =
arguments.
get(
arguments.
size() - 1);
assert
lastArgument instanceof
ExpressionValueArgument:
"Last argument of array-like setter must be ExpressionValueArgument: " +
lastArgument;
ValueArgument valueArgument = ((
ExpressionValueArgument)
lastArgument).
getValueArgument();
assert
valueArgument != null;
KtExpression element =
valueArgument.
getArgumentExpression();
return
context().
innerContextWithAliasesForExpressions(
Collections.
singletonMap(
element,
toSetTo));
}
@
NotNull
@
Override
public
AccessTranslator getCached() {
Map<
KtExpression,
JsExpression>
aliases = new
HashMap<>();
JsExpression arrayExpression =
context().
cacheExpressionIfNeeded(
getArrayExpression());
aliases.
put(
expression.
getArrayExpression(),
arrayExpression);
for (
KtExpression ktExpression :
expression.
getIndexExpressions()) {
JsExpression jsExpression =
context().
cacheExpressionIfNeeded(
Translation.
translateAsExpression(
ktExpression,
context()));
aliases.
put(
ktExpression,
jsExpression);
}
return new
CachedArrayAccessTranslator(
expression,
context().
innerContextWithAliasesForExpressions(
aliases),
arrayExpression);
}
private static class
CachedArrayAccessTranslator extends
ArrayAccessTranslator {
@
NotNull
private final
JsExpression arrayExpression;
protected
CachedArrayAccessTranslator(
@
NotNull KtArrayAccessExpression expression,
@
NotNull TranslationContext context,
@
NotNull JsExpression arrayExpression
) {
super(
expression,
context);
this.
arrayExpression =
arrayExpression;
}
@
NotNull
@
Override
protected
JsExpression getArrayExpression() {
return
arrayExpression;
}
@
NotNull
@
Override
public
AccessTranslator getCached() {
return this;
}
}
}