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.initializer; 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.ConstructorDescriptor; 024 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 025 import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; 026 import org.jetbrains.jet.lang.psi.JetClassOrObject; 027 import org.jetbrains.jet.lang.psi.JetDelegationSpecifier; 028 import org.jetbrains.jet.lang.psi.JetDelegatorToSuperCall; 029 import org.jetbrains.jet.lang.psi.JetParameter; 030 import org.jetbrains.jet.lang.types.JetType; 031 import org.jetbrains.jet.lexer.JetTokens; 032 import org.jetbrains.k2js.translate.context.Namer; 033 import org.jetbrains.k2js.translate.context.TranslationContext; 034 import org.jetbrains.k2js.translate.general.AbstractTranslator; 035 036 import java.util.ArrayList; 037 import java.util.Collections; 038 import java.util.List; 039 040 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType; 041 import static org.jetbrains.k2js.translate.utils.BindingUtils.*; 042 import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement; 043 import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters; 044 import static org.jetbrains.k2js.translate.utils.TranslationUtils.getQualifiedReference; 045 import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateArgumentList; 046 047 public final class ClassInitializerTranslator extends AbstractTranslator { 048 @NotNull 049 private final JetClassOrObject classDeclaration; 050 @NotNull 051 private final List<JsStatement> initializerStatements = new SmartList<JsStatement>(); 052 053 public ClassInitializerTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) { 054 // Note: it's important we use scope for class descriptor because anonymous function used in property initializers 055 // belong to the properties themselves 056 super(context.newDeclaration(getConstructor(context.bindingContext(), classDeclaration))); 057 this.classDeclaration = classDeclaration; 058 } 059 060 @NotNull 061 public JsFunction generateInitializeMethod() { 062 //TODO: it's inconsistent that we have scope for class and function for constructor, currently have problems implementing better way 063 ConstructorDescriptor primaryConstructor = getConstructor(bindingContext(), classDeclaration); 064 JsFunction result = context().getFunctionObject(primaryConstructor); 065 //NOTE: while we translate constructor parameters we also add property initializer statements 066 // for properties declared as constructor parameters 067 result.getParameters().addAll(translatePrimaryConstructorParameters()); 068 mayBeAddCallToSuperMethod(result); 069 new InitializerVisitor(initializerStatements).traverseContainer(classDeclaration, context()); 070 071 for (JsStatement statement : initializerStatements) { 072 if (statement instanceof JsBlock) { 073 result.getBody().getStatements().addAll(((JsBlock) statement).getStatements()); 074 } 075 else { 076 result.getBody().getStatements().add(statement); 077 } 078 } 079 080 return result; 081 } 082 083 @NotNull 084 public JsExpression generateEnumEntryInstanceCreation(@NotNull JetType enumClassType) { 085 JetDelegatorToSuperCall superCall = getSuperCall(); 086 List<JsExpression> arguments; 087 if (superCall != null) { 088 arguments = translateArguments(superCall); 089 } else { 090 arguments = Collections.emptyList(); 091 } 092 JsNameRef reference = getQualifiedReference(context(), getClassDescriptorForType(enumClassType)); 093 if(context().isEcma5()) { 094 return new JsInvocation(reference, arguments); 095 } else { 096 return new JsNew(reference, arguments); 097 } 098 } 099 100 private void mayBeAddCallToSuperMethod(JsFunction initializer) { 101 if (classDeclaration.hasModifier(JetTokens.ENUM_KEYWORD)) { 102 addCallToSuperMethod(Collections.<JsExpression>emptyList(), initializer); 103 return; 104 } 105 if (hasAncestorClass(bindingContext(), classDeclaration)) { 106 JetDelegatorToSuperCall superCall = getSuperCall(); 107 if (superCall == null) { 108 return; 109 } 110 addCallToSuperMethod(translateArguments(superCall), initializer); 111 } 112 } 113 114 private void addCallToSuperMethod(@NotNull List<JsExpression> arguments, JsFunction initializer) { 115 if (context().isEcma5()) { 116 JsName ref = context().scope().declareName(Namer.CALLEE_NAME); 117 initializer.setName(ref); 118 JsInvocation call = new JsInvocation(new JsNameRef("call", new JsNameRef("baseInitializer", ref.makeRef()))); 119 call.getArguments().add(JsLiteral.THIS); 120 call.getArguments().addAll(arguments); 121 initializerStatements.add(call.makeStmt()); 122 } 123 else { 124 JsName superMethodName = context().scope().declareName(Namer.superMethodName()); 125 initializerStatements.add(convertToStatement(new JsInvocation(new JsNameRef(superMethodName, JsLiteral.THIS), arguments))); 126 } 127 } 128 129 @NotNull 130 private List<JsExpression> translateArguments(@NotNull JetDelegatorToSuperCall superCall) { 131 return translateArgumentList(context(), superCall.getValueArguments()); 132 } 133 134 @Nullable 135 private JetDelegatorToSuperCall getSuperCall() { 136 JetDelegatorToSuperCall result = null; 137 for (JetDelegationSpecifier specifier : classDeclaration.getDelegationSpecifiers()) { 138 if (specifier instanceof JetDelegatorToSuperCall) { 139 result = (JetDelegatorToSuperCall) specifier; 140 } 141 } 142 return result; 143 } 144 145 @NotNull 146 List<JsParameter> translatePrimaryConstructorParameters() { 147 List<JetParameter> parameterList = getPrimaryConstructorParameters(classDeclaration); 148 List<JsParameter> result = new ArrayList<JsParameter>(); 149 for (JetParameter jetParameter : parameterList) { 150 result.add(translateParameter(jetParameter)); 151 } 152 return result; 153 } 154 155 @NotNull 156 private JsParameter translateParameter(@NotNull JetParameter jetParameter) { 157 DeclarationDescriptor parameterDescriptor = 158 getDescriptorForElement(bindingContext(), jetParameter); 159 JsName parameterName = context().getNameForDescriptor(parameterDescriptor); 160 JsParameter jsParameter = new JsParameter(parameterName); 161 mayBeAddInitializerStatementForProperty(jsParameter, jetParameter); 162 return jsParameter; 163 } 164 165 private void mayBeAddInitializerStatementForProperty(@NotNull JsParameter jsParameter, 166 @NotNull JetParameter jetParameter) { 167 PropertyDescriptor propertyDescriptor = 168 getPropertyDescriptorForConstructorParameter(bindingContext(), jetParameter); 169 if (propertyDescriptor == null) { 170 return; 171 } 172 JsNameRef initialValueForProperty = jsParameter.getName().makeRef(); 173 addInitializerOrPropertyDefinition(initialValueForProperty, propertyDescriptor); 174 } 175 176 private void addInitializerOrPropertyDefinition(@NotNull JsNameRef initialValue, @NotNull PropertyDescriptor propertyDescriptor) { 177 initializerStatements.add(InitializerUtils.generateInitializerForProperty(context(), propertyDescriptor, initialValue)); 178 } 179 }