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 }