JS: don't optimize RTTI when possible, i.e. don't remove is and as when object is known at compile time to have type in RHS of expression. Substitute kotlin.Any with Object. Fix #KT-7665, #KT-7664

This commit is contained in:
Alexey Andreev
2016-09-09 16:43:31 +03:00
parent c581ec6dd6
commit 66c78be10f
14 changed files with 71 additions and 74 deletions
@@ -1,3 +1,6 @@
// IGNORE_BACKEND: JS
// JS backend does not support Unit well. See KT-13932
val foo: () -> Unit = {}
fun box(): String {
@@ -111,7 +111,6 @@ enum class TypeCheck {
TYPEOF,
INSTANCEOF,
OR_NULL,
IS_ANY,
AND_PREDICATE
}
@@ -137,7 +137,7 @@ public final class RhinoUtils {
}
try {
ScriptableObject scope = getScope(ecmaVersion, context, jsLibraries);
Scriptable scope = getScope(ecmaVersion, context, jsLibraries);
putGlobalVariablesIntoScope(scope, variables);
context.evaluateString(scope, "kotlin.out = new kotlin.BufferedOutput();", "setup Kotlin.out", 0, null);
@@ -158,10 +158,8 @@ public final class RhinoUtils {
}
@NotNull
private static ScriptableObject getScope(@NotNull EcmaVersion version, @NotNull Context context, @NotNull List<String> jsLibraries) {
ScriptableObject scope = context.initStandardObjects(null, false);
scope.setParentScope(getParentScope(version, context, jsLibraries));
return scope;
private static Scriptable getScope(@NotNull EcmaVersion version, @NotNull Context context, @NotNull List<String> jsLibraries) {
return context.newObject(getParentScope(version, context, jsLibraries));
}
@NotNull
@@ -1765,12 +1765,6 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName);
}
@TestMetadata("noRuntimeTypeCheck.kt")
public void testNoRuntimeTypeCheck() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/cast/noRuntimeTypeCheck.kt");
doTest(fileName);
}
@TestMetadata("primitiveToClass.kt")
public void testPrimitiveToClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/cast/primitiveToClass.kt");
@@ -6974,6 +6968,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/standardClasses"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("any.kt")
public void testAny() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/any.kt");
doTest(fileName);
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/array.kt");
@@ -2691,7 +2691,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt");
doTest(fileName);
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("notIs.kt")
@@ -318,11 +318,6 @@ public final class Namer {
return invokeFunctionAndSetTypeCheckMetadata("andPredicate", Arrays.asList(a, b), TypeCheck.AND_PREDICATE);
}
@NotNull
public JsExpression isAny() {
return invokeFunctionAndSetTypeCheckMetadata("isAny", Collections.<JsExpression>emptyList(), TypeCheck.IS_ANY);
}
@NotNull
public JsExpression isComparable() {
return kotlin("isComparable");
@@ -279,6 +279,9 @@ public final class StaticContext {
@NotNull
public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) {
return rootScope.declareName("Object");
}
SuggestedName suggested = nameSuggestion.suggest(descriptor);
if (suggested == null) {
throw new IllegalArgumentException("Can't generate name for root declarations: " + descriptor);
@@ -109,7 +109,7 @@ class CatchTranslator(
// translateIsCheck won't ever return `null` if its second argument is `null`
val typeCheck = with (patternTranslator(nextContext)) {
translateIsCheck(parameterRef, null, paramType)
translateIsCheck(parameterRef, paramType)
}!!
val elseBlock = translateCatches(nextContext, parameterDescriptor, parameterRef, catches)
@@ -44,18 +44,14 @@ import org.jetbrains.kotlin.psi.KtIsExpression;
import org.jetbrains.kotlin.psi.KtTypeReference;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.types.DynamicTypesKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import java.util.Collections;
import static org.jetbrains.kotlin.builtins.FunctionTypesKt.isFunctionTypeOrSubtype;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isAnyOrNullableAny;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isArray;
import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt.getNameIfStandardType;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeByReference;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeForExpression;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.equality;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not;
import static org.jetbrains.kotlin.psi.KtPsiUtil.findChildByType;
@@ -86,8 +82,7 @@ public final class PatternTranslator extends AbstractTranslator {
KtTypeReference typeReference = expression.getRight();
assert typeReference != null: "Cast expression must have type reference";
KotlinType sourceType = getTypeForExpression(bindingContext(), left);
JsExpression isCheck = translateIsCheck(temporary.assignmentExpression(), sourceType, typeReference);
JsExpression isCheck = translateIsCheck(temporary.assignmentExpression(), typeReference);
if (isCheck == null) return expressionToCast;
JsExpression onFail;
@@ -108,8 +103,7 @@ public final class PatternTranslator extends AbstractTranslator {
JsExpression left = Translation.translateAsExpression(expression.getLeftHandSide(), context());
KtTypeReference typeReference = expression.getTypeReference();
assert typeReference != null;
KotlinType sourceType = getTypeForExpression(bindingContext(), expression.getLeftHandSide());
JsExpression result = translateIsCheck(left, sourceType, typeReference);
JsExpression result = translateIsCheck(left, typeReference);
if (result == null) return JsLiteral.getBoolean(!expression.isNegated());
if (expression.isNegated()) {
@@ -119,10 +113,8 @@ public final class PatternTranslator extends AbstractTranslator {
}
@Nullable
public JsExpression translateIsCheck(@NotNull JsExpression subject, @Nullable KotlinType sourceType,
@NotNull KtTypeReference targetTypeReference) {
public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull KtTypeReference targetTypeReference) {
KotlinType targetType = getTypeByReference(bindingContext(), targetTypeReference);
if (sourceType != null && !DynamicTypesKt.isDynamic(sourceType) && TypeUtilsKt.isSubtypeOf(sourceType, targetType)) return null;
JsExpression checkFunReference = doGetIsTypeCheckCallable(targetType);
if (checkFunReference == null) {
@@ -191,8 +183,6 @@ public final class PatternTranslator extends AbstractTranslator {
@Nullable
private JsExpression getIsTypeCheckCallableForBuiltin(@NotNull KotlinType type) {
if (isAnyOrNullableAny(type)) return namer().isAny();
if (isFunctionTypeOrSubtype(type) && !ReflectionTypes.isNumberedKPropertyOrKMutablePropertyType(type)) {
return namer().isTypeOf(program().getStringLiteral("function"));
}
@@ -174,9 +174,7 @@ public final class WhenTranslator extends AbstractTranslator {
KtExpression expressionToMatchNonTranslated = whenExpression.getSubjectExpression();
assert expressionToMatchNonTranslated != null : "expressionToMatch != null => expressionToMatchNonTranslated != null: " +
PsiUtilsKt.getTextWithLocation(conditionIsPattern);
KotlinType expressionToMatchType = BindingUtils.getTypeForExpression(bindingContext(), expressionToMatchNonTranslated);
JsExpression result = Translation.patternTranslator(context).translateIsCheck(expressionToMatch, expressionToMatchType,
typeReference);
JsExpression result = Translation.patternTranslator(context).translateIsCheck(expressionToMatch, typeReference);
return result != null ? result : JsLiteral.TRUE;
}
@@ -71,11 +71,6 @@ private class TypeCheckRewritingVisitor(private val context: TranslationContext)
private fun getReplacement(callee: JsInvocation, calleeArguments: List<JsExpression>, argument: JsExpression): JsExpression? {
val typeCheck = callee.typeCheck
return when (typeCheck) {
TypeCheck.IS_ANY -> {
// `Kotlin.isAny()(argument)` -> `argument != null`
if (calleeArguments.isEmpty()) TranslationUtils.isNotNullCheck(argument) else null
}
TypeCheck.TYPEOF -> {
// `Kotlin.isTypeOf(calleeArgument)(argument)` -> `typeOf argument === calleeArgument`
if (calleeArguments.size == 1) typeOfIs(argument, calleeArguments[0] as JsStringLiteral) else null
@@ -1,28 +0,0 @@
package foo
// CHECK_NOT_CALLED_IN_SCOPE: scope=box function=isType
open class A()
class B() : A()
fun box(): String {
assertTrue(B() is A)
assertTrue(B() is A?)
assertTrue(B() is B)
assertTrue(B() is B?)
assertTrue((B() as B?) is A?)
assertTrue((null as A?) is A?)
assertNotEquals(null, B() as? A)
assertNotEquals(null, B() as? A?)
assertNotEquals(null, B() as? B)
assertNotEquals(null, B() as? B?)
assertNotEquals(null, B() as A)
assertNotEquals(null, B() as A?)
assertNotEquals(null, B() as B)
assertNotEquals(null, B() as B?)
return "OK"
}
+32
View File
@@ -0,0 +1,32 @@
// CHECK_CALLED_IN_SCOPE: function=isType scope=box
package foo
class A : Any()
fun Any?.asAny() = this
fun box(): String {
val x = Any().asAny()
if (x !is Any) return "fail1"
if (x.asDynamic().constructor !== js("Object")) return "fail1a"
if (A().asAny() !is Any) return "fail2"
if (arrayOf(1, 2, 3).asAny() !is Any) return "fail3"
if (createNakedObject() is Any) return "fail4"
if (({ }).asAny() !is Any) return "fail5"
if ((23).asAny() !is Any) return "fail6"
if ((3.14).asAny() !is Any) return "fail7"
if (false.asAny() !is Any) return "fail8"
if ("bar".asAny() !is Any) return "fail9"
return "OK"
}
fun createNakedObject(): Any? = js("Object.create(null)")
+12 -6
View File
@@ -92,6 +92,18 @@
* @returns {Boolean}
*/
Kotlin.isType = function (object, klass) {
if (klass === Object) {
switch (typeof object) {
case "string":
case "number":
case "boolean":
case "function":
return true;
default:
return object instanceof Object;
}
}
if (object == null || klass == null || (typeof object !== 'object' && typeof object !== 'function')) {
return false;
}
@@ -258,12 +270,6 @@
return object == null || fn(object);
}
};
Kotlin.isAny = function () {
return function (object) {
return object != null;
}
};
Kotlin.andPredicate = function (a, b) {
return function (object) {