Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.jacodb.ets.dto

import java.math.BigInteger

import mu.KotlinLogging
import org.jacodb.ets.model.EtsDefineDataPropertyStmt
import org.jacodb.ets.model.EtsPropertyRef
Expand All @@ -38,6 +40,8 @@ import org.jacodb.ets.model.EtsBitOrExpr
import org.jacodb.ets.model.EtsBitXorExpr
import org.jacodb.ets.model.EtsBlockCfg
import org.jacodb.ets.model.EtsBooleanConstant
import org.jacodb.ets.model.EtsBigIntConstant
import org.jacodb.ets.model.EtsBigIntType
import org.jacodb.ets.model.EtsBooleanLiteralType
import org.jacodb.ets.model.EtsBooleanType
import org.jacodb.ets.model.EtsCallExpr
Expand Down Expand Up @@ -693,6 +697,8 @@ fun TypeDto.toEtsType(): EtsType = when (this) {

BooleanTypeDto -> EtsBooleanType

BigIntTypeDto -> EtsBigIntType

is ClassTypeDto -> toEtsClassType()

is ClassValueTypeDto -> EtsClassValueType(signature.toEtsClassSignature())
Expand Down Expand Up @@ -776,6 +782,8 @@ fun ConstantDto.toEtsConstant(): EtsConstant {

EtsNumberType -> EtsNumberConstant(value = value.toDouble())

EtsBigIntType -> EtsBigIntConstant(value = BigInteger(value))

EtsNullType -> EtsNullConstant

EtsUndefinedType -> EtsUndefinedConstant
Expand Down
5 changes: 5 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Serializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass

private val logger = KotlinLogging.logger {}

Expand All @@ -47,6 +49,9 @@ internal val valueModule = SerializersModule {
}

internal val typeModule = SerializersModule {
polymorphic(TypeDto::class) {
subclass(BigIntTypeDto::class, BigIntTypeDto.serializer())
}
polymorphicDefaultDeserializer(TypeDto::class) { RawTypeSerializer }
}

Expand Down
7 changes: 7 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Types.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ data object NumberTypeDto : PrimitiveTypeDto {
get() = "number"
}

@Serializable
@SerialName("BigIntType")
data object BigIntTypeDto : PrimitiveTypeDto {
override val name: String
get() = "bigint"
}

@Serializable
@SerialName("StringType")
data object StringTypeDto : PrimitiveTypeDto {
Expand Down
15 changes: 15 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.jacodb.ets.model

import java.math.BigInteger

interface EtsConstant : EtsImmediate

data class EtsStringConstant(
Expand Down Expand Up @@ -68,6 +70,19 @@ data class EtsNumberConstant(
}
}

data class EtsBigIntConstant(
val value: BigInteger,
) : EtsConstant {
override val type: EtsType
get() = EtsBigIntType

override fun toString(): String = "${value}n"

override fun <R> accept(visitor: EtsValue.Visitor<R>): R {
return visitor.visit(this)
}
}

object EtsNullConstant : EtsConstant {
override val type: EtsType
get() = EtsNullType
Expand Down
16 changes: 16 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ interface EtsType : TypeName, CommonType {
// Primitive
fun visit(type: EtsBooleanType): R
fun visit(type: EtsNumberType): R
fun visit(type: EtsBigIntType): R {
if (this is Default) return defaultVisit(type)
error("Cannot handle ${type::class.java.simpleName}: $type")
}
fun visit(type: EtsStringType): R
fun visit(type: EtsNullType): R
fun visit(type: EtsUndefinedType): R
Expand Down Expand Up @@ -77,6 +81,7 @@ interface EtsType : TypeName, CommonType {

override fun visit(type: EtsBooleanType): R = defaultVisit(type)
override fun visit(type: EtsNumberType): R = defaultVisit(type)
override fun visit(type: EtsBigIntType): R = defaultVisit(type)
override fun visit(type: EtsStringType): R = defaultVisit(type)
override fun visit(type: EtsNullType): R = defaultVisit(type)
override fun visit(type: EtsUndefinedType): R = defaultVisit(type)
Expand Down Expand Up @@ -234,6 +239,17 @@ object EtsNumberType : EtsPrimitiveType {
}
}

object EtsBigIntType : EtsPrimitiveType {
override val typeName: String
get() = "bigint"

override fun toString(): String = typeName

override fun <R> accept(visitor: EtsType.Visitor<R>): R {
return visitor.visit(this)
}
}

object EtsStringType : EtsPrimitiveType {
override val typeName: String
get() = "string"
Expand Down
5 changes: 5 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ interface EtsValue : EtsEntity, CommonValue {
fun visit(value: EtsStringConstant): R
fun visit(value: EtsBooleanConstant): R
fun visit(value: EtsNumberConstant): R
fun visit(value: EtsBigIntConstant): R {
if (this is Default) return defaultVisit(value)
error("Cannot handle ${value::class.java.simpleName}: $value")
}
fun visit(value: EtsNullConstant): R
fun visit(value: EtsUndefinedConstant): R

Expand All @@ -57,6 +61,7 @@ interface EtsValue : EtsEntity, CommonValue {
override fun visit(value: EtsStringConstant): R = defaultVisit(value)
override fun visit(value: EtsBooleanConstant): R = defaultVisit(value)
override fun visit(value: EtsNumberConstant): R = defaultVisit(value)
override fun visit(value: EtsBigIntConstant): R = defaultVisit(value)
override fun visit(value: EtsNullConstant): R = defaultVisit(value)
override fun visit(value: EtsUndefinedConstant): R = defaultVisit(value)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import org.jacodb.ets.model.EtsNotExpr
import org.jacodb.ets.model.EtsNullConstant
import org.jacodb.ets.model.EtsNullishCoalescingExpr
import org.jacodb.ets.model.EtsNumberConstant
import org.jacodb.ets.model.EtsBigIntConstant
import org.jacodb.ets.model.EtsOrExpr
import org.jacodb.ets.model.EtsParameterRef
import org.jacodb.ets.model.EtsPostDecExpr
Expand Down Expand Up @@ -157,6 +158,9 @@ private object EntityGetOperands : EtsEntity.Visitor<Sequence<EtsEntity>> {
override fun visit(value: EtsNumberConstant): Sequence<EtsEntity> =
emptySequence()

override fun visit(value: EtsBigIntConstant): Sequence<EtsEntity> =
emptySequence()

override fun visit(value: EtsNullConstant): Sequence<EtsEntity> =
emptySequence()

Expand Down
5 changes: 5 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import org.jacodb.ets.model.EtsNotExpr
import org.jacodb.ets.model.EtsNullConstant
import org.jacodb.ets.model.EtsNullishCoalescingExpr
import org.jacodb.ets.model.EtsNumberConstant
import org.jacodb.ets.model.EtsBigIntConstant
import org.jacodb.ets.model.EtsOrExpr
import org.jacodb.ets.model.EtsParameterRef
import org.jacodb.ets.model.EtsPostDecExpr
Expand Down Expand Up @@ -201,6 +202,10 @@ abstract class AbstractHandler : EtsEntity.Visitor<Unit>, EtsStmt.Visitor<Unit>
handle(value)
}

final override fun visit(value: EtsBigIntConstant) {
handle(value)
}

final override fun visit(value: EtsNullConstant) {
handle(value)
}
Expand Down
100 changes: 100 additions & 0 deletions jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsTsBigIntTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.ets.test

import kotlin.io.path.createTempDirectory
import kotlin.io.path.exists
import kotlin.io.path.readText
import kotlin.io.path.writeText
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.jacodb.ets.dto.EtsFileDto
import org.jacodb.ets.dto.dtoModule
import org.jacodb.ets.dto.toEtsFile
import org.jacodb.ets.utils.DEFAULT_ARK_CLASS_NAME
import org.jacodb.ets.utils.EtsIrProvider
import org.jacodb.ets.utils.generateEtsIR
import org.junit.jupiter.api.Test
import java.math.BigInteger
import org.jacodb.ets.dto.AssignStmtDto
import org.jacodb.ets.dto.BigIntTypeDto
import org.jacodb.ets.dto.ConstantDto
import org.jacodb.ets.model.EtsAssignStmt
import org.jacodb.ets.model.EtsBigIntConstant
import org.jacodb.ets.model.EtsBigIntType

class EtsTsBigIntTest {
companion object {
/** Run the PRODUCTION integration path: generateEtsIR + EtsFileDto.loadFromJson. */
private fun runFrontend(source: String, fileName: String = "test.ts"): EtsFileDto {
val dir = createTempDirectory("ts-frontend-test")
val inputPath = dir.resolve(fileName)
inputPath.writeText(source)

val outputPath = generateEtsIR(
inputPath,
isProject = false,
timeout = null,
provider = EtsIrProvider.TS_FRONTEND,
)
assertTrue(outputPath.exists(), "ts-frontend did not produce output: $outputPath")

return EtsFileDto.loadFromJson(outputPath.readText())
}
}

@Test
fun `bigint types and exact constants survive frontend JSON and model conversion`() {
val frontendDto = runFrontend(
source =
"""
export function increment(x: bigint): bigint { return x + 1n; }
export const large = 9007199254740993n;
export const negative = -0x20n;
""".trimIndent(),
)
val roundTripped = EtsFileDto.loadFromJson(
Json { serializersModule = dtoModule }.encodeToString(frontendDto),
)
val defaultClass = roundTripped.classes.single { it.signature.name == DEFAULT_ARK_CLASS_NAME }
val incrementDto = defaultClass.methods.single { it.signature.name == "increment" }
val constantsDto = defaultClass.methods.flatMap { it.body?.cfg?.blocks.orEmpty() }
.flatMap { it.stmts }
.filterIsInstance<AssignStmtDto>()
.mapNotNull { it.right as? ConstantDto }
.filter { it.type == BigIntTypeDto }

assertEquals(BigIntTypeDto, incrementDto.signature.parameters.single().type)
assertEquals(BigIntTypeDto, incrementDto.signature.returnType)
assertTrue(constantsDto.any { it.value == "9007199254740993" })
assertTrue(constantsDto.any { it.value == "-32" })

val modelClass = roundTripped.toEtsFile().classes.single { it.name == DEFAULT_ARK_CLASS_NAME }
val incrementModel = modelClass.methods.single { it.name == "increment" }
val bigInts = modelClass.methods.flatMap { it.cfg.stmts }
.filterIsInstance<EtsAssignStmt>()
.mapNotNull { it.rhv as? EtsBigIntConstant }

assertEquals(EtsBigIntType, incrementModel.signature.parameters.single().type)
assertEquals(EtsBigIntType, incrementModel.signature.returnType)
assertTrue(bigInts.any { it.value == BigInteger("9007199254740993") })
assertTrue(bigInts.any { it.value == BigInteger("-32") })
}

}
6 changes: 6 additions & 0 deletions jacodb-ets/ts-frontend/src/dto/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export type TypeDto =
| IntersectionTypeDto
| BooleanTypeDto
| NumberTypeDto
| BigIntTypeDto
| StringTypeDto
| NullTypeDto
| UndefinedTypeDto
Expand Down Expand Up @@ -118,6 +119,10 @@ export interface NumberTypeDto {
readonly _: "NumberType";
}

export interface BigIntTypeDto {
readonly _: "BigIntType";
}

export interface StringTypeDto {
readonly _: "StringType";
}
Expand Down Expand Up @@ -178,6 +183,7 @@ export const VOID_TYPE: VoidTypeDto = { _: "VoidType" };
export const NEVER_TYPE: NeverTypeDto = { _: "NeverType" };
export const BOOLEAN_TYPE: BooleanTypeDto = { _: "BooleanType" };
export const NUMBER_TYPE: NumberTypeDto = { _: "NumberType" };
export const BIGINT_TYPE: BigIntTypeDto = { _: "BigIntType" };
export const STRING_TYPE: StringTypeDto = { _: "StringType" };
export const NULL_TYPE: NullTypeDto = { _: "NullType" };
export const UNDEFINED_TYPE: UndefinedTypeDto = { _: "UndefinedType" };
11 changes: 11 additions & 0 deletions jacodb-ets/ts-frontend/src/lowering/exprLowering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
} from "../dto/signatures";
import {
BOOLEAN_TYPE,
BIGINT_TYPE,
ClassTypeDto,
NUMBER_TYPE,
NULL_TYPE,
Expand Down Expand Up @@ -211,6 +212,9 @@ export class ExprLowerer {
if (ts.isNumericLiteral(node)) {
return constant(numericConstantText(node), NUMBER_TYPE);
}
if (ts.isBigIntLiteral(node)) {
return constant(bigIntConstantText(node), BIGINT_TYPE);
}
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
return constant(node.text, STRING_TYPE);
}
Expand Down Expand Up @@ -903,6 +907,9 @@ export class ExprLowerer {
if (ts.isNumericLiteral(node.operand)) {
return constant(`-${numericConstantText(node.operand)}`, NUMBER_TYPE);
}
if (ts.isBigIntLiteral(node.operand)) {
return constant(`-${bigIntConstantText(node.operand)}`, BIGINT_TYPE);
}
const operand = this.lowerToImmediate(node.operand);
if (operand._ === "Constant" && operand.value === "Infinity" && operand.type._ === "NumberType") {
return constant("-Infinity", NUMBER_TYPE);
Expand Down Expand Up @@ -1949,6 +1956,10 @@ function numericConstantText(node: ts.NumericLiteral): string {
return Number.isFinite(Number(node.text)) ? node.text : node.getText();
}

function bigIntConstantText(node: ts.BigIntLiteral): string {
return BigInt(node.text.replace(/_/g, "").replace(/n$/, "")).toString();
}

function objectField(declaringClass: ClassSignatureDto, name: string, type: TypeDto): FieldDto {
return {
signature: { declaringClass, name, type },
Expand Down
Loading