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
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ const val STATIC_INIT_METHOD_NAME = "%statInit"

const val ANONYMOUS_CLASS_PREFIX = "%AC"
const val ANONYMOUS_METHOD_PREFIX = "%AM"
const val NAMED_FUNCTION_REFERENCE_PREFIX = "%AMnamed$"

const val TEMP_LOCAL_PREFIX = "%"
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import org.jacodb.ets.dto.RawValueDto
import org.jacodb.ets.dto.RelationOperationDto
import org.jacodb.ets.dto.RequireObjectCoercibleExprDto
import org.jacodb.ets.dto.ReturnStmtDto
import org.jacodb.ets.dto.StaticCallExprDto
import org.jacodb.ets.dto.StaticFieldRefDto
import org.jacodb.ets.dto.StringTypeDto
import org.jacodb.ets.dto.SymbolTypeDto
Expand Down Expand Up @@ -497,7 +496,7 @@ class EtsTsFrontendTest {
val stmts = defaultClass.methods.single { it.signature.name == DEFAULT_ARK_METHOD_NAME }
.body!!.cfg.blocks.flatMap { it.stmts }
val callIndex = stmts.indexOfFirst {
it is AssignStmtDto && (it.right as? StaticCallExprDto)?.method?.name == "sideEffect"
it is AssignStmtDto && (it.right as? PtrCallExprDto)?.method?.name == "sideEffect"
}
val exportIndex = stmts.indexOfFirst {
it is AssignStmtDto && (it.left as? StaticFieldRefDto)?.field?.name == "default"
Expand All @@ -509,6 +508,13 @@ class EtsTsFrontendTest {
assertTrue(callIndex >= 0)
assertTrue(exportIndex > callIndex)
assertTrue(afterIndex > exportIndex)
val call = stmts[callIndex] as AssignStmtDto
val bindingRead = stmts.filterIsInstance<AssignStmtDto>()
.single { (it.right as? StaticFieldRefDto)?.field?.name == "sideEffect" }

assertEquals(bindingRead.left, (call.right as PtrCallExprDto).ptr)
assertTrue(stmts.indexOf(bindingRead) < callIndex)
assertEquals(call.left, (stmts[exportIndex] as AssignStmtDto).right)
assertEquals("default", dto.exportInfos.single { it.exportName == "default" }.exportName)

val model = dto.toEtsFile()
Expand Down Expand Up @@ -683,22 +689,31 @@ class EtsTsFrontendTest {
val methodDto = roundTripped.classes.single { it.signature.name == DEFAULT_ARK_CLASS_NAME }
.methods.single { it.signature.name == "check" }
val assignments = methodDto.body!!.cfg.blocks.flatMap { it.stmts }.filterIsInstance<AssignStmtDto>()
val pickCall = assignments.single { (it.right as? StaticCallExprDto)?.method?.name == "pick" }
val argumentCall = assignments.single { (it.right as? StaticCallExprDto)?.method?.name == "argument" }
val pickCall = assignments.single { (it.right as? PtrCallExprDto)?.method?.name == "pick" }
val argumentCall = assignments.single { (it.right as? PtrCallExprDto)?.method?.name == "argument" }
val pickBindingRead = assignments.single { (it.right as? StaticFieldRefDto)?.field?.name == "pick" }
val argumentBindingRead = assignments.single { (it.right as? StaticFieldRefDto)?.field?.name == "argument" }
val allocation = assignments.single { it.right is NewExprDto }
val allocationValue = allocation.right as NewExprDto

assertEquals(pickCall.left as LocalDto, allocationValue.constructorValue as LocalDto)
assertEquals(pickBindingRead.left, (pickCall.right as PtrCallExprDto).ptr)
assertEquals(argumentBindingRead.left, (argumentCall.right as PtrCallExprDto).ptr)
assertTrue(assignments.indexOf(pickBindingRead) < assignments.indexOf(pickCall))
assertTrue(assignments.indexOf(argumentBindingRead) < assignments.indexOf(argumentCall))
assertTrue(assignments.indexOf(pickCall) < assignments.indexOf(argumentCall))
assertTrue(assignments.indexOf(argumentCall) < assignments.indexOf(allocation))

val modelMethod = roundTripped.toEtsFile().classes.single { it.name == DEFAULT_ARK_CLASS_NAME }
.methods.single { it.name == "check" }
val modelAllocation = modelMethod.cfg.stmts.filterIsInstance<EtsAssignStmt>()
.single { it.rhv is EtsNewExpr }.rhv as EtsNewExpr
val modelPickCall = modelMethod.cfg.stmts.filterIsInstance<EtsAssignStmt>()
.single { (it.rhv as? EtsPtrCallExpr)?.callee?.name == "pick" }
val constructorValue = modelAllocation.constructorValue as EtsLocal

assertEquals((pickCall.left as LocalDto).name, constructorValue.name)
assertEquals(modelPickCall.lhv, constructorValue)
assertEquals(constructorValue, modelAllocation.getOperands().single())
}

Expand Down Expand Up @@ -760,19 +775,25 @@ class EtsTsFrontendTest {
val defaultClass = dto.classes.single { it.signature.name == DEFAULT_ARK_CLASS_NAME }
val checkMethod = defaultClass.methods.single { it.signature.name == "check" }
val assignments = checkMethod.body!!.cfg.blocks.flatMap { it.stmts }.filterIsInstance<AssignStmtDto>()
val constructorCall = assignments.single { (it.right as? StaticCallExprDto)?.method?.name == "choose" }
val constructorCall = assignments.single { (it.right as? PtrCallExprDto)?.method?.name == "choose" }
val bindingRead = assignments.single { (it.right as? StaticFieldRefDto)?.field?.name == "choose" }
val instanceCheck = assignments.single { it.right is InstanceOfExprDto }.right as InstanceOfExprDto

assertEquals(constructorCall.left, instanceCheck.checkValue)
assertEquals(bindingRead.left, (constructorCall.right as PtrCallExprDto).ptr)
assertTrue(assignments.indexOf(bindingRead) < assignments.indexOf(constructorCall))
assertEquals(null, instanceCheck.checkType)

val model = dto.toEtsFile()
val modelMethod = model.classes.single { it.name == DEFAULT_ARK_CLASS_NAME }
.methods.single { it.name == "check" }
val modelCheck = modelMethod.cfg.stmts.filterIsInstance<EtsAssignStmt>()
.single { it.rhv is EtsInstanceOfExpr }.rhv as EtsInstanceOfExpr
val modelConstructorCall = modelMethod.cfg.stmts.filterIsInstance<EtsAssignStmt>()
.single { (it.rhv as? EtsPtrCallExpr)?.callee?.name == "choose" }

assertEquals((constructorCall.left as LocalDto).name, (modelCheck.checkValue as EtsLocal).name)
assertEquals(modelConstructorCall.lhv, modelCheck.checkValue)
assertEquals(null, modelCheck.checkType)
}

Expand Down
2 changes: 2 additions & 0 deletions jacodb-ets/ts-frontend/src/dto/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export const INSTANCE_INIT_METHOD_NAME = "%instInit";
export const STATIC_INIT_METHOD_NAME = "%statInit";
export const ANONYMOUS_CLASS_PREFIX = "%AC";
export const ANONYMOUS_METHOD_PREFIX = "%AM";
/** A value reference to an existing named method, rather than a lifted closure. */
export const NAMED_FUNCTION_REFERENCE_PREFIX = "%AMnamed$";
export const TEMP_LOCAL_PREFIX = "%";
/** Synthetic name of a destructuring (pattern) parameter; the index keeps names distinct. */
export const PATTERN_PARAMETER_PREFIX = "%pat";
Expand Down
36 changes: 33 additions & 3 deletions jacodb-ets/ts-frontend/src/lowering/astUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
/** Shared AST helpers used across the lowering modules. */

import * as ts from "typescript";
import { COMPUTED_MEMBER_NAME, Modifier, PATTERN_PARAMETER_PREFIX } from "../dto/constants";
import { COMPUTED_MEMBER_NAME, DEFAULT_ARK_CLASS_NAME, Modifier, PATTERN_PARAMETER_PREFIX } from "../dto/constants";
import { DecoratorDto } from "../dto/model";
import { MethodParameterDto } from "../dto/signatures";
import { MethodParameterDto, MethodSignatureDto } from "../dto/signatures";
import { TypeDto, UNKNOWN_TYPE } from "../dto/types";
import type { LoweringContext } from "./methodBuilder";

Expand Down Expand Up @@ -137,13 +137,15 @@ function staticMemberStorage(
return "unsupported";
}

/** Scope functions, ambient classes, enums, and namespaces have no standalone value reference in EtsIR yet. */
/** Ambient declarations, enums, and namespaces have no standalone value reference in EtsIR yet. */
function isUnmaterializedValue(identifier: ts.Identifier, checker: ts.TypeChecker): boolean {
const symbol = resolvedSymbolOf(identifier, checker);
const hasScopeFunctionBody = scopeFunctionImplementation(symbol) !== undefined;

return symbol?.declarations?.some((declaration) =>
(ts.isClassDeclaration(declaration) && declaration.getSourceFile().isDeclarationFile)
|| (ts.isFunctionDeclaration(declaration)
&& !hasScopeFunctionBody
&& (ts.isSourceFile(declaration.parent) || ts.isModuleBlock(declaration.parent)))
|| ts.isEnumDeclaration(declaration)
|| ts.isModuleDeclaration(declaration),
Expand Down Expand Up @@ -342,3 +344,31 @@ export function returnTypeOf(ctx: LoweringContext, decl: ts.SignatureDeclaration
}
return UNKNOWN_TYPE;
}

/** The original body emitted for a file/namespace function declaration. */
export function scopeFunctionImplementation(symbol: ts.Symbol | undefined): ts.FunctionDeclaration | undefined {
const implementations = symbol?.declarations?.filter((declaration): declaration is ts.FunctionDeclaration =>
ts.isFunctionDeclaration(declaration)
&& declaration.name !== undefined
&& declaration.body !== undefined
&& !declaration.getSourceFile().isDeclarationFile
&& (ts.isSourceFile(declaration.parent) || ts.isModuleBlock(declaration.parent)),
);
return implementations?.length === 1 ? implementations[0] : undefined;
}

export function scopeFunctionSignature(ctx: LoweringContext, decl: ts.FunctionDeclaration): MethodSignatureDto {
const declaringClass: MethodSignatureDto["declaringClass"] = {
name: DEFAULT_ARK_CLASS_NAME,
declaringFile: ctx.fileSignatureFor(decl.getSourceFile()),
};
const namespace = ctx.converter.namespaceSignatureOf(decl);
if (namespace !== undefined) declaringClass.declaringNamespace = namespace;

return {
declaringClass,
name: decl.name?.text ?? "default",
parameters: buildParameters(ctx, decl).parameters,
returnType: returnTypeOf(ctx, decl),
};
}
Loading
Loading