[JS IR BE] hashCode, toString, number conversion support
This commit is contained in:
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.types.Variance
|
|||||||
class JsIntrinsics(
|
class JsIntrinsics(
|
||||||
private val module: ModuleDescriptor,
|
private val module: ModuleDescriptor,
|
||||||
private val irBuiltIns: IrBuiltIns,
|
private val irBuiltIns: IrBuiltIns,
|
||||||
context: JsIrBackendContext
|
val context: JsIrBackendContext
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val stubBuilder = DeclarationStubGenerator(module, context.symbolTable, JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB)
|
private val stubBuilder = DeclarationStubGenerator(module, context.symbolTable, JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB)
|
||||||
@@ -89,15 +89,35 @@ class JsIntrinsics(
|
|||||||
val jsTypeOf = unOp("jsTypeOf", irBuiltIns.string)
|
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:
|
// Other:
|
||||||
|
|
||||||
val jsObjectCreate = defineObjectCreateIntrinsic() // Object.create
|
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 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 jsToJsType = defineToJsType() // creates name reference to KotlinType
|
||||||
val jsCode = context.getInternalFunctions("js").singleOrNull()?.let { context.symbolTable.referenceFunction(it) } // js("<code>")
|
val jsCode = getInternalFunction("js") // js("<code>")
|
||||||
|
val jsHashCode = getInternalFunction("hashCode")
|
||||||
|
val jsToString = getInternalFunction("toString")
|
||||||
|
val jsCompareTo = getInternalFunction("compareTo")
|
||||||
|
val jsEquals = getInternalFunction("equals")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Helpers:
|
// Helpers:
|
||||||
|
|
||||||
|
private fun getInternalFunction(name: String) =
|
||||||
|
context.symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
|
||||||
|
|
||||||
private fun defineToJsType(): IrSimpleFunction {
|
private fun defineToJsType(): IrSimpleFunction {
|
||||||
val desc = SimpleFunctionDescriptorImpl.create(
|
val desc = SimpleFunctionDescriptorImpl.create(
|
||||||
module,
|
module,
|
||||||
|
|||||||
+256
-44
@@ -6,11 +6,14 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
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.backend.common.utils.isSubtypeOfClass
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
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.Namer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
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.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
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.copyTypeArgumentsFrom
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
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.types.impl.originalKotlinType
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.SimpleType
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
|
|
||||||
|
|
||||||
class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileLoweringPass {
|
class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileLoweringPass {
|
||||||
|
private val intrinsics = context.intrinsics
|
||||||
|
private val irBuiltIns = context.irBuiltIns
|
||||||
|
|
||||||
// TODO: should/can we unify these maps?
|
// TODO: should/can we unify these maps?
|
||||||
private val memberToTransformer: Map<SimpleMemberKey, (IrCall) -> IrCall>
|
private val memberToTransformer: Map<SimpleMemberKey, (IrCall) -> IrExpression>
|
||||||
private val memberToIrFunction: Map<SimpleMemberKey, IrSimpleFunction>
|
private val memberToIrFunction: Map<SimpleMemberKey, IrSimpleFunctionSymbol>
|
||||||
private val symbolToIrFunction: Map<IrFunctionSymbol, IrSimpleFunction>
|
private val symbolToIrFunction: Map<IrFunctionSymbol, IrSimpleFunction>
|
||||||
private val nameToIrTransformer: Map<Name, (IrCall) -> IrCall>
|
private val nameToIrTransformer: Map<Name, (IrCall) -> IrExpression>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
memberToIrFunction = mutableMapOf()
|
memberToIrFunction = mutableMapOf()
|
||||||
@@ -43,65 +50,100 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
|
|||||||
|
|
||||||
memberToIrFunction.run {
|
memberToIrFunction.run {
|
||||||
for (type in primitiveNumbers) {
|
for (type in primitiveNumbers) {
|
||||||
op(type, OperatorNames.UNARY_PLUS, context.intrinsics.jsUnaryPlus)
|
op(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus)
|
||||||
op(type, OperatorNames.UNARY_MINUS, context.intrinsics.jsUnaryMinus)
|
op(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus)
|
||||||
|
|
||||||
op(type, OperatorNames.ADD, context.intrinsics.jsPlus)
|
op(type, OperatorNames.ADD, intrinsics.jsPlus)
|
||||||
op(type, OperatorNames.SUB, context.intrinsics.jsMinus)
|
op(type, OperatorNames.SUB, intrinsics.jsMinus)
|
||||||
op(type, OperatorNames.MUL, context.intrinsics.jsMult)
|
op(type, OperatorNames.MUL, intrinsics.jsMult)
|
||||||
op(type, OperatorNames.DIV, context.intrinsics.jsDiv)
|
op(type, OperatorNames.DIV, intrinsics.jsDiv)
|
||||||
op(type, OperatorNames.MOD, context.intrinsics.jsMod)
|
op(type, OperatorNames.MOD, intrinsics.jsMod)
|
||||||
op(type, OperatorNames.REM, context.intrinsics.jsMod)
|
op(type, OperatorNames.REM, intrinsics.jsMod)
|
||||||
}
|
}
|
||||||
|
|
||||||
context.irBuiltIns.stringType.let {
|
irBuiltIns.stringType.let {
|
||||||
op(it, OperatorNames.ADD, context.intrinsics.jsPlus)
|
op(it, OperatorNames.ADD, intrinsics.jsPlus)
|
||||||
}
|
}
|
||||||
|
|
||||||
context.irBuiltIns.intType.let {
|
irBuiltIns.intType.let {
|
||||||
op(it, OperatorNames.SHL, context.intrinsics.jsBitShiftL)
|
op(it, OperatorNames.SHL, intrinsics.jsBitShiftL)
|
||||||
op(it, OperatorNames.SHR, context.intrinsics.jsBitShiftR)
|
op(it, OperatorNames.SHR, intrinsics.jsBitShiftR)
|
||||||
op(it, OperatorNames.SHRU, context.intrinsics.jsBitShiftRU)
|
op(it, OperatorNames.SHRU, intrinsics.jsBitShiftRU)
|
||||||
op(it, OperatorNames.AND, context.intrinsics.jsBitAnd)
|
op(it, OperatorNames.AND, intrinsics.jsBitAnd)
|
||||||
op(it, OperatorNames.OR, context.intrinsics.jsBitOr)
|
op(it, OperatorNames.OR, intrinsics.jsBitOr)
|
||||||
op(it, OperatorNames.XOR, context.intrinsics.jsBitXor)
|
op(it, OperatorNames.XOR, intrinsics.jsBitXor)
|
||||||
op(it, OperatorNames.INV, context.intrinsics.jsBitNot)
|
op(it, OperatorNames.INV, intrinsics.jsBitNot)
|
||||||
}
|
}
|
||||||
|
|
||||||
context.irBuiltIns.booleanType.let {
|
irBuiltIns.booleanType.let {
|
||||||
op(it, OperatorNames.AND, context.intrinsics.jsBitAnd)
|
op(it, OperatorNames.AND, intrinsics.jsBitAnd)
|
||||||
op(it, OperatorNames.OR, context.intrinsics.jsBitOr)
|
op(it, OperatorNames.OR, intrinsics.jsBitOr)
|
||||||
op(it, OperatorNames.NOT, context.intrinsics.jsNot)
|
op(it, OperatorNames.NOT, intrinsics.jsNot)
|
||||||
op(it, OperatorNames.XOR, context.intrinsics.jsBitXor)
|
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 {
|
symbolToIrFunction.run {
|
||||||
add(context.irBuiltIns.eqeqeqSymbol, context.intrinsics.jsEqeqeq)
|
add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq)
|
||||||
// TODO: implement it a right way
|
// TODO: implement it a right way
|
||||||
add(context.irBuiltIns.eqeqSymbol, context.intrinsics.jsEqeq)
|
add(irBuiltIns.eqeqSymbol, intrinsics.jsEqeq)
|
||||||
// TODO: implement it a right way
|
// 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(irBuiltIns.lessFunByOperandType, intrinsics.jsLt)
|
||||||
add(context.irBuiltIns.lessOrEqualFunByOperandType, context.intrinsics.jsLtEq)
|
add(irBuiltIns.lessOrEqualFunByOperandType, intrinsics.jsLtEq)
|
||||||
add(context.irBuiltIns.greaterFunByOperandType, context.intrinsics.jsGt)
|
add(irBuiltIns.greaterFunByOperandType, intrinsics.jsGt)
|
||||||
add(context.irBuiltIns.greaterOrEqualFunByOperandType, context.intrinsics.jsGtEq)
|
add(irBuiltIns.greaterOrEqualFunByOperandType, intrinsics.jsGtEq)
|
||||||
}
|
}
|
||||||
|
|
||||||
memberToTransformer.run {
|
memberToTransformer.run {
|
||||||
for (type in primitiveNumbers) {
|
for (type in primitiveNumbers) {
|
||||||
// TODO: use increment and decrement when it's possible
|
// TODO: use increment and decrement when it's possible
|
||||||
op(type, OperatorNames.INC) {
|
op(type, OperatorNames.INC) {
|
||||||
irCall(it, context.intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply {
|
irCall(it, intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply {
|
||||||
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, 1))
|
putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
op(type, OperatorNames.DEC) {
|
op(type, OperatorNames.DEC) {
|
||||||
irCall(it, context.intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply {
|
irCall(it, intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply {
|
||||||
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, 1))
|
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 -> call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false},
|
||||||
{ call -> irCall(call, context.intrinsics.jsPropertySet.symbol, dispatchReceiverAsFirstArgument = true)}
|
{ 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)
|
val key = SimpleMemberKey(it.type, symbol.owner.name)
|
||||||
|
|
||||||
memberToIrFunction[key]?.let {
|
memberToIrFunction[key]?.let {
|
||||||
|
if (it == intrinsics.jsAsIs) {
|
||||||
|
return call.dispatchReceiver!!
|
||||||
|
}
|
||||||
// TODO: don't apply intrinsics when type of receiver or argument is Long
|
// 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 {
|
memberToTransformer[key]?.let {
|
||||||
@@ -159,17 +222,156 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
|
|||||||
return it(call)
|
return it(call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return call
|
return call
|
||||||
}
|
}
|
||||||
}, null)
|
}, 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?
|
// 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 {
|
call.run {
|
||||||
IrCallImpl(
|
IrCallImpl(
|
||||||
startOffset,
|
startOffset,
|
||||||
@@ -204,10 +406,20 @@ private fun IrCall.copyTypeAndValueArgumentsFrom(call: IrCall, dispatchReceiverA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <V> MutableMap<SimpleMemberKey, V>.op(type: IrType, name: Name, v: V) {
|
private fun MutableMap<SimpleMemberKey, IrSimpleFunctionSymbol>.op(type: IrType, name: Name, v: IrSimpleFunctionSymbol) {
|
||||||
put(SimpleMemberKey(type, name), v)
|
put(SimpleMemberKey(type, name), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MutableMap<SimpleMemberKey, IrSimpleFunctionSymbol>.op(type: IrType, name: Name, v: IrSimpleFunction) {
|
||||||
|
put(SimpleMemberKey(type, name), v.symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableMap<SimpleMemberKey, (IrCall) -> 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 <V> MutableMap<SimpleMemberKey, V>.op(type: IrType, name: String, v: V) {
|
private fun <V> MutableMap<SimpleMemberKey, V>.op(type: IrType, name: String, v: V) {
|
||||||
put(SimpleMemberKey(type, Name.identifier(name)), v)
|
put(SimpleMemberKey(type, Name.identifier(name)), v)
|
||||||
}
|
}
|
||||||
@@ -222,7 +434,7 @@ private fun <V> MutableMap<IrFunctionSymbol, V>.add(from: IrFunctionSymbol, to:
|
|||||||
put(from, to)
|
put(from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <K> MutableMap<K, (IrCall) -> IrCall>.addWithPredicate(from: K, predicate: (IrCall) -> Boolean, action: (IrCall) -> IrCall) {
|
private fun <K> MutableMap<K, (IrCall) -> IrExpression>.addWithPredicate(from: K, predicate: (IrCall) -> Boolean, action: (IrCall) -> IrExpression) {
|
||||||
put(from) { call: IrCall -> select({ predicate(call) }, { action(call) }, { call }) }
|
put(from) { call: IrCall -> select({ predicate(call) }, { action(call) }, { call }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
@@ -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")
|
||||||
|
}
|
||||||
+9
-1
@@ -62,4 +62,12 @@ val CallableMemberDescriptor.propertyIfAccessor
|
|||||||
else this
|
else this
|
||||||
|
|
||||||
val IrTypeParameter.isReified
|
val IrTypeParameter.isReified
|
||||||
get() = descriptor.isReified
|
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() }
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ fun IrType.isAny(): Boolean = isBuiltInClassType(KotlinBuiltIns::isAny, hasQuest
|
|||||||
fun IrType.isNullableAny(): Boolean = isBuiltInClassType(KotlinBuiltIns::isAny, hasQuestionMark = true)
|
fun IrType.isNullableAny(): Boolean = isBuiltInClassType(KotlinBuiltIns::isAny, hasQuestionMark = true)
|
||||||
|
|
||||||
fun IrType.isString(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.string)
|
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.isArray(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.array)
|
||||||
fun IrType.isNothing(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.nothing)
|
fun IrType.isNothing(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.nothing)
|
||||||
fun IrType.isPrimitiveType(): Boolean =
|
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.isLong(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._long)
|
||||||
fun IrType.isFloat(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._float)
|
fun IrType.isFloat(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._float)
|
||||||
fun IrType.isDouble(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._double)
|
fun IrType.isDouble(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES._double)
|
||||||
fun IrType.isNumber(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.number)
|
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)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// IGNORE_BACKEND: NATIVE
|
// IGNORE_BACKEND: NATIVE
|
||||||
|
|
||||||
fun equals1(a: Double, b: Double) = a.equals(b)
|
fun equals1(a: Double, b: Double) = a.equals(b)
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// IGNORE_BACKEND: JS, NATIVE
|
// IGNORE_BACKEND: JS, NATIVE
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
interface TextField {
|
interface TextField {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
class KeySpan(val left: String) {
|
class KeySpan(val left: String) {
|
||||||
|
|
||||||
public fun matches(value : String) : Boolean {
|
public fun matches(value : String) : Boolean {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
class A<in I>(init_o: I, private val init_k: I) {
|
class A<in I>(init_o: I, private val init_k: I) {
|
||||||
private val o: I = init_o
|
private val o: I = init_o
|
||||||
private fun k(): I = init_k
|
private fun k(): I = init_k
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
data class A(val a: Boolean)
|
data class A(val a: Boolean)
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
data class A(val a: Byte)
|
data class A(val a: Byte)
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
data class A(val a: Double)
|
data class A(val a: Double)
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
data class A(val a: Float)
|
data class A(val a: Float)
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
data class A(val a: Int)
|
data class A(val a: Int)
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
data class A(val a: Short)
|
data class A(val a: Short)
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
var log = ""
|
var log = ""
|
||||||
@@ -15,4 +14,4 @@ var String.calc: String by UserDataProperty("K")
|
|||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
return "O".calc
|
return "O".calc
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun <T> T.toPrefixedString(prefix: String = "", suffix: String="") = prefix + this.toString() + suffix
|
fun <T> T.toPrefixedString(prefix: String = "", suffix: String="") = prefix + this.toString() + suffix
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
return justPrint(9.compareTo(4))
|
return justPrint(9.compareTo(4))
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
val foo1 = fun Any.(): String {
|
val foo1 = fun Any.(): String {
|
||||||
return "239" + this
|
return "239" + this
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun Any.foo1() : ()-> String {
|
fun Any.foo1() : ()-> String {
|
||||||
return { "239" + this }
|
return { "239" + this }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun foo(): Int {
|
fun foo(): Int {
|
||||||
val a = "test"
|
val a = "test"
|
||||||
val b = "test"
|
val b = "test"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun less1(a: Double, b: Double) = a.compareTo(b) == -1
|
fun less1(a: Double, b: Double) = a.compareTo(b) == -1
|
||||||
|
|
||||||
fun less2(a: Double?, b: Double?) = a!!.compareTo(b!!) == -1
|
fun less2(a: Double?, b: Double?) = a!!.compareTo(b!!) == -1
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun equals1(a: Double, b: Double) = a.equals(b)
|
fun equals1(a: Double, b: Double) = a.equals(b)
|
||||||
|
|
||||||
fun equals2(a: Double?, b: Double?) = a!!.equals(b!!)
|
fun equals2(a: Double?, b: Double?) = a!!.equals(b!!)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val plusZero: Double? = 0.0
|
val plusZero: Double? = 0.0
|
||||||
val minusZero: Double = -0.0
|
val minusZero: Double = -0.0
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: +ProperIeee754Comparisons
|
// !LANGUAGE: +ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
|
|
||||||
fun ne(x: Any, y: Any) = x is Double && y is Float && x != y
|
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
|
fun lt(x: Any, y: Any) = x is Double && y is Float && x < y
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
public fun box() : String {
|
public fun box() : String {
|
||||||
var i : Short?
|
var i : Short?
|
||||||
i = 10
|
i = 10
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
if (12.toString().equals("13")) {
|
if (12.toString().equals("13")) {
|
||||||
return "Fail"
|
return "Fail"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
var aByte: Byte? = 0
|
var aByte: Byte? = 0
|
||||||
var bByte: Byte = 0
|
var bByte: Byte = 0
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun test(i: Int): Int {
|
fun test(i: Int): Int {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun checkLess(x: Boolean, y: Boolean) = when {
|
fun checkLess(x: Boolean, y: Boolean) = when {
|
||||||
x >= y -> "Fail $x >= $y"
|
x >= y -> "Fail $x >= $y"
|
||||||
!(x < y) -> "Fail !($x < $y)"
|
!(x < y) -> "Fail !($x < $y)"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun checkLess(x: Double, y: Int) = when {
|
fun checkLess(x: Double, y: Int) = when {
|
||||||
x >= y -> "Fail $x >= $y"
|
x >= y -> "Fail $x >= $y"
|
||||||
!(x < y) -> "Fail !($x < $y)"
|
!(x < y) -> "Fail !($x < $y)"
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
class A(val x: Int)
|
class A(val x: Int)
|
||||||
|
|
||||||
operator fun A.compareTo(other: A) = x.compareTo(other.x)
|
operator fun A.compareTo(other: A) = x.compareTo(other.x)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun checkLess(x: Int, y: Double) = when {
|
fun checkLess(x: Int, y: Double) = when {
|
||||||
x >= y -> "Fail $x >= $y"
|
x >= y -> "Fail $x >= $y"
|
||||||
!(x < y) -> "Fail !($x < $y)"
|
!(x < y) -> "Fail !($x < $y)"
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
||||||
|
|
||||||
val nx: Byte? = 0.toByte()
|
val nx: Byte? = 0.toByte()
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
||||||
|
|
||||||
val nx: Short? = 0.toShort()
|
val nx: Short? = 0.toShort()
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
||||||
|
|
||||||
val nx: Byte? = 0.toByte()
|
val nx: Byte? = 0.toByte()
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
||||||
|
|
||||||
val nx: Short? = 0.toShort()
|
val nx: Short? = 0.toShort()
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
||||||
|
|
||||||
val nx: Any? = 0.toByte()
|
val nx: Any? = 0.toByte()
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
|
||||||
|
|
||||||
val nx: Any? = 0.toShort()
|
val nx: Any? = 0.toShort()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
val f : (Any) -> String = { it.toString() }
|
val f : (Any) -> String = { it.toString() }
|
||||||
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box() : String {
|
fun box() : String {
|
||||||
230?.toByte()?.hashCode()
|
230?.toByte()?.hashCode()
|
||||||
9.hashCode()
|
9.hashCode()
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun <T> assertEquals(a: T, b: T) {
|
fun <T> assertEquals(a: T, b: T) {
|
||||||
if (a != b) throw AssertionError("$a != $b")
|
if (a != b) throw AssertionError("$a != $b")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
if (1 >= 1.9) return "Fail #1"
|
if (1 >= 1.9) return "Fail #1"
|
||||||
if (1.compareTo(1.1) >= 0) return "Fail #2"
|
if (1.compareTo(1.1) >= 0) return "Fail #2"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// KT-3517 Can't call .equals() on a boolean
|
// KT-3517 Can't call .equals() on a boolean
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
object TestObject {
|
object TestObject {
|
||||||
val testFloat: Float = 0.9999.toFloat()
|
val testFloat: Float = 0.9999.toFloat()
|
||||||
val otherFloat: Float = 1.01.toFloat()
|
val otherFloat: Float = 1.01.toFloat()
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
val _0 : Double = 0.0
|
val _0 : Double = 0.0
|
||||||
val _0dbl : Double = 0.toDouble()
|
val _0dbl : Double = 0.toDouble()
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
inline fun <reified T> Any?.check(): Boolean {
|
inline fun <reified T> Any?.check(): Boolean {
|
||||||
return this is T
|
return this is T
|
||||||
}
|
}
|
||||||
@@ -35,4 +34,4 @@ fun box(): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
if (!foo(1.toByte())) return "fail 1"
|
if (!foo(1.toByte())) return "fail 1"
|
||||||
if (!foo((1.toByte()).inc())) return "fail 2"
|
if (!foo((1.toByte()).inc())) return "fail 2"
|
||||||
|
|||||||
@@ -1113,6 +1113,15 @@ public abstract class KotlinBuiltIns {
|
|||||||
return classFqNameEquals(descriptor, FQ_NAMES._enum);
|
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) {
|
public static boolean isCharSequence(@Nullable KotlinType type) {
|
||||||
return type != null && isNotNullConstructedFromGivenClass(type, FQ_NAMES.charSequence);
|
return type != null && isNotNullConstructedFromGivenClass(type, FQ_NAMES.charSequence);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -545,7 +545,11 @@ abstract class BasicBoxTest(
|
|||||||
val psiManager = PsiManager.getInstance(project)
|
val psiManager = PsiManager.getInstance(project)
|
||||||
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
|
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<String>): List<KtFile> = fileNames.map(this::createPsiFile)
|
private fun createPsiFiles(fileNames: List<String>): List<KtFile> = fileNames.map(this::createPsiFile)
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ abstract class BasicIrBoxTest(
|
|||||||
) {
|
) {
|
||||||
val runtime = listOf(
|
val runtime = listOf(
|
||||||
"libraries/stdlib/js/src/kotlin/core.kt",
|
"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/annotations.kt",
|
||||||
"libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt",
|
"libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt",
|
||||||
"libraries/stdlib/js/irRuntime/exceptions.kt",
|
"libraries/stdlib/js/irRuntime/exceptions.kt",
|
||||||
|
|||||||
+23
@@ -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")
|
@TestMetadata("js/js.translator/testData/box/callableReference")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+23
@@ -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")
|
@TestMetadata("js/js.translator/testData/box/callableReference")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1109
|
// EXPECTED_REACHABLE_NODES: 1109
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1126
|
// EXPECTED_REACHABLE_NODES: 1126
|
||||||
// This test was adapted from compiler/testData/codegen/box/classes
|
// This test was adapted from compiler/testData/codegen/box/classes
|
||||||
package foo
|
package foo
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1114
|
// EXPECTED_REACHABLE_NODES: 1114
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1110
|
// EXPECTED_REACHABLE_NODES: 1110
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1114
|
// EXPECTED_REACHABLE_NODES: 1114
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1109
|
// EXPECTED_REACHABLE_NODES: 1109
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1109
|
// EXPECTED_REACHABLE_NODES: 1109
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1109
|
// EXPECTED_REACHABLE_NODES: 1109
|
||||||
/*
|
/*
|
||||||
* Copy of JVM-backend test
|
* Copy of JVM-backend test
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1112
|
// EXPECTED_REACHABLE_NODES: 1112
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1110
|
// EXPECTED_REACHABLE_NODES: 1110
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1110
|
// EXPECTED_REACHABLE_NODES: 1110
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1114
|
// EXPECTED_REACHABLE_NODES: 1114
|
||||||
// http://youtrack.jetbrains.com/issue/KT-5345
|
// http://youtrack.jetbrains.com/issue/KT-5345
|
||||||
// KT-5345 (Javascript) Type mismatch on Int / Float division
|
// KT-5345 (Javascript) Type mismatch on Int / Float division
|
||||||
|
|||||||
@@ -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<Comparable<*>>().compareTo(b) when bridge is implemented
|
||||||
|
return js("a.compareTo(b)").unsafeCast<Int>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun primitiveCompareTo(a: dynamic, b: dynamic): Int =
|
||||||
|
js("a < b ? -1 : a > b ? 1 : 0").unsafeCast<Int>()
|
||||||
|
|
||||||
|
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<Int>()
|
||||||
@@ -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<Boolean>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(o: dynamic): String = when {
|
||||||
|
o == null -> "null"
|
||||||
|
isArrayish(o) -> "[...]"
|
||||||
|
else -> js("o.toString()").unsafeCast<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<Int>()
|
||||||
|
}
|
||||||
@@ -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<Double>()
|
||||||
|
|
||||||
|
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<Byte>()
|
||||||
|
fun toShort(a: dynamic): Short = js("a << 16 >> 16").unsafeCast<Short>()
|
||||||
|
|
||||||
|
fun doubleToInt(a: dynamic) = js("""
|
||||||
|
if (a > 2147483647) return 2147483647;
|
||||||
|
if (a < -2147483648) return -2147483648;
|
||||||
|
return a | 0;
|
||||||
|
""").unsafeCast<Int>()
|
||||||
@@ -75,7 +75,7 @@ public fun isInterface(ctor: dynamic, IType: dynamic): Boolean {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inline private fun typeOf(obj: dynamic) = js("typeof obj").unsafeCast<String>()
|
fun typeOf(obj: dynamic) = js("typeof obj").unsafeCast<String>()
|
||||||
|
|
||||||
fun isObject(obj: dynamic): Boolean {
|
fun isObject(obj: dynamic): Boolean {
|
||||||
val objTypeOf = typeOf(obj)
|
val objTypeOf = typeOf(obj)
|
||||||
@@ -93,6 +93,10 @@ public fun isArray(obj: Any): Boolean {
|
|||||||
return js("Array.isArray(obj)").unsafeCast<Boolean>()
|
return js("Array.isArray(obj)").unsafeCast<Boolean>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun isArrayish(o: dynamic) =
|
||||||
|
isArray(o) || js("ArrayBuffer.isView(o)").unsafeCast<Boolean>()
|
||||||
|
|
||||||
|
|
||||||
public fun isChar(c: Any): Boolean {
|
public fun isChar(c: Any): Boolean {
|
||||||
return js("throw Error(\"isChar is not implemented\")").unsafeCast<Boolean>()
|
return js("throw Error(\"isChar is not implemented\")").unsafeCast<Boolean>()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user