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 import com.google.dart.compiler.backend.js.ast.*;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
022 import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
023 import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
024 import org.jetbrains.jet.lang.psi.JetClassOrObject;
025 import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
026 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027 import org.jetbrains.k2js.translate.LabelGenerator;
028 import org.jetbrains.k2js.translate.context.Namer;
029 import org.jetbrains.k2js.translate.context.TraceableThisAliasProvider;
030 import org.jetbrains.k2js.translate.context.TranslationContext;
031 import org.jetbrains.k2js.translate.declaration.ClassTranslator;
032
033 import java.util.ArrayList;
034 import java.util.List;
035
036 import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
037 import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
038 import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
039
040 // todo easy incremental compiler implementation - generated functions should be inside corresponding class/namespace definition
041 public class LiteralFunctionTranslator {
042 private final List<JsPropertyInitializer> properties = new ArrayList<JsPropertyInitializer>();
043 private final LabelGenerator labelGenerator = new LabelGenerator('f');
044 private final JsNameRef containingVarRef = new JsNameRef("_f");
045
046 private TranslationContext rootContext;
047
048 public void setRootContext(@NotNull TranslationContext rootContext) {
049 assert this.rootContext == null;
050 this.rootContext = rootContext;
051 JsName containingVarName = rootContext.scope().declareName(containingVarRef.getIdent());
052 containingVarRef.resolve(containingVarName);
053 }
054
055 public JsVar getDeclaration() {
056 return new JsVar(containingVarRef.getName(), properties.isEmpty() ? null : new JsObjectLiteral(properties, true));
057 }
058
059 public JsExpression translate(@NotNull JetFunctionLiteralExpression declaration) {
060 FunctionDescriptor descriptor = getFunctionDescriptor(rootContext.bindingContext(), declaration.getFunctionLiteral());
061
062 JsFunction fun = createFunction();
063 TranslationContext funContext;
064 boolean asInner;
065 if (descriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
066 // KT-2388
067 asInner = true;
068 fun.setName(fun.getScope().declareName(Namer.CALLEE_NAME));
069 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration();
070 assert classDescriptor != null;
071 funContext = createThisTraceableContext(classDescriptor, fun, new JsNameRef("o", fun.getName().makeRef()));
072 }
073 else {
074 asInner = DescriptorUtils.isTopLevelDeclaration(descriptor);
075 funContext = rootContext.contextWithScope(fun);
076 }
077
078 fun.getBody().getStatements().addAll(translateFunctionBody(descriptor, declaration.getFunctionLiteral(), funContext).getStatements());
079
080 InnerFunctionTranslator translator = null;
081 if (!asInner) {
082 translator = new InnerFunctionTranslator(declaration, descriptor, funContext, fun);
083 if (translator.isLocalVariablesAffected()) {
084 asInner = true;
085 }
086 }
087
088 if (asInner) {
089 FunctionTranslator.addParameters(fun.getParameters(), descriptor, funContext);
090 if (funContext.thisAliasProvider() instanceof TraceableThisAliasProvider) {
091 TraceableThisAliasProvider provider = (TraceableThisAliasProvider) funContext.thisAliasProvider();
092 if (provider.wasThisCaptured()) {
093 return new JsInvocation(rootContext.namer().kotlin("assignOwner"), fun, JsLiteral.THIS);
094 }
095 else {
096 fun.setName(null);
097 }
098 }
099
100
101 return fun;
102 }
103
104 return translate(translator, fun);
105 }
106
107 private JsFunction createFunction() {
108 return new JsFunction(rootContext.scope(), new JsBlock());
109 }
110
111 public JsExpression translate(@NotNull ClassDescriptor containingClass,
112 @NotNull JetClassOrObject declaration,
113 @NotNull ClassDescriptor descriptor,
114 @NotNull ClassTranslator classTranslator) {
115 JsFunction fun = createFunction();
116 JsName outerThisName = fun.getScope().declareName("$this");
117 TranslationContext funContext = createThisTraceableContext(containingClass, fun, outerThisName.makeRef());
118
119 fun.getBody().getStatements().add(new JsReturn(classTranslator.translateClassOrObjectCreation(funContext)));
120 return translate(new InnerObjectTranslator(declaration, descriptor, funContext, fun), fun);
121 }
122
123 private JsExpression translate(@NotNull InnerDeclarationTranslator translator, @NotNull JsFunction fun) {
124 JsNameRef nameRef = new JsNameRef(labelGenerator.generate(), containingVarRef);
125 properties.add(new JsPropertyInitializer(nameRef, fun));
126 return translator.translate(nameRef);
127 }
128
129 private TranslationContext createThisTraceableContext(@NotNull ClassDescriptor containingClass,
130 @NotNull JsFunction fun,
131 @NotNull JsNameRef thisRef) {
132 return rootContext.contextWithScope(fun, rootContext.aliasingContext().inner(
133 new TraceableThisAliasProvider(containingClass, thisRef)));
134 }
135 }
136