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.declaration;
018
019 import com.google.dart.compiler.backend.js.ast.*;
020 import com.intellij.util.SmartList;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.descriptors.*;
024 import org.jetbrains.jet.lang.psi.JetProperty;
025 import org.jetbrains.jet.lang.psi.JetPropertyAccessor;
026 import org.jetbrains.jet.lang.resolve.BindingContext;
027 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028 import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
029 import org.jetbrains.k2js.translate.context.TranslationContext;
030 import org.jetbrains.k2js.translate.expression.FunctionTranslator;
031 import org.jetbrains.k2js.translate.general.AbstractTranslator;
032 import org.jetbrains.k2js.translate.general.Translation;
033 import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
034 import org.jetbrains.k2js.translate.utils.TranslationUtils;
035
036 import java.util.List;
037
038 import static org.jetbrains.k2js.translate.context.Namer.getDelegateNameRef;
039 import static org.jetbrains.k2js.translate.utils.TranslationUtils.*;
040
041 /**
042 * Translates single property /w accessors.
043 */
044 public final class PropertyTranslator extends AbstractTranslator {
045 @NotNull
046 private final PropertyDescriptor descriptor;
047 @Nullable
048 private final JetProperty declaration;
049
050 public static void translateAccessors(@NotNull PropertyDescriptor descriptor, @NotNull List<JsPropertyInitializer> result, @NotNull TranslationContext context) {
051 translateAccessors(descriptor, null, result, context);
052 }
053
054 public static void translateAccessors(@NotNull PropertyDescriptor descriptor,
055 @Nullable JetProperty declaration,
056 @NotNull List<JsPropertyInitializer> result,
057 @NotNull TranslationContext context) {
058 if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
059 new PropertyTranslator(descriptor, declaration, context).translate(result);
060 }
061 }
062
063 private PropertyTranslator(@NotNull PropertyDescriptor descriptor, @Nullable JetProperty declaration, @NotNull TranslationContext context) {
064 super(context);
065
066 this.descriptor = descriptor;
067 this.declaration = declaration;
068 }
069
070 private void translate(@NotNull List<JsPropertyInitializer> result) {
071 List<JsPropertyInitializer> to;
072 if (!JsDescriptorUtils.isExtension(descriptor)) {
073 to = new SmartList<JsPropertyInitializer>();
074 result.add(new JsPropertyInitializer(context().getNameForDescriptor(descriptor).makeRef(), new JsObjectLiteral(to, true)));
075 }
076 else {
077 to = result;
078 }
079
080 to.add(generateGetter());
081 if (descriptor.isVar()) {
082 to.add(generateSetter());
083 }
084 }
085
086 private JsPropertyInitializer generateGetter() {
087 if (hasCustomGetter()) {
088 return translateCustomAccessor(getCustomGetterDeclaration());
089 }
090 else {
091 return generateDefaultGetter();
092 }
093 }
094
095 private JsPropertyInitializer generateSetter() {
096 if (hasCustomSetter()) {
097 return translateCustomAccessor(getCustomSetterDeclaration());
098 }
099 else {
100 return generateDefaultSetter();
101 }
102 }
103
104 private boolean hasCustomGetter() {
105 return declaration != null && declaration.getGetter() != null && getCustomGetterDeclaration().getBodyExpression() != null;
106 }
107
108 private boolean hasCustomSetter() {
109 return declaration != null && declaration.getSetter() != null && getCustomSetterDeclaration().getBodyExpression() != null;
110 }
111
112 @NotNull
113 private JetPropertyAccessor getCustomGetterDeclaration() {
114 assert declaration != null;
115 JetPropertyAccessor getterDeclaration = declaration.getGetter();
116 assert getterDeclaration != null;
117 return getterDeclaration;
118 }
119
120 @NotNull
121 private JetPropertyAccessor getCustomSetterDeclaration() {
122 assert declaration != null;
123 JetPropertyAccessor setter = declaration.getSetter();
124 assert setter != null;
125 return setter;
126 }
127
128 @NotNull
129 private JsPropertyInitializer generateDefaultGetter() {
130 PropertyGetterDescriptor getterDescriptor = descriptor.getGetter();
131 assert getterDescriptor != null : "Getter descriptor should not be null";
132 return generateDefaultAccessor(getterDescriptor, generateDefaultGetterFunction(getterDescriptor));
133 }
134
135 private String getPropertyName() {
136 return descriptor.getName().asString();
137 }
138
139 @NotNull
140 private JsFunction generateDefaultGetterFunction(@NotNull PropertyGetterDescriptor getterDescriptor) {
141 JsExpression value;
142 ResolvedCall<FunctionDescriptor> delegatedCall = bindingContext().get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor);
143 if (delegatedCall != null) {
144 value = CallTranslator.instance$.translate(context(), delegatedCall, getDelegateNameRef(getPropertyName()));
145 } else {
146 value = backingFieldReference(context(), this.descriptor);
147 }
148 return simpleReturnFunction(context().getScopeForDescriptor(getterDescriptor.getContainingDeclaration()), value);
149 }
150
151 @NotNull
152 private JsPropertyInitializer generateDefaultSetter() {
153 PropertySetterDescriptor setterDescriptor = descriptor.getSetter();
154 assert setterDescriptor != null : "Setter descriptor should not be null";
155 return generateDefaultAccessor(setterDescriptor, generateDefaultSetterFunction(setterDescriptor));
156 }
157
158 @NotNull
159 private JsFunction generateDefaultSetterFunction(@NotNull PropertySetterDescriptor setterDescriptor) {
160 JsFunction fun = new JsFunction(context().getScopeForDescriptor(setterDescriptor.getContainingDeclaration()));
161
162 assert setterDescriptor.getValueParameters().size() == 1 : "Setter must have 1 parameter";
163 ValueParameterDescriptor valueParameterDescriptor = setterDescriptor.getValueParameters().get(0);
164 JsParameter defaultParameter = new JsParameter(fun.getScope().declareTemporary());
165 JsNameRef defaultParameterRef = defaultParameter.getName().makeRef();
166
167 fun.getParameters().add(defaultParameter);
168 TranslationContext contextWithAliased = context().innerContextWithAliased(valueParameterDescriptor, defaultParameterRef);
169
170 JsExpression setExpression;
171 ResolvedCall<FunctionDescriptor> delegatedCall = bindingContext().get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL,
172 setterDescriptor);
173 if (delegatedCall != null) {
174 setExpression = CallTranslator.instance$.translate(contextWithAliased, delegatedCall, getDelegateNameRef(getPropertyName()));
175 } else {
176 setExpression = assignmentToBackingField(contextWithAliased, descriptor, defaultParameterRef);
177 }
178 fun.setBody(new JsBlock(setExpression.makeStmt()));
179 return fun;
180 }
181
182 @NotNull
183 private JsPropertyInitializer generateDefaultAccessor(@NotNull PropertyAccessorDescriptor accessorDescriptor,
184 @NotNull JsFunction function) {
185 return TranslationUtils.translateFunctionAsEcma5PropertyDescriptor(function, accessorDescriptor, context());
186 }
187
188 @NotNull
189 private JsPropertyInitializer translateCustomAccessor(@NotNull JetPropertyAccessor expression) {
190 FunctionTranslator translator = Translation.functionTranslator(expression, context());
191 return translator.translateAsEcma5PropertyDescriptor();
192 }
193 }