From 83f8cfaa667fad03252126d249786d23cd17387b Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Wed, 13 Jun 2018 15:54:34 +0300 Subject: [PATCH] [JS IR BE] hashCode, toString, number conversion support --- .../kotlin/ir/backend/js/JsIntrinsics.kt | 24 +- .../js/lower/IntrinsicifyCallsLowering.kt | 300 +++++++++++++++--- .../ir/backend/js/utils/ConversionNames.kt | 18 ++ .../backend/js/utils/descriptorBasedUtils.kt | 10 +- .../kotlin/ir/types/irTypePredicates.kt | 6 +- .../explicitEqualsOnDouble.kt | 1 - .../function/local/equalsHashCode.kt | 1 - .../box/classes/delegationMethodsWithArgs.kt | 1 - .../testData/codegen/box/classes/kt3114.kt | 1 - .../codegen/box/classes/privateToThis.kt | 1 - .../box/dataClasses/hashCode/boolean.kt | 1 - .../codegen/box/dataClasses/hashCode/byte.kt | 1 - .../box/dataClasses/hashCode/double.kt | 1 - .../codegen/box/dataClasses/hashCode/float.kt | 1 - .../codegen/box/dataClasses/hashCode/int.kt | 1 - .../codegen/box/dataClasses/hashCode/short.kt | 1 - .../provideDelegate/extensionDelegated.kt | 3 +- .../codegen/box/functions/defaultargs1.kt | 1 - .../testData/codegen/box/functions/ea33909.kt | 1 - .../functionExpression/functionExpression.kt | 1 - .../functionLiteralExpression.kt | 1 - .../testData/codegen/box/functions/kt2929.kt | 1 - .../box/ieee754/explicitCompareCall.kt | 1 - .../codegen/box/ieee754/explicitEqualsCall.kt | 1 - .../testData/codegen/box/ieee754/safeCall.kt | 1 - ...tToDifferentTypes_properIeeeComparisons.kt | 1 - .../postfixIncrementOnShortSmartCast.kt | 1 - .../codegen/box/intrinsics/ea35953.kt | 1 - .../codegen/box/intrinsics/kt12125_2.kt | 1 - .../codegen/box/intrinsics/kt12125_inc_2.kt | 1 - .../operatorConventions/compareTo/boolean.kt | 1 - .../compareTo/doubleInt.kt | 1 - .../compareTo/extensionObject.kt | 1 - .../compareTo/intDouble.kt | 1 - .../generated/boxedEqPrimitiveByte.kt | 1 - .../generated/boxedEqPrimitiveShort.kt | 1 - .../generated/primitiveEqBoxedByte.kt | 1 - .../generated/primitiveEqBoxedShort.kt | 1 - .../generated/primitiveEqObjectByte.kt | 1 - .../generated/primitiveEqObjectShort.kt | 1 - .../codegen/box/primitiveTypes/kt1093.kt | 1 - .../codegen/box/primitiveTypes/kt2269.kt | 1 - .../codegen/box/primitiveTypes/kt2768.kt | 1 - .../codegen/box/primitiveTypes/kt3078.kt | 1 - .../codegen/box/primitiveTypes/kt3517.kt | 1 - .../codegen/box/primitiveTypes/kt3576.kt | 1 - .../codegen/box/primitiveTypes/kt882.kt | 1 - .../codegen/box/reified/reifiedChain.kt | 3 +- .../codegen/box/unaryOp/callWithCommonType.kt | 1 - .../kotlin/builtins/KotlinBuiltIns.java | 9 + .../jetbrains/kotlin/js/test/BasicBoxTest.kt | 6 +- .../kotlin/js/test/BasicIrBoxTest.kt | 3 + .../js/test/semantics/BoxJsTestGenerated.java | 23 ++ .../test/semantics/IrBoxJsTestGenerated.java | 23 ++ .../testData/box/builtins/hashCode.kt | 32 ++ .../testData/box/builtins/toString.kt | 20 ++ .../extensionFunWithDefArgs.kt | 1 - .../delegation/delegationMethodsWithArgs.kt | 1 - .../expression/equals/explicitEqualsMethod.kt | 1 - .../explicitEqualsMethodForPrimitives.kt | 1 - .../compareToIntrinsicWithSideEffect.kt | 1 - .../implicitReceiverInExtension.kt | 1 - .../box/extensionProperty/absExtension.kt | 1 - .../testData/box/inlineMultiFile/generics.kt | 1 - .../box/number/byteAndShortConversions.kt | 1 - .../testData/box/number/doubleConversions.kt | 1 - .../testData/box/number/intConversions.kt | 1 - .../testData/box/number/intDivFloat.kt | 1 - libraries/stdlib/js/irRuntime/compareTo.kt | 43 +++ libraries/stdlib/js/irRuntime/core.kt | 101 ++++++ libraries/stdlib/js/irRuntime/dummy.kt | 0 .../stdlib/js/irRuntime/numberConversion.kt | 26 ++ .../stdlib/js/irRuntime/typeCheckUtils.kt | 6 +- 73 files changed, 602 insertions(+), 108 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/ConversionNames.kt create mode 100644 js/js.translator/testData/box/builtins/hashCode.kt create mode 100644 js/js.translator/testData/box/builtins/toString.kt create mode 100644 libraries/stdlib/js/irRuntime/compareTo.kt create mode 100644 libraries/stdlib/js/irRuntime/core.kt delete mode 100644 libraries/stdlib/js/irRuntime/dummy.kt create mode 100644 libraries/stdlib/js/irRuntime/numberConversion.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index c09ec6cc943..592fc5b91d0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.types.Variance class JsIntrinsics( private val module: ModuleDescriptor, private val irBuiltIns: IrBuiltIns, - context: JsIrBackendContext + val context: JsIrBackendContext ) { private val stubBuilder = DeclarationStubGenerator(module, context.symbolTable, JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB) @@ -89,15 +89,35 @@ class JsIntrinsics( val jsTypeOf = unOp("jsTypeOf", irBuiltIns.string) + // Number conversions: + + val jsAsIs = getInternalFunction("asIs") // as-is conversion. Call can be replaced with first paramenter + val jsNumberToByte = getInternalFunction("numberToByte") + val jsNumberToDouble = getInternalFunction("numberToDouble") + val jsNumberToInt = getInternalFunction("numberToInt") + val jsNumberToShort = getInternalFunction("numberToShort") + val jsToByte = getInternalFunction("toByte") + val jsToShort = getInternalFunction("toShort") + + // Other: val jsObjectCreate = defineObjectCreateIntrinsic() // Object.create val jsSetJSField = defineSetJSPropertyIntrinsic() // till we don't have dynamic type we use intrinsic which sets a field with any name val jsToJsType = defineToJsType() // creates name reference to KotlinType - val jsCode = context.getInternalFunctions("js").singleOrNull()?.let { context.symbolTable.referenceFunction(it) } // js("") + val jsCode = getInternalFunction("js") // js("") + val jsHashCode = getInternalFunction("hashCode") + val jsToString = getInternalFunction("toString") + val jsCompareTo = getInternalFunction("compareTo") + val jsEquals = getInternalFunction("equals") + + // Helpers: + private fun getInternalFunction(name: String) = + context.symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single()) + private fun defineToJsType(): IrSimpleFunction { val desc = SimpleFunctionDescriptorImpl.create( module, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt index b19a112ea67..e206d697057 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt @@ -6,11 +6,14 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.utils.isNullable import org.jetbrains.kotlin.backend.common.utils.isSubtypeOfClass import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames +import org.jetbrains.kotlin.ir.backend.js.utils.isFakeOverriddenFromAny import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction @@ -19,19 +22,23 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.originalKotlinType import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.SimpleType + class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileLoweringPass { + private val intrinsics = context.intrinsics + private val irBuiltIns = context.irBuiltIns // TODO: should/can we unify these maps? - private val memberToTransformer: Map IrCall> - private val memberToIrFunction: Map + private val memberToTransformer: Map IrExpression> + private val memberToIrFunction: Map private val symbolToIrFunction: Map - private val nameToIrTransformer: Map IrCall> + private val nameToIrTransformer: Map IrExpression> init { memberToIrFunction = mutableMapOf() @@ -43,65 +50,100 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL memberToIrFunction.run { for (type in primitiveNumbers) { - op(type, OperatorNames.UNARY_PLUS, context.intrinsics.jsUnaryPlus) - op(type, OperatorNames.UNARY_MINUS, context.intrinsics.jsUnaryMinus) + op(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus) + op(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus) - op(type, OperatorNames.ADD, context.intrinsics.jsPlus) - op(type, OperatorNames.SUB, context.intrinsics.jsMinus) - op(type, OperatorNames.MUL, context.intrinsics.jsMult) - op(type, OperatorNames.DIV, context.intrinsics.jsDiv) - op(type, OperatorNames.MOD, context.intrinsics.jsMod) - op(type, OperatorNames.REM, context.intrinsics.jsMod) + op(type, OperatorNames.ADD, intrinsics.jsPlus) + op(type, OperatorNames.SUB, intrinsics.jsMinus) + op(type, OperatorNames.MUL, intrinsics.jsMult) + op(type, OperatorNames.DIV, intrinsics.jsDiv) + op(type, OperatorNames.MOD, intrinsics.jsMod) + op(type, OperatorNames.REM, intrinsics.jsMod) } - context.irBuiltIns.stringType.let { - op(it, OperatorNames.ADD, context.intrinsics.jsPlus) + irBuiltIns.stringType.let { + op(it, OperatorNames.ADD, intrinsics.jsPlus) } - context.irBuiltIns.intType.let { - op(it, OperatorNames.SHL, context.intrinsics.jsBitShiftL) - op(it, OperatorNames.SHR, context.intrinsics.jsBitShiftR) - op(it, OperatorNames.SHRU, context.intrinsics.jsBitShiftRU) - op(it, OperatorNames.AND, context.intrinsics.jsBitAnd) - op(it, OperatorNames.OR, context.intrinsics.jsBitOr) - op(it, OperatorNames.XOR, context.intrinsics.jsBitXor) - op(it, OperatorNames.INV, context.intrinsics.jsBitNot) + irBuiltIns.intType.let { + op(it, OperatorNames.SHL, intrinsics.jsBitShiftL) + op(it, OperatorNames.SHR, intrinsics.jsBitShiftR) + op(it, OperatorNames.SHRU, intrinsics.jsBitShiftRU) + op(it, OperatorNames.AND, intrinsics.jsBitAnd) + op(it, OperatorNames.OR, intrinsics.jsBitOr) + op(it, OperatorNames.XOR, intrinsics.jsBitXor) + op(it, OperatorNames.INV, intrinsics.jsBitNot) } - context.irBuiltIns.booleanType.let { - op(it, OperatorNames.AND, context.intrinsics.jsBitAnd) - op(it, OperatorNames.OR, context.intrinsics.jsBitOr) - op(it, OperatorNames.NOT, context.intrinsics.jsNot) - op(it, OperatorNames.XOR, context.intrinsics.jsBitXor) + irBuiltIns.booleanType.let { + op(it, OperatorNames.AND, intrinsics.jsBitAnd) + op(it, OperatorNames.OR, intrinsics.jsBitOr) + op(it, OperatorNames.NOT, intrinsics.jsNot) + op(it, OperatorNames.XOR, intrinsics.jsBitXor) + } + + // Conversion rules are ported from NumberAndCharConversionFIF + // TODO: Add Char, Long and Number conversions + + irBuiltIns.byteType.let { + op(it, ConversionNames.TO_BYTE, intrinsics.jsAsIs) + op(it, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) + op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) + op(it, ConversionNames.TO_INT, intrinsics.jsAsIs) + op(it, ConversionNames.TO_SHORT, intrinsics.jsAsIs) + } + + for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) { + op(type, ConversionNames.TO_BYTE, intrinsics.jsNumberToByte) + op(type, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) + op(type, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) + op(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt) + op(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort) + } + + irBuiltIns.intType.let { + op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) + op(it, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) + op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) + op(it, ConversionNames.TO_INT, intrinsics.jsAsIs) + op(it, ConversionNames.TO_SHORT, intrinsics.jsToShort) + } + + irBuiltIns.shortType.let { + op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) + op(it, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) + op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) + op(it, ConversionNames.TO_INT, intrinsics.jsAsIs) + op(it, ConversionNames.TO_SHORT, intrinsics.jsAsIs) } } symbolToIrFunction.run { - add(context.irBuiltIns.eqeqeqSymbol, context.intrinsics.jsEqeqeq) + add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq) // TODO: implement it a right way - add(context.irBuiltIns.eqeqSymbol, context.intrinsics.jsEqeq) + add(irBuiltIns.eqeqSymbol, intrinsics.jsEqeq) // TODO: implement it a right way - add(context.irBuiltIns.ieee754equalsFunByOperandType, context.intrinsics.jsEqeqeq) + add(irBuiltIns.ieee754equalsFunByOperandType, intrinsics.jsEqeqeq) - add(context.irBuiltIns.booleanNotSymbol, context.intrinsics.jsNot) + add(irBuiltIns.booleanNotSymbol, intrinsics.jsNot) - add(context.irBuiltIns.lessFunByOperandType, context.intrinsics.jsLt) - add(context.irBuiltIns.lessOrEqualFunByOperandType, context.intrinsics.jsLtEq) - add(context.irBuiltIns.greaterFunByOperandType, context.intrinsics.jsGt) - add(context.irBuiltIns.greaterOrEqualFunByOperandType, context.intrinsics.jsGtEq) + add(irBuiltIns.lessFunByOperandType, intrinsics.jsLt) + add(irBuiltIns.lessOrEqualFunByOperandType, intrinsics.jsLtEq) + add(irBuiltIns.greaterFunByOperandType, intrinsics.jsGt) + add(irBuiltIns.greaterOrEqualFunByOperandType, intrinsics.jsGtEq) } memberToTransformer.run { for (type in primitiveNumbers) { // TODO: use increment and decrement when it's possible op(type, OperatorNames.INC) { - irCall(it, context.intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply { - putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, 1)) + irCall(it, intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply { + putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1)) } } op(type, OperatorNames.DEC) { - irCall(it, context.intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply { - putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, 1)) + irCall(it, intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply { + putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1)) } } } @@ -124,6 +166,24 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL { call -> call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false}, { call -> irCall(call, context.intrinsics.jsPropertySet.symbol, dispatchReceiverAsFirstArgument = true)} ) + + addWithPredicate( + Name.identifier("hashCode"), + { call -> (call.superQualifier == null) && (call.symbol.owner.descriptor.isFakeOverriddenFromAny()) }, + { call -> irCall(call, intrinsics.jsHashCode, dispatchReceiverAsFirstArgument = true) } + ) + + addWithPredicate( + Name.identifier("toString"), ::shouldReplaceToStringWithRuntimeCall, + { call -> irCall(call, intrinsics.jsToString, dispatchReceiverAsFirstArgument = true) } + ) + + addWithPredicate( + Name.identifier("compareTo"), ::shouldReplaceCompareToWithRuntimeCall, + { call -> irCall(call, intrinsics.jsCompareTo, dispatchReceiverAsFirstArgument = true) } + ) + + put(Name.identifier("equals"), ::transformEquals) } } @@ -146,8 +206,11 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL val key = SimpleMemberKey(it.type, symbol.owner.name) memberToIrFunction[key]?.let { + if (it == intrinsics.jsAsIs) { + return call.dispatchReceiver!! + } // TODO: don't apply intrinsics when type of receiver or argument is Long - return irCall(call, it.symbol, dispatchReceiverAsFirstArgument = true) + return irCall(call, it, dispatchReceiverAsFirstArgument = true) } memberToTransformer[key]?.let { @@ -159,17 +222,156 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL return it(call) } } - } return call } }, null) } + + private fun transformEquals(call: IrCall): IrExpression { + if (call.superQualifier != null) return call + val symbol = call.symbol + if (!symbol.isBound) return call + val function = (symbol.owner as? IrFunction) ?: return call + val lhs = function.dispatchReceiverParameter ?: function.extensionReceiverParameter ?: return call + val rhs = call.getValueArgument(0) ?: return call + return when (translateEquals(lhs.type, rhs.type)) { + is IdentityOperator -> irCall(call, intrinsics.jsEqeqeq.symbol) + is EqualityOperator -> irCall(call, intrinsics.jsEqeq.symbol) + is RuntimeFunctionCall -> irCall(call, intrinsics.jsEquals, true) + is RuntimeOrMethodCall -> if (symbol.owner.descriptor.isFakeOverriddenFromAny()) { + irCall(call, intrinsics.jsEquals, true) + } else { + call + } + } + } } +fun shouldReplaceToStringWithRuntimeCall(call: IrCall): Boolean { + if (call.superQualifier != null) return false + + // TODO: (KOTLIN-CR-2079) + // - User defined extension functions Any?.toString() call can be lost during lowering. + // - Use direct method call for dynamic types??? + // - Define Any?.toString() in runtime library and stop intrincifying extensions + + val receiverParameterType = with(call.symbol.owner) { + dispatchReceiverParameter ?: extensionReceiverParameter + }?.type ?: return false + + return receiverParameterType.run { + this.isArray() || + this.isAny() || this.isNullable() || this is IrDynamicType || this.isString() + } +} + +fun shouldReplaceCompareToWithRuntimeCall(call: IrCall): Boolean { + if (call.superQualifier != null) return false + + // TODO: Replace all compareTo to with runtime call when Comparable<*>.compareTo() bridge is implemented + return call.symbol.owner.dispatchReceiverParameter?.run { + type is IrDynamicType + || type.isJsNumber() + || type.isNullableJsNumber() + || type.isBoolean() || type.isNullableBoolean() + || type.isString() || type.isNullableString() + } ?: false +} + +/* + Equality translation table: + +| | JsN | JsN? | Long | Long? | Bool | Bool? | Other | Other? | +|----------------|------|------|------|-------|------|-------|-------|--------| +| JsN | === | === | == | == | === | === | K.eq | K.eq | +| JsN? | === | == | == | == | === | K.eq | K.eq | K.eq | +| Long | == | == | K.eq | K.eq | === | === | K.eq | K.eq | +| Long? | == | == | K.eq | K.eq | === | K.eq | K.eq | K.eq | +| Bool | === | === | === | === | === | === | K.eq | K.eq | +| Bool? | === | K.eq | === | K.eq | === | == | K.eq | K.eq | +| Other with .eq | .eq | .eq | .eq | .eq | .eq | .eq | .eq | .eq | +| Other w/o .eq | K.eq | K.eq | K.eq | K.eq | K.eq | K.eq | K.eq | K.eq | +| Other? | K.eq | K.eq | K.eq | K.eq | K.eq | K.eq | K.eq | K.eq | + + +JsNumber -- type lowered to JS Number + K.eq -- runtime library call + .eq -- .equals(x) method call + + */ + +sealed class EqualityLoweringType +class IdentityOperator : EqualityLoweringType() +class EqualityOperator : EqualityLoweringType() +class RuntimeFunctionCall : EqualityLoweringType() +class RuntimeOrMethodCall : EqualityLoweringType() + +fun translateEquals(lhs: IrType, rhs: IrType): EqualityLoweringType = when { + lhs.isJsNumber() -> translateEqualsForJsNumber(rhs) + lhs.isNullableJsNumber() -> translateEqualsForNullableJsNumber(rhs) + lhs.isLong() -> translateEqualsForLong(rhs) + lhs.isNullableLong() -> translateEqualsForNullableLong(rhs) + lhs.isBoolean() -> translateEqualsForBoolean(rhs) + lhs.isNullableBoolean() -> translateEqualsForNullableBoolean(rhs) + else -> RuntimeOrMethodCall() +} + +fun translateEqualsForJsNumber(rhs: IrType): EqualityLoweringType = when { + rhs.isJsNumber() || rhs.isNullableJsNumber() -> IdentityOperator() + rhs.isLong() || rhs.isNullableLong() -> EqualityOperator() + rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator() + else -> RuntimeFunctionCall() +} + +fun translateEqualsForNullableJsNumber(rhs: IrType): EqualityLoweringType = when { + rhs.isJsNumber() -> IdentityOperator() + rhs.isNullableJsNumber() -> EqualityOperator() + rhs.isLong() || rhs.isNullableLong() -> EqualityOperator() + rhs.isBoolean() -> IdentityOperator() + else -> RuntimeFunctionCall() +} + +fun translateEqualsForLong(rhs: IrType): EqualityLoweringType = when { + rhs.isJsNumber() || rhs.isNullableJsNumber() -> EqualityOperator() + rhs.isLong() || rhs.isNullableLong() -> RuntimeFunctionCall() + rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator() + else -> RuntimeFunctionCall() +} + +fun translateEqualsForNullableLong(rhs: IrType): EqualityLoweringType = when { + rhs.isJsNumber() || rhs.isNullableJsNumber() -> EqualityOperator() + rhs.isLong() || rhs.isNullableLong() -> RuntimeFunctionCall() + rhs.isBoolean() -> IdentityOperator() + else -> RuntimeFunctionCall() +} + +fun translateEqualsForBoolean(rhs: IrType): EqualityLoweringType = when { + rhs.isJsNumber() || rhs.isNullableJsNumber() -> IdentityOperator() + rhs.isLong() || rhs.isNullableLong() -> IdentityOperator() + rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator() + else -> RuntimeFunctionCall() +} + +fun translateEqualsForNullableBoolean(rhs: IrType): EqualityLoweringType = when { + rhs.isJsNumber() -> IdentityOperator() + rhs.isNullableJsNumber() -> RuntimeFunctionCall() + rhs.isLong() -> IdentityOperator() + rhs.isNullableLong() -> RuntimeFunctionCall() + rhs.isBoolean() -> IdentityOperator() + rhs.isNullableBoolean() -> EqualityOperator() + else -> RuntimeFunctionCall() +} + + +private fun IrType.isNullableJsNumber(): Boolean = isNullablePrimitiveType() && !isNullableLong() + +private fun IrType.isJsNumber(): Boolean = isPrimitiveType() && !isLong() + + // TODO extract to common place? -private fun irCall(call: IrCall, newSymbol: IrFunctionSymbol, dispatchReceiverAsFirstArgument: Boolean = false): IrCall = +fun irCall(call: IrCall, newSymbol: IrFunctionSymbol, dispatchReceiverAsFirstArgument: Boolean = false): IrCall = call.run { IrCallImpl( startOffset, @@ -204,10 +406,20 @@ private fun IrCall.copyTypeAndValueArgumentsFrom(call: IrCall, dispatchReceiverA } } -private fun MutableMap.op(type: IrType, name: Name, v: V) { +private fun MutableMap.op(type: IrType, name: Name, v: IrSimpleFunctionSymbol) { put(SimpleMemberKey(type, name), v) } +private fun MutableMap.op(type: IrType, name: Name, v: IrSimpleFunction) { + put(SimpleMemberKey(type, name), v.symbol) +} + +private fun MutableMap IrExpression>.op(type: IrType, name: Name, v: (IrCall) -> IrExpression) { + put(SimpleMemberKey(type, name), v) +} + + +// TODO issue: marked as unused, but used; rename works wrongly. private fun MutableMap.op(type: IrType, name: String, v: V) { put(SimpleMemberKey(type, Name.identifier(name)), v) } @@ -222,7 +434,7 @@ private fun MutableMap.add(from: IrFunctionSymbol, to: put(from, to) } -private fun MutableMap IrCall>.addWithPredicate(from: K, predicate: (IrCall) -> Boolean, action: (IrCall) -> IrCall) { +private fun MutableMap IrExpression>.addWithPredicate(from: K, predicate: (IrCall) -> Boolean, action: (IrCall) -> IrExpression) { put(from) { call: IrCall -> select({ predicate(call) }, { action(call) }, { call }) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/ConversionNames.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/ConversionNames.kt new file mode 100644 index 00000000000..f9e9af2d9c6 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/ConversionNames.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.utils + +import org.jetbrains.kotlin.name.Name + +object ConversionNames { + val TO_BYTE = Name.identifier("toByte") + val TO_CHAR = Name.identifier("toChar") + val TO_DOUBLE = Name.identifier("toDouble") + val TO_FLOAT = Name.identifier("toFloat") + val TO_INT = Name.identifier("toInt") + val TO_LONG = Name.identifier("toLong") + val TO_SHORT = Name.identifier("toShort") +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt index 92f598e686f..433992e758b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt @@ -62,4 +62,12 @@ val CallableMemberDescriptor.propertyIfAccessor else this val IrTypeParameter.isReified - get() = descriptor.isReified \ No newline at end of file + get() = descriptor.isReified + +// Return is method has no real implementation except fake overrides from Any +fun CallableMemberDescriptor.isFakeOverriddenFromAny(): Boolean { + if (kind.isReal) { + return (containingDeclaration is ClassDescriptor) && KotlinBuiltIns.isAny(containingDeclaration as ClassDescriptor) + } + return overriddenDescriptors.all { it.isFakeOverriddenFromAny() } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt index 6edaf1ff569..e0c4c9f64b7 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt @@ -35,6 +35,7 @@ fun IrType.isAny(): Boolean = isBuiltInClassType(KotlinBuiltIns::isAny, hasQuest fun IrType.isNullableAny(): Boolean = isBuiltInClassType(KotlinBuiltIns::isAny, hasQuestionMark = true) fun IrType.isString(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.string) +fun IrType.isNullableString(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES.string) fun IrType.isArray(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.array) fun IrType.isNothing(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.nothing) fun IrType.isPrimitiveType(): Boolean = @@ -57,4 +58,7 @@ fun IrType.isInt(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._int) fun IrType.isLong(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._long) fun IrType.isFloat(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._float) fun IrType.isDouble(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._double) -fun IrType.isNumber(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.number) \ No newline at end of file +fun IrType.isNumber(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.number) + +fun IrType.isNullableBoolean(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._boolean) +fun IrType.isNullableLong(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._long) diff --git a/compiler/testData/codegen/box/boxingOptimization/explicitEqualsOnDouble.kt b/compiler/testData/codegen/box/boxingOptimization/explicitEqualsOnDouble.kt index b827824c4d5..19130d51cb5 100644 --- a/compiler/testData/codegen/box/boxingOptimization/explicitEqualsOnDouble.kt +++ b/compiler/testData/codegen/box/boxingOptimization/explicitEqualsOnDouble.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: NATIVE fun equals1(a: Double, b: Double) = a.equals(b) diff --git a/compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt b/compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt index 22b86460129..e28cf97c283 100644 --- a/compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt +++ b/compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS, NATIVE fun box(): String { diff --git a/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt b/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt index 84bcd17426e..a8c0e37230c 100644 --- a/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt +++ b/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR package test interface TextField { diff --git a/compiler/testData/codegen/box/classes/kt3114.kt b/compiler/testData/codegen/box/classes/kt3114.kt index 2b73c48203f..32d42899074 100644 --- a/compiler/testData/codegen/box/classes/kt3114.kt +++ b/compiler/testData/codegen/box/classes/kt3114.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR class KeySpan(val left: String) { public fun matches(value : String) : Boolean { diff --git a/compiler/testData/codegen/box/classes/privateToThis.kt b/compiler/testData/codegen/box/classes/privateToThis.kt index e53e562f380..8905986a1f4 100644 --- a/compiler/testData/codegen/box/classes/privateToThis.kt +++ b/compiler/testData/codegen/box/classes/privateToThis.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR class A(init_o: I, private val init_k: I) { private val o: I = init_o private fun k(): I = init_k diff --git a/compiler/testData/codegen/box/dataClasses/hashCode/boolean.kt b/compiler/testData/codegen/box/dataClasses/hashCode/boolean.kt index 24a74b9c43d..d824a5ccf42 100644 --- a/compiler/testData/codegen/box/dataClasses/hashCode/boolean.kt +++ b/compiler/testData/codegen/box/dataClasses/hashCode/boolean.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR data class A(val a: Boolean) fun box() : String { diff --git a/compiler/testData/codegen/box/dataClasses/hashCode/byte.kt b/compiler/testData/codegen/box/dataClasses/hashCode/byte.kt index 9c0327e4fba..ce33977b555 100644 --- a/compiler/testData/codegen/box/dataClasses/hashCode/byte.kt +++ b/compiler/testData/codegen/box/dataClasses/hashCode/byte.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR data class A(val a: Byte) fun box() : String { diff --git a/compiler/testData/codegen/box/dataClasses/hashCode/double.kt b/compiler/testData/codegen/box/dataClasses/hashCode/double.kt index d2f7e06ffb0..f0899ad1b59 100644 --- a/compiler/testData/codegen/box/dataClasses/hashCode/double.kt +++ b/compiler/testData/codegen/box/dataClasses/hashCode/double.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR data class A(val a: Double) fun box() : String { diff --git a/compiler/testData/codegen/box/dataClasses/hashCode/float.kt b/compiler/testData/codegen/box/dataClasses/hashCode/float.kt index bc6fdc4a47d..41dc25c43c4 100644 --- a/compiler/testData/codegen/box/dataClasses/hashCode/float.kt +++ b/compiler/testData/codegen/box/dataClasses/hashCode/float.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR data class A(val a: Float) fun box() : String { diff --git a/compiler/testData/codegen/box/dataClasses/hashCode/int.kt b/compiler/testData/codegen/box/dataClasses/hashCode/int.kt index 830c93646e4..0596e4c72af 100644 --- a/compiler/testData/codegen/box/dataClasses/hashCode/int.kt +++ b/compiler/testData/codegen/box/dataClasses/hashCode/int.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR data class A(val a: Int) fun box() : String { diff --git a/compiler/testData/codegen/box/dataClasses/hashCode/short.kt b/compiler/testData/codegen/box/dataClasses/hashCode/short.kt index 67c79c4d296..5e220e27cfc 100644 --- a/compiler/testData/codegen/box/dataClasses/hashCode/short.kt +++ b/compiler/testData/codegen/box/dataClasses/hashCode/short.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR data class A(val a: Short) fun box() : String { diff --git a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/extensionDelegated.kt b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/extensionDelegated.kt index 9dd77230d33..6cd0284bab3 100644 --- a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/extensionDelegated.kt +++ b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/extensionDelegated.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR import kotlin.reflect.KProperty var log = "" @@ -15,4 +14,4 @@ var String.calc: String by UserDataProperty("K") fun box(): String { return "O".calc -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/functions/defaultargs1.kt b/compiler/testData/codegen/box/functions/defaultargs1.kt index 08d3656f98b..5666dd5603d 100644 --- a/compiler/testData/codegen/box/functions/defaultargs1.kt +++ b/compiler/testData/codegen/box/functions/defaultargs1.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun T.toPrefixedString(prefix: String = "", suffix: String="") = prefix + this.toString() + suffix fun box() : String { diff --git a/compiler/testData/codegen/box/functions/ea33909.kt b/compiler/testData/codegen/box/functions/ea33909.kt index 00b07415a23..f4022e8ba28 100644 --- a/compiler/testData/codegen/box/functions/ea33909.kt +++ b/compiler/testData/codegen/box/functions/ea33909.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { return justPrint(9.compareTo(4)) } diff --git a/compiler/testData/codegen/box/functions/functionExpression/functionExpression.kt b/compiler/testData/codegen/box/functions/functionExpression/functionExpression.kt index 7dd80c25e13..ae492d8c71a 100644 --- a/compiler/testData/codegen/box/functions/functionExpression/functionExpression.kt +++ b/compiler/testData/codegen/box/functions/functionExpression/functionExpression.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR val foo1 = fun Any.(): String { return "239" + this } diff --git a/compiler/testData/codegen/box/functions/functionExpression/functionLiteralExpression.kt b/compiler/testData/codegen/box/functions/functionExpression/functionLiteralExpression.kt index 88daa618104..a9de16d1fd8 100644 --- a/compiler/testData/codegen/box/functions/functionExpression/functionLiteralExpression.kt +++ b/compiler/testData/codegen/box/functions/functionExpression/functionLiteralExpression.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR fun Any.foo1() : ()-> String { return { "239" + this } } diff --git a/compiler/testData/codegen/box/functions/kt2929.kt b/compiler/testData/codegen/box/functions/kt2929.kt index 4c23058a3d2..4367923fc29 100644 --- a/compiler/testData/codegen/box/functions/kt2929.kt +++ b/compiler/testData/codegen/box/functions/kt2929.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun foo(): Int { val a = "test" val b = "test" diff --git a/compiler/testData/codegen/box/ieee754/explicitCompareCall.kt b/compiler/testData/codegen/box/ieee754/explicitCompareCall.kt index 7db22bacf43..57d0b06328d 100644 --- a/compiler/testData/codegen/box/ieee754/explicitCompareCall.kt +++ b/compiler/testData/codegen/box/ieee754/explicitCompareCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun less1(a: Double, b: Double) = a.compareTo(b) == -1 fun less2(a: Double?, b: Double?) = a!!.compareTo(b!!) == -1 diff --git a/compiler/testData/codegen/box/ieee754/explicitEqualsCall.kt b/compiler/testData/codegen/box/ieee754/explicitEqualsCall.kt index 071b453184b..51dfd696f72 100644 --- a/compiler/testData/codegen/box/ieee754/explicitEqualsCall.kt +++ b/compiler/testData/codegen/box/ieee754/explicitEqualsCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun equals1(a: Double, b: Double) = a.equals(b) fun equals2(a: Double?, b: Double?) = a!!.equals(b!!) diff --git a/compiler/testData/codegen/box/ieee754/safeCall.kt b/compiler/testData/codegen/box/ieee754/safeCall.kt index d31fbb67874..690b6168935 100644 --- a/compiler/testData/codegen/box/ieee754/safeCall.kt +++ b/compiler/testData/codegen/box/ieee754/safeCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { val plusZero: Double? = 0.0 val minusZero: Double = -0.0 diff --git a/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes_properIeeeComparisons.kt b/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes_properIeeeComparisons.kt index ce23acd383f..582053bdf55 100644 --- a/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes_properIeeeComparisons.kt +++ b/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes_properIeeeComparisons.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +ProperIeee754Comparisons -// IGNORE_BACKEND: JS_IR fun ne(x: Any, y: Any) = x is Double && y is Float && x != y fun lt(x: Any, y: Any) = x is Double && y is Float && x < y diff --git a/compiler/testData/codegen/box/increment/postfixIncrementOnShortSmartCast.kt b/compiler/testData/codegen/box/increment/postfixIncrementOnShortSmartCast.kt index c4a7cb6e803..0dec61d0503 100644 --- a/compiler/testData/codegen/box/increment/postfixIncrementOnShortSmartCast.kt +++ b/compiler/testData/codegen/box/increment/postfixIncrementOnShortSmartCast.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR public fun box() : String { var i : Short? i = 10 diff --git a/compiler/testData/codegen/box/intrinsics/ea35953.kt b/compiler/testData/codegen/box/intrinsics/ea35953.kt index c725a55c2d0..a1acb1d7481 100644 --- a/compiler/testData/codegen/box/intrinsics/ea35953.kt +++ b/compiler/testData/codegen/box/intrinsics/ea35953.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { if (12.toString().equals("13")) { return "Fail" diff --git a/compiler/testData/codegen/box/intrinsics/kt12125_2.kt b/compiler/testData/codegen/box/intrinsics/kt12125_2.kt index 30466f72463..c9890efe644 100644 --- a/compiler/testData/codegen/box/intrinsics/kt12125_2.kt +++ b/compiler/testData/codegen/box/intrinsics/kt12125_2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { var aByte: Byte? = 0 var bByte: Byte = 0 diff --git a/compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt b/compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt index 238ac19a33d..953ccf8dc76 100644 --- a/compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt +++ b/compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun test(i: Int): Int { return i } diff --git a/compiler/testData/codegen/box/operatorConventions/compareTo/boolean.kt b/compiler/testData/codegen/box/operatorConventions/compareTo/boolean.kt index 75452e21055..833e11a733a 100644 --- a/compiler/testData/codegen/box/operatorConventions/compareTo/boolean.kt +++ b/compiler/testData/codegen/box/operatorConventions/compareTo/boolean.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun checkLess(x: Boolean, y: Boolean) = when { x >= y -> "Fail $x >= $y" !(x < y) -> "Fail !($x < $y)" diff --git a/compiler/testData/codegen/box/operatorConventions/compareTo/doubleInt.kt b/compiler/testData/codegen/box/operatorConventions/compareTo/doubleInt.kt index 8ee753bcb15..9818affbfba 100644 --- a/compiler/testData/codegen/box/operatorConventions/compareTo/doubleInt.kt +++ b/compiler/testData/codegen/box/operatorConventions/compareTo/doubleInt.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun checkLess(x: Double, y: Int) = when { x >= y -> "Fail $x >= $y" !(x < y) -> "Fail !($x < $y)" diff --git a/compiler/testData/codegen/box/operatorConventions/compareTo/extensionObject.kt b/compiler/testData/codegen/box/operatorConventions/compareTo/extensionObject.kt index 895ab0bef52..1f355ed16e6 100644 --- a/compiler/testData/codegen/box/operatorConventions/compareTo/extensionObject.kt +++ b/compiler/testData/codegen/box/operatorConventions/compareTo/extensionObject.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR class A(val x: Int) operator fun A.compareTo(other: A) = x.compareTo(other.x) diff --git a/compiler/testData/codegen/box/operatorConventions/compareTo/intDouble.kt b/compiler/testData/codegen/box/operatorConventions/compareTo/intDouble.kt index 3d39c948895..a76709fe2db 100644 --- a/compiler/testData/codegen/box/operatorConventions/compareTo/intDouble.kt +++ b/compiler/testData/codegen/box/operatorConventions/compareTo/intDouble.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun checkLess(x: Int, y: Double) = when { x >= y -> "Fail $x >= $y" !(x < y) -> "Fail !($x < $y)" diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveByte.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveByte.kt index 976bcefdc45..327054de5dd 100644 --- a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveByte.kt +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveByte.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! val nx: Byte? = 0.toByte() diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveShort.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveShort.kt index fbcad803071..e93839a29c1 100644 --- a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveShort.kt +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/boxedEqPrimitiveShort.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! val nx: Short? = 0.toShort() diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedByte.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedByte.kt index 929748e1c1d..937d82722fa 100644 --- a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedByte.kt +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedByte.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! val nx: Byte? = 0.toByte() diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedShort.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedShort.kt index f8ee6fb2521..29bf73f855a 100644 --- a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedShort.kt +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqBoxedShort.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! val nx: Short? = 0.toShort() diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectByte.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectByte.kt index 24e31ea49e3..b0b853c6edc 100644 --- a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectByte.kt +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectByte.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! val nx: Any? = 0.toByte() diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectShort.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectShort.kt index b08b5d35ea8..61c27d231c0 100644 --- a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectShort.kt +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectShort.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! val nx: Any? = 0.toShort() diff --git a/compiler/testData/codegen/box/primitiveTypes/kt1093.kt b/compiler/testData/codegen/box/primitiveTypes/kt1093.kt index e983ee30e7e..668c35525f1 100644 --- a/compiler/testData/codegen/box/primitiveTypes/kt1093.kt +++ b/compiler/testData/codegen/box/primitiveTypes/kt1093.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR val f : (Any) -> String = { it.toString() } fun box() : String { diff --git a/compiler/testData/codegen/box/primitiveTypes/kt2269.kt b/compiler/testData/codegen/box/primitiveTypes/kt2269.kt index d6133c977be..836db5a10b0 100644 --- a/compiler/testData/codegen/box/primitiveTypes/kt2269.kt +++ b/compiler/testData/codegen/box/primitiveTypes/kt2269.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR fun box() : String { 230?.toByte()?.hashCode() 9.hashCode() diff --git a/compiler/testData/codegen/box/primitiveTypes/kt2768.kt b/compiler/testData/codegen/box/primitiveTypes/kt2768.kt index 3cbbb631c3b..b2b0ec976ef 100644 --- a/compiler/testData/codegen/box/primitiveTypes/kt2768.kt +++ b/compiler/testData/codegen/box/primitiveTypes/kt2768.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun assertEquals(a: T, b: T) { if (a != b) throw AssertionError("$a != $b") } diff --git a/compiler/testData/codegen/box/primitiveTypes/kt3078.kt b/compiler/testData/codegen/box/primitiveTypes/kt3078.kt index 8196310428a..cd834c89b2f 100644 --- a/compiler/testData/codegen/box/primitiveTypes/kt3078.kt +++ b/compiler/testData/codegen/box/primitiveTypes/kt3078.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { if (1 >= 1.9) return "Fail #1" if (1.compareTo(1.1) >= 0) return "Fail #2" diff --git a/compiler/testData/codegen/box/primitiveTypes/kt3517.kt b/compiler/testData/codegen/box/primitiveTypes/kt3517.kt index a1659ed7bde..535f32308cd 100644 --- a/compiler/testData/codegen/box/primitiveTypes/kt3517.kt +++ b/compiler/testData/codegen/box/primitiveTypes/kt3517.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // KT-3517 Can't call .equals() on a boolean fun box(): String { diff --git a/compiler/testData/codegen/box/primitiveTypes/kt3576.kt b/compiler/testData/codegen/box/primitiveTypes/kt3576.kt index 349501839e5..955272f0faa 100644 --- a/compiler/testData/codegen/box/primitiveTypes/kt3576.kt +++ b/compiler/testData/codegen/box/primitiveTypes/kt3576.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR object TestObject { val testFloat: Float = 0.9999.toFloat() val otherFloat: Float = 1.01.toFloat() diff --git a/compiler/testData/codegen/box/primitiveTypes/kt882.kt b/compiler/testData/codegen/box/primitiveTypes/kt882.kt index 4eabeced192..15da30a150e 100644 --- a/compiler/testData/codegen/box/primitiveTypes/kt882.kt +++ b/compiler/testData/codegen/box/primitiveTypes/kt882.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR val _0 : Double = 0.0 val _0dbl : Double = 0.toDouble() diff --git a/compiler/testData/codegen/box/reified/reifiedChain.kt b/compiler/testData/codegen/box/reified/reifiedChain.kt index 8ade2463910..ebd37fb8555 100644 --- a/compiler/testData/codegen/box/reified/reifiedChain.kt +++ b/compiler/testData/codegen/box/reified/reifiedChain.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR inline fun Any?.check(): Boolean { return this is T } @@ -35,4 +34,4 @@ fun box(): String { } return "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/unaryOp/callWithCommonType.kt b/compiler/testData/codegen/box/unaryOp/callWithCommonType.kt index 2bdbc80bbd3..a3fa43dc3c3 100644 --- a/compiler/testData/codegen/box/unaryOp/callWithCommonType.kt +++ b/compiler/testData/codegen/box/unaryOp/callWithCommonType.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { if (!foo(1.toByte())) return "fail 1" if (!foo((1.toByte()).inc())) return "fail 2" diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 2d604363114..be6af6fa056 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -1113,6 +1113,15 @@ public abstract class KotlinBuiltIns { return classFqNameEquals(descriptor, FQ_NAMES._enum); } + public static boolean isComparable(@NotNull ClassDescriptor descriptor) { + return classFqNameEquals(descriptor, FQ_NAMES.comparable.toUnsafe()); + } + + public static boolean isComparable(@NotNull KotlinType type) { + return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES.comparable.toUnsafe()); + } + + public static boolean isCharSequence(@Nullable KotlinType type) { return type != null && isNotNullConstructedFromGivenClass(type, FQ_NAMES.charSequence); } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index 9648d5a35a7..ddb4e9a6097 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -545,7 +545,11 @@ abstract class BasicBoxTest( val psiManager = PsiManager.getInstance(project) val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) - return psiManager.findFile(fileSystem.findFileByPath(fileName)!!) as KtFile + val file = fileSystem.findFileByPath(fileName) + if (file == null) + error("File not found: ${fileName}") + + return psiManager.findFile(file) as KtFile } private fun createPsiFiles(fileNames: List): List = fileNames.map(this::createPsiFile) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index b5dfd3568c5..950a9555256 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -45,6 +45,9 @@ abstract class BasicIrBoxTest( ) { val runtime = listOf( "libraries/stdlib/js/src/kotlin/core.kt", + "libraries/stdlib/js/irRuntime/core.kt", + "libraries/stdlib/js/irRuntime/numberConversion.kt", + "libraries/stdlib/js/irRuntime/compareTo.kt", "libraries/stdlib/js/irRuntime/annotations.kt", "libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt", "libraries/stdlib/js/irRuntime/exceptions.kt", diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 27b02bde55f..29a2ae80d7e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -47,6 +47,29 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { } } + @TestMetadata("js/js.translator/testData/box/builtins") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Builtins extends AbstractBoxJsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInBuiltins() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/builtins"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("hashCode.kt") + public void testHashCode() throws Exception { + runTest("js/js.translator/testData/box/builtins/hashCode.kt"); + } + + @TestMetadata("toString.kt") + public void testToString() throws Exception { + runTest("js/js.translator/testData/box/builtins/toString.kt"); + } + } + @TestMetadata("js/js.translator/testData/box/callableReference") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java index aa96bac641d..4d7b0515ba0 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java @@ -47,6 +47,29 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { } } + @TestMetadata("js/js.translator/testData/box/builtins") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Builtins extends AbstractIrBoxJsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInBuiltins() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/builtins"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS_IR, true); + } + + @TestMetadata("hashCode.kt") + public void testHashCode() throws Exception { + runTest("js/js.translator/testData/box/builtins/hashCode.kt"); + } + + @TestMetadata("toString.kt") + public void testToString() throws Exception { + runTest("js/js.translator/testData/box/builtins/toString.kt"); + } + } + @TestMetadata("js/js.translator/testData/box/callableReference") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.translator/testData/box/builtins/hashCode.kt b/js/js.translator/testData/box/builtins/hashCode.kt new file mode 100644 index 00000000000..41d5a742f81 --- /dev/null +++ b/js/js.translator/testData/box/builtins/hashCode.kt @@ -0,0 +1,32 @@ +// EXPECTED_REACHABLE_NODES: 1117 +package foo + +class A { + override fun hashCode() = 42 +} + +class B + +fun box(): String { + if (A().hashCode() != 42) return "Wrong hash 0" + + val o1 = B(); + if (o1.hashCode() != o1.hashCode()) return "Wrong hash 1" + + val o2 = js("\"\"") + if (o2.hashCode() != o2.hashCode()) return "Wrong hash 2" + + val o3 = js("123") + if (o3.hashCode() != o3.hashCode()) return "Wrong hash 3" + + val o4 = 123 + if (o4.hashCode() != o4.hashCode()) return "Wrong hash 4" + + val o5 = "123" + if (o5.hashCode() != o5.hashCode()) return "Wrong hash 5" + + val o6 = (123 as Any) + if (o6.hashCode() != o6.hashCode()) return "Wrong hash 6" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/builtins/toString.kt b/js/js.translator/testData/box/builtins/toString.kt new file mode 100644 index 00000000000..7c10b4a6bfb --- /dev/null +++ b/js/js.translator/testData/box/builtins/toString.kt @@ -0,0 +1,20 @@ +// EXPECTED_REACHABLE_NODES: 1117 +package foo + +class A { + override fun toString() = "42" +} + +class B + +fun box(): String { + assertEquals(A().toString(), "42") + assertEquals(B().toString(), "[object Object]") + assertEquals(js("\"\"").toString(), "") + assertEquals(js("123").toString(), "123") + assertEquals(123.toString(), "123") + assertEquals("123".toString(), "123") + assertEquals((123 as Any).toString(), "123") + assertEquals(null.toString(), "null") + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/defaultArguments/extensionFunWithDefArgs.kt b/js/js.translator/testData/box/defaultArguments/extensionFunWithDefArgs.kt index d67074972e6..361e1c478ec 100644 --- a/js/js.translator/testData/box/defaultArguments/extensionFunWithDefArgs.kt +++ b/js/js.translator/testData/box/defaultArguments/extensionFunWithDefArgs.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1109 package foo diff --git a/js/js.translator/testData/box/delegation/delegationMethodsWithArgs.kt b/js/js.translator/testData/box/delegation/delegationMethodsWithArgs.kt index de5ee39cc97..35538fb82a5 100644 --- a/js/js.translator/testData/box/delegation/delegationMethodsWithArgs.kt +++ b/js/js.translator/testData/box/delegation/delegationMethodsWithArgs.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1126 // This test was adapted from compiler/testData/codegen/box/classes package foo diff --git a/js/js.translator/testData/box/expression/equals/explicitEqualsMethod.kt b/js/js.translator/testData/box/expression/equals/explicitEqualsMethod.kt index 5d16b1a1cc5..594e4619e1a 100644 --- a/js/js.translator/testData/box/expression/equals/explicitEqualsMethod.kt +++ b/js/js.translator/testData/box/expression/equals/explicitEqualsMethod.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1114 package foo diff --git a/js/js.translator/testData/box/expression/equals/explicitEqualsMethodForPrimitives.kt b/js/js.translator/testData/box/expression/equals/explicitEqualsMethodForPrimitives.kt index ff183b65f35..839ffa541cb 100644 --- a/js/js.translator/testData/box/expression/equals/explicitEqualsMethodForPrimitives.kt +++ b/js/js.translator/testData/box/expression/equals/explicitEqualsMethodForPrimitives.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1110 package foo diff --git a/js/js.translator/testData/box/expression/evaluationOrder/compareToIntrinsicWithSideEffect.kt b/js/js.translator/testData/box/expression/evaluationOrder/compareToIntrinsicWithSideEffect.kt index a067375a1e5..1d6266ec502 100644 --- a/js/js.translator/testData/box/expression/evaluationOrder/compareToIntrinsicWithSideEffect.kt +++ b/js/js.translator/testData/box/expression/evaluationOrder/compareToIntrinsicWithSideEffect.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1114 package foo diff --git a/js/js.translator/testData/box/extensionFunction/implicitReceiverInExtension.kt b/js/js.translator/testData/box/extensionFunction/implicitReceiverInExtension.kt index 0c14f0eb888..5254f01fad0 100644 --- a/js/js.translator/testData/box/extensionFunction/implicitReceiverInExtension.kt +++ b/js/js.translator/testData/box/extensionFunction/implicitReceiverInExtension.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1109 package foo diff --git a/js/js.translator/testData/box/extensionProperty/absExtension.kt b/js/js.translator/testData/box/extensionProperty/absExtension.kt index b40906fc9a7..fd935a31c5c 100644 --- a/js/js.translator/testData/box/extensionProperty/absExtension.kt +++ b/js/js.translator/testData/box/extensionProperty/absExtension.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1109 package foo diff --git a/js/js.translator/testData/box/inlineMultiFile/generics.kt b/js/js.translator/testData/box/inlineMultiFile/generics.kt index 20d4179e88f..344d9c94ce9 100644 --- a/js/js.translator/testData/box/inlineMultiFile/generics.kt +++ b/js/js.translator/testData/box/inlineMultiFile/generics.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1109 /* * Copy of JVM-backend test diff --git a/js/js.translator/testData/box/number/byteAndShortConversions.kt b/js/js.translator/testData/box/number/byteAndShortConversions.kt index 60e89ae6d28..a1901ba20e4 100644 --- a/js/js.translator/testData/box/number/byteAndShortConversions.kt +++ b/js/js.translator/testData/box/number/byteAndShortConversions.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1112 package foo diff --git a/js/js.translator/testData/box/number/doubleConversions.kt b/js/js.translator/testData/box/number/doubleConversions.kt index bc0b4eaa660..a1afce438db 100644 --- a/js/js.translator/testData/box/number/doubleConversions.kt +++ b/js/js.translator/testData/box/number/doubleConversions.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1110 package foo diff --git a/js/js.translator/testData/box/number/intConversions.kt b/js/js.translator/testData/box/number/intConversions.kt index 2f546cf7f1e..8fbc8effc9d 100644 --- a/js/js.translator/testData/box/number/intConversions.kt +++ b/js/js.translator/testData/box/number/intConversions.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1110 package foo diff --git a/js/js.translator/testData/box/number/intDivFloat.kt b/js/js.translator/testData/box/number/intDivFloat.kt index d069cfb851e..9fa0714d466 100644 --- a/js/js.translator/testData/box/number/intDivFloat.kt +++ b/js/js.translator/testData/box/number/intDivFloat.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1114 // http://youtrack.jetbrains.com/issue/KT-5345 // KT-5345 (Javascript) Type mismatch on Int / Float division diff --git a/libraries/stdlib/js/irRuntime/compareTo.kt b/libraries/stdlib/js/irRuntime/compareTo.kt new file mode 100644 index 00000000000..b1a0a74b0cd --- /dev/null +++ b/libraries/stdlib/js/irRuntime/compareTo.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.js + + +// Adopted from misc.js + +fun compareTo(a: dynamic, b: dynamic): Int { + var typeA = typeOf(a) + if (typeA == "number") { + if (typeOf(b) == "number") { + return doubleCompareTo(a, b) + } + return primitiveCompareTo(a, b) + } + if (typeA == "string" || typeA == "boolean") { + return primitiveCompareTo(a, b) + } + + // TODO: Replace to a.unsafeCast>().compareTo(b) when bridge is implemented + return js("a.compareTo(b)").unsafeCast() +} + +fun primitiveCompareTo(a: dynamic, b: dynamic): Int = + js("a < b ? -1 : a > b ? 1 : 0").unsafeCast() + +fun doubleCompareTo(a: dynamic, b: dynamic): Int = + js(""" + if (a < b) return -1; + if (a > b) return 1; + + if (a === b) { + if (a !== 0) return 0; + + var ia = 1 / a; + return ia === 1 / b ? 0 : (ia < 0 ? -1 : 1); + } + + return a !== a ? (b !== b ? 0 : 1) : -1 + """).unsafeCast() \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/core.kt b/libraries/stdlib/js/irRuntime/core.kt new file mode 100644 index 00000000000..a899b7964af --- /dev/null +++ b/libraries/stdlib/js/irRuntime/core.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.js + +fun equals(obj1: dynamic, obj2: dynamic): Boolean { + if (obj1 == null) { + return obj2 == null + } + + if (obj2 == null) { + return false + } + + return js(""" + if (typeof obj1 === "object" && typeof obj1.equals === "function") { + return obj1.equals(obj2); + } + + if (obj1 !== obj1) { + return obj2 !== obj2; + } + + if (typeof obj1 === "number" && typeof obj2 === "number") { + return obj1 === obj2 && (obj1 !== 0 || 1 / obj1 === 1 / obj2) + } + return obj1 === obj2; + """).unsafeCast() +} + +fun toString(o: dynamic): String = when { + o == null -> "null" + isArrayish(o) -> "[...]" + else -> js("o.toString()").unsafeCast() +} + +// TODO: Simplify, extract kotlin declarations for inner helper functions +fun hashCode(obj: dynamic): Int { + return js( + """ + function hashCode(obj) { + if (obj == null) { + return 0; + } + var objType = typeof obj; + if ("object" === objType) { + return "function" === typeof obj.hashCode ? obj.hashCode() : getObjectHashCode(obj); + } + if ("function" === objType) { + return getObjectHashCode(obj); + } + if ("number" === objType) { + return getNumberHashCode(obj); + } + if ("boolean" === objType) { + return Number(obj) + } + + var str = String(obj); + return getStringHashCode(str); + }; + + /** @const */ + var POW_2_32 = 4294967296; + // TODO: consider switching to Symbol type once we are on ES6. + /** @const */ + var OBJECT_HASH_CODE_PROPERTY_NAME = "kotlinHashCodeValue${'$'}"; + + function getObjectHashCode(obj) { + if (!(OBJECT_HASH_CODE_PROPERTY_NAME in obj)) { + var hash = (Math.random() * POW_2_32) | 0; // Make 32-bit singed integer. + Object.defineProperty(obj, OBJECT_HASH_CODE_PROPERTY_NAME, { value: hash, enumerable: false }); + } + return obj[OBJECT_HASH_CODE_PROPERTY_NAME]; + } + + function getStringHashCode(str) { + var hash = 0; + for (var i = 0; i < str.length; i++) { + var code = str.charCodeAt(i); + hash = (hash * 31 + code) | 0; // Keep it 32-bit. + } + return hash; + } + + function getNumberHashCode(obj) { + if ((obj | 0) === obj) { + return obj | 0; + } + else { + bufFloat64[0] = obj; + return (bufInt32[highIndex] * 31 | 0) + bufInt32[lowIndex] | 0; + } + } + + return hashCode(obj); + """ + ).unsafeCast() +} diff --git a/libraries/stdlib/js/irRuntime/dummy.kt b/libraries/stdlib/js/irRuntime/dummy.kt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libraries/stdlib/js/irRuntime/numberConversion.kt b/libraries/stdlib/js/irRuntime/numberConversion.kt new file mode 100644 index 00000000000..41c3d99b113 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/numberConversion.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.js + +fun asIs(a: dynamic) = a + +fun numberToByte(a: dynamic): Byte = toByte(numberToInt(a)) + +fun numberToDouble(a: dynamic): Double = js("+a").unsafeCast() + +fun numberToInt(a: dynamic): Int = doubleToInt(a) + +fun numberToShort(a: dynamic): Short = toShort(numberToInt(a)) + +// << and >> shifts are used to preserve sign of the number +fun toByte(a: dynamic): Byte = js("a << 24 >> 24").unsafeCast() +fun toShort(a: dynamic): Short = js("a << 16 >> 16").unsafeCast() + +fun doubleToInt(a: dynamic) = js(""" + if (a > 2147483647) return 2147483647; + if (a < -2147483648) return -2147483648; + return a | 0; +""").unsafeCast() \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/typeCheckUtils.kt b/libraries/stdlib/js/irRuntime/typeCheckUtils.kt index 7ef8b526ad4..dba0d06c8b2 100644 --- a/libraries/stdlib/js/irRuntime/typeCheckUtils.kt +++ b/libraries/stdlib/js/irRuntime/typeCheckUtils.kt @@ -75,7 +75,7 @@ public fun isInterface(ctor: dynamic, IType: dynamic): Boolean { } */ -inline private fun typeOf(obj: dynamic) = js("typeof obj").unsafeCast() +fun typeOf(obj: dynamic) = js("typeof obj").unsafeCast() fun isObject(obj: dynamic): Boolean { val objTypeOf = typeOf(obj) @@ -93,6 +93,10 @@ public fun isArray(obj: Any): Boolean { return js("Array.isArray(obj)").unsafeCast() } +public fun isArrayish(o: dynamic) = + isArray(o) || js("ArrayBuffer.isView(o)").unsafeCast() + + public fun isChar(c: Any): Boolean { return js("throw Error(\"isChar is not implemented\")").unsafeCast() } \ No newline at end of file