JS/RTTI: fixed is-check on nullable reified parameters

#KT-7016 fixed
    #KT-8012 fixed
This commit is contained in:
Alexey Tsvetkov
2015-06-10 16:44:06 +03:00
committed by Alexey Andreev
parent e1d10abfa1
commit 0b9c041d0a
11 changed files with 121 additions and 56 deletions
@@ -59,5 +59,6 @@ var HasMetadata.sideEffects: Boolean by MetadataProperty(default = true)
enum class TypeCheck {
TYPEOF,
INSTANCEOF
INSTANCEOF,
OR_NULL
}
@@ -380,16 +380,27 @@ public final class Namer {
@NotNull
public JsExpression isTypeOf(@NotNull JsExpression type) {
JsInvocation invocation = new JsInvocation(kotlin("isTypeOf"), type);
MetadataProperties.setTypeCheck(invocation, TypeCheck.TYPEOF);
MetadataProperties.setSideEffects(invocation, false);
return invocation;
return invokeFunctionAndSetTypeCheckMetadata("isTypeOf", type, TypeCheck.TYPEOF);
}
@NotNull
public JsExpression isInstanceOf(@NotNull JsExpression type) {
JsInvocation invocation = new JsInvocation(kotlin("isInstanceOf"), type);
MetadataProperties.setTypeCheck(invocation, TypeCheck.INSTANCEOF);
return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.INSTANCEOF);
}
@NotNull
public JsExpression orNull(@NotNull JsExpression callable) {
return invokeFunctionAndSetTypeCheckMetadata("orNull", callable, TypeCheck.OR_NULL);
}
@NotNull
private JsExpression invokeFunctionAndSetTypeCheckMetadata(
@NotNull String functionName,
@NotNull JsExpression argument,
@NotNull TypeCheck metadata
) {
JsInvocation invocation = new JsInvocation(kotlin(functionName), argument);
MetadataProperties.setTypeCheck(invocation, metadata);
MetadataProperties.setSideEffects(invocation, false);
return invocation;
}
@@ -16,18 +16,19 @@
package org.jetbrains.kotlin.js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.patterns.NamePredicate;
import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
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.TranslationUtils;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtIsExpression;
@@ -35,8 +36,8 @@ import org.jetbrains.kotlin.psi.KtTypeReference;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.types.KotlinType;
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.equality;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.negated;
public final class PatternTranslator extends AbstractTranslator {
@@ -65,17 +66,20 @@ public final class PatternTranslator extends AbstractTranslator {
public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull KtTypeReference typeReference) {
KotlinType type = BindingUtils.getTypeByReference(bindingContext(), typeReference);
JsExpression checkFunReference = getIsTypeCheckCallable(type);
JsInvocation isCheck = new JsInvocation(checkFunReference, subject);
if (isNullable(typeReference)) {
return addNullCheck(subject, isCheck);
}
return isCheck;
return new JsInvocation(checkFunReference, subject);
}
@NotNull
public JsExpression getIsTypeCheckCallable(@NotNull KotlinType type) {
JsExpression callable = doGetIsTypeCheckCallable(type);
if (type.isMarkedNullable()) return namer().orNull(callable);
return callable;
}
@NotNull
private JsExpression doGetIsTypeCheckCallable(@NotNull KotlinType type) {
JsExpression builtinCheck = getIsTypeCheckCallableForBuiltin(type);
if (builtinCheck != null) return builtinCheck;
@@ -137,15 +141,6 @@ public final class PatternTranslator extends AbstractTranslator {
return alias;
}
@NotNull
private static JsExpression addNullCheck(@NotNull JsExpression expressionToMatch, @NotNull JsInvocation isCheck) {
return or(TranslationUtils.isNullCheck(expressionToMatch), isCheck);
}
private boolean isNullable(KtTypeReference typeReference) {
return getTypeByReference(bindingContext(), typeReference).isMarkedNullable();
}
@NotNull
private JsNameRef getClassNameReference(@NotNull KotlinType type) {
ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type);
@@ -20,29 +20,63 @@ import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.TypeCheck
import com.google.dart.compiler.backend.js.ast.metadata.typeCheck
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.or
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.typeOfIs
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.isNullCheck
import java.util.*
fun expandIsCalls(node: JsNode, context: TranslationContext) {
val visitor = object : JsVisitorWithContextImpl() {
override fun visit(x: JsInvocation, ctx: JsContext<JsNode>): Boolean {
val callee = x.qualifier as? JsInvocation
val instance = x.arguments.firstOrNull()
val type = callee?.arguments?.firstOrNull()
TypeCheckRewritingVisitor(context).accept(node)
}
val replacement = when (callee?.typeCheck) {
TypeCheck.TYPEOF -> typeOfIs(instance!!, type as JsStringLiteral)
TypeCheck.INSTANCEOF -> context.namer().isInstanceOf(instance!!, type!!)
else -> null
}
private class TypeCheckRewritingVisitor(private val context: TranslationContext) : JsVisitorWithContextImpl() {
if (replacement != null) {
ctx.replaceMe(replacement)
return false
}
private val scopes = Stack<JsScope>()
return super.visit(x, ctx)
}
override fun visit(x: JsFunction, ctx: JsContext<*>): Boolean {
scopes.push(x.scope)
return super.visit(x, ctx)
}
visitor.accept(node)
override fun endVisit(x: JsFunction, ctx: JsContext<*>) {
scopes.pop()
super.endVisit(x, ctx)
}
override fun visit(x: JsInvocation, ctx: JsContext<JsNode>): Boolean {
// callee(calleeArgument)(argument)
val callee = x.qualifier as? JsInvocation
val calleeArgument = callee?.arguments?.firstOrNull()
val argument = x.arguments.firstOrNull()
if (callee != null && calleeArgument != null && argument != null) {
val replacement = getReplacement(callee, calleeArgument, argument)
if (replacement != null) {
ctx.replaceMe(accept(replacement))
return false
}
}
return true
}
private fun getReplacement(callee: JsInvocation, calleeArgument: JsExpression, argument: JsExpression): JsExpression? {
return when (callee.typeCheck) {
// Kotlin.isTypeOf(calleeArgument)(argument) -> typeOf argument === calleeArgument
TypeCheck.TYPEOF ->
typeOfIs(argument, calleeArgument as JsStringLiteral)
// Kotlin.isInstanceOf(calleeArgument)(argument) -> argument instanceof calleeArgument
TypeCheck.INSTANCEOF ->
context.namer().isInstanceOf(argument, calleeArgument)
// Kotlin.orNull(calleeArgument)(argument) -> argument === null || calleeArgument(argument)
TypeCheck.OR_NULL ->
or(isNullCheck(argument), JsInvocation(calleeArgument, argument))
else ->
null
}
}
}
+12 -8
View File
@@ -533,23 +533,27 @@ var Kotlin = {};
Object.defineProperty(Kotlin.modules, id, {value: declaration});
};
function defineInlineFunction(tag, fun) {
Kotlin.defineInlineFunction = function(tag, fun) {
return fun;
}
};
Kotlin.defineInlineFunction = defineInlineFunction;
Kotlin.isTypeOf = defineInlineFunction('stdlib.kotlin.isTypeOf', function (type) {
Kotlin.isTypeOf = function(type) {
return function (object) {
return typeof object === type;
}
});
};
Kotlin.isInstanceOf = defineInlineFunction('stdlib.kotlin.isInstanceOf', function (klass) {
Kotlin.isInstanceOf = function (klass) {
return function (object) {
return Kotlin.isType(object, klass);
}
});
};
Kotlin.orNull = function (fn) {
return function (object) {
return object == null || fn(object);
}
};
Kotlin.kotlinModuleMetadata = function (abiVersion, moduleName, data) {
};
@@ -1,4 +1,4 @@
package foo
inline fun <reified T> isInstance(x: Any): Boolean =
x is T
inline fun <reified T> isInstance(x: Any?): Boolean =
x is T
+4
View File
@@ -7,5 +7,9 @@ fun box(): String {
assertEquals(true, isInstance<Boolean>(false))
assertEquals(false, isInstance<Boolean>("true"))
assertEquals(true, isInstance<Boolean?>(true), "isInstance<Boolean?>(true)")
assertEquals(true, isInstance<Boolean?>(null), "isInstance<Boolean?>(null)")
assertEquals(false, isInstance<Boolean?>("true"), "isInstance<Boolean?>(\"true\")")
return "OK"
}
+4
View File
@@ -7,5 +7,9 @@ fun box(): String {
assertEquals(false, isInstance<Char>(""))
assertEquals(false, isInstance<Char>("cc"))
assertEquals(true, isInstance<Char?>('c'), "isInstance<Char?>('c')")
assertEquals(true, isInstance<Char?>(null), "isInstance<Char?>(null)")
assertEquals(false, isInstance<Char?>("cc"), "isInstance<Char?>(\"cc\")")
return "OK"
}
+4
View File
@@ -17,5 +17,9 @@ fun box(): String {
assertEquals(true, isInstance<D>(D()))
assertEquals(true, isInstance<C>(D()))
assertEquals(true, isInstance<D?>(D()), "isInstance<D?>(D())")
assertEquals(true, isInstance<D?>(null), "isInstance<D?>(null)")
assertEquals(false, isInstance<D?>(C()), "isInstance<D?>(C())")
return "OK"
}
+4
View File
@@ -15,5 +15,9 @@ fun box(): String {
assertEquals(true, isInstance<Number>(0.0))
assertEquals(false, isInstance<Number>("0"))
assertEquals(true, isInstance<Int?>(0), "isInstance<Int?>(0)")
assertEquals(true, isInstance<Int?>(null), "isInstance<Int?>(null)")
assertEquals(false, isInstance<Int?>(true), "isInstance<Int?>(true)")
return "OK"
}
+4
View File
@@ -6,5 +6,9 @@ fun box(): String {
assertEquals(true, isInstance<String>(""))
assertEquals(true, isInstance<String>("a"))
assertEquals(true, isInstance<String?>(""), "isInstance<String?>(\"\")")
assertEquals(true, isInstance<String?>(null), "isInstance<String?>(null)")
assertEquals(false, isInstance<String?>(10), "isInstance<String?>(10)")
return "OK"
}