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