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