Refactoring: refactor Aliaser into aliasing context. A very rough draft but test pass.

This commit is contained in:
Pavel V. Talanov
2012-03-21 22:26:15 +04:00
parent 91f488fdf8
commit fe53d20a88
11 changed files with 249 additions and 232 deletions
@@ -1,100 +0,0 @@
/*
* 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.context;
import com.google.dart.compiler.backend.js.ast.JsName;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class Aliaser {
public static Aliaser newInstance() {
return new Aliaser();
}
@NotNull
private final Map<DeclarationDescriptor, JsName> aliasesForDescriptors = new HashMap<DeclarationDescriptor, JsName>();
@NotNull
private final Map<DeclarationDescriptor, Stack<JsName>> aliasesForThis
= new HashMap<DeclarationDescriptor, Stack<JsName>>();
private Aliaser() {
}
@NotNull
public JsNameRef getAliasForThis(@NotNull DeclarationDescriptor descriptor) {
Stack<JsName> aliasStack = aliasesForThis.get(descriptor.getOriginal());
assert !aliasStack.empty();
JsName aliasName = aliasStack.peek();
assert aliasName != null : "This " + descriptor.getOriginal() + " doesn't have an alias.";
return aliasName.makeRef();
}
public void setAliasForThis(@NotNull DeclarationDescriptor descriptor, @NotNull JsName alias) {
Stack<JsName> aliasStack = aliasesForThis.get(descriptor.getOriginal());
if (aliasStack == null) {
aliasStack = new Stack<JsName>();
aliasesForThis.put(descriptor, aliasStack);
}
aliasStack.push(alias);
}
public void removeAliasForThis(@NotNull DeclarationDescriptor descriptor) {
Stack<JsName> aliasStack = aliasesForThis.get(descriptor.getOriginal());
assert !aliasStack.empty();
aliasStack.pop();
if (aliasStack.empty()) {
aliasesForThis.put(descriptor, null);
}
}
public boolean hasAliasForThis(@NotNull DeclarationDescriptor descriptor) {
Stack<JsName> aliasStack = aliasesForThis.get(descriptor.getOriginal());
if (aliasStack == null) {
return false;
}
return (!aliasStack.empty());
}
@NotNull
public JsName getAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
JsName alias = aliasesForDescriptors.get(declaration.getOriginal());
assert alias != null : "Use has alias for declaration to check.";
return alias;
}
public void setAliasForDescriptor(@NotNull DeclarationDescriptor declaration, @NotNull JsName alias) {
assert (!hasAliasForDeclaration(declaration.getOriginal())) : "This declaration already has an alias!";
aliasesForDescriptors.put(declaration.getOriginal(), alias);
}
public void removeAliasForDescriptor(@NotNull DeclarationDescriptor declaration) {
assert (hasAliasForDeclaration(declaration.getOriginal())) : "This declaration does not has an alias!";
aliasesForDescriptors.remove(declaration.getOriginal());
}
public boolean hasAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
return aliasesForDescriptors.containsKey(declaration.getOriginal());
}
}
@@ -0,0 +1,92 @@
/*
* 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.context;
import com.google.common.collect.Maps;
import com.google.dart.compiler.backend.js.ast.JsName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import java.util.Map;
/**
* @author Pavel Talanov
*/
public class AliasingContext {
private static AliasingContext ROOT = new AliasingContext(null) {
@Override
public JsName getAliasForThis(@NotNull DeclarationDescriptor descriptor) {
return null;
}
@Override
public JsName getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
return null;
}
};
public static AliasingContext getCleanContext() {
return new AliasingContext(ROOT);
}
@NotNull
private final Map<DeclarationDescriptor, JsName> aliasesForDescriptors = Maps.newHashMap();
@NotNull
private final Map<DeclarationDescriptor, JsName> aliasesForThis = Maps.newHashMap();
@Nullable
private final AliasingContext parent;
private AliasingContext(@Nullable AliasingContext parent) {
this.parent = parent;
}
@NotNull
public AliasingContext withThisAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsName alias) {
AliasingContext newContext = new AliasingContext(this);
assert !newContext.aliasesForThis.containsKey(correspondingDescriptor);
newContext.aliasesForThis.put(correspondingDescriptor, alias);
return newContext;
}
@NotNull
private AliasingContext getParent() {
assert parent != null;
return parent;
}
@Nullable
public JsName getAliasForThis(@NotNull DeclarationDescriptor descriptor) {
JsName alias = aliasesForThis.get(descriptor.getOriginal());
if (alias != null) {
return alias;
}
return getParent().getAliasForThis(descriptor);
}
@Nullable
public JsName getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
JsName alias = aliasesForDescriptors.get(descriptor.getOriginal());
if (alias != null) {
return alias;
}
return getParent().getAliasForDescriptor(descriptor);
}
}
@@ -48,13 +48,11 @@ public final class StaticContext {
JsProgram program = new JsProgram("main"); JsProgram program = new JsProgram("main");
JsRootScope jsRootScope = program.getRootScope(); JsRootScope jsRootScope = program.getRootScope();
Namer namer = Namer.newInstance(jsRootScope); Namer namer = Namer.newInstance(jsRootScope);
Aliaser aliaser = Aliaser.newInstance();
NamingScope scope = NamingScope.rootScope(jsRootScope); NamingScope scope = NamingScope.rootScope(jsRootScope);
Intrinsics intrinsics = Intrinsics.standardLibraryIntrinsics(library); Intrinsics intrinsics = Intrinsics.standardLibraryIntrinsics(library);
StandardClasses standardClasses = StandardClasses standardClasses =
StandardClasses.bindImplementations(namer.getKotlinScope()); StandardClasses.bindImplementations(namer.getKotlinScope());
return new StaticContext(program, bindingContext, aliaser, return new StaticContext(program, bindingContext, namer, intrinsics, standardClasses, scope);
namer, intrinsics, standardClasses, scope);
} }
@NotNull @NotNull
@@ -62,10 +60,6 @@ public final class StaticContext {
@NotNull @NotNull
private final BindingContext bindingContext; private final BindingContext bindingContext;
@NotNull
private final Aliaser aliaser;
@NotNull @NotNull
private final Namer namer; private final Namer namer;
@@ -90,14 +84,12 @@ public final class StaticContext {
private final Map<NamingScope, JsFunction> scopeToFunction = Maps.newHashMap(); private final Map<NamingScope, JsFunction> scopeToFunction = Maps.newHashMap();
//qTODO: too many parameters in constructor //TODO: too many parameters in constructor
private StaticContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext, private StaticContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext,
@NotNull Aliaser aliaser,
@NotNull Namer namer, @NotNull Intrinsics intrinsics, @NotNull Namer namer, @NotNull Intrinsics intrinsics,
@NotNull StandardClasses standardClasses, @NotNull NamingScope rootScope) { @NotNull StandardClasses standardClasses, @NotNull NamingScope rootScope) {
this.program = program; this.program = program;
this.bindingContext = bindingContext; this.bindingContext = bindingContext;
this.aliaser = aliaser;
this.namer = namer; this.namer = namer;
this.intrinsics = intrinsics; this.intrinsics = intrinsics;
this.rootScope = rootScope; this.rootScope = rootScope;
@@ -114,11 +106,6 @@ public final class StaticContext {
return bindingContext; return bindingContext;
} }
@NotNull
public Aliaser getAliaser() {
return aliaser;
}
@NotNull @NotNull
public Intrinsics getIntrinsics() { public Intrinsics getIntrinsics() {
return intrinsics; return intrinsics;
@@ -38,28 +38,35 @@ public final class TranslationContext {
private final DynamicContext dynamicContext; private final DynamicContext dynamicContext;
@NotNull @NotNull
private final StaticContext staticContext; private final StaticContext staticContext;
@NotNull
private final AliasingContext aliasingContext;
@NotNull @NotNull
public static TranslationContext rootContext(@NotNull StaticContext staticContext) { public static TranslationContext rootContext(@NotNull StaticContext staticContext) {
JsProgram program = staticContext.getProgram(); JsProgram program = staticContext.getProgram();
JsBlock globalBlock = program.getGlobalBlock(); JsBlock globalBlock = program.getGlobalBlock();
DynamicContext rootDynamicContext = DynamicContext.rootContext(staticContext.getRootScope(), globalBlock);
AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
return new TranslationContext(staticContext, return new TranslationContext(staticContext,
DynamicContext.rootContext(staticContext.getRootScope(), globalBlock)); rootDynamicContext, rootAliasingContext);
} }
private TranslationContext(@NotNull StaticContext staticContext, @NotNull DynamicContext dynamicContext) { private TranslationContext(@NotNull StaticContext staticContext,
@NotNull DynamicContext dynamicContext,
@NotNull AliasingContext context) {
this.dynamicContext = dynamicContext; this.dynamicContext = dynamicContext;
this.staticContext = staticContext; this.staticContext = staticContext;
aliasingContext = context;
} }
@NotNull @NotNull
public TranslationContext contextWithScope(@NotNull NamingScope newScope, @NotNull JsBlock block) { public TranslationContext contextWithScope(@NotNull NamingScope newScope, @NotNull JsBlock block) {
return new TranslationContext(staticContext, DynamicContext.newContext(newScope, block)); return new TranslationContext(staticContext, DynamicContext.newContext(newScope, block), aliasingContext);
} }
@NotNull @NotNull
public TranslationContext innerBlock(@NotNull JsBlock block) { public TranslationContext innerBlock(@NotNull JsBlock block) {
return new TranslationContext(staticContext, dynamicContext.innerBlock(block)); return new TranslationContext(staticContext, dynamicContext.innerBlock(block), aliasingContext);
} }
@NotNull @NotNull
@@ -73,6 +80,11 @@ public final class TranslationContext {
return contextWithScope(dynamicContext.getScope().innerScope(scope), block); return contextWithScope(dynamicContext.getScope().innerScope(scope), block);
} }
@NotNull
public TranslationContext innerContextWithThisAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsName alias) {
return new TranslationContext(staticContext, dynamicContext, aliasingContext.withThisAliased(correspondingDescriptor, alias));
}
@NotNull @NotNull
public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) { public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof CallableDescriptor) { if (descriptor instanceof CallableDescriptor) {
@@ -119,11 +131,6 @@ public final class TranslationContext {
return dynamicContext.declareTemporary(initExpression); return dynamicContext.declareTemporary(initExpression);
} }
@NotNull
public Aliaser aliaser() {
return staticContext.getAliaser();
}
@NotNull @NotNull
public Namer namer() { public Namer namer() {
return staticContext.getNamer(); return staticContext.getNamer();
@@ -144,6 +151,11 @@ public final class TranslationContext {
return dynamicContext.jsScope(); return dynamicContext.jsScope();
} }
@NotNull
public AliasingContext aliasingContext() {
return aliasingContext;
}
@NotNull @NotNull
public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) { public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
return staticContext.getFunctionWithScope(descriptor); return staticContext.getFunctionWithScope(descriptor);
@@ -16,6 +16,8 @@
package org.jetbrains.k2js.translate.declaration; package org.jetbrains.k2js.translate.declaration;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
@@ -32,9 +34,7 @@ import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.ClassSortingUtils; import org.jetbrains.k2js.translate.utils.ClassSortingUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getAllClassesDefinedInNamespace; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getAllClassesDefinedInNamespace;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
@@ -49,7 +49,7 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
@NotNull @NotNull
private final List<ClassDescriptor> descriptors; private final List<ClassDescriptor> descriptors;
@NotNull @NotNull
private final Map<JsName, JsName> localToGlobalClassName; private final BiMap<JsName, JsName> localToGlobalClassName;
@NotNull @NotNull
private final JsFunction dummyFunction; private final JsFunction dummyFunction;
@Nullable @Nullable
@@ -61,7 +61,7 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context); super(context);
this.descriptors = descriptors; this.descriptors = descriptors;
this.localToGlobalClassName = new HashMap<JsName, JsName>(); this.localToGlobalClassName = HashBiMap.create();
this.dummyFunction = new JsFunction(context.jsScope()); this.dummyFunction = new JsFunction(context.jsScope());
} }
@@ -69,7 +69,7 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
declarationsObject = context().jsScope().declareName(Namer.nameForClassesVariable()); declarationsObject = context().jsScope().declareName(Namer.nameForClassesVariable());
assert declarationsObject != null; assert declarationsObject != null;
declarationsStatement = declarationsStatement =
newVar(declarationsObject, generateDummyFunctionInvocation()); newVar(declarationsObject, generateDummyFunctionInvocation());
} }
@NotNull @NotNull
@@ -112,16 +112,9 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
for (JetClass jetClass : getClassDeclarations()) { for (JetClass jetClass : getClassDeclarations()) {
classDeclarations.add(generateDeclaration(jetClass)); classDeclarations.add(generateDeclaration(jetClass));
} }
removeAliases();
return classDeclarations; return classDeclarations;
} }
private void removeAliases() {
for (JetClass jetClass : getClassDeclarations()) {
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(bindingContext(), jetClass);
aliaser().removeAliasForDescriptor(descriptor);
}
}
@NotNull @NotNull
private List<JetClass> getClassDeclarations() { private List<JetClass> getClassDeclarations() {
@@ -136,7 +129,7 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
private JsStatement generateDeclaration(@NotNull JetClass declaration) { private JsStatement generateDeclaration(@NotNull JetClass declaration) {
JsName localClassName = generateLocalAlias(declaration); JsName localClassName = generateLocalAlias(declaration);
JsInvocation classDeclarationExpression = JsInvocation classDeclarationExpression =
Translation.translateClassDeclaration(declaration, context()); Translation.translateClassDeclaration(declaration, localToGlobalClassName.inverse(), context());
return newVar(localClassName, classDeclarationExpression); return newVar(localClassName, classDeclarationExpression);
} }
@@ -145,8 +138,6 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
JsName globalClassName = context().getNameForElement(declaration); JsName globalClassName = context().getNameForElement(declaration);
JsName localAlias = dummyFunction.getScope().declareTemporary(); JsName localAlias = dummyFunction.getScope().declareTemporary();
localToGlobalClassName.put(localAlias, globalClassName); localToGlobalClassName.put(localAlias, globalClassName);
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(bindingContext(), declaration);
aliaser().setAliasForDescriptor(descriptor, localAlias);
return localAlias; return localAlias;
} }
@@ -33,7 +33,9 @@ import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import static com.google.dart.compiler.util.AstUtil.newSequence; import static com.google.dart.compiler.util.AstUtil.newSequence;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
@@ -53,21 +55,31 @@ public final class ClassTranslator extends AbstractTranslator {
@NotNull @NotNull
public static JsPropertyInitializer translateAsProperty(@NotNull JetClassOrObject classDeclaration, public static JsPropertyInitializer translateAsProperty(@NotNull JetClassOrObject classDeclaration,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
JsInvocation classCreationExpression = generateClassCreationExpression(classDeclaration, context); JsInvocation classCreationExpression =
generateClassCreationExpression(classDeclaration, context);
JsName className = context.getNameForElement(classDeclaration); JsName className = context.getNameForElement(classDeclaration);
return new JsPropertyInitializer(className.makeRef(), classCreationExpression); return new JsPropertyInitializer(className.makeRef(), classCreationExpression);
} }
@NotNull @NotNull
public static JsInvocation generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration, public static JsInvocation generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration,
@NotNull Map<JsName, JsName> aliasingMap,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (new ClassTranslator(classDeclaration, context)).translateClassOrObjectCreation(); return (new ClassTranslator(classDeclaration, aliasingMap, context)).translateClassOrObjectCreation();
}
@NotNull
public static JsInvocation generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration,
@NotNull TranslationContext context) {
return (new ClassTranslator(classDeclaration, Collections.<JsName, JsName>emptyMap(), context)).translateClassOrObjectCreation();
} }
@NotNull @NotNull
public static JsExpression generateObjectLiteralExpression(@NotNull JetObjectLiteralExpression objectLiteralExpression, public static JsExpression generateObjectLiteralExpression(@NotNull JetObjectLiteralExpression objectLiteralExpression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (new ClassTranslator(objectLiteralExpression.getObjectDeclaration(), context)).translateObjectLiteralExpression(); return (new ClassTranslator(objectLiteralExpression.getObjectDeclaration(), Collections.<JsName, JsName>emptyMap(), context))
.translateObjectLiteralExpression();
} }
@NotNull @NotNull
@@ -76,11 +88,20 @@ public final class ClassTranslator extends AbstractTranslator {
@NotNull @NotNull
private final JetClassOrObject classDeclaration; private final JetClassOrObject classDeclaration;
@Nullable
private TemporaryVariable aliasForContainingClassThis = null;
@NotNull @NotNull
private final ClassDescriptor descriptor; private final ClassDescriptor descriptor;
private ClassTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) { @NotNull
private final Map<JsName, JsName> aliasingMap;
private ClassTranslator(@NotNull JetClassOrObject classDeclaration,
@NotNull Map<JsName, JsName> aliasingMap,
@NotNull TranslationContext context) {
super(context.newDeclaration(classDeclaration)); super(context.newDeclaration(classDeclaration));
this.aliasingMap = aliasingMap;
this.descriptor = getClassDescriptor(context.bindingContext(), classDeclaration); this.descriptor = getClassDescriptor(context.bindingContext(), classDeclaration);
this.classDeclaration = classDeclaration; this.classDeclaration = classDeclaration;
} }
@@ -96,11 +117,8 @@ public final class ClassTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression translateAsObjectCreationExpressionWithEnclosingThisSaved(@NotNull ClassDescriptor containingClass) { private JsExpression translateAsObjectCreationExpressionWithEnclosingThisSaved(@NotNull ClassDescriptor containingClass) {
TemporaryVariable thisAlias = context().declareTemporary(getThisObject(context(), containingClass)); aliasForContainingClassThis = context().declareTemporary(getThisObject(context(), containingClass));
aliaser().setAliasForThis(containingClass, thisAlias.name()); return newSequence(aliasForContainingClassThis.assignmentExpression(), translateClassOrObjectCreation());
JsBinaryOperation result = newSequence(thisAlias.assignmentExpression(), translateClassOrObjectCreation());
aliaser().removeAliasForThis(containingClass);
return result;
} }
@NotNull @NotNull
@@ -155,7 +173,7 @@ public final class ClassTranslator extends AbstractTranslator {
private void addTraits(@NotNull List<JsExpression> superclassReferences, private void addTraits(@NotNull List<JsExpression> superclassReferences,
@NotNull List<ClassDescriptor> superclassDescriptors) { @NotNull List<ClassDescriptor> superclassDescriptors) {
for (ClassDescriptor superClassDescriptor : for (ClassDescriptor superClassDescriptor :
superclassDescriptors) { superclassDescriptors) {
assert (superClassDescriptor.getKind() == ClassKind.TRAIT) : "Only traits are expected here"; assert (superClassDescriptor.getKind() == ClassKind.TRAIT) : "Only traits are expected here";
superclassReferences.add(getClassReference(superClassDescriptor)); superclassReferences.add(getClassReference(superClassDescriptor));
} }
@@ -173,8 +191,10 @@ public final class ClassTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression getClassReference(@NotNull ClassDescriptor superClassDescriptor) { private JsExpression getClassReference(@NotNull ClassDescriptor superClassDescriptor) {
//NOTE: aliasing here is needed for the declaration generation step //NOTE: aliasing here is needed for the declaration generation step
if (aliaser().hasAliasForDeclaration(superClassDescriptor)) { JsName name = context().getNameForDescriptor(superClassDescriptor);
return aliaser().getAliasForDeclaration(superClassDescriptor).makeRef(); JsName alias = aliasingMap.get(name);
if (alias != null) {
return alias.makeRef();
} }
return getQualifiedReference(context(), superClassDescriptor); return getQualifiedReference(context(), superClassDescriptor);
} }
@@ -188,25 +208,36 @@ public final class ClassTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsObjectLiteral translateClassDeclarations() { private JsObjectLiteral translateClassDeclarations() {
TranslationContext classDeclarationContext = getClassDeclarationContext();
List<JsPropertyInitializer> propertyList = new ArrayList<JsPropertyInitializer>(); List<JsPropertyInitializer> propertyList = new ArrayList<JsPropertyInitializer>();
if (!isTrait()) { if (!isTrait()) {
propertyList.add(Translation.generateClassInitializerMethod(classDeclaration, context())); propertyList.add(Translation.generateClassInitializerMethod(classDeclaration, classDeclarationContext));
} }
propertyList.addAll(translatePropertiesAsConstructorParameters()); propertyList.addAll(translatePropertiesAsConstructorParameters(classDeclarationContext));
propertyList.addAll(declarationBodyVisitor.traverseClass(classDeclaration, context())); propertyList.addAll(declarationBodyVisitor.traverseClass(classDeclaration, classDeclarationContext));
return JsAstUtils.newObjectLiteral(propertyList); return JsAstUtils.newObjectLiteral(propertyList);
} }
@NotNull @NotNull
private List<JsPropertyInitializer> translatePropertiesAsConstructorParameters() { private List<JsPropertyInitializer> translatePropertiesAsConstructorParameters(@NotNull TranslationContext classDeclarationContext) {
List<JsPropertyInitializer> result = new ArrayList<JsPropertyInitializer>(); List<JsPropertyInitializer> result = new ArrayList<JsPropertyInitializer>();
for (JetParameter parameter : getPrimaryConstructorParameters(classDeclaration)) { for (JetParameter parameter : getPrimaryConstructorParameters(classDeclaration)) {
PropertyDescriptor descriptor = PropertyDescriptor descriptor =
getPropertyDescriptorForConstructorParameter(bindingContext(), parameter); getPropertyDescriptorForConstructorParameter(bindingContext(), parameter);
if (descriptor != null) { if (descriptor != null) {
result.addAll(PropertyTranslator.translateAccessors(descriptor, context())); result.addAll(PropertyTranslator.translateAccessors(descriptor, classDeclarationContext));
} }
} }
return result; return result;
} }
@NotNull
private TranslationContext getClassDeclarationContext() {
if (aliasForContainingClassThis == null) {
return context();
}
ClassDescriptor containingClass = getContainingClass(descriptor);
assert containingClass != null;
return context().innerContextWithThisAliased(containingClass, aliasForContainingClassThis.name());
}
} }
@@ -20,6 +20,7 @@ package org.jetbrains.k2js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody; import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetElement;
@@ -34,6 +35,7 @@ import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.reference.ReferenceTranslator; import org.jetbrains.k2js.translate.reference.ReferenceTranslator;
import org.jetbrains.k2js.translate.utils.DescriptorUtils; import org.jetbrains.k2js.translate.utils.DescriptorUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import org.jetbrains.k2js.translate.utils.closure.ClosureContext; import org.jetbrains.k2js.translate.utils.closure.ClosureContext;
import org.jetbrains.k2js.translate.utils.closure.ClosureUtils; import org.jetbrains.k2js.translate.utils.closure.ClosureUtils;
import org.jetbrains.k2js.translate.utils.mutator.Mutator; import org.jetbrains.k2js.translate.utils.mutator.Mutator;
@@ -45,7 +47,6 @@ import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescrip
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock; import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.setParameters; import static org.jetbrains.k2js.translate.utils.JsAstUtils.setParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.newAliasForThis;
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression; import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
@@ -62,13 +63,15 @@ public final class FunctionTranslator extends AbstractTranslator {
return new FunctionTranslator(function, context); return new FunctionTranslator(function, context);
} }
@Nullable
private TemporaryVariable aliasForContaingClassThis = null;
@NotNull @NotNull
private final JetDeclarationWithBody functionDeclaration; private final JetDeclarationWithBody functionDeclaration;
@Nullable
private JsName extensionFunctionReceiverName = null;
@NotNull @NotNull
private final JsFunction functionObject; private final JsFunction functionObject;
@NotNull @NotNull
private final TranslationContext functionBodyContext;
@NotNull
private final FunctionDescriptor descriptor; private final FunctionDescriptor descriptor;
// function body needs to be explicitly created here to include it in the context // function body needs to be explicitly created here to include it in the context
@NotNull @NotNull
@@ -81,14 +84,49 @@ public final class FunctionTranslator extends AbstractTranslator {
this.functionDeclaration = functionDeclaration; this.functionDeclaration = functionDeclaration;
this.functionObject = context().getFunctionObject(descriptor); this.functionObject = context().getFunctionObject(descriptor);
this.functionBody = functionObject.getBody(); this.functionBody = functionObject.getBody();
this.functionBodyContext = functionBodyContext().innerBlock(functionBody); }
@NotNull
private TranslationContext getFunctionBodyContext() {
if (isLiteral()) {
return getFunctionBodyContextForLiteral();
}
if (isExtensionFunction()) {
return getFunctionBodyContextForExtensionFunction();
}
return getContextWithFunctionBodyBlock();
}
@NotNull
private TranslationContext getFunctionBodyContextForLiteral() {
ClassDescriptor containingClass = getContainingClass(descriptor);
if (containingClass == null) {
return getContextWithFunctionBodyBlock();
}
JsExpression thisQualifier = TranslationUtils.getThisObject(context(), containingClass);
aliasForContaingClassThis = context().declareTemporary(thisQualifier);
return getContextWithFunctionBodyBlock().innerContextWithThisAliased(containingClass, aliasForContaingClassThis.name());
}
@NotNull
private TranslationContext getFunctionBodyContextForExtensionFunction() {
TranslationContext contextWithFunctionBodyBlock = getContextWithFunctionBodyBlock();
extensionFunctionReceiverName = contextWithFunctionBodyBlock.jsScope().declareName(Namer.getReceiverParameterName());
DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor);
assert expectedReceiverDescriptor != null;
return contextWithFunctionBodyBlock.innerContextWithThisAliased(expectedReceiverDescriptor, extensionFunctionReceiverName);
}
@NotNull
private TranslationContext getContextWithFunctionBodyBlock() {
return context().newDeclaration(functionDeclaration).innerBlock(functionBody);
} }
@NotNull @NotNull
public JsFunction translateAsLocalFunction() { public JsFunction translateAsLocalFunction() {
JsName functionName = context().getNameForElement(functionDeclaration); JsName functionName = context().getNameForElement(functionDeclaration);
generateFunctionObject(); generateFunctionObject(getFunctionBodyContext());
functionObject.setName(functionName); functionObject.setName(functionName);
return functionObject; return functionObject;
} }
@@ -96,7 +134,7 @@ public final class FunctionTranslator extends AbstractTranslator {
@NotNull @NotNull
public JsPropertyInitializer translateAsMethod() { public JsPropertyInitializer translateAsMethod() {
JsName functionName = context().getNameForElement(functionDeclaration); JsName functionName = context().getNameForElement(functionDeclaration);
generateFunctionObject(); generateFunctionObject(getFunctionBodyContext());
return new JsPropertyInitializer(functionName.makeRef(), functionObject); return new JsPropertyInitializer(functionName.makeRef(), functionObject);
} }
@@ -108,17 +146,26 @@ public final class FunctionTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression doTranslateAsLiteral() { private JsExpression doTranslateAsLiteral() {
assert getExpectedThisDescriptor(descriptor) == null; assert getExpectedThisDescriptor(descriptor) == null;
ClassDescriptor containingClass = getContainingClass(descriptor); generateFunctionObject(getFunctionBodyContext());
if (containingClass == null) { boolean shouldAliasThisObject = shouldAliasThisObject();
generateFunctionObject(); if (!shouldAliasThisObject) {
return functionObject; return functionObject;
} }
return generateFunctionObjectWithAliasedThisReference(containingClass); assert aliasForContaingClassThis != null;
return AstUtil.newSequence(aliasForContaingClassThis.assignmentExpression(), functionObject);
}
private boolean shouldAliasThisObject() {
if (!isLiteral()) {
return false;
}
ClassDescriptor containingClass = getContainingClass(descriptor);
return containingClass != null;
} }
@NotNull @NotNull
private JsExpression mayBeWrapInClosureCaptureExpression(@NotNull JsExpression wrappedExpression) { private JsExpression mayBeWrapInClosureCaptureExpression(@NotNull JsExpression wrappedExpression) {
ClosureContext closureContext = ClosureUtils.captureClosure(context(), (JetElement) functionDeclaration); ClosureContext closureContext = ClosureUtils.captureClosure(context(), (JetElement)functionDeclaration);
if (closureContext.getDescriptors().isEmpty()) { if (closureContext.getDescriptors().isEmpty()) {
return wrappedExpression; return wrappedExpression;
} }
@@ -138,35 +185,18 @@ public final class FunctionTranslator extends AbstractTranslator {
return dummyFunctionInvocation; return dummyFunctionInvocation;
} }
@NotNull private void generateFunctionObject(@NotNull TranslationContext context) {
private JsExpression generateFunctionObjectWithAliasedThisReference(@NotNull ClassDescriptor containingClass) {
TemporaryVariable aliasForThis = newAliasForThis(context(), containingClass);
generateFunctionObject();
aliaser().removeAliasForThis(containingClass);
return AstUtil.newSequence(aliasForThis.assignmentExpression(), functionObject);
}
private void generateFunctionObject() {
setParameters(functionObject, translateParameters()); setParameters(functionObject, translateParameters());
translateBody(); translateBody(context);
restoreContext();
} }
private void restoreContext() { private void translateBody(@NotNull TranslationContext context) {
if (isExtensionFunction()) {
DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor);
assert expectedReceiverDescriptor != null : "Extension functions should always have receiver descriptors.";
functionBodyContext.aliaser().removeAliasForThis(expectedReceiverDescriptor);
}
}
private void translateBody() {
JetExpression jetBodyExpression = functionDeclaration.getBodyExpression(); JetExpression jetBodyExpression = functionDeclaration.getBodyExpression();
if (jetBodyExpression == null) { if (jetBodyExpression == null) {
assert descriptor.getModality().equals(Modality.ABSTRACT); assert descriptor.getModality().equals(Modality.ABSTRACT);
return; return;
} }
JsNode realBody = Translation.translateExpression(jetBodyExpression, functionBodyContext); JsNode realBody = Translation.translateExpression(jetBodyExpression, context);
functionBody.getStatements().add(wrapWithReturnIfNeeded(realBody, mustAddReturnToGeneratedFunctionBody())); functionBody.getStatements().add(wrapWithReturnIfNeeded(realBody, mustAddReturnToGeneratedFunctionBody()));
} }
@@ -192,16 +222,11 @@ public final class FunctionTranslator extends AbstractTranslator {
if (!(node instanceof JsExpression)) { if (!(node instanceof JsExpression)) {
return node; return node;
} }
return new JsReturn((JsExpression) node); return new JsReturn((JsExpression)node);
} }
}); });
} }
@NotNull
private TranslationContext functionBodyContext() {
return context().newDeclaration(functionDeclaration);
}
@NotNull @NotNull
private List<JsParameter> translateParameters() { private List<JsParameter> translateParameters() {
List<JsParameter> jsParameters = new ArrayList<JsParameter>(); List<JsParameter> jsParameters = new ArrayList<JsParameter>();
@@ -220,11 +245,8 @@ public final class FunctionTranslator extends AbstractTranslator {
private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) { private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
if (isExtensionFunction()) { if (isExtensionFunction()) {
JsName receiver = functionBodyContext.jsScope().declareName(Namer.getReceiverParameterName()); assert extensionFunctionReceiverName != null;
DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor); jsParameters.add(new JsParameter(extensionFunctionReceiverName));
assert expectedReceiverDescriptor != null;
aliaser().setAliasForThis(expectedReceiverDescriptor, receiver);
jsParameters.add(new JsParameter(receiver));
} }
} }
@@ -19,7 +19,6 @@ package org.jetbrains.k2js.translate.general;
import com.google.dart.compiler.backend.js.ast.JsProgram; import com.google.dart.compiler.backend.js.ast.JsProgram;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.k2js.translate.context.Aliaser;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
/** /**
@@ -44,11 +43,6 @@ public abstract class AbstractTranslator {
return context; return context;
} }
@NotNull
protected Aliaser aliaser() {
return context.aliaser();
}
@NotNull @NotNull
protected BindingContext bindingContext() { protected BindingContext bindingContext() {
return context.bindingContext(); return context.bindingContext();
@@ -36,6 +36,7 @@ import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
import org.jetbrains.k2js.translate.initializer.NamespaceInitializerTranslator; import org.jetbrains.k2js.translate.initializer.NamespaceInitializerTranslator;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToExpression; import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToExpression;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement; import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement;
@@ -64,8 +65,9 @@ public final class Translation {
@NotNull @NotNull
public static JsInvocation translateClassDeclaration(@NotNull JetClass classDeclaration, public static JsInvocation translateClassDeclaration(@NotNull JetClass classDeclaration,
@NotNull Map<JsName, JsName> aliasingMap,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return ClassTranslator.generateClassCreationExpression(classDeclaration, context); return ClassTranslator.generateClassCreationExpression(classDeclaration, aliasingMap, context);
} }
@NotNull @NotNull
@@ -124,6 +126,4 @@ public final class Translation {
namer.exec(context.program()); namer.exec(context.program());
return context.program(); return context.program();
} }
} }
@@ -84,11 +84,11 @@ public final class InitializerVisitor extends TranslatorVisitor<List<JsStatement
public List<JsStatement> visitObjectDeclarationName(@NotNull JetObjectDeclarationName objectName, public List<JsStatement> visitObjectDeclarationName(@NotNull JetObjectDeclarationName objectName,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
PropertyDescriptor propertyDescriptorForObjectDeclaration PropertyDescriptor propertyDescriptorForObjectDeclaration
= getPropertyDescriptorForObjectDeclaration(context.bindingContext(), objectName); = getPropertyDescriptorForObjectDeclaration(context.bindingContext(), objectName);
JetObjectDeclaration objectDeclaration = getObjectDeclarationForName(objectName); JetObjectDeclaration objectDeclaration = getObjectDeclarationForName(objectName);
JsInvocation objectValue = ClassTranslator.generateClassCreationExpression(objectDeclaration, context); JsInvocation objectValue = ClassTranslator.generateClassCreationExpression(objectDeclaration, context);
JsExprStmt assignment = assignmentToBackingField(context, propertyDescriptorForObjectDeclaration, objectValue) JsExprStmt assignment = assignmentToBackingField(context, propertyDescriptorForObjectDeclaration, objectValue)
.makeStmt(); .makeStmt();
return Collections.<JsStatement>singletonList(assignment); return Collections.<JsStatement>singletonList(assignment);
} }
@@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.intrinsic.Intrinsic; import org.jetbrains.k2js.translate.intrinsic.Intrinsic;
@@ -117,23 +116,21 @@ public final class TranslationUtils {
@NotNull @NotNull
public static JsExpression getThisObject(@NotNull TranslationContext context, public static JsExpression getThisObject(@NotNull TranslationContext context,
@NotNull DeclarationDescriptor correspondingDeclaration) { @NotNull DeclarationDescriptor correspondingDeclaration) {
JsExpression thisRef = null;
if (correspondingDeclaration instanceof ClassDescriptor) { if (correspondingDeclaration instanceof ClassDescriptor) {
if (context.aliaser().hasAliasForThis(correspondingDeclaration)) { JsName alias = context.aliasingContext().getAliasForThis(correspondingDeclaration);
thisRef = context.aliaser().getAliasForThis(correspondingDeclaration); if (alias != null) {
return alias.makeRef();
} }
} }
if (correspondingDeclaration instanceof CallableDescriptor) { if (correspondingDeclaration instanceof CallableDescriptor) {
DeclarationDescriptor receiverDescriptor = DeclarationDescriptor receiverDescriptor =
getExpectedReceiverDescriptor((CallableDescriptor) correspondingDeclaration); getExpectedReceiverDescriptor((CallableDescriptor)correspondingDeclaration);
assert receiverDescriptor != null; assert receiverDescriptor != null;
if (context.aliaser().hasAliasForThis(receiverDescriptor)) { JsName alias = context.aliasingContext().getAliasForThis(receiverDescriptor);
thisRef = context.aliaser().getAliasForThis(receiverDescriptor); if (alias != null) {
return alias.makeRef();
} }
} }
if (thisRef != null) {
return thisRef;
}
return new JsThisRef(); return new JsThisRef();
} }
@@ -171,7 +168,7 @@ public final class TranslationUtils {
public static boolean isIntrinsicOperation(@NotNull TranslationContext context, public static boolean isIntrinsicOperation(@NotNull TranslationContext context,
@NotNull JetOperationExpression expression) { @NotNull JetOperationExpression expression) {
FunctionDescriptor operationDescriptor = FunctionDescriptor operationDescriptor =
BindingUtils.getFunctionDescriptorForOperationExpression(context.bindingContext(), expression); BindingUtils.getFunctionDescriptorForOperationExpression(context.bindingContext(), expression);
if (operationDescriptor == null) return true; if (operationDescriptor == null) return true;
if (context.intrinsics().isIntrinsic(operationDescriptor)) return true; if (context.intrinsics().isIntrinsic(operationDescriptor)) return true;
@@ -183,7 +180,7 @@ public final class TranslationUtils {
public static JsNameRef getMethodReferenceForOverloadedOperation(@NotNull TranslationContext context, public static JsNameRef getMethodReferenceForOverloadedOperation(@NotNull TranslationContext context,
@NotNull JetOperationExpression expression) { @NotNull JetOperationExpression expression) {
FunctionDescriptor overloadedOperationDescriptor = getFunctionDescriptorForOperationExpression FunctionDescriptor overloadedOperationDescriptor = getFunctionDescriptorForOperationExpression
(context.bindingContext(), expression); (context.bindingContext(), expression);
assert overloadedOperationDescriptor != null; assert overloadedOperationDescriptor != null;
JsNameRef overloadedOperationReference = context.getNameForDescriptor(overloadedOperationDescriptor).makeRef(); JsNameRef overloadedOperationReference = context.getNameForDescriptor(overloadedOperationDescriptor).makeRef();
assert overloadedOperationReference != null; assert overloadedOperationReference != null;
@@ -195,15 +192,6 @@ public final class TranslationUtils {
return context.program().getNumberLiteral(0); return context.program().getNumberLiteral(0);
} }
@NotNull
public static TemporaryVariable newAliasForThis(@NotNull TranslationContext context,
@NotNull DeclarationDescriptor descriptor) {
JsExpression thisQualifier = getThisObject(context, descriptor);
TemporaryVariable aliasForThis = context.declareTemporary(thisQualifier);
context.aliaser().setAliasForThis(descriptor, aliasForThis.name());
return aliasForThis;
}
@NotNull @NotNull
public static JsExpression applyIntrinsicToBinaryExpression(@NotNull TranslationContext context, public static JsExpression applyIntrinsicToBinaryExpression(@NotNull TranslationContext context,
@NotNull Intrinsic intrinsic, @NotNull Intrinsic intrinsic,