JS: support is-check on reified parameter

This commit is contained in:
Alexey Tsvetkov
2015-03-17 18:04:08 +03:00
parent 2c99484066
commit 2f786efebf
7 changed files with 158 additions and 70 deletions
@@ -368,8 +368,13 @@ public final class Namer {
} }
@NotNull @NotNull
public JsExpression isOperationReference() { public JsExpression isTypeOf(@NotNull JsExpression type) {
return kotlin(isTypeName); return new JsInvocation(kotlin("isTypeOf"), type);
}
@NotNull
public JsExpression isInstanceOf(@NotNull JsExpression type) {
return new JsInvocation(kotlin("isInstanceOf"), type);
} }
@NotNull @NotNull
@@ -126,6 +126,15 @@ public final class FunctionTranslator extends AbstractTranslator {
} }
List<JsParameter> jsParameters = new SmartList<JsParameter>(); List<JsParameter> jsParameters = new SmartList<JsParameter>();
for (TypeParameterDescriptor type : descriptor.getTypeParameters()) {
if (type.isReified()) {
JsName typeName = context().getNameForDescriptor(type);
JsName paramName = functionObject.getScope().declareName("is" + typeName.getIdent());
jsParameters.add(new JsParameter(paramName));
}
}
mayBeAddThisParameterForExtensionFunction(jsParameters); mayBeAddThisParameterForExtensionFunction(jsParameters);
addParameters(jsParameters, descriptor, context()); addParameters(jsParameters, descriptor, context());
return jsParameters; return jsParameters;
@@ -16,23 +16,25 @@
package org.jetbrains.kotlin.js.translate.expression; package org.jetbrains.kotlin.js.translate.expression;
import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.JsInvocation;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.patterns.NamePredicate; import org.jetbrains.kotlin.js.patterns.NamePredicate;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.psi.JetExpression;
import org.jetbrains.kotlin.psi.JetIsExpression; import org.jetbrains.kotlin.psi.JetIsExpression;
import org.jetbrains.kotlin.psi.JetTypeReference; import org.jetbrains.kotlin.psi.JetTypeReference;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.types.JetType;
import java.util.List;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeByReference; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeByReference;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*;
@@ -63,55 +65,97 @@ public final class PatternTranslator extends AbstractTranslator {
@NotNull @NotNull
public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull JetTypeReference typeReference) { public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull JetTypeReference typeReference) {
JsExpression result = translateAsIntrinsicTypeCheck(subject, typeReference); JetType type = BindingUtils.getTypeByReference(bindingContext(), typeReference);
if (result != null) { JsExpression checkFunReference = getIsTypeCheckCallable(type);
return result; JsInvocation isCheck = new JsInvocation(checkFunReference, subject);
}
return translateAsIsCheck(subject, typeReference);
}
@NotNull
private JsExpression translateAsIsCheck(@NotNull JsExpression expressionToMatch,
@NotNull JetTypeReference typeReference) {
JsInvocation isCheck = new JsInvocation(context().namer().isOperationReference(),
expressionToMatch, getClassNameReference(typeReference));
if (isNullable(typeReference)) { if (isNullable(typeReference)) {
return addNullCheck(expressionToMatch, isCheck); return addNullCheck(subject, isCheck);
} }
return isCheck; return isCheck;
} }
@Nullable @NotNull
private JsExpression translateAsIntrinsicTypeCheck(@NotNull JsExpression expressionToMatch, public JsExpression getIsTypeCheckCallable(@NotNull JetType type) {
@NotNull JetTypeReference typeReference) { JsExpression builtinCheck = getIsTypeCheckCallableForBuiltin(type);
Name typeName = getNameIfStandardType(getTypeByReference(bindingContext(), typeReference)); if (builtinCheck != null) return builtinCheck;
if (typeName == null) {
return null; ClassifierDescriptor typeDescriptor = type.getConstructor().getDeclarationDescriptor();
if (typeDescriptor instanceof TypeParameterDescriptor) {
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) typeDescriptor;
if (typeParameterDescriptor.isReified()) {
return getIsTypeCheckCallableForReifiedType(typeParameterDescriptor);
}
} }
String jsSTypeName; JsNameRef typeName = getClassNameReference(type);
return namer().isInstanceOf(typeName);
}
@Nullable
private JsExpression getIsTypeCheckCallableForBuiltin(@NotNull JetType type) {
Name typeName = getNameIfStandardType(type);
if (NamePredicate.STRING.apply(typeName)) { if (NamePredicate.STRING.apply(typeName)) {
jsSTypeName = "string"; return namer().isTypeOf(program().getStringLiteral("string"));
} }
else if (NamePredicate.BOOLEAN.apply(typeName)) {
jsSTypeName = "boolean"; if (NamePredicate.BOOLEAN.apply(typeName)) {
return namer().isTypeOf(program().getStringLiteral("boolean"));
} }
else if (NamePredicate.LONG.apply(typeName)) {
return JsAstUtils.isLong(expressionToMatch); if (NamePredicate.LONG.apply(typeName)) {
return namer().isInstanceOf(Namer.KOTLIN_LONG_NAME_REF);
} }
else if (NamePredicate.NUMBER.apply(typeName)) {
return JsAstUtils.isNumber(expressionToMatch); if (NamePredicate.NUMBER.apply(typeName)) {
return namer().kotlin(Namer.IS_NUMBER);
} }
else if (NamePredicate.CHAR.apply(typeName)) {
return JsAstUtils.isChar(expressionToMatch); if (NamePredicate.CHAR.apply(typeName)) {
return namer().kotlin(Namer.IS_CHAR);
} }
else if (NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS.apply(typeName)) {
jsSTypeName = "number"; if (NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS.apply(typeName)) {
return namer().isTypeOf(program().getStringLiteral("number"));
} }
else {
return null; return null;
}
@NotNull
private JsExpression getIsTypeCheckCallableForReifiedType(@NotNull TypeParameterDescriptor typeParameter) {
assert typeParameter.isReified(): "Expected reified type, actual: " + typeParameter;
DeclarationDescriptor containingDeclaration = typeParameter.getContainingDeclaration();
assert containingDeclaration instanceof CallableDescriptor:
"Expected type parameter " + typeParameter +
" to be contained in CallableDescriptor, actual: " + containingDeclaration.getClass();
CallableDescriptor containingDescriptor = (CallableDescriptor) containingDeclaration;
int index = countReifiedTypesBefore(containingDescriptor.getTypeParameters(), typeParameter.getIndex());
JsFunction containingFunction = context().getFunctionObject(containingDescriptor);
JsParameter isTypeFunParameter = containingFunction.getParameters().get(index);
return isTypeFunParameter.getName().makeRef();
}
private static int countReifiedTypesBefore(
@NotNull List<TypeParameterDescriptor> typeParameters,
int typeParamIndex
) {
int count = 0;
for (TypeParameterDescriptor typeParameter : typeParameters) {
if (typeParameter.getIndex() >= typeParamIndex) break;
if (typeParameter.isReified()) {
count++;
}
} }
return typeof(expressionToMatch, program().getStringLiteral(jsSTypeName));
return count;
} }
@NotNull @NotNull
@@ -124,9 +168,8 @@ public final class PatternTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
private JsNameRef getClassNameReference(@NotNull JetTypeReference typeReference) { private JsNameRef getClassNameReference(@NotNull JetType type) {
ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type);
(bindingContext(), typeReference);
return context().getQualifiedReference(referencedClass); return context().getQualifiedReference(referencedClass);
} }
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.js.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.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
@@ -44,4 +45,9 @@ public abstract class AbstractTranslator {
protected BindingContext bindingContext() { protected BindingContext bindingContext() {
return context.bindingContext(); return context.bindingContext();
} }
@NotNull
protected Namer namer() {
return context.namer();
}
} }
@@ -18,18 +18,21 @@ package org.jetbrains.kotlin.js.translate.reference
import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.*
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.js.descriptorUtils.nameIfStandardType
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable import org.jetbrains.kotlin.js.translate.context.TemporaryVariable
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.JetType
import java.util.ArrayList import java.util.ArrayList
import java.util.Collections import java.util.Collections
@@ -41,7 +44,7 @@ public class CallArgumentTranslator private (
context: TranslationContext context: TranslationContext
) : AbstractTranslator(context) { ) : AbstractTranslator(context) {
public class ArgumentsInfo( public data class ArgumentsInfo(
public val translateArguments: List<JsExpression>, public val translateArguments: List<JsExpression>,
public val hasSpreadOperator: Boolean, public val hasSpreadOperator: Boolean,
public val cachedReceiver: TemporaryConstVariable? public val cachedReceiver: TemporaryConstVariable?
@@ -174,7 +177,14 @@ public class CallArgumentTranslator private (
val argumentTranslator = CallArgumentTranslator(resolvedCall, receiver, innerContext) val argumentTranslator = CallArgumentTranslator(resolvedCall, receiver, innerContext)
val result = argumentTranslator.translate() val result = argumentTranslator.translate()
context.moveVarsFrom(innerContext) context.moveVarsFrom(innerContext)
return result val callDescriptor = resolvedCall.getCandidateDescriptor()
if (CallExpressionTranslator.shouldBeInlined(callDescriptor)) {
val typeArgs = resolvedCall.getTypeArguments()
return typeArgs.addReifiedTypeArgsTo(result, context)
}
return result;
} }
private fun translateSingleArgument(actualArgument: ResolvedValueArgument, result: MutableList<JsExpression>, context: TranslationContext): ArgumentsKind { private fun translateSingleArgument(actualArgument: ResolvedValueArgument, result: MutableList<JsExpression>, context: TranslationContext): ArgumentsKind {
@@ -320,3 +330,24 @@ public class CallArgumentTranslator private (
} }
} }
private fun Map<TypeParameterDescriptor, JetType>.addReifiedTypeArgsTo(
info: CallArgumentTranslator.ArgumentsInfo,
context: TranslationContext
): CallArgumentTranslator.ArgumentsInfo {
val reifiedTypeArguments = SmartList<JsExpression>()
val patternTranslator = PatternTranslator.newInstance(context)
for (param in keySet().sortBy { it.getIndex() }) {
if (!param.isReified()) continue
val argumentType = get(param)
if (argumentType == null) continue
val isCheckCallable = patternTranslator.getIsTypeCheckCallable(argumentType)
reifiedTypeArguments.add(isCheckCallable)
}
return info.copy(translateArguments = reifiedTypeArguments + info.translateArguments)
}
@@ -168,16 +168,6 @@ public final class JsAstUtils {
return invokeKotlinFunction(Namer.PRIMITIVE_COMPARE_TO, left, right); return invokeKotlinFunction(Namer.PRIMITIVE_COMPARE_TO, left, right);
} }
@NotNull
public static JsExpression isNumber(@NotNull JsExpression expression) {
return invokeKotlinFunction(Namer.IS_NUMBER, expression);
}
@NotNull
public static JsExpression isChar(@NotNull JsExpression expression) {
return invokeKotlinFunction(Namer.IS_CHAR, expression);
}
@NotNull @NotNull
private static JsExpression rangeTo(@NotNull String rangeClassName, @NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) { private static JsExpression rangeTo(@NotNull String rangeClassName, @NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) {
JsNameRef expr = new JsNameRef(rangeClassName, Namer.KOTLIN_NAME); JsNameRef expr = new JsNameRef(rangeClassName, Namer.KOTLIN_NAME);
@@ -196,11 +186,6 @@ public final class JsAstUtils {
return rangeTo(Namer.CHAR_RANGE, rangeStart, rangeEnd); return rangeTo(Namer.CHAR_RANGE, rangeStart, rangeEnd);
} }
@NotNull
public static JsExpression isLong(@NotNull JsExpression expression) {
return instanceOf(expression, Namer.KOTLIN_LONG_NAME_REF);
}
public static JsExpression newLong(long value, @NotNull TranslationContext context) { public static JsExpression newLong(long value, @NotNull TranslationContext context) {
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
int low = (int) value; int low = (int) value;
@@ -259,11 +244,6 @@ public final class JsAstUtils {
return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2); return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2);
} }
@NotNull
public static JsBinaryOperation instanceOf(@NotNull JsExpression op1, @NotNull JsExpression op2) {
return new JsBinaryOperation(JsBinaryOperator.INSTANCEOF, op1, op2);
}
public static void setQualifier(@NotNull JsExpression selector, @Nullable JsExpression receiver) { public static void setQualifier(@NotNull JsExpression selector, @Nullable JsExpression receiver) {
assert (selector instanceof JsInvocation || selector instanceof JsNameRef); assert (selector instanceof JsInvocation || selector instanceof JsNameRef);
if (selector instanceof JsInvocation) { if (selector instanceof JsInvocation) {
+16 -2
View File
@@ -475,9 +475,23 @@ var Kotlin = {};
Object.defineProperty(Kotlin.modules, id, {value: declaration}); Object.defineProperty(Kotlin.modules, id, {value: declaration});
}; };
Kotlin.defineInlineFunction = function(tag, fun) { function defineInlineFunction(tag, fun) {
return fun; return fun;
}; }
Kotlin.defineInlineFunction = defineInlineFunction;
Kotlin.isTypeOf = defineInlineFunction('stdlib.kotlin.isTypeOf', function (type) {
return function (object) {
return typeof object === type;
}
});
Kotlin.isInstanceOf = defineInlineFunction('stdlib.kotlin.isInstanceOf', function (klass) {
return function (object) {
return Kotlin.isType(object, klass);
}
});
Kotlin.kotlinModuleMetadata = function (abiVersion, moduleName, data) { Kotlin.kotlinModuleMetadata = function (abiVersion, moduleName, data) {
}; };