JS: support is-check on reified parameter
This commit is contained in:
@@ -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
|
||||
|
||||
+9
@@ -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;
|
||||
|
||||
+84
-41
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+36
-5
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user