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.initializer; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import org.jetbrains.annotations.NotNull; 021import org.jetbrains.annotations.Nullable; 022import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor; 023import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 024import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; 025import org.jetbrains.jet.lang.psi.JetClassOrObject; 026import org.jetbrains.jet.lang.psi.JetDelegationSpecifier; 027import org.jetbrains.jet.lang.psi.JetDelegatorToSuperCall; 028import org.jetbrains.jet.lang.psi.JetParameter; 029import org.jetbrains.k2js.translate.context.Namer; 030import org.jetbrains.k2js.translate.context.TranslationContext; 031import org.jetbrains.k2js.translate.general.AbstractTranslator; 032 033import java.util.ArrayList; 034import java.util.List; 035 036import static org.jetbrains.k2js.translate.utils.BindingUtils.*; 037import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement; 038import static org.jetbrains.k2js.translate.utils.JsAstUtils.setParameters; 039import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters; 040import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateArgumentList; 041 042public final class ClassInitializerTranslator extends AbstractTranslator { 043 @NotNull 044 private final JetClassOrObject classDeclaration; 045 @NotNull 046 private final List<JsStatement> initializerStatements = new ArrayList<JsStatement>(); 047 048 public ClassInitializerTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) { 049 // Note: it's important we use scope for class descriptor because anonymous function used in property initializers 050 // belong to the properties themselves 051 super(context.newDeclaration(getConstructor(context.bindingContext(), classDeclaration))); 052 this.classDeclaration = classDeclaration; 053 } 054 055 @NotNull 056 public JsFunction generateInitializeMethod() { 057 //TODO: it's inconsistent that we have scope for class and function for constructor, currently have problems implementing better way 058 ConstructorDescriptor primaryConstructor = getConstructor(bindingContext(), classDeclaration); 059 JsFunction result = context().getFunctionObject(primaryConstructor); 060 //NOTE: while we translate constructor parameters we also add property initializer statements 061 // for properties declared as constructor parameters 062 setParameters(result, translatePrimaryConstructorParameters()); 063 mayBeAddCallToSuperMethod(result); 064 (new InitializerVisitor(initializerStatements)).traverseClass(classDeclaration, context()); 065 result.getBody().getStatements().addAll(initializerStatements); 066 return result; 067 } 068 069 private void mayBeAddCallToSuperMethod(JsFunction initializer) { 070 if (hasAncestorClass(bindingContext(), classDeclaration)) { 071 JetDelegatorToSuperCall superCall = getSuperCall(); 072 if (superCall == null) return; 073 addCallToSuperMethod(superCall, initializer); 074 } 075 } 076 077 private void addCallToSuperMethod(@NotNull JetDelegatorToSuperCall superCall, JsFunction initializer) { 078 List<JsExpression> arguments = translateArguments(superCall); 079 080 //TODO: can be problematic to maintain 081 if (context().isEcma5()) { 082 JsName ref = context().scope().declareName(Namer.CALLEE_NAME); 083 initializer.setName(ref); 084 JsInvocation call = new JsInvocation(new JsNameRef("call", new JsNameRef("baseInitializer", ref.makeRef()))); 085 call.getArguments().add(JsLiteral.THIS); 086 call.getArguments().addAll(arguments); 087 initializerStatements.add(call.makeStmt()); 088 } 089 else { 090 JsName superMethodName = context().scope().declareName(Namer.superMethodName()); 091 initializerStatements.add(convertToStatement(new JsInvocation(new JsNameRef(superMethodName, JsLiteral.THIS), arguments))); 092 } 093 } 094 095 @NotNull 096 private List<JsExpression> translateArguments(@NotNull JetDelegatorToSuperCall superCall) { 097 return translateArgumentList(context(), superCall.getValueArguments()); 098 } 099 100 @Nullable 101 private JetDelegatorToSuperCall getSuperCall() { 102 JetDelegatorToSuperCall result = null; 103 for (JetDelegationSpecifier specifier : classDeclaration.getDelegationSpecifiers()) { 104 if (specifier instanceof JetDelegatorToSuperCall) { 105 result = (JetDelegatorToSuperCall) specifier; 106 } 107 } 108 return result; 109 } 110 111 @NotNull 112 List<JsParameter> translatePrimaryConstructorParameters() { 113 List<JetParameter> parameterList = getPrimaryConstructorParameters(classDeclaration); 114 List<JsParameter> result = new ArrayList<JsParameter>(); 115 for (JetParameter jetParameter : parameterList) { 116 result.add(translateParameter(jetParameter)); 117 } 118 return result; 119 } 120 121 @NotNull 122 private JsParameter translateParameter(@NotNull JetParameter jetParameter) { 123 DeclarationDescriptor parameterDescriptor = 124 getDescriptorForElement(bindingContext(), jetParameter); 125 JsName parameterName = context().getNameForDescriptor(parameterDescriptor); 126 JsParameter jsParameter = new JsParameter(parameterName); 127 mayBeAddInitializerStatementForProperty(jsParameter, jetParameter); 128 return jsParameter; 129 } 130 131 private void mayBeAddInitializerStatementForProperty(@NotNull JsParameter jsParameter, 132 @NotNull JetParameter jetParameter) { 133 PropertyDescriptor propertyDescriptor = 134 getPropertyDescriptorForConstructorParameter(bindingContext(), jetParameter); 135 if (propertyDescriptor == null) { 136 return; 137 } 138 JsNameRef initialValueForProperty = jsParameter.getName().makeRef(); 139 addInitializerOrPropertyDefinition(initialValueForProperty, propertyDescriptor); 140 } 141 142 private void addInitializerOrPropertyDefinition(@NotNull JsNameRef initialValue, @NotNull PropertyDescriptor propertyDescriptor) { 143 initializerStatements.add(InitializerUtils.generateInitializerForProperty(context(), propertyDescriptor, initialValue)); 144 } 145}