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
public JsExpression isOperationReference() {
return kotlin(isTypeName);
public JsExpression isTypeOf(@NotNull JsExpression type) {
return new JsInvocation(kotlin("isTypeOf"), type);
}
@NotNull
public JsExpression isInstanceOf(@NotNull JsExpression type) {
return new JsInvocation(kotlin("isInstanceOf"), type);
}
@NotNull
@@ -126,6 +126,15 @@ public final class FunctionTranslator extends AbstractTranslator {
}
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);
addParameters(jsParameters, descriptor, context());
return jsParameters;
@@ -16,23 +16,25 @@
package org.jetbrains.kotlin.js.translate.expression;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsInvocation;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
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.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.patterns.NamePredicate;
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.name.Name;
import org.jetbrains.kotlin.psi.JetExpression;
import org.jetbrains.kotlin.psi.JetIsExpression;
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.JsAstUtils.*;
@@ -63,55 +65,97 @@ public final class PatternTranslator extends AbstractTranslator {
@NotNull
public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull JetTypeReference typeReference) {
JsExpression result = translateAsIntrinsicTypeCheck(subject, typeReference);
if (result != null) {
return result;
}
return translateAsIsCheck(subject, typeReference);
}
JetType type = BindingUtils.getTypeByReference(bindingContext(), typeReference);
JsExpression checkFunReference = getIsTypeCheckCallable(type);
JsInvocation isCheck = new JsInvocation(checkFunReference, subject);
@NotNull
private JsExpression translateAsIsCheck(@NotNull JsExpression expressionToMatch,
@NotNull JetTypeReference typeReference) {
JsInvocation isCheck = new JsInvocation(context().namer().isOperationReference(),
expressionToMatch, getClassNameReference(typeReference));
if (isNullable(typeReference)) {
return addNullCheck(expressionToMatch, isCheck);
return addNullCheck(subject, isCheck);
}
return isCheck;
}
@Nullable
private JsExpression translateAsIntrinsicTypeCheck(@NotNull JsExpression expressionToMatch,
@NotNull JetTypeReference typeReference) {
Name typeName = getNameIfStandardType(getTypeByReference(bindingContext(), typeReference));
if (typeName == null) {
return null;
@NotNull
public JsExpression getIsTypeCheckCallable(@NotNull JetType type) {
JsExpression builtinCheck = getIsTypeCheckCallableForBuiltin(type);
if (builtinCheck != null) return builtinCheck;
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)) {
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
@@ -124,9 +168,8 @@ public final class PatternTranslator extends AbstractTranslator {
}
@NotNull
private JsNameRef getClassNameReference(@NotNull JetTypeReference typeReference) {
ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference
(bindingContext(), typeReference);
private JsNameRef getClassNameReference(@NotNull JetType type) {
ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type);
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.resolve.BindingContext;
@@ -44,4 +45,9 @@ public abstract class AbstractTranslator {
protected BindingContext 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.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.js.descriptorUtils.nameIfStandardType
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable
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.Translation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.JetType
import java.util.ArrayList
import java.util.Collections
@@ -41,7 +44,7 @@ public class CallArgumentTranslator private (
context: TranslationContext
) : AbstractTranslator(context) {
public class ArgumentsInfo(
public data class ArgumentsInfo(
public val translateArguments: List<JsExpression>,
public val hasSpreadOperator: Boolean,
public val cachedReceiver: TemporaryConstVariable?
@@ -174,7 +177,14 @@ public class CallArgumentTranslator private (
val argumentTranslator = CallArgumentTranslator(resolvedCall, receiver, innerContext)
val result = argumentTranslator.translate()
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 {
@@ -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);
}
@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
private static JsExpression rangeTo(@NotNull String rangeClassName, @NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) {
JsNameRef expr = new JsNameRef(rangeClassName, Namer.KOTLIN_NAME);
@@ -196,11 +186,6 @@ public final class JsAstUtils {
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) {
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
int low = (int) value;
@@ -259,11 +244,6 @@ public final class JsAstUtils {
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) {
assert (selector instanceof JsInvocation || selector instanceof JsNameRef);
if (selector instanceof JsInvocation) {
+16 -2
View File
@@ -475,9 +475,23 @@ var Kotlin = {};
Object.defineProperty(Kotlin.modules, id, {value: declaration});
};
Kotlin.defineInlineFunction = function(tag, fun) {
function defineInlineFunction(tag, 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) {
};