From 8e1cfd38d4f67678268e880ded21f17daa0d99ae Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Fri, 10 Aug 2012 15:38:47 +0400 Subject: [PATCH] Introduce literal function translator. Adapted from https://github.com/develar/kotlin/commit/a9e0a42fb1347fa8e21c86b5a073ef8a7c873da0. --- .../k2js/translate/context/StaticContext.java | 12 +- .../translate/context/TranslationContext.java | 7 +- .../expression/ExpressionVisitor.java | 4 +- .../expression/LiteralFunctionTranslator.java | 136 ++++++++++++++++++ 4 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.java diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java index c14ac6d6546..022d7c428df 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java @@ -30,6 +30,7 @@ import org.jetbrains.k2js.config.EcmaVersion; import org.jetbrains.k2js.config.LibrarySourcesConfig; import org.jetbrains.k2js.translate.context.generator.Generator; import org.jetbrains.k2js.translate.context.generator.Rule; +import org.jetbrains.k2js.translate.expression.LiteralFunctionTranslator; import org.jetbrains.k2js.translate.intrinsic.Intrinsics; import org.jetbrains.k2js.translate.utils.AnnotationsUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils; @@ -81,12 +82,16 @@ public final class StaticContext { private final Generator qualifiers = new QualifierGenerator(); @NotNull private final Generator qualifierIsNull = new QualifierIsNullGenerator(); + @NotNull private final Map scopeToFunction = Maps.newHashMap(); @NotNull private final EcmaVersion ecmaVersion; + @NotNull + private final LiteralFunctionTranslator literalFunctionTranslator = new LiteralFunctionTranslator(); + //TODO: too many parameters in constructor private StaticContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext, @NotNull Namer namer, @NotNull Intrinsics intrinsics, @@ -100,6 +105,11 @@ public final class StaticContext { this.ecmaVersion = ecmaVersion; } + @NotNull + public LiteralFunctionTranslator getLiteralFunctionTranslator() { + return literalFunctionTranslator; + } + public boolean isEcma5() { return ecmaVersion == EcmaVersion.v5; } @@ -434,7 +444,7 @@ public final class StaticContext { } else if (result == qualifier && result.getIdent().equals("kotlin")) { // todo WebDemoExamples2Test#testBuilder, package "kotlin" from kotlin/js/js.libraries/src/stdlib/JUMaps.kt must be inlined - return qualifier; + // return qualifier; } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java index 48375f1061b..864d556373c 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java @@ -24,7 +24,7 @@ import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.k2js.config.EcmaVersion; +import org.jetbrains.k2js.translate.expression.LiteralFunctionTranslator; import org.jetbrains.k2js.translate.intrinsic.Intrinsics; import java.util.Map; @@ -185,6 +185,11 @@ public class TranslationContext { return aliasingContext; } + @NotNull + public LiteralFunctionTranslator literalFunctionTranslator() { + return staticContext.getLiteralFunctionTranslator(); + } + @NotNull public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) { return staticContext.getFunctionWithScope(descriptor); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java index c3f3ca0df72..b6ab1df3ce4 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -331,8 +331,8 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override @NotNull public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, - @NotNull TranslationContext context) { - return Translation.functionTranslator(expression, context).translateAsLiteral(); + @NotNull TranslationContext context) { + return context.literalFunctionTranslator().translate(expression); } @Override diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.java new file mode 100644 index 00000000000..26ce2334df0 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.java @@ -0,0 +1,136 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression; + +import com.google.dart.compiler.backend.js.ast.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; +import org.jetbrains.k2js.translate.LabelGenerator; +import org.jetbrains.k2js.translate.context.Namer; +import org.jetbrains.k2js.translate.context.TraceableThisAliasProvider; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.declaration.ClassTranslator; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor; +import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody; + +// todo easy incremental compiler implementation — generated functions should be inside corresponding class/namespace definition +public class LiteralFunctionTranslator { + private final List properties = new ArrayList(); + private final LabelGenerator labelGenerator = new LabelGenerator('f'); + private final JsNameRef containingVarRef = new JsNameRef("_f"); + + private TranslationContext rootContext; + + public void setRootContext(@NotNull TranslationContext rootContext) { + assert this.rootContext == null; + this.rootContext = rootContext; + JsName containingVarName = rootContext.scope().declareName(containingVarRef.getIdent()); + containingVarRef.resolve(containingVarName); + } + + public JsVar getDeclaration() { + return new JsVar(containingVarRef.getName(), properties.isEmpty() ? null : new JsObjectLiteral(properties, true)); + } + + public JsExpression translate(@NotNull JetFunctionLiteralExpression declaration) { + FunctionDescriptor descriptor = getFunctionDescriptor(rootContext.bindingContext(), declaration); + + JsFunction fun = createFunction(); + TranslationContext funContext; + boolean asInner; + if (descriptor.getContainingDeclaration() instanceof ConstructorDescriptor) { + // KT-2388 + asInner = true; + fun.setName(fun.getScope().declareName(Namer.CALLEE_NAME)); + ClassDescriptor classDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration(); + assert classDescriptor != null; + funContext = createThisTraceableContext(classDescriptor, fun, new JsNameRef("o", fun.getName().makeRef())); + } + else { + asInner = descriptor.getContainingDeclaration() instanceof NamespaceDescriptor; + funContext = rootContext.contextWithScope(fun); + } + + fun.getBody().getStatements().addAll(translateFunctionBody(descriptor, declaration, funContext).getStatements()); + + InnerFunctionTranslator translator = null; + if (!asInner) { + translator = new InnerFunctionTranslator(declaration, descriptor, funContext, fun); + if (translator.isLocalVariablesAffected()) { + asInner = true; + } + } + + if (asInner) { + FunctionTranslator.addParameters(fun.getParameters(), descriptor, funContext); + if (funContext.thisAliasProvider() instanceof TraceableThisAliasProvider) { + TraceableThisAliasProvider provider = (TraceableThisAliasProvider) funContext.thisAliasProvider(); + if (provider.wasThisCaptured()) { + return new JsInvocation(rootContext.namer().kotlin("assignOwner"), fun, JsLiteral.THIS); + } + else { + fun.setName(null); + } + } + + + return fun; + } + + return translate(translator, fun); + } + + private JsFunction createFunction() { + return new JsFunction(rootContext.scope(), new JsBlock()); + } + + public JsExpression translate(@NotNull ClassDescriptor containingClass, + @NotNull JetClassOrObject declaration, + @NotNull ClassDescriptor descriptor, + @NotNull ClassTranslator classTranslator) { + JsFunction fun = createFunction(); + JsName outerThisName = fun.getScope().declareName("$this"); + TranslationContext funContext = createThisTraceableContext(containingClass, fun, outerThisName.makeRef()); + + fun.getBody().getStatements().add(new JsReturn(classTranslator.translateClassOrObjectCreation(funContext))); + return translate(new InnerObjectTranslator(declaration, descriptor, funContext, fun), fun); + } + + private JsExpression translate(@NotNull InnerDeclarationTranslator translator, @NotNull JsFunction fun) { + JsNameRef nameRef = new JsNameRef(labelGenerator.generate(), containingVarRef); + properties.add(new JsPropertyInitializer(nameRef, fun)); + return translator.translate(nameRef); + } + + private TranslationContext createThisTraceableContext(@NotNull ClassDescriptor containingClass, + @NotNull JsFunction fun, + @NotNull JsNameRef thisRef) { + return rootContext.contextWithScope(fun, rootContext.aliasingContext().inner( + new TraceableThisAliasProvider(containingClass, thisRef))); + } +} +