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.PropertyAccessorDescriptor;
024 import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
025 import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor;
026 import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor;
027 import org.jetbrains.jet.lang.psi.JetProperty;
028 import org.jetbrains.jet.lang.psi.JetPropertyAccessor;
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.utils.TranslationUtils.assignmentToBackingField;
039 import static org.jetbrains.k2js.translate.utils.TranslationUtils.backingFieldReference;
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 (context.isEcma5() && !JsDescriptorUtils.isAsPrivate(descriptor)) {
059 return;
060 }
061
062 new PropertyTranslator(descriptor, declaration, context).translate(result);
063 }
064
065 private PropertyTranslator(@NotNull PropertyDescriptor descriptor, @Nullable JetProperty declaration, @NotNull TranslationContext context) {
066 super(context);
067
068 this.descriptor = descriptor;
069 this.declaration = declaration;
070 }
071
072 private void translate(@NotNull List<JsPropertyInitializer> result) {
073 List<JsPropertyInitializer> to;
074 if (context().isEcma5() && !JsDescriptorUtils.isExtension(descriptor)) {
075 to = new SmartList<JsPropertyInitializer>();
076 result.add(new JsPropertyInitializer(context().nameToLiteral(descriptor), new JsObjectLiteral(to, true)));
077 }
078 else {
079 to = result;
080 }
081
082 to.add(generateGetter());
083 if (descriptor.isVar()) {
084 to.add(generateSetter());
085 }
086 }
087
088 private JsPropertyInitializer generateGetter() {
089 if (hasCustomGetter()) {
090 return translateCustomAccessor(getCustomGetterDeclaration());
091 }
092 else {
093 return generateDefaultGetter();
094 }
095 }
096
097 private JsPropertyInitializer generateSetter() {
098 if (hasCustomSetter()) {
099 return translateCustomAccessor(getCustomSetterDeclaration());
100 }
101 else {
102 return generateDefaultSetter();
103 }
104 }
105
106 private boolean hasCustomGetter() {
107 return declaration != null && declaration.getGetter() != null && getCustomGetterDeclaration().getBodyExpression() != null;
108 }
109
110 private boolean hasCustomSetter() {
111 return declaration != null && declaration.getSetter() != null && getCustomSetterDeclaration().getBodyExpression() != null;
112 }
113
114 @NotNull
115 private JetPropertyAccessor getCustomGetterDeclaration() {
116 assert declaration != null;
117 JetPropertyAccessor getterDeclaration = declaration.getGetter();
118 assert getterDeclaration != null;
119 return getterDeclaration;
120 }
121
122 @NotNull
123 private JetPropertyAccessor getCustomSetterDeclaration() {
124 assert declaration != null;
125 JetPropertyAccessor setter = declaration.getSetter();
126 assert setter != null;
127 return setter;
128 }
129
130 @NotNull
131 private JsPropertyInitializer generateDefaultGetter() {
132 PropertyGetterDescriptor getterDescriptor = descriptor.getGetter();
133 assert getterDescriptor != null : "Getter descriptor should not be null";
134 return generateDefaultAccessor(getterDescriptor, generateDefaultGetterFunction(getterDescriptor));
135 }
136
137 @NotNull
138 private JsFunction generateDefaultGetterFunction(@NotNull PropertyGetterDescriptor descriptor) {
139 JsFunction fun = new JsFunction(context().getScopeForDescriptor(descriptor.getContainingDeclaration()));
140 fun.setBody(new JsBlock(new JsReturn(backingFieldReference(context(), this.descriptor))));
141 return fun;
142 }
143
144 @NotNull
145 private JsPropertyInitializer generateDefaultSetter() {
146 PropertySetterDescriptor setterDescriptor = descriptor.getSetter();
147 assert setterDescriptor != null : "Setter descriptor should not be null";
148 return generateDefaultAccessor(setterDescriptor, generateDefaultSetterFunction(setterDescriptor));
149 }
150
151 @NotNull
152 private JsFunction generateDefaultSetterFunction(@NotNull PropertySetterDescriptor setterDescriptor) {
153 JsFunction fun = new JsFunction(context().getScopeForDescriptor(setterDescriptor.getContainingDeclaration()));
154 JsParameter defaultParameter = new JsParameter(propertyAccessContext(setterDescriptor).scope().declareTemporary());
155 fun.getParameters().add(defaultParameter);
156 fun.setBody(new JsBlock(assignmentToBackingField(context(), descriptor, defaultParameter.getName().makeRef()).makeStmt()));
157 return fun;
158 }
159
160 @NotNull
161 private JsPropertyInitializer generateDefaultAccessor(@NotNull PropertyAccessorDescriptor accessorDescriptor,
162 @NotNull JsFunction function) {
163 if (context().isEcma5()) {
164 return TranslationUtils.translateFunctionAsEcma5PropertyDescriptor(function, accessorDescriptor, context());
165 }
166 else {
167 return new JsPropertyInitializer(context().getNameForDescriptor(accessorDescriptor).makeRef(), function);
168 }
169 }
170
171 @NotNull
172 private TranslationContext propertyAccessContext(@NotNull PropertySetterDescriptor propertySetterDescriptor) {
173 return context().newDeclaration(propertySetterDescriptor);
174 }
175
176 @NotNull
177 private JsPropertyInitializer translateCustomAccessor(@NotNull JetPropertyAccessor expression) {
178 FunctionTranslator translator = Translation.functionTranslator(expression, context());
179 return context().isEcma5() ? translator.translateAsEcma5PropertyDescriptor() : translator.translateAsMethod();
180 }
181 }