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.expression; 018 019 020import com.google.dart.compiler.backend.js.ast.JsFunction; 021import com.google.dart.compiler.backend.js.ast.JsName; 022import com.google.dart.compiler.backend.js.ast.JsParameter; 023import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; 024import org.jetbrains.annotations.NotNull; 025import org.jetbrains.annotations.Nullable; 026import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 027import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; 028import org.jetbrains.jet.lang.descriptors.Modality; 029import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; 030import org.jetbrains.jet.lang.psi.JetDeclarationWithBody; 031import org.jetbrains.jet.lang.psi.JetExpression; 032import org.jetbrains.jet.lang.psi.JetFunctionLiteral; 033import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; 034import org.jetbrains.k2js.translate.context.Namer; 035import org.jetbrains.k2js.translate.context.TranslationContext; 036import org.jetbrains.k2js.translate.general.AbstractTranslator; 037import org.jetbrains.k2js.translate.utils.JsDescriptorUtils; 038import org.jetbrains.k2js.translate.utils.TranslationUtils; 039 040import java.util.ArrayList; 041import java.util.List; 042 043import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor; 044import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message; 045import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody; 046import static org.jetbrains.k2js.translate.utils.JsAstUtils.setParameters; 047import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor; 048 049public final class FunctionTranslator extends AbstractTranslator { 050 @NotNull 051 public static FunctionTranslator newInstance(@NotNull JetDeclarationWithBody function, 052 @NotNull TranslationContext context) { 053 return new FunctionTranslator(function, context); 054 } 055 056 @NotNull 057 private final TranslationContext functionBodyContext; 058 @NotNull 059 private final JetDeclarationWithBody functionDeclaration; 060 @Nullable 061 private JsName extensionFunctionReceiverName; 062 @NotNull 063 private final JsFunction functionObject; 064 @NotNull 065 private final FunctionDescriptor descriptor; 066 067 private FunctionTranslator(@NotNull JetDeclarationWithBody functionDeclaration, 068 @NotNull TranslationContext context) { 069 super(context); 070 this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration); 071 this.functionDeclaration = functionDeclaration; 072 this.functionObject = context().getFunctionObject(descriptor); 073 assert this.functionObject.getParameters().isEmpty() 074 : message(bindingContext(), descriptor, "Function " + functionDeclaration.getText() + " processed for the second time."); 075 //NOTE: it's important we compute the context before we start the computation 076 this.functionBodyContext = getFunctionBodyContext(); 077 } 078 079 @NotNull 080 private TranslationContext getFunctionBodyContext() { 081 if (isExtensionFunction()) { 082 return getFunctionBodyContextForExtensionFunction(); 083 } 084 return getContextWithFunctionBodyBlock(); 085 } 086 087 @NotNull 088 private TranslationContext getFunctionBodyContextForExtensionFunction() { 089 TranslationContext contextWithFunctionBodyBlock = getContextWithFunctionBodyBlock(); 090 extensionFunctionReceiverName = contextWithFunctionBodyBlock.scope().declareName(Namer.getReceiverParameterName()); 091 DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor); 092 assert expectedReceiverDescriptor != null; 093 return contextWithFunctionBodyBlock.innerContextWithThisAliased(expectedReceiverDescriptor, extensionFunctionReceiverName); 094 } 095 096 @NotNull 097 private TranslationContext getContextWithFunctionBodyBlock() { 098 return context().newDeclaration(functionDeclaration).innerBlock(functionObject.getBody()); 099 } 100 101 @NotNull 102 public JsFunction translateAsLocalFunction() { 103 JsName functionName = context().getNameForElement(functionDeclaration); 104 generateFunctionObject(); 105 functionObject.setName(functionName); 106 return functionObject; 107 } 108 109 @NotNull 110 public JsPropertyInitializer translateAsEcma5PropertyDescriptor() { 111 generateFunctionObject(); 112 return TranslationUtils.translateFunctionAsEcma5PropertyDescriptor(functionObject, descriptor, context()); 113 } 114 115 @NotNull 116 public JsPropertyInitializer translateAsMethod() { 117 JsName functionName = context().getNameForElement(functionDeclaration); 118 generateFunctionObject(); 119 return new JsPropertyInitializer(functionName.makeRef(), functionObject); 120 } 121 122 private void generateFunctionObject() { 123 setParameters(functionObject, translateParameters()); 124 translateBody(); 125 } 126 127 private void translateBody() { 128 JetExpression jetBodyExpression = functionDeclaration.getBodyExpression(); 129 if (jetBodyExpression == null) { 130 assert descriptor.getModality().equals(Modality.ABSTRACT); 131 return; 132 } 133 functionObject.getBody().getStatements().addAll(translateFunctionBody(descriptor, functionDeclaration, functionBodyContext).getStatements()); 134 } 135 136 @NotNull 137 private List<JsParameter> translateParameters() { 138 List<JsParameter> jsParameters = new ArrayList<JsParameter>(); 139 mayBeAddThisParameterForExtensionFunction(jsParameters); 140 addParameters(jsParameters, descriptor, context()); 141 return jsParameters; 142 } 143 144 public static void addParameters(List<JsParameter> list, FunctionDescriptor descriptor, TranslationContext context) { 145 for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) { 146 list.add(new JsParameter(context.getNameForDescriptor(valueParameter))); 147 } 148 } 149 150 private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) { 151 if (isExtensionFunction()) { 152 assert extensionFunctionReceiverName != null; 153 jsParameters.add(new JsParameter(extensionFunctionReceiverName)); 154 } 155 } 156 157 private boolean isExtensionFunction() { 158 return JsDescriptorUtils.isExtension(descriptor) && !(functionDeclaration instanceof JetFunctionLiteral); 159 } 160}