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 019import com.google.dart.compiler.backend.js.ast.*; 020import org.jetbrains.annotations.NotNull; 021import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 022import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor; 023import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; 024import org.jetbrains.jet.lang.psi.JetClassOrObject; 025import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; 026import org.jetbrains.jet.lang.resolve.DescriptorUtils; 027import org.jetbrains.k2js.translate.LabelGenerator; 028import org.jetbrains.k2js.translate.context.Namer; 029import org.jetbrains.k2js.translate.context.TraceableThisAliasProvider; 030import org.jetbrains.k2js.translate.context.TranslationContext; 031import org.jetbrains.k2js.translate.declaration.ClassTranslator; 032 033import java.util.ArrayList; 034import java.util.List; 035 036import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar; 037import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor; 038import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody; 039 040// todo easy incremental compiler implementation - generated functions should be inside corresponding class/namespace definition 041public 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