001    /*
002     * Copyright 2010-2015 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.kotlin.js.translate.expression;
018    
019    
020    import com.google.dart.compiler.backend.js.ast.*;
021    import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
022    import com.intellij.util.SmartList;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.js.translate.context.AliasingContext;
027    import org.jetbrains.kotlin.js.translate.context.Namer;
028    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
029    import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
030    import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
031    import org.jetbrains.kotlin.psi.JetDeclarationWithBody;
032    import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression;
033    import org.jetbrains.kotlin.resolve.DescriptorUtils;
034    import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage;
035    
036    import java.util.HashMap;
037    import java.util.List;
038    import java.util.Map;
039    
040    import static org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator.shouldBeInlined;
041    import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor;
042    import static org.jetbrains.kotlin.js.translate.utils.ErrorReportingUtils.message;
043    import static org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
044    import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.setParameters;
045    import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getIsEffectivelyPublicApi;
046    
047    public final class FunctionTranslator extends AbstractTranslator {
048        @NotNull
049        public static FunctionTranslator newInstance(@NotNull JetDeclarationWithBody function,
050                @NotNull TranslationContext context) {
051            return new FunctionTranslator(function, context);
052        }
053    
054        @NotNull
055        private TranslationContext functionBodyContext;
056        @NotNull
057        private final JetDeclarationWithBody functionDeclaration;
058        @Nullable
059        private JsName extensionFunctionReceiverName;
060        @NotNull
061        private final JsFunction functionObject;
062        @NotNull
063        private final FunctionDescriptor descriptor;
064    
065        private FunctionTranslator(@NotNull JetDeclarationWithBody functionDeclaration, @NotNull TranslationContext context) {
066            super(context);
067            this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration);
068            this.functionDeclaration = functionDeclaration;
069            this.functionObject = context().getFunctionObject(descriptor);
070            assert this.functionObject.getParameters().isEmpty()
071                    : message(descriptor, "Function " + functionDeclaration.getText() + " processed for the second time.");
072            //NOTE: it's important we compute the context before we start the computation
073            this.functionBodyContext = getFunctionBodyContext();
074        }
075    
076        @NotNull
077        private TranslationContext getFunctionBodyContext() {
078            AliasingContext aliasingContext;
079            if (isExtensionFunction()) {
080                DeclarationDescriptor expectedReceiverDescriptor = descriptor.getExtensionReceiverParameter();
081                assert expectedReceiverDescriptor != null;
082                extensionFunctionReceiverName = functionObject.getScope().declareName(Namer.getReceiverParameterName());
083                //noinspection ConstantConditions
084                aliasingContext = context().aliasingContext().inner(expectedReceiverDescriptor, extensionFunctionReceiverName.makeRef());
085            }
086            else {
087                aliasingContext = null;
088            }
089            return context().newFunctionBody(functionObject, aliasingContext);
090        }
091    
092        @NotNull
093        public JsPropertyInitializer translateAsEcma5PropertyDescriptor() {
094            generateFunctionObject();
095            return TranslationUtils.translateFunctionAsEcma5PropertyDescriptor(functionObject, descriptor, context());
096        }
097    
098        @NotNull
099        public JsPropertyInitializer translateAsMethod() {
100            JsName functionName = context().getNameForDescriptor(descriptor);
101            generateFunctionObject();
102    
103            if (shouldBeInlined(descriptor) && getIsEffectivelyPublicApi(descriptor)) {
104                InlineMetadata metadata = InlineMetadata.compose(functionObject, descriptor);
105                return new JsPropertyInitializer(functionName.makeRef(), metadata.getFunctionWithMetadata());
106            }
107    
108            return new JsPropertyInitializer(functionName.makeRef(), functionObject);
109        }
110    
111        private void generateFunctionObject() {
112            setParameters(functionObject, translateParameters());
113            translateBody();
114        }
115    
116        private void translateBody() {
117            if (!functionDeclaration.hasBody()) {
118                assert descriptor instanceof ConstructorDescriptor || descriptor.getModality().equals(Modality.ABSTRACT);
119                return;
120            }
121            functionObject.getBody().getStatements().addAll(translateFunctionBody(descriptor, functionDeclaration, functionBodyContext).getStatements());
122        }
123    
124        @NotNull
125        private List<JsParameter> translateParameters() {
126            List<JsParameter> jsParameters = new SmartList<JsParameter>();
127            Map<DeclarationDescriptor, JsExpression> aliases = new HashMap<DeclarationDescriptor, JsExpression>();
128    
129            for (TypeParameterDescriptor type : descriptor.getTypeParameters()) {
130                if (type.isReified()) {
131                    String suggestedName = Namer.isInstanceSuggestedName(type);
132                    JsName paramName = functionObject.getScope().declareName(suggestedName);
133                    jsParameters.add(new JsParameter(paramName));
134                    aliases.put(type, paramName.makeRef());
135                }
136            }
137    
138            functionBodyContext = functionBodyContext.innerContextWithDescriptorsAliased(aliases);
139    
140            if (extensionFunctionReceiverName == null && descriptor.getValueParameters().isEmpty()) {
141                return jsParameters;
142            }
143    
144            mayBeAddThisParameterForExtensionFunction(jsParameters);
145            addParameters(jsParameters, descriptor, context());
146            return jsParameters;
147        }
148    
149        public static void addParameters(List<JsParameter> list, FunctionDescriptor descriptor, TranslationContext context) {
150            for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) {
151                JsParameter jsParameter = new JsParameter(context.getNameForDescriptor(valueParameter));
152                MetadataProperties.setHasDefaultValue(jsParameter, DescriptorUtilPackage.hasDefaultValue(valueParameter));
153                list.add(jsParameter);
154            }
155        }
156    
157        private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
158            if (isExtensionFunction()) {
159                assert extensionFunctionReceiverName != null;
160                jsParameters.add(new JsParameter(extensionFunctionReceiverName));
161            }
162        }
163    
164        private boolean isExtensionFunction() {
165            return DescriptorUtils.isExtension(descriptor) && !(functionDeclaration instanceof JetFunctionLiteralExpression);
166        }
167    }