diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/AliasingContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/AliasingContext.java index 3eb08c3f9f2..7148969909f 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/AliasingContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/AliasingContext.java @@ -17,26 +17,49 @@ package org.jetbrains.k2js.translate.context; import com.google.common.collect.Maps; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsLiteral; 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.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassOrNamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ClassReceiver; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor; import java.util.Map; +import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getDeclarationDescriptorForReceiver; +import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor; + /** * @author Pavel Talanov */ public class AliasingContext { - - @NotNull - private static final AliasingContext ROOT = new AliasingContext(null) { + private static final ThisAliasProvider EMPTY_THIS_ALIAS_PROVIDER = new ThisAliasProvider() { + @Nullable @Override - public JsName getAliasForThis(@NotNull DeclarationDescriptor descriptor) { + public JsNameRef get(@NotNull DeclarationDescriptor descriptor) { return null; } + @Nullable + @Override + public JsExpression get(@NotNull ResolvedCall call) { + ReceiverDescriptor callThisObject = call.getThisObject(); + return callThisObject.exists() && (callThisObject instanceof ClassReceiver || callThisObject instanceof ExtensionReceiver) + ? JsLiteral.THIS + : null; + } + }; + + private static final AliasingContext ROOT = new AliasingContext(null, EMPTY_THIS_ALIAS_PROVIDER) { @Override public JsName getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) { return null; @@ -49,66 +72,105 @@ public class AliasingContext { }; public static AliasingContext getCleanContext() { - return new AliasingContext(ROOT); + return new AliasingContext(ROOT, ROOT.thisAliasProvider); } @NotNull private final Map aliasesForDescriptors = Maps.newHashMap(); + @NotNull - private final Map aliasesForThis = Maps.newHashMap(); + final ThisAliasProvider thisAliasProvider; @NotNull private final Map aliasesForExpressions = Maps.newHashMap(); @Nullable private final AliasingContext parent; - private AliasingContext(@Nullable AliasingContext parent) { + private AliasingContext(@Nullable AliasingContext parent, @NotNull ThisAliasProvider thisAliasProvider) { this.parent = parent; + this.thisAliasProvider = thisAliasProvider; + } + + public interface ThisAliasProvider { + @Nullable + JsNameRef get(@NotNull DeclarationDescriptor descriptor); + @Nullable + JsExpression get(@NotNull ResolvedCall call); + } + + public abstract static class AbstractThisAliasProvider implements ThisAliasProvider { + @NotNull + protected static DeclarationDescriptor normalize(@NotNull DeclarationDescriptor descriptor) { + if (descriptor instanceof ClassOrNamespaceDescriptor) { + return descriptor; + } + else if (descriptor instanceof CallableDescriptor) { + DeclarationDescriptor receiverDescriptor = getExpectedReceiverDescriptor((CallableDescriptor) descriptor); + assert receiverDescriptor != null; + return receiverDescriptor; + } + + return descriptor; + } + + @Nullable + @Override + public JsExpression get(@NotNull ResolvedCall call) { + ReceiverDescriptor thisObject = call.getThisObject(); + if (!thisObject.exists()) { + return null; + } + + if (thisObject instanceof ExtensionReceiver || thisObject instanceof ClassReceiver) { + JsNameRef ref = get(((ThisReceiverDescriptor) thisObject).getDeclarationDescriptor()); + if (ref != null) { + return ref; + } + } + + JsNameRef ref = get(getDeclarationDescriptorForReceiver(thisObject)); + return ref == null ? JsLiteral.THIS : ref; + } } @NotNull - public AliasingContext withThisAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsName alias) { - AliasingContext newContext = new AliasingContext(this); - newContext.aliasesForThis.put(correspondingDescriptor, alias); - return newContext; + public AliasingContext inner(@NotNull ThisAliasProvider thisAliasProvider) { + return new AliasingContext(this, thisAliasProvider); + } + + @NotNull + public AliasingContext inner(@NotNull final DeclarationDescriptor correspondingDescriptor, @NotNull final JsName alias) { + return inner(new AbstractThisAliasProvider() { + @Nullable + @Override + public JsNameRef get(@NotNull DeclarationDescriptor descriptor) { + return correspondingDescriptor == normalize(descriptor) ? alias.makeRef() : null; + } + }); } @NotNull public AliasingContext withAliasesForExpressions(@NotNull Map aliasesForExpressions) { - AliasingContext newContext = new AliasingContext(this); + AliasingContext newContext = new AliasingContext(this, thisAliasProvider); newContext.aliasesForExpressions.putAll(aliasesForExpressions); return newContext; } @NotNull public AliasingContext withDescriptorsAliased(@NotNull Map aliases) { - AliasingContext newContext = new AliasingContext(this); + AliasingContext newContext = new AliasingContext(this, thisAliasProvider); newContext.aliasesForDescriptors.putAll(aliases); 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); + assert parent != null; + return parent.getAliasForDescriptor(descriptor); } @Nullable @@ -117,6 +179,7 @@ public class AliasingContext { if (alias != null) { return alias; } - return getParent().getAliasForExpression(element); + assert parent != null; + return parent.getAliasForExpression(element); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/TraceableThisAliasProvider.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/TraceableThisAliasProvider.java new file mode 100644 index 00000000000..deb7adb28e4 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/TraceableThisAliasProvider.java @@ -0,0 +1,54 @@ +/* + * 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.JsNameRef; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.ClassOrNamespaceDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; + +public class TraceableThisAliasProvider extends AliasingContext.AbstractThisAliasProvider { + private final ClassOrNamespaceDescriptor descriptor; + private final JsNameRef thisRef; + private boolean thisWasCaptured; + + public boolean wasThisCaptured() { + return thisWasCaptured; + } + + public TraceableThisAliasProvider(@NotNull ClassOrNamespaceDescriptor descriptor, @NotNull JsNameRef thisRef) { + this.descriptor = descriptor; + this.thisRef = thisRef; + } + + @Nullable + public JsNameRef getRefIfWasCaptured() { + return thisWasCaptured ? thisRef : null; + } + + @Nullable + @Override + public JsNameRef get(@NotNull DeclarationDescriptor unnormalizedDescriptor) { + if (descriptor == normalize(unnormalizedDescriptor)) { + thisWasCaptured = true; + return thisRef; + } + + return null; + } +} 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 7ce9da41e4b..af645dd67b0 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 @@ -216,4 +216,15 @@ public class TranslationContext { public void addStatementToCurrentBlock(@NotNull JsStatement statement) { dynamicContext.jsBlock().getStatements().add(statement); } + + @NotNull + public AliasingContext.ThisAliasProvider thisAliasProvider() { + return aliasingContext().thisAliasProvider; + } + + @NotNull + public JsExpression getThisObject(@NotNull DeclarationDescriptor descriptor) { + JsNameRef ref = thisAliasProvider().get(descriptor); + return ref == null ? JsLiteral.THIS : ref; + } }