JS backend: refactoring support for binary intrinsic operations

This commit is contained in:
Michael Nedzelsky
2014-09-18 21:14:21 +04:00
parent 07a4d0e77e
commit 1d71016097
8 changed files with 213 additions and 216 deletions
@@ -1,31 +0,0 @@
/*
* Copyright 2010-2013 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.intrinsic.operation;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
import org.jetbrains.k2js.translate.context.TranslationContext;
public interface BinaryOperationIntrinsic {
boolean isApplicable(@NotNull JetBinaryExpression expression, @NotNull TranslationContext context);
@NotNull
JsExpression apply(@NotNull JetBinaryExpression expression, @NotNull JsExpression left,
@NotNull JsExpression right, @NotNull TranslationContext context);
}
@@ -1,49 +0,0 @@
/*
* Copyright 2010-2013 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.intrinsic.operation;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
import org.jetbrains.k2js.translate.context.TranslationContext;
import java.util.List;
public final class BinaryOperationIntrinsics {
@NotNull
private final List<BinaryOperationIntrinsic> intrinsics = Lists.newArrayList();
{
intrinsics.add(new EqualsIntrinsic());
intrinsics.add(new CompareToInstrinsic());
}
public BinaryOperationIntrinsics() {
}
@Nullable
public BinaryOperationIntrinsic getIntrinsic(@NotNull JetBinaryExpression expression, @NotNull TranslationContext context) {
for (BinaryOperationIntrinsic intrinsic : intrinsics) {
if (intrinsic.isApplicable(expression, context)) {
return intrinsic;
}
}
return null;
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2014 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.intrinsic.operation
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
import org.jetbrains.k2js.translate.context.TranslationContext
import org.jetbrains.k2js.translate.operation.OperatorTable
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils
import org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lexer.JetToken
import com.google.common.collect.ImmutableSet
object CompareToBOIF : BinaryOperationIntrinsicFactory {
private object CompareToIntrinsic : AbstractBinaryOperationIntrinsic() {
override fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
return JsBinaryOperation(operator, left, right)
}
}
override public fun getSupportTokens(): ImmutableSet<JetToken> = OperatorConventions.COMPARISON_OPERATIONS
override public fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? {
return if (JsDescriptorUtils.isBuiltin(descriptor)) CompareToIntrinsic else null
}
}
@@ -1,54 +0,0 @@
/*
* Copyright 2010-2013 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.intrinsic.operation;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.operation.OperatorTable;
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken;
public final class CompareToInstrinsic implements BinaryOperationIntrinsic {
@Override
public boolean isApplicable(@NotNull JetBinaryExpression expression, @NotNull TranslationContext context) {
if (!OperatorConventions.COMPARISON_OPERATIONS.contains(getOperationToken(expression))) {
return false;
}
CallableDescriptor descriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression);
assert descriptor != null;
return JsDescriptorUtils.isBuiltin(descriptor);
}
@NotNull
@Override
public JsExpression apply(@NotNull JetBinaryExpression expression,
@NotNull JsExpression left,
@NotNull JsExpression right,
@NotNull TranslationContext context) {
JsBinaryOperator operator = OperatorTable.getBinaryOperator(getOperationToken(expression));
return new JsBinaryOperation(operator, left, right);
}
}
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2014 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.intrinsic.operation
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
import org.jetbrains.k2js.translate.context.TranslationContext
import org.jetbrains.k2js.translate.intrinsic.functions.factories.TopLevelFIF
import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate
import org.jetbrains.k2js.translate.utils.JsAstUtils
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils
import org.jetbrains.k2js.translate.utils.TranslationUtils
import java.util.Arrays
import org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken
import org.jetbrains.jet.lexer.JetToken
import org.jetbrains.jet.lexer.JetTokens
import com.google.common.collect.ImmutableSet
object EqualsBOIF : BinaryOperationIntrinsicFactory {
private object EqualsIntrinsic : AbstractBinaryOperationIntrinsic() {
override fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val isNegated = getOperationToken(expression) == JetTokens.EXCLEQ
if (right == JsLiteral.NULL || left == JsLiteral.NULL) {
return TranslationUtils.nullCheck(if (right == JsLiteral.NULL) left else right, isNegated)
}
else if (canUseSimpleEquals(expression, context)) {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, left, right)
}
val result = TopLevelFIF.KOTLIN_EQUALS.apply(left, Arrays.asList<JsExpression>(right), context)
return if (isNegated) JsAstUtils.negated(result) else result
}
private fun canUseSimpleEquals(expression: JetBinaryExpression, context: TranslationContext): Boolean {
val left = expression.getLeft()
assert(left != null) { "No left-hand side: " + expression.getText() }
val typeName = JsDescriptorUtils.getNameIfStandardType(left!!, context)
return typeName != null && NamePredicate.PRIMITIVE_NUMBERS.apply(typeName)
}
}
override public fun getSupportTokens(): ImmutableSet<JetToken> = OperatorConventions.EQUALS_OPERATIONS
override public fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? {
if (JsDescriptorUtils.isBuiltin(descriptor) || TopLevelFIF.EQUALS_IN_ANY.apply(descriptor)) {
return EqualsIntrinsic
}
return null
}
}
@@ -1,80 +0,0 @@
/*
* Copyright 2010-2013 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.intrinsic.operation;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsLiteral;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.intrinsic.functions.factories.TopLevelFIF;
import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.Arrays;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken;
public final class EqualsIntrinsic implements BinaryOperationIntrinsic {
@Override
public boolean isApplicable(@NotNull JetBinaryExpression expression, @NotNull TranslationContext context) {
if (!OperatorConventions.EQUALS_OPERATIONS.contains(getOperationToken(expression))) {
return false;
}
CallableDescriptor functionDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression);
if (!(functionDescriptor instanceof FunctionDescriptor)) return false;
return JsDescriptorUtils.isBuiltin(functionDescriptor) || TopLevelFIF.EQUALS_IN_ANY.apply((FunctionDescriptor) functionDescriptor);
}
@Override
@NotNull
public JsExpression apply(@NotNull JetBinaryExpression expression,
@NotNull JsExpression left,
@NotNull JsExpression right,
@NotNull TranslationContext context) {
boolean isNegated = getOperationToken(expression).equals(JetTokens.EXCLEQ);
if (right == JsLiteral.NULL || left == JsLiteral.NULL) {
return TranslationUtils.nullCheck(right == JsLiteral.NULL ? left : right, isNegated);
}
else if (canUseSimpleEquals(expression, context)) {
return new JsBinaryOperation(isNegated ? JsBinaryOperator.REF_NEQ : JsBinaryOperator.REF_EQ, left, right);
}
JsExpression result = TopLevelFIF.KOTLIN_EQUALS.apply(left, Arrays.asList(right), context);
return isNegated ? JsAstUtils.negated(result) : result;
}
private static boolean canUseSimpleEquals(@NotNull JetBinaryExpression expression, @NotNull TranslationContext context) {
JetExpression left = expression.getLeft();
assert left != null : "No left-hand side: " + expression.getText();
Name typeName = JsDescriptorUtils.getNameIfStandardType(left, context);
return typeName != null && NamePredicate.PRIMITIVE_NUMBERS.apply(typeName);
}
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2014 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.intrinsic.operation
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.jet.lexer.JetToken
import org.jetbrains.k2js.translate.context.TranslationContext
import org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression
import org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken
import gnu.trove.THashMap
import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.common.collect.ImmutableSet
public trait BinaryOperationIntrinsic {
fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression
fun exists(): Boolean
}
public class BinaryOperationIntrinsics {
private val intrinsicCache = THashMap<Pair<JetToken, FunctionDescriptor>, BinaryOperationIntrinsic>()
private val factories = listOf(EqualsBOIF, CompareToBOIF)
public fun getIntrinsic(expression: JetBinaryExpression, context: TranslationContext): BinaryOperationIntrinsic {
val token = getOperationToken(expression)
val descriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression)
if (descriptor == null || descriptor !is FunctionDescriptor) {
return NO_INTRINSIC
}
return lookUpCache(token, descriptor) ?: computeAndCacheIntrinsic(token, descriptor)
}
private fun lookUpCache(token: JetToken, descriptor: FunctionDescriptor): BinaryOperationIntrinsic? =
intrinsicCache.get(Pair(token, descriptor))
private fun computeAndCacheIntrinsic(token: JetToken, descriptor: FunctionDescriptor): BinaryOperationIntrinsic {
val result = computeIntrinsic(token, descriptor)
intrinsicCache.put(Pair(token, descriptor), result)
return result
}
private fun computeIntrinsic(token: JetToken, descriptor: FunctionDescriptor): BinaryOperationIntrinsic {
for (factory in factories) {
if (factory.getSupportTokens().contains(token)) {
val intrinsic = factory.getIntrinsic(descriptor)
if (intrinsic != null) {
return intrinsic
}
}
}
return NO_INTRINSIC
}
}
trait BinaryOperationIntrinsicFactory {
public fun getSupportTokens(): ImmutableSet<JetToken>
public fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic?
}
abstract class AbstractBinaryOperationIntrinsic : BinaryOperationIntrinsic {
public override abstract fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression
public override fun exists(): Boolean = true
}
object NO_INTRINSIC : AbstractBinaryOperationIntrinsic() {
override fun exists(): Boolean = false
override fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression =
throw UnsupportedOperationException("BinaryOperationIntrinsic#NO_INTRINSIC_#apply")
}
@@ -89,7 +89,7 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
private JsExpression translate() {
BinaryOperationIntrinsic intrinsic = getIntrinsicForExpression();
if (intrinsic != null) {
if (intrinsic.exists()) {
return applyIntrinsic(intrinsic);
}
if (getOperationToken(expression).equals(JetTokens.ELVIS)) {
@@ -149,7 +149,7 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
return result;
}
@Nullable
@NotNull
private BinaryOperationIntrinsic getIntrinsicForExpression() {
return context().intrinsics().getBinaryOperationIntrinsics().getIntrinsic(expression, context());
}