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 017package org.jetbrains.k2js.translate.declaration; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import com.intellij.util.SmartList; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.annotations.Nullable; 023import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor; 024import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; 025import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor; 026import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor; 027import org.jetbrains.jet.lang.psi.JetProperty; 028import org.jetbrains.jet.lang.psi.JetPropertyAccessor; 029import org.jetbrains.k2js.translate.context.TranslationContext; 030import org.jetbrains.k2js.translate.expression.FunctionTranslator; 031import org.jetbrains.k2js.translate.general.AbstractTranslator; 032import org.jetbrains.k2js.translate.general.Translation; 033import org.jetbrains.k2js.translate.utils.JsDescriptorUtils; 034import org.jetbrains.k2js.translate.utils.TranslationUtils; 035 036import java.util.List; 037 038import static org.jetbrains.k2js.translate.utils.TranslationUtils.assignmentToBackingField; 039import static org.jetbrains.k2js.translate.utils.TranslationUtils.backingFieldReference; 040 041/** 042 * Translates single property /w accessors. 043 */ 044public 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}