From f5aa92651a5da53df097e53f8dbe8555aaab584b Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 24 May 2026 00:05:39 +0100 Subject: [PATCH 01/99] Refactor Clava to conform to the changes introduced by the new weaver generator. This is a massive (but imcomplete) commit. - Replaced the old XML-based Language Specification with the new Java embedded-DSL one. - Updated joinpoint concrete classes to extend their respective abstract classes with a generic CRTP pattern Self type parameter. - Simplified concrete types by having a single direct class hierarchy enforced by the generator. - Concretes no longer declare the node field. That is done in the basae abstract jp in LARA. - Refactored methods to use getNodeImpl() for consistency in accessing underlying AST nodes. - Updated ClavaDirective and ClavaPragmas to use the new AJoinpoint interface. - Adjusted ClavaAstMethods to accommodate changes in JoinPoint interface. --- Clava-JS/src-api/Joinpoints.ts | 3111 +++++++++-------- Clava-JS/src-api/clava/ClavaJoinPoints.ts | 14 +- Clava-JS/src-api/clava/code/Outliner.ts | 17 +- .../src-api/clava/code/SimplifyAssignment.ts | 26 +- .../src-api/clava/code/StatementDecomposer.ts | 21 +- .../src-api/clava/graphs/cfg/CfgBuilder.ts | 13 +- .../src-api/clava/graphs/cfg/NextCfgNode.ts | 7 +- Clava-JS/src-api/clava/pass/SimplifyLoops.ts | 8 +- .../src-api/clava/pass/TransformSwitchToIf.ts | 9 +- .../src-api/clava/stats/StaticOpsCounter.ts | 11 +- Clava-JS/src-api/lara/code/Energy.ts | 5 +- Clava-JS/src-api/lara/code/Logger.ts | 3 +- .../up/fe/specs/clava/ast/decl/VarDecl.java | 4 + ClavaWeaver/build.gradle | 50 +- .../clava/test/issues/Issue_aiq_1.js | 4 +- .../clava/weaverspecs/actionModel.xml | 816 ----- .../resources/clava/weaverspecs/artifacts.xml | 926 ----- .../clava/weaverspecs/joinPointModel.xml | 287 -- ClavaWeaver/settings.gradle | 4 +- .../pt/up/fe/specs/clava/weaver/CxxSpec.java | 1384 ++++++++ .../clava/weaver/ClavaWeaverResource.java | 45 - .../up/fe/specs/clava/weaver/CxxActions.java | 112 +- .../fe/specs/clava/weaver/CxxAttributes.java | 26 +- .../fe/specs/clava/weaver/CxxJoinpoints.java | 43 +- .../up/fe/specs/clava/weaver/CxxSelects.java | 36 +- .../up/fe/specs/clava/weaver/CxxWeaver.java | 25 +- .../fe/specs/clava/weaver/CxxWeaverApi.java | 6 +- .../weaver/abstracts/ACxxWeaverJoinPoint.java | 669 ++-- .../joinpoints/enums/AExpressionUseEnum.java | 26 - .../enums/AFunctionStorageClassEnum.java | 29 - .../joinpoints/enums/ALoopKindEnum.java | 27 - .../enums/AWrapperStmtKindEnum.java | 25 - .../specs/clava/weaver/actions/CallWrap.java | 54 +- .../clava/weaver/gears/ModifiedFilesGear.java | 10 +- .../clava/weaver/importable/AstFactory.java | 345 +- .../clava/weaver/importable/LowLevelApi.java | 21 - .../weaver/joinpoints/CXXCudaKernelCall.java | 23 +- .../weaver/joinpoints/CxxAccessSpecifier.java | 14 +- .../weaver/joinpoints/CxxArrayAccess.java | 51 +- .../clava/weaver/joinpoints/CxxAsmStmt.java | 29 +- .../clava/weaver/joinpoints/CxxAttribute.java | 14 +- .../clava/weaver/joinpoints/CxxBinaryOp.java | 37 +- .../clava/weaver/joinpoints/CxxBody.java | 12 +- .../weaver/joinpoints/CxxBoolLiteral.java | 18 +- .../clava/weaver/joinpoints/CxxBreak.java | 17 +- .../clava/weaver/joinpoints/CxxCall.java | 184 +- .../clava/weaver/joinpoints/CxxCase.java | 36 +- .../clava/weaver/joinpoints/CxxCast.java | 33 +- .../clava/weaver/joinpoints/CxxClass.java | 67 +- .../weaver/joinpoints/CxxClavaException.java | 48 - .../clava/weaver/joinpoints/CxxComment.java | 16 +- .../clava/weaver/joinpoints/CxxContinue.java | 13 +- .../clava/weaver/joinpoints/CxxDecl.java | 20 +- .../clava/weaver/joinpoints/CxxDeclStmt.java | 16 +- .../weaver/joinpoints/CxxDeclarator.java | 13 +- .../clava/weaver/joinpoints/CxxDefault.java | 12 +- .../weaver/joinpoints/CxxDeleteExpr.java | 12 +- .../clava/weaver/joinpoints/CxxEmpty.java | 11 +- .../clava/weaver/joinpoints/CxxEmptyStmt.java | 12 +- .../clava/weaver/joinpoints/CxxEnumDecl.java | 16 +- .../weaver/joinpoints/CxxEnumeratorDecl.java | 12 +- .../clava/weaver/joinpoints/CxxExprStmt.java | 17 +- .../weaver/joinpoints/CxxExpression.java | 103 +- .../clava/weaver/joinpoints/CxxField.java | 12 +- .../clava/weaver/joinpoints/CxxFile.java | 169 +- .../weaver/joinpoints/CxxFloatLiteral.java | 17 +- .../clava/weaver/joinpoints/CxxFunction.java | 374 +- .../clava/weaver/joinpoints/CxxGotoStmt.java | 21 +- .../specs/clava/weaver/joinpoints/CxxIf.java | 44 +- .../weaver/joinpoints/CxxImplicitValue.java | 12 +- .../clava/weaver/joinpoints/CxxInclude.java | 22 +- .../clava/weaver/joinpoints/CxxInitList.java | 17 +- .../weaver/joinpoints/CxxIntLiteral.java | 19 +- .../clava/weaver/joinpoints/CxxLabelDecl.java | 16 +- .../clava/weaver/joinpoints/CxxLabelStmt.java | 20 +- .../clava/weaver/joinpoints/CxxLiteral.java | 13 +- .../clava/weaver/joinpoints/CxxLoop.java | 284 +- .../clava/weaver/joinpoints/CxxMarker.java | 30 +- .../weaver/joinpoints/CxxMemberAccess.java | 38 +- .../weaver/joinpoints/CxxMemberCall.java | 20 +- .../clava/weaver/joinpoints/CxxMethod.java | 38 +- .../clava/weaver/joinpoints/CxxNamedDecl.java | 32 +- .../clava/weaver/joinpoints/CxxNewExpr.java | 12 +- .../specs/clava/weaver/joinpoints/CxxOmp.java | 144 +- .../specs/clava/weaver/joinpoints/CxxOp.java | 31 +- .../clava/weaver/joinpoints/CxxParam.java | 14 +- .../clava/weaver/joinpoints/CxxParenExpr.java | 16 +- .../clava/weaver/joinpoints/CxxPragma.java | 33 +- .../clava/weaver/joinpoints/CxxProgram.java | 135 +- .../clava/weaver/joinpoints/CxxRecord.java | 40 +- .../weaver/joinpoints/CxxReturnStmt.java | 29 +- .../clava/weaver/joinpoints/CxxScope.java | 116 +- .../clava/weaver/joinpoints/CxxStatement.java | 37 +- .../clava/weaver/joinpoints/CxxStruct.java | 12 +- .../clava/weaver/joinpoints/CxxSwitch.java | 28 +- .../weaver/joinpoints/CxxSwitchCase.java | 12 +- .../specs/clava/weaver/joinpoints/CxxTag.java | 24 +- .../clava/weaver/joinpoints/CxxTernaryOp.java | 25 +- .../clava/weaver/joinpoints/CxxThis.java | 20 +- .../weaver/joinpoints/CxxTypedefDecl.java | 12 +- .../weaver/joinpoints/CxxTypedefNameDecl.java | 12 +- .../weaver/joinpoints/CxxUnaryExprOrType.java | 37 +- .../clava/weaver/joinpoints/CxxUnaryOp.java | 24 +- .../clava/weaver/joinpoints/CxxVardecl.java | 97 +- .../clava/weaver/joinpoints/CxxVarref.java | 46 +- .../weaver/joinpoints/CxxWrapperStmt.java | 27 +- .../weaver/joinpoints/GenericJoinpoint.java | 12 +- .../weaver/joinpoints/cilk/CxxCilkFor.java | 14 +- .../weaver/joinpoints/cilk/CxxCilkSpawn.java | 14 +- .../weaver/joinpoints/cilk/CxxCilkSync.java | 13 +- .../joinpoints/types/CxxAdjustedType.java | 23 +- .../weaver/joinpoints/types/CxxArrayType.java | 21 +- .../joinpoints/types/CxxBuiltinType.java | 35 +- .../joinpoints/types/CxxElaboratedType.java | 24 +- .../weaver/joinpoints/types/CxxEnumType.java | 18 +- .../joinpoints/types/CxxFunctionType.java | 35 +- .../types/CxxIncompleteArrayType.java | 13 +- .../weaver/joinpoints/types/CxxParenType.java | 23 +- .../joinpoints/types/CxxPointerType.java | 25 +- .../weaver/joinpoints/types/CxxQualType.java | 21 +- .../weaver/joinpoints/types/CxxTagType.java | 18 +- .../types/CxxTemplateSpecializationType.java | 27 +- .../weaver/joinpoints/types/CxxType.java | 156 +- .../joinpoints/types/CxxTypedefType.java | 20 +- .../joinpoints/types/CxxUndefinedType.java | 13 +- .../types/CxxVariableArrayType.java | 21 +- .../clava/weaver/pragmas/ClavaDirective.java | 4 +- .../clava/weaver/pragmas/ClavaPragmas.java | 3 +- .../clava/weaver/utils/ClavaAstMethods.java | 4 +- 129 files changed, 5078 insertions(+), 6735 deletions(-) delete mode 100644 ClavaWeaver/resources/clava/weaverspecs/actionModel.xml delete mode 100644 ClavaWeaver/resources/clava/weaverspecs/artifacts.xml delete mode 100644 ClavaWeaver/resources/clava/weaverspecs/joinPointModel.xml create mode 100644 ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/ClavaWeaverResource.java delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AExpressionUseEnum.java delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AFunctionStorageClassEnum.java delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/ALoopKindEnum.java delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AWrapperStmtKindEnum.java delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClavaException.java diff --git a/Clava-JS/src-api/Joinpoints.ts b/Clava-JS/src-api/Joinpoints.ts index e7e764a90b..e670f5fca8 100644 --- a/Clava-JS/src-api/Joinpoints.ts +++ b/Clava-JS/src-api/Joinpoints.ts @@ -7,132 +7,132 @@ import { registerJoinpointMapper, wrapJoinPoint, unwrapJoinPoint, + InsertPosition, } from "@specs-feup/lara/api/LaraJoinPoint.js"; type PrivateMapper = { "Joinpoint": typeof Joinpoint, - "Attribute": typeof Attribute, - "ClavaException": typeof ClavaException, - "Comment": typeof Comment, - "Decl": typeof Decl, "Empty": typeof Empty, - "Expression": typeof Expression, + "Program": typeof Program, "FileJp": typeof FileJp, - "ImplicitValue": typeof ImplicitValue, - "Include": typeof Include, - "InitList": typeof InitList, - "Literal": typeof Literal, - "MemberAccess": typeof MemberAccess, + "Decl": typeof Decl, "NamedDecl": typeof NamedDecl, - "NewExpr": typeof NewExpr, - "Op": typeof Op, - "ParenExpr": typeof ParenExpr, - "Pragma": typeof Pragma, - "Program": typeof Program, + "Declarator": typeof Declarator, + "Include": typeof Include, "RecordJp": typeof RecordJp, - "Statement": typeof Statement, + "Field": typeof Field, "Struct": typeof Struct, - "Switch": typeof Switch, - "SwitchCase": typeof SwitchCase, - "Tag": typeof Tag, - "TernaryOp": typeof TernaryOp, - "This": typeof This, - "Type": typeof Type, + "Class": typeof Class, + "Vardecl": typeof Vardecl, "TypedefNameDecl": typeof TypedefNameDecl, - "TypedefType": typeof TypedefType, - "UnaryExprOrType": typeof UnaryExprOrType, - "UnaryOp": typeof UnaryOp, - "UndefinedType": typeof UndefinedType, - "Varref": typeof Varref, - "WrapperStmt": typeof WrapperStmt, + "TypedefDecl": typeof TypedefDecl, + "EnumDecl": typeof EnumDecl, + "EnumeratorDecl": typeof EnumeratorDecl, + "LabelDecl": typeof LabelDecl, "AccessSpecifier": typeof AccessSpecifier, - "AdjustedType": typeof AdjustedType, - "ArrayAccess": typeof ArrayAccess, - "ArrayType": typeof ArrayType, - "AsmStmt": typeof AsmStmt, - "BinaryOp": typeof BinaryOp, - "BoolLiteral": typeof BoolLiteral, - "Break": typeof Break, - "BuiltinType": typeof BuiltinType, - "Call": typeof Call, + "Param": typeof Param, + "FunctionJp": typeof FunctionJp, + "Method": typeof Method, + "Pragma": typeof Pragma, + "Marker": typeof Marker, + "Tag": typeof Tag, + "Omp": typeof Omp, + "Statement": typeof Statement, + "Scope": typeof Scope, + "Body": typeof Body, + "Loop": typeof Loop, + "If": typeof If, + "WrapperStmt": typeof WrapperStmt, + "ReturnStmt": typeof ReturnStmt, + "Switch": typeof Switch, + "SwitchCase": typeof SwitchCase, "Case": typeof Case, - "Cast": typeof Cast, - "CilkSpawn": typeof CilkSpawn, - "CilkSync": typeof CilkSync, - "Class": typeof Class, - "Continue": typeof Continue, - "CudaKernelCall": typeof CudaKernelCall, - "DeclStmt": typeof DeclStmt, - "Declarator": typeof Declarator, "Default": typeof Default, - "DeleteExpr": typeof DeleteExpr, - "ElaboratedType": typeof ElaboratedType, - "EmptyStmt": typeof EmptyStmt, - "EnumDecl": typeof EnumDecl, - "EnumeratorDecl": typeof EnumeratorDecl, + "DeclStmt": typeof DeclStmt, "ExprStmt": typeof ExprStmt, - "Field": typeof Field, - "FloatLiteral": typeof FloatLiteral, - "FunctionJp": typeof FunctionJp, - "FunctionType": typeof FunctionType, "GotoStmt": typeof GotoStmt, - "If": typeof If, - "IncompleteArrayType": typeof IncompleteArrayType, - "IntLiteral": typeof IntLiteral, - "LabelDecl": typeof LabelDecl, "LabelStmt": typeof LabelStmt, - "Loop": typeof Loop, - "Marker": typeof Marker, + "EmptyStmt": typeof EmptyStmt, + "Continue": typeof Continue, + "Break": typeof Break, + "AsmStmt": typeof AsmStmt, + "Expression": typeof Expression, + "Call": typeof Call, "MemberCall": typeof MemberCall, - "Method": typeof Method, - "Omp": typeof Omp, - "ParenType": typeof ParenType, + "CudaKernelCall": typeof CudaKernelCall, + "Op": typeof Op, + "BinaryOp": typeof BinaryOp, + "UnaryOp": typeof UnaryOp, + "TernaryOp": typeof TernaryOp, + "NewExpr": typeof NewExpr, + "DeleteExpr": typeof DeleteExpr, + "Varref": typeof Varref, + "Cast": typeof Cast, + "ParenExpr": typeof ParenExpr, + "ArrayAccess": typeof ArrayAccess, + "MemberAccess": typeof MemberAccess, + "UnaryExprOrType": typeof UnaryExprOrType, + "This": typeof This, + "Literal": typeof Literal, + "IntLiteral": typeof IntLiteral, + "FloatLiteral": typeof FloatLiteral, + "BoolLiteral": typeof BoolLiteral, + "InitList": typeof InitList, + "ImplicitValue": typeof ImplicitValue, + "Comment": typeof Comment, + "CilkFor": typeof CilkFor, + "CilkSync": typeof CilkSync, + "CilkSpawn": typeof CilkSpawn, + "Attribute": typeof Attribute, + "Type": typeof Type, "PointerType": typeof PointerType, - "QualType": typeof QualType, - "ReturnStmt": typeof ReturnStmt, - "Scope": typeof Scope, - "TagType": typeof TagType, - "TemplateSpecializationType": typeof TemplateSpecializationType, - "TypedefDecl": typeof TypedefDecl, - "Vardecl": typeof Vardecl, + "ArrayType": typeof ArrayType, + "AdjustedType": typeof AdjustedType, "VariableArrayType": typeof VariableArrayType, - "Body": typeof Body, - "CilkFor": typeof CilkFor, + "IncompleteArrayType": typeof IncompleteArrayType, + "TagType": typeof TagType, "EnumType": typeof EnumType, - "Param": typeof Param, + "TemplateSpecializationType": typeof TemplateSpecializationType, + "FunctionType": typeof FunctionType, + "QualType": typeof QualType, + "BuiltinType": typeof BuiltinType, + "ParenType": typeof ParenType, + "UndefinedType": typeof UndefinedType, + "ElaboratedType": typeof ElaboratedType, + "TypedefType": typeof TypedefType, }; type DefaultAttributeMap = { + Program: "name", FileJp: "name", - Include: "name", NamedDecl: "name", - Pragma: "name", - Program: "name", + Declarator: "name", + Include: "name", RecordJp: "name", + Field: "name", Struct: "name", - Tag: "id", - TypedefNameDecl: "name", - Varref: "name", - AccessSpecifier: "kind", - Call: "name", - CilkSpawn: "name", Class: "name", - CudaKernelCall: "name", - Declarator: "name", + Vardecl: "name", + TypedefNameDecl: "name", + TypedefDecl: "name", EnumDecl: "name", EnumeratorDecl: "name", - Field: "name", - FunctionJp: "name", LabelDecl: "name", - Loop: "kind", - Marker: "id", - MemberCall: "name", + AccessSpecifier: "kind", + Param: "name", + FunctionJp: "name", Method: "name", + Pragma: "name", + Marker: "id", + Tag: "id", Omp: "kind", - TypedefDecl: "name", - Vardecl: "name", + Loop: "kind", + Call: "name", + MemberCall: "name", + CudaKernelCall: "name", + Varref: "name", CilkFor: "kind", - Param: "name", + CilkSpawn: "name", } export class Joinpoint extends LaraJoinPoint { @@ -145,145 +145,134 @@ export class Joinpoint extends LaraJoinPoint { /** * String with a dump of the AST representation starting from this node. This representation corresponds to the internal Java representation of the ClavaAst, where the node names correspond to Java classes. To get an equivalent representation with join point names, use the attribute 'dump' */ - get ast(): string { return wrapJoinPoint(this._javaObject.getAst()) } + get ast(): string { return wrapJoinPoint(this._javaObject.ast()) } /** * Returns an array with the children of the node, considering null nodes */ - get astChildren(): Joinpoint[] { return wrapJoinPoint(this._javaObject.getAstChildren()) } + get astChildren(): Joinpoint[] { return wrapJoinPoint(this._javaObject.astChildren()) } /** - * String that uniquely identifies this node + * The AST ID of the current node */ - get astId(): string { return wrapJoinPoint(this._javaObject.getAstId()) } + get astId(): string { return wrapJoinPoint(this._javaObject.astId()) } /** * The name of the Java class of this node, which is similar to the equivalent node in Clang AST */ - get astName(): string { return wrapJoinPoint(this._javaObject.getAstName()) } + get astName(): string { return wrapJoinPoint(this._javaObject.astName()) } /** * Returns the number of children of the node, considering null nodes */ - get astNumChildren(): number { return wrapJoinPoint(this._javaObject.getAstNumChildren()) } + get astNumChildren(): number { return wrapJoinPoint(this._javaObject.astNumChildren()) } /** * The bit width of the type returned by this join point, in relation to the definitions of its Translation Unit, or undefined if there is no type or bitwidth defined, or if the join point is not in a TranslationUnit */ - get bitWidth(): number { return wrapJoinPoint(this._javaObject.getBitWidth()) } + get bitWidth(): number { return wrapJoinPoint(this._javaObject.bitWidth()) } /** * String list of the names of the join points that form a path from the root to this node */ - get chain(): string[] { return wrapJoinPoint(this._javaObject.getChain()) } - /** - * Returns an array with the children of the node, ignoring null nodes - */ - get children(): Joinpoint[] { return wrapJoinPoint(this._javaObject.getChildren()) } + get chain(): string[] { return wrapJoinPoint(this._javaObject.chain()) } /** - * String with the code represented by this node + * The children of this join point, ignoring null nodes */ - get code(): string { return wrapJoinPoint(this._javaObject.getCode()) } + get children(): Joinpoint[] { return wrapJoinPoint(this._javaObject.children()) } /** - * The starting column of the current node in the original code + * Returns the current region of this join point */ - get column(): number { return wrapJoinPoint(this._javaObject.getColumn()) } - /** - * Returns the node that declares the scope of this node - */ - get currentRegion(): Joinpoint { return wrapJoinPoint(this._javaObject.getCurrentRegion()) } + get currentRegion(): Joinpoint { return wrapJoinPoint(this._javaObject.currentRegion()) } /** * JS object associated with this node, containing parsed data of #pragma clava data when the node can be a target of pragmas. This is a special object, managed internally, and cannot be reassigned, to change its contents requires using key-value pairs. If the node can be the target of a pragma, the information stored in this object is persisted between rebuilds. */ - get data(): any { const data = (this._javaObject.getData() as string | undefined); return data ? JSON.parse(data) : data; } + get data(): any { const data = (this._javaObject.data() as string | undefined); return data ? JSON.parse(data) : data; } /** * JS object associated with this node, containing parsed data of #pragma clava data when the node can be a target of pragmas. This is a special object, managed internally, and cannot be reassigned, to change its contents requires using key-value pairs. If the node can be the target of a pragma, the information stored in this object is persisted between rebuilds. */ set data(value: object) { this._javaObject.setData(JSON.stringify(value)); } /** - * The depth of this join point in the AST. If it is the root join point returns 0, if it is a child of the root node returns 1, etc. + * Returns the depth of this node in the AST. Root=0 */ - get depth(): number { return wrapJoinPoint(this._javaObject.getDepth()) } + get depth(): number { return wrapJoinPoint(this._javaObject.depth()) } /** - * Retrieves all descendants of the join point + * All descendants of this join point */ - get descendants(): Joinpoint[] { return wrapJoinPoint(this._javaObject.getDescendants()) } + get descendants(): Joinpoint[] { return wrapJoinPoint(this._javaObject.descendants()) } /** * The ending column of the current node in the original code */ - get endColumn(): number { return wrapJoinPoint(this._javaObject.getEndColumn()) } + get endColumn(): number { return wrapJoinPoint(this._javaObject.endColumn()) } /** * The ending line of the current node in the original code */ - get endLine(): number { return wrapJoinPoint(this._javaObject.getEndLine()) } + get endLine(): number { return wrapJoinPoint(this._javaObject.endLine()) } /** - * The name of the file where the code of this node is located, if available + * The filename of the current node */ - get filename(): string { return wrapJoinPoint(this._javaObject.getFilename()) } + get filename(): string { return wrapJoinPoint(this._javaObject.filename()) } /** - * The complete path to the file where the code of this node comes from + * The file path of the current node */ - get filepath(): string { return wrapJoinPoint(this._javaObject.getFilepath()) } + get filepath(): string { return wrapJoinPoint(this._javaObject.filepath()) } /** * Returns the first child of this node, or undefined if it has no child */ - get firstChild(): Joinpoint { return wrapJoinPoint(this._javaObject.getFirstChild()) } + get firstChild(): Joinpoint { return wrapJoinPoint(this._javaObject.firstChild()) } /** * Returns the first child of this node, or undefined if it has no child */ set firstChild(value: Joinpoint) { this._javaObject.setFirstChild(unwrapJoinPoint(value)); } /** - * True if the node has children, false otherwise - */ - get hasChildren(): boolean { return wrapJoinPoint(this._javaObject.getHasChildren()) } - /** - * True if this node has a parent + * True if the node has any children */ - get hasParent(): boolean { return wrapJoinPoint(this._javaObject.getHasParent()) } + get hasChildren(): boolean { return wrapJoinPoint(this._javaObject.hasChildren()) } + get hasParent(): boolean { return wrapJoinPoint(this._javaObject.hasParent()) } /** * True, if the join point has a type */ - get hasType(): boolean { return wrapJoinPoint(this._javaObject.getHasType()) } + get hasType(): boolean { return wrapJoinPoint(this._javaObject.hasType()) } /** * Returns comments that are not explicitly in the AST, but embedded in other nodes */ - get inlineComments(): Comment[] { return wrapJoinPoint(this._javaObject.getInlineComments()) } + get inlineComments(): Comment[] { return wrapJoinPoint(this._javaObject.inlineComments()) } /** * Returns comments that are not explicitly in the AST, but embedded in other nodes */ set inlineComments(value: string[] | string) { this._javaObject.setInlineComments(unwrapJoinPoint(value)); } /** - * True if this is a Cilk node (i.e., cilk_spawn, cilk_sync or cilk_for) + * True if the node is a Cilk node */ - get isCilk(): boolean { return wrapJoinPoint(this._javaObject.getIsCilk()) } + get isCilk(): boolean { return wrapJoinPoint(this._javaObject.isCilk()) } /** - * True, if the join point is part of a system header file + * True, if the join point is inside a header (e.g., function declaration) */ - get isInSystemHeader(): boolean { return wrapJoinPoint(this._javaObject.getIsInSystemHeader()) } + get isInsideHeader(): boolean { return wrapJoinPoint(this._javaObject.isInsideHeader()) } /** - * True, if the join point is inside a header (e.g., if condition, for, while) + * True, if the join point is inside a loop header (e.g., for, while) */ - get isInsideHeader(): boolean { return wrapJoinPoint(this._javaObject.getIsInsideHeader()) } + get isInsideLoopHeader(): boolean { return wrapJoinPoint(this._javaObject.isInsideLoopHeader()) } /** - * True, if the join point is inside a loop header (e.g., for, while) + * True, if the join point is inside a system header (e.g., #include ) */ - get isInsideLoopHeader(): boolean { return wrapJoinPoint(this._javaObject.getIsInsideLoopHeader()) } + get isInSystemHeader(): boolean { return wrapJoinPoint(this._javaObject.isInSystemHeader()) } /** * True if any descendant or the node itself was defined as a macro */ - get isMacro(): boolean { return wrapJoinPoint(this._javaObject.getIsMacro()) } + get isMacro(): boolean { return wrapJoinPoint(this._javaObject.isMacro()) } /** * The names of the Java fields of this node. Can be used as key of the attribute 'javaValue' * * @deprecated used attribute 'keys' instead, together with 'getValue' */ - get javaFields(): string[] { return wrapJoinPoint(this._javaObject.getJavaFields()) } + get javaFields(): string[] { return wrapJoinPoint(this._javaObject.javaFields()) } /** - * Id that is based on the position of the node in the code, and should remain stable between compilations (warning: only a few nodes - file, function, loop - currently support it) + * Returns the ID of this join point. The ID is based on the position of the node in the code, and should remain stable between compilations (warning: only a few nodes - file, function, loop - currently support it) */ - get jpId(): string { return wrapJoinPoint(this._javaObject.getJpId()) } + get jpId(): string { return wrapJoinPoint(this._javaObject.jpId()) } /** * A list of the properties currently supported by this node. Can be used as parameter of the attribute 'getValue' */ - get keys(): string[] { return wrapJoinPoint(this._javaObject.getKeys()) } + get keys(): string[] { return wrapJoinPoint(this._javaObject.keys()) } /** * Returns the last child of this node, or undefined if it has no child */ - get lastChild(): Joinpoint { return wrapJoinPoint(this._javaObject.getLastChild()) } + get lastChild(): Joinpoint { return wrapJoinPoint(this._javaObject.lastChild()) } /** * Returns the last child of this node, or undefined if it has no child */ @@ -291,69 +280,85 @@ export class Joinpoint extends LaraJoinPoint { /** * Returns the node that came before this node, or undefined if there is none */ - get leftJp(): Joinpoint { return wrapJoinPoint(this._javaObject.getLeftJp()) } - /** - * The starting line of the current node in the original code - */ - get line(): number { return wrapJoinPoint(this._javaObject.getLine()) } + get leftJp(): Joinpoint { return wrapJoinPoint(this._javaObject.leftJp()) } /** * A string with information about the file and code position of this node, if available */ - get location(): string { return wrapJoinPoint(this._javaObject.getLocation()) } + get location(): string { return wrapJoinPoint(this._javaObject.location()) } /** * Returns the number of children of the node, ignoring null nodes */ - get numChildren(): number { return wrapJoinPoint(this._javaObject.getNumChildren()) } + get numChildren(): number { return wrapJoinPoint(this._javaObject.numChildren()) } /** * If this join point was not originally from the parsed AST, returns the first join point of the original AST that contributed to its origin */ - get originNode(): Joinpoint { return wrapJoinPoint(this._javaObject.getOriginNode()) } + get originNode(): Joinpoint { return wrapJoinPoint(this._javaObject.originNode()) } /** * Returns the parent node in the AST, or undefined if it is the root node */ - get parent(): Joinpoint { return wrapJoinPoint(this._javaObject.getParent()) } + get parent(): Joinpoint { return wrapJoinPoint(this._javaObject.parent()) } /** - * Returns the node that declares the scope that is a parent of the scope of this node + * Returns the parent region of this join point, or undefined if there is none */ - get parentRegion(): Joinpoint { return wrapJoinPoint(this._javaObject.getParentRegion()) } + get parentRegion(): Joinpoint { return wrapJoinPoint(this._javaObject.parentRegion()) } /** - * The pragmas associated with this node + * Returns the pragmas associated with this join point */ - get pragmas(): Pragma[] { return wrapJoinPoint(this._javaObject.getPragmas()) } + get pragmas(): Pragma[] { return wrapJoinPoint(this._javaObject.pragmas()) } /** * Returns the node that comes after this node, or undefined if there is none */ - get rightJp(): Joinpoint { return wrapJoinPoint(this._javaObject.getRightJp()) } + get rightJp(): Joinpoint { return wrapJoinPoint(this._javaObject.rightJp()) } /** - * Returns the 'program' joinpoint + * Returns the 'program' joinpoint at the root of the hierarchy */ - get root(): Program { return wrapJoinPoint(this._javaObject.getRoot()) } + get root(): Program { return wrapJoinPoint(this._javaObject.root()) } /** - * The nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array + * The scope nodes of this join point */ - get scopeNodes(): Joinpoint[] { return wrapJoinPoint(this._javaObject.getScopeNodes()) } + get scopeNodes(): Joinpoint[] { return wrapJoinPoint(this._javaObject.scopeNodes()) } /** * Returns an array with the siblings that came before this node */ - get siblingsLeft(): Joinpoint[] { return wrapJoinPoint(this._javaObject.getSiblingsLeft()) } + get siblingsLeft(): Joinpoint[] { return wrapJoinPoint(this._javaObject.siblingsLeft()) } /** * Returns an array with the siblings that come after this node */ - get siblingsRight(): Joinpoint[] { return wrapJoinPoint(this._javaObject.getSiblingsRight()) } + get siblingsRight(): Joinpoint[] { return wrapJoinPoint(this._javaObject.siblingsRight()) } /** * Converts this join point to a statement, or returns undefined if it was not possible */ - get stmt(): Statement { return wrapJoinPoint(this._javaObject.getStmt()) } - get type(): Type { return wrapJoinPoint(this._javaObject.getType()) } + get stmt(): Statement { return wrapJoinPoint(this._javaObject.stmt()) } + get type(): Type { return wrapJoinPoint(this._javaObject.type()) } set type(value: Type) { this._javaObject.setType(unwrapJoinPoint(value)); } /** * True, if this node is a Java instance of the given name, which corresponds to a simple Java class name of an AST node. For an equivalent function for join point names, use 'instanceOf(joinPointName)' */ astIsInstance(className: string): boolean { return wrapJoinPoint(this._javaObject.astIsInstance(unwrapJoinPoint(className))); } /** - * True if the given node is a descendant of this node + * Compares this join point with another join point for identity (i.e., whether they represent the same AST node) + */ + compareNodes(aJoinPoint: Joinpoint): boolean { return wrapJoinPoint(this._javaObject.compareNodes(unwrapJoinPoint(aJoinPoint))); } + /** + * Checks if the joinpoint contains the given joinpoint */ contains(jp: Joinpoint): boolean { return wrapJoinPoint(this._javaObject.contains(unwrapJoinPoint(jp))); } + /** + * Performs a copy of the node and its children, but not of the nodes in its fields + */ + copy(): Joinpoint { return wrapJoinPoint(this._javaObject.copy()); } + /** + * Clears all properties from the .data object + */ + dataClear(): void { return wrapJoinPoint(this._javaObject.dataClear()); } + /** + * Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive) + */ + deepCopy(): Joinpoint { return wrapJoinPoint(this._javaObject.deepCopy()); } + /** + * Removes the node associated to this joinpoint from the AST + */ + detach(): Joinpoint { return wrapJoinPoint(this._javaObject.detach()); } /** * Looks for an ancestor joinpoint name, walking back on the AST */ @@ -379,11 +384,11 @@ export class Joinpoint extends LaraJoinPoint { */ getDescendants(type: string): Joinpoint[] { return wrapJoinPoint(this._javaObject.getDescendants(unwrapJoinPoint(type))); } /** - * Retrieves the descendants of the given type, including the node itself + * Retrieves the descendants of the given type, including the current joinpoint */ getDescendantsAndSelf(type: string): Joinpoint[] { return wrapJoinPoint(this._javaObject.getDescendantsAndSelf(unwrapJoinPoint(type))); } /** - * Looks in the descendants for the first node of the given type + * Retrieves the first node of the given type in the descendants */ getFirstJp(type: string): Joinpoint { return wrapJoinPoint(this._javaObject.getFirstJp(unwrapJoinPoint(type))); } /** @@ -391,7 +396,7 @@ export class Joinpoint extends LaraJoinPoint { */ getJavaFieldType(fieldName: string): string { return wrapJoinPoint(this._javaObject.getJavaFieldType(unwrapJoinPoint(fieldName))); } /** - * Java Class instance with the type of the given key + * Returns the type of the property with the given name */ getKeyType(key: string): object { return wrapJoinPoint(this._javaObject.getKeyType(unwrapJoinPoint(key))); } /** @@ -399,57 +404,44 @@ export class Joinpoint extends LaraJoinPoint { */ getUserField(fieldName: string): object { return wrapJoinPoint(this._javaObject.getUserField(unwrapJoinPoint(fieldName))); } /** - * The value associated with the given property key + * Returns the value of the property with the given name */ getValue(key: string): object { return wrapJoinPoint(this._javaObject.getValue(unwrapJoinPoint(key))); } /** * True, if the given join point or AST node is the same (== test) as the current join point AST node */ hasNode(nodeOrJp: object): boolean { return wrapJoinPoint(this._javaObject.hasNode(unwrapJoinPoint(nodeOrJp))); } + insert(position: InsertPosition, code: string): Joinpoint[]; + insert(position: InsertPosition, joinpoint: Joinpoint): Joinpoint[]; + insert(p1: InsertPosition, p2: string | Joinpoint): Joinpoint[] { return wrapJoinPoint(this._javaObject.insert(unwrapJoinPoint(p1), unwrapJoinPoint(p2))); } /** - * List with the values of fields that are join points, recursively - */ - jpFields(recursive: boolean = false): Joinpoint[] { return wrapJoinPoint(this._javaObject.jpFields(unwrapJoinPoint(recursive))); } - /** - * Performs a copy of the node and its children, but not of the nodes in its fields - */ - copy(): Joinpoint { return wrapJoinPoint(this._javaObject.copy()); } - /** - * Clears all properties from the .data object - */ - dataClear(): void { return wrapJoinPoint(this._javaObject.dataClear()); } - /** - * Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive) - */ - deepCopy(): Joinpoint { return wrapJoinPoint(this._javaObject.deepCopy()); } - /** - * Removes the node associated to this joinpoint from the AST - */ - detach(): Joinpoint { return wrapJoinPoint(this._javaObject.detach()); } - /** - * Inserts the given join point after this join point + * Inserts the given joinpoint after this joinpoint */ insertAfter(node: Joinpoint): Joinpoint; /** - * Overload which accepts a string + * Overload that accepts a string */ - insertAfter(code: string): Joinpoint; + insertAfter(node: string): Joinpoint; /** - * Inserts the given join point after this join point + * Inserts the given joinpoint after this joinpoint */ insertAfter(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertAfter(unwrapJoinPoint(p1))); } /** - * Inserts the given join point before this join point + * Inserts the given joinpoint before this joinpoint */ insertBefore(node: Joinpoint): Joinpoint; /** - * Overload which accepts a string + * Overload that accepts a string */ insertBefore(node: string): Joinpoint; /** - * Inserts the given join point before this join point + * Inserts the given joinpoint before this joinpoint */ insertBefore(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertBefore(unwrapJoinPoint(p1))); } + /** + * List with the values of fields that are join points, recursively + */ + jpFields(recursive: boolean = false): Joinpoint[] { return wrapJoinPoint(this._javaObject.jpFields(unwrapJoinPoint(recursive))); } /** * Adds a message that will be printed to the user after weaving finishes. Identical messages are removed */ @@ -463,11 +455,11 @@ export class Joinpoint extends LaraJoinPoint { */ replaceWith(node: Joinpoint): Joinpoint; /** - * Overload which accepts a string + * Overload that accepts a string */ replaceWith(node: string): Joinpoint; /** - * Overload which accepts a list of join points + * Overload that accepts a list of joinpoints */ replaceWith(node: Joinpoint[]): Joinpoint; /** @@ -475,31 +467,35 @@ export class Joinpoint extends LaraJoinPoint { */ replaceWith(p1: Joinpoint | string | Joinpoint[]): Joinpoint { return wrapJoinPoint(this._javaObject.replaceWith(unwrapJoinPoint(p1))); } /** - * Overload which accepts a list of strings + * Overload that accepts a list of strings */ replaceWithStrings(node: string[]): Joinpoint { return wrapJoinPoint(this._javaObject.replaceWithStrings(unwrapJoinPoint(node))); } + /** + * Compares this join point with another join point for identity (i.e., whether they represent the same AST node) + */ + same(other: Joinpoint): boolean { return wrapJoinPoint(this._javaObject.same(unwrapJoinPoint(other))); } /** * Setting data directly is not supported, this action just emits a warning and does nothing */ setData(source: object): void { return wrapJoinPoint(this._javaObject.setData(JSON.stringify(source))); } /** - * Replaces the first child, or inserts the join point if no child is present. Returns the replaced child, or undefined if there was no child present. + * Replaces the first child, or inserts the join point if no child is present. Returns the replaced child, or undefined if there was no child present */ setFirstChild(node: Joinpoint): Joinpoint { return wrapJoinPoint(this._javaObject.setFirstChild(unwrapJoinPoint(node))); } /** - * Sets the commented that are embedded in a node + * Sets the comments that are embedded in a node */ setInlineComments(comments: string[]): void; /** - * Sets the commented that are embedded in a node + * Sets the comments that are embedded in a node */ setInlineComments(comments: string): void; /** - * Sets the commented that are embedded in a node + * Sets the comments that are embedded in a node */ setInlineComments(p1: string[] | string): void { return wrapJoinPoint(this._javaObject.setInlineComments(unwrapJoinPoint(p1))); } /** - * Replaces the last child, or inserts the join point if no child is present. Returns the replaced child, or undefined if there was no child present. + * Replaces the last child, or inserts the join point if no child is present. Returns the replaced child, or undefined if there was no child present */ setLastChild(node: Joinpoint): Joinpoint { return wrapJoinPoint(this._javaObject.setLastChild(unwrapJoinPoint(node))); } /** @@ -511,7 +507,7 @@ export class Joinpoint extends LaraJoinPoint { */ setUserField(fieldName: string, value: object): object; /** - * Overload which accepts a map + * Overload that accepts a map */ setUserField(fieldNameAndValue: Record): object; /** @@ -528,96 +524,126 @@ export class Joinpoint extends LaraJoinPoint { toComment(prefix: string = "", suffix: string = ""): Joinpoint { return wrapJoinPoint(this._javaObject.toComment(unwrapJoinPoint(prefix), unwrapJoinPoint(suffix))); } } -export class Attribute extends Joinpoint { + /** + * Utility joinpoint, to represent empty nodes when directly accessing the tree + */ +export class Empty extends Joinpoint { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get kind(): string { return wrapJoinPoint(this._javaObject.getKind()) } } /** - * Utility joinpoint, to represent certain problems when generating join points + * Represents the complete program and is the top-most joinpoint in the hierarchy */ -export class ClavaException extends Joinpoint { +export class Program extends Joinpoint { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; - get exception(): object { return wrapJoinPoint(this._javaObject.getException()) } - get exceptionType(): string { return wrapJoinPoint(this._javaObject.getExceptionType()) } - get message(): string { return wrapJoinPoint(this._javaObject.getMessage()) } -} - -export class Comment extends Joinpoint { + get baseFolder(): string { return wrapJoinPoint(this._javaObject.baseFolder()) } + get defaultFlags(): string[] { return wrapJoinPoint(this._javaObject.defaultFlags()) } /** - * @internal + * Paths to includes that the current program depends on */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get text(): string { return wrapJoinPoint(this._javaObject.getText()) } - set text(value: string) { this._javaObject.setText(unwrapJoinPoint(value)); } - setText(text: string): void { return wrapJoinPoint(this._javaObject.setText(unwrapJoinPoint(text))); } -} - + get extraIncludes(): string[] { return wrapJoinPoint(this._javaObject.extraIncludes()) } /** - * Represents one declaration (e.g., int foo(){return 0;}) or definition (e.g., int foo();) in the code + * Link libraries of external projects the current program depends on */ -export class Decl extends Joinpoint { + get extraLibs(): string[] { return wrapJoinPoint(this._javaObject.extraLibs()) } /** - * @internal + * Paths to folders of projects that the current program depends on */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; + get extraProjects(): string[] { return wrapJoinPoint(this._javaObject.extraProjects()) } /** - * The attributes (e.g. Pure, CUDAGlobal) associated to this decl + * Paths to sources that the current program depends on */ - get attrs(): Attribute[] { return wrapJoinPoint(this._javaObject.getAttrs()) } -} - + get extraSources(): string[] { return wrapJoinPoint(this._javaObject.extraSources()) } /** - * Utility joinpoint, to represent empty nodes when directly accessing the tree + * The files of the program */ -export class Empty extends Joinpoint { + get files(): FileJp[] { return wrapJoinPoint(this._javaObject.files()) } + get includeFolders(): string[] { return wrapJoinPoint(this._javaObject.includeFolders()) } /** - * @internal + * True if the program was compiled with a C++ standard */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; -} - -export class Expression extends Joinpoint { + get isCxx(): boolean { return wrapJoinPoint(this._javaObject.isCxx()) } /** - * @internal + * A function join point with the main function of the program, if one is available */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; + get main(): FunctionJp { return wrapJoinPoint(this._javaObject.main()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } /** - * A 'decl' join point that represents the declaration associated with this expression, or undefined if there is none + * The name of the standard (e.g., c99, c++11) */ - get decl(): Decl { return wrapJoinPoint(this._javaObject.getDecl()) } + get standard(): string { return wrapJoinPoint(this._javaObject.standard()) } /** - * Returns a cast joinpoint if this expression has an associated implicit cast, undefined otherwise + * The flag of the standard (e.g., -std=c++11) */ - get implicitCast(): Cast { return wrapJoinPoint(this._javaObject.getImplicitCast()) } + get stdFlag(): string { return wrapJoinPoint(this._javaObject.stdFlag()) } + get userFlags(): string[] { return wrapJoinPoint(this._javaObject.userFlags()) } + get weavingFolder(): string { return wrapJoinPoint(this._javaObject.weavingFolder()) } /** - * True if the expression is part of an argument of a function call + * Adds a path to an include that the current program depends on + */ + addExtraInclude(path: string): void { return wrapJoinPoint(this._javaObject.addExtraInclude(unwrapJoinPoint(path))); } + /** + * Adds a path based on a git repository to an include that the current program depends on + */ + addExtraIncludeFromGit(gitRepo: string, path?: string): void { return wrapJoinPoint(this._javaObject.addExtraIncludeFromGit(unwrapJoinPoint(gitRepo), unwrapJoinPoint(path))); } + /** + * Adds a library (e.g., -pthreads) that the current program depends on + */ + addExtraLib(lib: string): void { return wrapJoinPoint(this._javaObject.addExtraLib(unwrapJoinPoint(lib))); } + /** + * Adds a path to a source that the current program depends on + */ + addExtraSource(path: string): void { return wrapJoinPoint(this._javaObject.addExtraSource(unwrapJoinPoint(path))); } + /** + * Adds a path based on a git repository to a source that the current program depends on + */ + addExtraSourceFromGit(gitRepo: string, path?: string): void { return wrapJoinPoint(this._javaObject.addExtraSourceFromGit(unwrapJoinPoint(gitRepo), unwrapJoinPoint(path))); } + /** + * Adds a file join point to the current program + */ + addFile(file: FileJp): Joinpoint { return wrapJoinPoint(this._javaObject.addFile(unwrapJoinPoint(file))); } + /** + * Adds a file join point to the current program, from the given path, which can be either a Java File or a String + */ + addFileFromPath(filepath: object): Joinpoint { return wrapJoinPoint(this._javaObject.addFileFromPath(unwrapJoinPoint(filepath))); } + /** + * Adds a path based on a git repository to a project that the current program depends on + */ + addProjectFromGit(gitRepo: string, libs: string[], path?: string): void { return wrapJoinPoint(this._javaObject.addProjectFromGit(unwrapJoinPoint(gitRepo), unwrapJoinPoint(libs), unwrapJoinPoint(path))); } + /** + * Registers a function to be executed when the program exits */ - get isFunctionArgument(): boolean { return wrapJoinPoint(this._javaObject.getIsFunctionArgument()) } - get use(): "read" | "write" | "readwrite" { return wrapJoinPoint(this._javaObject.getUse()) } - get vardecl(): Vardecl { return wrapJoinPoint(this._javaObject.getVardecl()) } + atexit(func: FunctionJp): void { return wrapJoinPoint(this._javaObject.atexit(unwrapJoinPoint(func))); } + /** + * Discards the AST at the top of the AST stack + */ + pop(): void { return wrapJoinPoint(this._javaObject.pop()); } + /** + * Creates a copy of the current AST and pushes it to the top of the AST stack + */ + push(): void { return wrapJoinPoint(this._javaObject.push()); } + /** + * Recompiles the program currently represented by the AST, transforming literal code into AST nodes. Returns true if all files could be parsed correctly, or false otherwise + */ + rebuild(): boolean { return wrapJoinPoint(this._javaObject.rebuild()); } + /** + * Similar to rebuild, but tries to fix compilation errors. Resulting program may not represent the originally intended functionality + */ + rebuildFuzzy(): void { return wrapJoinPoint(this._javaObject.rebuildFuzzy()); } } /** - * Represents a source file (.c, .cpp., .cl, etc) + * Represents a source file (.c, .cpp, .cl, etc) */ export class FileJp extends Joinpoint { /** @@ -627,73 +653,63 @@ export class FileJp extends Joinpoint { name: "name", }; /** - * The path to the source folder that was given as the base folder of this file + * The base source path for this file */ - get baseSourcePath(): string { return wrapJoinPoint(this._javaObject.getBaseSourcePath()) } + get baseSourcePath(): string { return wrapJoinPoint(this._javaObject.baseSourcePath()) } /** - * The output of the parser if there were errors during parsing + * The error output produced during the parsing of this file, if any */ - get errorOutput(): string { return wrapJoinPoint(this._javaObject.getErrorOutput()) } + get errorOutput(): string { return wrapJoinPoint(this._javaObject.errorOutput()) } /** - * A Java file to the file that originated this translation unit + * The Java File object associated with this file */ - get file(): object { return wrapJoinPoint(this._javaObject.getFile()) } + get file(): object { return wrapJoinPoint(this._javaObject.file()) } /** - * True if this file contains a 'main' method + * True if this file has the main function as a descendant */ - get hasMain(): boolean { return wrapJoinPoint(this._javaObject.getHasMain()) } + get hasMain(): boolean { return wrapJoinPoint(this._javaObject.hasMain()) } /** - * True if there were errors during parsing + * True if there were errors during the parsing of this file */ - get hasParsingErrors(): boolean { return wrapJoinPoint(this._javaObject.getHasParsingErrors()) } + get hasParsingErrors(): boolean { return wrapJoinPoint(this._javaObject.hasParsingErrors()) } /** - * The includes of this file + * The include directives in this file */ - get includes(): Include[] { return wrapJoinPoint(this._javaObject.getIncludes()) } + get includes(): Include[] { return wrapJoinPoint(this._javaObject.includes()) } /** - * True if this file is considered a C++ file + * True if this file is a being parsed as a C++ file */ - get isCxx(): boolean { return wrapJoinPoint(this._javaObject.getIsCxx()) } + get isCxx(): boolean { return wrapJoinPoint(this._javaObject.isCxx()) } /** - * True if this file is considered a header file + * True if this file is a header file */ - get isHeader(): boolean { return wrapJoinPoint(this._javaObject.getIsHeader()) } + get isHeader(): boolean { return wrapJoinPoint(this._javaObject.isHeader()) } /** - * True if this file is an OpenCL filetype - */ - get isOpenCL(): boolean { return wrapJoinPoint(this._javaObject.getIsOpenCL()) } - /** - * The name of the file - */ - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } - /** - * The name of the file + * True if this file is an OpenCL file */ + get isOpenCL(): boolean { return wrapJoinPoint(this._javaObject.isOpenCL()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } /** - * The folder of the source file + * The folder path for this file */ - get path(): string { return wrapJoinPoint(this._javaObject.getPath()) } + get path(): string { return wrapJoinPoint(this._javaObject.path()) } /** - * The path to the file relative to the base source path + * The file path relative to the base folder of the program */ - get relativeFilepath(): string { return wrapJoinPoint(this._javaObject.getRelativeFilepath()) } + get relativeFilepath(): string { return wrapJoinPoint(this._javaObject.relativeFilepath()) } /** - * The path to the folder of the source file relative to the base source path + * The folder path relative to the base folder of the program */ - get relativeFolderpath(): string { return wrapJoinPoint(this._javaObject.getRelativeFolderpath()) } + get relativeFolderpath(): string { return wrapJoinPoint(this._javaObject.relativeFolderpath()) } /** - * The path to the folder of the source file relative to the base source path + * The folder path relative to the base folder of the program */ set relativeFolderpath(value: string) { this._javaObject.setRelativeFolderpath(unwrapJoinPoint(value)); } /** * The name of the source folder of this file, or undefined if it has none */ - get sourceFoldername(): string { return wrapJoinPoint(this._javaObject.getSourceFoldername()) } - /** - * The complete path to the file that will be generated by the weaver, given a destination folder - */ - getDestinationFilepath(destinationFolderpath?: string): string { return wrapJoinPoint(this._javaObject.getDestinationFilepath(unwrapJoinPoint(destinationFolderpath))); } + get sourceFoldername(): string { return wrapJoinPoint(this._javaObject.sourceFoldername()) } /** * Adds a C include to the current file. If the file already has the include, it does nothing */ @@ -714,6 +730,10 @@ export class FileJp extends Joinpoint { * Overload of addInclude which accepts a join point */ addIncludeJp(jp: Joinpoint): void { return wrapJoinPoint(this._javaObject.addIncludeJp(unwrapJoinPoint(jp))); } + /** + * The complete path to the file that will be generated by the weaver, given a destination folder + */ + getDestinationFilepath(destinationFolderpath?: string): string { return wrapJoinPoint(this._javaObject.getDestinationFilepath(unwrapJoinPoint(destinationFolderpath))); } /** * Adds the node in the join point to the start of the file */ @@ -742,10 +762,6 @@ export class FileJp extends Joinpoint { * Recompiles only this file, returns a join point to the new recompiled file, or throws an exception if a problem happens */ rebuild(): FileJp { return wrapJoinPoint(this._javaObject.rebuild()); } - /** - * Recompiles only this file, returns a join point to the new recompiled file, or returns a clavaException join point if a problem happens - */ - rebuildTry(): Joinpoint { return wrapJoinPoint(this._javaObject.rebuildTry()); } /** * Changes the name of the file */ @@ -760,340 +776,347 @@ export class FileJp extends Joinpoint { write(destinationFoldername: string): string { return wrapJoinPoint(this._javaObject.write(unwrapJoinPoint(destinationFoldername))); } } -export class ImplicitValue extends Expression { + /** + * Represents one declaration (e.g., int foo(){return 0;}) or definition (e.g., int foo();) + */ +export class Decl extends Joinpoint { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; + /** + * The attributes of this declaration (e.g. Pure, CUDAGlobal), if any + */ + get attrs(): Attribute[] { return wrapJoinPoint(this._javaObject.attrs()) } } /** - * Represents an include directive (e.g., #include ) + * Represents a decl with a name */ -export class Include extends Decl { +export class NamedDecl extends Decl { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: "name", }; + get isPublic(): boolean { return wrapJoinPoint(this._javaObject.isPublic()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } + set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } + get qualifiedName(): string { return wrapJoinPoint(this._javaObject.qualifiedName()) } + set qualifiedName(value: string) { this._javaObject.setQualifiedName(unwrapJoinPoint(value)); } + get qualifiedPrefix(): string { return wrapJoinPoint(this._javaObject.qualifiedPrefix()) } + set qualifiedPrefix(value: string) { this._javaObject.setQualifiedPrefix(unwrapJoinPoint(value)); } /** - * True if this is an angled include (i.e., system include) + * Sets the name of this namedDecl */ - get isAngled(): boolean { return wrapJoinPoint(this._javaObject.getIsAngled()) } + setName(name: string): void { return wrapJoinPoint(this._javaObject.setName(unwrapJoinPoint(name))); } /** - * The name of the include + * Sets the qualified name of this namedDecl (changes both the name and qualified prefix) */ - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } + setQualifiedName(name: string): void { return wrapJoinPoint(this._javaObject.setQualifiedName(unwrapJoinPoint(name))); } /** - * The path to the folder of the source file of the include, relative to the name of the include + * Sets the qualified prefix of this namedDecl */ - get relativeFolderpath(): string { return wrapJoinPoint(this._javaObject.getRelativeFolderpath()) } + setQualifiedPrefix(qualifiedPrefix: string): void { return wrapJoinPoint(this._javaObject.setQualifiedPrefix(unwrapJoinPoint(qualifiedPrefix))); } } -export class InitList extends Expression { - /** - * @internal - */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; /** - * [May be undefined] If this initializer list initializes an array with more elements than there are initializers in the list, specifies an expression to be used for value initialization of the rest of the elements + * Represents a decl that comes from a declarator (e.g., function, field, variable) */ - get arrayFiller(): Expression { return wrapJoinPoint(this._javaObject.getArrayFiller()) } -} - -export class Literal extends Expression { +export class Declarator extends NamedDecl { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; } -export class MemberAccess extends Expression { + /** + * Represents an include directive (e.g., #include ) + */ +export class Include extends Decl { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; /** - * True if this is a member access that uses arrow (i.e., foo->bar), false if uses dot (i.e., foo.bar) - */ - get arrow(): boolean { return wrapJoinPoint(this._javaObject.getArrow()) } - /** - * True if this is a member access that uses arrow (i.e., foo->bar), false if uses dot (i.e., foo.bar) + * True if the include is angled (e.g., #include ) instead of quoted (e.g., #include "myheader.h") */ - set arrow(value: boolean) { this._javaObject.setArrow(unwrapJoinPoint(value)); } + get isAngled(): boolean { return wrapJoinPoint(this._javaObject.isAngled()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } /** - * Expression of the base of this member access + * The path to the folder of the source file of the include, relative to the name of the include */ - get base(): Expression { return wrapJoinPoint(this._javaObject.getBase()) } - get memberChain(): Expression[] { return wrapJoinPoint(this._javaObject.getMemberChain()) } - get memberChainNames(): string[] { return wrapJoinPoint(this._javaObject.getMemberChainNames()) } - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } - setArrow(isArrow: boolean): void { return wrapJoinPoint(this._javaObject.setArrow(unwrapJoinPoint(isArrow))); } + get relativeFolderpath(): string { return wrapJoinPoint(this._javaObject.relativeFolderpath()) } } /** - * Represents a decl with a name + * Represents a record declaration (struct, union, or class) */ -export class NamedDecl extends Decl { +export class RecordJp extends NamedDecl { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: "name", }; - get isPublic(): boolean { return wrapJoinPoint(this._javaObject.getIsPublic()) } - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } - set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } - get qualifiedName(): string { return wrapJoinPoint(this._javaObject.getQualifiedName()) } - set qualifiedName(value: string) { this._javaObject.setQualifiedName(unwrapJoinPoint(value)); } - get qualifiedPrefix(): string { return wrapJoinPoint(this._javaObject.getQualifiedPrefix()) } - set qualifiedPrefix(value: string) { this._javaObject.setQualifiedPrefix(unwrapJoinPoint(value)); } + get fields(): Field[] { return wrapJoinPoint(this._javaObject.fields()) } + get functions(): FunctionJp[] { return wrapJoinPoint(this._javaObject.functions()) } /** - * Sets the name of this namedDecl + * True if this record declaration is an implementation (i.e., it has a body) instead of just a forward declaration */ - setName(name: string): void { return wrapJoinPoint(this._javaObject.setName(unwrapJoinPoint(name))); } + get isImplementation(): boolean { return wrapJoinPoint(this._javaObject.isImplementation()) } /** - * Sets the qualified name of this namedDecl (changes both the name and qualified prefix) + * True if this record declaration is a prototype (i.e., it has no body) instead of an implementation */ - setQualifiedName(name: string): void { return wrapJoinPoint(this._javaObject.setQualifiedName(unwrapJoinPoint(name))); } + get isPrototype(): boolean { return wrapJoinPoint(this._javaObject.isPrototype()) } + get kind(): string { return wrapJoinPoint(this._javaObject.kind()) } /** - * Sets the qualified prefix of this namedDecl + * Adds a field to a record (struct, class) */ - setQualifiedPrefix(qualifiedPrefix: string): void { return wrapJoinPoint(this._javaObject.setQualifiedPrefix(unwrapJoinPoint(qualifiedPrefix))); } + addField(field: Field): void { return wrapJoinPoint(this._javaObject.addField(unwrapJoinPoint(field))); } } -export class NewExpr extends Expression { + /** + * Represents a member of a struct/union/class + */ +export class Field extends Declarator { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; } -export class Op extends Expression { + /** + * Represents a struct declaration + */ +export class Struct extends RecordJp { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; - get isBitwise(): boolean { return wrapJoinPoint(this._javaObject.getIsBitwise()) } - /** - * The kind of the operator. If it is a binary operator, can be one of: ptr_mem_d, ptr_mem_i, mul, div, rem, add, sub, shl, shr, cmp, lt, gt, le, ge, eq, ne, and, xor, or, l_and, l_or, assign, mul_assign, div_assign, rem_assign, add_assign, sub_assign, shl_assign, shr_assign, and_assign, xor_assign, or_assign, comma. If it is a unary operator, can be one of: post_inc, post_dec, pre_inc, pre_dec, addr_of, deref, plus, minus, not, l_not, real, imag, extension, cowait. If it is a ternary operator, the value will be 'ternary' - */ - get kind(): "ptr_mem_d" | "ptr_mem_i" | "mul" | "div" | "rem" | "add" | "sub" | "shl" | "shr" | "cmp" | "lt" | "gt" | "le" | "ge" | "eq" | "ne" | "and" | "xor" | "or" | "l_and" | "l_or" | "assign" | "mul_assign" | "div_assign" | "rem_assign" | "add_assign" | "sub_assign" | "shl_assign" | "shr_assign" | "and_assign" | "xor_assign" | "or_assign" | "comma" | "post_inc" | "post_dec" | "pre_inc" | "pre_dec" | "addr_of" | "deref" | "plus" | "minus" | "not" | "l_not" | "real" | "imag" | "extension" | "cowait" | "ternary" { return wrapJoinPoint(this._javaObject.getKind()) } - get operator(): string { return wrapJoinPoint(this._javaObject.getOperator()) } } -export class ParenExpr extends Expression { + /** + * Represents a C++ class declaration + */ +export class Class extends RecordJp { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; /** - * Returns the expression inside this parenthesis expression + * The base classes of this class and its base classes */ - get subExpr(): Expression { return wrapJoinPoint(this._javaObject.getSubExpr()) } -} - + get allBases(): Class[] { return wrapJoinPoint(this._javaObject.allBases()) } /** - * Represents a pragma in the code (e.g., #pragma kernel) + * The methods of this class and its base classes */ -export class Pragma extends Joinpoint { + get allMethods(): Method[] { return wrapJoinPoint(this._javaObject.allMethods()) } /** - * @internal + * The base classes of this class */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; + get bases(): Class[] { return wrapJoinPoint(this._javaObject.bases()) } /** - * Everything that is after the name of the pragma + * Class join points can either represent declarations or definitions, returns the definition of this class, if present, or the first declaration, if only declarations are present */ - get content(): string { return wrapJoinPoint(this._javaObject.getContent()) } + get canonical(): Class { return wrapJoinPoint(this._javaObject.canonical()) } /** - * Everything that is after the name of the pragma + * The implementation (or definition) of this class present in the AST, or undefined if none is found */ - set content(value: string) { this._javaObject.setContent(unwrapJoinPoint(value)); } + get implementation(): Class { return wrapJoinPoint(this._javaObject.implementation()) } /** - * The name of the pragma. E.g. for #pragma foo bar, returns 'foo' + * True if this class contains at least one pure function */ - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } + get isAbstract(): boolean { return wrapJoinPoint(this._javaObject.isAbstract()) } /** - * The name of the pragma. E.g. for #pragma foo bar, returns 'foo' + * True if this class join point is the canonical one, which is the definition if it is present, or the first declaration if only declarations are present */ - set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } + get isCanonical(): boolean { return wrapJoinPoint(this._javaObject.isCanonical()) } /** - * The first node below the pragma that is not a comment or another pragma. Example of pragma targets are statements and declarations + * True if this class contains only pure functions */ - get target(): Joinpoint { return wrapJoinPoint(this._javaObject.getTarget()) } + get isInterface(): boolean { return wrapJoinPoint(this._javaObject.isInterface()) } /** - * All the nodes below the target node, including the target node, up until a pragma with the name given by argument 'endPragma'. If no end pragma is found, returns the same result as if not providing the argument + * The methods of this class */ - getTargetNodes(endPragma?: string): Joinpoint[] { return wrapJoinPoint(this._javaObject.getTargetNodes(unwrapJoinPoint(endPragma))); } - setContent(content: string): void { return wrapJoinPoint(this._javaObject.setContent(unwrapJoinPoint(content))); } - setName(name: string): void { return wrapJoinPoint(this._javaObject.setName(unwrapJoinPoint(name))); } + get methods(): Method[] { return wrapJoinPoint(this._javaObject.methods()) } + /** + * The prototypes (or declarations) of this class present in the AST, if any + */ + get prototypes(): Class[] { return wrapJoinPoint(this._javaObject.prototypes()) } + /** + * Adds a method to a class. If the given method has a definition, creates an equivalent declaration and adds it to the class, otherwise simply adds the declaration to the class. In both cases, the declaration is only added to the class if there is no declaration already with the same signature + */ + addMethod(method: Method): void { return wrapJoinPoint(this._javaObject.addMethod(unwrapJoinPoint(method))); } } /** - * Represents the complete program and is the top-most joinpoint in the hierarchy + * Represents a variable declaration or definition */ -export class Program extends Joinpoint { +export class Vardecl extends Declarator { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: "name", }; - get baseFolder(): string { return wrapJoinPoint(this._javaObject.getBaseFolder()) } - get defaultFlags(): string[] { return wrapJoinPoint(this._javaObject.getDefaultFlags()) } - /** - * Paths to includes that the current program depends on - */ - get extraIncludes(): string[] { return wrapJoinPoint(this._javaObject.getExtraIncludes()) } /** - * Link libraries of external projects the current program depends on + * The vardecl corresponding to the actual definition. For global variables, returns the vardecl of the file where it is actually defined (instead of the vardecl that defines an external link to the variable) */ - get extraLibs(): string[] { return wrapJoinPoint(this._javaObject.getExtraLibs()) } + get definition(): Vardecl { return wrapJoinPoint(this._javaObject.definition()) } /** - * Paths to folders of projects that the current program depends on + * True if this variable declaration has an initializer */ - get extraProjects(): string[] { return wrapJoinPoint(this._javaObject.getExtraProjects()) } + get hasInit(): boolean { return wrapJoinPoint(this._javaObject.hasInit()) } /** - * Paths to sources that the current program depends on + * The initializer of this variable declaration, if it has one */ - get extraSources(): string[] { return wrapJoinPoint(this._javaObject.getExtraSources()) } + get init(): Expression { return wrapJoinPoint(this._javaObject.init()) } /** - * The source files in this program + * The initializer of this variable declaration, if it has one */ - get files(): FileJp[] { return wrapJoinPoint(this._javaObject.getFiles()) } - get includeFolders(): string[] { return wrapJoinPoint(this._javaObject.getIncludeFolders()) } + set init(value: Expression | string) { this._javaObject.setInit(unwrapJoinPoint(value)); } /** - * True if the program was compiled with a C++ standard + * The initialization style of this vardecl, which can be no_init, cinit, callinit, listinit */ - get isCxx(): boolean { return wrapJoinPoint(this._javaObject.getIsCxx()) } + get initStyle(): string { return wrapJoinPoint(this._javaObject.initStyle()) } /** - * A function join point with the main function of the program, if one is available + * True if this variable declaration is global. This includes all global variables as well as static variables declared within a function. */ - get main(): FunctionJp { return wrapJoinPoint(this._javaObject.getMain()) } - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } + get isGlobal(): boolean { return wrapJoinPoint(this._javaObject.isGlobal()) } /** - * The name of the standard (e.g., c99, c++11) + * True if this variable declaration is a function parameter */ - get standard(): string { return wrapJoinPoint(this._javaObject.getStandard()) } + get isParam(): boolean { return wrapJoinPoint(this._javaObject.isParam()) } /** - * The flag of the standard (e.g., -std=c++11) + * The storage class of this variable declaration. Can be 'none', 'extern', 'static', '__private_extern__', 'auto' or 'register' */ - get stdFlag(): string { return wrapJoinPoint(this._javaObject.getStdFlag()) } - get userFlags(): string[] { return wrapJoinPoint(this._javaObject.getUserFlags()) } - get weavingFolder(): string { return wrapJoinPoint(this._javaObject.getWeavingFolder()) } + get storageClass(): StorageClass { return wrapJoinPoint(this._javaObject.storageClass()) } /** - * Adds a path to an include that the current program depends on + * The storage class of this variable declaration. Can be 'none', 'extern', 'static', '__private_extern__', 'auto' or 'register' */ - addExtraInclude(path: string): void { return wrapJoinPoint(this._javaObject.addExtraInclude(unwrapJoinPoint(path))); } + set storageClass(value: StorageClass) { this._javaObject.setStorageClass(unwrapJoinPoint(value)); } /** - * Adds a path based on a git repository to an include that the current program depends on + * If vardecl already has an initialization, removes it */ - addExtraIncludeFromGit(gitRepo: string, path?: string): void { return wrapJoinPoint(this._javaObject.addExtraIncludeFromGit(unwrapJoinPoint(gitRepo), unwrapJoinPoint(path))); } + removeInit(removeConst: boolean = true): void { return wrapJoinPoint(this._javaObject.removeInit(unwrapJoinPoint(removeConst))); } /** - * Adds a library (e.g., -pthreads) that the current program depends on + * Sets the given expression as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization */ - addExtraLib(lib: string): void { return wrapJoinPoint(this._javaObject.addExtraLib(unwrapJoinPoint(lib))); } + setInit(init: Expression): void; /** - * Adds a path to a source that the current program depends on + * Converts the given string to a literal expression and sets it as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization */ - addExtraSource(path: string): void { return wrapJoinPoint(this._javaObject.addExtraSource(unwrapJoinPoint(path))); } + setInit(init: string): void; /** - * Adds a path based on a git repository to a source that the current program depends on + * Sets the given expression as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization */ - addExtraSourceFromGit(gitRepo: string, path?: string): void { return wrapJoinPoint(this._javaObject.addExtraSourceFromGit(unwrapJoinPoint(gitRepo), unwrapJoinPoint(path))); } + setInit(p1: Expression | string): void { return wrapJoinPoint(this._javaObject.setInit(unwrapJoinPoint(p1))); } /** - * Adds a file join point to the current program + * Sets the storage class specifier, which can be none, extern, static, __private_extern__, auto */ - addFile(file: FileJp): Joinpoint { return wrapJoinPoint(this._javaObject.addFile(unwrapJoinPoint(file))); } + setStorageClass(storageClass: StorageClass): void { return wrapJoinPoint(this._javaObject.setStorageClass(unwrapJoinPoint(storageClass))); } /** - * Adds a file join point to the current program, from the given path, which can be either a Java File or a String + * Creates a new varref based on this vardecl */ - addFileFromPath(filepath: object): Joinpoint { return wrapJoinPoint(this._javaObject.addFileFromPath(unwrapJoinPoint(filepath))); } + varref(): Varref { return wrapJoinPoint(this._javaObject.varref()); } +} + /** - * Adds a path based on a git repository to a project that the current program depends on + * Base node for declarations which introduce a typedef-name */ - addProjectFromGit(gitRepo: string, libs: string[], path?: string): void { return wrapJoinPoint(this._javaObject.addProjectFromGit(unwrapJoinPoint(gitRepo), unwrapJoinPoint(libs), unwrapJoinPoint(path))); } +export class TypedefNameDecl extends NamedDecl { /** - * Registers a function to be executed when the program exits + * @internal */ - atexit(func: FunctionJp): void { return wrapJoinPoint(this._javaObject.atexit(unwrapJoinPoint(func))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; +} + /** - * Discards the AST at the top of the ASt stack + * Declaration of a typedef-name via the 'typedef' type specifier */ - pop(): void { return wrapJoinPoint(this._javaObject.pop()); } +export class TypedefDecl extends TypedefNameDecl { /** - * Creates a copy of the current AST and pushes it to the top of the AST stack + * @internal */ - push(): void { return wrapJoinPoint(this._javaObject.push()); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; +} + /** - * Recompiles the program currently represented by the AST, transforming literal code into AST nodes. Returns true if all files could be parsed correctly, or false otherwise + * Represents an enum declaration */ - rebuild(): boolean { return wrapJoinPoint(this._javaObject.rebuild()); } +export class EnumDecl extends NamedDecl { /** - * Similar to rebuild, but tries to fix compilation errors. Resulting program may not represent the originally intended functionality + * @internal */ - rebuildFuzzy(): void { return wrapJoinPoint(this._javaObject.rebuildFuzzy()); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; + get enumerators(): EnumeratorDecl[] { return wrapJoinPoint(this._javaObject.enumerators()) } } /** - * Common class of struct, union and class + * Represents an enumerator in an enum */ -export class RecordJp extends NamedDecl { +export class EnumeratorDecl extends NamedDecl { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: "name", }; - get fields(): Field[] { return wrapJoinPoint(this._javaObject.getFields()) } - get functions(): FunctionJp[] { return wrapJoinPoint(this._javaObject.getFunctions()) } - /** - * True if this particular join point is an implementation (i.e. has its body fully specified), false otherwise - */ - get isImplementation(): boolean { return wrapJoinPoint(this._javaObject.getIsImplementation()) } +} + /** - * True if this particular join point is a prototype (i.e. does not have its body fully specified), false otherwise + * Represents a label declaration */ - get isPrototype(): boolean { return wrapJoinPoint(this._javaObject.getIsPrototype()) } - get kind(): string { return wrapJoinPoint(this._javaObject.getKind()) } +export class LabelDecl extends NamedDecl { /** - * Adds a field to a record (struct, class). + * @internal */ - addField(field: Field): void { return wrapJoinPoint(this._javaObject.addField(unwrapJoinPoint(field))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; + get labelStmt(): LabelStmt { return wrapJoinPoint(this._javaObject.labelStmt()) } } -export class Statement extends Joinpoint { + /** + * Represents an access specifier (public:, private:, or protected:) in a class declaration + */ +export class AccessSpecifier extends Decl { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "kind", }; - get isFirst(): boolean { return wrapJoinPoint(this._javaObject.getIsFirst()) } - get isLast(): boolean { return wrapJoinPoint(this._javaObject.getIsLast()) } + /** + * The type of specifier. Can return 'public', 'protected', 'private' or 'none' + */ + get kind(): string { return wrapJoinPoint(this._javaObject.kind()) } } /** - * Represets a struct declaration + * Represents a function parameter */ -export class Struct extends RecordJp { +export class Param extends Vardecl { /** * @internal */ @@ -1102,643 +1125,490 @@ export class Struct extends RecordJp { }; } -export class Switch extends Statement { + /** + * Represents a function declaration or definition + */ +export class FunctionJp extends Declarator { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; + get body(): Scope { return wrapJoinPoint(this._javaObject.body()) } + set body(value: Scope) { this._javaObject.setBody(unwrapJoinPoint(value)); } + get calls(): Call[] { return wrapJoinPoint(this._javaObject.calls()) } /** - * The case statements inside this switch + * Function join points can either represent declarations or definitions, returns the definition of this function, if present, or the first declaration, if only declarations are present */ - get cases(): Case[] { return wrapJoinPoint(this._javaObject.getCases()) } + get canonical(): FunctionJp { return wrapJoinPoint(this._javaObject.canonical()) } /** - * The condition of this switch statement + * Returns the first prototype of this function that could be found, or undefined if there is none */ - get condition(): Expression { return wrapJoinPoint(this._javaObject.getCondition()) } + get declarationJp(): FunctionJp { return wrapJoinPoint(this._javaObject.declarationJp()) } /** - * The default case statement of this switch statement or undefined if it does not have a default case + * Returns the prototypes of this function that are present in the code. If there are none, returns an empty array */ - get getDefaultCase(): Case { return wrapJoinPoint(this._javaObject.getGetDefaultCase()) } + get declarationJps(): FunctionJp[] { return wrapJoinPoint(this._javaObject.declarationJps()) } /** - * True if there is a default case in this switch statement, false otherwise + * Returns the implementation of this function if there is one, or undefined otherwise */ - get hasDefaultCase(): boolean { return wrapJoinPoint(this._javaObject.getHasDefaultCase()) } -} - -export class SwitchCase extends Statement { + get definitionJp(): FunctionJp { return wrapJoinPoint(this._javaObject.definitionJp()) } /** - * @internal + * The function type of this function, which includes the return type and the parameter types */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; -} - + get functionType(): FunctionType { return wrapJoinPoint(this._javaObject.functionType()) } /** - * A pragma that references a point in the code and sticks to it + * The function type of this function, which includes the return type and the parameter types */ -export class Tag extends Pragma { + set functionType(value: FunctionType) { this._javaObject.setFunctionType(unwrapJoinPoint(value)); } /** - * @internal + * True if this particular function join point has a body, false otherwise + * + * @deprecated Use .isImplementation instead */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "id", - }; + get hasDefinition(): boolean { return wrapJoinPoint(this._javaObject.hasDefinition()) } + get id(): string { return wrapJoinPoint(this._javaObject.id()) } /** - * The ID of the pragma + * True, if this is the function returned by the 'canonical' attribute */ - get id(): string { return wrapJoinPoint(this._javaObject.getId()) } -} - -export class TernaryOp extends Op { + get isCanonical(): boolean { return wrapJoinPoint(this._javaObject.isCanonical()) } + get isCudaKernel(): boolean { return wrapJoinPoint(this._javaObject.isCudaKernel()) } + get isDelete(): boolean { return wrapJoinPoint(this._javaObject.isDelete()) } /** - * @internal + * True if this function join point is an implementation, false otherwise */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get cond(): Expression { return wrapJoinPoint(this._javaObject.getCond()) } - get falseExpr(): Expression { return wrapJoinPoint(this._javaObject.getFalseExpr()) } - get trueExpr(): Expression { return wrapJoinPoint(this._javaObject.getTrueExpr()) } -} - -export class This extends Expression { + get isImplementation(): boolean { return wrapJoinPoint(this._javaObject.isImplementation()) } + get isInline(): boolean { return wrapJoinPoint(this._javaObject.isInline()) } + get isModulePrivate(): boolean { return wrapJoinPoint(this._javaObject.isModulePrivate()) } /** - * @internal + * True if this function join point is a prototype, false otherwise */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; -} - -export class Type extends Joinpoint { + get isPrototype(): boolean { return wrapJoinPoint(this._javaObject.isPrototype()) } + get isPure(): boolean { return wrapJoinPoint(this._javaObject.isPure()) } + get isVirtual(): boolean { return wrapJoinPoint(this._javaObject.isVirtual()) } + get paramNames(): string[] { return wrapJoinPoint(this._javaObject.paramNames()) } + get params(): Param[] { return wrapJoinPoint(this._javaObject.params()) } + set params(value: Param[]) { this._javaObject.setParams(unwrapJoinPoint(value)); } + get returnType(): Type { return wrapJoinPoint(this._javaObject.returnType()) } + set returnType(value: Type) { this._javaObject.setReturnType(unwrapJoinPoint(value)); } /** - * @internal + * The signature of this function (e.g., name of the function, plus the parameters types) */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get arrayDims(): number[] { return wrapJoinPoint(this._javaObject.getArrayDims()) } - get arraySize(): number { return wrapJoinPoint(this._javaObject.getArraySize()) } - get constant(): boolean { return wrapJoinPoint(this._javaObject.getConstant()) } + get signature(): string { return wrapJoinPoint(this._javaObject.signature()) } + get storageClass(): StorageClass { return wrapJoinPoint(this._javaObject.storageClass()) } + set storageClass(value: StorageClass) { this._javaObject.setStorageClass(unwrapJoinPoint(value)); } /** - * Single-step desugar. Returns the type itself if it does not have sugar + * Adds a new parameter to the function */ - get desugar(): Type { return wrapJoinPoint(this._javaObject.getDesugar()) } + addParam(param: Param): void; /** - * Single-step desugar. Returns the type itself if it does not have sugar + * Adds a new parameter to the function */ - set desugar(value: Type) { this._javaObject.setDesugar(unwrapJoinPoint(value)); } + addParam(name: string, type?: Type): void; /** - * Completely desugars the type + * Adds a new parameter to the function */ - get desugarAll(): Type { return wrapJoinPoint(this._javaObject.getDesugarAll()) } + addParam(p1: Param | string, p2?: Type): void { return wrapJoinPoint(this._javaObject.addParam(unwrapJoinPoint(p1), unwrapJoinPoint(p2))); } /** - * A tree representation of the fields of this type + * Clones this function assigning it a new name, inserts the cloned function after the original function. If the name is the same and the original method, automatically removes the cloned method from the class */ - get fieldTree(): string { return wrapJoinPoint(this._javaObject.getFieldTree()) } - get hasSugar(): boolean { return wrapJoinPoint(this._javaObject.getHasSugar()) } - get hasTemplateArgs(): boolean { return wrapJoinPoint(this._javaObject.getHasTemplateArgs()) } - get isArray(): boolean { return wrapJoinPoint(this._javaObject.getIsArray()) } + clone(newName: string, insert: boolean = true): FunctionJp { return wrapJoinPoint(this._javaObject.clone(unwrapJoinPoint(newName), unwrapJoinPoint(insert))); } /** - * True if this is a type declared with the 'auto' keyword + * Generates a clone of the provided function on a new file with the provided name (or with a weaver-generated name if one is not provided) */ - get isAuto(): boolean { return wrapJoinPoint(this._javaObject.getIsAuto()) } - get isBuiltin(): boolean { return wrapJoinPoint(this._javaObject.getIsBuiltin()) } - get isPointer(): boolean { return wrapJoinPoint(this._javaObject.getIsPointer()) } - get isTopLevel(): boolean { return wrapJoinPoint(this._javaObject.getIsTopLevel()) } - get kind(): string { return wrapJoinPoint(this._javaObject.getKind()) } + cloneOnFile(newName: string, fileName?: string): FunctionJp; /** - * Ignores certain types (e.g., DecayedType) + * Generates a clone of the provided function on a new file (with the provided join point) */ - get normalize(): Type { return wrapJoinPoint(this._javaObject.getNormalize()) } - get templateArgsStrings(): string[] { return wrapJoinPoint(this._javaObject.getTemplateArgsStrings()) } - get templateArgsTypes(): Type[] { return wrapJoinPoint(this._javaObject.getTemplateArgsTypes()) } - set templateArgsTypes(value: Type[]) { this._javaObject.setTemplateArgsTypes(unwrapJoinPoint(value)); } + cloneOnFile(newName: string, file: FileJp): FunctionJp; /** - * Maps names of join point fields that represent type join points, to their respective values + * Generates a clone of the provided function on a new file with the provided name (or with a weaver-generated name if one is not provided) */ - get typeFields(): Record { return wrapJoinPoint(this._javaObject.getTypeFields()) } + cloneOnFile(p1: string, p2?: string | FileJp): FunctionJp { return wrapJoinPoint(this._javaObject.cloneOnFile(unwrapJoinPoint(p1), unwrapJoinPoint(p2))); } + getDeclaration(withReturnType: boolean): string { return wrapJoinPoint(this._javaObject.getDeclaration(unwrapJoinPoint(withReturnType))); } /** - * If the type encapsulates another type, returns the encapsulated type + * Inserts the joinpoint before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node */ - get unwrap(): Type { return wrapJoinPoint(this._javaObject.getUnwrap()) } + insertReturn(code: Joinpoint): Joinpoint; /** - * Returns a new node based on this type with the qualifier const + * Inserts code as a literal statement before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node */ - asConst(): Type { return wrapJoinPoint(this._javaObject.asConst()); } + insertReturn(code: string): Joinpoint; /** - * Sets the desugared type of this type + * Inserts the joinpoint before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node */ - setDesugar(desugaredType: Type): void { return wrapJoinPoint(this._javaObject.setDesugar(unwrapJoinPoint(desugaredType))); } + insertReturn(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertReturn(unwrapJoinPoint(p1))); } /** - * Sets a single template argument type of a template type + * Creates a new call to this function */ - setTemplateArgType(index: number, templateArgType: Type): void { return wrapJoinPoint(this._javaObject.setTemplateArgType(unwrapJoinPoint(index), unwrapJoinPoint(templateArgType))); } + newCall(args: Joinpoint[]): Call { return wrapJoinPoint(this._javaObject.newCall(unwrapJoinPoint(args))); } /** - * Sets the template argument types of a template type + * Sets the body of the function */ - setTemplateArgsTypes(templateArgTypes: Type[]): void { return wrapJoinPoint(this._javaObject.setTemplateArgsTypes(unwrapJoinPoint(templateArgTypes))); } + setBody(body: Scope): void { return wrapJoinPoint(this._javaObject.setBody(unwrapJoinPoint(body))); } /** - * Changes a single occurence of a type field that has the current value with new value. Returns true if there was a change + * Sets the type of the function */ - setTypeFieldByValueRecursive(currentValue: object, newValue: object): boolean { return wrapJoinPoint(this._javaObject.setTypeFieldByValueRecursive(unwrapJoinPoint(currentValue), unwrapJoinPoint(newValue))); } + setFunctionType(functionType: FunctionType): void { return wrapJoinPoint(this._javaObject.setFunctionType(unwrapJoinPoint(functionType))); } /** - * Replaces an underlying type of this instance with new type, if it matches the old type. Returns true if there were changes + * Sets the parameter of the function at the given position */ - setUnderlyingType(oldValue: Type, newValue: Type): Type { return wrapJoinPoint(this._javaObject.setUnderlyingType(unwrapJoinPoint(oldValue), unwrapJoinPoint(newValue))); } -} - + setParam(index: number, param: Param): void; /** - * Base node for declarations which introduce a typedef-name + * Sets the parameter of the function at the given position */ -export class TypedefNameDecl extends NamedDecl { + setParam(index: number, name: string, type?: Type): void; /** - * @internal + * Sets the parameter of the function at the given position */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; -} - + setParam(p1: number, p2: Param | string, p3?: Type): void { return wrapJoinPoint(this._javaObject.setParam(unwrapJoinPoint(p1), unwrapJoinPoint(p2), unwrapJoinPoint(p3))); } /** - * Represents the type of a typedef. + * Sets the parameters of the function */ -export class TypedefType extends Type { + setParams(params: Param[]): void { return wrapJoinPoint(this._javaObject.setParams(unwrapJoinPoint(params))); } /** - * @internal + * Overload that accepts strings that represent type-varname pairs (e.g., int param1) */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; + setParamsFromStrings(params: string[]): void { return wrapJoinPoint(this._javaObject.setParamsFromStrings(unwrapJoinPoint(params))); } /** - * The typedef declaration associated with this typedef type + * Sets the type of a parameter of the function */ - get decl(): TypedefNameDecl { return wrapJoinPoint(this._javaObject.getDecl()) } + setParamType(index: number, newType: Type): void { return wrapJoinPoint(this._javaObject.setParamType(unwrapJoinPoint(index), unwrapJoinPoint(newType))); } /** - * The type that is being typedef'd + * Sets the return type of the function */ - get underlyingType(): Type { return wrapJoinPoint(this._javaObject.getUnderlyingType()) } -} - -export class UnaryExprOrType extends Expression { + setReturnType(returnType: Type): void { return wrapJoinPoint(this._javaObject.setReturnType(unwrapJoinPoint(returnType))); } /** - * @internal + * Sets the storage class of this specific function decl. AUTO and REGISTER are not allowed for functions, and EXTERN is not allowed in function implementations, or function declarations that are in the same file as the implementation. Returns true if the storage class changed, false otherwise */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get argExpr(): Expression { return wrapJoinPoint(this._javaObject.getArgExpr()) } - get argType(): Type { return wrapJoinPoint(this._javaObject.getArgType()) } - set argType(value: Type) { this._javaObject.setArgType(unwrapJoinPoint(value)); } - get hasArgExpr(): boolean { return wrapJoinPoint(this._javaObject.getHasArgExpr()) } - get hasTypeExpr(): boolean { return wrapJoinPoint(this._javaObject.getHasTypeExpr()) } - get kind(): string { return wrapJoinPoint(this._javaObject.getKind()) } - setArgType(argType: Type): void { return wrapJoinPoint(this._javaObject.setArgType(unwrapJoinPoint(argType))); } + setStorageClass(storageClass: StorageClass): boolean { return wrapJoinPoint(this._javaObject.setStorageClass(unwrapJoinPoint(storageClass))); } } -export class UnaryOp extends Op { /** - * @internal + * Represents a method in a class declaration */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get isPointerDeref(): boolean { return wrapJoinPoint(this._javaObject.getIsPointerDeref()) } - get operand(): Expression { return wrapJoinPoint(this._javaObject.getOperand()) } -} - -export class UndefinedType extends Type { +export class Method extends FunctionJp { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "name", }; + get record(): Class { return wrapJoinPoint(this._javaObject.record()) } + /** + * Removes the class of the method + */ + removeRecord(): void { return wrapJoinPoint(this._javaObject.removeRecord()); } } /** - * A reference to a variable + * Represents a pragma in the code (e.g., #pragma kernel) */ -export class Varref extends Expression { +export class Pragma extends Joinpoint { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: "name", }; - get declaration(): Declarator { return wrapJoinPoint(this._javaObject.getDeclaration()) } /** - * True if this variable reference has a MS-style property, false otherwise + * Everything that is after the name of the pragma */ - get hasProperty(): boolean { return wrapJoinPoint(this._javaObject.getHasProperty()) } + get content(): string { return wrapJoinPoint(this._javaObject.content()) } /** - * True if this varref represents a function call + * Everything that is after the name of the pragma + */ + set content(value: string) { this._javaObject.setContent(unwrapJoinPoint(value)); } + /** + * The name of the pragma. E.g. for #pragma foo bar, returns 'foo' + */ + get name(): string { return wrapJoinPoint(this._javaObject.name()) } + /** + * The name of the pragma. E.g. for #pragma foo bar, returns 'foo' */ - get isFunctionCall(): boolean { return wrapJoinPoint(this._javaObject.getIsFunctionCall()) } - get kind(): string { return wrapJoinPoint(this._javaObject.getKind()) } - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } /** - * If this variable reference has a MS-style property, returns the property name. Returns undefined otherwise + * The first node below the pragma that is not a comment or another pragma. Example of pragma targets are statements and declarations */ - get property(): string { return wrapJoinPoint(this._javaObject.getProperty()) } + get target(): Joinpoint { return wrapJoinPoint(this._javaObject.target()) } /** - * Expression from where the attribute 'use' is calculated. In certain cases (e.g., array access, pointer dereference) the 'use' attribute is not calculated on the node itself, but on an ancestor of the node. This attribute returns that node + * All the nodes below the target node, including the target node, up until a pragma with the name given by argument 'endPragma'. If no end pragma is found, returns the same result as if not providing the argument */ - get useExpr(): Expression { return wrapJoinPoint(this._javaObject.getUseExpr()) } + getTargetNodes(endPragma?: string): Joinpoint[] { return wrapJoinPoint(this._javaObject.getTargetNodes(unwrapJoinPoint(endPragma))); } + setContent(content: string): void { return wrapJoinPoint(this._javaObject.setContent(unwrapJoinPoint(content))); } setName(name: string): void { return wrapJoinPoint(this._javaObject.setName(unwrapJoinPoint(name))); } } -export class WrapperStmt extends Statement { /** - * @internal + * Represents a marker pragma, which is used to mark a specific node in the code (e.g., #pragma myMarker) and can be used to store custom data */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get content(): Joinpoint { return wrapJoinPoint(this._javaObject.getContent()) } - get kind(): "comment" | "pragma" { return wrapJoinPoint(this._javaObject.getKind()) } -} - -export class AccessSpecifier extends Decl { +export class Marker extends Pragma { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "kind", + name: "id", }; /** - * The type of specifier. Can return 'public', 'protected', 'private' or 'none' + * The scope that is targeted by the marker */ - get kind(): string { return wrapJoinPoint(this._javaObject.getKind()) } + get contents(): Scope { return wrapJoinPoint(this._javaObject.contents()) } + get id(): string { return wrapJoinPoint(this._javaObject.id()) } } -export class AdjustedType extends Type { + /** + * Represents a tag pragma, which is used to reference a specific node in the code + */ +export class Tag extends Pragma { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "id", }; - /** - * The type that is being adjusted - */ - get originalType(): Type { return wrapJoinPoint(this._javaObject.getOriginalType()) } + get id(): string { return wrapJoinPoint(this._javaObject.id()) } } -export class ArrayAccess extends Expression { + /** + * Represents an OpenMP pragma (e.g., #pragma omp parallel) + */ +export class Omp extends Pragma { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, + name: "kind", }; /** - * Expression representing the variable of the array access (can be a varref, memberAccess...) + * The names of the kinds of all clauses in the pragma, or empty array if no clause is defined */ - get arrayVar(): Expression { return wrapJoinPoint(this._javaObject.getArrayVar()) } + get clauseKinds(): string[] { return wrapJoinPoint(this._javaObject.clauseKinds()) } /** - * If the array access is done over a variable, returns the name of the variable. Equivalent to $arrayAccess.arrayVar.name - */ - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } - /** - * The number of subscripts of this array access - */ - get numSubscripts(): number { return wrapJoinPoint(this._javaObject.getNumSubscripts()) } - /** - * A view of the current arrayAccess without the last subscript, or undefined if this arrayAccess only has one subscript - */ - get parentAccess(): ArrayAccess { return wrapJoinPoint(this._javaObject.getParentAccess()) } - /** - * Expression of the array access subscript - */ - get subscript(): Expression[] { return wrapJoinPoint(this._javaObject.getSubscript()) } -} - -export class ArrayType extends Type { - /** - * @internal - */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get elementType(): Type { return wrapJoinPoint(this._javaObject.getElementType()) } - set elementType(value: Type) { this._javaObject.setElementType(unwrapJoinPoint(value)); } - /** - * Sets the element type of the array + * An integer expression, or undefined if no 'collapse' clause is defined */ - setElementType(arrayElementType: Type): void { return wrapJoinPoint(this._javaObject.setElementType(unwrapJoinPoint(arrayElementType))); } -} - -export class AsmStmt extends Statement { + get collapse(): string { return wrapJoinPoint(this._javaObject.collapse()) } /** - * @internal + * An integer expression, or undefined if no 'collapse' clause is defined */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get clobbers(): string[] { return wrapJoinPoint(this._javaObject.getClobbers()) } - get isSimple(): boolean { return wrapJoinPoint(this._javaObject.getIsSimple()) } - get isVolatile(): boolean { return wrapJoinPoint(this._javaObject.getIsVolatile()) } -} - -export class BinaryOp extends Op { + set collapse(value: string | number) { this._javaObject.setCollapse(unwrapJoinPoint(value)); } /** - * @internal + * The variable names of all copyin clauses, or empty array if no copyin clause is defined */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get isAssignment(): boolean { return wrapJoinPoint(this._javaObject.getIsAssignment()) } - get left(): Expression { return wrapJoinPoint(this._javaObject.getLeft()) } - set left(value: Expression) { this._javaObject.setLeft(unwrapJoinPoint(value)); } - get right(): Expression { return wrapJoinPoint(this._javaObject.getRight()) } - set right(value: Expression) { this._javaObject.setRight(unwrapJoinPoint(value)); } - setLeft(left: Expression): void { return wrapJoinPoint(this._javaObject.setLeft(unwrapJoinPoint(left))); } - setRight(right: Expression): void { return wrapJoinPoint(this._javaObject.setRight(unwrapJoinPoint(right))); } -} - -export class BoolLiteral extends Literal { + get copyin(): string[] { return wrapJoinPoint(this._javaObject.copyin()) } /** - * @internal + * The variable names of all copyin clauses, or empty array if no copyin clause is defined */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get value(): boolean { return wrapJoinPoint(this._javaObject.getValue()) } -} - -export class Break extends Statement { + set copyin(value: string[]) { this._javaObject.setCopyin(unwrapJoinPoint(value)); } /** - * @internal + * One of 'shared' or 'none', or undefined if no 'default' clause is defined */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; + get default(): string { return wrapJoinPoint(this._javaObject.default()) } /** - * The enclosing statement related to this break. It should be either a loop or a switch statement. + * One of 'shared' or 'none', or undefined if no 'default' clause is defined */ - get enclosingStmt(): Statement { return wrapJoinPoint(this._javaObject.getEnclosingStmt()) } -} - -export class BuiltinType extends Type { + set default(value: string) { this._javaObject.setDefault(unwrapJoinPoint(value)); } /** - * @internal + * The variable names of all firstprivate clauses, or empty array if no firstprivate clause is defined */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get builtinKind(): string { return wrapJoinPoint(this._javaObject.getBuiltinKind()) } + get firstprivate(): string[] { return wrapJoinPoint(this._javaObject.firstprivate()) } /** - * True, if ot is a floating type (e.g., float, double) + * The variable names of all firstprivate clauses, or empty array if no firstprivate clause is defined */ - get isFloat(): boolean { return wrapJoinPoint(this._javaObject.getIsFloat()) } + set firstprivate(value: string[]) { this._javaObject.setFirstprivate(unwrapJoinPoint(value)); } /** - * True, if it is an integer type + * The kind of the directive */ - get isInteger(): boolean { return wrapJoinPoint(this._javaObject.getIsInteger()) } + get kind(): string { return wrapJoinPoint(this._javaObject.kind()) } /** - * True, if it is a signed integer type + * The kind of the directive */ - get isSigned(): boolean { return wrapJoinPoint(this._javaObject.getIsSigned()) } + set kind(value: string) { this._javaObject.setKind(unwrapJoinPoint(value)); } /** - * True, if it is an unsigned integer type + * The variable names of all lastprivate clauses, or empty array if no lastprivate clause is defined */ - get isUnsigned(): boolean { return wrapJoinPoint(this._javaObject.getIsUnsigned()) } + get lastprivate(): string[] { return wrapJoinPoint(this._javaObject.lastprivate()) } /** - * True, if it is the type 'void' + * The variable names of all lastprivate clauses, or empty array if no lastprivate clause is defined */ - get isVoid(): boolean { return wrapJoinPoint(this._javaObject.getIsVoid()) } -} - -export class Call extends Expression { + set lastprivate(value: string[]) { this._javaObject.setLastprivate(unwrapJoinPoint(value)); } /** - * @internal + * An integer expression, or undefined if no 'num_threads' clause is defined */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; + get numThreads(): string { return wrapJoinPoint(this._javaObject.numThreads()) } /** - * An alias for 'args' + * An integer expression, or undefined if no 'num_threads' clause is defined */ - get argList(): Expression[] { return wrapJoinPoint(this._javaObject.getArgList()) } + set numThreads(value: string) { this._javaObject.setNumThreads(unwrapJoinPoint(value)); } /** - * An array with the arguments of the call + * An integer expression, or undefined if no 'ordered' clause with a parameter is defined */ - get args(): Expression[] { return wrapJoinPoint(this._javaObject.getArgs()) } + get ordered(): string { return wrapJoinPoint(this._javaObject.ordered()) } /** - * A 'function' join point that represents the function of the call that was found, it can return either an implementation or a function prototype; 'undefined' if no declaration was found + * An integer expression, or undefined if no 'ordered' clause with a parameter is defined */ - get declaration(): FunctionJp { return wrapJoinPoint(this._javaObject.getDeclaration()) } + set ordered(value: string) { this._javaObject.setOrdered(unwrapJoinPoint(value)); } /** - * A 'function' join point that represents the function definition of the call; 'undefined' if no definition was found + * The variable names of all private clauses, or empty array if no private clause is defined */ - get definition(): FunctionJp { return wrapJoinPoint(this._javaObject.getDefinition()) } + get private(): string[] { return wrapJoinPoint(this._javaObject.private()) } /** - * A function join point that represents the 'raw' function of the call (e.g. if this is a call to a templated function, returns a declaration representing the template specialization, instead of the original function) + * The variable names of all private clauses, or empty array if no private clause is defined */ - get directCallee(): FunctionJp { return wrapJoinPoint(this._javaObject.getDirectCallee()) } + set private(value: string[]) { this._javaObject.setPrivate(unwrapJoinPoint(value)); } /** - * A function join point associated with this call. If a definition is present, it is given priority over returning a declaration. If only declarations are present, returns a declaration + * One of 'master', 'close' or 'spread', or undefined if no 'proc_bind' clause is defined */ - get function(): FunctionJp { return wrapJoinPoint(this._javaObject.getFunction()) } + get procBind(): string { return wrapJoinPoint(this._javaObject.procBind()) } /** - * The function type of the call, which includes the return type and the types of the parameters + * One of 'master', 'close' or 'spread', or undefined if no 'proc_bind' clause is defined */ - get functionType(): FunctionType { return wrapJoinPoint(this._javaObject.getFunctionType()) } - get isMemberAccess(): boolean { return wrapJoinPoint(this._javaObject.getIsMemberAccess()) } - get isStmtCall(): boolean { return wrapJoinPoint(this._javaObject.getIsStmtCall()) } - get memberAccess(): MemberAccess { return wrapJoinPoint(this._javaObject.getMemberAccess()) } - get memberNames(): string[] { return wrapJoinPoint(this._javaObject.getMemberNames()) } - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } - set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } - get numArgs(): number { return wrapJoinPoint(this._javaObject.getNumArgs()) } + set procBind(value: string) { this._javaObject.setProcBind(unwrapJoinPoint(value)); } /** - * The return type of the call + * The reduction kinds in the reductions clauses of the this pragma, or empty array if no reduction is defined */ - get returnType(): Type { return wrapJoinPoint(this._javaObject.getReturnType()) } + get reductionKinds(): string[] { return wrapJoinPoint(this._javaObject.reductionKinds()) } /** - * Similar to $function.signature, but if no function decl could be found (e.g., function from system include), returns a signature based on just the name of the function + * An integer expression, or undefined if no 'schedule' clause with chunk size is defined */ - get signature(): string { return wrapJoinPoint(this._javaObject.getSignature()) } - getArg(index: number): Expression { return wrapJoinPoint(this._javaObject.getArg(unwrapJoinPoint(index))); } + get scheduleChunkSize(): string { return wrapJoinPoint(this._javaObject.scheduleChunkSize()) } /** - * Adds an argument at the end of the call, creating an expression using the given code and type. If a type is not provided, a dummy type is used + * An integer expression, or undefined if no 'schedule' clause with chunk size is defined */ - addArg(argCode: string, type?: Type): void; + set scheduleChunkSize(value: string | number) { this._javaObject.setScheduleChunkSize(unwrapJoinPoint(value)); } /** - * Adds an argument at the end of the call, creating a literal 'type' from the type string + * One of 'static', 'dynamic', 'guided', 'auto' or 'runtime', or undefined if no 'schedule' clause is defined */ - addArg(arg: string, type: string): void; + get scheduleKind(): string { return wrapJoinPoint(this._javaObject.scheduleKind()) } /** - * Adds an argument at the end of the call, creating an expression using the given code and type. If a type is not provided, a dummy type is used + * One of 'static', 'dynamic', 'guided', 'auto' or 'runtime', or undefined if no 'schedule' clause is defined */ - addArg(p1: string, p2?: Type | string): void { return wrapJoinPoint(this._javaObject.addArg(unwrapJoinPoint(p1), unwrapJoinPoint(p2))); } + set scheduleKind(value: string) { this._javaObject.setScheduleKind(unwrapJoinPoint(value)); } /** - * Tries to inline this call + * A list with possible values of 'monotonic', 'nonmonotonic' or 'simd', or undefined if no 'schedule' clause with modifiers is defined */ - inline(): boolean { return wrapJoinPoint(this._javaObject.inline()); } - setArg(index: number, expr: Expression): void { return wrapJoinPoint(this._javaObject.setArg(unwrapJoinPoint(index), unwrapJoinPoint(expr))); } - setArgFromString(index: number, expr: string): void { return wrapJoinPoint(this._javaObject.setArgFromString(unwrapJoinPoint(index), unwrapJoinPoint(expr))); } + get scheduleModifiers(): string[] { return wrapJoinPoint(this._javaObject.scheduleModifiers()) } /** - * Changes the name of the call + * A list with possible values of 'monotonic', 'nonmonotonic' or 'simd', or undefined if no 'schedule' clause with modifiers is defined */ - setName(name: string): void { return wrapJoinPoint(this._javaObject.setName(unwrapJoinPoint(name))); } + set scheduleModifiers(value: string[]) { this._javaObject.setScheduleModifiers(unwrapJoinPoint(value)); } /** - * Wraps this call with a possibly new wrapping function + * The variable names of all shared clauses, or empty array if no shared clause is defined */ - wrap(name: string): void { return wrapJoinPoint(this._javaObject.wrap(unwrapJoinPoint(name))); } -} - -export class Case extends SwitchCase { + get shared(): string[] { return wrapJoinPoint(this._javaObject.shared()) } /** - * @internal + * The variable names of all shared clauses, or empty array if no shared clause is defined */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; + set shared(value: string[]) { this._javaObject.setShared(unwrapJoinPoint(value)); } /** - * The instructions that are associated with this case in the source code. This does not represent what instructions are actually executed (e.g., if a case does not have a break, does not show instructions of the next case) + * The variable names for the given reduction kind, or empty array if no reduction of that kind is defined */ - get instructions(): Statement[] { return wrapJoinPoint(this._javaObject.getInstructions()) } + getReduction(kind: string): string[] { return wrapJoinPoint(this._javaObject.getReduction(unwrapJoinPoint(kind))); } /** - * True if this is a default case, false otherwise + * True if the directive has at least one clause of the given clause kind, false otherwise */ - get isDefault(): boolean { return wrapJoinPoint(this._javaObject.getIsDefault()) } + hasClause(clauseName: string): boolean { return wrapJoinPoint(this._javaObject.hasClause(unwrapJoinPoint(clauseName))); } /** - * True if this case does not contain instructions (i.e., it is directly above another case), false otherwise + * True if the directive has the given clause kind, false otherwise */ - get isEmpty(): boolean { return wrapJoinPoint(this._javaObject.getIsEmpty()) } + isClauseLegal(clauseName: string): boolean { return wrapJoinPoint(this._javaObject.isClauseLegal(unwrapJoinPoint(clauseName))); } /** - * The case statement that comes after this case, or undefined if there are no more case statements + * Removes any clause of the given kind from the OpenMP pragma */ - get nextCase(): Case { return wrapJoinPoint(this._javaObject.getNextCase()) } + removeClause(clauseKind: string): void { return wrapJoinPoint(this._javaObject.removeClause(unwrapJoinPoint(clauseKind))); } /** - * The first statement that is not a case that will be executed by this case statement + * Sets the value of the collapse clause of an OpenMP pragma */ - get nextInstruction(): Statement { return wrapJoinPoint(this._javaObject.getNextInstruction()) } + setCollapse(newExpr: string): void; /** - * The values that the case statement will match. It can return zero (e.g., 'default:'), one (e.g., 'case 1:') or two (e.g., 'case 2...4:') expressions, depending on the format of the case + * Sets the value of the collapse clause of an OpenMP pragma */ - get values(): Expression[] { return wrapJoinPoint(this._javaObject.getValues()) } -} - -export class Cast extends Expression { + setCollapse(newExpr: number): void; /** - * @internal + * Sets the value of the collapse clause of an OpenMP pragma */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get fromType(): Type { return wrapJoinPoint(this._javaObject.getFromType()) } + setCollapse(p1: string | number): void { return wrapJoinPoint(this._javaObject.setCollapse(unwrapJoinPoint(p1))); } /** - * @deprecated Use expr.implicitCast instead + * Sets the variables of a copyin clause of an OpenMP pragma */ - get isImplicitCast(): boolean { return wrapJoinPoint(this._javaObject.getIsImplicitCast()) } - get subExpr(): Expression { return wrapJoinPoint(this._javaObject.getSubExpr()) } - get toType(): Type { return wrapJoinPoint(this._javaObject.getToType()) } -} - -export class CilkSpawn extends Call { + setCopyin(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setCopyin(unwrapJoinPoint(newVariables))); } /** - * @internal + * Sets the value of the default clause of an OpenMP pragma */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; -} - -export class CilkSync extends Statement { + setDefault(newDefault: string): void { return wrapJoinPoint(this._javaObject.setDefault(unwrapJoinPoint(newDefault))); } /** - * @internal + * Sets the variables of a firstprivate clause of an OpenMP pragma */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; -} - + setFirstprivate(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setFirstprivate(unwrapJoinPoint(newVariables))); } /** - * Represents a C++ class + * Sets the directive kind of the OpenMP pragma. Any unsupported clauses will be discarded */ -export class Class extends RecordJp { + setKind(directiveKind: string): void { return wrapJoinPoint(this._javaObject.setKind(unwrapJoinPoint(directiveKind))); } /** - * @internal + * Sets the variables of a lastprivate clause of an OpenMP pragma */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; + setLastprivate(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setLastprivate(unwrapJoinPoint(newVariables))); } /** - * All the classes this class inherits from + * Sets the value of the num_threads clause of an OpenMP pragma */ - get allBases(): Class[] { return wrapJoinPoint(this._javaObject.getAllBases()) } + setNumThreads(newExpr: string): void { return wrapJoinPoint(this._javaObject.setNumThreads(unwrapJoinPoint(newExpr))); } /** - * All the methods of this class, including inherited ones + * Sets the value of the ordered clause of an OpenMP pragma */ - get allMethods(): Method[] { return wrapJoinPoint(this._javaObject.getAllMethods()) } + setOrdered(parameters?: string): void { return wrapJoinPoint(this._javaObject.setOrdered(unwrapJoinPoint(parameters))); } /** - * The classes this class directly inherits from + * Sets the variables of a private clause of an OpenMP pragma */ - get bases(): Class[] { return wrapJoinPoint(this._javaObject.getBases()) } + setPrivate(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setPrivate(unwrapJoinPoint(newVariables))); } /** - * Class join points can either represent declarations or definitions, returns the definition of this class, if present, or the first declaration, if only declarations are present + * Sets the value of the proc_bind clause of an OpenMP pragma */ - get canonical(): Class { return wrapJoinPoint(this._javaObject.getCanonical()) } + setProcBind(newBind: string): void { return wrapJoinPoint(this._javaObject.setProcBind(unwrapJoinPoint(newBind))); } /** - * The implementation (or definition) of this class present in the AST, or undefined if none is found + * Sets the variables for a given kind of a reduction clause of an OpenMP pragma */ - get implementation(): Class { return wrapJoinPoint(this._javaObject.getImplementation()) } + setReduction(kind: string, newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setReduction(unwrapJoinPoint(kind), unwrapJoinPoint(newVariables))); } /** - * True, if contains at least one pure function + * Sets the value of the chunk size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception */ - get isAbstract(): boolean { return wrapJoinPoint(this._javaObject.getIsAbstract()) } + setScheduleChunkSize(chunkSize: string): void; /** - * True if this is the class returned by the 'canonical' attribute + * Sets the value of the chunk size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception */ - get isCanonical(): boolean { return wrapJoinPoint(this._javaObject.getIsCanonical()) } + setScheduleChunkSize(chunkSize: number): void; /** - * True, if all functions are pure + * Sets the value of the chunk size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception */ - get isInterface(): boolean { return wrapJoinPoint(this._javaObject.getIsInterface()) } + setScheduleChunkSize(p1: string | number): void { return wrapJoinPoint(this._javaObject.setScheduleChunkSize(unwrapJoinPoint(p1))); } /** - * The methods declared by this class + * Sets the value of the schedule clause of an OpenMP pragma */ - get methods(): Method[] { return wrapJoinPoint(this._javaObject.getMethods()) } + setScheduleKind(scheduleKind: string): void { return wrapJoinPoint(this._javaObject.setScheduleKind(unwrapJoinPoint(scheduleKind))); } /** - * The prototypes (or declarations) of this class present in the AST, if any + * Sets the value of the modifiers in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception */ - get prototypes(): Class[] { return wrapJoinPoint(this._javaObject.getPrototypes()) } + setScheduleModifiers(modifiers: string[]): void { return wrapJoinPoint(this._javaObject.setScheduleModifiers(unwrapJoinPoint(modifiers))); } /** - * Adds a method to a class. If the given method has a definition, creates an equivalent declaration and adds it to the class, otherwise simply added the declaration to the class. In both cases, the declaration is only added to the class if there is no declaration already with the same signature. + * Sets the variables of a shared clause of an OpenMP pragma */ - addMethod(method: Method): void { return wrapJoinPoint(this._javaObject.addMethod(unwrapJoinPoint(method))); } + setShared(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setShared(unwrapJoinPoint(newVariables))); } } -export class Continue extends Statement { +export class Statement extends Joinpoint { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; + get isFirst(): boolean { return wrapJoinPoint(this._javaObject.isFirst()) } + get isLast(): boolean { return wrapJoinPoint(this._javaObject.isLast()) } } -export class CudaKernelCall extends Call { /** - * @internal + * Represents a group of statements (e.g., function body, loop body, if/else body, etc.) */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; - get config(): Expression[] { return wrapJoinPoint(this._javaObject.getConfig()) } - set config(value: Expression[]) { this._javaObject.setConfig(unwrapJoinPoint(value)); } - setConfig(args: Expression[]): void { return wrapJoinPoint(this._javaObject.setConfig(unwrapJoinPoint(args))); } - setConfigFromStrings(args: string[]): void { return wrapJoinPoint(this._javaObject.setConfigFromStrings(unwrapJoinPoint(args))); } -} - -export class DeclStmt extends Statement { +export class Scope extends Statement { /** * @internal */ @@ -1746,330 +1616,209 @@ export class DeclStmt extends Statement { name: null, }; /** - * The declarations in this statement + * Returns the descendant statements of this scope, excluding other scopes, loops, ifs and wrapper statements */ - get decls(): Decl[] { return wrapJoinPoint(this._javaObject.getDecls()) } -} - + get allStmts(): Statement[] { return wrapJoinPoint(this._javaObject.allStmts()) } /** - * Represents a decl that comes from a declarator (e.g., function, field, variable) + * Returns the first statement in the scope */ -export class Declarator extends NamedDecl { + get firstStmt(): Statement { return wrapJoinPoint(this._javaObject.firstStmt()) } /** - * @internal + * Returns the last statement in the scope */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; -} - -export class Default extends SwitchCase { + get lastStmt(): Statement { return wrapJoinPoint(this._javaObject.lastStmt()) } /** - * @internal + * True if the scope does not have curly braces */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; -} - -export class DeleteExpr extends Expression { - /** - * @internal - */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; -} - - /** - * Represents a type that was referred to using an elaborated type keyword, e.g., struct S, or via a qualified name, e.g., N::M::type, or both. This type is used to keep track of a type name as written in the source code, including tag keywords and any nested-name-specifiers. The type itself is always 'sugar', used to express what was written in the source code but containing no additional semantic information. - */ -export class ElaboratedType extends Type { + get naked(): boolean { return wrapJoinPoint(this._javaObject.naked()) } /** - * @internal + * True if the scope does not have curly braces */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; + set naked(value: boolean) { this._javaObject.setNaked(unwrapJoinPoint(value)); } /** - * The keyword of this elaborated type, if present. Can be one of: struct, interface, union, class, enum, typename + * The statement that owns the scope (e.g., function, loop...) */ - get keyword(): string { return wrapJoinPoint(this._javaObject.getKeyword()) } + get owner(): Joinpoint { return wrapJoinPoint(this._javaObject.owner()) } /** - * The type that is being prefixed with the qualifier + * Returns the direct (children) statements of this scope */ - get namedType(): Type { return wrapJoinPoint(this._javaObject.getNamedType()) } + get stmts(): Statement[] { return wrapJoinPoint(this._javaObject.stmts()) } /** - * The qualifier of this elaborated type, if present (e.g., A::) + * Adds a new local variable to this scope */ - get qualifier(): string { return wrapJoinPoint(this._javaObject.getQualifier()) } -} - -export class EmptyStmt extends Statement { + addLocal(name: string, type: Joinpoint, initValue?: string): Joinpoint { return wrapJoinPoint(this._javaObject.addLocal(unwrapJoinPoint(name), unwrapJoinPoint(type), unwrapJoinPoint(initValue))); } /** - * @internal + * CFG tester */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; -} - + cfg(): string { return wrapJoinPoint(this._javaObject.cfg()); } /** - * Represents an enum + * Clears the contents of this scope (untested) */ -export class EnumDecl extends NamedDecl { + clear(): void { return wrapJoinPoint(this._javaObject.clear()); } /** - * @internal + * DFG tester */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; - get enumerators(): EnumeratorDecl[] { return wrapJoinPoint(this._javaObject.getEnumerators()) } -} - -export class EnumeratorDecl extends NamedDecl { + dfg(): string { return wrapJoinPoint(this._javaObject.dfg()); } /** - * @internal + * The number of statements in the scope, including the statements inside the declaration and bodies of structures such as ifs and loops, and not considering comments and pragmas. If flat is true, does not consider the statements inside structures such as ifs and loops (e.g., a loop counts as one statement) */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; -} - -export class ExprStmt extends Statement { + getNumStatements(flat: boolean = false): number { return wrapJoinPoint(this._javaObject.getNumStatements(unwrapJoinPoint(flat))); } + insertBegin(node: Joinpoint): Joinpoint; + insertBegin(code: string): Joinpoint; + insertBegin(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertBegin(unwrapJoinPoint(p1))); } + insertEnd(node: Joinpoint): Joinpoint; + insertEnd(code: string): Joinpoint; + insertEnd(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertEnd(unwrapJoinPoint(p1))); } /** - * @internal + * Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; + insertReturn(code: Joinpoint): Joinpoint; /** - * The expression join point associated to this exprStmt + * Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node */ - get expr(): Expression { return wrapJoinPoint(this._javaObject.getExpr()) } -} - + insertReturn(code: string): Joinpoint; /** - * Represents a member of a struct/union/class + * Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node */ -export class Field extends Declarator { + insertReturn(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertReturn(unwrapJoinPoint(p1))); } /** - * @internal + * Sets the 'naked' status of a scope (a scope is naked if it does not have curly braces) */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", - }; + setNaked(isNaked: boolean): void { return wrapJoinPoint(this._javaObject.setNaked(unwrapJoinPoint(isNaked))); } } -export class FloatLiteral extends Literal { +export class Body extends Scope { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get value(): number { return wrapJoinPoint(this._javaObject.getValue()) } } - /** - * Represents a function declaration or definition - */ -export class FunctionJp extends Declarator { +export class Loop extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", + name: "kind", }; - get body(): Scope { return wrapJoinPoint(this._javaObject.getBody()) } + get body(): Scope { return wrapJoinPoint(this._javaObject.body()) } set body(value: Scope) { this._javaObject.setBody(unwrapJoinPoint(value)); } - get calls(): Call[] { return wrapJoinPoint(this._javaObject.getCalls()) } - /** - * Function join points can either represent declarations or definitions, returns the definition of this function, if present, or the first declaration, if only declarations are present - */ - get canonical(): FunctionJp { return wrapJoinPoint(this._javaObject.getCanonical()) } - /** - * Returns the first prototype of this function that could be found, or undefined if there is none - */ - get declarationJp(): FunctionJp { return wrapJoinPoint(this._javaObject.getDeclarationJp()) } - /** - * Returns the prototypes of this function that are present in the code. If there are none, returns an empty array - */ - get declarationJps(): FunctionJp[] { return wrapJoinPoint(this._javaObject.getDeclarationJps()) } - /** - * Returns the implementation of this function if there is one, or undefined otherwise - */ - get definitionJp(): FunctionJp { return wrapJoinPoint(this._javaObject.getDefinitionJp()) } - /** - * The type of the call, which includes the return type and the types of the parameters - */ - get functionType(): FunctionType { return wrapJoinPoint(this._javaObject.getFunctionType()) } /** - * The type of the call, which includes the return type and the types of the parameters - */ - set functionType(value: FunctionType) { this._javaObject.setFunctionType(unwrapJoinPoint(value)); } - /** - * True if this particular function join point has a body, false otherwise - * - * @deprecated Use .isImplementation instead - */ - get hasDefinition(): boolean { return wrapJoinPoint(this._javaObject.getHasDefinition()) } - get id(): string { return wrapJoinPoint(this._javaObject.getId()) } - /** - * True, if this is the function returned by the 'canonical' attribute - */ - get isCanonical(): boolean { return wrapJoinPoint(this._javaObject.getIsCanonical()) } - get isCudaKernel(): boolean { return wrapJoinPoint(this._javaObject.getIsCudaKernel()) } - get isDelete(): boolean { return wrapJoinPoint(this._javaObject.getIsDelete()) } - /** - * True if this particular function join point is an implementation (i.e. has a body), false otherwise - */ - get isImplementation(): boolean { return wrapJoinPoint(this._javaObject.getIsImplementation()) } - get isInline(): boolean { return wrapJoinPoint(this._javaObject.getIsInline()) } - get isModulePrivate(): boolean { return wrapJoinPoint(this._javaObject.getIsModulePrivate()) } - /** - * True if this particular function join point is a prototype (i.e. does not have a body), false otherwise + * The statement of the loop condition */ - get isPrototype(): boolean { return wrapJoinPoint(this._javaObject.getIsPrototype()) } - get isPure(): boolean { return wrapJoinPoint(this._javaObject.getIsPure()) } - get isVirtual(): boolean { return wrapJoinPoint(this._javaObject.getIsVirtual()) } - get paramNames(): string[] { return wrapJoinPoint(this._javaObject.getParamNames()) } - get params(): Param[] { return wrapJoinPoint(this._javaObject.getParams()) } - set params(value: Param[]) { this._javaObject.setParams(unwrapJoinPoint(value)); } - get returnType(): Type { return wrapJoinPoint(this._javaObject.getReturnType()) } - set returnType(value: Type) { this._javaObject.setReturnType(unwrapJoinPoint(value)); } + get cond(): Statement { return wrapJoinPoint(this._javaObject.cond()) } /** - * A string with the signature of this function (e.g., name of the function, plus the parameters types) + * The statement of the loop condition */ - get signature(): string { return wrapJoinPoint(this._javaObject.getSignature()) } + set cond(value: string) { this._javaObject.setCond(unwrapJoinPoint(value)); } + get condRelation(): Relation { return wrapJoinPoint(this._javaObject.condRelation()) } + set condRelation(value: Relation) { this._javaObject.setCondRelation(unwrapJoinPoint(value)); } + get controlVar(): string { return wrapJoinPoint(this._javaObject.controlVar()) } + get controlVarref(): Varref { return wrapJoinPoint(this._javaObject.controlVarref()) } /** - * The storage class of this function (i.e., one of NONE, EXTERN, PRIVATE_EXTERN or STATIC) + * The expression of the last value of the control variable (e.g. '10' in 'size_t i = 0; i < 10; i++') */ - get storageClass(): StorageClass { return wrapJoinPoint(this._javaObject.getStorageClass()) } + get endValue(): string { return wrapJoinPoint(this._javaObject.endValue()) } /** - * The storage class of this function (i.e., one of NONE, EXTERN, PRIVATE_EXTERN or STATIC) + * The expression of the last value of the control variable (e.g. '10' in 'size_t i = 0; i < 10; i++') */ - set storageClass(value: StorageClass) { this._javaObject.setStorageClass(unwrapJoinPoint(value)); } - getDeclaration(withReturnType: boolean): string { return wrapJoinPoint(this._javaObject.getDeclaration(unwrapJoinPoint(withReturnType))); } + set endValue(value: string) { this._javaObject.setEndValue(unwrapJoinPoint(value)); } /** - * Adds a new parameter to the function + * True if the condition of the loop in the canonical format, and is one of: <, <=, >, >= */ - addParam(param: Param): void; + get hasCondRelation(): boolean { return wrapJoinPoint(this._javaObject.hasCondRelation()) } /** - * Adds a new parameter to the function + * Uniquely identifies the loop inside the program */ - addParam(name: string, type?: Type): void; + get id(): string { return wrapJoinPoint(this._javaObject.id()) } /** - * Adds a new parameter to the function + * The statement of the loop initialization */ - addParam(p1: Param | string, p2?: Type): void { return wrapJoinPoint(this._javaObject.addParam(unwrapJoinPoint(p1), unwrapJoinPoint(p2))); } + get init(): Statement { return wrapJoinPoint(this._javaObject.init()) } /** - * Clones this function assigning it a new name, inserts the cloned function after the original function. If the name is the same and the original method, automatically removes the cloned method from the class + * The statement of the loop initialization */ - clone(newName: string, insert: boolean = true): FunctionJp { return wrapJoinPoint(this._javaObject.clone(unwrapJoinPoint(newName), unwrapJoinPoint(insert))); } + set init(value: string) { this._javaObject.setInit(unwrapJoinPoint(value)); } /** - * Generates a clone of the provided function on a new file with the provided name (or with a weaver-generated name if one is not provided). + * The expression of the first value of the control variable (e.g. '0' in 'size_t i = 0;') */ - cloneOnFile(newName: string, fileName?: string): FunctionJp; + get initValue(): string { return wrapJoinPoint(this._javaObject.initValue()) } /** - * Generates a clone of the provided function on a new file (with the provided join point). + * The expression of the first value of the control variable (e.g. '0' in 'size_t i = 0;') */ - cloneOnFile(newName: string, fileName: FileJp): FunctionJp; + set initValue(value: string) { this._javaObject.setInitValue(unwrapJoinPoint(value)); } + get isInnermost(): boolean { return wrapJoinPoint(this._javaObject.isInnermost()) } + get isOutermost(): boolean { return wrapJoinPoint(this._javaObject.isOutermost()) } + get isParallel(): boolean { return wrapJoinPoint(this._javaObject.isParallel()) } + set isParallel(value: boolean) { this._javaObject.setIsParallel(unwrapJoinPoint(value)); } + get iterations(): number { return wrapJoinPoint(this._javaObject.iterations()) } + get iterationsExpr(): Expression { return wrapJoinPoint(this._javaObject.iterationsExpr()) } + get kind(): LoopKind { return wrapJoinPoint(this._javaObject.kind()) } + set kind(value: LoopKind) { this._javaObject.setKind(unwrapJoinPoint(value)); } + get nestedLevel(): number { return wrapJoinPoint(this._javaObject.nestedLevel()) } + get rank(): number[] { return wrapJoinPoint(this._javaObject.rank()) } /** - * Generates a clone of the provided function on a new file with the provided name (or with a weaver-generated name if one is not provided). + * The statement of the loop step */ - cloneOnFile(p1: string, p2?: string | FileJp): FunctionJp { return wrapJoinPoint(this._javaObject.cloneOnFile(unwrapJoinPoint(p1), unwrapJoinPoint(p2))); } + get step(): Statement { return wrapJoinPoint(this._javaObject.step()) } /** - * Inserts the joinpoint before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node + * The statement of the loop step */ - insertReturn(code: Joinpoint): Joinpoint; + set step(value: string) { this._javaObject.setStep(unwrapJoinPoint(value)); } /** - * Inserts code as a literal statement before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node + * The expression of the step value of the control variable (e.g. '1' in 'size_t i = 0; i < 10; i++') */ - insertReturn(code: string): Joinpoint; + get stepValue(): string { return wrapJoinPoint(this._javaObject.stepValue()) } /** - * Inserts the joinpoint before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node + * Interchanges two for loops, if possible */ - insertReturn(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertReturn(unwrapJoinPoint(p1))); } + interchange(otherLoop: Loop): void { return wrapJoinPoint(this._javaObject.interchange(unwrapJoinPoint(otherLoop))); } /** - * Creates a new call to this function + * True if this loop can be interchanged with the given loop, which means that they are adjacent and have no data dependencies that would prevent their interchange. This is a conservative test. */ - newCall(args: Joinpoint[]): Call { return wrapJoinPoint(this._javaObject.newCall(unwrapJoinPoint(args))); } + isInterchangeable(otherLoop: Loop): boolean { return wrapJoinPoint(this._javaObject.isInterchangeable(unwrapJoinPoint(otherLoop))); } /** - * Sets the body of the function + * Sets the body of the loop */ setBody(body: Scope): void { return wrapJoinPoint(this._javaObject.setBody(unwrapJoinPoint(body))); } /** - * Sets the type of the function - */ - setFunctionType(functionType: FunctionType): void { return wrapJoinPoint(this._javaObject.setFunctionType(unwrapJoinPoint(functionType))); } - /** - * Sets the parameter of the function at the given position - */ - setParam(index: number, param: Param): void; - /** - * Sets the parameter of the function at the given position - */ - setParam(index: number, name: string, type?: Type): void; - /** - * Sets the parameter of the function at the given position - */ - setParam(p1: number, p2: Param | string, p3?: Type): void { return wrapJoinPoint(this._javaObject.setParam(unwrapJoinPoint(p1), unwrapJoinPoint(p2), unwrapJoinPoint(p3))); } - /** - * Sets the type of a parameter of the function - */ - setParamType(index: number, newType: Type): void { return wrapJoinPoint(this._javaObject.setParamType(unwrapJoinPoint(index), unwrapJoinPoint(newType))); } - /** - * Sets the parameters of the function + * Sets the conditional statement of the loop. Works with loops of kind 'for' */ - setParams(params: Param[]): void { return wrapJoinPoint(this._javaObject.setParams(unwrapJoinPoint(params))); } + setCond(condCode: string): void { return wrapJoinPoint(this._javaObject.setCond(unwrapJoinPoint(condCode))); } /** - * Overload that accepts strings that represent type-varname pairs (e.g., int param1) + * Changes the operator of a canonical condition, if possible. Supported operators: lt, le, gt, ge */ - setParamsFromStrings(params: string[]): void { return wrapJoinPoint(this._javaObject.setParamsFromStrings(unwrapJoinPoint(params))); } + setCondRelation(operator: Relation): void { return wrapJoinPoint(this._javaObject.setCondRelation(unwrapJoinPoint(operator))); } /** - * Sets the return type of the function + * Sets the end value of the loop. Works with loops of kind 'for' */ - setReturnType(returnType: Type): void { return wrapJoinPoint(this._javaObject.setReturnType(unwrapJoinPoint(returnType))); } + setEndValue(initCode: string): void { return wrapJoinPoint(this._javaObject.setEndValue(unwrapJoinPoint(initCode))); } /** - * Sets the storage class of this specific function decl. AUTO and REGISTER are not allowed for functions, and EXTERN is not allowed in function implementations, or function declarations that are in the same file as the implementation. Returns true if the storage class changed, false otherwise. + * Sets the init statement of the loop */ - setStorageClass(storageClass: StorageClass): boolean { return wrapJoinPoint(this._javaObject.setStorageClass(unwrapJoinPoint(storageClass))); } -} - -export class FunctionType extends Type { + setInit(initCode: string): void { return wrapJoinPoint(this._javaObject.setInit(unwrapJoinPoint(initCode))); } /** - * @internal + * Sets the init value of the loop. Works with loops of kind 'for' */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get paramTypes(): Type[] { return wrapJoinPoint(this._javaObject.getParamTypes()) } - get returnType(): Type { return wrapJoinPoint(this._javaObject.getReturnType()) } - set returnType(value: Type) { this._javaObject.setReturnType(unwrapJoinPoint(value)); } + setInitValue(initCode: string): void { return wrapJoinPoint(this._javaObject.setInitValue(unwrapJoinPoint(initCode))); } /** - * Sets the type of a parameter of the FunctionType. Be careful that if you directly change the type of a paramemter and the function type is associated with a function declaration, this change will not be reflected in the function. If you want to change the type of a parameter of a function declaration, use $function.setParaType + * Sets the attribute 'isParallel' of the loop */ - setParamType(index: number, newType: Type): void { return wrapJoinPoint(this._javaObject.setParamType(unwrapJoinPoint(index), unwrapJoinPoint(newType))); } + setIsParallel(isParallel: boolean): void { return wrapJoinPoint(this._javaObject.setIsParallel(unwrapJoinPoint(isParallel))); } /** - * Sets the return type of the FunctionType + * Sets the kind of the loop */ - setReturnType(newType: Type): void { return wrapJoinPoint(this._javaObject.setReturnType(unwrapJoinPoint(newType))); } -} - -export class GotoStmt extends Statement { + setKind(kind: LoopKind): void { return wrapJoinPoint(this._javaObject.setKind(unwrapJoinPoint(kind))); } /** - * @internal + * Sets the step statement of the loop. Works with loops of kind 'for' */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: null, - }; - get label(): LabelDecl { return wrapJoinPoint(this._javaObject.getLabel()) } - set label(value: LabelDecl) { this._javaObject.setLabel(unwrapJoinPoint(value)); } + setStep(stepCode: string): void { return wrapJoinPoint(this._javaObject.setStep(unwrapJoinPoint(stepCode))); } /** - * Sets the label of the goto + * Applies loop tiling to this loop */ - setLabel(label: LabelDecl): void { return wrapJoinPoint(this._javaObject.setLabel(unwrapJoinPoint(label))); } + tile(blockSize: string, reference: Statement, useTernary: boolean = true): Statement { return wrapJoinPoint(this._javaObject.tile(unwrapJoinPoint(blockSize), unwrapJoinPoint(reference), unwrapJoinPoint(useTernary))); } } export class If extends Statement { @@ -2079,12 +1828,12 @@ export class If extends Statement { static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get cond(): Expression { return wrapJoinPoint(this._javaObject.getCond()) } + get cond(): Expression { return wrapJoinPoint(this._javaObject.cond()) } set cond(value: Expression) { this._javaObject.setCond(unwrapJoinPoint(value)); } - get condDecl(): Vardecl { return wrapJoinPoint(this._javaObject.getCondDecl()) } - get else(): Scope { return wrapJoinPoint(this._javaObject.getElse()) } + get condDecl(): Vardecl { return wrapJoinPoint(this._javaObject.condDecl()) } + get else(): Scope { return wrapJoinPoint(this._javaObject.else()) } set else(value: Statement) { this._javaObject.setElse(unwrapJoinPoint(value)); } - get then(): Scope { return wrapJoinPoint(this._javaObject.getThen()) } + get then(): Scope { return wrapJoinPoint(this._javaObject.then()) } set then(value: Statement) { this._javaObject.setThen(unwrapJoinPoint(value)); } /** * Sets the condition of the if @@ -2100,573 +1849,797 @@ export class If extends Statement { setThen(then: Statement): void { return wrapJoinPoint(this._javaObject.setThen(unwrapJoinPoint(then))); } } -export class IncompleteArrayType extends ArrayType { +export class WrapperStmt extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; + get content(): Joinpoint { return wrapJoinPoint(this._javaObject.content()) } + get kind(): WrapperStatementKind { return wrapJoinPoint(this._javaObject.kind()) } } -export class IntLiteral extends Literal { +export class ReturnStmt extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get value(): number { return wrapJoinPoint(this._javaObject.getValue()) } + get returnExpr(): Expression { return wrapJoinPoint(this._javaObject.returnExpr()) } } -export class LabelDecl extends NamedDecl { +export class Switch extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", + name: null, }; - get labelStmt(): LabelStmt { return wrapJoinPoint(this._javaObject.getLabelStmt()) } + /** + * The case statements inside this switch + */ + get cases(): Case[] { return wrapJoinPoint(this._javaObject.cases()) } + get condition(): Expression { return wrapJoinPoint(this._javaObject.condition()) } + /** + * The default case statement of this switch statement or undefined if it does not have a default case + */ + get getDefaultCase(): Case { return wrapJoinPoint(this._javaObject.getDefaultCase()) } + /** + * True if there is a default case in this switch statement, false otherwise + */ + get hasDefaultCase(): boolean { return wrapJoinPoint(this._javaObject.hasDefaultCase()) } } -export class LabelStmt extends Statement { +export class SwitchCase extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get decl(): LabelDecl { return wrapJoinPoint(this._javaObject.getDecl()) } - set decl(value: LabelDecl) { this._javaObject.setDecl(unwrapJoinPoint(value)); } - /** - * Sets the label of the label statement - */ - setDecl(label: LabelDecl): void { return wrapJoinPoint(this._javaObject.setDecl(unwrapJoinPoint(label))); } -} - -export class Loop extends Statement { +} + +export class Case extends SwitchCase { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "kind", + name: null, }; - get body(): Scope { return wrapJoinPoint(this._javaObject.getBody()) } - set body(value: Scope) { this._javaObject.setBody(unwrapJoinPoint(value)); } - /** - * The statement of the loop condition - */ - get cond(): Statement { return wrapJoinPoint(this._javaObject.getCond()) } - /** - * The statement of the loop condition - */ - set cond(value: string) { this._javaObject.setCond(unwrapJoinPoint(value)); } - get condRelation(): Relation { return wrapJoinPoint(this._javaObject.getCondRelation()) } - set condRelation(value: Relation) { this._javaObject.setCondRelation(unwrapJoinPoint(value)); } - get controlVar(): string { return wrapJoinPoint(this._javaObject.getControlVar()) } - get controlVarref(): Varref { return wrapJoinPoint(this._javaObject.getControlVarref()) } - /** - * The expression of the last value of the control variable (e.g. 'length' in 'i < length;') - */ - get endValue(): string { return wrapJoinPoint(this._javaObject.getEndValue()) } - /** - * The expression of the last value of the control variable (e.g. 'length' in 'i < length;') - */ - set endValue(value: string) { this._javaObject.setEndValue(unwrapJoinPoint(value)); } - /** - * True if the condition of the loop in the canonical format, and is one of: <, <=, >, >= - */ - get hasCondRelation(): boolean { return wrapJoinPoint(this._javaObject.getHasCondRelation()) } - /** - * Uniquely identifies the loop inside the program - */ - get id(): string { return wrapJoinPoint(this._javaObject.getId()) } - /** - * The statement of the loop initialization - */ - get init(): Statement { return wrapJoinPoint(this._javaObject.getInit()) } /** - * The statement of the loop initialization - */ - set init(value: string) { this._javaObject.setInit(unwrapJoinPoint(value)); } - /** - * The expression of the first value of the control variable (e.g. '0' in 'size_t i = 0;') - */ - get initValue(): string { return wrapJoinPoint(this._javaObject.getInitValue()) } - /** - * The expression of the first value of the control variable (e.g. '0' in 'size_t i = 0;') - */ - set initValue(value: string) { this._javaObject.setInitValue(unwrapJoinPoint(value)); } - get isInnermost(): boolean { return wrapJoinPoint(this._javaObject.getIsInnermost()) } - get isOutermost(): boolean { return wrapJoinPoint(this._javaObject.getIsOutermost()) } - get isParallel(): boolean { return wrapJoinPoint(this._javaObject.getIsParallel()) } - set isParallel(value: boolean) { this._javaObject.setIsParallel(unwrapJoinPoint(value)); } - get iterations(): number { return wrapJoinPoint(this._javaObject.getIterations()) } - get iterationsExpr(): Expression { return wrapJoinPoint(this._javaObject.getIterationsExpr()) } - get kind(): "for" | "while" | "dowhile" | "foreach" { return wrapJoinPoint(this._javaObject.getKind()) } - set kind(value: string) { this._javaObject.setKind(unwrapJoinPoint(value)); } - get nestedLevel(): number { return wrapJoinPoint(this._javaObject.getNestedLevel()) } - get rank(): number[] { return wrapJoinPoint(this._javaObject.getRank()) } - /** - * The statement of the loop step - */ - get step(): Statement { return wrapJoinPoint(this._javaObject.getStep()) } - /** - * The statement of the loop step - */ - set step(value: string) { this._javaObject.setStep(unwrapJoinPoint(value)); } - /** - * The expression of the iteration step - */ - get stepValue(): string { return wrapJoinPoint(this._javaObject.getStepValue()) } - /** - * Tests whether the loops are interchangeable. This is a conservative test. + * The instructions that are associated with this case in the source code. This does not represent what instructions are actually executed (e.g., if a case does not have a break, does not show instructions of the next case) */ - isInterchangeable(otherLoop: Loop): boolean { return wrapJoinPoint(this._javaObject.isInterchangeable(unwrapJoinPoint(otherLoop))); } + get instructions(): Statement[] { return wrapJoinPoint(this._javaObject.instructions()) } + get isDefault(): boolean { return wrapJoinPoint(this._javaObject.isDefault()) } /** - * Interchanges two for loops, if possible + * True if this case does not contain instructions (i.e., it is directly above another case), false otherwise */ - interchange(otherLoop: Loop): void { return wrapJoinPoint(this._javaObject.interchange(unwrapJoinPoint(otherLoop))); } + get isEmpty(): boolean { return wrapJoinPoint(this._javaObject.isEmpty()) } /** - * Sets the body of the loop + * The case statement that comes after this case, or undefined if there are no more case statements */ - setBody(body: Scope): void { return wrapJoinPoint(this._javaObject.setBody(unwrapJoinPoint(body))); } + get nextCase(): Case { return wrapJoinPoint(this._javaObject.nextCase()) } /** - * Sets the conditional statement of the loop. Works with loops of kind 'for' + * The first statement that is not a case that will be executed by this case statement */ - setCond(condCode: string): void { return wrapJoinPoint(this._javaObject.setCond(unwrapJoinPoint(condCode))); } + get nextInstruction(): Statement { return wrapJoinPoint(this._javaObject.nextInstruction()) } /** - * Changes the operator of a canonical condition, if possible. Supported operators: lt, le, gt, ge + * The values that the case statement will match. It can return zero (e.g., 'default:'), one (e.g., 'case 1:') or two (e.g., 'case 2...4:') expressions, depending on the format of the case */ - setCondRelation(operator: Relation): void { return wrapJoinPoint(this._javaObject.setCondRelation(unwrapJoinPoint(operator))); } + get values(): Expression[] { return wrapJoinPoint(this._javaObject.values()) } +} + +export class Default extends SwitchCase { /** - * Sets the end value of the loop. Works with loops of kind 'for' + * @internal */ - setEndValue(initCode: string): void { return wrapJoinPoint(this._javaObject.setEndValue(unwrapJoinPoint(initCode))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; +} + +export class DeclStmt extends Statement { /** - * Sets the init statement of the loop + * @internal */ - setInit(initCode: string): void { return wrapJoinPoint(this._javaObject.setInit(unwrapJoinPoint(initCode))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; /** - * Sets the init value of the loop. Works with loops of kind 'for' + * The declarations in this statement */ - setInitValue(initCode: string): void { return wrapJoinPoint(this._javaObject.setInitValue(unwrapJoinPoint(initCode))); } + get decls(): Decl[] { return wrapJoinPoint(this._javaObject.decls()) } +} + +export class ExprStmt extends Statement { /** - * Sets the attribute 'isParallel' of the loop + * @internal */ - setIsParallel(isParallel: boolean): void { return wrapJoinPoint(this._javaObject.setIsParallel(unwrapJoinPoint(isParallel))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; /** - * Sets the kind of the loop + * The expression join point associated to this exprStmt */ - setKind(kind: string): void { return wrapJoinPoint(this._javaObject.setKind(unwrapJoinPoint(kind))); } + get expr(): Expression { return wrapJoinPoint(this._javaObject.expr()) } +} + +export class GotoStmt extends Statement { /** - * Sets the step statement of the loop. Works with loops of kind 'for' + * @internal */ - setStep(stepCode: string): void { return wrapJoinPoint(this._javaObject.setStep(unwrapJoinPoint(stepCode))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get label(): LabelDecl { return wrapJoinPoint(this._javaObject.label()) } + set label(value: LabelDecl) { this._javaObject.setLabel(unwrapJoinPoint(value)); } /** - * Applies loop tiling to this loop. + * Sets the label of the goto */ - tile(blockSize: string, reference: Statement, useTernary: boolean = true): Statement { return wrapJoinPoint(this._javaObject.tile(unwrapJoinPoint(blockSize), unwrapJoinPoint(reference), unwrapJoinPoint(useTernary))); } + setLabel(label: LabelDecl): void { return wrapJoinPoint(this._javaObject.setLabel(unwrapJoinPoint(label))); } } - /** - * Special pragma that can be used to mark scopes (e.g., #pragma lara marker loop1) - */ -export class Marker extends Pragma { +export class LabelStmt extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "id", + name: null, }; + get decl(): LabelDecl { return wrapJoinPoint(this._javaObject.decl()) } + set decl(value: LabelDecl) { this._javaObject.setDecl(unwrapJoinPoint(value)); } /** - * A scope, associated with this marker + * Sets the label of the label statement */ - get contents(): Joinpoint { return wrapJoinPoint(this._javaObject.getContents()) } - get id(): string { return wrapJoinPoint(this._javaObject.getId()) } + setDecl(label: LabelDecl): void { return wrapJoinPoint(this._javaObject.setDecl(unwrapJoinPoint(label))); } } -export class MemberCall extends Call { +export class EmptyStmt extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", + name: null, }; - get base(): Expression { return wrapJoinPoint(this._javaObject.getBase()) } - get rootBase(): Expression { return wrapJoinPoint(this._javaObject.getRootBase()) } } +export class Continue extends Statement { /** - * Represents a C++ class method declaration or definition + * @internal */ -export class Method extends FunctionJp { + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; +} + +export class Break extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", + name: null, }; - get record(): Class { return wrapJoinPoint(this._javaObject.getRecord()) } /** - * Removes the of the method + * The enclosing statement related to this break. It should be either a loop or a switch statement. */ - removeRecord(): void { return wrapJoinPoint(this._javaObject.removeRecord()); } + get enclosingStmt(): Statement { return wrapJoinPoint(this._javaObject.enclosingStmt()) } } - /** - * Represents an OpenMP pragma (e.g., #pragma omp parallel) - */ -export class Omp extends Pragma { +export class AsmStmt extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "kind", + name: null, }; + get clobbers(): string[] { return wrapJoinPoint(this._javaObject.clobbers()) } + get isSimple(): boolean { return wrapJoinPoint(this._javaObject.isSimple()) } + get isVolatile(): boolean { return wrapJoinPoint(this._javaObject.isVolatile()) } +} + +export class Expression extends Joinpoint { /** - * The names of the kinds of all clauses in the pragma, or empty array if no clause is defined + * @internal */ - get clauseKinds(): string[] { return wrapJoinPoint(this._javaObject.getClauseKinds()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; /** - * An integer expression, or undefined if no 'collapse' clause is defined + * A 'decl' join point that represents the declaration associated with this expression, or undefined if there is none */ - get collapse(): string { return wrapJoinPoint(this._javaObject.getCollapse()) } + get decl(): Decl { return wrapJoinPoint(this._javaObject.decl()) } /** - * An integer expression, or undefined if no 'collapse' clause is defined + * Returns a cast joinpoint if this expression has an associated implicit cast, undefined otherwise */ - set collapse(value: string | number) { this._javaObject.setCollapse(unwrapJoinPoint(value)); } + get implicitCast(): Cast { return wrapJoinPoint(this._javaObject.implicitCast()) } /** - * The variable names of all copyin clauses, or empty array if no copyin clause is defined + * True if the expression is part of an argument of a function call */ - get copyin(): string[] { return wrapJoinPoint(this._javaObject.getCopyin()) } + get isFunctionArgument(): boolean { return wrapJoinPoint(this._javaObject.isFunctionArgument()) } + get use(): ExpressionUse { return wrapJoinPoint(this._javaObject.use()) } /** - * The variable names of all copyin clauses, or empty array if no copyin clause is defined + * A 'vardecl' join point that represents the variable declaration associated with this expression, or undefined if there is none */ - set copyin(value: string[]) { this._javaObject.setCopyin(unwrapJoinPoint(value)); } + get vardecl(): Vardecl { return wrapJoinPoint(this._javaObject.vardecl()) } +} + +export class Call extends Expression { /** - * One of 'shared' or 'none', or undefined if no 'default' clause is defined + * @internal */ - get default(): string { return wrapJoinPoint(this._javaObject.getDefault()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; /** - * One of 'shared' or 'none', or undefined if no 'default' clause is defined + * An alias for 'args' + * + * @deprecated */ - set default(value: string) { this._javaObject.setDefault(unwrapJoinPoint(value)); } + get argList(): Expression[] { return wrapJoinPoint(this._javaObject.argList()) } /** - * The variable names of all firstprivate clauses, or empty array if no firstprivate clause is defined + * An array with the arguments of the call */ - get firstprivate(): string[] { return wrapJoinPoint(this._javaObject.getFirstprivate()) } + get args(): Expression[] { return wrapJoinPoint(this._javaObject.args()) } /** - * The variable names of all firstprivate clauses, or empty array if no firstprivate clause is defined + * A 'function' join point that represents the function of the call that was found, it can return either an implementation or a function prototype; 'undefined' if no declaration was found */ - set firstprivate(value: string[]) { this._javaObject.setFirstprivate(unwrapJoinPoint(value)); } + get declaration(): FunctionJp { return wrapJoinPoint(this._javaObject.declaration()) } /** - * The kind of the directive + * A 'function' join point that represents the function definition of the call; 'undefined' if no definition was found */ - get kind(): string { return wrapJoinPoint(this._javaObject.getKind()) } + get definition(): FunctionJp { return wrapJoinPoint(this._javaObject.definition()) } /** - * The kind of the directive + * A function join point that represents the 'raw' function of the call (e.g. if this is a call to a templated function, returns a declaration representing the template specialization, instead of the original function) */ - set kind(value: string) { this._javaObject.setKind(unwrapJoinPoint(value)); } + get directCallee(): FunctionJp { return wrapJoinPoint(this._javaObject.directCallee()) } /** - * The variable names of all lastprivate clauses, or empty array if no lastprivate clause is defined + * A function join point associated with this call. If a definition is present, it is given priority over returning a declaration. If only declarations are present, returns a declaration */ - get lastprivate(): string[] { return wrapJoinPoint(this._javaObject.getLastprivate()) } + get function(): FunctionJp { return wrapJoinPoint(this._javaObject.function()) } /** - * The variable names of all lastprivate clauses, or empty array if no lastprivate clause is defined + * The function type of the call, which includes the return type and the types of the parameters */ - set lastprivate(value: string[]) { this._javaObject.setLastprivate(unwrapJoinPoint(value)); } + get functionType(): FunctionType { return wrapJoinPoint(this._javaObject.functionType()) } + get isMemberAccess(): boolean { return wrapJoinPoint(this._javaObject.isMemberAccess()) } + get isStmtCall(): boolean { return wrapJoinPoint(this._javaObject.isStmtCall()) } + get memberAccess(): MemberAccess { return wrapJoinPoint(this._javaObject.memberAccess()) } + get memberNames(): string[] { return wrapJoinPoint(this._javaObject.memberNames()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } + set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } + get numArgs(): number { return wrapJoinPoint(this._javaObject.numArgs()) } /** - * An integer expression, or undefined if no 'num_threads' clause is defined + * The return type of the call */ - get numThreads(): string { return wrapJoinPoint(this._javaObject.getNumThreads()) } + get returnType(): Type { return wrapJoinPoint(this._javaObject.returnType()) } /** - * An integer expression, or undefined if no 'num_threads' clause is defined + * Similar to $function.signature, but if no function decl could be found (e.g., function from system include), returns a signature based on just the name of the function */ - set numThreads(value: string) { this._javaObject.setNumThreads(unwrapJoinPoint(value)); } + get signature(): string { return wrapJoinPoint(this._javaObject.signature()) } /** - * An integer expression, or undefined if no 'ordered' clause with a parameter is defined + * Adds an argument at the end of the call, creating an expression using the given code and type. If a type is not provided, a dummy type is used */ - get ordered(): string { return wrapJoinPoint(this._javaObject.getOrdered()) } + addArg(argCode: string, type?: Type): void; /** - * An integer expression, or undefined if no 'ordered' clause with a parameter is defined + * Adds an argument at the end of the call, creating a literal 'type' from the type string */ - set ordered(value: string) { this._javaObject.setOrdered(unwrapJoinPoint(value)); } + addArg(arg: string, type: string): void; /** - * The variable names of all private clauses, or empty array if no private clause is defined + * Adds an argument at the end of the call, creating an expression using the given code and type. If a type is not provided, a dummy type is used */ - get private(): string[] { return wrapJoinPoint(this._javaObject.getPrivate()) } + addArg(p1: string, p2?: Type | string): void { return wrapJoinPoint(this._javaObject.addArg(unwrapJoinPoint(p1), unwrapJoinPoint(p2))); } + getArg(index: number): Expression { return wrapJoinPoint(this._javaObject.getArg(unwrapJoinPoint(index))); } /** - * The variable names of all private clauses, or empty array if no private clause is defined + * Tries to inline this call */ - set private(value: string[]) { this._javaObject.setPrivate(unwrapJoinPoint(value)); } + inline(): boolean { return wrapJoinPoint(this._javaObject.inline()); } + setArg(index: number, expr: Expression): void { return wrapJoinPoint(this._javaObject.setArg(unwrapJoinPoint(index), unwrapJoinPoint(expr))); } + setArgFromString(index: number, expr: string): void { return wrapJoinPoint(this._javaObject.setArgFromString(unwrapJoinPoint(index), unwrapJoinPoint(expr))); } /** - * One of 'master', 'close' or 'spread', or undefined if no 'proc_bind' clause is defined + * Changes the name of the call */ - get procBind(): string { return wrapJoinPoint(this._javaObject.getProcBind()) } + setName(name: string): void { return wrapJoinPoint(this._javaObject.setName(unwrapJoinPoint(name))); } /** - * One of 'master', 'close' or 'spread', or undefined if no 'proc_bind' clause is defined + * Wraps this call with a possibly new wrapping function */ - set procBind(value: string) { this._javaObject.setProcBind(unwrapJoinPoint(value)); } + wrap(name: string): void { return wrapJoinPoint(this._javaObject.wrap(unwrapJoinPoint(name))); } +} + +export class MemberCall extends Call { /** - * The reduction kinds in the reductions clauses of the this pragma, or empty array if no reduction is defined + * @internal */ - get reductionKinds(): string[] { return wrapJoinPoint(this._javaObject.getReductionKinds()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; + get base(): Expression { return wrapJoinPoint(this._javaObject.base()) } + get rootBase(): Expression { return wrapJoinPoint(this._javaObject.rootBase()) } +} + +export class CudaKernelCall extends Call { /** - * An integer expression, or undefined if no 'schedule' clause with chunk size is defined + * @internal */ - get scheduleChunkSize(): string { return wrapJoinPoint(this._javaObject.getScheduleChunkSize()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; + get config(): Expression[] { return wrapJoinPoint(this._javaObject.config()) } + set config(value: Expression[]) { this._javaObject.setConfig(unwrapJoinPoint(value)); } + setConfig(args: Expression[]): void { return wrapJoinPoint(this._javaObject.setConfig(unwrapJoinPoint(args))); } + setConfigFromStrings(args: string[]): void { return wrapJoinPoint(this._javaObject.setConfigFromStrings(unwrapJoinPoint(args))); } +} + +export class Op extends Expression { /** - * An integer expression, or undefined if no 'schedule' clause with chunk size is defined + * @internal */ - set scheduleChunkSize(value: string | number) { this._javaObject.setScheduleChunkSize(unwrapJoinPoint(value)); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get isBitwise(): boolean { return wrapJoinPoint(this._javaObject.isBitwise()) } /** - * One of 'static', 'dynamic', 'guided', 'auto' or 'runtime', or undefined if no 'schedule' clause is defined + * The kind of the operator. If it is a binary operator, can be one of: ptr_mem_d, ptr_mem_i, mul, div, rem, add, sub, shl, shr, cmp, lt, gt, le, ge, eq, ne, and, xor, or, l_and, l_or, assign, mul_assign, div_assign, rem_assign, add_assign, sub_assign, shl_assign, shr_assign, and_assign, xor_assign, or_assign, comma. If it is a unary operator, can be one of: post_inc, post_dec, pre_inc, pre_dec, addr_of, deref, plus, minus, not, l_not, real, imag, extension, cowait. If it is a ternary operator, the value will be 'ternary' */ - get scheduleKind(): string { return wrapJoinPoint(this._javaObject.getScheduleKind()) } + get kind(): OpKind { return wrapJoinPoint(this._javaObject.kind()) } + get operator(): string { return wrapJoinPoint(this._javaObject.operator()) } +} + +export class BinaryOp extends Op { /** - * One of 'static', 'dynamic', 'guided', 'auto' or 'runtime', or undefined if no 'schedule' clause is defined + * @internal */ - set scheduleKind(value: string) { this._javaObject.setScheduleKind(unwrapJoinPoint(value)); } - /** - * A list with possible values of 'monotonic', 'nonmonotonic' or 'simd', or undefined if no 'schedule' clause with modifiers is defined + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get isAssignment(): boolean { return wrapJoinPoint(this._javaObject.isAssignment()) } + get left(): Expression { return wrapJoinPoint(this._javaObject.left()) } + set left(value: Expression) { this._javaObject.setLeft(unwrapJoinPoint(value)); } + get right(): Expression { return wrapJoinPoint(this._javaObject.right()) } + set right(value: Expression) { this._javaObject.setRight(unwrapJoinPoint(value)); } + setLeft(left: Expression): void { return wrapJoinPoint(this._javaObject.setLeft(unwrapJoinPoint(left))); } + setRight(right: Expression): void { return wrapJoinPoint(this._javaObject.setRight(unwrapJoinPoint(right))); } +} + +export class UnaryOp extends Op { + /** + * @internal */ - get scheduleModifiers(): string[] { return wrapJoinPoint(this._javaObject.getScheduleModifiers()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get isPointerDeref(): boolean { return wrapJoinPoint(this._javaObject.isPointerDeref()) } + get operand(): Expression { return wrapJoinPoint(this._javaObject.operand()) } +} + +export class TernaryOp extends Op { /** - * A list with possible values of 'monotonic', 'nonmonotonic' or 'simd', or undefined if no 'schedule' clause with modifiers is defined + * @internal */ - set scheduleModifiers(value: string[]) { this._javaObject.setScheduleModifiers(unwrapJoinPoint(value)); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get cond(): Expression { return wrapJoinPoint(this._javaObject.cond()) } + get falseExpr(): Expression { return wrapJoinPoint(this._javaObject.falseExpr()) } + get trueExpr(): Expression { return wrapJoinPoint(this._javaObject.trueExpr()) } +} + +export class NewExpr extends Expression { /** - * The variable names of all shared clauses, or empty array if no shared clause is defined + * @internal */ - get shared(): string[] { return wrapJoinPoint(this._javaObject.getShared()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; +} + +export class DeleteExpr extends Expression { /** - * The variable names of all shared clauses, or empty array if no shared clause is defined + * @internal */ - set shared(value: string[]) { this._javaObject.setShared(unwrapJoinPoint(value)); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; +} + /** - * The variable names for the given reduction kind, or empty array if no reduction of that kind is defined + * A reference to a variable */ - getReduction(kind: string): string[] { return wrapJoinPoint(this._javaObject.getReduction(unwrapJoinPoint(kind))); } +export class Varref extends Expression { /** - * True if the directive has at least one clause of the given clause kind, false otherwise + * @internal */ - hasClause(clauseName: string): boolean { return wrapJoinPoint(this._javaObject.hasClause(unwrapJoinPoint(clauseName))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; + get declaration(): Declarator { return wrapJoinPoint(this._javaObject.declaration()) } /** - * True if it is legal to use the given clause kind in this directive, false otherwise + * True if this variable reference has a MS-style property, false otherwise */ - isClauseLegal(clauseName: string): boolean { return wrapJoinPoint(this._javaObject.isClauseLegal(unwrapJoinPoint(clauseName))); } + get hasProperty(): boolean { return wrapJoinPoint(this._javaObject.hasProperty()) } /** - * Removes any clause of the given kind from the OpenMP pragma + * True if this varref represents a function call */ - removeClause(clauseKind: string): void { return wrapJoinPoint(this._javaObject.removeClause(unwrapJoinPoint(clauseKind))); } + get isFunctionCall(): boolean { return wrapJoinPoint(this._javaObject.isFunctionCall()) } + get kind(): string { return wrapJoinPoint(this._javaObject.kind()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } + set name(value: string) { this._javaObject.setName(unwrapJoinPoint(value)); } /** - * Sets the value of the collapse clause of an OpenMP pragma + * If this variable reference has a MS-style property, returns the property name. Returns undefined otherwise */ - setCollapse(newExpr: string): void; + get property(): string { return wrapJoinPoint(this._javaObject.property()) } /** - * Sets the value of the collapse clause of an OpenMP pragma + * Expression from where the attribute 'use' is calculated. In certain cases (e.g., array access, pointer dereference) the 'use' attribute is not calculated on the node itself, but on an ancestor of the node. This attribute returns that node */ - setCollapse(newExpr: number): void; + get useExpr(): Expression { return wrapJoinPoint(this._javaObject.useExpr()) } + setName(name: string): void { return wrapJoinPoint(this._javaObject.setName(unwrapJoinPoint(name))); } +} + +export class Cast extends Expression { /** - * Sets the value of the collapse clause of an OpenMP pragma + * @internal */ - setCollapse(p1: string | number): void { return wrapJoinPoint(this._javaObject.setCollapse(unwrapJoinPoint(p1))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get fromType(): Type { return wrapJoinPoint(this._javaObject.fromType()) } /** - * Sets the variables of a copyin clause of an OpenMP pragma + * @deprecated Use expr.implicitCast instead */ - setCopyin(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setCopyin(unwrapJoinPoint(newVariables))); } + get isImplicitCast(): boolean { return wrapJoinPoint(this._javaObject.isImplicitCast()) } + get subExpr(): Expression { return wrapJoinPoint(this._javaObject.subExpr()) } + get toType(): Type { return wrapJoinPoint(this._javaObject.toType()) } +} + +export class ParenExpr extends Expression { /** - * Sets the value of the default clause of an OpenMP pragma + * @internal */ - setDefault(newDefault: string): void { return wrapJoinPoint(this._javaObject.setDefault(unwrapJoinPoint(newDefault))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; /** - * Sets the variables of a firstprivate clause of an OpenMP pragma + * Returns the expression inside this parenthesis expression */ - setFirstprivate(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setFirstprivate(unwrapJoinPoint(newVariables))); } + get subExpr(): Expression { return wrapJoinPoint(this._javaObject.subExpr()) } +} + +export class ArrayAccess extends Expression { /** - * Sets the directive kind of the OpenMP pragma. Any unsupported clauses will be discarded + * @internal */ - setKind(directiveKind: string): void { return wrapJoinPoint(this._javaObject.setKind(unwrapJoinPoint(directiveKind))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; /** - * Sets the variables of a lastprivate clause of an OpenMP pragma + * Expression representing the variable of the array access (can be a varref, memberAccess...) */ - setLastprivate(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setLastprivate(unwrapJoinPoint(newVariables))); } + get arrayVar(): Expression { return wrapJoinPoint(this._javaObject.arrayVar()) } /** - * Sets the value of the num_threads clause of an OpenMP pragma + * If the array access is done over a variable, returns the name of the variable. Equivalent to $arrayAccess.arrayVar.name */ - setNumThreads(newExpr: string): void { return wrapJoinPoint(this._javaObject.setNumThreads(unwrapJoinPoint(newExpr))); } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } /** - * Sets the value of the ordered clause of an OpenMP pragma + * The number of subscripts of this array access */ - setOrdered(parameters?: string): void { return wrapJoinPoint(this._javaObject.setOrdered(unwrapJoinPoint(parameters))); } + get numSubscripts(): number { return wrapJoinPoint(this._javaObject.numSubscripts()) } /** - * Sets the variables of a private clause of an OpenMP pragma + * A view of the current arrayAccess without the last subscript, or undefined if this arrayAccess only has one subscript */ - setPrivate(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setPrivate(unwrapJoinPoint(newVariables))); } + get parentAccess(): ArrayAccess { return wrapJoinPoint(this._javaObject.parentAccess()) } /** - * Sets the value of the proc_bind clause of an OpenMP pragma + * Expression of the array access subscript */ - setProcBind(newBind: string): void { return wrapJoinPoint(this._javaObject.setProcBind(unwrapJoinPoint(newBind))); } + get subscript(): Expression[] { return wrapJoinPoint(this._javaObject.subscript()) } +} + +export class MemberAccess extends Expression { /** - * Sets the variables for a given kind of a reduction clause of an OpenMP pragma + * @internal */ - setReduction(kind: string, newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setReduction(unwrapJoinPoint(kind), unwrapJoinPoint(newVariables))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; /** - * Sets the value of the chunck size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception + * True if this is a member access that uses arrow (i.e., foo->bar), false if uses dot (i.e., foo.bar) */ - setScheduleChunkSize(chunkSize: string): void; + get arrow(): boolean { return wrapJoinPoint(this._javaObject.arrow()) } /** - * Sets the value of the chunck size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception + * True if this is a member access that uses arrow (i.e., foo->bar), false if uses dot (i.e., foo.bar) */ - setScheduleChunkSize(chunkSize: number): void; + set arrow(value: boolean) { this._javaObject.setArrow(unwrapJoinPoint(value)); } /** - * Sets the value of the chunck size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception + * Expression of the base of this member access */ - setScheduleChunkSize(p1: string | number): void { return wrapJoinPoint(this._javaObject.setScheduleChunkSize(unwrapJoinPoint(p1))); } + get base(): Expression { return wrapJoinPoint(this._javaObject.base()) } + get memberChain(): Expression[] { return wrapJoinPoint(this._javaObject.memberChain()) } + get memberChainNames(): string[] { return wrapJoinPoint(this._javaObject.memberChainNames()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } + setArrow(isArrow: boolean): void { return wrapJoinPoint(this._javaObject.setArrow(unwrapJoinPoint(isArrow))); } +} + +export class UnaryExprOrType extends Expression { /** - * Sets the value of the schedule clause of an OpenMP pragma + * @internal */ - setScheduleKind(scheduleKind: string): void { return wrapJoinPoint(this._javaObject.setScheduleKind(unwrapJoinPoint(scheduleKind))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get argExpr(): Expression { return wrapJoinPoint(this._javaObject.argExpr()) } + get argType(): Type { return wrapJoinPoint(this._javaObject.argType()) } + set argType(value: Type) { this._javaObject.setArgType(unwrapJoinPoint(value)); } + get hasArgExpr(): boolean { return wrapJoinPoint(this._javaObject.hasArgExpr()) } + get hasTypeExpr(): boolean { return wrapJoinPoint(this._javaObject.hasTypeExpr()) } + get kind(): string { return wrapJoinPoint(this._javaObject.kind()) } + setArgType(argType: Type): void { return wrapJoinPoint(this._javaObject.setArgType(unwrapJoinPoint(argType))); } +} + +export class This extends Expression { /** - * Sets the value of the modifiers in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception + * @internal */ - setScheduleModifiers(modifiers: string[]): void { return wrapJoinPoint(this._javaObject.setScheduleModifiers(unwrapJoinPoint(modifiers))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; +} + +export class Literal extends Expression { /** - * Sets the variables of a shared clause of an OpenMP pragma + * @internal */ - setShared(newVariables: string[]): void { return wrapJoinPoint(this._javaObject.setShared(unwrapJoinPoint(newVariables))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; } -export class ParenType extends Type { +export class IntLiteral extends Literal { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get innerType(): Type { return wrapJoinPoint(this._javaObject.getInnerType()) } - set innerType(value: Type) { this._javaObject.setInnerType(unwrapJoinPoint(value)); } + get value(): number { return wrapJoinPoint(this._javaObject.value()) } +} + +export class FloatLiteral extends Literal { /** - * Sets the inner type of this paren type + * @internal */ - setInnerType(innerType: Type): void { return wrapJoinPoint(this._javaObject.setInnerType(unwrapJoinPoint(innerType))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get value(): number { return wrapJoinPoint(this._javaObject.value()) } } -export class PointerType extends Type { +export class BoolLiteral extends Literal { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get pointee(): Type { return wrapJoinPoint(this._javaObject.getPointee()) } - set pointee(value: Type) { this._javaObject.setPointee(unwrapJoinPoint(value)); } + get value(): boolean { return wrapJoinPoint(this._javaObject.value()) } +} + +export class InitList extends Expression { /** - * Number of pointer levels from this pointer + * @internal */ - get pointerLevels(): number { return wrapJoinPoint(this._javaObject.getPointerLevels()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; /** - * Sets the pointee type of this pointer type + * [May be undefined] If this initializer list initializes an array with more elements than there are initializers in the list, specifies an expression to be used for value initialization of the rest of the elements */ - setPointee(pointeeType: Type): void { return wrapJoinPoint(this._javaObject.setPointee(unwrapJoinPoint(pointeeType))); } + get arrayFiller(): Expression { return wrapJoinPoint(this._javaObject.arrayFiller()) } } -export class QualType extends Type { +export class ImplicitValue extends Expression { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get qualifiers(): string[] { return wrapJoinPoint(this._javaObject.getQualifiers()) } - get unqualifiedType(): Type { return wrapJoinPoint(this._javaObject.getUnqualifiedType()) } } -export class ReturnStmt extends Statement { +export class Comment extends Joinpoint { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get returnExpr(): Expression { return wrapJoinPoint(this._javaObject.getReturnExpr()) } + get text(): string { return wrapJoinPoint(this._javaObject.text()) } + set text(value: string) { this._javaObject.setText(unwrapJoinPoint(value)); } + setText(text: string): void { return wrapJoinPoint(this._javaObject.setText(unwrapJoinPoint(text))); } } +export class CilkFor extends Loop { /** - * Represents a group of statements + * @internal */ -export class Scope extends Statement { + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "kind", + }; +} + +export class CilkSync extends Statement { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; +} + +export class CilkSpawn extends Call { /** - * Returns the descendant statements of this scope, excluding other scopes, loops, ifs and wrapper statements + * @internal */ - get allStmts(): Statement[] { return wrapJoinPoint(this._javaObject.getAllStmts()) } - get firstStmt(): Statement { return wrapJoinPoint(this._javaObject.getFirstStmt()) } - get lastStmt(): Statement { return wrapJoinPoint(this._javaObject.getLastStmt()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: "name", + }; +} + +export class Attribute extends Joinpoint { /** - * True if the scope does not have curly braces + * @internal */ - get naked(): boolean { return wrapJoinPoint(this._javaObject.getNaked()) } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get kind(): string { return wrapJoinPoint(this._javaObject.kind()) } +} + +export class Type extends Joinpoint { /** - * True if the scope does not have curly braces + * @internal */ - set naked(value: boolean) { this._javaObject.setNaked(unwrapJoinPoint(value)); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get arrayDims(): number[] { return wrapJoinPoint(this._javaObject.arrayDims()) } + get arraySize(): number { return wrapJoinPoint(this._javaObject.arraySize()) } + get constant(): boolean { return wrapJoinPoint(this._javaObject.constant()) } /** - * The statement that owns the scope (e.g., function, loop...) + * Single-step desugar. Returns the type itself if it does not have sugar */ - get owner(): Joinpoint { return wrapJoinPoint(this._javaObject.getOwner()) } + get desugar(): Type { return wrapJoinPoint(this._javaObject.desugar()) } /** - * Returns the direct (children) statements of this scope + * Single-step desugar. Returns the type itself if it does not have sugar */ - get stmts(): Statement[] { return wrapJoinPoint(this._javaObject.getStmts()) } + set desugar(value: Type) { this._javaObject.setDesugar(unwrapJoinPoint(value)); } /** - * The number of statements in the scope, including the statements inside the declaration and bodies of structures such as ifs and loops, and not considering comments and pragmas. If flat is true, does not consider the statements inside structures such as ifs and loops (e.g., a loop counts as one statement) + * Completely desugars the type */ - getNumStatements(flat: boolean = false): number { return wrapJoinPoint(this._javaObject.getNumStatements(unwrapJoinPoint(flat))); } + get desugarAll(): Type { return wrapJoinPoint(this._javaObject.desugarAll()) } /** - * Adds a new local variable to this scope + * A tree representation of the fields of this type */ - addLocal(name: string, type: Joinpoint, initValue?: string): Joinpoint { return wrapJoinPoint(this._javaObject.addLocal(unwrapJoinPoint(name), unwrapJoinPoint(type), unwrapJoinPoint(initValue))); } + get fieldTree(): string { return wrapJoinPoint(this._javaObject.fieldTree()) } + get hasSugar(): boolean { return wrapJoinPoint(this._javaObject.hasSugar()) } + get hasTemplateArgs(): boolean { return wrapJoinPoint(this._javaObject.hasTemplateArgs()) } + get isArray(): boolean { return wrapJoinPoint(this._javaObject.isArray()) } /** - * CFG tester + * True if this is a type declared with the 'auto' keyword */ - cfg(): string { return wrapJoinPoint(this._javaObject.cfg()); } + get isAuto(): boolean { return wrapJoinPoint(this._javaObject.isAuto()) } + get isBuiltin(): boolean { return wrapJoinPoint(this._javaObject.isBuiltin()) } + get isPointer(): boolean { return wrapJoinPoint(this._javaObject.isPointer()) } + get isTopLevel(): boolean { return wrapJoinPoint(this._javaObject.isTopLevel()) } + get kind(): string { return wrapJoinPoint(this._javaObject.kind()) } /** - * Clears the contents of this scope (untested) + * Ignores certain types (e.g., DecayedType) */ - clear(): void { return wrapJoinPoint(this._javaObject.clear()); } + get normalize(): Type { return wrapJoinPoint(this._javaObject.normalize()) } + get templateArgsStrings(): string[] { return wrapJoinPoint(this._javaObject.templateArgsStrings()) } + get templateArgsTypes(): Type[] { return wrapJoinPoint(this._javaObject.templateArgsTypes()) } + set templateArgsTypes(value: Type[]) { this._javaObject.setTemplateArgsTypes(unwrapJoinPoint(value)); } /** - * DFG tester + * Maps names of join point fields that represent type join points, to their respective values */ - dfg(): string { return wrapJoinPoint(this._javaObject.dfg()); } - insertBegin(node: Joinpoint): Joinpoint; - insertBegin(code: string): Joinpoint; - insertBegin(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertBegin(unwrapJoinPoint(p1))); } - insertEnd(node: Joinpoint): Joinpoint; - insertEnd(code: string): Joinpoint; - insertEnd(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertEnd(unwrapJoinPoint(p1))); } + get typeFields(): Record { return wrapJoinPoint(this._javaObject.typeFields()) } /** - * Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node + * If the type encapsulates another type, returns the encapsulated type */ - insertReturn(code: Joinpoint): Joinpoint; + get unwrap(): Type { return wrapJoinPoint(this._javaObject.unwrap()) } /** - * Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node + * Returns a new node based on this type with the qualifier const */ - insertReturn(code: string): Joinpoint; + asConst(): Type { return wrapJoinPoint(this._javaObject.asConst()); } /** - * Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node + * Sets the desugared type of this type */ - insertReturn(p1: Joinpoint | string): Joinpoint { return wrapJoinPoint(this._javaObject.insertReturn(unwrapJoinPoint(p1))); } + setDesugar(desugaredType: Type): void { return wrapJoinPoint(this._javaObject.setDesugar(unwrapJoinPoint(desugaredType))); } /** - * Sets the 'naked' status of a scope (a scope is naked if it does not have curly braces) + * Sets the template argument types of a template type + */ + setTemplateArgsTypes(templateArgTypes: Type[]): void { return wrapJoinPoint(this._javaObject.setTemplateArgsTypes(unwrapJoinPoint(templateArgTypes))); } + /** + * Sets a single template argument type of a template type + */ + setTemplateArgType(index: number, templateArgType: Type): void { return wrapJoinPoint(this._javaObject.setTemplateArgType(unwrapJoinPoint(index), unwrapJoinPoint(templateArgType))); } + /** + * Changes a single occurrence of a type field that has the current value with new value. Returns true if there was a change + */ + setTypeFieldByValueRecursive(currentValue: object, newValue: object): boolean { return wrapJoinPoint(this._javaObject.setTypeFieldByValueRecursive(unwrapJoinPoint(currentValue), unwrapJoinPoint(newValue))); } + /** + * Replaces an underlying type of this instance with new type, if it matches the old type + */ + setUnderlyingType(oldValue: Type, newValue: Type): Type { return wrapJoinPoint(this._javaObject.setUnderlyingType(unwrapJoinPoint(oldValue), unwrapJoinPoint(newValue))); } +} + +export class PointerType extends Type { + /** + * @internal + */ + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get pointee(): Type { return wrapJoinPoint(this._javaObject.pointee()) } + set pointee(value: Type) { this._javaObject.setPointee(unwrapJoinPoint(value)); } + /** + * Number of pointer levels from this pointer + */ + get pointerLevels(): number { return wrapJoinPoint(this._javaObject.pointerLevels()) } + /** + * Sets the pointee type of this pointer type + */ + setPointee(pointeeType: Type): void { return wrapJoinPoint(this._javaObject.setPointee(unwrapJoinPoint(pointeeType))); } +} + +export class ArrayType extends Type { + /** + * @internal + */ + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get elementType(): Type { return wrapJoinPoint(this._javaObject.elementType()) } + set elementType(value: Type) { this._javaObject.setElementType(unwrapJoinPoint(value)); } + /** + * Sets the element type of the array + */ + setElementType(arrayElementType: Type): void { return wrapJoinPoint(this._javaObject.setElementType(unwrapJoinPoint(arrayElementType))); } +} + +export class AdjustedType extends Type { + /** + * @internal + */ + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + /** + * The type that is being adjusted + */ + get originalType(): Type { return wrapJoinPoint(this._javaObject.originalType()) } +} + +export class VariableArrayType extends ArrayType { + /** + * @internal + */ + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get sizeExpr(): Expression { return wrapJoinPoint(this._javaObject.sizeExpr()) } + set sizeExpr(value: Expression) { this._javaObject.setSizeExpr(unwrapJoinPoint(value)); } + /** + * Sets the size expression of this variable array type + */ + setSizeExpr(sizeExpr: Expression): void { return wrapJoinPoint(this._javaObject.setSizeExpr(unwrapJoinPoint(sizeExpr))); } +} + +export class IncompleteArrayType extends ArrayType { + /** + * @internal */ - setNaked(isNaked: boolean): void { return wrapJoinPoint(this._javaObject.setNaked(unwrapJoinPoint(isNaked))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; } export class TagType extends Type { @@ -2679,123 +2652,110 @@ export class TagType extends Type { /** * A 'decl' join point that represents the declaration of this tag type */ - get decl(): Decl { return wrapJoinPoint(this._javaObject.getDecl()) } - get name(): string { return wrapJoinPoint(this._javaObject.getName()) } + get decl(): Decl { return wrapJoinPoint(this._javaObject.decl()) } + get name(): string { return wrapJoinPoint(this._javaObject.name()) } } -export class TemplateSpecializationType extends Type { +export class EnumType extends TagType { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get args(): string[] { return wrapJoinPoint(this._javaObject.getArgs()) } - get firstArgType(): Type { return wrapJoinPoint(this._javaObject.getFirstArgType()) } - get numArgs(): number { return wrapJoinPoint(this._javaObject.getNumArgs()) } - get templateName(): string { return wrapJoinPoint(this._javaObject.getTemplateName()) } + get integerType(): Type { return wrapJoinPoint(this._javaObject.integerType()) } } - /** - * Declaration of a typedef-name via the 'typedef' type specifier - */ -export class TypedefDecl extends TypedefNameDecl { +export class TemplateSpecializationType extends Type { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", + name: null, }; + get args(): string[] { return wrapJoinPoint(this._javaObject.args()) } + get firstArgType(): Type { return wrapJoinPoint(this._javaObject.firstArgType()) } + get numArgs(): number { return wrapJoinPoint(this._javaObject.numArgs()) } + get templateName(): string { return wrapJoinPoint(this._javaObject.templateName()) } } - /** - * Represents a variable declaration or definition - */ -export class Vardecl extends Declarator { +export class FunctionType extends Type { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", + name: null, }; + get paramTypes(): Type[] { return wrapJoinPoint(this._javaObject.paramTypes()) } + get returnType(): Type { return wrapJoinPoint(this._javaObject.returnType()) } + set returnType(value: Type) { this._javaObject.setReturnType(unwrapJoinPoint(value)); } /** - * The vardecl corresponding to the actual definition. For global variables, returns the vardecl of the file where it is actually defined (instead of the vardecl that defines an external link to the variable) - */ - get definition(): Vardecl { return wrapJoinPoint(this._javaObject.getDefinition()) } - /** - * True, if vardecl has an initialization value - */ - get hasInit(): boolean { return wrapJoinPoint(this._javaObject.getHasInit()) } - /** - * If vardecl has an initialization value, returns an expression with that value - */ - get init(): Expression { return wrapJoinPoint(this._javaObject.getInit()) } - /** - * If vardecl has an initialization value, returns an expression with that value - */ - set init(value: Expression | string) { this._javaObject.setInit(unwrapJoinPoint(value)); } - /** - * The initialization style of this vardecl, which can be no_init, cinit, callinit, listinit - */ - get initStyle(): string { return wrapJoinPoint(this._javaObject.getInitStyle()) } - /** - * True, if this variable does not have local storage. This includes all global variables as well as static variables declared within a function. - */ - get isGlobal(): boolean { return wrapJoinPoint(this._javaObject.getIsGlobal()) } - /** - * True, if vardecl is a function parameter + * Sets the type of a parameter of the FunctionType. Be careful that if you directly change the type of a parameter and the function type is associated with a function declaration, this change will not be reflected in the function. If you want to change the type of a parameter of a function declaration, use function.setParamType */ - get isParam(): boolean { return wrapJoinPoint(this._javaObject.getIsParam()) } + setParamType(index: number, newType: Type): void { return wrapJoinPoint(this._javaObject.setParamType(unwrapJoinPoint(index), unwrapJoinPoint(newType))); } /** - * Storage class specifier, which can be none, extern, static, __private_extern__, auto, register + * Sets the return type of the FunctionType */ - get storageClass(): StorageClass { return wrapJoinPoint(this._javaObject.getStorageClass()) } + setReturnType(newType: Type): void { return wrapJoinPoint(this._javaObject.setReturnType(unwrapJoinPoint(newType))); } +} + +export class QualType extends Type { /** - * Storage class specifier, which can be none, extern, static, __private_extern__, auto, register + * @internal */ - set storageClass(value: StorageClass) { this._javaObject.setStorageClass(unwrapJoinPoint(value)); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get qualifiers(): string[] { return wrapJoinPoint(this._javaObject.qualifiers()) } + get unqualifiedType(): Type { return wrapJoinPoint(this._javaObject.unqualifiedType()) } +} + +export class BuiltinType extends Type { /** - * If vardecl already has an initialization, removes it. + * @internal */ - removeInit(removeConst: boolean = true): void { return wrapJoinPoint(this._javaObject.removeInit(unwrapJoinPoint(removeConst))); } + static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { + name: null, + }; + get builtinKind(): string { return wrapJoinPoint(this._javaObject.builtinKind()) } /** - * Sets the given expression as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization + * True, if it is a floating type (e.g., float, double) */ - setInit(init: Expression): void; + get isFloat(): boolean { return wrapJoinPoint(this._javaObject.isFloat()) } /** - * Converts the given string to a literal expression and sets it as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization + * True, if it is an integer type */ - setInit(init: string): void; + get isInteger(): boolean { return wrapJoinPoint(this._javaObject.isInteger()) } /** - * Sets the given expression as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization + * True, if it is a signed type */ - setInit(p1: Expression | string): void { return wrapJoinPoint(this._javaObject.setInit(unwrapJoinPoint(p1))); } + get isSigned(): boolean { return wrapJoinPoint(this._javaObject.isSigned()) } /** - * Sets the storage class specifier, which can be none, extern, static, __private_extern__, autovardecl + * True, if it is an unsigned type */ - setStorageClass(storageClass: StorageClass): void { return wrapJoinPoint(this._javaObject.setStorageClass(unwrapJoinPoint(storageClass))); } + get isUnsigned(): boolean { return wrapJoinPoint(this._javaObject.isUnsigned()) } /** - * Creates a new varref based on this vardecl + * True, if it is a void type */ - varref(): Varref { return wrapJoinPoint(this._javaObject.varref()); } + get isVoid(): boolean { return wrapJoinPoint(this._javaObject.isVoid()) } } -export class VariableArrayType extends ArrayType { +export class ParenType extends Type { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get sizeExpr(): Expression { return wrapJoinPoint(this._javaObject.getSizeExpr()) } - set sizeExpr(value: Expression) { this._javaObject.setSizeExpr(unwrapJoinPoint(value)); } + get innerType(): Type { return wrapJoinPoint(this._javaObject.innerType()) } + set innerType(value: Type) { this._javaObject.setInnerType(unwrapJoinPoint(value)); } /** - * Sets the size expression of this variable array type + * Sets the inner type of this paren type */ - setSizeExpr(sizeExpr: Expression): void { return wrapJoinPoint(this._javaObject.setSizeExpr(unwrapJoinPoint(sizeExpr))); } + setInnerType(innerType: Type): void { return wrapJoinPoint(this._javaObject.setInnerType(unwrapJoinPoint(innerType))); } } -export class Body extends Scope { +export class UndefinedType extends Type { /** * @internal */ @@ -2804,143 +2764,224 @@ export class Body extends Scope { }; } -export class CilkFor extends Loop { /** - * @internal + * Represents a type that was referred to using an elaborated type keyword, e.g., struct S, or via a qualified name, e.g., N::M::type, or both. This type is used to keep track of a type name as written in the source code, including tag keywords and any nested-name-specifiers. The type itself is always 'sugar', used to express what was written in the source code but containing no additional semantic information. */ - static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "kind", - }; -} - -export class EnumType extends TagType { +export class ElaboratedType extends Type { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { name: null, }; - get integerType(): Type { return wrapJoinPoint(this._javaObject.getIntegerType()) } + /** + * The keyword of this elaborated type, if present. Can be one of: struct, interface, union, class, enum, typename + */ + get keyword(): string { return wrapJoinPoint(this._javaObject.keyword()) } + /** + * The type that is being prefixed with the qualifier + */ + get namedType(): Type { return wrapJoinPoint(this._javaObject.namedType()) } + /** + * The qualifier of this elaborated type, if present (e.g., A::) + */ + get qualifier(): string { return wrapJoinPoint(this._javaObject.qualifier()) } } -export class Param extends Vardecl { +export class TypedefType extends Type { /** * @internal */ static readonly _defaultAttributeInfo: {readonly map?: DefaultAttributeMap, readonly name: string | null, readonly type?: PrivateMapper, readonly jpMapper?: typeof JoinpointMapper} = { - name: "name", + name: null, }; + /** + * The typedef declaration associated with this typedef type + */ + get decl(): TypedefNameDecl { return wrapJoinPoint(this._javaObject.decl()) } + /** + * The type being aliased + */ + get underlyingType(): Type { return wrapJoinPoint(this._javaObject.underlyingType()) } } export enum StorageClass { + NONE = "none", AUTO = "auto", EXTERN = "extern", - NONE = "none", PRIVATE_EXTERN = "private_extern", REGISTER = "register", STATIC = "static", } export enum Relation { - EQ = "eq", - GE = "ge", - GT = "gt", LE = "le", LT = "lt", + GE = "ge", + GT = "gt", + EQ = "eq", NE = "ne", } +export enum LoopKind { + for = "for", + while = "while", + dowhile = "dowhile", + foreach = "foreach", +} + +export enum ExpressionUse { + read = "read", + write = "write", + readwrite = "readwrite", +} + +export enum OpKind { + ptr_mem_d = "ptr_mem_d", + ptr_mem_i = "ptr_mem_i", + mul = "mul", + div = "div", + rem = "rem", + add = "add", + sub = "sub", + shl = "shl", + shr = "shr", + cmp = "cmp", + lt = "lt", + gt = "gt", + le = "le", + ge = "ge", + eq = "eq", + ne = "ne", + and = "and", + xor = "xor", + or = "or", + l_and = "l_and", + l_or = "l_or", + assign = "assign", + mul_assign = "mul_assign", + div_assign = "div_assign", + rem_assign = "rem_assign", + add_assign = "add_assign", + sub_assign = "sub_assign", + shl_assign = "shl_assign", + shr_assign = "shr_assign", + and_assign = "and_assign", + xor_assign = "xor_assign", + or_assign = "or_assign", + comma = "comma", + post_inc = "post_inc", + post_dec = "post_dec", + pre_inc = "pre_inc", + pre_dec = "pre_dec", + addr_of = "addr_of", + deref = "deref", + plus = "plus", + minus = "minus", + not = "not", + l_not = "l_not", + real = "real", + imag = "imag", + extension = "extension", + cowait = "cowait", + ternary = "ternary", +} + +export enum WrapperStatementKind { + comment = "comment", + pragma = "pragma", +} + const JoinpointMapper = { joinpoint: Joinpoint, - attribute: Attribute, - clavaException: ClavaException, - comment: Comment, - decl: Decl, empty: Empty, - expression: Expression, + program: Program, file: FileJp, - implicitValue: ImplicitValue, - include: Include, - initList: InitList, - literal: Literal, - memberAccess: MemberAccess, + decl: Decl, namedDecl: NamedDecl, - newExpr: NewExpr, - op: Op, - parenExpr: ParenExpr, - pragma: Pragma, - program: Program, + declarator: Declarator, + include: Include, record: RecordJp, - statement: Statement, + field: Field, struct: Struct, - switch: Switch, - switchCase: SwitchCase, - tag: Tag, - ternaryOp: TernaryOp, - this: This, - type: Type, + class: Class, + vardecl: Vardecl, typedefNameDecl: TypedefNameDecl, - typedefType: TypedefType, - unaryExprOrType: UnaryExprOrType, - unaryOp: UnaryOp, - undefinedType: UndefinedType, - varref: Varref, - wrapperStmt: WrapperStmt, + typedefDecl: TypedefDecl, + enumDecl: EnumDecl, + enumeratorDecl: EnumeratorDecl, + labelDecl: LabelDecl, accessSpecifier: AccessSpecifier, - adjustedType: AdjustedType, - arrayAccess: ArrayAccess, - arrayType: ArrayType, - asmStmt: AsmStmt, - binaryOp: BinaryOp, - boolLiteral: BoolLiteral, - break: Break, - builtinType: BuiltinType, - call: Call, + param: Param, + function: FunctionJp, + method: Method, + pragma: Pragma, + marker: Marker, + tag: Tag, + omp: Omp, + statement: Statement, + scope: Scope, + body: Body, + loop: Loop, + if: If, + wrapperStmt: WrapperStmt, + returnStmt: ReturnStmt, + switch: Switch, + switchCase: SwitchCase, case: Case, - cast: Cast, - cilkSpawn: CilkSpawn, - cilkSync: CilkSync, - class: Class, - continue: Continue, - cudaKernelCall: CudaKernelCall, - declStmt: DeclStmt, - declarator: Declarator, default: Default, - deleteExpr: DeleteExpr, - elaboratedType: ElaboratedType, - emptyStmt: EmptyStmt, - enumDecl: EnumDecl, - enumeratorDecl: EnumeratorDecl, + declStmt: DeclStmt, exprStmt: ExprStmt, - field: Field, - floatLiteral: FloatLiteral, - function: FunctionJp, - functionType: FunctionType, gotoStmt: GotoStmt, - if: If, - incompleteArrayType: IncompleteArrayType, - intLiteral: IntLiteral, - labelDecl: LabelDecl, labelStmt: LabelStmt, - loop: Loop, - marker: Marker, + emptyStmt: EmptyStmt, + continue: Continue, + break: Break, + asmStmt: AsmStmt, + expression: Expression, + call: Call, memberCall: MemberCall, - method: Method, - omp: Omp, - parenType: ParenType, + cudaKernelCall: CudaKernelCall, + op: Op, + binaryOp: BinaryOp, + unaryOp: UnaryOp, + ternaryOp: TernaryOp, + newExpr: NewExpr, + deleteExpr: DeleteExpr, + varref: Varref, + cast: Cast, + parenExpr: ParenExpr, + arrayAccess: ArrayAccess, + memberAccess: MemberAccess, + unaryExprOrType: UnaryExprOrType, + This: This, + literal: Literal, + intLiteral: IntLiteral, + floatLiteral: FloatLiteral, + boolLiteral: BoolLiteral, + initList: InitList, + implicitValue: ImplicitValue, + comment: Comment, + cilkFor: CilkFor, + cilkSync: CilkSync, + cilkSpawn: CilkSpawn, + attribute: Attribute, + type: Type, pointerType: PointerType, - qualType: QualType, - returnStmt: ReturnStmt, - scope: Scope, - tagType: TagType, - templateSpecializationType: TemplateSpecializationType, - typedefDecl: TypedefDecl, - vardecl: Vardecl, + arrayType: ArrayType, + adjustedType: AdjustedType, variableArrayType: VariableArrayType, - body: Body, - cilkFor: CilkFor, + incompleteArrayType: IncompleteArrayType, + tagType: TagType, enumType: EnumType, - param: Param, + templateSpecializationType: TemplateSpecializationType, + functionType: FunctionType, + qualType: QualType, + builtinType: BuiltinType, + parenType: ParenType, + undefinedType: UndefinedType, + elaboratedType: ElaboratedType, + typedefType: TypedefType, }; let registered = false; diff --git a/Clava-JS/src-api/clava/ClavaJoinPoints.ts b/Clava-JS/src-api/clava/ClavaJoinPoints.ts index a514ad85f2..873f9181fc 100644 --- a/Clava-JS/src-api/clava/ClavaJoinPoints.ts +++ b/Clava-JS/src-api/clava/ClavaJoinPoints.ts @@ -486,7 +486,7 @@ export default class ClavaJoinPoints { } static compoundAssign( - op: string, + op: Joinpoints.OpKind, $leftHand: Joinpoints.Expression, $rightHand: Joinpoints.Expression ): Joinpoints.BinaryOp { @@ -536,7 +536,7 @@ export default class ClavaJoinPoints { * @param $type - The return type of the operator. If a string, it is converted to a literal type. */ static binaryOp( - op: string, + op: Joinpoints.OpKind, $left: Joinpoints.Expression | string, $right: Joinpoints.Expression | string, $type: Joinpoints.Type | string = "int" @@ -570,7 +570,7 @@ export default class ClavaJoinPoints { * @param $type - The return type of the operator. If undefined, tries to infer the correct type based on the type of the $expr (inference might not be implemented for all operators). */ static unaryOp( - op: string, + op: Joinpoints.OpKind, $expr: Joinpoints.Expression, $type?: Joinpoints.Type | string ): Joinpoints.UnaryOp; @@ -582,12 +582,12 @@ export default class ClavaJoinPoints { * @param $type - The return type of the operator that will be converted to a literal type. */ static unaryOp( - op: string, + op: Joinpoints.OpKind, $expr: string, $type: Joinpoints.Type | string ): Joinpoints.UnaryOp; static unaryOp( - op: string, + op: Joinpoints.OpKind, $expr: Joinpoints.Expression | string, $type?: Joinpoints.Type | string ): Joinpoints.UnaryOp { @@ -1068,12 +1068,12 @@ export default class ClavaJoinPoints { */ static memberAccess( baseExpr: Joinpoints.Expression, - fieldName: String, + fieldName: string, fieldType: Joinpoints.Type ): Joinpoints.MemberAccess; static memberAccess( baseExpr: Joinpoints.Expression, - field: Joinpoints.Field | String, + field: Joinpoints.Field | string, fieldType?: Joinpoints.Type ): Joinpoints.MemberAccess { if (typeof field === "string") { diff --git a/Clava-JS/src-api/clava/code/Outliner.ts b/Clava-JS/src-api/clava/code/Outliner.ts index b999f497e0..2a0ea6f954 100644 --- a/Clava-JS/src-api/clava/code/Outliner.ts +++ b/Clava-JS/src-api/clava/code/Outliner.ts @@ -11,6 +11,7 @@ import { FileJp, FunctionJp, Joinpoint, + OpKind, Param, PointerType, ReturnStmt, @@ -228,13 +229,13 @@ export default class Outliner { for (const ret of returnStmts) { const resVarParam = fun.params[fun.params.length - 2]; const derefResVarParam = ClavaJoinPoints.unaryOp( - "*", + OpKind.deref, resVarParam.varref() ); const retVal = ret.children[0]; retVal.detach(); const op1 = ClavaJoinPoints.binaryOp( - "=", + OpKind.eq, derefResVarParam, retVal as any, resVarParam.type @@ -243,10 +244,10 @@ export default class Outliner { const boolVarParam = fun.params[fun.params.length - 1]; const newVarref = ClavaJoinPoints.varRef(boolVarParam); - const derefBoolVarParam = ClavaJoinPoints.unaryOp("*", newVarref); + const derefBoolVarParam = ClavaJoinPoints.unaryOp(OpKind.deref, newVarref); const trueVal = ClavaJoinPoints.integerLiteral(1); const op2 = ClavaJoinPoints.binaryOp( - "=", + OpKind.eq, derefBoolVarParam, trueVal, boolVarParam.type @@ -256,8 +257,8 @@ export default class Outliner { fun.setType(ClavaJoinPoints.type("void")); // actions on the function call - const resVarAddr = ClavaJoinPoints.unaryOp("&", resVarRef); - const boolVarAddr = ClavaJoinPoints.unaryOp("&", boolVarRef); + const resVarAddr = ClavaJoinPoints.unaryOp(OpKind.addr_of, resVarRef); + const boolVarAddr = ClavaJoinPoints.unaryOp(OpKind.addr_of, boolVarRef); const allArgs = call.argList.concat([resVarAddr, boolVarAddr]); call = this.createCall(call, fun, allArgs); @@ -341,7 +342,7 @@ export default class Outliner { param.type instanceof PointerType && ref.type instanceof BuiltinType ) { - const addressOfScalar = ClavaJoinPoints.unaryOp("&", ref); + const addressOfScalar = ClavaJoinPoints.unaryOp(OpKind.addr_of, ref); args.push(addressOfScalar); } else { args.push(ref); @@ -409,7 +410,7 @@ export default class Outliner { varref.type instanceof BuiltinType ) { const newVarref = ClavaJoinPoints.varRef(param); - const op = ClavaJoinPoints.unaryOp("*", newVarref); + const op = ClavaJoinPoints.unaryOp(OpKind.deref, newVarref); varref.replaceWith(op); } } diff --git a/Clava-JS/src-api/clava/code/SimplifyAssignment.ts b/Clava-JS/src-api/clava/code/SimplifyAssignment.ts index 26338e9e27..fb176fefe3 100644 --- a/Clava-JS/src-api/clava/code/SimplifyAssignment.ts +++ b/Clava-JS/src-api/clava/code/SimplifyAssignment.ts @@ -1,4 +1,4 @@ -import { BinaryOp, Expression } from "../../Joinpoints.js"; +import { BinaryOp, Expression, OpKind } from "../../Joinpoints.js"; import ClavaJoinPoints from "../ClavaJoinPoints.js"; /** @@ -7,7 +7,7 @@ import ClavaJoinPoints from "../ClavaJoinPoints.js"; */ export default function SimplifyAssignment($complexAssignment: BinaryOp): void { // early return if current node is not suitable for this transform - if (!ops.has($complexAssignment.operator)) { + if (!ops.has($complexAssignment.kind)) { return; } @@ -15,7 +15,7 @@ export default function SimplifyAssignment($complexAssignment: BinaryOp): void { const $rValue = $complexAssignment.right; const $binaryOp = ClavaJoinPoints.binaryOp( - ops.get($complexAssignment.operator)!, + ops.get($complexAssignment.kind)!, $lValue.copy() as Expression, $rValue, $complexAssignment.type @@ -27,14 +27,14 @@ export default function SimplifyAssignment($complexAssignment: BinaryOp): void { * Non-assignment counterparts of complex assignment operators (lookup table) */ const ops = new Map([ - ["*=", "*"], - ["/=", "/"], - ["%=", "%"], - ["+=", "+"], - ["-=", "-"], - ["<<=", "<<"], - [">>=", ">>"], - ["&=", "&"], - ["^=", "^"], - ["|=", "|"], + [OpKind.mul_assign, OpKind.mul], + [OpKind.div_assign, OpKind.div], + [OpKind.rem_assign, OpKind.rem], + [OpKind.add_assign, OpKind.add], + [OpKind.sub_assign, OpKind.sub], + [OpKind.shl_assign, OpKind.shl], + [OpKind.shr_assign, OpKind.shr], + [OpKind.and_assign, OpKind.and], + [OpKind.xor_assign, OpKind.xor], + [OpKind.or_assign, OpKind.or], ]); diff --git a/Clava-JS/src-api/clava/code/StatementDecomposer.ts b/Clava-JS/src-api/clava/code/StatementDecomposer.ts index ffba11fbb7..00ba26bcaf 100644 --- a/Clava-JS/src-api/clava/code/StatementDecomposer.ts +++ b/Clava-JS/src-api/clava/code/StatementDecomposer.ts @@ -11,6 +11,7 @@ import { Joinpoint, LabelStmt, MemberCall, + OpKind, ReturnStmt, Scope, Statement, @@ -332,10 +333,10 @@ export default class StatementDecomposer { const rightResult = this.decomposeExpr($assign.right); const $newAssign = - $assign.operator === "=" + $assign.kind === OpKind.assign ? ClavaJoinPoints.assign($assign.left, rightResult.$resultExpr) : ClavaJoinPoints.compoundAssign( - $assign.operator, + $assign.kind, $assign.left, rightResult.$resultExpr ); @@ -407,23 +408,23 @@ export default class StatementDecomposer { // only decompose increment / decrement operations, separating the change // from the result of the change if ( - kind !== "post_dec" && - kind !== "post_inc" && - kind !== "pre_dec" && - kind !== "pre_inc" + kind !== OpKind.post_dec && + kind !== OpKind.post_inc && + kind !== OpKind.pre_dec && + kind !== OpKind.pre_inc ) { return new DecomposeResult([], $unaryOp, []); } switch (kind) { - case "post_dec": - case "post_inc": { + case OpKind.post_dec: + case OpKind.post_inc: { const $innerExpr = $unaryOp.operand.copy() as Expression; const succeedingStmts = [ClavaJoinPoints.exprStmt($unaryOp)]; return new DecomposeResult([], $innerExpr, succeedingStmts); } - case "pre_dec": - case "pre_inc": { + case OpKind.pre_dec: + case OpKind.pre_inc: { const $innerExpr = $unaryOp.operand.copy() as Expression; const precedingStmts = [ClavaJoinPoints.exprStmt($unaryOp)]; return new DecomposeResult(precedingStmts, $innerExpr, []); diff --git a/Clava-JS/src-api/clava/graphs/cfg/CfgBuilder.ts b/Clava-JS/src-api/clava/graphs/cfg/CfgBuilder.ts index e2a4e46d99..cf8e1a430a 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/CfgBuilder.ts +++ b/Clava-JS/src-api/clava/graphs/cfg/CfgBuilder.ts @@ -9,6 +9,7 @@ import { LabelDecl, LabelStmt, Loop, + LoopKind, Scope, Statement, Switch, @@ -335,13 +336,13 @@ export default class CfgBuilder { let afterStmt = undefined; switch ($loop.kind) { - case "for": + case LoopKind.for: afterStmt = $loop.init; break; - case "while": + case LoopKind.while: afterStmt = $loop.cond; break; - case "dowhile": + case LoopKind.dowhile: afterStmt = $loop.body; break; default: @@ -420,7 +421,7 @@ export default class CfgBuilder { throw new Error("Loop is undefined"); } - const $afterStmt = $loop.kind === "for" ? $loop.step : $loop.cond; + const $afterStmt = $loop.kind === LoopKind.for ? $loop.step : $loop.cond; const afterNode = this.nodes.get($afterStmt.astId) ?? this.endNode; this.addEdge(node, afterNode, CfgEdgeType.UNCONDITIONAL); @@ -526,7 +527,7 @@ export default class CfgBuilder { throw new Error("$loop is not an instance of Loop"); } - if ($loop.kind !== "for") { + if ($loop.kind !== LoopKind.for) { throw new Error("Not implemented for loops of kind " + $loop.kind); } @@ -563,7 +564,7 @@ export default class CfgBuilder { throw new Error("$loop is not an instance of Loop"); } - if ($loop.kind !== "for") { + if ($loop.kind !== LoopKind.for) { throw new Error("Not implemented for loops of kind " + $loop.kind); } diff --git a/Clava-JS/src-api/clava/graphs/cfg/NextCfgNode.ts b/Clava-JS/src-api/clava/graphs/cfg/NextCfgNode.ts index 02493c0aa7..09b3af62b3 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/NextCfgNode.ts +++ b/Clava-JS/src-api/clava/graphs/cfg/NextCfgNode.ts @@ -4,6 +4,7 @@ import { FunctionJp, If, Loop, + LoopKind, Scope, Statement, } from "../../../Joinpoints.js"; @@ -117,15 +118,15 @@ export default class NextCfgNode { // Next stmt is what comes next of if switch ($scopeParent.kind) { - case "while": - case "dowhile": + case LoopKind.while: + case LoopKind.dowhile: if ($scopeParent.cond === undefined) { throw new Error( "Not implemented when for loops do not have a condition statement" ); } return $scopeParent.cond; - case "for": + case LoopKind.for: if ($scopeParent.step === undefined) { throw new Error( "Not implemented when for loops do not have a step statement" diff --git a/Clava-JS/src-api/clava/pass/SimplifyLoops.ts b/Clava-JS/src-api/clava/pass/SimplifyLoops.ts index 5f68dccfd0..c2da23b91a 100644 --- a/Clava-JS/src-api/clava/pass/SimplifyLoops.ts +++ b/Clava-JS/src-api/clava/pass/SimplifyLoops.ts @@ -1,6 +1,6 @@ import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; -import { DeclStmt, ExprStmt, Joinpoint, Loop } from "../../Joinpoints.js"; +import { DeclStmt, ExprStmt, Joinpoint, Loop, LoopKind } from "../../Joinpoints.js"; import ClavaJoinPoints from "../ClavaJoinPoints.js"; import DoToWhileStmt from "../code/DoToWhileStmt.js"; import ForToWhileStmt from "../code/ForToWhileStmt.js"; @@ -49,17 +49,17 @@ export default class SimplifyLoops extends Pass { } if ( $jp instanceof Loop && - ($jp.kind === "for" || $jp.kind === "dowhile" || $jp.kind === "while") + ($jp.kind === LoopKind.for || $jp.kind === LoopKind.dowhile || $jp.kind === LoopKind.while) ) { yield $jp; } } private makeWhileLoop($loop: Loop): Loop { - if ($loop.kind === "for") { + if ($loop.kind === LoopKind.for) { const $forToWhileScope = ForToWhileStmt($loop, this.label_suffix++); return $forToWhileScope.children[1] as Loop; - } else if ($loop.kind === "dowhile") { + } else if ($loop.kind === LoopKind.dowhile) { return DoToWhileStmt($loop, this.label_suffix++); } else { return $loop; diff --git a/Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts b/Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts index 2201ce9991..c0d6df7fee 100644 --- a/Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts +++ b/Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts @@ -8,6 +8,7 @@ import { GotoStmt, If, Joinpoint, + OpKind, Statement, Switch, } from "../../Joinpoints.js"; @@ -180,26 +181,26 @@ export default class TransformSwitchToIf extends SimplePass { let $ifCondition; if ($case.values.length == 1) $ifCondition = ClavaJoinPoints.binaryOp( - "==", + OpKind.eq, $switchCondition, $case.values[0], "boolean" ); else { const $binOpGE = ClavaJoinPoints.binaryOp( - ">=", + OpKind.ge, $switchCondition, $case.values[0], "boolean" ); const $binOpLE = ClavaJoinPoints.binaryOp( - "<=", + OpKind.le, $switchCondition, $case.values[1], "boolean" ); $ifCondition = ClavaJoinPoints.binaryOp( - "&&", + OpKind.l_and, $binOpGE, $binOpLE, "boolean" diff --git a/Clava-JS/src-api/clava/stats/StaticOpsCounter.ts b/Clava-JS/src-api/clava/stats/StaticOpsCounter.ts index afdf1fff75..4a4ebd9fbe 100644 --- a/Clava-JS/src-api/clava/stats/StaticOpsCounter.ts +++ b/Clava-JS/src-api/clava/stats/StaticOpsCounter.ts @@ -5,10 +5,13 @@ import { BuiltinType, Call, Expression, + ExpressionUse, FunctionJp, Joinpoint, Loop, + LoopKind, Op, + OpKind, Param, Statement, Type, @@ -105,7 +108,7 @@ export default class StaticOpsCounter { } if ($stmt instanceof Loop) { - if ($stmt.kind !== "for") { + if ($stmt.kind !== LoopKind.for) { console.log( `Ignoring loops that are not 'fors' (location ${$stmt.location}) for now` ); @@ -280,19 +283,19 @@ export default class StaticOpsCounter { for (const $ref of refs) { // Ignore - if ($ref.use === "read") { + if ($ref.use === ExpressionUse.read) { continue; } // Not supported yet - if ($ref.use === "readwrite") { + if ($ref.use === ExpressionUse.readwrite) { console.log("Readwrite not supported yet"); return undefined; } // Check if assignment const $refParent = $ref.parent as Op; - if ($refParent.kind !== "assign") { + if ($refParent.kind !== OpKind.assign) { console.log("Not supported when not an assignment"); return undefined; } diff --git a/Clava-JS/src-api/lara/code/Energy.ts b/Clava-JS/src-api/lara/code/Energy.ts index 4e74a4cfe2..2fe4d6199f 100644 --- a/Clava-JS/src-api/lara/code/Energy.ts +++ b/Clava-JS/src-api/lara/code/Energy.ts @@ -6,6 +6,7 @@ import Logger from "./Logger.js"; import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.js"; import { FileJp, Joinpoint } from "../../Joinpoints.js"; +import { InsertPosition } from "@specs-feup/lara/api/LaraJoinPoint.js"; export default class Energy extends EnergyBase { /** @@ -59,7 +60,7 @@ export default class Energy extends EnergyBase { const codeBefore = Energy.energy_rapl_measure(energyVarStart); const codeAfter = Energy.energy_rapl_measure(energyVarEnd); - $start.insert("before", codeBefore); + $start.insert(InsertPosition.before, codeBefore); logger.append(prefix).appendLongLong(energyVarEnd + " - " + energyVarStart); if (this.printUnit) { @@ -67,7 +68,7 @@ export default class Energy extends EnergyBase { } logger.ln(); logger.log($end); - $end.insert("after", codeAfter); + $end.insert(InsertPosition.after, codeAfter); } private static energy_rapl_measure(energyVar: string): string { diff --git a/Clava-JS/src-api/lara/code/Logger.ts b/Clava-JS/src-api/lara/code/Logger.ts index 59a1aaeba5..415a0ce678 100644 --- a/Clava-JS/src-api/lara/code/Logger.ts +++ b/Clava-JS/src-api/lara/code/Logger.ts @@ -8,6 +8,7 @@ import { Joinpoint, Scope, } from "../../Joinpoints.js"; +import { InsertPosition } from "@specs-feup/lara/api/LaraJoinPoint.js"; export default class Logger extends LoggerBase { private _useSpecsLogger: boolean; @@ -280,7 +281,7 @@ export default class Logger extends LoggerBase { } _insertCode($jp: Joinpoint, insertBefore: boolean, code: string) { - const insertBeforeString = insertBefore ? "before" : "after"; + const insertBeforeString = insertBefore ? InsertPosition.before : InsertPosition.after; if (insertBefore) { $jp.insert(insertBeforeString, code); diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/VarDecl.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/VarDecl.java index 00a49dfd21..137e16b32f 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/VarDecl.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/VarDecl.java @@ -259,6 +259,10 @@ public void setStorageClass(String value) { set(STORAGE_CLASS, storageClass); } + public void setStorageClass(StorageClass storageClass) { + set(STORAGE_CLASS, storageClass); + } + @Override public SpecsList> getSignatureKeys() { return super.getSignatureKeys().andAdd(STORAGE_CLASS); diff --git a/ClavaWeaver/build.gradle b/ClavaWeaver/build.gradle index 687d2b6e97..ef20f599d7 100644 --- a/ClavaWeaver/build.gradle +++ b/ClavaWeaver/build.gradle @@ -11,7 +11,6 @@ java { withSourcesJar() } - // Repositories providers repositories { mavenCentral() @@ -21,11 +20,26 @@ configurations { weaverGeneratorRuntime } +// Project sources +sourceSets { + // Spec source set: compiled independently to avoid circular dependency + spec { + java { + srcDir 'src-spec' + } + } + main { + java { + srcDir 'src' + } + } +} + dependencies { implementation ":jOptions" implementation ":SpecsUtils" - implementation ":LanguageSpecification" + implementation ":LangSpec2" implementation ":LARAI" implementation ":LaraUtils" implementation ":WeaverInterface" @@ -37,32 +51,24 @@ dependencies { implementation 'com.google.guava:guava:33.4.0-jre' - weaverGeneratorRuntime ":WeaverGenerator" -} + weaverGeneratorRuntime ":WeaverGen2" -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - } + // Spec source set: only needs LangSpec2 and WeaverInterface (for BaseJoinPointSpec) + specImplementation ":LangSpec2" + specImplementation ":WeaverInterface" } -// Re-run the weaver generator +// Generate weaver abstracts using WeaverGen2 (Java DSL-based) tasks.register('generateWeaver', JavaExec) { group = "Execution" - description = "Generates the Weaver Abstracts" - classpath = configurations.weaverGeneratorRuntime - mainClass = 'org.lara.interpreter.weaver.generator.commandline.WeaverGenerator' + description = "Generates the Weaver Abstracts using WeaverGen2" + classpath = configurations.weaverGeneratorRuntime + sourceSets.spec.runtimeClasspath + mainClass = 'org.lara.weavergen2.WeaverGen2' args = [ - "-w", "CxxWeaver", - "-x", "./resources/clava/weaverspecs", - "-o", "./src", - "-p", "pt.up.fe.specs.clava.weaver", - "-n", "pt.up.fe.specs.clava.ClavaNode", - "-e", - "-j" + "pt.up.fe.specs.clava.weaver.CxxSpec", + "${projectDir}/src", + "--base", "org.lara.interpreter.weaver.interf.BaseJoinPointSpec", + "--node", "pt.up.fe.specs.clava.ClavaNode" ] } diff --git a/ClavaWeaver/resources/clava/test/issues/Issue_aiq_1.js b/ClavaWeaver/resources/clava/test/issues/Issue_aiq_1.js index 3bf6b2b675..068c79a804 100644 --- a/ClavaWeaver/resources/clava/test/issues/Issue_aiq_1.js +++ b/ClavaWeaver/resources/clava/test/issues/Issue_aiq_1.js @@ -1,7 +1,7 @@ import Query from "@specs-feup/lara/api/weaver/Query.js" -import {Loop} from "@specs-feup/clava/api/Joinpoints.js" +import { Loop, LoopKind } from "@specs-feup/clava/api/Joinpoints.js" -const forLoops = Query.search(Loop, (loop) => loop.kind === "for").get(); +const forLoops = Query.search(Loop, (loop) => loop.kind === LoopKind.for).get(); for (const forLoop of forLoops) { console.log("Cond: {\n" + forLoop.cond.code + "\n}\n"); diff --git a/ClavaWeaver/resources/clava/weaverspecs/actionModel.xml b/ClavaWeaver/resources/clava/weaverspecs/actionModel.xml deleted file mode 100644 index 2a555fea90..0000000000 --- a/ClavaWeaver/resources/clava/weaverspecs/actionModel.xml +++ /dev/null @@ -1,816 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ClavaWeaver/resources/clava/weaverspecs/artifacts.xml b/ClavaWeaver/resources/clava/weaverspecs/artifacts.xml deleted file mode 100644 index 538427b3ea..0000000000 --- a/ClavaWeaver/resources/clava/weaverspecs/artifacts.xml +++ /dev/null @@ -1,926 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ClavaWeaver/resources/clava/weaverspecs/joinPointModel.xml b/ClavaWeaver/resources/clava/weaverspecs/joinPointModel.xml deleted file mode 100644 index 0d53c11350..0000000000 --- a/ClavaWeaver/resources/clava/weaverspecs/joinPointModel.xml +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ClavaWeaver/settings.gradle b/ClavaWeaver/settings.gradle index 3c3e60e781..c743ff65f4 100644 --- a/ClavaWeaver/settings.gradle +++ b/ClavaWeaver/settings.gradle @@ -6,10 +6,10 @@ def laraFrameworkRoot = System.getenv('LARA_FRAMEWORK_HOME') ?: '../../lara-fram includeBuild("${specsJavaLibsRoot}/jOptions") includeBuild("${specsJavaLibsRoot}/SpecsUtils") -includeBuild("${laraFrameworkRoot}/LanguageSpecification") +includeBuild("${laraFrameworkRoot}/LangSpec2") includeBuild("${laraFrameworkRoot}/LARAI") includeBuild("${laraFrameworkRoot}/LaraUtils") -includeBuild("${laraFrameworkRoot}/WeaverGenerator") +includeBuild("${laraFrameworkRoot}/WeaverGen2") includeBuild("${laraFrameworkRoot}/WeaverInterface") includeBuild("../AntarexClavaApi") diff --git a/ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java b/ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java new file mode 100644 index 0000000000..b230065523 --- /dev/null +++ b/ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java @@ -0,0 +1,1384 @@ +package pt.up.fe.specs.clava.weaver; + +import org.lara.langspec2.dsl.WeaverSpec; +import org.lara.langspec2.types.JpDataType.BoundKind; +import org.lara.langspec2.types.JpDataType.WildcardType; + +/** + * Weaver specification for the Clava C/C++ weaver, translated from the XML + * specification files + * (joinPointModel.xml and artifacts.xml) into the Java DSL. + */ +public class CxxSpec extends WeaverSpec { + + @Override + public void define() { + weaverName("CxxWeaver"); + packageName("pt.up.fe.specs.clava.weaver"); + rootJoinPoint("program"); + + // ===================================================================== + // Enum definitions + // ===================================================================== + + enumDef("StorageClass") + .value("NONE") + .value("AUTO") + .value("EXTERN") + .value("PRIVATE_EXTERN") + .value("REGISTER") + .value("STATIC") + .end(); + + enumDef("Relation") + .value("LE") + .value("LT") + .value("GE") + .value("GT") + .value("EQ") + .value("NE") + .end(); + + enumDef("LoopKind") + .value("for") + .value("while") + .value("dowhile") + .value("foreach") + .end(); + + enumDef("ExpressionUse") + .value("read") + .value("write") + .value("readwrite") + .end(); + + enumDef("OpKind") + .value("ptr_mem_d") + .value("ptr_mem_i") + .value("mul") + .value("div") + .value("rem") + .value("add") + .value("sub") + .value("shl") + .value("shr") + .value("cmp") + .value("lt") + .value("gt") + .value("le") + .value("ge") + .value("eq") + .value("ne") + .value("and") + .value("xor") + .value("or") + .value("l_and") + .value("l_or") + .value("assign") + .value("mul_assign") + .value("div_assign") + .value("rem_assign") + .value("add_assign") + .value("sub_assign") + .value("shl_assign") + .value("shr_assign") + .value("and_assign") + .value("xor_assign") + .value("or_assign") + .value("comma") + .value("post_inc") + .value("post_dec") + .value("pre_inc") + .value("pre_dec") + .value("addr_of") + .value("deref") + .value("plus") + .value("minus") + .value("not") + .value("l_not") + .value("real") + .value("imag") + .value("extension") + .value("cowait") + .value("ternary") + .end(); + + enumDef("WrapperStatementKind") + .value("comment") + .value("pragma") + .end(); + + // ===================================================================== + // Global attributes (weaver-specific, not in BaseJoinPointSpec) + // Excludes base contract: dump, joinPointType, node, self, super, + // children, descendants, scopeNodes, insert, toString, equals, instanceOf + // ===================================================================== + + global() + .attribute("root", jpRef("program"), "Returns the 'program' joinpoint at the root of the hierarchy") + .attribute("getAncestor") + .tooltip("Looks for an ancestor joinpoint name, walking back on the AST") + .param("type", STRING) + .returns(jpRef("joinpoint")) + .attribute("getDescendants") + .tooltip("Retrieves the descendants of the given type") + .param("type", STRING) + .returns(array(jpRef("joinpoint"))) + .attribute("getDescendantsAndSelf") + .tooltip("Retrieves the descendants of the given type, including the current joinpoint") + .param("type", STRING) + .returns(array(jpRef("joinpoint"))) + .attribute("getChainAncestor") + .tooltip("Looks for an ancestor joinpoint name, walking back on the joinpoint chain") + .param("type", STRING) + .returns(jpRef("joinpoint")) + .attribute("getAstAncestor") + .tooltip("[DEPRECATED: Looks for an ancestor AST name, walking back on the AST]") + .param("type", STRING) + .returns(jpRef("joinpoint")) + .attribute("contains") + .tooltip("Checks if the joinpoint contains the given joinpoint") + .param("jp", jpRef("joinpoint")) + .returns(BOOLEAN) + .attribute("hasParent", BOOLEAN) + .attribute("getFirstJp") + .tooltip("Retrieves the first node of the given type in the descendants") + .param("type", STRING) + .returns(jpRef("joinpoint")) + .attribute("endLine", INTEGER, "The ending line of the current node in the original code") + .attribute("endColumn", INTEGER, "The ending column of the current node in the original code") + .attribute("location", STRING, "A string with information about the file and code position of this node, if available") + .attribute("filename", STRING, "The filename of the current node") + .attribute("filepath", STRING, "The file path of the current node") + .attribute("astId", STRING, "The AST ID of the current node") + .attribute("ast", STRING, "String with a dump of the AST representation starting from this node. This representation corresponds to the internal Java representation of the ClavaAst, where the node names correspond to Java classes. To get an equivalent representation with join point names, use the attribute 'dump'") + .attribute("type", jpRef("type")) + .attribute("hasType", BOOLEAN, "True, if the join point has a type") + .attribute("bitWidth", INTEGER, "The bit width of the type returned by this join point, in relation to the definitions of its Translation Unit, or undefined if there is no type or bitwidth defined, or if the join point is not in a TranslationUnit") + .attribute("astName", STRING, "The name of the Java class of this node, which is similar to the equivalent node in Clang AST") + .attribute("astNumChildren", INT, "Returns the number of children of the node, considering null nodes") + .attribute("astChildren", array(jpRef("joinpoint")), "Returns an array with the children of the node, considering null nodes") + .attribute("getAstChild") + .tooltip("Returns the child of the node at the given index, considering null nodes") + .param("index", INT) + .returns(jpRef("joinpoint")) + .attribute("numChildren", INT, "Returns the number of children of the node, ignoring null nodes") + .attribute("getChild") + .tooltip("Returns the child of the node at the given index, ignoring null nodes") + .param("index", INT) + .returns(jpRef("joinpoint")) + .attribute("siblingsLeft", array(jpRef("joinpoint")), "Returns an array with the siblings that came before this node") + .attribute("siblingsRight", array(jpRef("joinpoint")), "Returns an array with the siblings that come after this node") + .attribute("leftJp", jpRef("joinpoint"), "Returns the node that came before this node, or undefined if there is none") + .attribute("rightJp", jpRef("joinpoint"), "Returns the node that comes after this node, or undefined if there is none") + .attribute("astIsInstance") + .tooltip("True, if this node is a Java instance of the given name, which corresponds to a simple Java class name of an AST node. For an equivalent function for join point names, use 'instanceOf(joinPointName)'") + .param("className", STRING) + .returns(BOOLEAN) + .attribute("hasNode") + .tooltip("True, if the given join point or AST node is the same (== test) as the current join point AST node") + .param("nodeOrJp", OBJECT) + .returns(BOOLEAN) + .attribute("chain", array(STRING), "String list of the names of the join points that form a path from the root to this node") + .attribute("javaFields", array(STRING), "[DEPRECATED: used attribute 'keys' instead, together with 'getValue'] The names of the Java fields of this node. Can be used as key of the attribute 'javaValue'") + .attribute("getJavaFieldType") + .tooltip("String with the full Java class name of the type of the Java field with the provided name") + .param("fieldName", STRING) + .returns(STRING) + .attribute("isInsideLoopHeader", BOOLEAN, "True, if the join point is inside a loop header (e.g., for, while)") + .attribute("isInsideHeader", BOOLEAN, "True, if the join point is inside a header (e.g., function declaration)") + .attribute("isInSystemHeader", BOOLEAN, "True, if the join point is inside a system header (e.g., #include )") + .attribute("getUserField") + .tooltip("Retrives values that have been associated to nodes of the AST with 'setUserField'") + .param("fieldName", STRING) + .returns(OBJECT) + .attribute("parentRegion", jpRef("joinpoint"), "Returns the parent region of this join point, or undefined if there is none") + .attribute("currentRegion", jpRef("joinpoint"), "Returns the current region of this join point") + .attribute("pragmas", array(jpRef("pragma")), "Returns the pragmas associated with this join point") + .attribute("data", OBJECT, "JS object associated with this node, containing parsed data of #pragma clava data when the node can be a target of pragmas. This is a special object, managed internally, and cannot be reassigned, to change its contents requires using key-value pairs. If the node can be the target of a pragma, the information stored in this object is persisted between rebuilds.") + .attribute("keys", array(STRING), "A list of the properties currently supported by this node. Can be used as parameter of the attribute 'getValue'") + .attribute("getValue") + .tooltip("Returns the value of the property with the given name") + .param("key", STRING) + .returns(OBJECT) + .attribute("getKeyType") + .tooltip("Returns the type of the property with the given name") + .param("key", STRING) + .returns(OBJECT) + .attribute("isMacro", BOOLEAN, "True if any descendant or the node itself was defined as a macro") + .attribute("firstChild", jpRef("joinpoint"), "Returns the first child of this node, or undefined if it has no child") + .attribute("lastChild", jpRef("joinpoint"), "Returns the last child of this node, or undefined if it has no child") + .attribute("hasChildren", BOOLEAN, "True if the node has any children") + .attribute("isCilk", BOOLEAN, "True if the node is a Cilk node") + .attribute("depth", INT, "Returns the depth of this node in the AST. Root=0") + .attribute("jpId", STRING, "Returns the ID of this join point. The ID is based on the position of the node in the code, and should remain stable between compilations (warning: only a few nodes - file, function, loop - currently support it)") + .attribute("stmt", jpRef("statement"), "Converts this join point to a statement, or returns undefined if it was not possible") + .attribute("inlineComments", array(jpRef("comment")), "Returns comments that are not explicitly in the AST, but embedded in other nodes") + .attribute("originNode", jpRef("joinpoint"), "If this join point was not originally from the parsed AST, returns the first join point of the original AST that contributed to its origin") + .attribute("jpFields") + .tooltip("List with the values of fields that are join points, recursively") + .param("recursive", BOOLEAN, "false") + .returns(array(jpRef("joinpoint"))) + .action("replaceWith") + .tooltip("Replaces this node with the given node") + .param("node", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("replaceWith") + .tooltip("Overload that accepts a string") + .param("node", STRING) + .returns(jpRef("joinpoint")) + .action("replaceWith") + .tooltip("Overload that accepts a list of joinpoints") + .param("node", array(jpRef("joinpoint"))) + .returns(jpRef("joinpoint")) + .action("replaceWithStrings") + .tooltip("Overload that accepts a list of strings") + .param("node", array(STRING)) + .returns(jpRef("joinpoint")) + .action("insertBefore") + .tooltip("Inserts the given joinpoint before this joinpoint") + .param("node", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("insertBefore") + .tooltip("Overload that accepts a string") + .param("node", STRING) + .returns(jpRef("joinpoint")) + .action("insertAfter") + .tooltip("Inserts the given joinpoint after this joinpoint") + .param("node", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("insertAfter") + .tooltip("Overload that accepts a string") + .param("node", STRING) + .returns(jpRef("joinpoint")) + .action("detach") + .tooltip("Removes the node associated to this joinpoint from the AST") + .returns(jpRef("joinpoint")) + .action("setType") + .tooltip("Sets the type of a node, if it has a type") + .param("type", jpRef("type")) + .returns(VOID) + .action("copy") + .tooltip("Performs a copy of the node and its children, but not of the nodes in its fields") + .returns(jpRef("joinpoint")) + .action("deepCopy") + .tooltip("Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)") + .returns(jpRef("joinpoint")) + .action("setUserField") + .tooltip("Associates arbitrary values to nodes of the AST") + .param("fieldName", STRING) + .param("value", OBJECT) + .returns(OBJECT) + .action("setUserField") + .tooltip("Overload that accepts a map") + .param("fieldNameAndValue", map(STRING, new WildcardType(BoundKind.UNBOUNDED, null))) + .returns(OBJECT) + .action("setValue") + .tooltip("Sets the value associated with the given property key") + .param("key", STRING) + .param("value", OBJECT) + .returns(jpRef("joinpoint")) + .action("messageToUser") + .tooltip("Adds a message that will be printed to the user after weaving finishes. Identical messages are removed") + .param("message", STRING) + .returns(VOID) + .action("removeChildren") + .tooltip("Removes the children of this node") + .returns(VOID) + .action("setFirstChild") + .tooltip("Replaces the first child, or inserts the join point if no child is present. Returns the replaced child, or undefined if there was no child present") + .param("node", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("setLastChild") + .tooltip("Replaces the last child, or inserts the join point if no child is present. Returns the replaced child, or undefined if there was no child present") + .param("node", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("toComment") + .tooltip("Replaces this join point with a comment with the same contents as .code") + .param("prefix", STRING, "\"\"") + .param("suffix", STRING, "\"\"") + .returns(jpRef("joinpoint")) + .action("setInlineComments") + .tooltip("Sets the comments that are embedded in a node") + .param("comments", array(STRING)) + .returns(VOID) + .action("setInlineComments") + .tooltip("Sets the comments that are embedded in a node") + .param("comments", STRING) + .returns(VOID) + .action("setData") + .tooltip("Setting data directly is not supported, this action just emits a warning and does nothing") + .param("source", OBJECT) + .returns(VOID) + .action("dataClear") + .tooltip("Clears all properties from the .data object") + .returns(VOID); + + // ===================================================================== + // Join point definitions + // ===================================================================== + + // --- Utility join points --- + + joinPoint("empty") + .tooltip("Utility joinpoint, to represent empty nodes when directly accessing the tree"); + + // --- Program / File --- + + joinPoint("program") + .tooltip("Represents the complete program and is the top-most joinpoint in the hierarchy") + .defaultAttribute("name") + .attribute("name", STRING) + .attribute("isCxx", BOOLEAN, "True if the program was compiled with a C++ standard") + .attribute("standard", STRING, "The name of the standard (e.g., c99, c++11)") + .attribute("stdFlag", STRING, "The flag of the standard (e.g., -std=c++11)") + .attribute("defaultFlags", array(STRING)) + .attribute("userFlags", array(STRING)) + .attribute("includeFolders", array(STRING)) + .attribute("baseFolder", STRING) + .attribute("weavingFolder", STRING) + .attribute("extraSources", array(STRING), "Paths to sources that the current program depends on") + .attribute("extraIncludes", array(STRING), "Paths to includes that the current program depends on") + .attribute("extraProjects", array(STRING), "Paths to folders of projects that the current program depends on") + .attribute("extraLibs", array(STRING), "Link libraries of external projects the current program depends on") + .attribute("main", jpRef("function"), "A function join point with the main function of the program, if one is available") + .attribute("files", array(jpRef("file")), "The files of the program") + .action("rebuild") + .tooltip("Recompiles the program currently represented by the AST, transforming literal code into AST nodes. Returns true if all files could be parsed correctly, or false otherwise") + .returns(BOOLEAN) + .action("rebuildFuzzy") + .tooltip("Similar to rebuild, but tries to fix compilation errors. Resulting program may not represent the originally intended functionality") + .returns(VOID) + .action("addFile") + .tooltip("Adds a file join point to the current program") + .param("file", jpRef("file")) + .returns(jpRef("joinpoint")) + .action("addFileFromPath") + .tooltip("Adds a file join point to the current program, from the given path, which can be either a Java File or a String") + .param("filepath", OBJECT) + .returns(jpRef("joinpoint")) + .action("push") + .tooltip("Creates a copy of the current AST and pushes it to the top of the AST stack") + .returns(VOID) + .action("pop") + .tooltip("Discards the AST at the top of the AST stack") + .returns(VOID) + .action("addExtraInclude") + .tooltip("Adds a path to an include that the current program depends on") + .param("path", STRING) + .returns(VOID) + .action("addExtraIncludeFromGit") + .tooltip("Adds a path based on a git repository to an include that the current program depends on") + .param("gitRepo", STRING) + .param("path", STRING, "null") + .returns(VOID) + .action("addExtraSource") + .tooltip("Adds a path to a source that the current program depends on") + .param("path", STRING) + .returns(VOID) + .action("addExtraSourceFromGit") + .tooltip("Adds a path based on a git repository to a source that the current program depends on") + .param("gitRepo", STRING) + .param("path", STRING, "null") + .returns(VOID) + .action("addProjectFromGit") + .tooltip("Adds a path based on a git repository to a project that the current program depends on") + .param("gitRepo", STRING) + .param("libs", array(STRING)) + .param("path", STRING, "null") + .returns(VOID) + .action("addExtraLib") + .tooltip("Adds a library (e.g., -pthreads) that the current program depends on") + .param("lib", STRING) + .returns(VOID) + .action("atexit") + .tooltip("Registers a function to be executed when the program exits") + .param("function", jpRef("function")) + .returns(VOID); + + joinPoint("file") + .tooltip("Represents a source file (.c, .cpp, .cl, etc)") + .defaultAttribute("name") + .attribute("name", STRING) + .attribute("file", OBJECT, "The Java File object associated with this file") + .attribute("hasMain", BOOLEAN, "True if this file has the main function as a descendant") + .attribute("path", STRING, "The folder path for this file") + .attribute("relativeFilepath", STRING, "The file path relative to the base folder of the program") + .attribute("relativeFolderpath", STRING, "The folder path relative to the base folder of the program") + .attribute("baseSourcePath", STRING, "The base source path for this file") + .attribute("isCxx", BOOLEAN, "True if this file is a being parsed as a C++ file") + .attribute("isHeader", BOOLEAN, "True if this file is a header file") + .attribute("isOpenCL", BOOLEAN, "True if this file is an OpenCL file") + .attribute("getDestinationFilepath") + .tooltip("The complete path to the file that will be generated by the weaver, given a destination folder") + .param("destinationFolderpath", STRING, "null") + .returns(STRING) + .attribute("sourceFoldername", STRING, "The name of the source folder of this file, or undefined if it has none") + .attribute("hasParsingErrors", BOOLEAN, "True if there were errors during the parsing of this file") + .attribute("errorOutput", STRING, "The error output produced during the parsing of this file, if any") + .attribute("includes", array(jpRef("include")), "The include directives in this file") + .action("addInclude") + .tooltip("Adds an include to the current file. If the file already has the include, it does nothing") + .param("name", STRING) + .param("isAngled", BOOLEAN, "false") + .returns(VOID) + .action("addIncludeJp") + .tooltip("Overload of addInclude which accepts a join point") + .param("jp", jpRef("joinpoint")) + .returns(VOID) + .action("addCInclude") + .tooltip("Adds a C include to the current file. If the file already has the include, it does nothing") + .param("name", STRING) + .param("isAngled", BOOLEAN, "false") + .returns(VOID) + .action("addGlobal") + .tooltip("Adds a global variable to this file") + .param("name", STRING) + .param("type", jpRef("joinpoint")) + .param("initValue", STRING) + .returns(jpRef("vardecl")) + .action("write") + .tooltip("Writes the code of this file to a given folder") + .param("destinationFoldername", STRING) + .returns(STRING) + .action("setName") + .tooltip("Changes the name of the file") + .param("filename", STRING) + .returns(VOID) + .action("rebuild") + .tooltip("Recompiles only this file, returns a join point to the new recompiled file, or throws an exception if a problem happens") + .returns(jpRef("file")) + .action("insertBegin") + .tooltip("Adds the node in the join point to the start of the file") + .param("node", jpRef("joinpoint")) + .returns(VOID) + .action("insertBegin") + .tooltip("Adds the String as a Decl to the end of the file") + .param("code", STRING) + .returns(VOID) + .action("insertEnd") + .tooltip("Adds the node in the join point to the end of the file") + .param("node", jpRef("joinpoint")) + .returns(VOID) + .action("insertEnd") + .tooltip("Adds the String as a Decl to the end of the file") + .param("code", STRING) + .returns(VOID) + .action("addFunction") + .tooltip("Adds a function to the file that returns void and has no parameters") + .param("name", STRING) + .returns(jpRef("joinpoint")) + .action("setRelativeFolderpath") + .tooltip("Sets the path to the folder of the source file relative to the base source path") + .param("path", STRING) + .returns(VOID); + + // --- Declarations --- + + joinPoint("decl") + .tooltip("Represents one declaration (e.g., int foo(){return 0;}) or definition (e.g., int foo();)") + .attribute("attrs", array(jpRef("attribute")), "The attributes of this declaration (e.g. Pure, CUDAGlobal), if any"); + + joinPoint("namedDecl").extending("decl") + .tooltip("Represents a decl with a name") + .defaultAttribute("name") + .attribute("name", STRING) + .attribute("isPublic", BOOLEAN) + .attribute("qualifiedPrefix", STRING) + .attribute("qualifiedName", STRING) + .action("setName") + .tooltip("Sets the name of this namedDecl") + .param("name", STRING) + .returns(VOID) + .action("setQualifiedPrefix") + .tooltip("Sets the qualified prefix of this namedDecl") + .param("qualifiedPrefix", STRING) + .returns(VOID) + .action("setQualifiedName") + .tooltip("Sets the qualified name of this namedDecl (changes both the name and qualified prefix)") + .param("name", STRING) + .returns(VOID); + + joinPoint("declarator").extending("namedDecl") + .tooltip("Represents a decl that comes from a declarator (e.g., function, field, variable)"); + + joinPoint("include").extending("decl") + .tooltip("Represents an include directive (e.g., #include )") + .defaultAttribute("name") + .attribute("name", STRING) + .attribute("isAngled", BOOLEAN, "True if the include is angled (e.g., #include ) instead of quoted (e.g., #include \"myheader.h\")") + .attribute("relativeFolderpath", STRING, "The path to the folder of the source file of the include, relative to the name of the include"); + + joinPoint("record").extending("namedDecl") + .tooltip("Represents a record declaration (struct, union, or class)") + .attribute("kind", STRING) + .attribute("fields", array(jpRef("field"))) + .attribute("functions", array(jpRef("function"))) + .attribute("isImplementation", BOOLEAN, "True if this record declaration is an implementation (i.e., it has a body) instead of just a forward declaration") + .attribute("isPrototype", BOOLEAN, "True if this record declaration is a prototype (i.e., it has no body) instead of an implementation") + .action("addField") + .tooltip("Adds a field to a record (struct, class)") + .param("field", jpRef("field")) + .returns(VOID); + + joinPoint("field").extending("declarator") + .tooltip("Represents a member of a struct/union/class"); + + joinPoint("struct").extending("record") + .tooltip("Represents a struct declaration"); + + joinPoint("class").extending("record") + .tooltip("Represents a C++ class declaration") + .defaultAttribute("name") + .attribute("methods", array(jpRef("method")), "The methods of this class") + .attribute("bases", array(jpRef("class")), "The base classes of this class") + .attribute("allMethods", array(jpRef("method")), "The methods of this class and its base classes") + .attribute("allBases", array(jpRef("class")), "The base classes of this class and its base classes") + .attribute("isAbstract", BOOLEAN, "True if this class contains at least one pure function") + .attribute("isInterface", BOOLEAN, "True if this class contains only pure functions") + .attribute("prototypes", array(jpRef("class")), "The prototypes (or declarations) of this class present in the AST, if any") + .attribute("implementation", jpRef("class"), "The implementation (or definition) of this class present in the AST, or undefined if none is found") + .attribute("canonical", jpRef("class"), "Class join points can either represent declarations or definitions, returns the definition of this class, if present, or the first declaration, if only declarations are present") + .attribute("isCanonical", BOOLEAN, "True if this class join point is the canonical one, which is the definition if it is present, or the first declaration if only declarations are present") + .action("addMethod") + .tooltip("Adds a method to a class. If the given method has a definition, creates an equivalent declaration and adds it to the class, otherwise simply adds the declaration to the class. In both cases, the declaration is only added to the class if there is no declaration already with the same signature") + .param("method", jpRef("method")) + .returns(VOID); + + joinPoint("vardecl").extending("declarator") + .tooltip("Represents a variable declaration or definition") + .defaultAttribute("name") + .attribute("hasInit", BOOLEAN, "True if this variable declaration has an initializer") + .attribute("init", jpRef("expression"), "The initializer of this variable declaration, if it has one") + .attribute("initStyle", STRING, "The initialization style of this vardecl, which can be no_init, cinit, callinit, listinit") + .attribute("isParam", BOOLEAN, "True if this variable declaration is a function parameter") + .attribute("storageClass", enumRef("StorageClass"), "The storage class of this variable declaration. Can be 'none', 'extern', 'static', '__private_extern__', 'auto' or 'register'") + .attribute("isGlobal", BOOLEAN, "True if this variable declaration is global. This includes all global variables as well as static variables declared within a function.") + .attribute("definition", jpRef("vardecl"), "The vardecl corresponding to the actual definition. For global variables, returns the vardecl of the file where it is actually defined (instead of the vardecl that defines an external link to the variable)") + .action("setInit") + .tooltip("Sets the given expression as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization") + .param("init", jpRef("expression")) + .returns(VOID) + .action("setInit") + .tooltip("Converts the given string to a literal expression and sets it as the initialization of this vardecl. If undefined is passed and vardecl already has an initialization, removes that initialization") + .param("init", STRING) + .returns(VOID) + .action("removeInit") + .tooltip("If vardecl already has an initialization, removes it") + .param("removeConst", BOOLEAN, "true") + .returns(VOID) + .action("varref") + .tooltip("Creates a new varref based on this vardecl") + .returns(jpRef("varref")) + .action("setStorageClass") + .tooltip("Sets the storage class specifier, which can be none, extern, static, __private_extern__, auto") + .param("storageClass", enumRef("StorageClass")) + .returns(VOID); + + joinPoint("typedefNameDecl").extending("namedDecl") + .tooltip("Base node for declarations which introduce a typedef-name"); + + joinPoint("typedefDecl").extending("typedefNameDecl") + .tooltip("Declaration of a typedef-name via the 'typedef' type specifier"); + + joinPoint("enumDecl").extending("namedDecl") + .tooltip("Represents an enum declaration") + .attribute("enumerators", array(jpRef("enumeratorDecl"))); + + joinPoint("enumeratorDecl").extending("namedDecl") + .tooltip("Represents an enumerator in an enum"); + + joinPoint("labelDecl").extending("namedDecl") + .tooltip("Represents a label declaration") + .attribute("labelStmt", jpRef("labelStmt")); + + joinPoint("accessSpecifier").extending("decl") + .tooltip("Represents an access specifier (public:, private:, or protected:) in a class declaration") + .defaultAttribute("kind") + .attribute("kind", STRING, "The type of specifier. Can return 'public', 'protected', 'private' or 'none'"); + + joinPoint("param").extending("vardecl") + .tooltip("Represents a function parameter"); + + joinPoint("function").extending("declarator") + .tooltip("Represents a function declaration or definition") + .attribute("hasDefinition", BOOLEAN, "[DEPRECATED: Use .isImplementation instead] True if this particular function join point has a body, false otherwise") + .attribute("isImplementation", BOOLEAN, "True if this function join point is an implementation, false otherwise") + .attribute("isPrototype", BOOLEAN, "True if this function join point is a prototype, false otherwise") + .attribute("functionType", jpRef("functionType"), "The function type of this function, which includes the return type and the parameter types") + .attribute("declarationJp", jpRef("function"), "Returns the first prototype of this function that could be found, or undefined if there is none") + .attribute("declarationJps", array(jpRef("function")), "Returns the prototypes of this function that are present in the code. If there are none, returns an empty array") + .attribute("definitionJp", jpRef("function"), "Returns the implementation of this function if there is one, or undefined otherwise") + .attribute("getDeclaration") + .param("withReturnType", BOOLEAN) + .returns(STRING) + .attribute("body", jpRef("scope")) + .attribute("paramNames", array(STRING)) + .attribute("params", array(jpRef("param"))) + .attribute("id", STRING) + .attribute("isInline", BOOLEAN) + .attribute("isVirtual", BOOLEAN) + .attribute("isModulePrivate", BOOLEAN) + .attribute("isPure", BOOLEAN) + .attribute("isDelete", BOOLEAN) + .attribute("storageClass", enumRef("StorageClass")) + .attribute("calls", array(jpRef("call"))) + .attribute("signature", STRING, "The signature of this function (e.g., name of the function, plus the parameters types)") + .attribute("returnType", jpRef("type")) + .attribute("isCudaKernel", BOOLEAN) + .attribute("canonical", jpRef("function"), "Function join points can either represent declarations or definitions, returns the definition of this function, if present, or the first declaration, if only declarations are present") + .attribute("isCanonical", BOOLEAN, "True, if this is the function returned by the 'canonical' attribute") + .action("clone") + .tooltip("Clones this function assigning it a new name, inserts the cloned function after the original function. If the name is the same and the original method, automatically removes the cloned method from the class") + .param("newName", STRING) + .param("insert", BOOLEAN, "true") + .returns(jpRef("function")) + .action("cloneOnFile") + .tooltip("Generates a clone of the provided function on a new file with the provided name (or with a weaver-generated name if one is not provided)") + .param("newName", STRING) + .param("fileName", STRING, "null") + .returns(jpRef("function")) + .action("cloneOnFile") + .tooltip("Generates a clone of the provided function on a new file (with the provided join point)") + .param("newName", STRING) + .param("file", jpRef("file")) + .returns(jpRef("function")) + .action("insertReturn") + .tooltip("Inserts the joinpoint before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node") + .param("code", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("insertReturn") + .tooltip("Inserts code as a literal statement before the return points of the function (return statements and implicitly, at the end of the function). Returns the last inserted node") + .param("code", STRING) + .returns(jpRef("joinpoint")) + .action("setParams") + .tooltip("Sets the parameters of the function") + .param("params", array(jpRef("param"))) + .returns(VOID) + .action("setParamsFromStrings") + .tooltip("Overload that accepts strings that represent type-varname pairs (e.g., int param1)") + .param("params", array(STRING)) + .returns(VOID) + .action("setParam") + .tooltip("Sets the parameter of the function at the given position") + .param("index", INT) + .param("param", jpRef("param")) + .returns(VOID) + .action("setParam") + .tooltip("Sets the parameter of the function at the given position") + .param("index", INT) + .param("name", STRING) + .param("type", jpRef("type"), "null") + .returns(VOID) + .action("setBody") + .tooltip("Sets the body of the function") + .param("body", jpRef("scope")) + .returns(VOID) + .action("newCall") + .tooltip("Creates a new call to this function") + .param("args", array(jpRef("joinpoint"))) + .returns(jpRef("call")) + .action("setFunctionType") + .tooltip("Sets the type of the function") + .param("functionType", jpRef("functionType")) + .returns(VOID) + .action("setReturnType") + .tooltip("Sets the return type of the function") + .param("returnType", jpRef("type")) + .returns(VOID) + .action("setParamType") + .tooltip("Sets the type of a parameter of the function") + .param("index", INT) + .param("newType", jpRef("type")) + .returns(VOID) + .action("addParam") + .tooltip("Adds a new parameter to the function") + .param("param", jpRef("param")) + .returns(VOID) + .action("addParam") + .tooltip("Adds a new parameter to the function") + .param("name", STRING) + .param("type", jpRef("type"), "null") + .returns(VOID) + .action("setStorageClass") + .tooltip("Sets the storage class of this specific function decl. AUTO and REGISTER are not allowed for functions, and EXTERN is not allowed in function implementations, or function declarations that are in the same file as the implementation. Returns true if the storage class changed, false otherwise") + .param("storageClass", enumRef("StorageClass")) + .returns(BOOLEAN); + + joinPoint("method").extending("function") + .tooltip("Represents a method in a class declaration") + .defaultAttribute("name") + .attribute("record", jpRef("class")) + .action("removeRecord") + .tooltip("Removes the class of the method") + .returns(VOID); + + // --- Pragmas --- + + joinPoint("pragma") + .tooltip("Represents a pragma in the code (e.g., #pragma kernel)") + .defaultAttribute("name") + .attribute("name", STRING, "The name of the pragma. E.g. for #pragma foo bar, returns 'foo'") + .attribute("target", jpRef("joinpoint"), "The first node below the pragma that is not a comment or another pragma. Example of pragma targets are statements and declarations") + .attribute("content", STRING, "Everything that is after the name of the pragma") + .attribute("getTargetNodes") + .tooltip("All the nodes below the target node, including the target node, up until a pragma with the name given by argument 'endPragma'. If no end pragma is found, returns the same result as if not providing the argument") + .param("endPragma", STRING, "null") + .returns(array(jpRef("joinpoint"))) + .action("setName") + .param("name", STRING) + .returns(VOID) + .action("setContent") + .param("content", STRING) + .returns(VOID); + + joinPoint("marker").extending("pragma") + .tooltip( + "Represents a marker pragma, which is used to mark a specific node in the code (e.g., #pragma myMarker) and can be used to store custom data") + .defaultAttribute("id") + .attribute("id", STRING) + .attribute("contents", jpRef("scope"), "The scope that is targeted by the marker"); + + joinPoint("tag").extending("pragma") + .tooltip("Represents a tag pragma, which is used to reference a specific node in the code") + .defaultAttribute("id") + .attribute("id", STRING); + + joinPoint("omp").extending("pragma") + .tooltip("Represents an OpenMP pragma (e.g., #pragma omp parallel)") + .defaultAttribute("kind") + .attribute("kind", STRING, "The kind of the directive") + .attribute("numThreads", STRING, "An integer expression, or undefined if no 'num_threads' clause is defined") + .attribute("procBind", STRING, "One of 'master', 'close' or 'spread', or undefined if no 'proc_bind' clause is defined") + .attribute("private", array(STRING), "The variable names of all private clauses, or empty array if no private clause is defined") + .attribute("hasClause") + .tooltip("True if the directive has at least one clause of the given clause kind, false otherwise") + .param("clauseName", STRING) + .returns(BOOLEAN) + .attribute("isClauseLegal") + .tooltip("True if the directive has the given clause kind, false otherwise") + .param("clauseName", STRING) + .returns(BOOLEAN) + .attribute("clauseKinds", array(STRING), "The names of the kinds of all clauses in the pragma, or empty array if no clause is defined") + .attribute("getReduction") + .tooltip("The variable names for the given reduction kind, or empty array if no reduction of that kind is defined") + .param("kind", STRING) + .returns(array(STRING)) + .attribute("reductionKinds", array(STRING), "The reduction kinds in the reductions clauses of the this pragma, or empty array if no reduction is defined") + .attribute("default", STRING, "One of 'shared' or 'none', or undefined if no 'default' clause is defined") + .attribute("firstprivate", array(STRING), "The variable names of all firstprivate clauses, or empty array if no firstprivate clause is defined") + .attribute("lastprivate", array(STRING), "The variable names of all lastprivate clauses, or empty array if no lastprivate clause is defined") + .attribute("shared", array(STRING), "The variable names of all shared clauses, or empty array if no shared clause is defined") + .attribute("copyin", array(STRING), "The variable names of all copyin clauses, or empty array if no copyin clause is defined") + .attribute("scheduleKind", STRING, "One of 'static', 'dynamic', 'guided', 'auto' or 'runtime', or undefined if no 'schedule' clause is defined") + .attribute("scheduleChunkSize", STRING, "An integer expression, or undefined if no 'schedule' clause with chunk size is defined") + .attribute("scheduleModifiers", array(STRING), "A list with possible values of 'monotonic', 'nonmonotonic' or 'simd', or undefined if no 'schedule' clause with modifiers is defined") + .attribute("collapse", STRING, "An integer expression, or undefined if no 'collapse' clause is defined") + .attribute("ordered", STRING, "An integer expression, or undefined if no 'ordered' clause with a parameter is defined") + .action("setKind") + .tooltip("Sets the directive kind of the OpenMP pragma. Any unsupported clauses will be discarded") + .param("directiveKind", STRING) + .returns(VOID) + .action("removeClause") + .tooltip("Removes any clause of the given kind from the OpenMP pragma") + .param("clauseKind", STRING) + .returns(VOID) + .action("setNumThreads") + .tooltip("Sets the value of the num_threads clause of an OpenMP pragma") + .param("newExpr", STRING) + .returns(VOID) + .action("setProcBind") + .tooltip("Sets the value of the proc_bind clause of an OpenMP pragma") + .param("newBind", STRING) + .returns(VOID) + .action("setPrivate") + .tooltip("Sets the variables of a private clause of an OpenMP pragma") + .param("newVariables", array(STRING)) + .returns(VOID) + .action("setReduction") + .tooltip("Sets the variables for a given kind of a reduction clause of an OpenMP pragma") + .param("kind", STRING) + .param("newVariables", array(STRING)) + .returns(VOID) + .action("setDefault") + .tooltip("Sets the value of the default clause of an OpenMP pragma") + .param("newDefault", STRING) + .returns(VOID) + .action("setFirstprivate") + .tooltip("Sets the variables of a firstprivate clause of an OpenMP pragma") + .param("newVariables", array(STRING)) + .returns(VOID) + .action("setLastprivate") + .tooltip("Sets the variables of a lastprivate clause of an OpenMP pragma") + .param("newVariables", array(STRING)) + .returns(VOID) + .action("setShared") + .tooltip("Sets the variables of a shared clause of an OpenMP pragma") + .param("newVariables", array(STRING)) + .returns(VOID) + .action("setCopyin") + .tooltip("Sets the variables of a copyin clause of an OpenMP pragma") + .param("newVariables", array(STRING)) + .returns(VOID) + .action("setScheduleKind") + .tooltip("Sets the value of the schedule clause of an OpenMP pragma") + .param("scheduleKind", STRING) + .returns(VOID) + .action("setScheduleChunkSize") + .tooltip("Sets the value of the chunk size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception") + .param("chunkSize", STRING) + .returns(VOID) + .action("setScheduleChunkSize") + .tooltip("Sets the value of the chunk size in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception") + .param("chunkSize", INT) + .returns(VOID) + .action("setScheduleModifiers") + .tooltip("Sets the value of the modifiers in the schedule clause of an OpenMP pragma. Can only be called if there is already a schedule clause in the directive, otherwise throws an exception") + .param("modifiers", array(STRING)) + .returns(VOID) + .action("setCollapse") + .tooltip("Sets the value of the collapse clause of an OpenMP pragma") + .param("newExpr", STRING) + .returns(VOID) + .action("setCollapse") + .tooltip("Sets the value of the collapse clause of an OpenMP pragma") + .param("newExpr", INT) + .returns(VOID) + .action("setOrdered") + .tooltip("Sets the value of the ordered clause of an OpenMP pragma") + .param("parameters", STRING, "null") + .returns(VOID); + + // --- Statements --- + + joinPoint("statement") + .attribute("isFirst", BOOLEAN) + .attribute("isLast", BOOLEAN); + + joinPoint("scope").extending("statement") + .tooltip("Represents a group of statements (e.g., function body, loop body, if/else body, etc.)") + .attribute("getNumStatements") + .tooltip("The number of statements in the scope, including the statements inside the declaration and bodies of structures such as ifs and loops, and not considering comments and pragmas. If flat is true, does not consider the statements inside structures such as ifs and loops (e.g., a loop counts as one statement)") + .param("flat", BOOLEAN, "false") + .returns(LONG) + .attribute("naked", BOOLEAN, "True if the scope does not have curly braces") + .attribute("stmts", array(jpRef("statement")), "Returns the direct (children) statements of this scope") + .attribute("allStmts", array(jpRef("statement")), "Returns the descendant statements of this scope, excluding other scopes, loops, ifs and wrapper statements") + .attribute("firstStmt", jpRef("statement"), "Returns the first statement in the scope") + .attribute("lastStmt", jpRef("statement"), "Returns the last statement in the scope") + .attribute("owner", jpRef("joinpoint"), "The statement that owns the scope (e.g., function, loop...)") + .action("insertBegin") + .param("node", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("insertBegin") + .param("code", STRING) + .returns(jpRef("joinpoint")) + .action("insertEnd") + .param("node", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("insertEnd") + .param("code", STRING) + .returns(jpRef("joinpoint")) + .action("insertReturn") + .tooltip("Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node") + .param("code", jpRef("joinpoint")) + .returns(jpRef("joinpoint")) + .action("insertReturn") + .tooltip("Inserts the joinpoint before the return points of the scope (return statements and implicitly, at the end of the scope). Returns the last inserted node") + .param("code", STRING) + .returns(jpRef("joinpoint")) + .action("addLocal") + .tooltip("Adds a new local variable to this scope") + .param("name", STRING) + .param("type", jpRef("joinpoint")) + .param("initValue", STRING, "null") + .returns(jpRef("joinpoint")) + .action("setNaked") + .tooltip("Sets the 'naked' status of a scope (a scope is naked if it does not have curly braces)") + .param("isNaked", BOOLEAN) + .returns(VOID) + .action("clear") + .tooltip("Clears the contents of this scope (untested)") + .returns(VOID) + .action("cfg") + .tooltip("CFG tester") + .returns(STRING) + .action("dfg") + .tooltip("DFG tester") + .returns(STRING); + + joinPoint("body").extending("scope"); + + joinPoint("loop").extending("statement") + .defaultAttribute("kind") + .attribute("kind", enumRef("LoopKind")) + .attribute("id", STRING, "Uniquely identifies the loop inside the program") + .attribute("isInnermost", BOOLEAN) + .attribute("isOutermost", BOOLEAN) + .attribute("nestedLevel", INT) + .attribute("controlVar", STRING) + .attribute("controlVarref", jpRef("varref")) + .attribute("rank", array(INT)) + .attribute("isParallel", BOOLEAN) + .attribute("iterations", INTEGER) + .attribute("iterationsExpr", jpRef("expression")) + .attribute("isInterchangeable") + .tooltip("True if this loop can be interchanged with the given loop, which means that they are adjacent and have no data dependencies that would prevent their interchange. This is a conservative test.") + .param("otherLoop", jpRef("loop")) + .returns(BOOLEAN) + .attribute("init", jpRef("statement"), "The statement of the loop initialization") + .attribute("initValue", STRING, "The expression of the first value of the control variable (e.g. '0' in 'size_t i = 0;')") + .attribute("cond", jpRef("statement"), "The statement of the loop condition") + .attribute("step", jpRef("statement"), "The statement of the loop step") + .attribute("endValue", STRING, "The expression of the last value of the control variable (e.g. '10' in 'size_t i = 0; i < 10; i++')") + .attribute("stepValue", STRING, "The expression of the step value of the control variable (e.g. '1' in 'size_t i = 0; i < 10; i++')") + .attribute("hasCondRelation", BOOLEAN, "True if the condition of the loop in the canonical format, and is one of: <, <=, >, >=") + .attribute("condRelation", enumRef("Relation")) + .attribute("body", jpRef("scope")) + .action("setKind") + .tooltip("Sets the kind of the loop") + .param("kind", enumRef("LoopKind")) + .returns(VOID) + .action("setInit") + .tooltip("Sets the init statement of the loop") + .param("initCode", STRING) + .returns(VOID) + .action("setInitValue") + .tooltip("Sets the init value of the loop. Works with loops of kind 'for'") + .param("initCode", STRING) + .returns(VOID) + .action("setEndValue") + .tooltip("Sets the end value of the loop. Works with loops of kind 'for'") + .param("initCode", STRING) + .returns(VOID) + .action("setCond") + .tooltip("Sets the conditional statement of the loop. Works with loops of kind 'for'") + .param("condCode", STRING) + .returns(VOID) + .action("setStep") + .tooltip("Sets the step statement of the loop. Works with loops of kind 'for'") + .param("stepCode", STRING) + .returns(VOID) + .action("setIsParallel") + .tooltip("Sets the attribute 'isParallel' of the loop") + .param("isParallel", BOOLEAN) + .returns(VOID) + .action("interchange") + .tooltip("Interchanges two for loops, if possible") + .param("otherLoop", jpRef("loop")) + .returns(VOID) + .action("tile") + .tooltip("Applies loop tiling to this loop") + .param("blockSize", STRING) + .param("reference", jpRef("statement")) + .param("useTernary", BOOLEAN, "true") + .returns(jpRef("statement")) + .action("setCondRelation") + .tooltip("Changes the operator of a canonical condition, if possible. Supported operators: lt, le, gt, ge") + .param("operator", enumRef("Relation")) + .returns(VOID) + .action("setBody") + .tooltip("Sets the body of the loop") + .param("body", jpRef("scope")) + .returns(VOID); + + joinPoint("if").extending("statement") + .attribute("cond", jpRef("expression")) + .attribute("condDecl", jpRef("vardecl")) + .attribute("then", jpRef("scope")) + .attribute("else", jpRef("scope")) + .action("setCond") + .tooltip("Sets the condition of the if") + .param("cond", jpRef("expression")) + .returns(VOID) + .action("setThen") + .tooltip("Sets the body of the if") + .param("then", jpRef("statement")) + .returns(VOID) + .action("setElse") + .tooltip("Sets the body of the else") + .param("else", jpRef("statement")) + .returns(VOID); + + joinPoint("wrapperStmt").extending("statement") + .attribute("kind", enumRef("WrapperStatementKind")) + .attribute("content", jpRef("joinpoint")); + + joinPoint("returnStmt").extending("statement") + .attribute("returnExpr", jpRef("expression")); + + joinPoint("switch").extending("statement") + .attribute("hasDefaultCase", BOOLEAN, "True if there is a default case in this switch statement, false otherwise") + .attribute("getDefaultCase", jpRef("case"), "The default case statement of this switch statement or undefined if it does not have a default case") + .attribute("cases", array(jpRef("case")), "The case statements inside this switch") + .attribute("condition", jpRef("expression")); + + joinPoint("switchCase").extending("statement"); + + joinPoint("case").extending("switchCase") + .attribute("isDefault", BOOLEAN) + .attribute("isEmpty", BOOLEAN, "True if this case does not contain instructions (i.e., it is directly above another case), false otherwise") + .attribute("nextInstruction", jpRef("statement"), "The first statement that is not a case that will be executed by this case statement") + .attribute("instructions", array(jpRef("statement")), "The instructions that are associated with this case in the source code. This does not represent what instructions are actually executed (e.g., if a case does not have a break, does not show instructions of the next case)") + .attribute("nextCase", jpRef("case"), "The case statement that comes after this case, or undefined if there are no more case statements") + .attribute("values", array(jpRef("expression")), "The values that the case statement will match. It can return zero (e.g., 'default:'), one (e.g., 'case 1:') or two (e.g., 'case 2...4:') expressions, depending on the format of the case"); + + joinPoint("default").extending("switchCase"); + + joinPoint("declStmt").extending("statement") + .attribute("decls", array(jpRef("decl")), "The declarations in this statement"); + + joinPoint("exprStmt").extending("statement") + .attribute("expr", jpRef("expression"), "The expression join point associated to this exprStmt"); + + joinPoint("gotoStmt").extending("statement") + .attribute("label", jpRef("labelDecl")) + .action("setLabel") + .tooltip("Sets the label of the goto") + .param("label", jpRef("labelDecl")) + .returns(VOID); + + joinPoint("labelStmt").extending("statement") + .attribute("decl", jpRef("labelDecl")) + .action("setDecl") + .tooltip("Sets the label of the label statement") + .param("label", jpRef("labelDecl")) + .returns(VOID); + + joinPoint("emptyStmt").extending("statement"); + + joinPoint("continue").extending("statement"); + + joinPoint("break").extending("statement") + .attribute("enclosingStmt", jpRef("statement"), "The enclosing statement related to this break. It should be either a loop or a switch statement."); + + joinPoint("asmStmt").extending("statement") + .attribute("isSimple", BOOLEAN) + .attribute("isVolatile", BOOLEAN) + .attribute("clobbers", array(STRING)); + + // --- Expressions --- + + joinPoint("expression") + .attribute("decl", jpRef("decl"), "A 'decl' join point that represents the declaration associated with this expression, or undefined if there is none") + .attribute("vardecl", jpRef("vardecl"), "A 'vardecl' join point that represents the variable declaration associated with this expression, or undefined if there is none") + .attribute("use", enumRef("ExpressionUse")) + .attribute("isFunctionArgument", BOOLEAN, "True if the expression is part of an argument of a function call") + .attribute("implicitCast", jpRef("cast"), "Returns a cast joinpoint if this expression has an associated implicit cast, undefined otherwise"); + + joinPoint("call").extending("expression") + .defaultAttribute("name") + .attribute("name", STRING) + .attribute("numArgs", INT) + .attribute("memberNames", array(STRING)) + .attribute("declaration", jpRef("function"), "A 'function' join point that represents the function of the call that was found, it can return either an implementation or a function prototype; 'undefined' if no declaration was found") + .attribute("definition", jpRef("function"), "A 'function' join point that represents the function definition of the call; 'undefined' if no definition was found") + .attribute("argList", array(jpRef("expression")), "[DEPRECATED:] An alias for 'args'") + .attribute("args", array(jpRef("expression")), "An array with the arguments of the call") + .attribute("getArg") + .param("index", INT) + .returns(jpRef("expression")) + .attribute("returnType", jpRef("type"), "The return type of the call") + .attribute("functionType", jpRef("functionType"), "The function type of the call, which includes the return type and the types of the parameters") + .attribute("isMemberAccess", BOOLEAN) + .attribute("memberAccess", jpRef("memberAccess")) + .attribute("isStmtCall", BOOLEAN) + .attribute("function", jpRef("function"), "A function join point associated with this call. If a definition is present, it is given priority over returning a declaration. If only declarations are present, returns a declaration") + .attribute("signature", STRING, "Similar to $function.signature, but if no function decl could be found (e.g., function from system include), returns a signature based on just the name of the function") + .attribute("directCallee", jpRef("function"), "A function join point that represents the 'raw' function of the call (e.g. if this is a call to a templated function, returns a declaration representing the template specialization, instead of the original function)") + .action("setName") + .tooltip("Changes the name of the call") + .param("name", STRING) + .returns(VOID) + .action("wrap") + .tooltip("Wraps this call with a possibly new wrapping function") + .param("name", STRING) + .returns(VOID) + .action("inline") + .tooltip("Tries to inline this call") + .returns(BOOLEAN) + .action("setArgFromString") + .param("index", INT) + .param("expr", STRING) + .returns(VOID) + .action("setArg") + .param("index", INT) + .param("expr", jpRef("expression")) + .returns(VOID) + .action("addArg") + .tooltip("Adds an argument at the end of the call, creating an expression using the given code and type. If a type is not provided, a dummy type is used") + .param("argCode", STRING) + .param("type", jpRef("type"), "null") + .returns(VOID) + .action("addArg") + .tooltip("Adds an argument at the end of the call, creating a literal 'type' from the type string") + .param("arg", STRING) + .param("type", STRING) + .returns(VOID); + + joinPoint("memberCall").extending("call") + .attribute("base", jpRef("expression")) + .attribute("rootBase", jpRef("expression")); + + joinPoint("cudaKernelCall").extending("call") + .attribute("config", array(jpRef("expression"))) + .action("setConfig") + .param("args", array(jpRef("expression"))) + .returns(VOID) + .action("setConfigFromStrings") + .param("args", array(STRING)) + .returns(VOID); + + joinPoint("op").extending("expression") + .attribute("operator", STRING) + .attribute("kind", enumRef("OpKind"), "The kind of the operator. If it is a binary operator, can be one of: ptr_mem_d, ptr_mem_i, mul, div, rem, add, sub, shl, shr, cmp, lt, gt, le, ge, eq, ne, and, xor, or, l_and, l_or, assign, mul_assign, div_assign, rem_assign, add_assign, sub_assign, shl_assign, shr_assign, and_assign, xor_assign, or_assign, comma. If it is a unary operator, can be one of: post_inc, post_dec, pre_inc, pre_dec, addr_of, deref, plus, minus, not, l_not, real, imag, extension, cowait. If it is a ternary operator, the value will be 'ternary'") + .attribute("isBitwise", BOOLEAN); + + joinPoint("binaryOp").extending("op") + .attribute("left", jpRef("expression")) + .attribute("right", jpRef("expression")) + .attribute("isAssignment", BOOLEAN) + .action("setLeft") + .param("left", jpRef("expression")) + .returns(VOID) + .action("setRight") + .param("right", jpRef("expression")) + .returns(VOID); + + joinPoint("unaryOp").extending("op") + .attribute("operand", jpRef("expression")) + .attribute("isPointerDeref", BOOLEAN); + + joinPoint("ternaryOp").extending("op") + .attribute("cond", jpRef("expression")) + .attribute("trueExpr", jpRef("expression")) + .attribute("falseExpr", jpRef("expression")); + + joinPoint("newExpr").extending("expression"); + + joinPoint("deleteExpr").extending("expression"); + + joinPoint("varref").extending("expression") + .tooltip("A reference to a variable") + .defaultAttribute("name") + .attribute("name", STRING) + .attribute("kind", STRING) + .attribute("useExpr", jpRef("expression"), "Expression from where the attribute 'use' is calculated. In certain cases (e.g., array access, pointer dereference) the 'use' attribute is not calculated on the node itself, but on an ancestor of the node. This attribute returns that node") + .attribute("isFunctionCall", BOOLEAN, "True if this varref represents a function call") + .attribute("declaration", jpRef("declarator")) + .attribute("property", STRING, "If this variable reference has a MS-style property, returns the property name. Returns undefined otherwise") + .attribute("hasProperty", BOOLEAN, "True if this variable reference has a MS-style property, false otherwise") + .action("setName") + .param("name", STRING) + .returns(VOID); + + joinPoint("cast").extending("expression") + .attribute("isImplicitCast", BOOLEAN, "[DEPRECATED: Use expr.implicitCast instead]") + .attribute("fromType", jpRef("type")) + .attribute("toType", jpRef("type")) + .attribute("subExpr", jpRef("expression")); + + joinPoint("parenExpr").extending("expression") + .attribute("subExpr", jpRef("expression"), "Returns the expression inside this parenthesis expression"); + + joinPoint("arrayAccess").extending("expression") + .attribute("arrayVar", jpRef("expression"), "Expression representing the variable of the array access (can be a varref, memberAccess...)") + .attribute("subscript", array(jpRef("expression")), "Expression of the array access subscript") + .attribute("parentAccess", jpRef("arrayAccess"), "A view of the current arrayAccess without the last subscript, or undefined if this arrayAccess only has one subscript") + .attribute("numSubscripts", INT, "The number of subscripts of this array access") + .attribute("name", STRING, "If the array access is done over a variable, returns the name of the variable. Equivalent to $arrayAccess.arrayVar.name"); + + joinPoint("memberAccess").extending("expression") + .attribute("name", STRING) + .attribute("memberChain", array(jpRef("expression"))) + .attribute("memberChainNames", array(STRING)) + .attribute("base", jpRef("expression"), "Expression of the base of this member access") + .attribute("arrow", BOOLEAN, "True if this is a member access that uses arrow (i.e., foo->bar), false if uses dot (i.e., foo.bar)") + .action("setArrow") + .param("isArrow", BOOLEAN) + .returns(VOID); + + joinPoint("unaryExprOrType").extending("expression") + .attribute("kind", STRING) + .attribute("hasTypeExpr", BOOLEAN) + .attribute("hasArgExpr", BOOLEAN) + .attribute("argType", jpRef("type")) + .attribute("argExpr", jpRef("expression")) + .action("setArgType") + .param("argType", jpRef("type")) + .returns(VOID); + + joinPoint("This").extending("expression"); + + joinPoint("literal").extending("expression"); + + joinPoint("intLiteral").extending("literal") + .attribute("value", LONG); + + joinPoint("floatLiteral").extending("literal") + .attribute("value", DOUBLE); + + joinPoint("boolLiteral").extending("literal") + .attribute("value", BOOLEAN); + + joinPoint("initList").extending("expression") + .attribute("arrayFiller", jpRef("expression"), "[May be undefined] If this initializer list initializes an array with more elements than there are initializers in the list, specifies an expression to be used for value initialization of the rest of the elements"); + + joinPoint("implicitValue").extending("expression"); + + // --- Comment --- + + joinPoint("comment") + .attribute("text", STRING) + .action("setText") + .param("text", STRING) + .returns(VOID); + + // --- Cilk --- + + joinPoint("cilkFor").extending("loop"); + + joinPoint("cilkSync").extending("statement"); + + joinPoint("cilkSpawn").extending("call"); + + // --- Attribute --- + + joinPoint("attribute") + .attribute("kind", STRING); + + // --- Types --- + + joinPoint("type") + .attribute("kind", STRING) + .attribute("isTopLevel", BOOLEAN) + .attribute("isArray", BOOLEAN) + .attribute("isPointer", BOOLEAN) + .attribute("isAuto", BOOLEAN, "True if this is a type declared with the 'auto' keyword") + .attribute("arraySize", INT) + .attribute("arrayDims", array(INT)) + .attribute("hasTemplateArgs", BOOLEAN) + .attribute("templateArgsStrings", array(STRING)) + .attribute("templateArgsTypes", array(jpRef("type"))) + .attribute("hasSugar", BOOLEAN) + .attribute("desugar", jpRef("type"), "Single-step desugar. Returns the type itself if it does not have sugar") + .attribute("desugarAll", jpRef("type"), "Completely desugars the type") + .attribute("isBuiltin", BOOLEAN) + .attribute("constant", BOOLEAN) + .attribute("unwrap", jpRef("type"), "If the type encapsulates another type, returns the encapsulated type") + .attribute("normalize", jpRef("type"), "Ignores certain types (e.g., DecayedType)") + .attribute("typeFields", map(STRING, jpRef("type")), "Maps names of join point fields that represent type join points, to their respective values") + .attribute("fieldTree", STRING, "A tree representation of the fields of this type") + .action("setTemplateArgsTypes") + .tooltip("Sets the template argument types of a template type") + .param("templateArgTypes", array(jpRef("type"))) + .returns(VOID) + .action("setTemplateArgType") + .tooltip("Sets a single template argument type of a template type") + .param("index", INT) + .param("templateArgType", jpRef("type")) + .returns(VOID) + .action("setDesugar") + .tooltip("Sets the desugared type of this type") + .param("desugaredType", jpRef("type")) + .returns(VOID) + .action("setTypeFieldByValueRecursive") + .tooltip("Changes a single occurrence of a type field that has the current value with new value. Returns true if there was a change") + .param("currentValue", OBJECT) + .param("newValue", OBJECT) + .returns(BOOLEAN) + .action("setUnderlyingType") + .tooltip("Replaces an underlying type of this instance with new type, if it matches the old type") + .param("oldValue", jpRef("type")) + .param("newValue", jpRef("type")) + .returns(jpRef("type")) + .action("asConst") + .tooltip("Returns a new node based on this type with the qualifier const") + .returns(jpRef("type")); + + joinPoint("pointerType").extending("type") + .attribute("pointee", jpRef("type")) + .attribute("pointerLevels", INT, "Number of pointer levels from this pointer") + .action("setPointee") + .tooltip("Sets the pointee type of this pointer type") + .param("pointeeType", jpRef("type")) + .returns(VOID); + + joinPoint("arrayType").extending("type") + .attribute("elementType", jpRef("type")) + .action("setElementType") + .tooltip("Sets the element type of the array") + .param("arrayElementType", jpRef("type")) + .returns(VOID); + + joinPoint("adjustedType").extending("type") + .attribute("originalType", jpRef("type"), "The type that is being adjusted"); + + joinPoint("variableArrayType").extending("arrayType") + .attribute("sizeExpr", jpRef("expression")) + .action("setSizeExpr") + .tooltip("Sets the size expression of this variable array type") + .param("sizeExpr", jpRef("expression")) + .returns(VOID); + + joinPoint("incompleteArrayType").extending("arrayType"); + + joinPoint("tagType").extending("type") + .attribute("name", STRING) + .attribute("decl", jpRef("decl"), "A 'decl' join point that represents the declaration of this tag type"); + + joinPoint("enumType").extending("tagType") + .attribute("integerType", jpRef("type")); + + joinPoint("templateSpecializationType").extending("type") + .attribute("templateName", STRING) + .attribute("numArgs", INT) + .attribute("args", array(STRING)) + .attribute("firstArgType", jpRef("type")); + + joinPoint("functionType").extending("type") + .attribute("returnType", jpRef("type")) + .attribute("paramTypes", array(jpRef("type"))) + .action("setReturnType") + .tooltip("Sets the return type of the FunctionType") + .param("newType", jpRef("type")) + .returns(VOID) + .action("setParamType") + .tooltip("Sets the type of a parameter of the FunctionType. Be careful that if you directly change the type of a parameter and the function type is associated with a function declaration, this change will not be reflected in the function. If you want to change the type of a parameter of a function declaration, use function.setParamType") + .param("index", INT) + .param("newType", jpRef("type")) + .returns(VOID); + + joinPoint("qualType").extending("type") + .attribute("qualifiers", array(STRING)) + .attribute("unqualifiedType", jpRef("type")); + + joinPoint("builtinType").extending("type") + .attribute("builtinKind", STRING) + .attribute("isInteger", BOOLEAN, "True, if it is an integer type") + .attribute("isFloat", BOOLEAN, "True, if it is a floating type (e.g., float, double)") + .attribute("isSigned", BOOLEAN, "True, if it is a signed type") + .attribute("isUnsigned", BOOLEAN, "True, if it is an unsigned type") + .attribute("isVoid", BOOLEAN, "True, if it is a void type"); + + joinPoint("parenType").extending("type") + .attribute("innerType", jpRef("type")) + .action("setInnerType") + .tooltip("Sets the inner type of this paren type") + .param("innerType", jpRef("type")) + .returns(VOID); + + joinPoint("undefinedType").extending("type"); + + joinPoint("elaboratedType").extending("type") + .tooltip( + "Represents a type that was referred to using an elaborated type keyword, e.g., struct S, or via a qualified name, e.g., N::M::type, or both. This type is used to keep track of a type name as written in the source code, including tag keywords and any nested-name-specifiers. The type itself is always 'sugar', used to express what was written in the source code but containing no additional semantic information.") + .attribute("keyword", STRING, "The keyword of this elaborated type, if present. Can be one of: struct, interface, union, class, enum, typename") + .attribute("qualifier", STRING,"The qualifier of this elaborated type, if present (e.g., A::)") + .attribute("namedType", jpRef("type"), "The type that is being prefixed with the qualifier"); + + joinPoint("typedefType").extending("type") + .attribute("decl", jpRef("typedefNameDecl"), "The typedef declaration associated with this typedef type") + .attribute("underlyingType", jpRef("type"), "The type being aliased"); + } +} diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/ClavaWeaverResource.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/ClavaWeaverResource.java deleted file mode 100644 index 99202e6be5..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/ClavaWeaverResource.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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 pt.up.fe.specs.clava.weaver; - -import pt.up.fe.specs.util.providers.ResourceProvider; - -/** - * @author Joao Bispo - * - */ -public enum ClavaWeaverResource implements ResourceProvider { - JOINPOINTS("joinPointModel.xml"), - ARTIFACTS("artifacts.xml"), - ACTIONS("actionModel.xml"); - - private final String resource; - - private static final String basePackage = "clava/weaverspecs/"; - - /** - * @param resource - */ - private ClavaWeaverResource(String resource) { - this.resource = basePackage + resource; - } - - /* (non-Javadoc) - * @see org.suikasoft.SharedLibrary.Interfaces.ResourceProvider#getResource() - */ - @Override - public String getResource() { - return resource; - } -} diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxActions.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxActions.java index 930ee3deca..7be3c44b01 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxActions.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxActions.java @@ -14,6 +14,8 @@ package pt.up.fe.specs.clava.weaver; import com.google.common.base.Preconditions; + +import org.lara.interpreter.weaver.interf.enums.InsertPosition; import org.lara.interpreter.weaver.interf.events.Stage; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaNodes; @@ -23,7 +25,7 @@ import pt.up.fe.specs.clava.ast.extra.App; import pt.up.fe.specs.clava.ast.stmt.*; import pt.up.fe.specs.clava.utils.NodePosition; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AScope; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; import pt.up.fe.specs.util.SpecsCheck; @@ -63,13 +65,13 @@ public class CxxActions { * @param position * @param from */ - public static AJoinPoint insertAsStmt(ClavaNode target, String code, Insert insert, CxxWeaver weaver) { + public static AJoinpoint insertAsStmt(ClavaNode target, String code, Insert insert, CxxWeaver weaver) { // If target is part of App, clear caches target.getAncestorTry(App.class).ifPresent(app -> { app.clearCache(); weaver.getEventTrigger().triggerAction(Stage.DURING, - "CxxActions.insertAsStmt", - CxxJoinpoints.create(target, weaver), Arrays.asList(insert, code), Optional.empty()); + CxxJoinpoints.create(target, weaver), + "CxxActions.insertAsStmt", Optional.empty(), Arrays.asList(insert, code)); }); // Convert Insert to NodePosition @@ -99,13 +101,13 @@ private static void checkInsertAfterReturn(ClavaNode base, ClavaNode newNode) { } } - public static AJoinPoint[] insertAsChild(String position, ClavaNode base, ClavaNode node, CxxWeaver weaver) { + public static AJoinpoint[] insertAsChild(String position, ClavaNode base, ClavaNode node, CxxWeaver weaver) { // If base is part of App, clear caches base.getAncestorTry(App.class).ifPresent(app -> { app.clearCache(); weaver.getEventTrigger().triggerAction(Stage.DURING, - "CxxActions.insertAsChild", - CxxJoinpoints.create(base, weaver), Arrays.asList(position, CxxJoinpoints.create(node, weaver)), Optional.empty()); + CxxJoinpoints.create(base, weaver), + "CxxActions.insertAsChild", Optional.empty(), Arrays.asList(position, CxxJoinpoints.create(node, weaver))); }); switch (position) { @@ -132,7 +134,7 @@ public static AJoinPoint[] insertAsChild(String position, ClavaNode base, ClavaN // // Remove all children // base.removeChildren(0, base.getNumChildren()); base.addChild(node); - return new AJoinPoint[]{CxxJoinpoints.create(node, weaver)}; + return new AJoinpoint[]{CxxJoinpoints.create(node, weaver)}; default: throw new RuntimeException("Case not defined:" + position); } @@ -156,10 +158,10 @@ public static ClavaNode replace(ClavaNode target, ClavaNode newNode, CxxWeaver w return NodeInsertUtils.replace(target, newNode); } - public static AJoinPoint insertBefore(AJoinPoint baseJp, AJoinPoint newJp, CxxWeaver weaver) { + public static AJoinpoint insertBefore(AJoinpoint baseJp, AJoinpoint newJp, CxxWeaver weaver) { return insert(baseJp, newJp, Insert.BEFORE, (base, node) -> NodeInsertUtils.insertBefore(base, node), weaver); - // Stmt newStmt = ClavaNodes.toStmt(newJp.getNode()); - // Stmt baseStmt = getValidStatement(baseJp.getNode(), Insert.BEFORE); + // Stmt newStmt = ClavaNodes.toStmt(newJp.getNodeImpl()); + // Stmt baseStmt = getValidStatement(baseJp.getNodeImpl(), Insert.BEFORE); // if (baseStmt == null) { // return null; // } @@ -168,14 +170,14 @@ public static AJoinPoint insertBefore(AJoinPoint baseJp, AJoinPoint newJp, CxxWe // return CxxJoinpoints.create(newStmt); } - public static AJoinPoint insertAfter(AJoinPoint baseJp, AJoinPoint newJp, CxxWeaver weaver) { - checkInsertAfterReturn(baseJp.getNode(), newJp.getNode()); + public static AJoinpoint insertAfter(AJoinpoint baseJp, AJoinpoint newJp, CxxWeaver weaver) { + checkInsertAfterReturn(baseJp.getNodeImpl(), newJp.getNodeImpl()); return insert(baseJp, newJp, Insert.AFTER, (base, node) -> NodeInsertUtils.insertAfter(base, node), weaver); // // If inside a scope, treat nodes at the statement level // // if - // Stmt newStmt = ClavaNodes.toStmt(newJp.getNode()); - // Stmt baseStmt = getValidStatement(baseJp.getNode(), Insert.AFTER); + // Stmt newStmt = ClavaNodes.toStmt(newJp.getNodeImpl()); + // Stmt baseStmt = getValidStatement(baseJp.getNodeImpl(), Insert.AFTER); // if (baseStmt == null) { // return null; // } @@ -184,36 +186,36 @@ public static AJoinPoint insertAfter(AJoinPoint baseJp, AJoinPoint newJp, CxxWea // return CxxJoinpoints.create(newStmt); } - public static AJoinPoint insert(AJoinPoint baseJp, - AJoinPoint newJp, Insert position, + public static AJoinpoint insert(AJoinpoint baseJp, + AJoinpoint newJp, Insert position, BiConsumer insertFunction, CxxWeaver weaver) { // Set origin point from target to newNode if locations are invalid and no origin point is set - var newNode = newJp.getNode(); - var target = baseJp.getNode(); + var newNode = newJp.getNodeImpl(); + var target = baseJp.getNodeImpl(); newNode.setOrigin(target); // Special case: if this node is a statement in a loop header, insert using a special function. if (baseJp.getIsInsideLoopHeaderImpl() && (position != Insert.REPLACE && position != Insert.AROUND) - && baseJp.getNode() instanceof Stmt) { + && baseJp.getNodeImpl() instanceof Stmt) { return insertInLoopHeader(baseJp, newJp, position); } // If baseJp will do a statement-base insertion, adapt nodes // Check if base is inside a scope - boolean isInsideScope = baseJp.getNode().getAncestorTry(CompoundStmt.class).isPresent(); + boolean isInsideScope = baseJp.getNodeImpl().getAncestorTry(CompoundStmt.class).isPresent(); - // Optional targetStmt = ClavaNodes.getStatement(baseJp.getNode()); - ClavaNode adaptedBase = isInsideScope ? ClavaNodes.getValidStatement(baseJp.getNode(), position.toPosition()) - : baseJp.getNode(); + // Optional targetStmt = ClavaNodes.getStatement(baseJp.getNodeImpl()); + ClavaNode adaptedBase = isInsideScope ? ClavaNodes.getValidStatement(baseJp.getNodeImpl(), position.toPosition()) + : baseJp.getNodeImpl(); if (adaptedBase == null) { return null; } - ClavaNode adaptedNew = isInsideScope ? ClavaNodes.toStmt(newJp.getNode()) : newJp.getNode(); + ClavaNode adaptedNew = isInsideScope ? ClavaNodes.toStmt(newJp.getNodeImpl()) : newJp.getNodeImpl(); // If adaptedNew is not a comment or a pragma, and we are inserting before, adaptedBase should be the first // comment or pragma associated with current base @@ -229,23 +231,23 @@ public static AJoinPoint insert(AJoinPoint baseJp, // If base is part of App, clear caches adaptedBase.getAncestorTry(App.class).ifPresent(app -> { app.clearCache(); - weaver.getEventTrigger().triggerAction(Stage.DURING, "CxxActions.insert", - baseJp, - Arrays.asList(position, newJp), Optional.ofNullable((Object) returnedJp)); + weaver.getEventTrigger().triggerAction(Stage.DURING, baseJp, + "CxxActions.insert", + Optional.ofNullable((Object) returnedJp), Arrays.asList(position, newJp)); }); return returnedJp; } - private static AJoinPoint insertInLoopHeader(AJoinPoint baseJp, AJoinPoint newJp, Insert position) { + private static AJoinpoint insertInLoopHeader(AJoinpoint baseJp, AJoinpoint newJp, Insert position) { // Check position if (position != Insert.BEFORE && position != Insert.AFTER) { throw new RuntimeException("Insertion position not supported: " + position); } // System.out.println("#ASDASDSAD"); - var baseNode = baseJp.getNode(); - var newNode = newJp.getNode(); + var baseNode = baseJp.getNodeImpl(); + var newNode = newJp.getNodeImpl(); // System.out.println("BASE NODE: " + baseNode.getClass()); // If DeclStmt, insert as new initialization if (baseNode instanceof DeclStmt) { @@ -323,10 +325,10 @@ private static AJoinPoint insertInLoopHeader(AJoinPoint baseJp, AJoinPoint newJp * @param weaver * @return */ - public static AJoinPoint insertJpAsStatement(AJoinPoint baseJp, AJoinPoint newJp, String position, + public static AJoinpoint insertJpAsStatement(AJoinpoint baseJp, AJoinpoint newJp, String position, CxxWeaver weaver) { - AStatement stmtJp = CxxJoinpoints.create(ClavaNodes.toStmt(newJp.getNode()), weaver, AStatement.class); + AStatement stmtJp = CxxJoinpoints.create(ClavaNodes.toStmt(newJp.getNodeImpl()), weaver, AStatement.class); return insertJp(baseJp, stmtJp, position, weaver); } @@ -338,29 +340,29 @@ public static AJoinPoint insertJpAsStatement(AJoinPoint baseJp, AJoinPoint newJp * @param newJpS * @param position */ - public static AJoinPoint insertJp(AJoinPoint baseJp, AJoinPoint newJp, String position, CxxWeaver weaver) { + public static AJoinpoint insertJp(AJoinpoint baseJp, AJoinpoint newJp, String position, CxxWeaver weaver) { // If baseJp is part of App, clear caches - baseJp.getNode().getAncestorTry(App.class).ifPresent(app -> { + baseJp.getNodeImpl().getAncestorTry(App.class).ifPresent(app -> { app.clearCache(); weaver.getEventTrigger().triggerAction(Stage.DURING, - "CxxActions.insertJp", - baseJp, Arrays.asList(position, newJp), Optional.empty()); + baseJp, + "CxxActions.insertJp", Optional.empty(), Arrays.asList(position, newJp)); }); switch (position) { case "before": - var newBase = ClavaNodes.getFirstNodeOfTargetRegion(baseJp.getNode(), newJp.getNode()); - NodeInsertUtils.insertBefore(newBase, newJp.getNode()); + var newBase = ClavaNodes.getFirstNodeOfTargetRegion(baseJp.getNodeImpl(), newJp.getNodeImpl()); + NodeInsertUtils.insertBefore(newBase, newJp.getNodeImpl()); break; case "after": - NodeInsertUtils.insertAfter(baseJp.getNode(), newJp.getNode()); + NodeInsertUtils.insertAfter(baseJp.getNodeImpl(), newJp.getNodeImpl()); break; case "around": case "replace": - weaver.clearUserField(baseJp.getNode()); - NodeInsertUtils.replace(baseJp.getNode(), newJp.getNode()); + weaver.clearUserField(baseJp.getNodeImpl()); + NodeInsertUtils.replace(baseJp.getNodeImpl(), newJp.getNodeImpl()); break; default: @@ -370,25 +372,24 @@ public static AJoinPoint insertJp(AJoinPoint baseJp, AJoinPoint newJp, String po return newJp; } - public static void insertStmt(String position, Stmt body, Stmt stmt, CxxWeaver weaver) { + public static void insertStmt(InsertPosition position, Stmt body, Stmt stmt, CxxWeaver weaver) { Preconditions.checkArgument(body instanceof CompoundStmt); // If body is part of App, clear caches body.getAncestorTry(App.class).ifPresent(app -> { app.clearCache(); weaver.getEventTrigger().triggerAction(Stage.DURING, - "CxxActions.insertStmt", - CxxJoinpoints.create(body, weaver), Arrays.asList(position, CxxJoinpoints.create(stmt, weaver)), Optional.empty()); + CxxJoinpoints.create(body, weaver), + "CxxActions.insertStmt", Optional.empty(), Arrays.asList(position, CxxJoinpoints.create(stmt, weaver))); }); switch (position) { - case "before": + case BEFORE: // Insert before all statements in body body.addChild(0, stmt); break; - case "after": - + case AFTER: if (body.hasChildren()) { checkInsertAfterReturn(body.getChild(body.getNumChildren() - 1), stmt); } @@ -396,8 +397,7 @@ public static void insertStmt(String position, Stmt body, Stmt stmt, CxxWeaver w body.addChild(stmt); break; - case "around": - case "replace": + case REPLACE: // Remove all children removeChildren(body, weaver); // Add given statement @@ -413,8 +413,8 @@ public static void removeChildren(ClavaNode node, CxxWeaver weaver) { node.getAncestorTry(App.class).ifPresent(app -> { app.clearCache(); weaver.getEventTrigger().triggerAction(Stage.DURING, - "CxxActions.removeChildren", - CxxJoinpoints.create(node, weaver), Collections.emptyList(), Optional.empty()); + CxxJoinpoints.create(node, weaver), + "CxxActions.removeChildren", Optional.empty(), Collections.emptyList()); }); // Clear use fields @@ -426,11 +426,11 @@ public static void removeChildren(ClavaNode node, CxxWeaver weaver) { node.removeChildren(0, node.getNumChildren()); } - public static AJoinPoint insertReturn(AScope scope, AJoinPoint code, CxxWeaver weaver) { + public static AJoinpoint insertReturn(AScope scope, AJoinpoint code, CxxWeaver weaver) { // Does not take into account situations where functions returns in all paths of an if/else. // This means it can lead to dead-code, although for C/C++ that does not seem to be problematic. - List bodyStmts = ((CompoundStmt) scope.getNode()).toStatements(); + List bodyStmts = ((CompoundStmt) scope.getNodeImpl()).toStatements(); // Check if it has return statement, ignoring wrapper statements Stmt lastStmt = SpecsCollections.reverseStream(bodyStmts) @@ -446,14 +446,14 @@ public static AJoinPoint insertReturn(AScope scope, AJoinPoint code, CxxWeaver w .map(ReturnStmt.class::cast) .collect(Collectors.toList()); - AJoinPoint lastInsertPoint = null; + AJoinpoint lastInsertPoint = null; if (lastReturnStmt != null) { returnStatements = SpecsCollections.concat(returnStatements, lastReturnStmt); } for (ReturnStmt returnStmt : returnStatements) { - AJoinPoint returnJp = CxxJoinpoints.create(returnStmt, weaver); + AJoinpoint returnJp = CxxJoinpoints.create(returnStmt, weaver); lastInsertPoint = returnJp.insertBeforeImpl(code); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxAttributes.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxAttributes.java index bb51427241..a3edcb830d 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxAttributes.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxAttributes.java @@ -28,9 +28,8 @@ import pt.up.fe.specs.clava.ast.stmt.CompoundStmt; import pt.up.fe.specs.clava.ast.stmt.LoopStmt; import pt.up.fe.specs.clava.utils.StmtWithCondition; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.enums.AExpressionUseEnum; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; +import pt.up.fe.specs.clava.weaver.enums.ExpressionUse; public class CxxAttributes { @@ -164,7 +163,7 @@ public static Optional getParentRegion(ClavaNode node) { // Get current region Optional currentRegionTry = getCurrentRegion(node); if (!currentRegionTry.isPresent()) { - // ClavaLog.info("Join point '" + getJoinPointType() + "' does not support parentRegion"); + // ClavaLog.info("Join point '" + joinPointType() + "' does not support parentRegion"); return Optional.empty(); } @@ -187,14 +186,14 @@ public static Optional getParentRegion(ClavaNode node) { // return CxxJoinpoints.create(getCurrentRegion(currentRegion.getParent()), this); } - public static String convertUse(ExprUse use) { + public static ExpressionUse convertUse(ExprUse use) { switch (use) { case READ: - return AExpressionUseEnum.READ.getName(); + return ExpressionUse.READ; case WRITE: - return AExpressionUseEnum.WRITE.getName(); + return ExpressionUse.WRITE; case READWRITE: - return AExpressionUseEnum.READWRITE.getName(); + return ExpressionUse.READWRITE; default: throw new RuntimeException("Case not defined:" + use); } @@ -246,18 +245,17 @@ public static Object fromLara(Object value) { // Special cases // If join point , convert to Clava node - if (value instanceof AJoinPoint) { - return ((ACxxWeaverJoinPoint) value).getNode(); + if (value instanceof AJoinpoint jp) { + return jp.getNodeImpl(); } // If CxxWeaverDataClass, unwrap to conventional DataClass - if (value instanceof CxxWeaverDataClass) { - return ((CxxWeaverDataClass) value).getOriginalData(); + if (value instanceof CxxWeaverDataClass weaverDataClass) { + return weaverDataClass.getOriginalData(); } // If a List, apply adapt over all elements of the list - if (value instanceof List) { - var valueList = (List) value; + if (value instanceof List valueList) { var newValue = new ArrayList(valueList.size()); for (var valueElement : valueList) { diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java index 4c9aec82d9..c37be1c142 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java @@ -31,8 +31,7 @@ import pt.up.fe.specs.clava.ast.stmt.*; import pt.up.fe.specs.clava.ast.type.*; import pt.up.fe.specs.clava.utils.NullNode; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.joinpoints.*; import pt.up.fe.specs.clava.weaver.joinpoints.cilk.CxxCilkFor; import pt.up.fe.specs.clava.weaver.joinpoints.cilk.CxxCilkSpawn; @@ -48,7 +47,7 @@ public class CxxJoinpoints { - private static final BiFunctionClassMap JOINPOINT_FACTORY; + private static final BiFunctionClassMap> JOINPOINT_FACTORY; static { JOINPOINT_FACTORY = new BiFunctionClassMap<>(); @@ -143,33 +142,33 @@ public class CxxJoinpoints { JOINPOINT_FACTORY.put(ClavaNode.class, CxxJoinpoints::defaultFactory); } - private static ACxxWeaverJoinPoint nullNode(ClavaNode node, CxxWeaver weaver) { + private static AJoinpoint nullNode(ClavaNode node, CxxWeaver weaver) { SpecsCheck.checkArgument(node instanceof NullNode, () -> "Expected an instance of NullNode, received: " + node); return null; } - private static ACxxWeaverJoinPoint compoundStmtFactory(CompoundStmt stmt, CxxWeaver weaver) { + private static AJoinpoint compoundStmtFactory(CompoundStmt stmt, CxxWeaver weaver) { // If no parent, use Scope as default if (!stmt.hasParent()) { - return new CxxScope(stmt, weaver); + return new CxxScope<>(stmt, weaver); } // If CompoundStmt parent is another CompoundStmt, is a Scope. if (stmt.getParent() instanceof CompoundStmt) { - return new CxxScope(stmt, weaver); + return new CxxScope<>(stmt, weaver); } // Otherwise, is a Body - return new CxxBody(stmt, weaver); + return new CxxBody<>(stmt, weaver); } - private static ACxxWeaverJoinPoint defaultFactory(ClavaNode node, CxxWeaver weaver) { + private static AJoinpoint defaultFactory(ClavaNode node, CxxWeaver weaver) { SpecsLogs.warn("Factory not defined for nodes of class '" + node.getClass().getSimpleName() + "'"); - return new GenericJoinpoint(node, weaver); + return new GenericJoinpoint<>(node, weaver); } - public static ACxxWeaverJoinPoint createFromLara(Object node, CxxWeaver weaver) { + public static AJoinpoint createFromLara(Object node, CxxWeaver weaver) { if (!(node instanceof ClavaNode)) { throw new RuntimeException( "Expected input to be a ClavaNode, is " + node.getClass().getSimpleName() + ": " + node); @@ -178,7 +177,7 @@ public static ACxxWeaverJoinPoint createFromLara(Object node, CxxWeaver weaver) return create((ClavaNode) node, weaver); } - public static ACxxWeaverJoinPoint create(ClavaNode node, CxxWeaver weaver) { + public static AJoinpoint create(ClavaNode node, CxxWeaver weaver) { if (node == null) { ClavaLog.debug("CxxJoinpoints: tried to create join point from null node, returning undefined"); return null; @@ -187,7 +186,7 @@ public static ACxxWeaverJoinPoint create(ClavaNode node, CxxWeaver weaver) { return JOINPOINT_FACTORY.apply(node, weaver); } - public static T create(ClavaNode node, CxxWeaver weaver, Class targetClass) { + public static > T create(ClavaNode node, CxxWeaver weaver, Class targetClass) { if (targetClass == null) { throw new RuntimeException("Check if you meant to call 'create' with a single argument"); } @@ -195,24 +194,24 @@ public static T create(ClavaNode node, CxxWeaver weaver, return targetClass.cast(create(node, weaver)); } - public static T[] create(List nodes, CxxWeaver weaver, Class targetClass) { + public static > T[] create(List nodes, CxxWeaver weaver, Class targetClass) { return nodes.stream() .map(node -> create(node, weaver, targetClass)) .toArray(size -> SpecsCollections.newArray(targetClass, size)); } - public static CxxProgram getProgram(AJoinPoint joinpoint) { - AJoinPoint currentJp = joinpoint; + public static CxxProgram getProgram(AJoinpoint joinpoint) { + AJoinpoint currentJp = joinpoint; while (currentJp.getHasParentImpl()) { currentJp = currentJp.getParentImpl(); } // Check that root node is a CxxProgram - if (!(currentJp instanceof CxxProgram)) { - throw new RuntimeException("Expected root node to be of type '" + CxxProgram.class + "'"); + if (currentJp instanceof CxxProgram program) { + return program; } - return (CxxProgram) currentJp; + throw new RuntimeException("Expected root node to be of type '" + CxxProgram.class + "'"); } /** @@ -221,8 +220,8 @@ public static CxxProgram getProgram(AJoinPoint joinpoint) { * @param joinpointClass * @return */ - public static Optional getAncestorandSelf(AJoinPoint joinpoint, Class joinpointClass) { - AJoinPoint currentJp = joinpoint; + public static > Optional getAncestorandSelf(AJoinpoint joinpoint, Class joinpointClass) { + AJoinpoint currentJp = joinpoint; if (joinpointClass.isInstance(currentJp)) { return Optional.of(joinpointClass.cast(currentJp)); @@ -239,7 +238,7 @@ public static Optional getAncestorandSelf(AJoinPoint j return Optional.empty(); } - public static CxxWeaver getWeaver(AJoinPoint joinpoint) { + public static CxxWeaver getWeaver(AJoinpoint joinpoint) { // Get root joinpoint (program) return getProgram(joinpoint).getWeaverEngine(); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java index a9d654354a..dcc1b3ca88 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java @@ -26,14 +26,15 @@ import pt.up.fe.specs.clava.ast.stmt.Stmt; import pt.up.fe.specs.clava.ast.stmt.WrapperStmt; import pt.up.fe.specs.clava.utils.NullNode; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.joinpoints.CxxArrayAccess; +import pt.up.fe.specs.util.SpecsCollections; public class CxxSelects { /** - * Method that helps selecting join points. + * Selects join points. + * * * @param targetJoinpoint * @param directChildren @@ -106,33 +107,28 @@ public static boolean stmtFilter(ClavaNode node) { return true; } - // public static AJoinPoint[] selectedNodesToJps(List selectedNodes, WeaverEngine weaverEngine) - // { - // return selectedNodesToJps(selectedNodes.stream(), jp -> true, weaverEngine); - // } - - public static AJoinPoint[] selectedNodesToJps(Stream selectedNodes, + public static AJoinpoint[] selectedNodesToJps(Stream selectedNodes, CxxWeaver weaverEngine) { return selectedNodesToJps(selectedNodes, jp -> true, weaverEngine); } - public static AJoinPoint[] selectedNodesToJps(Stream selectedNodes, - Predicate filter, CxxWeaver weaverEngine) { + public static AJoinpoint[] selectedNodesToJps(Stream selectedNodes, + Predicate filter, CxxWeaver weaverEngine) { return selectedNodesToJpsStream(selectedNodes, filter, weaverEngine) .collect(Collectors.toList()) - // .toArray(new AJoinPoint[0]); - .toArray(AJoinPoint[]::new); + // .toArray(new AJoinpoint[0]); + .toArray(AJoinpoint[]::new); } - public static Stream selectedNodesToJpsStream(Stream selectedNodes, + public static Stream> selectedNodesToJpsStream(Stream selectedNodes, CxxWeaver weaverEngine) { return selectedNodesToJpsStream(selectedNodes, jp -> true, weaverEngine); } - public static Stream selectedNodesToJpsStream(Stream selectedNodes, - Predicate filter, CxxWeaver weaverEngine) { + public static Stream selectedNodesToJpsStream(Stream selectedNodes, + Predicate filter, CxxWeaver weaverEngine) { var selectedJps = selectedNodes // Ignore null nodes @@ -143,18 +139,18 @@ public static Stream selectedNodesToJpsStream(Stream filter.test(jp)) - // Cast back to AJoinPoint - .map(jp -> (AJoinPoint) jp); + // Cast back to AJoinpoint + .map(jp -> (AJoinpoint) jp); return selectedJps; } - private static boolean defaultSelectFilter(AJoinPoint jp) { + private static boolean defaultSelectFilter(AJoinpoint jp) { // TODO: If more cases, use a ClassMap instead // If ArraySubscript, return only if top-level if (jp instanceof CxxArrayAccess) { - return ((ArraySubscriptExpr) jp.getNode()).isTopLevel(); + return ((ArraySubscriptExpr) jp.getNodeImpl()).isTopLevel(); } return true; diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.java index 58926c96cd..ee675b71f5 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.java @@ -3,10 +3,8 @@ import org.lara.interpreter.joptions.config.interpreter.LaraiKeys; import org.lara.interpreter.weaver.ast.AstMethods; import org.lara.interpreter.weaver.interf.AGear; -import org.lara.interpreter.weaver.interf.JoinPoint; import org.lara.interpreter.weaver.interf.events.Stage; import org.lara.interpreter.weaver.options.WeaverOption; -import org.lara.language.specification.dsl.LanguageSpecification; import org.suikasoft.jOptions.Interfaces.DataStore; import org.suikasoft.jOptions.storedefinition.StoreDefinition; import org.suikasoft.jOptions.storedefinition.StoreDefinitionBuilder; @@ -27,6 +25,7 @@ import pt.up.fe.specs.clava.language.Standard; import pt.up.fe.specs.clava.parsing.snippet.SnippetParser; import pt.up.fe.specs.clava.utils.SourceType; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.weaver.ACxxWeaver; import pt.up.fe.specs.clava.weaver.gears.CacheHandlerGear; import pt.up.fe.specs.clava.weaver.gears.ModifiedFilesGear; @@ -53,7 +52,7 @@ * implementation should be done by extending those * abstract classes with user-defined classes.
* The abstract class - * {@link pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint} can be used + * {@link pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint} can be used * to add user-defined * methods and fields which the user intends to add for all join points and are * not intended to be used in LARA aspects. @@ -62,11 +61,6 @@ */ public class CxxWeaver extends ACxxWeaver { - public static LanguageSpecification buildLanguageSpecification() { - return LanguageSpecification.newInstance(ClavaWeaverResource.JOINPOINTS, ClavaWeaverResource.ARTIFACTS, - ClavaWeaverResource.ACTIONS); - } - private static final List CLAVA_PREDEFINED_EXTERNAL_DEPS = Arrays.asList("LAT - Lara Autotuning Tool", "https://github.com/specs-feup/LAT-Lara-Autotuning-Tool.git", "Benchmark - CHStone (import lara.benchmark.CHStoneBenchmarkSet)", @@ -214,8 +208,8 @@ public Optional getAppTry() { return weaverData.getAst(); } - public CxxProgram getAppJp() { - return new CxxProgram(getApp(), this); + public CxxProgram getAppJp() { + return new CxxProgram<>(getApp(), this); } private Map> getUserValues() { @@ -741,7 +735,7 @@ private static Optional headerFlagToFile(String headerFlag) { * @return an instance of the join point root/program */ @Override - public JoinPoint getRootJp() { + public AJoinpoint getRootJp() { return CxxJoinpoints.create(getApp(), this); } @@ -1106,8 +1100,8 @@ public TranslationUnit rebuildFile(TranslationUnit tUnit) { // After rebuilding, clear current app cache getApp().clearCache(); getEventTrigger().triggerAction(Stage.DURING, - "CxxWeaver.rebuildFile", - CxxJoinpoints.create(tUnit, this), Collections.emptyList(), Optional.empty()); + CxxJoinpoints.create(tUnit, this), + "CxxWeaver.rebuildFile", Optional.empty(), Collections.emptyList()); // Return correct TranslationUnit for (TranslationUnit tu : rebuiltApp.getTranslationUnits()) { @@ -1536,11 +1530,6 @@ private void obtainFiles(File folder, File baseFolder, Map processed allFiles.stream().forEach(filename -> processedFiles.put(new File(filename), baseFolder)); } - @Override - protected LanguageSpecification buildLangSpecs() { - return buildLanguageSpecification(); - } - @Override public List getPredefinedExternalDependencies() { return SpecsCollections.concatList(super.getPredefinedExternalDependencies(), CLAVA_PREDEFINED_EXTERNAL_DEPS); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java index 3b8edfa14b..b154daf1a7 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java @@ -19,12 +19,12 @@ import java.util.stream.Collectors; import pt.up.fe.specs.clava.ast.extra.App; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AInclude; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; public class CxxWeaverApi { - public static ACxxWeaverJoinPoint findJp(CxxWeaver weaver, String filepath, String astId) { + public static AJoinpoint findJp(CxxWeaver weaver, String filepath, String astId) { // Get AST at the top of the stack App topAst = weaver.getApp(); @@ -37,7 +37,7 @@ public static void writeCode(CxxWeaver weaver, File outputFolder) { weaver.writeCode(outputFolder); } - public static List getAvailableUserIncludes(CxxWeaver weaver) { + public static List> getAvailableUserIncludes(CxxWeaver weaver) { return weaver.getAvailableIncludes().stream() .map(weaver.getFactory()::includeDecl) .map(includeDecl -> CxxJoinpoints.create(includeDecl, weaver, AInclude.class)) diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/ACxxWeaverJoinPoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/ACxxWeaverJoinPoint.java index 7e18d27c83..f0b6624985 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/ACxxWeaverJoinPoint.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/ACxxWeaverJoinPoint.java @@ -1,9 +1,25 @@ package pt.up.fe.specs.clava.weaver.abstracts; -import com.google.common.base.Preconditions; -import org.lara.interpreter.weaver.interf.JoinPoint; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.lara.interpreter.weaver.interf.enums.InsertPosition; import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.storedefinition.StoreDefinition; + +import com.google.common.base.Preconditions; + import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaNodes; @@ -20,8 +36,19 @@ import pt.up.fe.specs.clava.utils.NodeWithScope; import pt.up.fe.specs.clava.utils.NullNode; import pt.up.fe.specs.clava.utils.Typable; -import pt.up.fe.specs.clava.weaver.*; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.*; +import pt.up.fe.specs.clava.weaver.CxxActions; +import pt.up.fe.specs.clava.weaver.CxxAttributes; +import pt.up.fe.specs.clava.weaver.CxxJoinpoints; +import pt.up.fe.specs.clava.weaver.CxxSelects; +import pt.up.fe.specs.clava.weaver.CxxWeaver; +import pt.up.fe.specs.clava.weaver.Insert; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AComment; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.APragma; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AProgram; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; +import pt.up.fe.specs.clava.weaver.abstracts.weaver.ACxxWeaver; import pt.up.fe.specs.clava.weaver.importable.AstFactory; import pt.up.fe.specs.clava.weaver.importable.LowLevelApi; import pt.up.fe.specs.util.SpecsLogs; @@ -30,21 +57,14 @@ import pt.up.fe.specs.util.stringsplitter.StringSplitter; import pt.up.fe.specs.util.stringsplitter.StringSplitterRules; -import java.io.File; -import java.util.*; -import java.util.Map.Entry; -import java.util.stream.Collectors; -import java.util.stream.Stream; - /** - * Abstract class which can be edited by the developer. This class will not be overwritten. - * - * @author Lara Weaver Generator + * Abstract class which can be edited by the developer. + * This class will NOT be overwritten by the generator. */ -public abstract class ACxxWeaverJoinPoint extends AJoinPoint { +public abstract class ACxxWeaverJoinPoint> extends AJoinpoint { - public ACxxWeaverJoinPoint(CxxWeaver weaver) { - super(weaver); + protected ACxxWeaverJoinPoint(ClavaNode node, ACxxWeaver weaver) { + super(node, weaver); } @Override @@ -52,11 +72,10 @@ public CxxWeaver getWeaverEngine() { return (CxxWeaver) super.getWeaverEngine(); } - // private static final String BASE_CLAVA_AST_PACKAGE = "pt.up.fe.specs.clava.ast"; - // - // protected static String getBaseClavaAstPackage() { - // return BASE_CLAVA_AST_PACKAGE; - // } + @Override + public boolean getSameImpl(AJoinpoint other) { + return this.get_class().equals(other.get_class()) && this.getNodeImpl().equals(other.getNodeImpl()); + } private static final Set> IGNORE_NODES; @@ -70,21 +89,6 @@ public ClavaFactory getFactory() { return getWeaverEngine().getFactory(); } - /** - * Implementation of GET_NAME. Returns null if no mapping is found. - */ - /* - private static final String GET_NAME_DEFAULT = "!NO_NAME!"; - private static final FunctionClassMap GET_NAME; - static { - GET_NAME = new FunctionClassMap<>(GET_NAME_DEFAULT); - GET_NAME.put(NamedDecl.class, namedDecl -> namedDecl.hasDeclName() ? namedDecl.getDeclName() : null); - GET_NAME.put(TagType.class, tagType -> tagType.getDeclInfo().getDeclName()); - GET_NAME.put(DeclRefExpr.class, declRef -> declRef.getRefName()); - GET_NAME.put(Stmt.class, stmt -> stmt.getClass().getSimpleName()); - } - */ - /** * Compares the two join points based on their node reference of the used compiler/parsing tool.
* This is the default implementation for comparing two join points.
@@ -92,107 +96,90 @@ public ClavaFactory getFactory() { * changes are made for all join points, or override this method in specific join points. */ @Override - public boolean compareNodes(AJoinPoint aJoinPoint) { - return getNode().equals(aJoinPoint.getNode()); + public boolean getCompareNodesImpl(AJoinpoint aJoinPoint) { + return this.getNodeImpl().equals(aJoinPoint.getNodeImpl()); } @Override - public AProgram getRootImpl() { + public AProgram getRootImpl() { return getWeaverEngine().getAppJp(); - /* - ACxxWeaverJoinPoint current = this; - while (current.getHasParentImpl()) { - current = current.getParentImpl(); - } - - - return (current instanceof CxxProgram) ? (CxxProgram) current : null; - */ - // Preconditions.checkArgument(current instanceof CxxProgram, - // "Expected root joinpoint to be a CxxProgram, it is a '" + current.getClass().getSimpleName() + "'"); - // - // return (CxxProgram) current; } /** * @return the parent joinpoint */ @Override - public AJoinPoint getParentImpl() { - ClavaNode node = getNode(); + public AJoinpoint getParentImpl() { + ClavaNode node = getNodeImpl(); if (!node.hasParent()) { return null; } ClavaNode currentParent = node.getParent(); - // if (currentParent instanceof WrapperStmt) { - // currentParent = currentParent.getParent(); - // } return CxxJoinpoints.create(currentParent, getWeaverEngine()); } @Override - public JoinPoint getJpParent() { + public AJoinpoint getJpParent() { return getParentImpl(); } @Override - public AJoinPoint getAncestorImpl(String type) { + public AJoinpoint getGetAncestorImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of ancestor in attribute 'ancestor'"); if (type.equals("program")) { ClavaLog.warning("Consider using attribute .root, instead of .ancestor('program')"); } - ClavaNode currentNode = getNode(); + ClavaNode currentNode = getNodeImpl(); while (currentNode.hasParent()) { // Create join point for testing type - ACxxWeaverJoinPoint parentJp = CxxJoinpoints.create(currentNode.getParent(), getWeaverEngine()); + AJoinpoint parentJp = CxxJoinpoints.create(currentNode.getParent(), getWeaverEngine()); if (parentJp.instanceOf(type)) { return parentJp; } - currentNode = parentJp.getNode(); + currentNode = parentJp.getNodeImpl(); } return null; } @Override - public AJoinPoint[] getDescendantsArrayImpl(String type) { + public AJoinpoint[] getGetDescendantsImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of descendants in attribute 'descendants'"); - return CxxSelects.selectedNodesToJps(getNode().getDescendantsStream(), jp -> jp.instanceOf(type), + return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsStream(), jp -> jp.instanceOf(type), getWeaverEngine()); } @Override - public AJoinPoint[] getDescendantsArrayImpl() { - return CxxSelects.selectedNodesToJps(getNode().getDescendantsStream(), getWeaverEngine()); + public AJoinpoint[] getDescendantsImpl() { + return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsStream(), getWeaverEngine()); } @Override - public AJoinPoint[] getDescendantsAndSelfArrayImpl(String type) { + public AJoinpoint[] getGetDescendantsAndSelfImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of descendants in attribute 'descendants'"); - return CxxSelects.selectedNodesToJps(getNode().getDescendantsAndSelfStream(), jp -> jp.instanceOf(type), + return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsAndSelfStream(), jp -> jp.instanceOf(type), getWeaverEngine()); } @Override - public AJoinPoint getChainAncestorImpl(String type) { + public AJoinpoint getGetChainAncestorImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of ancestor in attribute 'chainAncestor'"); if (type.equals("program")) { ClavaLog.warning("Consider using attribute .root, instead of .chainAncestor('program')"); } - AJoinPoint currentJp = this; + AJoinpoint currentJp = this; while (currentJp.getHasParentImpl()) { var parentJp = currentJp.getParentImpl(); - // if (parentJp.getJoinpointType().equals(type)) { if (parentJp.instanceOf(type)) { return parentJp; } @@ -204,13 +191,13 @@ public AJoinPoint getChainAncestorImpl(String type) { } @Override - public AJoinPoint getAstAncestorImpl(String type) { + public AJoinpoint getGetAstAncestorImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of ancestor in attribute 'astAncestor'"); // Obtain ClavaNode class from type Class nodeClass = ClassesService.getClavaClass(type); - ClavaNode currentNode = getNode(); + ClavaNode currentNode = getNodeImpl(); while (currentNode.hasParent()) { ClavaNode parentNode = currentNode.getParent(); @@ -225,101 +212,85 @@ public AJoinPoint getAstAncestorImpl(String type) { } @Override - public Boolean getHasParentImpl() { - return getNode().hasParent(); - // return getParentImpl() != null; + public boolean getHasParentImpl() { + return getNodeImpl().hasParent(); } @Override public String getAstImpl() { - return getNode().toTree(); + return getNodeImpl().toTree(); } @Override public String getCodeImpl() { - return getNode().getCode(); + return getNodeImpl().getCode(); } @Override public Integer getLineImpl() { - // ClavaNode node = getNode(); - // Objects.requireNonNull(node); - // int line = getNode().getLocation().getStartLine(); - // return line != SourceLocation.getInvalidLoc() ? line : null; - SourceRange location = getNode().getLocation(); + SourceRange location = getNodeImpl().getLocation(); return location.isValid() ? location.getStartLine() : null; - } @Override public Integer getColumnImpl() { - SourceRange location = getNode().getLocation(); + SourceRange location = getNodeImpl().getLocation(); return location.isValid() ? location.getStartCol() : null; } @Override public Integer getEndLineImpl() { - SourceRange location = getNode().getLocation(); + SourceRange location = getNodeImpl().getLocation(); return location.isValid() ? location.getEndLine() : null; } @Override public Integer getEndColumnImpl() { - SourceRange location = getNode().getLocation(); + SourceRange location = getNodeImpl().getLocation(); return location.isValid() ? location.getEndCol() : null; } @Override public String getFilenameImpl() { - SourceRange location = getNode().getLocation(); + SourceRange location = getNodeImpl().getLocation(); return location.isValid() ? location.getFilename() : null; } @Override public String getFilepathImpl() { - SourceRange location = getNode().getLocation(); + SourceRange location = getNodeImpl().getLocation(); return location.isValid() ? location.getFilepath() : null; } @Override - public AJoinPoint[] insertImpl(String position, String code) { - - Insert insert = Insert.getHelper().fromValue(position); - // CxxActions.in - - return new AJoinPoint[]{CxxActions.insertAsStmt(getNode(), code, insert, getWeaverEngine())}; - // - // if (insert == Insert.AFTER || insert == Insert.BEFORE) { - // Stmt literalStmt = ClavaNodeFactory.literalStmt(code); - // CxxActions.insertStmtAndBelow(getNode(), literalStmt, insert); - // return; - // } - + public AJoinpoint[] insertImpl(InsertPosition position, String code) { + Insert insert = Insert.getHelper().fromValue(position.getDisplay()); + return new AJoinpoint[]{CxxActions.insertAsStmt(getNodeImpl(), code, insert, getWeaverEngine())}; } @Override - public AJoinPoint[] insertImpl(String position, JoinPoint JoinPoint) { + public AJoinpoint[] insertImpl(String position, JoinPoint2 JoinPoint) { throw new NotImplementedException(this); } @Override - public void setTypeImpl(AType type) { + public void setTypeImpl(AType type) { // Check if node has a type - ClavaNode node = getNode(); + ClavaNode node = getNodeImpl(); if (!(node instanceof Typable)) { - SpecsLogs.msgLib("[Ignore] Setting type ('" + type.getNode().getNodeName() + SpecsLogs.msgLib("[Ignore] Setting type ('" + type.getNodeImpl().getNodeName() + "') of a node that has no type ('" + node.getNodeName() + "')"); return; } - ((Typable) node).setType((Type) type.getNode()); + ((Typable) node).setType((Type) type.getNodeImpl()); } @Override - public AJoinPoint insertBeforeImpl(AJoinPoint node) { + public AJoinpoint insertBeforeImpl(AJoinpoint node) { // Check if type - if (node.getNode() instanceof Type) { + if (node.getNodeImpl() instanceof Type) { ClavaLog.info("Action 'insertBefore' not available for 'type' join points"); return null; } @@ -328,17 +299,15 @@ public AJoinPoint insertBeforeImpl(AJoinPoint node) { } @Override - public AJoinPoint insertBeforeImpl(String code) { - // return insertBeforeImpl(CxxJoinpoints.create(ClavaNodeFactory.literalStmt(code), this)); - // return insertBeforeImpl(CxxJoinpoints.create(CxxWeaver.getSnippetParser().parseStmt(code))); + public AJoinpoint insertBeforeImpl(String code) { return insertBeforeImpl(toJpToBeInserted(code)); } @Override - public AJoinPoint insertAfterImpl(AJoinPoint node) { + public AJoinpoint insertAfterImpl(AJoinpoint node) { // Check if type - if (node.getNode() instanceof Type) { + if (node.getNodeImpl() instanceof Type) { ClavaLog.info("Action 'insertAfter' not available for 'type' join points"); return null; } @@ -347,15 +316,15 @@ public AJoinPoint insertAfterImpl(AJoinPoint node) { } @Override - public AJoinPoint insertAfterImpl(String code) { + public AJoinpoint insertAfterImpl(String code) { return insertAfterImpl(toJpToBeInserted(code)); } - private AJoinPoint toJpToBeInserted(String code) { + private AJoinpoint toJpToBeInserted(String code) { // Special case: if this node is a statement in a loop header, insert as an expression if (this instanceof AStatement && getIsInsideLoopHeaderImpl()) { - if (getNode() instanceof DeclStmt) { + if (getNodeImpl() instanceof DeclStmt) { System.out.println("Code: " + code); // Convert to VarDecl var equalIndex = code.indexOf('='); @@ -389,13 +358,13 @@ private AJoinPoint toJpToBeInserted(String code) { return AstFactory.varDecl(getWeaverEngine(), declName, init); } - if (getNode() instanceof ExprStmt) { + if (getNodeImpl() instanceof ExprStmt) { return AstFactory.exprLiteral(getWeaverEngine(), code); } throw new RuntimeException( "Inserting before/after a loop header statement only support for 'declStmt' and 'exprStmt', this is a " - + getJoinPointType()); + + joinPointType()); } @@ -403,26 +372,22 @@ private AJoinPoint toJpToBeInserted(String code) { } @Override - public AJoinPoint replaceWithImpl(AJoinPoint node) { - return CxxJoinpoints.create(CxxActions.replace(getNode(), node.getNode(), getWeaverEngine()), getWeaverEngine()); - - // Return input joinpoint - // return node; - + public AJoinpoint replaceWithImpl(AJoinpoint node) { + return CxxJoinpoints.create(CxxActions.replace(getNodeImpl(), node.getNodeImpl(), getWeaverEngine()), getWeaverEngine()); } @Override - public AJoinPoint replaceWithImpl(String node) { - return CxxActions.insertAsStmt(getNode(), node, Insert.REPLACE, getWeaverEngine()); + public AJoinpoint replaceWithImpl(String node) { + return CxxActions.insertAsStmt(getNodeImpl(), node, Insert.REPLACE, getWeaverEngine()); } @Override - public AJoinPoint replaceWithImpl(AJoinPoint[] node) { + public AJoinpoint replaceWithImpl(AJoinpoint[] node) { // Insert nodes after in reverse order, to preserve order of comments and pragmas var reverseNodes = Arrays.asList(node); Collections.reverse(reverseNodes); - AJoinPoint topInserted = null; + AJoinpoint topInserted = null; for (var nodeToInsert : reverseNodes) { topInserted = insertAfterImpl(nodeToInsert); } @@ -435,12 +400,12 @@ public AJoinPoint replaceWithImpl(AJoinPoint[] node) { } @Override - public AJoinPoint replaceWithStringsImpl(String[] node) { + public AJoinpoint replaceWithStringsImpl(String[] node) { // Insert nodes after in reverse order, to preserve order of comments and pragmas var reverseNodes = Arrays.asList(node); Collections.reverse(reverseNodes); - AJoinPoint topInserted = null; + AJoinpoint topInserted = null; for (var nodeToInsert : reverseNodes) { topInserted = insertAfterImpl(nodeToInsert); } @@ -453,12 +418,12 @@ public AJoinPoint replaceWithStringsImpl(String[] node) { } @Override - public AJoinPoint detachImpl() { - ClavaNode node = getNode(); + public AJoinpoint detachImpl() { + ClavaNode node = getNodeImpl(); if (!node.hasParent()) { SpecsLogs.msgInfo( - "action detach: could not find a parent in joinpoint of type '" + getJoinPointType() + "'"); + "action detach: could not find a parent in joinpoint of type '" + joinPointType() + "'"); return this; } @@ -473,11 +438,11 @@ public AJoinPoint detachImpl() { } @Override - public AType getTypeImpl() { - ClavaNode node = getNode(); + public AType getTypeImpl() { + ClavaNode node = getNodeImpl(); if (!(node instanceof Typable)) { - SpecsLogs.msgInfo("Joinpoint of type '" + getJoinPointType() + "' with node '" + node.getNodeName() + SpecsLogs.msgInfo("Joinpoint of type '" + joinPointType() + "' with node '" + node.getNodeName() + "' does not have a type"); return null; } @@ -486,81 +451,30 @@ public AType getTypeImpl() { } @Override - public Boolean getHasTypeImpl() { - ClavaNode node = getNode(); + public boolean getHasTypeImpl() { + ClavaNode node = getNodeImpl(); return node instanceof Typable; } - // @Override - // public String toString() { - // return "Joinpoint '" + getJoinpointType() + "'"; - // } - /** * In case a joinpoint child needs to access the list of the parent joinpoint statements. * * @return */ - public List selectStatements() { + public List> selectStatements() { throw new RuntimeException("Not supported for joinpoint '" + getClass() + "'"); } - /* - @Override - public String getName() { - - String name = GET_NAME.apply(getNodeNormalized()); - - if (name != null && name.equals(GET_NAME_DEFAULT)) { - CxxLog.warning("attribute 'name' not implemented for joinpoint '" + getClass().getSimpleName() + "'"); - return null; - } - - return name; - /* - // TODO: Add .getName() to ClavaNode, returning an Optional - - // ClavaNode node = getNode(); - ClavaNode node = getNodeNormalized(); - if (node instanceof NamedDecl) { - NamedDecl namedDecl = ((NamedDecl) node); - return namedDecl.hasDeclName() ? namedDecl.getDeclName() : null; - } - - // if (node instanceof Type) { - // return node.getNodeName(); - // } - - if (node instanceof TagType) { - return ((TagType) node).getDeclInfo().getDeclName(); - } - - if (node instanceof DeclRefExpr) { - return ((DeclRefExpr) node).getRefName(); - } - - if (node instanceof Stmt) { - return ((Stmt) node).getClass().getSimpleName(); - } - - CxxLog.warning("attribute 'name' not implemented for joinpoint '" + getClass().getSimpleName() + "'"); - return null; - // throw new RuntimeException( - // "attribute 'name' not implemented for joinpoint '" + getClass().getSimpleName() + "'"); - // return ""; - * - */ - // } @Override public String getLocationImpl() { - return getNode().getLocation().toString(); + return getNodeImpl().getLocation().toString(); } @Override - public Boolean containsImpl(AJoinPoint jp) { - ClavaNode clavaNode = jp.getNode(); + public boolean getContainsImpl(AJoinpoint jp) { + ClavaNode clavaNode = jp.getNodeImpl(); - return getNode().getDescendantsStream() + return getNodeImpl().getDescendantsStream() .filter(child -> child == clavaNode) .findFirst().isPresent(); } @@ -571,7 +485,7 @@ public Boolean containsImpl(AJoinPoint jp) { * @return */ public ClavaNode getNodeNormalized() { - ClavaNode currentNode = getNode(); + ClavaNode currentNode = getNodeImpl(); while (IGNORE_NODES.contains(currentNode.getClass())) { Preconditions.checkArgument(currentNode.getNumChildren() == 1, @@ -583,9 +497,9 @@ public ClavaNode getNodeNormalized() { } @Override - public Integer getAstNumChildrenImpl() { + public int getAstNumChildrenImpl() { // return getAstChildrenArrayImpl().length; - ClavaNode node = getNode(); + ClavaNode node = getNodeImpl(); if (node == null) { return -1; } @@ -594,18 +508,18 @@ public Integer getAstNumChildrenImpl() { } @Override - public AJoinPoint[] getAstChildrenArrayImpl() { - return getNode().getChildren().stream() + public AJoinpoint[] getAstChildrenImpl() { + return getNodeImpl().getChildren().stream() .map(node -> CxxJoinpoints.create(node, getWeaverEngine())) // .filter(jp -> jp != null) .collect(Collectors.toList()) - .toArray(new AJoinPoint[0]); + .toArray(new AJoinpoint[0]); } @Override - public AJoinPoint getAstChildImpl(int index) { - ClavaNode node = getNode(); + public AJoinpoint getGetAstChildImpl(int index) { + ClavaNode node = getNodeImpl(); if (node == null) { return null; } @@ -620,9 +534,8 @@ public AJoinPoint getAstChildImpl(int index) { } @Override - public Integer getNumChildrenImpl() { - return (int) getNode().getChildren().stream() - // return (int) getChildrenPrivate().stream() + public int getNumChildrenImpl() { + return (int) getNodeImpl().getChildren().stream() .filter(node -> !(node instanceof NullNode)) .count(); } @@ -634,11 +547,11 @@ public Integer getNumChildrenImpl() { * @return */ @Override - public AJoinPoint[] getScopeNodesArrayImpl() { - var node = getNode(); + public AJoinpoint[] getScopeNodesImpl() { + var node = getNodeImpl(); if (!(node instanceof NodeWithScope)) { - return new AJoinPoint[0]; + return new AJoinpoint[0]; } var stream = ((NodeWithScope) node).getNodeScope() @@ -649,69 +562,57 @@ public AJoinPoint[] getScopeNodesArrayImpl() { } @Override - public Stream getJpChildrenStream() { - return CxxSelects.selectedNodesToJpsStream(getNode().getChildren().stream(), getWeaverEngine()) - .map(JoinPoint.class::cast); + public Stream> getJpChildrenStream() { + return CxxSelects.selectedNodesToJpsStream(getNodeImpl().getChildren().stream(), getWeaverEngine()); } @Override - public AJoinPoint[] getChildrenArrayImpl() { - return CxxSelects.selectedNodesToJps(getNode().getChildren().stream(), getWeaverEngine()); + public AJoinpoint[] getChildrenImpl() { + return CxxSelects.selectedNodesToJps(getNodeImpl().getChildren().stream(), getWeaverEngine()); } @Override - public AJoinPoint[] getSiblingsRightArrayImpl() { - var siblingsRight = getNode().getSiblingsRight(); + public AJoinpoint[] getSiblingsRightImpl() { + var siblingsRight = getNodeImpl().getSiblingsRight(); return CxxSelects.selectedNodesToJps(siblingsRight.stream(), getWeaverEngine()); } @Override - public AJoinPoint[] getSiblingsLeftArrayImpl() { - var siblingsLeft = getNode().getSiblingsLeft(); + public AJoinpoint[] getSiblingsLeftImpl() { + var siblingsLeft = getNodeImpl().getSiblingsLeft(); return CxxSelects.selectedNodesToJps(siblingsLeft.stream(), getWeaverEngine()); } @Override - public AJoinPoint getLeftJpImpl() { - return getNode().getLeft().map(node -> CxxJoinpoints.create(node, getWeaverEngine())).orElse(null); + public AJoinpoint getLeftJpImpl() { + return getNodeImpl().getLeft().map(node -> CxxJoinpoints.create(node, getWeaverEngine())).orElse(null); } @Override - public AJoinPoint getRightJpImpl() { - return getNode().getRight().map(node -> CxxJoinpoints.create(node, getWeaverEngine())).orElse(null); + public AJoinpoint getRightJpImpl() { + return getNodeImpl().getRight().map(node -> CxxJoinpoints.create(node, getWeaverEngine())).orElse(null); } @Override - public AJoinPoint getChildImpl(int index) { - return getNode().getChildren().stream() - // return getChildrenPrivate().stream() + public AJoinpoint getGetChildImpl(int index) { + return getNodeImpl().getChildren().stream() .filter(node -> !(node instanceof NullNode)) .skip(index) .findFirst() .map(node -> CxxJoinpoints.create(node, getWeaverEngine())) .orElse(null); - - // AJoinPoint[] children = getChildrenArrayImpl(); - // - // if (index >= children.length) { - // ClavaLog.warning( - // "Index '" + index + "' is out of range, node only has " + children.length + " defined children"); - // return null; - // } - // - // return children.; } @Override - public String[] getChainArrayImpl() { + public String[] getChainImpl() { List chain = new ArrayList<>(); - AJoinPoint currentJoinpoint = this; + AJoinpoint currentJoinpoint = this; while (currentJoinpoint != null) { // Add joinpoint to chain - chain.add(currentJoinpoint.getJoinPointType()); + chain.add(currentJoinpoint.getJoinPointTypeImpl()); // Update current joinpoint if (currentJoinpoint.getHasParentImpl()) { @@ -739,33 +640,32 @@ public String getAstNameImpl() { } @Override - public String[] getJavaFieldsArrayImpl() { - return LowLevelApi.getFields(getNode()).toArray(new String[0]); + public String[] getJavaFieldsImpl() { + return LowLevelApi.getFields(getNodeImpl()).toArray(new String[0]); } @Override - public String getJavaFieldTypeImpl(String fieldName) { - return LowLevelApi.getFieldClass(getNode(), fieldName).getName(); + public String getGetJavaFieldTypeImpl(String fieldName) { + return LowLevelApi.getFieldClass(getNodeImpl(), fieldName).getName(); } @Override public String getAstIdImpl() { - // return getNode().getExtendedId().orElse(""); - return getNode().getExtendedId().orElseThrow(() -> new RuntimeException("No ID found in node " + getNode())); + return getNodeImpl().getExtendedId().orElseThrow(() -> new RuntimeException("No ID found in node " + getNodeImpl())); } @Override - public Boolean getIsInsideLoopHeaderImpl() { - return CxxAttributes.isInsideLoopHeader(getNode()); + public boolean getIsInsideLoopHeaderImpl() { + return CxxAttributes.isInsideLoopHeader(getNodeImpl()); } @Override - public Boolean getIsInsideHeaderImpl() { - return CxxAttributes.isInsideCHeader(getNode()); + public boolean getIsInsideHeaderImpl() { + return CxxAttributes.isInsideCHeader(getNodeImpl()); } @Override - public Object getUserFieldImpl(String fieldName) { + public Object getGetUserFieldImpl(String fieldName) { return getWeaverEngine().getUserField(getNodeNormalized(), fieldName); } @@ -775,67 +675,30 @@ public Object setUserFieldImpl(String fieldName, Object value) { } @Override - public Object setUserFieldImpl(Map fieldNameAndValue) { + public Object setUserFieldImpl(Map fieldNameAndValue) { Object lastPrevious = null; - for (Entry entry : fieldNameAndValue.entrySet()) { - lastPrevious = setUserField(entry.getKey().toString(), entry.getValue()); + for (Entry entry : fieldNameAndValue.entrySet()) { + lastPrevious = setUserField(entry.getKey(), entry.getValue()); } return lastPrevious; } - // @Override - // public Object setUserFieldImpl(Object fieldNameAndValue) { - // System.out.println("CLASS:" + fieldNameAndValue.getClass()); - // System.out.println("VALUE:" + fieldNameAndValue); - // return super.setUserFieldImpl(fieldNameAndValue); - // } @Override - public AJoinPoint getParentRegionImpl() { - - return CxxAttributes.getParentRegion(getNode()) + public AJoinpoint getParentRegionImpl() { + return CxxAttributes.getParentRegion(getNodeImpl()) .map(node -> CxxJoinpoints.create(node, getWeaverEngine())) .orElse(null); - /* - Optional parentRegionTry = CxxAttributes.getParentRegion(getNode()); - - if (!parentRegionTry.isPresent()) { - ClavaLog.info("Join point '" + getJoinPointType() + "' does not support parentRegion"); - return null; - } - - return CxxJoinpoints.create(parentRegionTry.get(), this); - */ - /* - // Get current region - ClavaNode currentRegion = getCurrentRegion(getNode()); - if (currentRegion == null) { - ClavaLog.info("Join point '" + getJoinPointType() + "' does not support parentRegion"); - return null; - } - - // If already at top region, return that node - if (currentRegion instanceof TranslationUnit) { - return CxxJoinpoints.create(currentRegion, this); - } - System.out.println("CURRENT REGION:" + currentRegion.getNodeName() + ", " + currentRegion.getLocation()); - System.out.println( - "PARENT:" + currentRegion.getParent().getNodeName() + ", " + currentRegion.getParent().getLocation()); - System.out.println("PARENT REGION" + getCurrentRegion(currentRegion.getParent()).getNodeName() + ", " - + getCurrentRegion(currentRegion.getParent()).getLocation()); - // Go up one node, and return the current region - return CxxJoinpoints.create(getCurrentRegion(currentRegion.getParent()), this); - */ } @Override - public AJoinPoint getCurrentRegionImpl() { - Optional currentRegionTry = CxxAttributes.getCurrentRegion(getNode()); + public AJoinpoint getCurrentRegionImpl() { + Optional currentRegionTry = CxxAttributes.getCurrentRegion(getNodeImpl()); if (!currentRegionTry.isPresent()) { ClavaLog.info( - "Join point '" + getJoinPointType() + "'@" + getLocationImpl() + " does not support currentRegion"); + "Join point '" + joinPointType() + "'@" + getLocationImpl() + " does not support currentRegion"); return null; } @@ -843,39 +706,36 @@ public AJoinPoint getCurrentRegionImpl() { } @Override - public boolean equals(Object obj) { - if (!(obj instanceof AJoinPoint)) { + public boolean equalsImpl(Self jp) { + if (!(jp instanceof AJoinpoint)) { return false; } - // System.out.println("Equals? " + getNode().equals(((AJoinPoint) obj).getNode())); - // System.out.println("Node 1:" + getNode()); - // System.out.println("Node 2:" + ((AJoinPoint) obj).getNode()); - return getNode().equals(((AJoinPoint) obj).getNode()); + return getNode().equals(((AJoinpoint) obj).getNode()); } @Override public int hashCode() { - return getNode().hashCode(); + return getNodeImpl().hashCode(); } @Override - public AJoinPoint copyImpl() { - return CxxJoinpoints.create(getNode().copy(), getWeaverEngine()); + public AJoinpoint copyImpl() { + return CxxJoinpoints.create(getNodeImpl().copy(), getWeaverEngine()); } @Override - public AJoinPoint deepCopyImpl() { - return CxxJoinpoints.create(getNode().deepCopy(), getWeaverEngine()); + public AJoinpoint deepCopyImpl() { + return CxxJoinpoints.create(getNodeImpl().deepCopy(), getWeaverEngine()); } @Override - public Boolean hasNodeImpl(Object nodeOrJp) { - if (nodeOrJp instanceof AJoinPoint) { - return hasNodeImpl(((AJoinPoint) nodeOrJp).getNode()); + public boolean getHasNodeImpl(Object nodeOrJp) { + if (nodeOrJp instanceof AJoinpoint) { + return getHasNodeImpl(((AJoinpoint) nodeOrJp).getNodeImpl()); } if (nodeOrJp instanceof ClavaNode) { - return getNode() == nodeOrJp; + return getNodeImpl() == nodeOrJp; } ClavaLog.warning("joinpoint attribute 'hasNode': input type '" + nodeOrJp.getClass() @@ -887,18 +747,11 @@ public Boolean hasNodeImpl(Object nodeOrJp) { * @return the base ClavaAst class for this kind of nodes. */ private String getBaseClavaNodePackage() { - return getNode().getClass().getPackage().getName(); + return getNodeImpl().getClass().getPackage().getName(); } - // @Override - // public List selectDescendant() { - // return getNode().getDescendantsStream() - // .map(descendant -> CxxJoinpoints.create(descendant, this)) - // .collect(Collectors.toList()); - // } - @Override - public Boolean astIsInstanceImpl(String className) { + public boolean getAstIsInstanceImpl(String className) { // Assume nodes are in the same package String packageName = getBaseClavaNodePackage(); @@ -915,7 +768,7 @@ public Boolean astIsInstanceImpl(String className) { String fullClassName = packageName + "." + className; try { - return Class.forName(fullClassName).isInstance(getNode()); + return Class.forName(fullClassName).isInstance(getNodeImpl()); } catch (ClassNotFoundException e) { SpecsLogs.msgInfo("Could not find class '" + fullClassName + "' to compare against this node"); return false; @@ -923,10 +776,10 @@ public Boolean astIsInstanceImpl(String className) { } @Override - public APragma[] getPragmasArrayImpl() { - return ClavaNodes.getPragmas(getNode()).stream() + public APragma[] getPragmasImpl() { + return ClavaNodes.getPragmas(getNodeImpl()).stream() .map(pragma -> CxxJoinpoints.create(pragma, getWeaverEngine())) - .toArray(APragma[]::new); + .toArray(APragma[]::new); } static int jsNameCounter = 0; @@ -935,12 +788,12 @@ public APragma[] getPragmasArrayImpl() { public Object getDataImpl() { // Check if data object already exists - if (ClavaData.hasData(getNode())) { + if (ClavaData.hasData(getNodeImpl())) { // Return data object from managed cache - return ClavaData.getCacheData(getNode()); + return ClavaData.getCacheData(getNodeImpl()); } - var dataPragma = ClavaData.getClavaData(getNode()); + var dataPragma = ClavaData.getClavaData(getNodeImpl()); // TODO: Refactor, so that decoding of pragma is done separately // TODO: life-cycle management of data objects according to node id @@ -948,7 +801,7 @@ public Object getDataImpl() { // Pragma exists and data has not been created yet // if (!hasClavaData && dataPragma != null) { if (dataPragma != null) { - ClavaNode node = getNode(); + ClavaNode node = getNodeImpl(); TranslationUnit tu = node instanceof TranslationUnit ? (TranslationUnit) node : node.getAncestorTry(TranslationUnit.class).orElse(null); @@ -975,7 +828,7 @@ public Object getDataImpl() { } try { - ClavaData.setData(getNode(), sanitizedJsonString); + ClavaData.setData(getNodeImpl(), sanitizedJsonString); } catch (Exception e) { SpecsLogs.warn( @@ -988,31 +841,31 @@ public Object getDataImpl() { // Create cache object and repeat the process dataClearImpl(); - return ClavaData.getCacheData(getNode()); + return ClavaData.getCacheData(getNodeImpl()); } @Override public void setDataImpl(Object source) { - var dataPragma = ClavaData.getClavaData(getNode()); + var dataPragma = ClavaData.getClavaData(getNodeImpl()); if (dataPragma == null) { - ClavaData.buildClavaData(getNode()); + ClavaData.buildClavaData(getNodeImpl()); } String sanitizedJson = ClavaData.sanitizeJsonString(source.toString()); - ClavaData.setData(getNode(), sanitizedJson); + ClavaData.setData(getNodeImpl(), sanitizedJson); } @Override public void dataClearImpl() { // TODO: Remove pragma entirely - ClavaData.clearData(getNode()); + ClavaData.clearData(getNodeImpl()); } @Override - public String[] getKeysArrayImpl() { - List keys = new ArrayList<>(getNode().getStoreDefinition() + public String[] getKeysImpl() { + List keys = new ArrayList<>(getNodeImpl().getStoreDefinition() .getKeyMap() .keySet()); @@ -1023,25 +876,25 @@ public String[] getKeysArrayImpl() { } @Override - public Object getValueImpl(String key) { - var keys = getNode().getStoreDefinition(); + public Object getGetValueImpl(String key) { + var keys = getNodeImpl().getStoreDefinition(); if (!keys.hasKey(key)) { - ClavaLog.info("getValue(): key '" + key + "' not supported for join point '" + getJoinPointType() + "'"); + ClavaLog.info("getValue(): key '" + key + "' not supported for join point '" + joinPointType() + "'"); return null; } // Get key DataKey datakey = keys.getKey(key); - var value = getNode().get(datakey); + var value = getNodeImpl().get(datakey); return CxxAttributes.toLara(value, getWeaverEngine()); } @Override - public AJoinPoint setValueImpl(String key, Object value) { + public AJoinpoint setValueImpl(String key, Object value) { // Get key - DataKey datakey = getNode().getStoreDefinition().getKeyRaw(key); + DataKey datakey = getNodeImpl().getStoreDefinition().getKeyRaw(key); // If string, use decoder if (value instanceof String) { @@ -1049,8 +902,8 @@ public AJoinPoint setValueImpl(String key, Object value) { } // If join point, use underlying node - if (value instanceof AJoinPoint) { - value = ((AJoinPoint) value).getNode(); + if (value instanceof AJoinpoint) { + value = ((AJoinpoint) value).getNodeImpl(); } // Adapt to optional, if needed @@ -1060,12 +913,12 @@ public AJoinPoint setValueImpl(String key, Object value) { } // Returns new join point of the node - return CxxJoinpoints.create(getNode().set(datakey, value), getWeaverEngine()); + return CxxJoinpoints.create(getNodeImpl().set(datakey, value), getWeaverEngine()); } @Override - public Object getKeyTypeImpl(String key) { - StoreDefinition def = getNode().getStoreDefinition(); + public Object getGetKeyTypeImpl(String key) { + StoreDefinition def = getNodeImpl().getStoreDefinition(); if (!def.hasKey(key)) { ClavaLog.info("$jp.keyType(): key '" + key + "' does not exist"); @@ -1076,31 +929,24 @@ public Object getKeyTypeImpl(String key) { } @Override - public AJoinPoint getFirstJpImpl(String type) { - AJoinPoint firstJp = getNode().getDescendantsStream() + public AJoinpoint getGetFirstJpImpl(String type) { + AJoinpoint firstJp = getNodeImpl().getDescendantsStream() .map(descendant -> CxxJoinpoints.create(descendant, getWeaverEngine())) - .filter(jp -> jp != null && jp.getJoinPointType().equals(type)) + .filter(jp -> jp != null && jp.getJoinPointTypeImpl().equals(type)) .findFirst() .orElse(null); if (firstJp == null) { ClavaLog.debug( - () -> "Could not find a join point '" + type + "' inside the node at " + getNode().getLocation()); + () -> "Could not find a join point '" + type + "' inside the node at " + getNodeImpl().getLocation()); } return firstJp; - // for (AJoinPoint descendant : getDescendantsArrayImpl()) { - // if (descendant.getJoinPointType().equals(type)) { - // return descendant; - // } - // } - // - // return null; } @Override - public Boolean getIsMacroImpl() { - return getNode().get(ClavaNode.IS_MACRO); + public boolean getIsMacroImpl() { + return getNodeImpl().get(ClavaNode.IS_MACRO); } @Override @@ -1108,36 +954,16 @@ public void messageToUserImpl(String message) { getWeaverEngine().addMessageToUser(message); } - /** - * Generic select function, used by the default select implementations. - * - * @param joinPointClass - * @param op - * @return - */ - // public List select(Class joinPointClass, SelectOp op) { - // // throw new RuntimeException( - // // "Generic select function not implemented yet. Implement it in order to use the default implementations of - // // select"); - // - // Predicate filter = node -> joinPointClass.isInstance(CxxJoinpoints.create(node, null)); - // - // return CxxSelects.select(joinPointClass, getNode().getChildren(), true, this, filter); - // } - - /** - * - */ @Override public void removeChildrenImpl() { - for (AJoinPoint child : getChildrenArrayImpl()) { + for (AJoinpoint child : getChildrenImpl()) { child.detachImpl(); } } @Override - public AJoinPoint getFirstChildImpl() { - ClavaNode node = getNode(); + public AJoinpoint getFirstChildImpl() { + ClavaNode node = getNodeImpl(); if (!node.hasChildren()) { return null; @@ -1147,10 +973,10 @@ public AJoinPoint getFirstChildImpl() { } @Override - public AJoinPoint setFirstChildImpl(AJoinPoint value) { + public AJoinpoint setFirstChildImpl(AJoinpoint value) { // If no children, just insert the node if (!getHasChildrenImpl()) { - getNode().addChild(value.getNode()); + getNodeImpl().addChild(value.getNodeImpl()); return null; } @@ -1161,10 +987,10 @@ public AJoinPoint setFirstChildImpl(AJoinPoint value) { } @Override - public AJoinPoint getLastChildImpl() { + public AJoinpoint getLastChildImpl() { // Get last child from jp children, so that null nodes are ignored - var children = getChildrenArrayImpl(); + var children = getChildrenImpl(); if (children.length == 0) { return null; @@ -1174,10 +1000,10 @@ public AJoinPoint getLastChildImpl() { } @Override - public AJoinPoint setLastChildImpl(AJoinPoint value) { + public AJoinpoint setLastChildImpl(AJoinpoint value) { // If no children, just insert the node if (!getHasChildrenImpl()) { - getNode().addChild(value.getNode()); + getNodeImpl().addChild(value.getNodeImpl()); return null; } @@ -1188,27 +1014,27 @@ public AJoinPoint setLastChildImpl(AJoinPoint value) { } @Override - public Boolean getHasChildrenImpl() { - return getNode().hasChildren(); + public boolean getHasChildrenImpl() { + return getNodeImpl().hasChildren(); } @Override - public Boolean getIsCilkImpl() { - return getNode() instanceof CilkNode; + public boolean getIsCilkImpl() { + return getNodeImpl() instanceof CilkNode; } @Override - public Integer getDepthImpl() { - return getNode().getDepth(); + public int getDepthImpl() { + return getNodeImpl().getDepth(); } @Override public String getJpIdImpl() { - return getNode().getStableId(); + return getNodeImpl().getStableId(); } @Override - public AJoinPoint toCommentImpl(String prefix, String suffix) { + public AJoinpoint toCommentImpl(String prefix, String suffix) { var prefixClean = prefix == null ? "" : prefix; var suffixClean = suffix == null ? "" : suffix; @@ -1216,56 +1042,35 @@ public AJoinPoint toCommentImpl(String prefix, String suffix) { } @Override - public AStatement getStmtImpl() { - return ClavaNodes.toStmtTry(getNode()) + public AStatement getStmtImpl() { + return ClavaNodes.toStmtTry(getNodeImpl()) .map(stmt -> CxxJoinpoints.create(stmt, getWeaverEngine(), AStatement.class)) .orElse(null); } @Override public Integer getBitWidthImpl() { - AType type = getTypeImpl(); + AType type = getTypeImpl(); if (type == null) { return null; } - Type typeNode = (Type) type.getNode(); + Type typeNode = (Type) type.getNodeImpl(); - Integer bitwidth = typeNode.getBitwidth(this.getNode()); + Integer bitwidth = typeNode.getBitwidth(this.getNodeImpl()); return bitwidth != -1 ? bitwidth : null; } @Override - public AComment[] getInlineCommentsArrayImpl() { - return CxxJoinpoints.create(getNode().get(ClavaNode.INLINE_COMMENTS), getWeaverEngine(), AComment.class); + public AComment[] getInlineCommentsImpl() { + return CxxJoinpoints.create(getNodeImpl().get(ClavaNode.INLINE_COMMENTS), getWeaverEngine(), AComment.class); } - // @Override - // public void setInlineCommentsImpl(AComment[] comments) { - // defInlineCommentsImpl(comments); - // } - - // @Override - // public void defInlineCommentsImpl(AComment[] value) { - // if (value == null || value.length == 0) { - // getNode().removeInlineComments(); - // return; - // } - // - // // sArrays.stream(value).map(comment -> (Com)) - // - // var comments = Arrays.stream(value) - // .map(jp -> (Comment) jp.getNode()) - // .collect(Collectors.toList()); - // - // getNode().set(ClavaNode.INLINE_COMMENTS, comments); - // } - @Override public void setInlineCommentsImpl(String[] comments) { if (comments == null || comments.length == 0) { - getNode().removeInlineComments(); + getNodeImpl().removeInlineComments(); return; } @@ -1274,7 +1079,7 @@ public void setInlineCommentsImpl(String[] comments) { .map(comment -> getFactory().inlineComment(comment, false)) .collect(Collectors.toList()); - getNode().set(ClavaNode.INLINE_COMMENTS, newComments); + getNodeImpl().set(ClavaNode.INLINE_COMMENTS, newComments); } @Override public void setInlineCommentsImpl(String comment) { @@ -1287,21 +1092,21 @@ public void setInlineCommentsImpl(String comment) { } @Override - public Boolean getIsInSystemHeaderImpl() { - return getNode().get(ClavaNode.IS_IN_SYSTEM_HEADER); + public boolean getIsInSystemHeaderImpl() { + return getNodeImpl().get(ClavaNode.IS_IN_SYSTEM_HEADER); } @Override - public AJoinPoint getOriginNodeImpl() { - return CxxJoinpoints.create(getNode().getOrigin(), getWeaverEngine()); + public AJoinpoint getOriginNodeImpl() { + return CxxJoinpoints.create(getNodeImpl().getOrigin(), getWeaverEngine()); } @Override - public AJoinPoint[] jpFieldsArrayImpl(Boolean recursive) { + public AJoinpoint[] getJpFieldsImpl(boolean recursive) { if (recursive) { - return CxxJoinpoints.create(getNode().getNodeFieldsRecursive(), getWeaverEngine(), AJoinPoint.class); + return CxxJoinpoints.create(getNodeImpl().getNodeFieldsRecursive(), getWeaverEngine(), AJoinpoint.class); } - return CxxJoinpoints.create(getNode().getNodeFields(), getWeaverEngine(), AJoinPoint.class); + return CxxJoinpoints.create(getNodeImpl().getNodeFields(), getWeaverEngine(), AJoinpoint.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AExpressionUseEnum.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AExpressionUseEnum.java deleted file mode 100644 index 55295087e0..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AExpressionUseEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -package pt.up.fe.specs.clava.weaver.abstracts.joinpoints.enums; - -import org.lara.interpreter.weaver.interf.NamedEnum; - -/** - * - */ -public enum AExpressionUseEnum implements NamedEnum{ - READ("read"), - WRITE("write"), - READWRITE("readwrite"); - private String name; - - /** - * - */ - private AExpressionUseEnum(String name){ - this.name = name; - } - /** - * - */ - public String getName() { - return name; - } -} diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AFunctionStorageClassEnum.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AFunctionStorageClassEnum.java deleted file mode 100644 index d4a4a61d8d..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AFunctionStorageClassEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package pt.up.fe.specs.clava.weaver.abstracts.joinpoints.enums; - -import org.lara.interpreter.weaver.interf.NamedEnum; - -/** - * - */ -public enum AFunctionStorageClassEnum implements NamedEnum{ - NONE("none"), - AUTO("auto"), - EXTERN("extern"), - PRIVATE_EXTERN("private_extern"), - REGISTER("register"), - STATIC("static"); - private String name; - - /** - * - */ - private AFunctionStorageClassEnum(String name){ - this.name = name; - } - /** - * - */ - public String getName() { - return name; - } -} diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/ALoopKindEnum.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/ALoopKindEnum.java deleted file mode 100644 index 132f73b5e2..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/ALoopKindEnum.java +++ /dev/null @@ -1,27 +0,0 @@ -package pt.up.fe.specs.clava.weaver.abstracts.joinpoints.enums; - -import org.lara.interpreter.weaver.interf.NamedEnum; - -/** - * - */ -public enum ALoopKindEnum implements NamedEnum{ - FOR("for"), - WHILE("while"), - DOWHILE("dowhile"), - FOREACH("foreach"); - private String name; - - /** - * - */ - private ALoopKindEnum(String name){ - this.name = name; - } - /** - * - */ - public String getName() { - return name; - } -} diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AWrapperStmtKindEnum.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AWrapperStmtKindEnum.java deleted file mode 100644 index b749c6bc2d..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/joinpoints/enums/AWrapperStmtKindEnum.java +++ /dev/null @@ -1,25 +0,0 @@ -package pt.up.fe.specs.clava.weaver.abstracts.joinpoints.enums; - -import org.lara.interpreter.weaver.interf.NamedEnum; - -/** - * - */ -public enum AWrapperStmtKindEnum implements NamedEnum{ - COMMENT("comment"), - PRAGMA("pragma"); - private String name; - - /** - * - */ - private AWrapperStmtKindEnum(String name){ - this.name = name; - } - /** - * - */ - public String getName() { - return name; - } -} diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java index abcd5ffece..1526bc539f 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java @@ -65,18 +65,18 @@ public class CallWrap { private static final String WRAPPERS_FOLDERNAME = "clava_wrappers"; - private final CxxCall cxxCall; - private final CxxProgram app; + private final CxxCall cxxCall; + private final CxxProgram app; private final ClavaFactory factory; private final CxxWeaver weaver; - public CallWrap(CxxWeaver cxxWeaver, CxxCall cxxCall) { + public CallWrap(CxxWeaver cxxWeaver, CxxCall cxxCall) { this.weaver = cxxWeaver; this.cxxCall = cxxCall; - app = (CxxProgram) cxxCall.getRootImpl(); + app = (CxxProgram) cxxCall.getRootImpl(); - factory = app.getNode().getFactory(); + factory = app.getNodeImpl().getFactory(); } public void addWrapper(String name) { @@ -116,7 +116,7 @@ public void addWrapper(String name) { // Add include String includePath = getHeaderFile().getRelativeFilepath(); - cxxCall.getNode().getAncestor(TranslationUnit.class).addInclude(includePath, false); + cxxCall.getNodeImpl().getAncestor(TranslationUnit.class).addInclude(includePath, false); // Set call name cxxCall.setName(name); @@ -131,7 +131,7 @@ public void addWrapper(String name) { private void createUserIncludeWrapper(String name) { // Get declaration of function call - FunctionDecl declaration = (FunctionDecl) cxxCall.getDeclarationImpl().getNode(); + FunctionDecl declaration = (FunctionDecl) cxxCall.getDeclarationImpl().getNodeImpl(); // Get include file TranslationUnit includeFile = declaration.getAncestor(TranslationUnit.class); @@ -178,7 +178,7 @@ private void addWrapperFunction(String name, FunctionDecl declaration) { */ private void addWrapperFunctionInPlace(String name, boolean hasDecl) { - FunctionDecl originalDefinition = (FunctionDecl) cxxCall.getDefinitionImpl().getNode(); + FunctionDecl originalDefinition = (FunctionDecl) cxxCall.getDefinitionImpl().getNodeImpl(); FunctionDecl wrapperFunctionDeclImpl = (FunctionDecl) originalDefinition.copy(); wrapperFunctionDeclImpl.setDeclName(name); @@ -194,7 +194,7 @@ private void addWrapperFunctionInPlace(String name, boolean hasDecl) { // add to original file TranslationUnit originalFile = originalDefinition.getAncestor(TranslationUnit.class); - TranslationUnit updatedFile = cxxCall.getNode().getApp().getTranslationUnit(originalFile.getLocation()); + TranslationUnit updatedFile = cxxCall.getNodeImpl().getApp().getTranslationUnit(originalFile.getLocation()); // adds the wrapper implementation after the implementation of the original int originalDefinitionIndex = getIndex(originalDefinition, updatedFile); @@ -205,7 +205,7 @@ private void addWrapperFunctionInPlace(String name, boolean hasDecl) { forwardDecl.getBody().get().detach(); if (hasDecl) { // ... after the declaration of the original - FunctionDecl originalDeclaration = (FunctionDecl) cxxCall.getDeclarationImpl().getNode(); + FunctionDecl originalDeclaration = (FunctionDecl) cxxCall.getDeclarationImpl().getNodeImpl(); int originalDeclarationIndex = getIndex(originalDeclaration, updatedFile); updatedFile.addChild(originalDeclarationIndex + 1, forwardDecl); } else { @@ -265,9 +265,9 @@ private void createSystemIncludeWrapper(String name) { private CallWrapType getWrapType() { // Get declaration of function call - AFunction functionDeclJp = cxxCall.getDeclarationImpl(); - AFunction functionDefJp = cxxCall.getDefinitionImpl(); - // AJoinPoint functionDeclJp = cxxCall.getDeclImpl(); + AFunction functionDeclJp = cxxCall.getDeclarationImpl(); + AFunction functionDefJp = cxxCall.getDefinitionImpl(); + // AJoinpoint functionDeclJp = cxxCall.getDeclImpl(); // If no declaration join point is found, this probably means that the call is from // a system header. Currently we cannot know a system include from a function call, @@ -283,25 +283,25 @@ private CallWrapType getWrapType() { // If definition but no declaration, check if it is associated with a File. If not, consider it a system // header function - if (functionDefJp.getNode().getAncestorTry(TranslationUnit.class).isEmpty()) { + if (functionDefJp.getNodeImpl().getAncestorTry(TranslationUnit.class).isEmpty()) { return CallWrapType.SYSTEM_INCLUDE; } // If no declaration but definition is present, this most likely indicates that the function is defined in // the // file of the function call - FunctionDecl funcDef = (FunctionDecl) functionDefJp.getNode(); + FunctionDecl funcDef = (FunctionDecl) functionDefJp.getNodeImpl(); SpecsLogs.msgLib("Could not find declaration of function '" + funcDef.getDeclName() + "' at " + funcDef.getLocation()); return CallWrapType.NO_INCLUDE; } - FunctionDecl functionDecl = (FunctionDecl) functionDeclJp.getNode(); + FunctionDecl functionDecl = (FunctionDecl) functionDeclJp.getNodeImpl(); // Get include file of declaration // FunctionDecl declaration = declarationTry.get(); - FunctionDecl declaration = (FunctionDecl) functionDeclJp.getNode(); + FunctionDecl declaration = (FunctionDecl) functionDeclJp.getNodeImpl(); Optional includeFileTry = declaration.getAncestorTry(TranslationUnit.class); // TODO: Confirm with Pedro what should be done here @@ -331,24 +331,24 @@ private void initClavaWrappers() { // If wrapper files do not exist, create them String implementationFilename = getImplFilename(); - Optional wrapperImpl = app.getNode().getFile(implementationFilename); + Optional wrapperImpl = app.getNodeImpl().getFile(implementationFilename); if (!wrapperImpl.isPresent()) { // Ensure the header file does not exit yet - Preconditions.checkArgument(!app.getNode().getFile(WRAPPER_H_FILENAME).isPresent(), + Preconditions.checkArgument(!app.getNodeImpl().getFile(WRAPPER_H_FILENAME).isPresent(), "Expected header file to not exist yet"); // Create implementation and header file - AFile implFile = AstFactory.file(this.weaver, implementationFilename, WRAPPERS_FOLDERNAME); - AFile headerFile = AstFactory.file(this.weaver, WRAPPER_H_FILENAME, WRAPPERS_FOLDERNAME); + AFile implFile = AstFactory.file(this.weaver, implementationFilename, WRAPPERS_FOLDERNAME); + AFile headerFile = AstFactory.file(this.weaver, WRAPPER_H_FILENAME, WRAPPERS_FOLDERNAME); app.addFileImpl(headerFile); app.addFileImpl(implFile); } // Ensure the header file also exists - Preconditions.checkArgument(app.getNode().getFile(WRAPPER_H_FILENAME).isPresent(), + Preconditions.checkArgument(app.getNodeImpl().getFile(WRAPPER_H_FILENAME).isPresent(), "Expected header file to exist"); return; @@ -362,16 +362,16 @@ private String getImplFilename() { } private List getWrapperIncludesFromFile() { - TranslationUnit callFile = cxxCall.getNode().getAncestor(TranslationUnit.class); + TranslationUnit callFile = cxxCall.getNodeImpl().getAncestor(TranslationUnit.class); return TreeNodeUtils.copy(callFile.getIncludes().getIncludes()); } private FunctionType getFunctionType() { - return cxxCall.getNode().getCalleeDeclRef().getType().toTry(FunctionType.class).get(); + return cxxCall.getNodeImpl().getCalleeDeclRef().getType().toTry(FunctionType.class).get(); } private List createFunctionCallCode(List paramNames) { - CallExpr call = cxxCall.getNode(); + CallExpr call = cxxCall.getNodeImpl(); List wrapperStmts = new ArrayList<>(); @@ -430,7 +430,7 @@ private TranslationUnit getImplementationFile() { // Make sure Clava wrapper files exist initClavaWrappers(); - return app.getNode().getFile(getImplFilename()).orElseThrow(() -> new RuntimeException( + return app.getNodeImpl().getFile(getImplFilename()).orElseThrow(() -> new RuntimeException( "Implementation file not found, make sure init function was called")); } @@ -439,7 +439,7 @@ private TranslationUnit getHeaderFile() { // Make sure Clava wrapper files exist initClavaWrappers(); - return app.getNode().getFile(WRAPPER_H_FILENAME).orElseThrow(() -> new RuntimeException( + return app.getNodeImpl().getFile(WRAPPER_H_FILENAME).orElseThrow(() -> new RuntimeException( "Header file not found, make sure init function was called")); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/gears/ModifiedFilesGear.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/gears/ModifiedFilesGear.java index 0e9e134fa2..b605291af6 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/gears/ModifiedFilesGear.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/gears/ModifiedFilesGear.java @@ -22,7 +22,7 @@ import org.lara.interpreter.weaver.interf.AGear; import org.lara.interpreter.weaver.interf.events.data.ActionEvent; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.joinpoints.CxxFile; import pt.up.fe.specs.clava.weaver.joinpoints.CxxProgram; @@ -47,17 +47,17 @@ public void onAction(ActionEvent data) { } // System.out.println("ACTION THAT CHANGES AST:" + data.getActionName()); - // All join points are AJoinPoint instances - AJoinPoint jp = (AJoinPoint) data.getJoinPoint(); + // All join points are AJoinpoint instances + AJoinpoint jp = (AJoinpoint) data.getJoinPoint(); // If join point 'program', automatically mark all files as modified if (jp instanceof CxxProgram) { - ((CxxProgram) jp).getNode().getFiles().stream().forEach(modifiedFiles::add); + ((CxxProgram) jp).getNodeImpl().getFiles().stream().forEach(modifiedFiles::add); return; } // Store file of this join point - CxxFile fileJp = (CxxFile) jp.getAncestorImpl("file"); + CxxFile fileJp = (CxxFile) jp.getGetAncestorImpl("file"); if (fileJp == null) { return; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java index fa8d4db2b8..45aff59c68 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java @@ -33,7 +33,6 @@ import pt.up.fe.specs.clava.utils.Typable; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.*; import pt.up.fe.specs.clava.weaver.joinpoints.CxxFunction; import pt.up.fe.specs.util.SpecsLogs; @@ -56,10 +55,10 @@ public class AstFactory { * @param joinpoint * @return */ - public static AJoinPoint varDecl(CxxWeaver weaver, String varName, AJoinPoint init) { + public static AJoinpoint varDecl(CxxWeaver weaver, String varName, AJoinpoint init) { // Check that init is an expression - ClavaNode expr = init.getNode(); + ClavaNode expr = init.getNodeImpl(); if (!(expr instanceof Expr)) { SpecsLogs.msgInfo("CxxFactory.varDecl: parameter 'init' must be of type expression, it is of type '" + expr.getNodeName() + "'"); @@ -68,7 +67,7 @@ public static AJoinPoint varDecl(CxxWeaver weaver, String varName, AJoinPoint in Expr initExpr = (Expr) expr; - Type initType = (Type) init.getTypeImpl().getNode(); + Type initType = (Type) init.getTypeImpl().getNodeImpl(); DataStore config = weaver.getConfig(); @@ -90,8 +89,8 @@ public static AJoinPoint varDecl(CxxWeaver weaver, String varName, AJoinPoint in * @param joinpoint * @return */ - public static AJoinPoint varDeclNoInit(CxxWeaver weaver, String varName, AType type) { - VarDecl varDecl = weaver.getFactory().varDecl(varName, (Type) type.getNode()); + public static AJoinpoint varDeclNoInit(CxxWeaver weaver, String varName, AType type) { + VarDecl varDecl = weaver.getFactory().varDecl(varName, (Type) type.getNodeImpl()); return CxxJoinpoints.create(varDecl, weaver, AVardecl.class); } @@ -119,7 +118,7 @@ private static Type getVarDeclType(CxxWeaver weaver, Standard standard, Type ret return returnType; } - public static CxxFunction functionVoid(CxxWeaver weaver, String name) { + public static CxxFunction functionVoid(CxxWeaver weaver, String name) { BuiltinType voidType = weaver.getFactory().builtinType(BuiltinKind.Void); FunctionProtoType functionType = weaver.getFactory().functionProtoType(voidType); @@ -130,50 +129,50 @@ public static CxxFunction functionVoid(CxxWeaver weaver, String name) { return CxxJoinpoints.create(functionDecl, weaver, CxxFunction.class); } - public static AStatement stmtLiteral(CxxWeaver weaver, String code) { + public static AStatement stmtLiteral(CxxWeaver weaver, String code) { return CxxJoinpoints.create(weaver.getSnippetParser().parseStmt(code), weaver, AStatement.class); } - public static AType typeLiteral(CxxWeaver weaver, String code) { + public static AType typeLiteral(CxxWeaver weaver, String code) { return CxxJoinpoints.create(weaver.getFactory().literalType(code), weaver, AType.class); } - public static ADecl declLiteral(CxxWeaver weaver, String code) { + public static ADecl declLiteral(CxxWeaver weaver, String code) { return CxxJoinpoints.create(weaver.getFactory().literalDecl(code), weaver, ADecl.class); } - public static AExpression exprLiteral(CxxWeaver weaver, String code) { + public static AExpression exprLiteral(CxxWeaver weaver, String code) { return exprLiteral(weaver, code, CxxJoinpoints.create(weaver.getFactory().nullType(), weaver)); } - public static AExpression exprLiteral(CxxWeaver weaver, String code, AJoinPoint type) { - Type astType = type instanceof AType ? (Type) type.getNode() + public static AExpression exprLiteral(CxxWeaver weaver, String code, AJoinpoint type) { + Type astType = type instanceof AType ? (Type) type.getNodeImpl() : weaver.getFactory().nullType(); return CxxJoinpoints.create(weaver.getFactory().literalExpr(code, astType), weaver, AExpression.class); } - public static AExpression cxxConstructExpr(CxxWeaver weaver, AType type, Object[] constructorArguments) { - return cxxConstructExpr(weaver, type, SpecsCollections.asListT(AJoinPoint.class, constructorArguments)); + public static AExpression cxxConstructExpr(CxxWeaver weaver, AType type, Object[] constructorArguments) { + return cxxConstructExpr(weaver, type, SpecsCollections.asListT(AJoinpoint.class, constructorArguments)); } - public static AExpression cxxConstructExpr(CxxWeaver weaver, AType type, List constructorArguments) { + public static AExpression cxxConstructExpr(CxxWeaver weaver, AType type, List> constructorArguments) { List exprArgs = constructorArguments.stream() - .map(arg -> (Expr) arg.getNode()) + .map(arg -> (Expr) arg.getNodeImpl()) .collect(Collectors.toList()); - return CxxJoinpoints.create(weaver.getFactory().cxxConstructExpr((Type) type.getNode(), exprArgs), weaver, AExpression.class); + return CxxJoinpoints.create(weaver.getFactory().cxxConstructExpr((Type) type.getNodeImpl(), exprArgs), weaver, AExpression.class); } - public static ACall callFromFunction(CxxWeaver weaver, AFunction function, Object[] args) { - return callFromFunction(weaver, function, SpecsCollections.asListT(AJoinPoint.class, args)); + public static ACall callFromFunction(CxxWeaver weaver, AFunction function, Object[] args) { + return callFromFunction(weaver, function, SpecsCollections.asListT(AJoinpoint.class, args)); } - public static ACall callFromFunction(CxxWeaver weaver, AFunction function, List args) { - var functionDecl = (FunctionDecl) function.getNode(); + public static ACall callFromFunction(CxxWeaver weaver, AFunction function, List> args) { + var functionDecl = (FunctionDecl) function.getNodeImpl(); List exprArgs = args.stream() - .map(arg -> (Expr) arg.getNode()) + .map(arg -> (Expr) arg.getNodeImpl()) .collect(Collectors.toList()); var call = weaver.getFactory().callExpr(functionDecl, exprArgs); @@ -181,24 +180,24 @@ public static ACall callFromFunction(CxxWeaver weaver, AFunction function, List< return CxxJoinpoints.create(call, weaver, ACall.class); } - public static ACall call(CxxWeaver weaver, String functionName, AType typeJp, Object[] args) { - return call(weaver, functionName, typeJp, SpecsCollections.asListT(AJoinPoint.class, args)); + public static ACall call(CxxWeaver weaver, String functionName, AType typeJp, Object[] args) { + return call(weaver, functionName, typeJp, SpecsCollections.asListT(AJoinpoint.class, args)); } - public static ACall call(CxxWeaver weaver, String functionName, AType typeJp, List args) { + public static ACall call(CxxWeaver weaver, String functionName, AType typeJp, List> args) { - Type returnType = (Type) typeJp.getNode(); + Type returnType = (Type) typeJp.getNodeImpl(); DeclRefExpr declRef = weaver.getFactory().declRefExpr(functionName, returnType); List argTypes = args.stream() - .map(arg -> ((Typable) arg.getNode()).getType()) + .map(arg -> ((Typable) arg.getNodeImpl()).getType()) .collect(Collectors.toList()); FunctionProtoType type = weaver.getFactory().functionProtoType(returnType, argTypes); List exprArgs = args.stream() - .map(arg -> (Expr) arg.getNode()) + .map(arg -> (Expr) arg.getNodeImpl()) .collect(Collectors.toList()); CallExpr call = weaver.getFactory().callExpr(declRef, type, exprArgs); @@ -213,7 +212,7 @@ public static ACall call(CxxWeaver weaver, String functionName, AType typeJp, Li * @param joinpoint * @return */ - public static AFile file(CxxWeaver weaver, File file, String relativePath) { + public static AFile file(CxxWeaver weaver, File file, String relativePath) { // Test if path is absolute if (relativePath != null && new File(relativePath).isAbsolute()) { @@ -233,7 +232,7 @@ public static AFile file(CxxWeaver weaver, File file, String relativePath) { // If file already exists, insert the code of the file literaly if (file.isFile()) { - fileJp.getNode().setOptional(TranslationUnit.LITERAL_SOURCE, SpecsIo.read(file)); + fileJp.getNodeImpl().setOptional(TranslationUnit.LITERAL_SOURCE, SpecsIo.read(file)); } return fileJp; @@ -247,11 +246,11 @@ public static AFile file(CxxWeaver weaver, File file, String relativePath) { * @param relativePath * @return */ - public static AFile file(CxxWeaver weaver, String filename, String contents, String relativePath) { + public static AFile file(CxxWeaver weaver, String filename, String contents, String relativePath) { var fileJp = file(weaver, new File(filename), relativePath); // Add contents - fileJp.getNode().setOptional(TranslationUnit.LITERAL_SOURCE, contents); + fileJp.getNodeImpl().setOptional(TranslationUnit.LITERAL_SOURCE, contents); return fileJp; } @@ -263,23 +262,23 @@ public static AFile file(CxxWeaver weaver, String filename, String contents, Str * @param relativePath * @return */ - public static AFile file(CxxWeaver weaver, String filename, String relativePath) { + public static AFile file(CxxWeaver weaver, String filename, String relativePath) { return file(weaver, new File(filename), relativePath); } - public static AJoinPoint externC(CxxWeaver weaver, AJoinPoint jpDecl) { + public static AJoinpoint externC(CxxWeaver weaver, AJoinpoint jpDecl) { // Allowed classes for now: CxxFunction // TODO: This might be expanded in the future boolean isFunction = jpDecl instanceof CxxFunction; if (!isFunction) { ClavaLog.warning( - "Constructor 'externC' does not support joinpoint of type '" + jpDecl.getJoinPointType() + "'"); + "Constructor 'externC' does not support joinpoint of type '" + jpDecl.getJoinPointTypeImpl() + "'"); return null; } // Check that node does not already has a parent LinkageSpecDecl - ClavaNode decl = jpDecl.getNode(); + ClavaNode decl = jpDecl.getNodeImpl(); if (decl.getParent() instanceof LinkageSpecDecl) { ClavaLog.warning("Given joinpoint already is marked as 'extern \"C\"'"); return null; @@ -291,12 +290,12 @@ public static AJoinPoint externC(CxxWeaver weaver, AJoinPoint jpDecl) { return CxxJoinpoints.create(linkage, weaver); } - public static ACxxWeaverJoinPoint constArrayType(CxxWeaver weaver, + public static AJoinpoint constArrayType(CxxWeaver weaver, String typeCode, String standard, List dims) { return constArrayType(weaver, weaver.getFactory().literalType(typeCode), standard, dims); } - public static ACxxWeaverJoinPoint constArrayType(CxxWeaver weaver, + public static AJoinpoint constArrayType(CxxWeaver weaver, String typeCode, String standard, Object[] dims) { return constArrayType(weaver, typeCode, standard, SpecsCollections.asListT(Integer.class, dims)); } @@ -309,7 +308,7 @@ public static ACxxWeaverJoinPoint constArrayType(CxxWeaver weaver, * @param dims * @return */ - public static ACxxWeaverJoinPoint constArrayType(CxxWeaver weaver, + public static AJoinpoint constArrayType(CxxWeaver weaver, Type outType, String standardString, List dims) { Objects.requireNonNull(dims); @@ -326,45 +325,45 @@ public static ACxxWeaverJoinPoint constArrayType(CxxWeaver weaver, return CxxJoinpoints.create(outType, weaver); } - public static ACxxWeaverJoinPoint constArrayType(CxxWeaver weaver, + public static AJoinpoint constArrayType(CxxWeaver weaver, Type outType, String standardString, Object[] dims) { return constArrayType(weaver, outType, standardString, SpecsCollections.asListT(Integer.class, dims)); } - public static AVariableArrayType variableArrayType(CxxWeaver weaver, AType elementType, AExpression sizeExpr) { - Type variableArrayType = weaver.getFactory().variableArrayType((Type) elementType.getNode(), - (Expr) sizeExpr.getNode()); + public static AVariableArrayType variableArrayType(CxxWeaver weaver, AType elementType, AExpression sizeExpr) { + Type variableArrayType = weaver.getFactory().variableArrayType((Type) elementType.getNodeImpl(), + (Expr) sizeExpr.getNodeImpl()); return CxxJoinpoints.create(variableArrayType, weaver, AVariableArrayType.class); } - public static AIncompleteArrayType incompleteArrayType(CxxWeaver weaver, AType elementType) { - Type incompleteArrayType = weaver.getFactory().incompleteArrayType(((Type) elementType.getNode())); + public static AIncompleteArrayType incompleteArrayType(CxxWeaver weaver, AType elementType) { + Type incompleteArrayType = weaver.getFactory().incompleteArrayType(((Type) elementType.getNodeImpl())); return CxxJoinpoints.create(incompleteArrayType, weaver, AIncompleteArrayType.class); } - public static AJoinPoint omp(CxxWeaver weaver, String directiveName) { + public static AJoinpoint omp(CxxWeaver weaver, String directiveName) { // Get directive OmpDirectiveKind kind = OmpDirectiveKind.getHelper().fromValue(directiveName); return CxxJoinpoints.create(OmpParser.newOmpPragma(kind, weaver.getContex()), weaver); } - public static AStatement caseStmt(CxxWeaver weaver, AExpression value) { + public static AStatement caseStmt(CxxWeaver weaver, AExpression value) { - CaseStmt caseStmt = weaver.getFactory().caseStmt((Expr) value.getNode()); + CaseStmt caseStmt = weaver.getFactory().caseStmt((Expr) value.getNodeImpl()); return CxxJoinpoints.create(caseStmt, weaver, AStatement.class); } - public static AStatement defaultStmt(CxxWeaver weaver) { + public static AStatement defaultStmt(CxxWeaver weaver) { var defaultStmt = weaver.getFactory().defaultStmt(); return CxxJoinpoints.create(defaultStmt, weaver, AStatement.class); } - public static AStatement breakStmt(CxxWeaver weaver) { + public static AStatement breakStmt(CxxWeaver weaver) { var breakStmt = weaver.getFactory().breakStmt(); return CxxJoinpoints.create(breakStmt, weaver, AStatement.class); } @@ -374,29 +373,29 @@ public static AStatement breakStmt(CxxWeaver weaver) { * @param expr * @return a list with a case statement and a break statement */ - public static List caseFromExpr(CxxWeaver weaver, AExpression value, AExpression expr) { + public static List> caseFromExpr(CxxWeaver weaver, AExpression value, AExpression expr) { // Create compound stmt - ExprStmt exprStmt = weaver.getFactory().exprStmt((Expr) expr.getNode()); + ExprStmt exprStmt = weaver.getFactory().exprStmt((Expr) expr.getNodeImpl()); BreakStmt breakStmt = weaver.getFactory().breakStmt(); var breakJp = CxxJoinpoints.create(breakStmt, weaver, AStatement.class); CompoundStmt compoundStmt = weaver.getFactory().compoundStmt(exprStmt); compoundStmt.setNaked(true); - AStatement caseStmt = caseStmt(weaver, value); + AStatement caseStmt = caseStmt(weaver, value); var compoundJp = CxxJoinpoints.create(compoundStmt, weaver, AStatement.class); return Arrays.asList(caseStmt, compoundJp, breakJp); } - public static AStatement switchStmt(CxxWeaver weaver, AExpression condition, AStatement body) { - Stmt switchStmt = weaver.getFactory().switchStmt((Expr) condition.getNode(), (Stmt) body.getNode()); + public static AStatement switchStmt(CxxWeaver weaver, AExpression condition, AStatement body) { + Stmt switchStmt = weaver.getFactory().switchStmt((Expr) condition.getNodeImpl(), (Stmt) body.getNodeImpl()); return CxxJoinpoints.create(switchStmt, weaver, AStatement.class); } - public static AStatement switchStmt(CxxWeaver weaver, AExpression condition, Object[] casesArray) { + public static AStatement switchStmt(CxxWeaver weaver, AExpression condition, Object[] casesArray) { var cases = SpecsCollections.cast(casesArray, AExpression.class); if (cases.length % 2 != 0) { @@ -409,88 +408,88 @@ public static AStatement switchStmt(CxxWeaver weaver, AExpression condition, Obj for (int i = 0; i < cases.length; i += 2) { statements.addAll(caseFromExpr(weaver, cases[i], cases[i + 1]).stream() - .map(aStmt -> (Stmt) aStmt.getNode()) + .map(aStmt -> (Stmt) aStmt.getNodeImpl()) .collect(Collectors.toList())); } CompoundStmt body = weaver.getFactory().compoundStmt(statements); - Stmt switchStmt = weaver.getFactory().switchStmt((Expr) condition.getNode(), body); + Stmt switchStmt = weaver.getFactory().switchStmt((Expr) condition.getNodeImpl(), body); return CxxJoinpoints.create(switchStmt, weaver, AStatement.class); } ////// Methods that only use ClavaFactory - public static ACxxWeaverJoinPoint builtinType(CxxWeaver weaver, String typeCode) { + public static AJoinpoint builtinType(CxxWeaver weaver, String typeCode) { BuiltinType type = weaver.getFactory().builtinType(typeCode); return CxxJoinpoints.create(type, weaver); } - public static ACxxWeaverJoinPoint pointerTypeFromBuiltin(CxxWeaver weaver, String typeCode) { + public static AJoinpoint pointerTypeFromBuiltin(CxxWeaver weaver, String typeCode) { BuiltinType pointeeType = weaver.getFactory().builtinType(typeCode); PointerType pointerType = weaver.getFactory().pointerType(pointeeType); - ACxxWeaverJoinPoint jp = CxxJoinpoints.create(pointerType, weaver); + AJoinpoint jp = CxxJoinpoints.create(pointerType, weaver); return jp; } - public static ACxxWeaverJoinPoint pointerType(CxxWeaver weaver, AType pointeeType) { - PointerType pointerType = weaver.getFactory().pointerType((Type) pointeeType.getNode()); + public static AJoinpoint pointerType(CxxWeaver weaver, AType pointeeType) { + PointerType pointerType = weaver.getFactory().pointerType((Type) pointeeType.getNodeImpl()); - ACxxWeaverJoinPoint jp = CxxJoinpoints.create(pointerType, weaver); + AJoinpoint jp = CxxJoinpoints.create(pointerType, weaver); return jp; } - public static AExpression doubleLiteral(CxxWeaver weaver, String floating) { + public static AExpression doubleLiteral(CxxWeaver weaver, String floating) { return doubleLiteral(weaver, Double.parseDouble(floating)); } - public static AExpression doubleLiteral(CxxWeaver weaver, double floating) { + public static AExpression doubleLiteral(CxxWeaver weaver, double floating) { FloatingLiteral floatingLiteral = weaver.getFactory() .floatingLiteral(FloatKind.DOUBLE, floating); return CxxJoinpoints.create(floatingLiteral, weaver, AExpression.class); } - public static ACxxWeaverJoinPoint longType(CxxWeaver weaver) { + public static AJoinpoint longType(CxxWeaver weaver) { BuiltinType type = weaver.getFactory().builtinType(BuiltinKind.Long); return CxxJoinpoints.create(type, weaver); } - public static AExpression integerLiteral(CxxWeaver weaver, String integer) { + public static AExpression integerLiteral(CxxWeaver weaver, String integer) { return integerLiteral(weaver, Integer.parseInt(integer)); } - public static AExpression integerLiteral(CxxWeaver weaver, int integer) { + public static AExpression integerLiteral(CxxWeaver weaver, int integer) { IntegerLiteral intLiteral = weaver.getFactory().integerLiteral(integer); return CxxJoinpoints.create(intLiteral, weaver, AExpression.class); } - public static AScope scope(CxxWeaver weaver) { + public static AScope scope(CxxWeaver weaver) { return scope(weaver, Collections.emptyList()); } - public static AScope scope(CxxWeaver weaver, Object[] statements) { + public static AScope scope(CxxWeaver weaver, Object[] statements) { return scope(weaver, SpecsCollections.asListT(AStatement.class, statements)); } - public static AScope scope(CxxWeaver weaver, List statements) { - List stmtNodes = SpecsCollections.map(statements, stmt -> (Stmt) stmt.getNode()); + public static AScope scope(CxxWeaver weaver, List> statements) { + List stmtNodes = SpecsCollections.map(statements, stmt -> (Stmt) stmt.getNodeImpl()); return CxxJoinpoints.create(weaver.getFactory().compoundStmt(stmtNodes), weaver, AScope.class); } - public static AVarref varref(CxxWeaver weaver, String declName, AType type) { - Type typeNode = (Type) type.getNode(); + public static AVarref varref(CxxWeaver weaver, String declName, AType type) { + Type typeNode = (Type) type.getNodeImpl(); return CxxJoinpoints.create(weaver.getFactory().declRefExpr(declName, typeNode), weaver, AVarref.class); } - public static AVarref varref(CxxWeaver weaver, ANamedDecl namedDecl) { - NamedDecl decl = (NamedDecl) namedDecl.getNode(); + public static AVarref varref(CxxWeaver weaver, ANamedDecl namedDecl) { + NamedDecl decl = (NamedDecl) namedDecl.getNodeImpl(); if (!(decl instanceof ValueDecl)) { ClavaLog.info( @@ -501,25 +500,25 @@ public static AVarref varref(CxxWeaver weaver, ANamedDecl namedDecl) { return CxxJoinpoints.create(weaver.getFactory().declRefExpr((ValueDecl) decl), weaver, AVarref.class); } - public static AStatement returnStmt(CxxWeaver weaver, AExpression expr) { - return CxxJoinpoints.create(weaver.getFactory().returnStmt((Expr) expr.getNode()), weaver, AStatement.class); + public static AStatement returnStmt(CxxWeaver weaver, AExpression expr) { + return CxxJoinpoints.create(weaver.getFactory().returnStmt((Expr) expr.getNodeImpl()), weaver, AStatement.class); } - public static AStatement returnStmt(CxxWeaver weaver) { + public static AStatement returnStmt(CxxWeaver weaver) { return CxxJoinpoints.create(weaver.getFactory().returnStmt(), weaver, AStatement.class); } - public static AFunctionType functionType(CxxWeaver weaver, AType returnTypeJp, Object[] argTypesJps) { + public static AFunctionType functionType(CxxWeaver weaver, AType returnTypeJp, Object[] argTypesJps) { return functionType(weaver, returnTypeJp, SpecsCollections.asListT(AType.class, argTypesJps)); } - public static AFunctionType functionType(CxxWeaver weaver, AType returnTypeJp, List argTypesJps) { + public static AFunctionType functionType(CxxWeaver weaver, AType returnTypeJp, List> argTypesJps) { - Type returnType = (Type) returnTypeJp.getNode(); + Type returnType = (Type) returnTypeJp.getNodeImpl(); List argTypes = argTypesJps.stream() - .map(arg -> ((Type) arg.getNode())) + .map(arg -> ((Type) arg.getNodeImpl())) .collect(Collectors.toList()); FunctionProtoType type = weaver.getFactory().functionProtoType(returnType, argTypes); @@ -527,22 +526,22 @@ public static AFunctionType functionType(CxxWeaver weaver, AType returnTypeJp, L return CxxJoinpoints.create(type, weaver, AFunctionType.class); } - public static AFunction functionDeclFromType(CxxWeaver weaver, String functionName, AFunctionType functionTypeJp) { - FunctionType functionType = (FunctionType) functionTypeJp.getNode(); + public static AFunction functionDeclFromType(CxxWeaver weaver, String functionName, AFunctionType functionTypeJp) { + FunctionType functionType = (FunctionType) functionTypeJp.getNodeImpl(); return CxxJoinpoints.create(weaver.getFactory().functionDecl(functionName, functionType), weaver, AFunction.class); } - public static AFunction functionDecl(CxxWeaver weaver, String functionName, AType returnTypeJp, List namedDeclJps) { + public static AFunction functionDecl(CxxWeaver weaver, String functionName, AType returnTypeJp, List> namedDeclJps) { - Type returnType = (Type) returnTypeJp.getNode(); + Type returnType = (Type) returnTypeJp.getNodeImpl(); // Get the arg types and create the parameters List argTypes = new ArrayList<>(namedDeclJps.size()); List params = new ArrayList<>(); - for (AJoinPoint namedDeclJp : namedDeclJps) { - ClavaNode node = namedDeclJp.getNode(); + for (AJoinpoint namedDeclJp : namedDeclJps) { + ClavaNode node = namedDeclJp.getNodeImpl(); if (!(node instanceof ValueDecl)) { ClavaLog.info("AstFactory.functionDecl: decl '" + node.getClass() + "' is not compatible as parameter of function"); @@ -565,13 +564,13 @@ public static AFunction functionDecl(CxxWeaver weaver, String functionName, ATyp return CxxJoinpoints.create(functionDecl, weaver, AFunction.class); } - public static AFunction functionDecl(CxxWeaver weaver, String functionName, AType returnTypeJp, Object... namedDeclJps) { - return functionDecl(weaver, functionName, returnTypeJp, SpecsCollections.asListT(AJoinPoint.class, namedDeclJps)); + public static AFunction functionDecl(CxxWeaver weaver, String functionName, AType returnTypeJp, Object... namedDeclJps) { + return functionDecl(weaver, functionName, returnTypeJp, SpecsCollections.asListT(AJoinpoint.class, namedDeclJps)); } - public static ABinaryOp assignment(CxxWeaver weaver, AExpression leftHand, AExpression rightHand) { - Expr lhs = (Expr) leftHand.getNode(); - Expr rhs = (Expr) rightHand.getNode(); + public static ABinaryOp assignment(CxxWeaver weaver, AExpression leftHand, AExpression rightHand) { + Expr lhs = (Expr) leftHand.getNodeImpl(); + Expr rhs = (Expr) rightHand.getNodeImpl(); BinaryOperator assign = weaver.getFactory().binaryOperator(BinaryOperatorKind.Assign, lhs.getType(), lhs, rhs); @@ -579,86 +578,86 @@ public static ABinaryOp assignment(CxxWeaver weaver, AExpression leftHand, AExpr return CxxJoinpoints.create(assign, weaver, ABinaryOp.class); } - public static AIf ifStmt(CxxWeaver weaver, AExpression condition, AStatement thenBody, AStatement elseBody) { - var thenNode = thenBody != null ? ClavaNodes.toCompoundStmt((Stmt) thenBody.getNode()) : null; - var elseNode = elseBody != null ? ClavaNodes.toCompoundStmt((Stmt) elseBody.getNode()) : null; + public static AIf ifStmt(CxxWeaver weaver, AExpression condition, AStatement thenBody, AStatement elseBody) { + var thenNode = thenBody != null ? ClavaNodes.toCompoundStmt((Stmt) thenBody.getNodeImpl()) : null; + var elseNode = elseBody != null ? ClavaNodes.toCompoundStmt((Stmt) elseBody.getNodeImpl()) : null; - IfStmt ifStmt = weaver.getFactory().ifStmt((Expr) condition.getNode(), thenNode, elseNode); + IfStmt ifStmt = weaver.getFactory().ifStmt((Expr) condition.getNodeImpl(), thenNode, elseNode); return CxxJoinpoints.create(ifStmt, weaver, AIf.class); } - public static ABinaryOp binaryOp(CxxWeaver weaver, String op, AExpression left, AExpression right, AType type) { + public static ABinaryOp binaryOp(CxxWeaver weaver, String op, AExpression left, AExpression right, AType type) { BinaryOperatorKind opKind = BinaryOperator.getOpByNameOrSymbol(op); - BinaryOperator opNode = weaver.getFactory().binaryOperator(opKind, (Type) type.getNode(), - (Expr) left.getNode(), (Expr) right.getNode()); + BinaryOperator opNode = weaver.getFactory().binaryOperator(opKind, (Type) type.getNodeImpl(), + (Expr) left.getNodeImpl(), (Expr) right.getNodeImpl()); return CxxJoinpoints.create(opNode, weaver, ABinaryOp.class); } - public static ABinaryOp compoundAssignment(CxxWeaver weaver, String op, AExpression lhs, AExpression rhs) { + public static ABinaryOp compoundAssignment(CxxWeaver weaver, String op, AExpression lhs, AExpression rhs) { var opKind = BinaryOperator.getOpByNameOrSymbol(op); - var type = ((Expr) lhs.getNode()).getType(); + var type = ((Expr) lhs.getNodeImpl()).getType(); - var opNode = weaver.getFactory().compoundAssignOperator(opKind, type, (Expr) lhs.getNode(), - (Expr) rhs.getNode()); + var opNode = weaver.getFactory().compoundAssignOperator(opKind, type, (Expr) lhs.getNodeImpl(), + (Expr) rhs.getNodeImpl()); return CxxJoinpoints.create(opNode, weaver, ABinaryOp.class); } - public static AUnaryOp unaryOp(CxxWeaver weaver, String op, AExpression expr, AType type) { + public static AUnaryOp unaryOp(CxxWeaver weaver, String op, AExpression expr, AType type) { UnaryOperatorKind opKind = UnaryOperator.getOpByNameOrSymbol(op); // If type is null, try to infer type from operator - var typeNode = type != null ? (Type) type.getNode() - : Types.inferUnaryType(opKind, (Type) expr.getTypeImpl().getNode(), weaver.getFactory()); + var typeNode = type != null ? (Type) type.getNodeImpl() + : Types.inferUnaryType(opKind, (Type) expr.getTypeImpl().getNodeImpl(), weaver.getFactory()); UnaryOperator opNode = weaver.getFactory().unaryOperator(opKind, typeNode, - (Expr) expr.getNode()); + (Expr) expr.getNodeImpl()); return CxxJoinpoints.create(opNode, weaver, AUnaryOp.class); } - public static ATernaryOp ternaryOp(CxxWeaver weaver, AExpression cond, AExpression trueExpr, AExpression falseExpr, AType type) { + public static ATernaryOp ternaryOp(CxxWeaver weaver, AExpression cond, AExpression trueExpr, AExpression falseExpr, AType type) { ConditionalOperator opNode = weaver.getFactory().conditionalOperator( - (Type) type.getNode(), - (Expr) cond.getNode(), - (Expr) trueExpr.getNode(), - (Expr) falseExpr.getNode()); + (Type) type.getNodeImpl(), + (Expr) cond.getNodeImpl(), + (Expr) trueExpr.getNodeImpl(), + (Expr) falseExpr.getNodeImpl()); return CxxJoinpoints.create(opNode, weaver, ATernaryOp.class); } - public static AExpression parenthesis(CxxWeaver weaver, AExpression expression) { - ParenExpr parenExpr = weaver.getFactory().parenExpr((Expr) expression.getNode()); + public static AExpression parenthesis(CxxWeaver weaver, AExpression expression) { + ParenExpr parenExpr = weaver.getFactory().parenExpr((Expr) expression.getNodeImpl()); return CxxJoinpoints.create(parenExpr, weaver, AExpression.class); } - public static AArrayAccess arrayAccess(CxxWeaver weaver, AExpression base, List subscripts) { + public static AArrayAccess arrayAccess(CxxWeaver weaver, AExpression base, List> subscripts) { var subscriptsExpr = subscripts.stream() - .map(arg -> ((Expr) arg.getNode())) + .map(arg -> ((Expr) arg.getNodeImpl())) .collect(Collectors.toList()); - var arraySubscriptExpr = weaver.getFactory().arraySubscriptExpr((Expr) base.getNode(), subscriptsExpr); + var arraySubscriptExpr = weaver.getFactory().arraySubscriptExpr((Expr) base.getNodeImpl(), subscriptsExpr); return CxxJoinpoints.create(arraySubscriptExpr, weaver, AArrayAccess.class); } - public static AArrayAccess arrayAccess(CxxWeaver weaver, AExpression base, Object[] subscripts) { + public static AArrayAccess arrayAccess(CxxWeaver weaver, AExpression base, Object[] subscripts) { return arrayAccess(weaver, base, SpecsCollections.asListT(AExpression.class, subscripts)); } - public static AInitList initList(CxxWeaver weaver, List values) { + public static AInitList initList(CxxWeaver weaver, List> values) { var valuesExpr = values.stream() - .map(arg -> ((Expr) arg.getNode())) + .map(arg -> ((Expr) arg.getNodeImpl())) .collect(Collectors.toList()); var initList = weaver.getFactory().initListExpr((valuesExpr)); return CxxJoinpoints.create(initList, weaver, AInitList.class); } - public static AInitList initList(CxxWeaver weaver, Object[] values) { + public static AInitList initList(CxxWeaver weaver, Object[] values) { return initList(weaver, SpecsCollections.asListT(AExpression.class, values)); } @@ -669,25 +668,25 @@ public static AInitList initList(CxxWeaver weaver, Object[] values) { * @param joinpoint * @return */ - public static AType typedefType(CxxWeaver weaver, ATypedefDecl typedefDecl) { - var typedefType = weaver.getFactory().typedefType((TypedefDecl) typedefDecl.getNode()); + public static AType typedefType(CxxWeaver weaver, ATypedefDecl typedefDecl) { + var typedefType = weaver.getFactory().typedefType((TypedefDecl) typedefDecl.getNodeImpl()); return CxxJoinpoints.create(typedefType, weaver, AType.class); } - public static ATypedefDecl typedefDecl(CxxWeaver weaver, AType underlyingType, String identifier) { - var typedefDecl = weaver.getFactory().typedefDecl((Type) underlyingType.getNode(), identifier); + public static ATypedefDecl typedefDecl(CxxWeaver weaver, AType underlyingType, String identifier) { + var typedefDecl = weaver.getFactory().typedefDecl((Type) underlyingType.getNodeImpl(), identifier); return CxxJoinpoints.create(typedefDecl, weaver, ATypedefDecl.class); } - public static AElaboratedType structType(CxxWeaver weaver, AStruct struct) { - var namedType = (Type) struct.getTypeImpl().getNode(); + public static AElaboratedType structType(CxxWeaver weaver, AStruct struct) { + var namedType = (Type) struct.getTypeImpl().getNodeImpl(); var elaboratedType = weaver.getFactory().elaboratedType(ElaboratedTypeKeyword.STRUCT, namedType); return CxxJoinpoints.create(elaboratedType, weaver, AElaboratedType.class); } - public static ACast cStyleCast(CxxWeaver weaver, AType type, AExpression expr) { - var cast = weaver.getFactory().cStyleCastExpr((Type) type.getNode(), (Expr) expr.getNode()); + public static ACast cStyleCast(CxxWeaver weaver, AType type, AExpression expr) { + var cast = weaver.getFactory().cStyleCastExpr((Type) type.getNodeImpl(), (Expr) expr.getNodeImpl()); return CxxJoinpoints.create(cast, weaver, ACast.class); } @@ -699,15 +698,15 @@ public static ACast cStyleCast(CxxWeaver weaver, AType type, AExpression expr) { * @param joinpoint * @return */ - public static AClass classDecl(CxxWeaver weaver, String className, List fields) { - var fieldsNodes = fields.stream().map(field -> (FieldDecl) field.getNode()) + public static AClass classDecl(CxxWeaver weaver, String className, List> fields) { + var fieldsNodes = fields.stream().map(field -> (FieldDecl) field.getNodeImpl()) .collect(Collectors.toList()); var classDecl = weaver.getFactory().cxxRecordDecl(className, fieldsNodes); return CxxJoinpoints.create(classDecl, weaver, AClass.class); } - public static AClass classDecl(CxxWeaver weaver, String className, Object... fields) { + public static AClass classDecl(CxxWeaver weaver, String className, Object... fields) { return classDecl(weaver, className, SpecsCollections.asListT(AField.class, fields)); } @@ -718,8 +717,8 @@ public static AClass classDecl(CxxWeaver weaver, String className, Object... fie * @param fieldType * @return */ - public static AField field(CxxWeaver weaver, String fieldName, AType fieldType) { - var fieldDecl = weaver.getFactory().fieldDecl(fieldName, (Type) fieldType.getNode()); + public static AField field(CxxWeaver weaver, String fieldName, AType fieldType) { + var fieldDecl = weaver.getFactory().fieldDecl(fieldName, (Type) fieldType.getNodeImpl()); return CxxJoinpoints.create(fieldDecl, weaver, AField.class); } @@ -730,21 +729,21 @@ public static AField field(CxxWeaver weaver, String fieldName, AType fieldType) * @param fieldType * @return */ - public static AAccessSpecifier accessSpecifier(CxxWeaver weaver, String accessSpecifierString) { + public static AAccessSpecifier accessSpecifier(CxxWeaver weaver, String accessSpecifierString) { var accessSpecifier = SpecsEnums.fromName(AccessSpecifier.class, accessSpecifierString.toUpperCase()); var accessSpecifierDecl = weaver.getFactory().accessSpecDecl(accessSpecifier); return CxxJoinpoints.create(accessSpecifierDecl, weaver, AAccessSpecifier.class); } - public static ALoop forStmt(CxxWeaver weaver, - AStatement init, AStatement condition, AStatement inc, AStatement body) { + public static ALoop forStmt(CxxWeaver weaver, + AStatement init, AStatement condition, AStatement inc, AStatement body) { // If null, create NullStmt - var initStmt = init != null ? (Stmt) init.getNode() : weaver.getFactory().nullStmt(); - var condStmt = condition != null ? (Stmt) condition.getNode() : weaver.getFactory().nullStmt(); - var incStmt = inc != null ? (Stmt) inc.getNode() : weaver.getFactory().nullStmt(); - var bodyStmt = body != null ? (Stmt) body.getNode() : weaver.getFactory().nullStmt(); + var initStmt = init != null ? (Stmt) init.getNodeImpl() : weaver.getFactory().nullStmt(); + var condStmt = condition != null ? (Stmt) condition.getNodeImpl() : weaver.getFactory().nullStmt(); + var incStmt = inc != null ? (Stmt) inc.getNodeImpl() : weaver.getFactory().nullStmt(); + var bodyStmt = body != null ? (Stmt) body.getNodeImpl() : weaver.getFactory().nullStmt(); // If body is not a CompoundStmt, make it @@ -755,9 +754,9 @@ public static ALoop forStmt(CxxWeaver weaver, return CxxJoinpoints.create(forStmt, weaver, ALoop.class); } - public static ALoop whileStmt(CxxWeaver weaver, AStatement condition, AStatement body) { - var condStmt = condition != null ? (Stmt) condition.getNode() : weaver.getFactory().nullStmt(); - var bodyStmt = body != null ? (Stmt) body.getNode() : weaver.getFactory().nullStmt(); + public static ALoop whileStmt(CxxWeaver weaver, AStatement condition, AStatement body) { + var condStmt = condition != null ? (Stmt) condition.getNodeImpl() : weaver.getFactory().nullStmt(); + var bodyStmt = body != null ? (Stmt) body.getNodeImpl() : weaver.getFactory().nullStmt(); var compoundStmt = ClavaNodes.toCompoundStmt(bodyStmt); @@ -772,12 +771,12 @@ public static ALoop whileStmt(CxxWeaver weaver, AStatement condition, AStatement * @param type * @return */ - public static AParam param(CxxWeaver weaver, String name, AType type) { - var param = weaver.getFactory().parmVarDecl(name, (Type) type.getNode()); + public static AParam param(CxxWeaver weaver, String name, AType type) { + var param = weaver.getFactory().parmVarDecl(name, (Type) type.getNodeImpl()); return CxxJoinpoints.create(param, weaver, AParam.class); } - public static AComment comment(CxxWeaver weaver, String text) { + public static AComment comment(CxxWeaver weaver, String text) { // TODO: Detect C standard, to detect if inline comments are supported? @@ -789,8 +788,8 @@ public static AComment comment(CxxWeaver weaver, String text) { return CxxJoinpoints.create(comment, weaver, AComment.class); } - public static AExprStmt exprStmt(CxxWeaver weaver, AExpression expr) { - var exprStmt = weaver.getFactory().exprStmt((Expr) expr.getNode()); + public static AExprStmt exprStmt(CxxWeaver weaver, AExpression expr) { + var exprStmt = weaver.getFactory().exprStmt((Expr) expr.getNodeImpl()); return CxxJoinpoints.create(exprStmt, weaver, AExprStmt.class); } @@ -801,15 +800,15 @@ public static AExprStmt exprStmt(CxxWeaver weaver, AExpression expr) { * @param joinpoint * @return */ - public static ADeclStmt declStmt(CxxWeaver weaver, List decls) { - var declNodes = decls.stream().map(decl -> (Decl) decl.getNode()) + public static ADeclStmt declStmt(CxxWeaver weaver, List> decls) { + var declNodes = decls.stream().map(decl -> (Decl) decl.getNodeImpl()) .collect(Collectors.toList()); var declStmt = weaver.getFactory().declStmt(declNodes); return CxxJoinpoints.create(declStmt, weaver, ADeclStmt.class); } - public static ADeclStmt declStmt(CxxWeaver weaver, Object... decls) { + public static ADeclStmt declStmt(CxxWeaver weaver, Object... decls) { return declStmt(weaver, SpecsCollections.asListT(ADecl.class, decls)); } @@ -820,7 +819,7 @@ public static ADeclStmt declStmt(CxxWeaver weaver, Object... decls) { * @param name Name of the label * @return The created label declaration */ - public static ALabelDecl labelDecl(CxxWeaver weaver, String name) { + public static ALabelDecl labelDecl(CxxWeaver weaver, String name) { var decl = weaver.getFactory().labelDecl(name); return CxxJoinpoints.create(decl, weaver, ALabelDecl.class); } @@ -831,8 +830,8 @@ public static ALabelDecl labelDecl(CxxWeaver weaver, String name) { * @param decl The declaration for this statement * @return The label statement to be used in the code. */ - public static ALabelStmt labelStmt(CxxWeaver weaver, ALabelDecl decl) { - var stmt = decl.getFactory().labelStmt((LabelDecl) decl.getNode()); + public static ALabelStmt labelStmt(CxxWeaver weaver, ALabelDecl decl) { + var stmt = decl.getFactory().labelStmt((LabelDecl) decl.getNodeImpl()); return CxxJoinpoints.create(stmt, weaver, ALabelStmt.class); } @@ -842,7 +841,7 @@ public static ALabelStmt labelStmt(CxxWeaver weaver, ALabelDecl decl) { * @param name Name of the label * @return The created */ - public static ALabelStmt labelStmt(CxxWeaver weaver, String name) { + public static ALabelStmt labelStmt(CxxWeaver weaver, String name) { return labelStmt(weaver, labelDecl(weaver, name)); } @@ -852,41 +851,41 @@ public static ALabelStmt labelStmt(CxxWeaver weaver, String name) { * @param label The declaration of the label to jump to * @return The created goto statement */ - public static AGotoStmt gotoStmt(CxxWeaver weaver, ALabelDecl label) { - var stmt = label.getFactory().gotoStmt((LabelDecl) label.getNode()); + public static AGotoStmt gotoStmt(CxxWeaver weaver, ALabelDecl label) { + var stmt = label.getFactory().gotoStmt((LabelDecl) label.getNodeImpl()); return CxxJoinpoints.create(stmt, weaver, AGotoStmt.class); } - public static AEmptyStmt emptyStmt(CxxWeaver weaver) { + public static AEmptyStmt emptyStmt(CxxWeaver weaver) { var stmt = weaver.getFactory().emptyStmt(); return CxxJoinpoints.create(stmt, weaver, AEmptyStmt.class); } - public static AProgram program(CxxWeaver weaver) { + public static AProgram program(CxxWeaver weaver) { var app = weaver.getFactory().app(Collections.emptyList()); return CxxJoinpoints.create(app, weaver, AProgram.class); } - public static AMemberAccess memberAccess(CxxWeaver weaver, AExpression baseExpr, AField field) { - var fieldNode = (FieldDecl) field.getNode(); + public static AMemberAccess memberAccess(CxxWeaver weaver, AExpression baseExpr, AField field) { + var fieldNode = (FieldDecl) field.getNodeImpl(); - var memberAccess = weaver.getFactory().memberExpr(fieldNode.get(FieldDecl.DECL_NAME), fieldNode.get(FieldDecl.TYPE), (Expr) baseExpr.getNode()); + var memberAccess = weaver.getFactory().memberExpr(fieldNode.get(FieldDecl.DECL_NAME), fieldNode.get(FieldDecl.TYPE), (Expr) baseExpr.getNodeImpl()); return CxxJoinpoints.create(memberAccess, weaver, AMemberAccess.class); } - public static AMemberAccess memberAccess(CxxWeaver weaver, AExpression baseExpr, String fieldName, AType fieldType) { - var memberAccess = weaver.getFactory().memberExpr(fieldName, (Type) fieldType.getNode(), (Expr) baseExpr.getNode()); + public static AMemberAccess memberAccess(CxxWeaver weaver, AExpression baseExpr, String fieldName, AType fieldType) { + var memberAccess = weaver.getFactory().memberExpr(fieldName, (Type) fieldType.getNodeImpl(), (Expr) baseExpr.getNodeImpl()); return CxxJoinpoints.create(memberAccess, weaver, AMemberAccess.class); } - public static AUnaryExprOrType sizeof(CxxWeaver weaver, AExpression exprArg) { - var sizeof = weaver.getFactory().sizeof((Expr) exprArg.getNode()); + public static AUnaryExprOrType sizeof(CxxWeaver weaver, AExpression exprArg) { + var sizeof = weaver.getFactory().sizeof((Expr) exprArg.getNodeImpl()); return CxxJoinpoints.create(sizeof, weaver, AUnaryExprOrType.class); } - public static AUnaryExprOrType sizeof(CxxWeaver weaver, AType typeArg) { - var sizeof = weaver.getFactory().sizeof((Type) typeArg.getNode()); + public static AUnaryExprOrType sizeof(CxxWeaver weaver, AType typeArg) { + var sizeof = weaver.getFactory().sizeof((Type) typeArg.getNodeImpl()); return CxxJoinpoints.create(sizeof, weaver, AUnaryExprOrType.class); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/LowLevelApi.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/LowLevelApi.java index 56534f9bd9..26c7a820f0 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/LowLevelApi.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/LowLevelApi.java @@ -17,8 +17,6 @@ import java.util.ArrayList; import java.util.List; -import pt.up.fe.specs.clava.ClavaNode; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; import pt.up.fe.specs.util.SpecsLogs; public class LowLevelApi { @@ -37,20 +35,6 @@ public static List getFields(Object object) { return fieldNames; } - public static Object getValue(Object object, String fieldName) { - try { - Field field = object.getClass().getDeclaredField(fieldName); - field.setAccessible(true); // You might want to set modifier to public first. - Object value = field.get(object); - return value; - } catch ( - IllegalArgumentException | NoSuchFieldException | SecurityException | IllegalAccessException e) { - SpecsLogs.warn("Error message:\n", e); - } - - return null; - } - public static Class getFieldClass(Object object, String fieldName) { try { Field field = object.getClass().getDeclaredField(fieldName); @@ -63,9 +47,4 @@ public static Class getFieldClass(Object object, String fieldName) { return null; } - - public static ClavaNode getNode(ACxxWeaverJoinPoint joinpoint) { - return joinpoint.getNode(); - } - } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CXXCudaKernelCall.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CXXCudaKernelCall.java index ca6d8949df..6cbb38b845 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CXXCudaKernelCall.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CXXCudaKernelCall.java @@ -2,7 +2,6 @@ import java.util.Arrays; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.CUDAKernelCallExpr; import pt.up.fe.specs.clava.ast.expr.Expr; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -12,36 +11,32 @@ import pt.up.fe.specs.clava.weaver.importable.AstFactory; import pt.up.fe.specs.util.SpecsCollections; -public class CXXCudaKernelCall extends ACudaKernelCall { - - private final CUDAKernelCallExpr kernelCall; +public class CXXCudaKernelCall> extends ACudaKernelCall { public CXXCudaKernelCall(CUDAKernelCallExpr kernelCall, CxxWeaver weaver) { - super(new CxxCall(kernelCall, weaver), weaver); - - this.kernelCall = kernelCall; + super(kernelCall, weaver); } @Override - public ClavaNode getNode() { - return kernelCall; + public CUDAKernelCallExpr getNodeImpl() { + return (CUDAKernelCallExpr) super.getNodeImpl(); } @Override - public AExpression[] getConfigArrayImpl() { - return CxxJoinpoints.create(kernelCall.getConfiguration(), getWeaverEngine(), AExpression.class); + public AExpression[] getConfigImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getConfiguration(), getWeaverEngine(), AExpression.class); } @Override - public void setConfigImpl(AExpression[] args) { - kernelCall.setConfiguration(SpecsCollections.toList(args, jp -> (Expr) jp.getNode())); + public void setConfigImpl(AExpression[] args) { + this.getNodeImpl().setConfiguration(SpecsCollections.toList(args, jp -> (Expr) jp.getNodeImpl())); } @Override public void setConfigFromStringsImpl(String[] args) { var exprArray = Arrays.stream(args) .map(arg -> AstFactory.exprLiteral(getWeaverEngine(), arg)) - .toArray(size -> new AExpression[size]); + .toArray(AExpression[]::new); setConfigImpl(exprArray); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAccessSpecifier.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAccessSpecifier.java index 1c3f6c8d17..0a2df3440f 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAccessSpecifier.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAccessSpecifier.java @@ -13,28 +13,24 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.AccessSpecDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AAccessSpecifier; -public class CxxAccessSpecifier extends AAccessSpecifier { - - private final AccessSpecDecl accessSpecifier; +public class CxxAccessSpecifier> extends AAccessSpecifier { public CxxAccessSpecifier(AccessSpecDecl accessSpecifier, CxxWeaver weaver) { - super(new CxxDecl(accessSpecifier, weaver), weaver); - this.accessSpecifier = accessSpecifier; + super(accessSpecifier, weaver); } @Override - public ClavaNode getNode() { - return accessSpecifier; + public AccessSpecDecl getNodeImpl() { + return (AccessSpecDecl) super.getNodeImpl(); } @Override public String getKindImpl() { - return accessSpecifier.get(AccessSpecDecl.ACCESS_SPECIFIER).name().toLowerCase(); + return this.getNodeImpl().get(AccessSpecDecl.ACCESS_SPECIFIER).name().toLowerCase(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxArrayAccess.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxArrayAccess.java index 77a118658c..8074b42698 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxArrayAccess.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxArrayAccess.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.ArraySubscriptExpr; import pt.up.fe.specs.clava.utils.Nameable; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -24,70 +23,66 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVardecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVarref; -public class CxxArrayAccess extends AArrayAccess { - - private final ArraySubscriptExpr arraySub; +public class CxxArrayAccess> extends AArrayAccess { public CxxArrayAccess(ArraySubscriptExpr arraySub, CxxWeaver weaver) { - super(new CxxExpression(arraySub, weaver), weaver); - this.arraySub = arraySub; + super(arraySub, weaver); } @Override - public ClavaNode getNode() { - return arraySub; + public ArraySubscriptExpr getNodeImpl() { + return (ArraySubscriptExpr) super.getNodeImpl(); } @Override - public AExpression getArrayVarImpl() { - return CxxJoinpoints.create(arraySub.getArrayExpr(), getWeaverEngine(), AExpression.class); + public AExpression getArrayVarImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getArrayExpr(), getWeaverEngine(), AExpression.class); } @Override - public AExpression[] getSubscriptArrayImpl() { - return arraySub.getSubscripts().stream() + public AExpression[] getSubscriptImpl() { + return this.getNodeImpl().getSubscripts().stream() .map(expr -> CxxJoinpoints.create(expr, getWeaverEngine(), AExpression.class)) - .toArray(length -> new AExpression[length]); + .toArray(AExpression[]::new); } @Override - public AVardecl getVardeclImpl() { - AExpression arrayVar = getArrayVarImpl(); + public AVardecl getVardeclImpl() { + AExpression arrayVar = getArrayVarImpl(); - if (!(arrayVar instanceof AVarref)) { - return null; + if (arrayVar instanceof AVarref varref) { + return varref.getVardeclImpl(); } - return ((AVarref) arrayVar).getVardeclImpl(); - + return null; } @Override - public ADecl getDeclImpl() { + public ADecl getDeclImpl() { return getVardeclImpl(); } @Override - public AArrayAccess getParentAccessImpl() { - return arraySub.getParentAccess() + public AArrayAccess getParentAccessImpl() { + return this.getNodeImpl().getParentAccess() .map(parentAccess -> CxxJoinpoints.create(parentAccess, getWeaverEngine(), AArrayAccess.class)) .orElse(null); } @Override - public Integer getNumSubscriptsImpl() { - return arraySub.getSubscripts().size(); + public int getNumSubscriptsImpl() { + return this.getNodeImpl().getSubscripts().size(); } @Override public String getNameImpl() { - var arrayVar = getArrayVarImpl().getNode(); + var arrayVar = getArrayVarImpl().getNodeImpl(); - if (!(arrayVar instanceof Nameable)) { - return null; + if (arrayVar instanceof Nameable nameable) { + return nameable.getName(); } - return ((Nameable) arrayVar).getName(); + return null; } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAsmStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAsmStmt.java index 9296ffe2c9..5a0aaa58ba 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAsmStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAsmStmt.java @@ -1,42 +1,33 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.AsmStmt; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AAsmStmt; -public class CxxAsmStmt extends AAsmStmt { +public class CxxAsmStmt> extends AAsmStmt { - private final AsmStmt asmStmt; - - /** - * @param asmStmt - */ public CxxAsmStmt(AsmStmt asmStmt, CxxWeaver weaver) { - super(new CxxStatement(asmStmt, weaver), weaver); - - this.asmStmt = asmStmt; + super(asmStmt, weaver); } @Override - public ClavaNode getNode() { - return asmStmt; + public AsmStmt getNodeImpl() { + return (AsmStmt) super.getNodeImpl(); } @Override - public String[] getClobbersArrayImpl() { - return asmStmt.get(AsmStmt.CLOBBERS).toArray(new String[0]); + public String[] getClobbersImpl() { + return this.getNodeImpl().get(AsmStmt.CLOBBERS).toArray(new String[0]); } @Override - public Boolean getIsSimpleImpl() { - return asmStmt.get(AsmStmt.IS_SIMPLE); + public boolean getIsSimpleImpl() { + return this.getNodeImpl().get(AsmStmt.IS_SIMPLE); } @Override - public Boolean getIsVolatileImpl() { - return asmStmt.get(AsmStmt.IS_VOLATILE); + public boolean getIsVolatileImpl() { + return this.getNodeImpl().get(AsmStmt.IS_VOLATILE); } - } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAttribute.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAttribute.java index 630131e4af..00e856a13b 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAttribute.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxAttribute.java @@ -13,28 +13,24 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.attr.Attribute; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AAttribute; -public class CxxAttribute extends AAttribute { - - private final Attribute attr; +public class CxxAttribute> extends AAttribute { public CxxAttribute(Attribute attr, CxxWeaver weaver) { - super(weaver); - this.attr = attr; + super(attr, weaver); } @Override - public ClavaNode getNode() { - return attr; + public Attribute getNodeImpl() { + return (Attribute) super.getNodeImpl(); } @Override public String getKindImpl() { - var attrName = attr.getKind().name(); + var attrName = this.getNodeImpl().getKind().name(); if (attrName.endsWith("Attr")) { attrName = attrName.substring(0, attrName.length() - "Attr".length()); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBinaryOp.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBinaryOp.java index 3cfde82223..8ec7fcb8ef 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBinaryOp.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBinaryOp.java @@ -16,7 +16,6 @@ import java.util.Arrays; import java.util.List; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.BinaryOperator; import pt.up.fe.specs.clava.ast.expr.CompoundAssignOperator; import pt.up.fe.specs.clava.ast.expr.Expr; @@ -26,52 +25,48 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ABinaryOp; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; -public class CxxBinaryOp extends ABinaryOp { - - private final BinaryOperator op; +public class CxxBinaryOp> extends ABinaryOp { public CxxBinaryOp(BinaryOperator op, CxxWeaver weaver) { - super(new CxxOp(op, weaver), weaver); - - this.op = op; + super(op, weaver); } @Override - public ClavaNode getNode() { - return op; + public BinaryOperator getNodeImpl() { + return (BinaryOperator) super.getNodeImpl(); } @Override - public AExpression getLeftImpl() { - List left = Arrays.asList((AExpression) CxxJoinpoints.create(op.getLhs(), + public AExpression getLeftImpl() { + List> left = Arrays.asList((AExpression) CxxJoinpoints.create(this.getNodeImpl().getLhs(), getWeaverEngine())); return left.isEmpty() ? null : left.get(0); } @Override - public AExpression getRightImpl() { - List right = Arrays.asList((AExpression) CxxJoinpoints.create(op.getRhs(), + public AExpression getRightImpl() { + List> right = Arrays.asList((AExpression) CxxJoinpoints.create(this.getNodeImpl().getRhs(), getWeaverEngine())); return right.isEmpty() ? null : right.get(0); } @Override - public Boolean getIsAssignmentImpl() { - return op.getOp() == BinaryOperatorKind.Assign || op instanceof CompoundAssignOperator; + public boolean getIsAssignmentImpl() { + return this.getNodeImpl().getOp() == BinaryOperatorKind.Assign || this.getNodeImpl() instanceof CompoundAssignOperator; } @Override - public Boolean getIsBitwiseImpl() { - return op.getOp().isBitwise(); + public boolean getIsBitwiseImpl() { + return this.getNodeImpl().getOp().isBitwise(); } @Override - public void setLeftImpl(AExpression left) { - op.setLhs((Expr) left.getNode()); + public void setLeftImpl(AExpression left) { + this.getNodeImpl().setLhs((Expr) left.getNodeImpl()); } @Override - public void setRightImpl(AExpression right) { - op.setRhs((Expr) right.getNode()); + public void setRightImpl(AExpression right) { + this.getNodeImpl().setRhs((Expr) right.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBody.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBody.java index 8e20baa4af..e85614fd9c 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBody.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBody.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.CompoundStmt; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ABody; -public class CxxBody extends ABody { - - private final CompoundStmt scope; +public class CxxBody> extends ABody { public CxxBody(CompoundStmt scope, CxxWeaver weaver) { - super(new CxxScope(scope, weaver), weaver); - this.scope = scope; + super(scope, weaver); } @Override - public ClavaNode getNode() { - return scope; + public CompoundStmt getNodeImpl() { + return (CompoundStmt) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBoolLiteral.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBoolLiteral.java index a67a326804..90bbc992bf 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBoolLiteral.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBoolLiteral.java @@ -13,30 +13,24 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.CXXBoolLiteralExpr; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ABoolLiteral; -public class CxxBoolLiteral extends ABoolLiteral { - - private final CXXBoolLiteralExpr literal; +public class CxxBoolLiteral> extends ABoolLiteral { public CxxBoolLiteral(CXXBoolLiteralExpr literal, CxxWeaver weaver) { - super(new CxxLiteral(literal, weaver), weaver); - - this.literal = literal; + super(literal, weaver); } @Override - public ClavaNode getNode() { - return literal; + public CXXBoolLiteralExpr getNodeImpl() { + return (CXXBoolLiteralExpr) super.getNodeImpl(); } @Override - public Boolean getValueImpl() { - return literal.get(CXXBoolLiteralExpr.VALUE); + public boolean getValueImpl() { + return this.getNodeImpl().get(CXXBoolLiteralExpr.VALUE); } - } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBreak.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBreak.java index 11f3d5a155..619ad8ead9 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBreak.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxBreak.java @@ -13,31 +13,26 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.BreakStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ABreak; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; -public class CxxBreak extends ABreak { - - private final BreakStmt breakStmt; +public class CxxBreak> extends ABreak { public CxxBreak(BreakStmt breakStmt, CxxWeaver weaver) { - super(new CxxStatement(breakStmt, weaver), weaver); - - this.breakStmt = breakStmt; + super(breakStmt, weaver); } @Override - public ClavaNode getNode() { - return breakStmt; + public BreakStmt getNodeImpl() { + return (BreakStmt) super.getNodeImpl(); } @Override - public AStatement getEnclosingStmtImpl() { - return CxxJoinpoints.create(breakStmt.getEnclosingStmt(), getWeaverEngine(), AStatement.class); + public AStatement getEnclosingStmtImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getEnclosingStmt(), getWeaverEngine(), AStatement.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCall.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCall.java index b535eef9bd..bf442803bd 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCall.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCall.java @@ -40,32 +40,30 @@ import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.treenode.NodeInsertUtils; -public class CxxCall extends ACall { - - private final CallExpr call; +public class CxxCall> extends ACall { public CxxCall(CallExpr call, CxxWeaver weaver) { - super(new CxxExpression(call, weaver), weaver); - - this.call = call; + super(call, weaver); } @Override - public String getNameImpl() { - return call.getCalleeNameTry().orElse(null); + public CallExpr getNodeImpl() { + return (CallExpr) super.getNodeImpl(); } @Override - public Integer getNumArgsImpl() { - return call.getArgs().size(); + public String getNameImpl() { + return this.getNodeImpl().getCalleeNameTry().orElse(null); } @Override - public CallExpr getNode() { - return call; + public int getNumArgsImpl() { + return this.getNodeImpl().getArgs().size(); } - public void extractImpl(String variableName, Boolean declareVariable) { + public void extractImpl(String variableName, boolean declareVariable) { + var call = this.getNodeImpl(); + // Check that call is inside an ExprStmt if (!(call.getParent() instanceof ExprStmt)) { SpecsLogs.msgInfo("Action currently supported only for calls alone in a statement. Skipping for code:" @@ -82,19 +80,11 @@ public void extractImpl(String variableName, Boolean declareVariable) { // DeclStmt -> VarDecl -> Call if (declareVariable) { - // VarDeclData varDeclData = new VarDeclData(StorageClass.NONE, TLSKind.NONE, false, false, - // InitializationStyle.CINIT, false); - // DeclData declData = new DeclData(false, false, true, false, false, false); - // VarDecl varDecl = ClavaNodeFactory.varDecl(varDeclData, variableName, returnType, declData, - // call.getInfo(), - // call); - VarDecl varDecl = getFactory().varDecl(variableName, returnType); varDecl.setInit(call); varDecl.set(VarDecl.IS_USED); DeclStmt declStmt = call.getFactoryWithNode().declStmt(varDecl); - // DeclStmt declStmt = ClavaNodeFactory.declStmt(call.getInfo(), Arrays.asList(varDecl)); // Replace stmt NodeInsertUtils.replace(exprStmt, declStmt, true); @@ -105,83 +95,65 @@ public void extractImpl(String variableName, Boolean declareVariable) { Expr varExpr = getWeaverEngine().getFactory().literalExpr(variableName, returnType); BinaryOperator assign = getWeaverEngine().getFactory().binaryOperator(BinaryOperatorKind.Assign, returnType, varExpr, call); - // BinaryOperator assign = ClavaNodeFactory.binaryOperator(BinaryOperatorKind.ASSIGN, new - // ExprData(returnType), - // call.getInfo(), varExpr, call); ExprStmt newStmt = getWeaverEngine().getFactory().exprStmt(assign); // Replace stmt NodeInsertUtils.replace(exprStmt, newStmt, true); /* - ExprStmt: (0x46d4420) - BinaryOperator: (0x46d4420) types:int, valueKind:L_VALUE, op:ASSIGNMENT - DeclRefExpr: (0x46d43d8) types:int, valueKind:L_VALUE, refType:Var, refName:samples, type2: - IntegerLiteral: (0x46d4400) types:int, valueKind:R_VALUE + * ExprStmt: (0x46d4420) + * BinaryOperator: (0x46d4420) types:int, valueKind:L_VALUE, op:ASSIGNMENT + * DeclRefExpr: (0x46d43d8) types:int, valueKind:L_VALUE, refType:Var, + * refName:samples, type2: + * IntegerLiteral: (0x46d4400) types:int, valueKind:R_VALUE */ } } @Override - public AType getTypeImpl() { + public AType getTypeImpl() { + var call = this.getNodeImpl(); if (call instanceof CXXMemberCallExpr) { return CxxJoinpoints.create(((CXXMemberCallExpr) call).getType(), getWeaverEngine(), AType.class); } // Return the type of the function (return type), after desugaring Type calleeType = call.getCallee().getType().desugarAll(); - // System.out.println("CALLEE:" + call.getCallee()); // If PointerType to FunctionType, remove pointer - // if (calleeType instanceof PointerType && ((PointerType) calleeType).getPointeeType() instanceof FunctionType) - // { - // calleeType = ((PointerType) calleeType).getPointeeType(); - // } - // System.out.println("CALLEE TYPE:" + calleeType); if (calleeType instanceof FunctionType) { return CxxJoinpoints.create(((FunctionType) calleeType).getReturnType(), getWeaverEngine(), AType.class); } - /* - if (!(calleeType instanceof LiteralType)) { - LoggingUtils - .msgWarn("Expected LiteralType, got '" + calleeType.getClass().getSimpleName() + "'. Check if ok"); - } - */ - return CxxJoinpoints.create(calleeType, getWeaverEngine(), AType.class); } @Override - public String[] getMemberNamesArrayImpl() { - return call.getCallMemberNames().toArray(new String[0]); + public String[] getMemberNamesImpl() { + return this.getNodeImpl().getCallMemberNames().toArray(new String[0]); } @Override public void setNameImpl(String name) { - call.setCallName(name); + this.getNodeImpl().setCallName(name); } @Override - public AFunction getDeclarationImpl() { - return call.getPrototypes().stream() + public AFunction getDeclarationImpl() { + return this.getNodeImpl().getPrototypes().stream() .map(decl -> CxxJoinpoints.create(decl, getWeaverEngine(), AFunction.class)) .findFirst() .orElse(null); - // return call.getFunctionDecl().map(FunctionDecl::getPrototypes) - // .map(decl -> CxxJoinpoints.create(decl, AFunction.class)).orElse(null); - // var declarations = getDeclarationsArrayImpl(); - // return declarations.length != 0 ? declarations[0] : null; - // return call.getDeclaration().map(decl -> (AFunction) CxxJoinpoints.create(decl)).orElse(null); } @Override - public AFunction getDefinitionImpl() { - return call.getDefinition().map(decl -> CxxJoinpoints.create(decl, getWeaverEngine(), AFunction.class)).orElse(null); + public AFunction getDefinitionImpl() { + return this.getNodeImpl().getDefinition().map(decl -> CxxJoinpoints.create(decl, getWeaverEngine(), AFunction.class)) + .orElse(null); } @Override - public AExpression[] getArgsArrayImpl() { - return call.getArgs() + public AExpression[] getArgsImpl() { + return this.getNodeImpl().getArgs() .stream() // .map(Expr::getCode) .map(arg -> CxxJoinpoints.create(arg, getWeaverEngine(), AExpression.class)) @@ -190,14 +162,13 @@ public AExpression[] getArgsArrayImpl() { } @Override - public AExpression[] getArgListArrayImpl() { - return getArgsArrayImpl(); + public AExpression[] getArgListImpl() { + return getArgsImpl(); } @Override - public AType getReturnTypeImpl() { - - return CxxJoinpoints.create(call.getType(), getWeaverEngine(), AType.class); + public AType getReturnTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getType(), getWeaverEngine(), AType.class); } @Override @@ -207,6 +178,8 @@ public void wrapImpl(String name) { @Override public boolean inlineImpl() { + var call = this.getNodeImpl(); + // Only inline if call is associated to an App if (!call.getAppTry().isPresent()) { SpecsLogs.msgInfo("Tried to inline call that is not associated to an app"); @@ -214,98 +187,78 @@ public boolean inlineImpl() { } return call.getApp().inline(call); - // call.getAncestor(App.class).inline(call); - // new CallInliner(call).inline(); } @Override public void setArgFromStringImpl(int index, String expr) { // Get arg of equivalent index, to extract type - Expr arg = call.getArgs().get(index); + Expr arg = this.getNodeImpl().getArgs().get(index); Expr literalExpr = getWeaverEngine().getFactory().literalExpr(expr, arg.getExprType()); setArgImpl(index, CxxJoinpoints.create(literalExpr, getWeaverEngine(), AExpression.class)); } @Override - public void addArgImpl(String arg, AType type) { + public void addArgImpl(String arg, AType type) { Type processedType; - + if (type == null) { processedType = getWeaverEngine().getFactory().dummyType("from $call.addArg()"); } else { - processedType = (Type) type.getNode(); + processedType = (Type) type.getNodeImpl(); } - - call.addArgument(arg, processedType); + + this.getNodeImpl().addArgument(arg, processedType); } @Override public void addArgImpl(String arg, String type) { - call.addArgument(arg, getWeaverEngine().getFactory().literalType(type)); + this.getNodeImpl().addArgument(arg, getWeaverEngine().getFactory().literalType(type)); } @Override - public void setArgImpl(int index, AExpression expr) { - // Check num args - // int numArgs = getArgListArrayImpl().length; - // if (index >= 0 && index < numArgs) { - // SpecsLogs.msgInfo( - // "Not setting call argument, index is '" + index + "' and call has " + numArgs + " arguments"); - // return; - // } - - call.setArgument(index, (Expr) expr.getNode()); + public void setArgImpl(int index, AExpression expr) { + this.getNodeImpl().setArgument(index, (Expr) expr.getNodeImpl()); } @Override - public AExpression getArgImpl(int index) { - call.checkIndex(index); - Expr arg = call.getArgs().get(index); + public AExpression getGetArgImpl(int index) { + this.getNodeImpl().checkIndex(index); + Expr arg = this.getNodeImpl().getArgs().get(index); return CxxJoinpoints.create(arg, getWeaverEngine(), AExpression.class); - } @Override - public Boolean getIsMemberAccessImpl() { - return call instanceof CXXMemberCallExpr; + public boolean getIsMemberAccessImpl() { + return this.getNodeImpl() instanceof CXXMemberCallExpr; } @Override - public AMemberAccess getMemberAccessImpl() { - if (!(call instanceof CXXMemberCallExpr)) { + public AMemberAccess getMemberAccessImpl() { + if (!(this.getNodeImpl() instanceof CXXMemberCallExpr)) { return null; } - var callee = ((CXXMemberCallExpr) call).getCallee(); - - // if (!(callee instanceof MemberExpr)) { - // return null; - // } + var callee = ((CXXMemberCallExpr) this.getNodeImpl()).getCallee(); MemberExpr memberExpr = callee; - // MemberExpr memberExpr = ((CXXMemberCallExpr) call).getCallee(); - return CxxJoinpoints.create(memberExpr, getWeaverEngine(), AMemberAccess.class); - } @Override - public AFunctionType getFunctionTypeImpl() { - return call.getFunctionType() + public AFunctionType getFunctionTypeImpl() { + return this.getNodeImpl().getFunctionType() .map(type -> CxxJoinpoints.create(type, getWeaverEngine(), AFunctionType.class)) .orElse(null); - - // return (AType) CxxJoinpoints.create(call.getFunctionType(), this); } @Override - public Boolean getIsStmtCallImpl() { - return call.isStmtCall(); + public boolean getIsStmtCallImpl() { + return this.getNodeImpl().isStmtCall(); } @Override - public AFunction getFunctionImpl() { + public AFunction getFunctionImpl() { // First, try the implementation var definition = getDefinitionImpl(); @@ -315,47 +268,32 @@ public AFunction getFunctionImpl() { // Implementation not found return declaration return getDeclarationImpl(); - - // return call.getFunctionDecl() - // .map(fDecl -> CxxJoinpoints.create(fDecl, AFunction.class)) - // .orElse(null); } @Override public String getSignatureImpl() { - AFunction function = getFunctionImpl(); + AFunction function = getFunctionImpl(); if (function != null) { return function.getSignatureImpl(); } - // if (getDeclarationImpl() != null) { - // System.out.println("DECL SIG:" + getDeclarationImpl().getSignatureImpl()); - // } - // System.out.println("DECL:" + getDeclarationImpl()); - // System.out.println("DEF:" + getDefinitionImpl()); - return "<" + getNameImpl() + ">"; } @Override - public AFunction getDeclImpl() { - return call.getFunctionDecl() + public AFunction getDeclImpl() { + return this.getNodeImpl().getFunctionDecl() .map(fDecl -> CxxJoinpoints.create(fDecl, getWeaverEngine(), AFunction.class)) .orElse(null); } @Override - public AFunction getDirectCalleeImpl() { - return call.get(CallExpr.DIRECT_CALLEE) + public AFunction getDirectCalleeImpl() { + return this.getNodeImpl().get(CallExpr.DIRECT_CALLEE) .map(callee -> CxxJoinpoints.create(callee, getWeaverEngine(), AFunction.class)) .orElse(null); } - - // @Override - // public String getSignatureImpl() { - // return call.getSignature(); - // } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCase.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCase.java index df7c94bc46..36ef631d31 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCase.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCase.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.SwitchCase; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; @@ -21,33 +20,30 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; -public class CxxCase extends ACase { - - private final SwitchCase caseStmt; +public class CxxCase> extends ACase { public CxxCase(SwitchCase caseStmt, CxxWeaver weaver) { - super(new CxxSwitchCase(caseStmt, weaver), weaver); - this.caseStmt = caseStmt; + super(caseStmt, weaver); } @Override - public ClavaNode getNode() { - return caseStmt; + public SwitchCase getNodeImpl() { + return (SwitchCase) super.getNodeImpl(); } @Override - public Boolean getIsDefaultImpl() { - return caseStmt.isDefaultCase(); + public boolean getIsDefaultImpl() { + return this.getNodeImpl().isDefaultCase(); } @Override - public Boolean getIsEmptyImpl() { - return caseStmt.isEmptyCase(); + public boolean getIsEmptyImpl() { + return this.getNodeImpl().isEmptyCase(); } @Override - public AStatement getNextInstructionImpl() { - var nextInst = caseStmt.nextExecutedInstruction(); + public AStatement getNextInstructionImpl() { + var nextInst = this.getNodeImpl().nextExecutedInstruction(); if (nextInst == null) { return null; } @@ -56,18 +52,18 @@ public AStatement getNextInstructionImpl() { } @Override - public AStatement[] getInstructionsArrayImpl() { - return CxxJoinpoints.create(caseStmt.getInstructions(), getWeaverEngine(), AStatement.class); + public AStatement[] getInstructionsImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getInstructions(), getWeaverEngine(), AStatement.class); } @Override - public ACase getNextCaseImpl() { - return CxxJoinpoints.create(caseStmt.nextCase(), getWeaverEngine(), ACase.class); + public ACase getNextCaseImpl() { + return CxxJoinpoints.create(this.getNodeImpl().nextCase(), getWeaverEngine(), ACase.class); } @Override - public AExpression[] getValuesArrayImpl() { - return CxxJoinpoints.create(caseStmt.getValues(), getWeaverEngine(), AExpression.class); + public AExpression[] getValuesImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getValues(), getWeaverEngine(), AExpression.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCast.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCast.java index bc4a3c52e6..8494987a55 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCast.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCast.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.CastExpr; import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -24,51 +23,47 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVardecl; -public class CxxCast extends ACast { - - private final CastExpr cast; +public class CxxCast> extends ACast { public CxxCast(CastExpr cast, CxxWeaver weaver) { - super(new CxxExpression(cast, weaver), weaver); - - this.cast = cast; + super(cast, weaver); } @Override - public ClavaNode getNode() { - return cast; + public CastExpr getNodeImpl() { + return (CastExpr) super.getNodeImpl(); } @Override - public Boolean getIsImplicitCastImpl() { + public boolean getIsImplicitCastImpl() { throw new RuntimeException("cast.isImplicitCast deprecated, please use instead expr.implicitCast"); } @Override - public AType getFromTypeImpl() { - Type fromType = cast.getSubExpr().getType(); + public AType getFromTypeImpl() { + Type fromType = this.getNodeImpl().getSubExpr().getType(); return CxxJoinpoints.create(fromType, getWeaverEngine(), AType.class); } @Override - public AType getToTypeImpl() { - return CxxJoinpoints.create(cast.getCastType(), getWeaverEngine(), AType.class); + public AType getToTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getCastType(), getWeaverEngine(), AType.class); } @Override - public AVardecl getVardeclImpl() { - return CxxJoinpoints.create(cast.getSubExpr(), getWeaverEngine(), AExpression.class).getVardeclImpl(); + public AVardecl getVardeclImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getSubExpr(), getWeaverEngine(), AExpression.class).getVardeclImpl(); } @Override - public ADecl getDeclImpl() { + public ADecl getDeclImpl() { return getVardeclImpl(); } @Override - public AExpression getSubExprImpl() { - return CxxJoinpoints.create(cast.getSubExpr(), getWeaverEngine(), AExpression.class); + public AExpression getSubExprImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getSubExpr(), getWeaverEngine(), AExpression.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java index e5febc5a74..3a597fb7b4 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.CXXMethodDecl; import pt.up.fe.specs.clava.ast.decl.CXXRecordDecl; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -22,16 +21,12 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AClass; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AMethod; -public class CxxClass extends AClass { - - private final CXXRecordDecl cxxRecordDecl; +public class CxxClass> extends AClass { public CxxClass(CXXRecordDecl cxxRecordDecl, CxxWeaver weaver) { - super(new CxxStruct(cxxRecordDecl, weaver), weaver); - - this.cxxRecordDecl = cxxRecordDecl; + super(cxxRecordDecl, weaver); } - + public Boolean isAbstract() { return (Boolean) this.getIsAbstract(); /* @@ -50,74 +45,68 @@ public Boolean isAbstract() { } @Override - public ClavaNode getNode() { - return cxxRecordDecl; + public CXXRecordDecl getNodeImpl() { + return (CXXRecordDecl) super.getNodeImpl(); } @Override - public AMethod[] getMethodsArrayImpl() { - return CxxSelects.select(getWeaverEngine(), AMethod.class, cxxRecordDecl.getMethods(), false, node -> true).toArray(new AMethod[0]); + public AMethod[] getMethodsImpl() { + return CxxSelects.select(getWeaverEngine(), AMethod.class, this.getNodeImpl().getMethods(), false, node -> true); } @Override - public void addMethodImpl(AMethod method) { - cxxRecordDecl.addMethod((CXXMethodDecl) method.getNode()); + public void addMethodImpl(AMethod method) { + this.getNodeImpl().addMethod((CXXMethodDecl) method.getNodeImpl()); } @Override - public AClass[] getBasesArrayImpl() { + public AClass[] getBasesImpl() { - return cxxRecordDecl.getBases().stream() + return this.getNodeImpl().getBases().stream() .map(decl -> CxxJoinpoints.create(decl, getWeaverEngine(), AClass.class)) // Collect to array .toArray(size -> new AClass[size]); - - // return cxxRecordDecl.get(CXXRecordDecl.RECORD_BASES).stream() - // // Map Decl - // .map(baseSpec -> CxxJoinpoints.create(baseSpec.getBaseDecl(cxxRecordDecl), AClass.class)) - // // Collect to array - // .toArray(size -> new AClass[size]); } @Override - public AMethod[] getAllMethodsArrayImpl() { - return CxxJoinpoints.create(cxxRecordDecl.getAllMethods(false), getWeaverEngine(), AMethod.class); + public AMethod[] getAllMethodsImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getAllMethods(false), getWeaverEngine(), AMethod.class); } @Override - public AClass[] getAllBasesArrayImpl() { - return CxxJoinpoints.create(cxxRecordDecl.getAllBases(), getWeaverEngine(), AClass.class); + public AClass[] getAllBasesImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getAllBases(), getWeaverEngine(), AClass.class); } @Override - public Boolean getIsAbstractImpl() { - return cxxRecordDecl.isAbstract(); + public boolean getIsAbstractImpl() { + return this.getNodeImpl().isAbstract(); } @Override - public Boolean getIsInterfaceImpl() { - return cxxRecordDecl.isInterface(); + public boolean getIsInterfaceImpl() { + return this.getNodeImpl().isInterface(); } @Override - public AClass[] getPrototypesArrayImpl() { - return cxxRecordDecl.getDeclarations().stream() + public AClass[] getPrototypesImpl() { + return this.getNodeImpl().getDeclarations().stream() .map(node -> CxxJoinpoints.create(node, getWeaverEngine(), AClass.class)) - .toArray(size -> new AClass[size]); + .toArray(AClass[]::new); } @Override - public AClass getImplementationImpl() { - return cxxRecordDecl.getDefinition() + public AClass getImplementationImpl() { + return this.getNodeImpl().getDefinition() .map(node -> CxxJoinpoints.create(node, getWeaverEngine(), AClass.class)) .orElse(null); } @Override - public AClass getCanonicalImpl() { + public AClass getCanonicalImpl() { // First, try the implementation var implementation = getImplementationImpl(); @@ -126,7 +115,7 @@ public AClass getCanonicalImpl() { } // Implementation not found return prototype - var prototypes = getPrototypesArrayImpl(); + var prototypes = getPrototypesImpl(); if (prototypes.length == 0) { return null; @@ -136,8 +125,8 @@ public AClass getCanonicalImpl() { } @Override - public Boolean getIsCanonicalImpl() { - return cxxRecordDecl.equals(getCanonicalImpl().getNode()); + public boolean getIsCanonicalImpl() { + return this.getNodeImpl().equals(getCanonicalImpl().getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClavaException.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClavaException.java deleted file mode 100644 index c9fc1b57da..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClavaException.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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 pt.up.fe.specs.clava.weaver.joinpoints; - -import pt.up.fe.specs.clava.ClavaNode; -import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AClavaException; - -public class CxxClavaException extends AClavaException { - - private final Throwable exception; - - public CxxClavaException(Throwable exception, CxxWeaver weaver) { - super(weaver); - this.exception = exception; - } - - @Override - public ClavaNode getNode() { - throw new RuntimeException("ClavaException join point does not have an AST node"); - } - - @Override - public String getMessageImpl() { - return exception.getMessage(); - } - - @Override - public Object getExceptionImpl() { - return exception; - } - - @Override - public String getExceptionTypeImpl() { - return exception.getClass().getSimpleName(); - } -} diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxComment.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxComment.java index 2ba2cea606..accc442f9b 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxComment.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxComment.java @@ -13,33 +13,29 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.comment.Comment; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AComment; -public class CxxComment extends AComment { - - private final Comment comment; +public class CxxComment> extends AComment { public CxxComment(Comment comment, CxxWeaver weaver) { - super(weaver); - this.comment = comment; + super(comment, weaver); } @Override - public ClavaNode getNode() { - return comment; + public Comment getNodeImpl() { + return (Comment) super.getNodeImpl(); } @Override public String getTextImpl() { - return comment.getText(); + return this.getNodeImpl().getText(); } @Override public void setTextImpl(String text) { - comment.setText(text); + this.getNodeImpl().setText(text); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxContinue.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxContinue.java index 32f73d42ba..67f387fac6 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxContinue.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxContinue.java @@ -13,24 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.ContinueStmt; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AContinue; -public class CxxContinue extends AContinue { - - private final ContinueStmt continueStmt; +public class CxxContinue> extends AContinue { public CxxContinue(ContinueStmt continueStmt, CxxWeaver weaver) { - super(new CxxStatement(continueStmt, weaver), weaver); - - this.continueStmt = continueStmt; + super(continueStmt, weaver); } @Override - public ClavaNode getNode() { - return continueStmt; + public ContinueStmt getNodeImpl() { + return (ContinueStmt) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDecl.java index 269af9c21a..7deae923a8 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDecl.java @@ -13,31 +13,27 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.Decl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AAttribute; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADecl; -public class CxxDecl extends ADecl { - - private final Decl decl; +public class CxxDecl> extends ADecl { public CxxDecl(Decl decl, CxxWeaver weaver) { - super(weaver); - this.decl = decl; + super(decl, weaver); } @Override - public ClavaNode getNode() { - return decl; + public Decl getNodeImpl() { + return (Decl) super.getNodeImpl(); } @Override - public AAttribute[] getAttrsArrayImpl() { - return decl.get(Decl.ATTRIBUTES).stream() - .map(attr -> new CxxAttribute(attr, getWeaverEngine())) - .toArray(size -> new AAttribute[size]); + public AAttribute[] getAttrsImpl() { + return this.getNodeImpl().get(Decl.ATTRIBUTES).stream() + .map(attr -> new CxxAttribute<>(attr, getWeaverEngine())) + .toArray(AAttribute[]::new); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclStmt.java index 09d32ec1ea..c489c10108 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclStmt.java @@ -13,30 +13,26 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.DeclStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADeclStmt; -public class CxxDeclStmt extends ADeclStmt { - - private final DeclStmt declStmt; +public class CxxDeclStmt> extends ADeclStmt { public CxxDeclStmt(DeclStmt declStmt, CxxWeaver weaver) { - super(new CxxStatement(declStmt, weaver), weaver); - this.declStmt = declStmt; + super(declStmt, weaver); } @Override - public ClavaNode getNode() { - return declStmt; + public DeclStmt getNodeImpl() { + return (DeclStmt) super.getNodeImpl(); } @Override - public ADecl[] getDeclsArrayImpl() { - return CxxJoinpoints.create(declStmt.getDecls(), getWeaverEngine(), ADecl.class); + public ADecl[] getDeclsImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getDecls(), getWeaverEngine(), ADecl.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclarator.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclarator.java index ae1874df1a..bd9c7438dd 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclarator.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeclarator.java @@ -13,24 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.DeclaratorDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADeclarator; -public class CxxDeclarator extends ADeclarator { - - private final DeclaratorDecl declaratorDecl; +public class CxxDeclarator> extends ADeclarator { public CxxDeclarator(DeclaratorDecl declaratorDecl, CxxWeaver weaver) { - super(new CxxNamedDecl(declaratorDecl, weaver), weaver); - - this.declaratorDecl = declaratorDecl; + super(declaratorDecl, weaver); } @Override - public ClavaNode getNode() { - return declaratorDecl; + public DeclaratorDecl getNodeImpl() { + return (DeclaratorDecl) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDefault.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDefault.java index 6a7d6872ed..2c6f2c8857 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDefault.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDefault.java @@ -1,21 +1,17 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.DefaultStmt; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADefault; -public class CxxDefault extends ADefault { - - private final DefaultStmt defaultStmt; +public class CxxDefault> extends ADefault { public CxxDefault(DefaultStmt defaultStmt, CxxWeaver weaver) { - super(new CxxSwitchCase(defaultStmt, weaver), weaver); - this.defaultStmt = defaultStmt; + super(defaultStmt, weaver); } @Override - public ClavaNode getNode() { - return defaultStmt; + public DefaultStmt getNodeImpl() { + return (DefaultStmt) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeleteExpr.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeleteExpr.java index f189fa1e5f..e83f20cc60 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeleteExpr.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxDeleteExpr.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.CXXDeleteExpr; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADeleteExpr; -public class CxxDeleteExpr extends ADeleteExpr { - - private final CXXDeleteExpr deleteExpr; +public class CxxDeleteExpr> extends ADeleteExpr { public CxxDeleteExpr(CXXDeleteExpr deleteExpr, CxxWeaver weaver) { - super(new CxxExpression(deleteExpr, weaver), weaver); - this.deleteExpr = deleteExpr; + super(deleteExpr, weaver); } @Override - public ClavaNode getNode() { - return deleteExpr; + public CXXDeleteExpr getNodeImpl() { + return (CXXDeleteExpr) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmpty.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmpty.java index 882ff0e8f5..3a4f9f91f1 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmpty.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmpty.java @@ -17,18 +17,15 @@ import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AEmpty; -public class CxxEmpty extends AEmpty { - - private final ClavaNode emptyNode; +public class CxxEmpty> extends AEmpty { public CxxEmpty(ClavaNode emptyNode, CxxWeaver weaver) { - super(weaver); - this.emptyNode = emptyNode; + super(emptyNode, weaver); } @Override - public ClavaNode getNode() { - return emptyNode; + public ClavaNode getNodeImpl() { + return (ClavaNode) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmptyStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmptyStmt.java index bed95388bf..5a9d64f944 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmptyStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEmptyStmt.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.EmptyStmt; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AEmptyStmt; -public class CxxEmptyStmt extends AEmptyStmt { - - private final EmptyStmt emptyStmt; +public class CxxEmptyStmt> extends AEmptyStmt { public CxxEmptyStmt(EmptyStmt emptyStmt, CxxWeaver weaver) { - super(new CxxStatement(emptyStmt, weaver), weaver); - this.emptyStmt = emptyStmt; + super(emptyStmt, weaver); } @Override - public ClavaNode getNode() { - return emptyStmt; + public EmptyStmt getNodeImpl() { + return (EmptyStmt) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumDecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumDecl.java index fc544b4fba..958823b8a7 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumDecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumDecl.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.EnumConstantDecl; import pt.up.fe.specs.clava.ast.decl.EnumDecl; import pt.up.fe.specs.clava.weaver.CxxSelects; @@ -21,23 +20,20 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AEnumDecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AEnumeratorDecl; -public class CxxEnumDecl extends AEnumDecl { - - private final EnumDecl enumDecl; +public class CxxEnumDecl> extends AEnumDecl { public CxxEnumDecl(EnumDecl enumDecl, CxxWeaver weaver) { - super(new CxxNamedDecl(enumDecl, weaver), weaver); - this.enumDecl = enumDecl; + super(enumDecl, weaver); } @Override - public ClavaNode getNode() { - return enumDecl; + public EnumDecl getNodeImpl() { + return (EnumDecl) super.getNodeImpl(); } @Override - public AEnumeratorDecl[] getEnumeratorsArrayImpl() { - return CxxSelects.select(getWeaverEngine(), AEnumeratorDecl.class, enumDecl.getChildren(), false, EnumConstantDecl.class).toArray(new AEnumeratorDecl[0]); + public AEnumeratorDecl[] getEnumeratorsImpl() { + return CxxSelects.select(getWeaverEngine(), AEnumeratorDecl.class, this.getNodeImpl().getChildren(), false, EnumConstantDecl.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumeratorDecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumeratorDecl.java index 0b048e1b51..79de88c645 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumeratorDecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxEnumeratorDecl.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.EnumConstantDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AEnumeratorDecl; -public class CxxEnumeratorDecl extends AEnumeratorDecl { - - private final EnumConstantDecl enumConstantDecl; +public class CxxEnumeratorDecl> extends AEnumeratorDecl { public CxxEnumeratorDecl(EnumConstantDecl enumDecl, CxxWeaver weaver) { - super(new CxxNamedDecl(enumDecl, weaver), weaver); - this.enumConstantDecl = enumDecl; + super(enumDecl, weaver); } @Override - public ClavaNode getNode() { - return enumConstantDecl; + public EnumConstantDecl getNodeImpl() { + return (EnumConstantDecl) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExprStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExprStmt.java index 2a5d03ef2a..04bee9f4e2 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExprStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExprStmt.java @@ -13,31 +13,26 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.ExprStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExprStmt; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; -public class CxxExprStmt extends AExprStmt { - - private final ExprStmt exprStmt; +public class CxxExprStmt> extends AExprStmt { public CxxExprStmt(ExprStmt exprStmt, CxxWeaver weaver) { - super(new CxxStatement(exprStmt, weaver), weaver); - - this.exprStmt = exprStmt; + super(exprStmt, weaver); } @Override - public ClavaNode getNode() { - return exprStmt; + public ExprStmt getNodeImpl() { + return (ExprStmt) super.getNodeImpl(); } @Override - public AExpression getExprImpl() { - return CxxJoinpoints.create(exprStmt.getExpr(), getWeaverEngine(), AExpression.class); + public AExpression getExprImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getExpr(), getWeaverEngine(), AExpression.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java index 4bb7b4e537..1128ef13dd 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java @@ -13,91 +13,49 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import pt.up.fe.specs.clava.ast.expr.Expr; import pt.up.fe.specs.clava.ast.stmt.ExprStmt; import pt.up.fe.specs.clava.weaver.CxxAttributes; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ACast; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADecl; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVardecl; +import pt.up.fe.specs.clava.weaver.enums.ExpressionUse; +import pt.up.fe.specs.util.SpecsLogs; -public class CxxExpression extends AExpression { - - private final Expr expr; +public class CxxExpression> extends AExpression { public CxxExpression(Expr expr, CxxWeaver weaver) { - super(weaver); - this.expr = expr; + super(expr, weaver); } @Override - public ClavaNode getNode() { - return expr; + public Expr getNodeImpl() { + return (Expr) super.getNodeImpl(); } @Override - public AVardecl getVardeclImpl() { + public AVardecl getVardeclImpl() { // Get more specific join point for current node - - // SpecsLogs.msgInfo("attribute 'vardecl' not implemented yet for joinpoint " + getJoinPointType()); + SpecsLogs.msgInfo("attribute 'vardecl' not implemented yet for joinpoint " + joinPointType()); return null; - /* - // DeclRefExpr declRefExpr = toDeclRefExpr(expr); - // if (declRefExpr == null) { - // return null; - // } - if (!(expr instanceof DeclRefExpr)) { - return null; - } - - Optional varDecl = ((DeclRefExpr) expr).getVariableDeclaration(); - // Optional varDecl = declRefExpr.getVariableDeclaration(); - - if (!varDecl.isPresent()) { - return null; - } - - return CxxJoinpoints.create(varDecl.get(), null); - */ } - /* - private DeclRefExpr toDeclRefExpr(ClavaNode node) { - if (node instanceof DeclRefExpr) { - return (DeclRefExpr) node; - } - - if (node.getNumChildren() == 1) { - return toDeclRefExpr(node.getChild(0)); - } - - return null; - } - */ - @Override - public String getUseImpl() { - return CxxAttributes.convertUse(expr.use()); - /* - switch (expr.use()) { - case READ: - return AExpressionUseEnum.READ.getName(); - case WRITE: - return AExpressionUseEnum.WRITE.getName(); - case READWRITE: - return AExpressionUseEnum.READWRITE.getName(); - default: - throw new RuntimeException("Case not defined:" + expr.use()); - } - */ + public ExpressionUse getUseImpl() { + return CxxAttributes.convertUse(this.getNodeImpl().use()); } - public static List selectVarDecl(AExpression expression) { - AVardecl vardecl = expression.getVardeclImpl(); + public static List> selectVarDecl(AExpression expression) { + AVardecl vardecl = expression.getVardeclImpl(); if (vardecl == null) { return Collections.emptyList(); } @@ -106,36 +64,37 @@ public static List selectVarDecl(AExpression expression) { } @Override - public Boolean getIsFunctionArgumentImpl() { - return expr.isFunctionArgument(); + public boolean getIsFunctionArgumentImpl() { + return this.getNodeImpl().isFunctionArgument(); } @Override - public ACast getImplicitCastImpl() { + public ACast getImplicitCastImpl() { // // Check if expr has an implicit cast // expr.hasValue(key) - return expr.getImplicitCast() + return this.getNodeImpl().getImplicitCast() .map(castExpr -> CxxJoinpoints.create(castExpr, getWeaverEngine(), ACast.class)) .orElse(null); } @Override - public ADecl getDeclImpl() { - return expr.getDecl() + public ADecl getDeclImpl() { + return this.getNodeImpl().getDecl() .map(decl -> CxxJoinpoints.create(decl, getWeaverEngine(), ADecl.class)) .orElse(null); } @Override - public AJoinPoint replaceWithImpl(AJoinPoint node) { + public AJoinpoint replaceWithImpl(AJoinpoint node) { // If node to replace is statement, check if this expression is inside an ExprStmt - if (node instanceof AStatement && node.getNode().getParent() instanceof ExprStmt) { + if (node instanceof AStatement && node.getNodeImpl().getParent() instanceof ExprStmt) { return node.getParentImpl().replaceWithImpl(node); } return super.replaceWithImpl(node); } + } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxField.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxField.java index a6ad283a9d..bd4cafa516 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxField.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxField.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.FieldDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AField; -public class CxxField extends AField { - - private final FieldDecl field; +public class CxxField> extends AField { public CxxField(FieldDecl field, CxxWeaver weaver) { - super(new CxxDeclarator(field, weaver), weaver); - this.field = field; + super(field, weaver); } @Override - public ClavaNode getNode() { - return field; + public FieldDecl getNodeImpl() { + return (FieldDecl) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java index 1250f73167..1c8c47faa8 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java @@ -17,6 +17,8 @@ import java.util.List; import java.util.stream.Collectors; +import org.lara.interpreter.weaver.interf.enums.InsertPosition; + import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.Decl; @@ -34,53 +36,50 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFile; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFunction; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AInclude; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVardecl; import pt.up.fe.specs.clava.weaver.importable.AstFactory; import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; -public class CxxFile extends AFile { - - private final TranslationUnit tunit; +public class CxxFile> extends AFile { public CxxFile(TranslationUnit tunit, CxxWeaver weaver) { - super(weaver); - this.tunit = tunit; + super(tunit, weaver); + } + + @Override + public TranslationUnit getNodeImpl() { + return (TranslationUnit) super.getNodeImpl(); } @Override public String getNameImpl() { - return tunit.getFilename(); + return this.getNodeImpl().getFilename(); } @Override public void setNameImpl(String filename) { - var previousFile = tunit.get(TranslationUnit.SOURCE_FILE); + var previousFile = this.getNodeImpl().get(TranslationUnit.SOURCE_FILE); var baseFolder = previousFile != null ? previousFile.getParentFile() : null; var newFile = new File(baseFolder, filename); - tunit.set(TranslationUnit.SOURCE_FILE, newFile); - } - - @Override - public TranslationUnit getNode() { - return tunit; + this.getNodeImpl().set(TranslationUnit.SOURCE_FILE, newFile); } public TranslationUnit getTu() { - return tunit; + return this.getNodeImpl(); } @Override - public Boolean getHasMainImpl() { + public boolean getHasMainImpl() { return getFunctions().stream() .filter(function -> function.getDeclName().equals("main")) .findFirst().isPresent(); } private List getFunctions() { - return tunit.getDescendantsStream() + return this.getNodeImpl().getDescendantsStream() // FunctionDecl represents C function, C++ methods, constructors and destructors .filter(node -> node instanceof FunctionDecl) .map(function -> (FunctionDecl) function) @@ -89,139 +88,139 @@ private List getFunctions() { @Override public void addIncludeImpl(String name, boolean isAngled) { - tunit.addInclude(name, isAngled); + this.getNodeImpl().addInclude(name, isAngled); } @Override public void addCIncludeImpl(String name, boolean isAngled) { - tunit.addCInclude(name, isAngled); + this.getNodeImpl().addCInclude(name, isAngled); } @Override - public AJoinPoint[] insertImpl(String position, String code) { + public AJoinpoint[] insertImpl(InsertPosition position, String code) { var tentativeNode = getWeaverEngine().getSnippetParser().parseStmt(code); ClavaNode nodeToInsert = tentativeNode instanceof WrapperStmt ? tentativeNode.getChild(0) : getWeaverEngine().getFactory().literalDecl(code); - return CxxActions.insertAsChild(position, getNode(), nodeToInsert, getWeaverEngine()); + return CxxActions.insertAsChild(position.getDisplay(), this.getNodeImpl(), nodeToInsert, getWeaverEngine()); } @Override - public AJoinPoint insertAfterImpl(AJoinPoint node) { + public AJoinpoint insertAfterImpl(AJoinpoint node) { // Check node is a decl - if (!(node.getNode() instanceof Decl)) { + if (!(node.getNodeImpl() instanceof Decl)) { SpecsLogs.msgInfo( - "Can only insert Decl nodes in a file, tried to insert a '" + node.getJoinPointType() + "'"); + "Can only insert Decl nodes in a file, tried to insert a '" + node.joinPointType() + "'"); return null; } - CxxActions.insertAsChild("after", getNode(), node.getNode(), getWeaverEngine()); + CxxActions.insertAsChild("after", this.getNodeImpl(), node.getNodeImpl(), getWeaverEngine()); return node; } @Override - public AJoinPoint insertBeforeImpl(AJoinPoint node) { + public AJoinpoint insertBeforeImpl(AJoinpoint node) { // Check node is a decl - if (node.getNode() instanceof Decl) { + if (node.getNodeImpl() instanceof Decl) { SpecsLogs.msgInfo( - "Can only insert Decl nodes in a file, tried to insert a '" + node.getJoinPointType() + "'"); + "Can only insert Decl nodes in a file, tried to insert a '" + node.joinPointType() + "'"); return null; } - CxxActions.insertAsChild("before", getNode(), node.getNode(), getWeaverEngine()); + CxxActions.insertAsChild("before", this.getNodeImpl(), node.getNodeImpl(), getWeaverEngine()); return node; } @Override public String getPathImpl() { - return tunit.getFolderpath().orElse(null); + return this.getNodeImpl().getFolderpath().orElse(null); } @Override - public void addIncludeJpImpl(AJoinPoint jp) { + public void addIncludeJpImpl(AJoinpoint jp) { // If jp is a function, include declaration if available if (jp.instanceOf("function")) { - AFunction functionJp = (AFunction) jp; - AJoinPoint decl = functionJp.getDeclarationJpImpl(); + AFunction functionJp = (AFunction) jp; + AJoinpoint decl = functionJp.getDeclarationJpImpl(); jp = decl != null ? decl : jp; } // Get first joinpoint that is a CxxFile - CxxFile includeFile = CxxJoinpoints.getAncestorandSelf(jp, CxxFile.class).get(); + CxxFile includeFile = CxxJoinpoints.getAncestorandSelf(jp, CxxFile.class).get(); // If file is the same as the current file, ignore - if (includeFile.tunit.getLocation().equals(tunit.getLocation())) { - ClavaLog.debug("addIncludeJp: ignoring include '" + includeFile.getNode().getRelativeFilepath() + if (includeFile.getNodeImpl().getLocation().equals(this.getNodeImpl().getLocation())) { + ClavaLog.debug("addIncludeJp: ignoring include '" + includeFile.getNodeImpl().getRelativeFilepath() + "', since it is in the same file"); return; } - if (!includeFile.tunit.isHeaderFile()) { - ClavaLog.info("addIncludeJp: not adding file '" + includeFile.getNode().getRelativeFilepath() + if (!includeFile.getNodeImpl().isHeaderFile()) { + ClavaLog.info("addIncludeJp: not adding file '" + includeFile.getNodeImpl().getRelativeFilepath() + "' as an include, since it is not a header file"); return; } - String includePath = includeFile.getNode().getRelativeFilepath(); + String includePath = includeFile.getNodeImpl().getRelativeFilepath(); - tunit.addInclude(includePath, false); + this.getNodeImpl().addInclude(includePath, false); } @Override public String getFilepathImpl() { - return tunit.getFile().getPath(); + return this.getNodeImpl().getFile().getPath(); } @Override public String getRelativeFolderpathImpl() { - return tunit.getRelativeFolderpath().orElse(null); + return this.getNodeImpl().getRelativeFolderpath().orElse(null); } @Override public void setRelativeFolderpathImpl(String path) { - tunit.setRelativePath(path); + this.getNodeImpl().setRelativePath(path); } @Override public String getRelativeFilepathImpl() { - return tunit.getRelativeFilepath(); + return this.getNodeImpl().getRelativeFilepath(); } @Override - public Boolean getIsCxxImpl() { - return tunit.isCXXUnit(); + public boolean getIsCxxImpl() { + return this.getNodeImpl().isCXXUnit(); } @Override - public AVardecl addGlobalImpl(String name, AJoinPoint type, String initValue) { + public AVardecl addGlobalImpl(String name, AJoinpoint type, String initValue) { // Check if joinpoint is a CxxType if (!(type instanceof AType)) { - SpecsLogs.msgInfo("addGlobal: the provided join point (" + type.getJoinPointType() + ") is not a type"); + SpecsLogs.msgInfo("addGlobal: the provided join point (" + type.joinPointType() + ") is not a type"); return null; } - Type typeNode = (Type) type.getNode(); + Type typeNode = (Type) type.getNodeImpl(); LiteralExpr literalExpr = getWeaverEngine().getFactory().literalExpr(initValue, typeNode); - VarDecl global = tunit.getApp().getGlobalManager().addGlobal(tunit, name, typeNode, literalExpr); + VarDecl global = this.getNodeImpl().getApp().getGlobalManager().addGlobal(this.getNodeImpl(), name, typeNode, literalExpr); return CxxJoinpoints.create(global, getWeaverEngine(), AVardecl.class); } @Override - public void insertBeginImpl(AJoinPoint node) { - if (!tunit.hasChildren()) { - tunit.addChild(node.getNode()); + public void insertBeginImpl(AJoinpoint node) { + if (!this.getNodeImpl().hasChildren()) { + this.getNodeImpl().addChild(node.getNodeImpl()); return; } - tunit.addChild(0, node.getNode()); + this.getNodeImpl().addChild(0, node.getNodeImpl()); } @Override @@ -230,13 +229,13 @@ public void insertBeginImpl(String code) { } @Override - public void insertEndImpl(AJoinPoint node) { - if (!tunit.hasChildren()) { - tunit.addChild(node.getNode()); + public void insertEndImpl(AJoinpoint node) { + if (!this.getNodeImpl().hasChildren()) { + this.getNodeImpl().addChild(node.getNodeImpl()); return; } - tunit.addChild(node.getNode()); + this.getNodeImpl().addChild(node.getNodeImpl()); } @Override @@ -245,18 +244,18 @@ public void insertEndImpl(String code) { } @Override - public AJoinPoint addFunctionImpl(String name) { - CxxFunction function = AstFactory.functionVoid(getWeaverEngine(), name); + public AJoinpoint addFunctionImpl(String name) { + CxxFunction function = AstFactory.functionVoid(getWeaverEngine(), name); // Add function to the tree - tunit.addChild(function.getNode()); + this.getNodeImpl().addChild(function.getNodeImpl()); return function; } @Override - public Boolean getIsHeaderImpl() { - return tunit.isHeaderFile(); + public boolean getIsHeaderImpl() { + return this.getNodeImpl().isHeaderFile(); } @Override @@ -267,31 +266,31 @@ public String writeImpl(String destinationFoldername) { return null; } - File writtenFile = tunit.write(destinationFolder); + File writtenFile = this.getNodeImpl().write(destinationFolder); getWeaverEngine().getWeaverData().addManualWrittenFile(writtenFile); return writtenFile.getAbsolutePath(); } @Override - public Boolean getIsOpenCLImpl() { - return tunit.isOpenCLFile(); + public boolean getIsOpenCLImpl() { + return this.getNodeImpl().isOpenCLFile(); } @Override - public AInclude[] getIncludesArrayImpl() { - return CxxSelects.select(getWeaverEngine(), AInclude.class, tunit.getChildren(), false, IncludeDecl.class).toArray(size -> new AInclude[size]); + public AInclude[] getIncludesImpl() { + return CxxSelects.select(getWeaverEngine(), AInclude.class, this.getNodeImpl().getChildren(), false, IncludeDecl.class); } @Override public String getBaseSourcePathImpl() { SpecsLogs.warn( "Attribute $file.baseSourcePath is deprecated, please use attribute $file.relativeFolderpath, which returns the same."); - return tunit.getRelativeFolderpath().orElse(null); + return this.getNodeImpl().getRelativeFolderpath().orElse(null); } @Override - public String getDestinationFilepathImpl(String destinationFolderpath) { + public String getGetDestinationFilepathImpl(String destinationFolderpath) { File file; if (destinationFolderpath == "" ) { @@ -299,47 +298,37 @@ public String getDestinationFilepathImpl(String destinationFolderpath) { } else { file = new File(destinationFolderpath); } - - return tunit.getDestinationFile(file).getAbsolutePath(); + + return this.getNodeImpl().getDestinationFile(file).getAbsolutePath(); } @Override - public AFile rebuildImpl() { - TranslationUnit rebuiltTunit = getWeaverEngine().rebuildFile(tunit); + public AFile rebuildImpl() { + TranslationUnit rebuiltTunit = getWeaverEngine().rebuildFile(this.getNodeImpl()); - AFile rebuiltFile = CxxJoinpoints.create(rebuiltTunit, getWeaverEngine(), AFile.class); + AFile rebuiltFile = CxxJoinpoints.create(rebuiltTunit, getWeaverEngine(), AFile.class); replaceWith(rebuiltFile); return rebuiltFile; } - @Override - public AJoinPoint rebuildTryImpl() { - try { - return rebuildImpl(); - } catch (Exception e) { - System.out.println("EXCEPTION: " + e); - return new CxxClavaException(e, getWeaverEngine()); - } - } - @Override public Object getFileImpl() { - return tunit.getFile(); + return this.getNodeImpl().getFile(); } @Override public String getSourceFoldernameImpl() { - return tunit.get(TranslationUnit.SOURCE_FOLDERNAME).orElse(null); + return this.getNodeImpl().get(TranslationUnit.SOURCE_FOLDERNAME).orElse(null); } @Override - public Boolean getHasParsingErrorsImpl() { - return tunit.get(TranslationUnit.HAS_PARSING_ERRORS); + public boolean getHasParsingErrorsImpl() { + return this.getNodeImpl().get(TranslationUnit.HAS_PARSING_ERRORS); } @Override public String getErrorOutputImpl() { - return tunit.get(TranslationUnit.ERROR_OUTPUT); + return this.getNodeImpl().get(TranslationUnit.ERROR_OUTPUT); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFloatLiteral.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFloatLiteral.java index 0905a7eb1d..7718dace3e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFloatLiteral.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFloatLiteral.java @@ -13,29 +13,24 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.FloatingLiteral; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFloatLiteral; -public class CxxFloatLiteral extends AFloatLiteral { - - private final FloatingLiteral literal; +public class CxxFloatLiteral> extends AFloatLiteral { public CxxFloatLiteral(FloatingLiteral literal, CxxWeaver weaver) { - super(new CxxLiteral(literal, weaver), weaver); - - this.literal = literal; + super(literal, weaver); } @Override - public ClavaNode getNode() { - return literal; + public FloatingLiteral getNodeImpl() { + return (FloatingLiteral) super.getNodeImpl(); } @Override - public Double getValueImpl() { - return literal.get(FloatingLiteral.VALUE).doubleValue(); + public double getValueImpl() { + return this.getNodeImpl().get(FloatingLiteral.VALUE).doubleValue(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFunction.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFunction.java index 6e76ffc89b..7319758734 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFunction.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFunction.java @@ -13,12 +13,23 @@ package pt.up.fe.specs.clava.weaver.joinpoints; +import java.io.File; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.lara.interpreter.weaver.interf.enums.InsertPosition; + import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaNodes; import pt.up.fe.specs.clava.ast.attr.CUDAGlobalAttr; -import pt.up.fe.specs.clava.ast.decl.*; -import pt.up.fe.specs.clava.ast.decl.enums.StorageClass; +import pt.up.fe.specs.clava.ast.decl.FunctionDecl; +import pt.up.fe.specs.clava.ast.decl.IncludeDecl; +import pt.up.fe.specs.clava.ast.decl.ParmVarDecl; +import pt.up.fe.specs.clava.ast.decl.VarDecl; import pt.up.fe.specs.clava.ast.expr.Expr; import pt.up.fe.specs.clava.ast.extra.App; import pt.up.fe.specs.clava.ast.extra.TranslationUnit; @@ -29,66 +40,86 @@ import pt.up.fe.specs.clava.weaver.CxxActions; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.*; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ABody; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ACall; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFile; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFunction; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFunctionType; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AParam; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AScope; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; +import pt.up.fe.specs.clava.weaver.enums.StorageClass; import pt.up.fe.specs.clava.weaver.importable.AstFactory; import pt.up.fe.specs.util.SpecsCollections; import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; +import pt.up.fe.specs.util.lazy.Lazy; +import pt.up.fe.specs.util.lazy.ThreadSafeLazy; import pt.up.fe.specs.util.treenode.NodeInsertUtils; import pt.up.fe.specs.util.treenode.TreeNodeUtils; -import java.io.File; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; +public class CxxFunction> extends AFunction { + + private static final Lazy> STORAGE_TYPE = new ThreadSafeLazy<>( + () -> buildStorageTypeMap()); + + private static Map buildStorageTypeMap() { + HashMap storageClasses = new HashMap<>(); -public class CxxFunction extends AFunction { - private final FunctionDecl function; + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.None, StorageClass.NONE); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Extern, StorageClass.EXTERN); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Static, StorageClass.STATIC); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.PrivateExtern, StorageClass.PRIVATE_EXTERN); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Auto, StorageClass.AUTO); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Register, StorageClass.REGISTER); + + return storageClasses; + } public CxxFunction(FunctionDecl function, CxxWeaver weaver) { - super(new CxxDeclarator(function, weaver), weaver); - this.function = function; + super(function, weaver); } @Override - public FunctionDecl getNode() { - return function; + public FunctionDecl getNodeImpl() { + return (FunctionDecl) super.getNodeImpl(); } @Override - public AType getTypeImpl() { - return CxxJoinpoints.create(function.getReturnType(), getWeaverEngine(), AType.class); + public AType getTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getReturnType(), getWeaverEngine(), AType.class); } @Override - public AFunctionType getFunctionTypeImpl() { - return CxxJoinpoints.create(function.getFunctionType(), getWeaverEngine(), AFunctionType.class); + public AFunctionType getFunctionTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getFunctionType(), getWeaverEngine(), AFunctionType.class); } @Override - public ACall newCallImpl(AJoinPoint[] args) { - return AstFactory.callFromFunction(getWeaverEngine(), this, SpecsCollections.asListT(AJoinPoint.class, (Object[]) args)); + public ACall newCallImpl(AJoinpoint[] args) { + return AstFactory.callFromFunction(getWeaverEngine(), this, SpecsCollections.asListT(AJoinpoint.class, (Object[]) args)); } @Override - public Boolean getHasDefinitionImpl() { + public boolean getHasDefinitionImpl() { return getIsImplementationImpl(); } @Override - public Boolean getIsImplementationImpl() { - return function.hasBody(); + public boolean getIsImplementationImpl() { + return this.getNodeImpl().hasBody(); } @Override - public Boolean getIsPrototypeImpl() { - return !function.hasBody(); + public boolean getIsPrototypeImpl() { + return !this.getNodeImpl().hasBody(); } - private AJoinPoint processNodeToInsert(AJoinPoint node) { + private AJoinpoint processNodeToInsert(AJoinpoint node) { // If node is an expression or VarDecl, convert to Stmt first - var clavaNode = node.getNode(); + var clavaNode = node.getNodeImpl(); if (clavaNode instanceof VarDecl || clavaNode instanceof Expr) { return CxxJoinpoints.create(ClavaNodes.toStmt(clavaNode), getWeaverEngine()); @@ -99,88 +130,87 @@ private AJoinPoint processNodeToInsert(AJoinPoint node) { } @Override - public AJoinPoint[] insertImpl(String position, String code) { + public AJoinpoint[] insertImpl(InsertPosition position, String code) { // Stmt literalStmt = ClavaNodeFactory.literalStmt(code); Stmt literalStmt = getWeaverEngine().getSnippetParser().parseStmt(code); return insertStmt(literalStmt, position); } @Override - public AJoinPoint insertAfterImpl(AJoinPoint node) { + public AJoinpoint insertAfterImpl(AJoinpoint node) { var processNode = processNodeToInsert(node); return CxxActions.insertJp(this, processNode, "after", getWeaverEngine()); } @Override - public AJoinPoint insertAfterImpl(String code) { + public AJoinpoint insertAfterImpl(String code) { return insertAfterImpl(CxxJoinpoints.create(getWeaverEngine().getSnippetParser().parseStmt(code), getWeaverEngine())); } @Override - public AJoinPoint insertBeforeImpl(AJoinPoint node) { + public AJoinpoint insertBeforeImpl(AJoinpoint node) { var processNode = processNodeToInsert(node); return CxxActions.insertJp(this, processNode, "before", getWeaverEngine()); } @Override - public AJoinPoint insertBeforeImpl(String code) { + public AJoinpoint insertBeforeImpl(String code) { return insertBeforeImpl(CxxJoinpoints.create(getWeaverEngine().getSnippetParser().parseStmt(code), getWeaverEngine())); } @Override - public AJoinPoint replaceWithImpl(AJoinPoint node) { + public AJoinpoint replaceWithImpl(AJoinpoint node) { var processNode = processNodeToInsert(node); return CxxActions.insertJp(this, processNode, "replace", getWeaverEngine()); } - private AJoinPoint[] insertStmt(Stmt newNode, String position) { + private AJoinpoint[] insertStmt(Stmt newNode, InsertPosition position) { switch (position) { - case "before": - NodeInsertUtils.insertBefore(function, newNode); + case BEFORE: + NodeInsertUtils.insertBefore(this.getNodeImpl(), newNode); return null; - case "after": - NodeInsertUtils.insertAfter(function, newNode); + case AFTER: + NodeInsertUtils.insertAfter(this.getNodeImpl(), newNode); return null; - case "around": - case "replace": - NodeInsertUtils.replace(function, newNode); - return new AJoinPoint[]{CxxJoinpoints.create(newNode, getWeaverEngine())}; + case REPLACE: + NodeInsertUtils.replace(this.getNodeImpl(), newNode); + return new AJoinpoint[]{CxxJoinpoints.create(newNode, getWeaverEngine())}; default: throw new RuntimeException("Case not defined:" + position); } } @Override - public String getDeclarationImpl(Boolean withReturnType) { - return function.getDeclarationId(withReturnType); + public String getGetDeclarationImpl(boolean withReturnType) { + return this.getNodeImpl().getDeclarationId(withReturnType); } @Override - public ABody getBodyImpl() { - if (!function.hasBody()) { + public ABody getBodyImpl() { + if (!this.getNodeImpl().hasBody()) { return null; } - return CxxJoinpoints.create(function.getBody().get(), getWeaverEngine(), ABody.class); + return CxxJoinpoints.create(this.getNodeImpl().getBody().get(), getWeaverEngine(), ABody.class); } @Override - public AFunction cloneImpl(String newName, Boolean insert) { + public AFunction cloneImpl(String newName, boolean insert) { /* make clone and insert after the function of this join point */ - return makeCloneAndInsert(newName, function, insert); + return makeCloneAndInsert(newName, this.getNodeImpl(), insert); } - private AFunction makeCloneAndInsert(String newName, ClavaNode reference, boolean insert) { + private AFunction makeCloneAndInsert(String newName, ClavaNode reference, boolean insert) { FunctionDecl newFunc = null; if (reference instanceof FunctionDecl) { - newFunc = function.cloneAndInsert(newName, insert); + newFunc = this.getNodeImpl().cloneAndInsert(newName, insert); } else if (reference instanceof TranslationUnit) { - newFunc = function.cloneAndInsertOnFile(newName, (TranslationUnit) reference, insert); + newFunc = this.getNodeImpl().cloneAndInsertOnFile(newName, (TranslationUnit) reference, insert); } else { throw new IllegalArgumentException( "The node (" + reference + ") needs to be either a FuncDecl or a TranslationUnit."); @@ -190,9 +220,9 @@ private AFunction makeCloneAndInsert(String newName, ClavaNode reference, boolea } @Override - public AFunction cloneOnFileImpl(String newName, String fileName) { + public AFunction cloneOnFileImpl(String newName, String fileName) { if (fileName == null) { - boolean isCxx = function.getAncestor(TranslationUnit.class).isCXXUnit(); + boolean isCxx = this.getNodeImpl().getAncestor(TranslationUnit.class).isCXXUnit(); String extension = getIsPrototypeImpl() ? ".h" : isCxx ? ".cpp" : ".c"; String prefix = newName; @@ -202,7 +232,7 @@ public AFunction cloneOnFileImpl(String newName, String fileName) { // First, check if the given filename is the same as a file in the AST - App app = (App) getRootImpl().getNode(); + App app = (App) getRootImpl().getNodeImpl(); var currentFile = new File(fileName); var existingFile = app.getTranslationUnits().stream() @@ -210,7 +240,7 @@ public AFunction cloneOnFileImpl(String newName, String fileName) { .findFirst(); if (existingFile.isPresent()) { - return cloneOnFileImpl(newName, new CxxFile(existingFile.get(), getWeaverEngine())); + return cloneOnFileImpl(newName, new CxxFile<>(existingFile.get(), getWeaverEngine())); } // Extract relative path @@ -220,54 +250,27 @@ public AFunction cloneOnFileImpl(String newName, String fileName) { var newFile = AstFactory.file(getWeaverEngine(), fileName, relativePath); // Set same source foldername - var originalFile = function.getAncestorTry(TranslationUnit.class).orElse(null); + var originalFile = this.getNodeImpl().getAncestorTry(TranslationUnit.class).orElse(null); if (originalFile != null) { - // newFile.getNode().set(TranslationUnit.SOURCE_FOLDERNAME, - // originalFile.get(TranslationUnit.SOURCE_FOLDERNAME)); - newFile.getNode().copyValue(TranslationUnit.SOURCE_FOLDERNAME, originalFile); - // originalFile.get(TranslationUnit.SOURCE_FOLDERNAME). + newFile.getNodeImpl().copyValue(TranslationUnit.SOURCE_FOLDERNAME, originalFile); } - // System.out.println("NEW FILE:" + newFile.getNode()); - // System.out.println("CURRRENT FILE:" + function.getAncestor(TranslationUnit.class)); - app.addFile((TranslationUnit) newFile.getNode()); + app.addFile((TranslationUnit) newFile.getNodeImpl()); return cloneOnFileImpl(newName, newFile); } @Override // TODO: copy header file inclusion - public AFunction cloneOnFileImpl(String newName, AFile file) { - - // if (!function.hasBody()) { - // /*add the clone to the original place in order to be included where needed */ - // return makeCloneAndInsert(newName, function, true); - // } - - /* if this is a definition, add the clone to the correct file */ - - // App app = getRootImpl().getNode(); - // - // Optional file = app.getFile(fileName); - // - // if (!file.isPresent()) { - // - // TranslationUnit tu = getFactory().translationUnit(new File(fileName), Collections.emptyList()); - // - // app.addFile(tu); - // - // file = Optional.of(tu); - // } - - var tu = (TranslationUnit) file.getNode(); + public AFunction cloneOnFileImpl(String newName, AFile file) { + var tu = (TranslationUnit) file.getNodeImpl(); var cloneFunction = makeCloneAndInsert(newName, tu, true); /* copy headers from the current file to the file with the clone */ - TranslationUnit originalFile = function.getAncestorTry(TranslationUnit.class).orElse(null); + TranslationUnit originalFile = this.getNodeImpl().getAncestorTry(TranslationUnit.class).orElse(null); if (originalFile != null) { var includesCopy = TreeNodeUtils.copy(originalFile.getIncludes().getIncludes()); - // List allIncludes = getIncludesCopyFromFile(originalFile); File baseIncludePath = null; @@ -284,9 +287,6 @@ public AFunction cloneOnFileImpl(String newName, AFile file) { .map(relativeFolder -> new File(relativeDepth, relativeFolder)) .orElse(baseIncludePath); - // System.out.println("BASE: " + baseIncludePath); - // System.out.println("DEPTH: " + relativeFolderDepth); - // Adapt includes for (var includeDecl : includesCopy) { var include = includeDecl.getInclude(); @@ -295,10 +295,9 @@ public AFunction cloneOnFileImpl(String newName, AFile file) { if (include.isAngled()) { continue; } - // System.out.println("INCLUDE BEFORE: " + includeDecl.getCode()); + var newInclude = include.setInclude(new File(baseIncludePath, include.getInclude()).toString()); includeDecl.set(IncludeDecl.INCLUDE, newInclude); - // System.out.println("INCLUDE AFTER: " + includeDecl.getCode()); } // Add includes @@ -310,9 +309,8 @@ public AFunction cloneOnFileImpl(String newName, AFile file) { } @Override - public String[] getParamNamesArrayImpl() { - - return function.getParameters() + public String[] getParamNamesImpl() { + return this.getNodeImpl().getParameters() .stream() .map(ParmVarDecl::getCode) .collect(Collectors.toList()) @@ -320,8 +318,8 @@ public String[] getParamNamesArrayImpl() { } @Override - public AParam[] getParamsArrayImpl() { - return function.getParameters() + public AParam[] getParamsImpl() { + return this.getNodeImpl().getParameters() .stream() .map(param -> CxxJoinpoints.create(param, getWeaverEngine(), AParam.class)) @@ -330,58 +328,23 @@ public AParam[] getParamsArrayImpl() { } @Override - public AJoinPoint insertReturnImpl(String code) { + public AJoinpoint insertReturnImpl(String code) { return insertReturnImpl(CxxJoinpoints.create(getWeaverEngine().getSnippetParser().parseStmt(code), getWeaverEngine())); } @Override - public AJoinPoint insertReturnImpl(AJoinPoint code) { + public AJoinpoint insertReturnImpl(AJoinpoint code) { // Does not take into account situations where functions returns in all paths of an if/else. // This means it can lead to dead-code, although for C/C++ that does not seem to be problematic. // Do not insert if function has no implementation - if (!function.hasBody()) { + if (!this.getNodeImpl().hasBody()) { ClavaLog.info("insertReturn: could not insert in function without body"); return null; } return CxxActions.insertReturn(getBodyImpl(), code, getWeaverEngine()); - - // - // List bodyStmts = function.getBody().get().toStatements(); - // - // // Check if it has return statement, ignoring wrapper statements - // Stmt lastStmt = SpecsCollections.reverseStream(bodyStmts) - // .filter(stmt -> !(stmt instanceof WrapperStmt)) - // .findFirst().orElse(null); - // - // ReturnStmt lastReturnStmt = lastStmt instanceof ReturnStmt ? (ReturnStmt) lastStmt : null; - // - // // Get list of all return statements inside children - // List returnStatements = bodyStmts.stream() - // .flatMap(Stmt::getDescendantsStream) - // .filter(ReturnStmt.class::isInstance) - // .map(ReturnStmt.class::cast) - // .collect(Collectors.toList()); - // - // AJoinPoint lastInsertPoint = null; - // - // if (lastReturnStmt != null) { - // returnStatements = SpecsCollections.concat(returnStatements, lastReturnStmt); - // } - // - // for (ReturnStmt returnStmt : returnStatements) { - // ACxxWeaverJoinPoint returnJp = CxxJoinpoints.create(returnStmt); - // lastInsertPoint = returnJp.insertBefore(code); - // } - // - // // If there is no return in the body, add at the end of the function - // if (lastReturnStmt == null) { - // lastInsertPoint = getBodyImpl().insertEnd(code); - // } - // - // return lastInsertPoint; } /** @@ -389,19 +352,19 @@ public AJoinPoint insertReturnImpl(AJoinPoint code) { */ @Override public String getIdImpl() { - return getDeclarationImpl(false); + return getGetDeclarationImpl(false); } @Override - public AFunction[] getDeclarationJpsArrayImpl() { - return function.getPrototypes().stream() + public AFunction[] getDeclarationJpsImpl() { + return this.getNodeImpl().getPrototypes().stream() .map(node -> CxxJoinpoints.create(node, getWeaverEngine(), AFunction.class)) - .toArray(size -> new AFunction[size]); + .toArray(AFunction[]::new); } @Override - public AFunction getDeclarationJpImpl() { - var prototypes = getDeclarationJpsArrayImpl(); + public AFunction getDeclarationJpImpl() { + var prototypes = getDeclarationJpsImpl(); if (prototypes.length == 0) { return null; @@ -416,8 +379,8 @@ public AFunction getDeclarationJpImpl() { } @Override - public AFunction getDefinitionJpImpl() { - return function.getImplementation() + public AFunction getDefinitionJpImpl() { + return this.getNodeImpl().getImplementation() .map(node -> CxxJoinpoints.create(node, getWeaverEngine(), AFunction.class)) .orElse(null); } @@ -426,7 +389,7 @@ public AFunction getDefinitionJpImpl() { * Setting the type of a Function join point sets the return type */ @Override - public void setTypeImpl(AType type) { + public void setTypeImpl(AType type) { setReturnTypeImpl(type); } @@ -436,77 +399,92 @@ public void setNameImpl(String name) { // Needs to first fetch both definition and declaration. // If one is renamed before fetching the other, the other will not be found - var impl = function.getImplementation(); - var proto = function.getPrototypes(); + var impl = this.getNodeImpl().getImplementation(); + var proto = this.getNodeImpl().getPrototypes(); impl.ifPresent(node -> node.setName(name)); proto.stream().forEach(node -> node.setName(name)); } @Override - public String getStorageClassImpl() { - return function.get(FunctionDecl.STORAGE_CLASS).getString(); + public StorageClass getStorageClassImpl() { + var nodeStorageClass = this.getNodeImpl().get(FunctionDecl.STORAGE_CLASS); + if (nodeStorageClass == null) { + throw new RuntimeException("Storage class of function '" + getSignatureImpl() + "' is null"); + } + + StorageClass jpStorageClass = STORAGE_TYPE.get().get(nodeStorageClass); + if (jpStorageClass == null) { + throw new RuntimeException("Storage class '" + nodeStorageClass + "' of function '" + getSignatureImpl() + + "' is not supported in the join point model"); + } + + return jpStorageClass; } @Override - public boolean setStorageClassImpl(String storageClass) { - // Get corresponding enum - var storageClassEnum = StorageClass.getHelper().fromValue(storageClass); + public boolean setStorageClassImpl(StorageClass storageClass) { + var nodeStorageClass = STORAGE_TYPE.get().entrySet().stream() + .filter(entry -> entry.getValue() == storageClass) + .map(Map.Entry::getKey) + .findFirst() + .orElseThrow(() -> new RuntimeException( + "Storage class '" + storageClass + "' is not supported in the join point model")); - return function.setStorageClass(storageClassEnum); + return this.getNodeImpl().setStorageClass(nodeStorageClass); } @Override - public Boolean getIsInlineImpl() { - return function.get(FunctionDecl.IS_INLINE_SPECIFIED); + public boolean getIsInlineImpl() { + return this.getNodeImpl().get(FunctionDecl.IS_INLINE_SPECIFIED); } @Override - public Boolean getIsVirtualImpl() { - return function.get(FunctionDecl.IS_VIRTUAL_AS_WRITTEN); + public boolean getIsVirtualImpl() { + return this.getNodeImpl().get(FunctionDecl.IS_VIRTUAL_AS_WRITTEN); } @Override - public Boolean getIsModulePrivateImpl() { - return function.get(FunctionDecl.IS_MODULE_PRIVATE); + public boolean getIsModulePrivateImpl() { + return this.getNodeImpl().get(FunctionDecl.IS_MODULE_PRIVATE); } @Override - public Boolean getIsPureImpl() { - return function.get(FunctionDecl.IS_PURE); + public boolean getIsPureImpl() { + return this.getNodeImpl().get(FunctionDecl.IS_PURE); } @Override - public Boolean getIsDeleteImpl() { - return function.get(FunctionDecl.IS_DELETED); + public boolean getIsDeleteImpl() { + return this.getNodeImpl().get(FunctionDecl.IS_DELETED); } @Override - public ACall[] getCallsArrayImpl() { - return function.getCalls().stream() + public ACall[] getCallsImpl() { + return this.getNodeImpl().getCalls().stream() .map(call -> CxxJoinpoints.create(call, getWeaverEngine(), ACall.class)) .toArray(ACall[]::new); } @Override - public void setParamsImpl(AParam[] params) { + public void setParamsImpl(AParam[] params) { List newParams = Arrays.stream( params) - .map(param -> (ParmVarDecl) param.getNode()) + .map(param -> (ParmVarDecl) param.getNodeImpl()) .collect(Collectors.toList()); - function.setParameters(newParams); + this.getNodeImpl().setParameters(newParams); } @Override public void setParamsFromStringsImpl(String[] params) { - AParam[] newParams = new AParam[params.length]; + AParam[] newParams = new AParam[params.length]; // Each value is a type - varName pair, separate them by last space for (int i = 0; i < params.length; i++) { String typeVarname = params[i]; - var parmVarDecl = ClavaNodes.toParam(typeVarname, function); + var parmVarDecl = ClavaNodes.toParam(typeVarname, this.getNodeImpl()); newParams[i] = CxxJoinpoints.create(parmVarDecl, getWeaverEngine(), AParam.class); } @@ -516,37 +494,37 @@ public void setParamsFromStringsImpl(String[] params) { @Override public String getSignatureImpl() { - return function.getSignature(); + return this.getNodeImpl().getSignature(); } @Override - public void setBodyImpl(AScope body) { - function.setBody((CompoundStmt) body.getNode()); + public void setBodyImpl(AScope body) { + this.getNodeImpl().setBody((CompoundStmt) body.getNodeImpl()); } @Override - public void setFunctionTypeImpl(AFunctionType functionType) { - function.setFunctionType((FunctionType) functionType.getNode()); + public void setFunctionTypeImpl(AFunctionType functionType) { + this.getNodeImpl().setFunctionType((FunctionType) functionType.getNodeImpl()); } @Override - public AType getReturnTypeImpl() { - return CxxJoinpoints.create(function.getReturnType(), getWeaverEngine(), AType.class); + public AType getReturnTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getReturnType(), getWeaverEngine(), AType.class); } @Override - public void setReturnTypeImpl(AType returnType) { - function.setReturnType((Type) returnType.getNode()); + public void setReturnTypeImpl(AType returnType) { + this.getNodeImpl().setReturnType((Type) returnType.getNodeImpl()); } @Override - public void setParamTypeImpl(int index, AType newType) { - function.setParamType(index, (Type) newType.getNode()); + public void setParamTypeImpl(int index, AType newType) { + this.getNodeImpl().setParamType(index, (Type) newType.getNodeImpl()); } @Override - public void addParamImpl(AParam param) { - var originalParams = getParamsArrayImpl(); + public void addParamImpl(AParam param) { + var originalParams = getParamsImpl(); var newParams = Arrays.copyOf(originalParams, originalParams.length + 1); newParams[newParams.length - 1] = param; @@ -555,23 +533,23 @@ public void addParamImpl(AParam param) { } @Override - public void addParamImpl(String name, AType type) { + public void addParamImpl(String name, AType type) { ClavaNode paramNode; if (type == null) { - paramNode = ClavaNodes.toParam(name, function); + paramNode = ClavaNodes.toParam(name, this.getNodeImpl()); } else { - paramNode = getFactory().parmVarDecl(name, (Type) type.getNode()); + paramNode = getFactory().parmVarDecl(name, (Type) type.getNodeImpl()); } addParamImpl(CxxJoinpoints.create(paramNode, getWeaverEngine(), AParam.class)); } @Override - public void setParamImpl(int index, AParam param) { - var params = getParamsArrayImpl(); + public void setParamImpl(int index, AParam param) { + var params = getParamsImpl(); if (index >= params.length) { SpecsLogs.info("Tried to set parameter '" + param.getCodeImpl() + "' at index '" + index - + "' but function '" + function.getSignature() + "' only has " + params.length + " parameters"); + + "' but function '" + this.getNodeImpl().getSignature() + "' only has " + params.length + " parameters"); return; } @@ -581,34 +559,34 @@ public void setParamImpl(int index, AParam param) { } @Override - public void setParamImpl(int index, String name, AType type) { + public void setParamImpl(int index, String name, AType type) { ClavaNode paramNode; if (type == null) { - paramNode = ClavaNodes.toParam(name, function); + paramNode = ClavaNodes.toParam(name, this.getNodeImpl()); } else { - paramNode = getFactory().parmVarDecl(name, (Type) type.getNode()); + paramNode = getFactory().parmVarDecl(name, (Type) type.getNodeImpl()); } setParamImpl(index, CxxJoinpoints.create(paramNode, getWeaverEngine(), AParam.class)); } @Override - public Boolean getIsCudaKernelImpl() { - return function.get(FunctionDecl.ATTRIBUTES).stream() + public boolean getIsCudaKernelImpl() { + return this.getNodeImpl().get(FunctionDecl.ATTRIBUTES).stream() .filter(attr -> attr instanceof CUDAGlobalAttr) .findFirst() .isPresent(); } @Override - public AFunction getCanonicalImpl() { - return CxxJoinpoints.create(function.canonical(), getWeaverEngine(), AFunction.class); + public AFunction getCanonicalImpl() { + return CxxJoinpoints.create(this.getNodeImpl().canonical(), getWeaverEngine(), AFunction.class); } @Override - public Boolean getIsCanonicalImpl() { - return function.isCanonical(); + public boolean getIsCanonicalImpl() { + return this.getNodeImpl().isCanonical(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxGotoStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxGotoStmt.java index b6e191a330..49514056f0 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxGotoStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxGotoStmt.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.LabelDecl; import pt.up.fe.specs.clava.ast.stmt.GotoStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,29 +20,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AGotoStmt; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ALabelDecl; -public class CxxGotoStmt extends AGotoStmt { - - private final GotoStmt gotoStmt; +public class CxxGotoStmt> extends AGotoStmt { public CxxGotoStmt(GotoStmt gotoStmt, CxxWeaver weaver) { - super(new CxxStatement(gotoStmt, weaver), weaver); - - this.gotoStmt = gotoStmt; + super(gotoStmt, weaver); } @Override - public ClavaNode getNode() { - return gotoStmt; + public GotoStmt getNodeImpl() { + return (GotoStmt) super.getNodeImpl(); } @Override - public void setLabelImpl(ALabelDecl label) { - gotoStmt.setLabel((LabelDecl) label.getNode()); + public void setLabelImpl(ALabelDecl label) { + this.getNodeImpl().setLabel((LabelDecl) label.getNodeImpl()); } @Override - public ALabelDecl getLabelImpl() { - return CxxJoinpoints.create(gotoStmt.getLabel(), getWeaverEngine(), ALabelDecl.class); + public ALabelDecl getLabelImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getLabel(), getWeaverEngine(), ALabelDecl.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIf.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIf.java index a632ca3bc8..ba625e6760 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIf.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIf.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.stream.Collectors; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.Expr; import pt.up.fe.specs.clava.ast.stmt.IfStmt; import pt.up.fe.specs.clava.ast.stmt.Stmt; @@ -31,66 +30,63 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVardecl; import pt.up.fe.specs.util.SpecsCollections; -public class CxxIf extends AIf { - - private final IfStmt ifStmt; +public class CxxIf> extends AIf { public CxxIf(IfStmt ifStmt, CxxWeaver weaver) { - super(new CxxStatement(ifStmt, weaver), weaver); - this.ifStmt = ifStmt; + super(ifStmt, weaver); } @Override - public ClavaNode getNode() { - return ifStmt; + public IfStmt getNodeImpl() { + return (IfStmt) super.getNodeImpl(); } @Override - public AExpression getCondImpl() { - List list = Collections.emptyList(); + public AExpression getCondImpl() { + List> list = Collections.emptyList(); - if ((ifStmt.getCondition() instanceof Expr)) { - list = Arrays.asList(CxxJoinpoints.create(ifStmt.getCondition(), getWeaverEngine(), AExpression.class)); + if ((this.getNodeImpl().getCondition() instanceof Expr)) { + list = Arrays.asList(CxxJoinpoints.create(this.getNodeImpl().getCondition(), getWeaverEngine(), AExpression.class)); } return SpecsCollections.orElseNull(list); } @Override - public AVardecl getCondDeclImpl() { - return SpecsCollections.orElseNull(SpecsCollections.toList(ifStmt.getDeclCondition() + public AVardecl getCondDeclImpl() { + return SpecsCollections.orElseNull(SpecsCollections.toList(this.getNodeImpl().getDeclCondition() .map(varDecl -> CxxJoinpoints.create(varDecl, getWeaverEngine(), AVardecl.class)))); } @Override - public AScope getThenImpl() { + public AScope getThenImpl() { return SpecsCollections.orElseNull( - ifStmt.getThen().map(then -> Arrays.asList(CxxJoinpoints.create(then, + this.getNodeImpl().getThen().map(then -> Arrays.asList(CxxJoinpoints.create(then, getWeaverEngine(), AScope.class))) .orElse(Collections.emptyList())); } @Override - public AScope getElseImpl() { - return SpecsCollections.orElseNull(SpecsCollections.toStream(ifStmt.getElse()) + public AScope getElseImpl() { + return SpecsCollections.orElseNull(SpecsCollections.toStream(this.getNodeImpl().getElse()) .map(stmt -> CxxJoinpoints.create(stmt, getWeaverEngine(), AScope.class)) .collect(Collectors.toList())); } @Override - public void setCondImpl(AExpression cond) { - ifStmt.setCondition((Expr) cond.getNode()); + public void setCondImpl(AExpression cond) { + this.getNodeImpl().setCondition((Expr) cond.getNodeImpl()); } @Override - public void setThenImpl(AStatement then) { - ifStmt.setThen((Stmt) then.getNode()); + public void setThenImpl(AStatement then) { + this.getNodeImpl().setThen((Stmt) then.getNodeImpl()); } @Override - public void setElseImpl(AStatement _else) { - ifStmt.setElse((Stmt) _else.getNode()); + public void setElseImpl(AStatement _else) { + this.getNodeImpl().setElse((Stmt) _else.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxImplicitValue.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxImplicitValue.java index 8942625528..e438428aa7 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxImplicitValue.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxImplicitValue.java @@ -13,22 +13,18 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.ImplicitValueInitExpr; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AImplicitValue; -public class CxxImplicitValue extends AImplicitValue { - - private final ImplicitValueInitExpr implicitValue; +public class CxxImplicitValue> extends AImplicitValue { public CxxImplicitValue(ImplicitValueInitExpr implicitValue, CxxWeaver weaver) { - super(new CxxExpression(implicitValue, weaver), weaver); - this.implicitValue = implicitValue; + super(implicitValue, weaver); } @Override - public ClavaNode getNode() { - return implicitValue; + public ImplicitValueInitExpr getNodeImpl() { + return (ImplicitValueInitExpr) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInclude.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInclude.java index b150bb4621..e5982aaf2a 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInclude.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInclude.java @@ -13,43 +13,39 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.IncludeDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AInclude; -public class CxxInclude extends AInclude { - - private final IncludeDecl include; +public class CxxInclude> extends AInclude { public CxxInclude(IncludeDecl include, CxxWeaver weaver) { - super(new CxxDecl(include, weaver), weaver); - this.include = include; + super(include, weaver); } @Override - public ClavaNode getNode() { - return include; + public IncludeDecl getNodeImpl() { + return (IncludeDecl) super.getNodeImpl(); } @Override public String getNameImpl() { - return include.getInclude().getInclude(); + return this.getNodeImpl().getInclude().getInclude(); } @Override - public Boolean getIsAngledImpl() { - return include.getInclude().isAngled(); + public boolean getIsAngledImpl() { + return this.getNodeImpl().getInclude().isAngled(); } @Override public String getFilepathImpl() { - return include.getInclude().getSourceFile().getAbsolutePath(); + return this.getNodeImpl().getInclude().getSourceFile().getAbsolutePath(); } @Override public String getRelativeFolderpathImpl() { - return include.getInclude().getRelativeFolder().getAbsolutePath(); + return this.getNodeImpl().getInclude().getRelativeFolder().getAbsolutePath(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInitList.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInitList.java index aa82d92145..51207f553e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInitList.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxInitList.java @@ -13,31 +13,26 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.InitListExpr; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AInitList; -public class CxxInitList extends AInitList { - - private final InitListExpr initList; +public class CxxInitList> extends AInitList { public CxxInitList(InitListExpr initList, CxxWeaver weaver) { - super(new CxxExpression(initList, weaver), weaver); - - this.initList = initList; + super(initList, weaver); } @Override - public ClavaNode getNode() { - return initList; + public InitListExpr getNodeImpl() { + return (InitListExpr) super.getNodeImpl(); } @Override - public AExpression getArrayFillerImpl() { - return initList.get(InitListExpr.ARRAY_FILLER) + public AExpression getArrayFillerImpl() { + return this.getNodeImpl().get(InitListExpr.ARRAY_FILLER) .map(n -> CxxJoinpoints.create(n, getWeaverEngine(), AExpression.class)) .orElse(null); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIntLiteral.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIntLiteral.java index 6d9d6bc256..cb28d3d3c7 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIntLiteral.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxIntLiteral.java @@ -13,30 +13,23 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.IntegerLiteral; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AIntLiteral; -public class CxxIntLiteral extends AIntLiteral { - - private final IntegerLiteral literal; +public class CxxIntLiteral> extends AIntLiteral { public CxxIntLiteral(IntegerLiteral literal, CxxWeaver weaver) { - super(new CxxLiteral(literal, weaver), weaver); - - this.literal = literal; + super(literal, weaver); } @Override - public ClavaNode getNode() { - return literal; + public IntegerLiteral getNodeImpl() { + return (IntegerLiteral) super.getNodeImpl(); } @Override - public Long getValueImpl() { - return literal.get(IntegerLiteral.VALUE).longValue(); + public long getValueImpl() { + return this.getNodeImpl().get(IntegerLiteral.VALUE).longValue(); } - - } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelDecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelDecl.java index 001b8ad71f..6963a3459e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelDecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelDecl.java @@ -13,30 +13,26 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.LabelDecl; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ALabelDecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ALabelStmt; -public class CxxLabelDecl extends ALabelDecl { - - private final LabelDecl labelDecl; +public class CxxLabelDecl> extends ALabelDecl { public CxxLabelDecl(LabelDecl labelDecl, CxxWeaver weaver) { - super(new CxxNamedDecl(labelDecl, weaver), weaver); - this.labelDecl = labelDecl; + super(labelDecl, weaver); } @Override - public ClavaNode getNode() { - return labelDecl; + public LabelDecl getNodeImpl() { + return (LabelDecl) super.getNodeImpl(); } @Override - public ALabelStmt getLabelStmtImpl() { - return labelDecl.get(LabelDecl.LABEL_STMT) + public ALabelStmt getLabelStmtImpl() { + return this.getNodeImpl().get(LabelDecl.LABEL_STMT) .map(labelStmt -> CxxJoinpoints.create(labelStmt, getWeaverEngine(), ALabelStmt.class)) .orElse(null); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelStmt.java index a69b2c5c3d..72f6f5a95e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLabelStmt.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.LabelDecl; import pt.up.fe.specs.clava.ast.stmt.LabelStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,28 +20,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ALabelDecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ALabelStmt; -public class CxxLabelStmt extends ALabelStmt { - - private final LabelStmt labelStmt; +public class CxxLabelStmt> extends ALabelStmt { public CxxLabelStmt(LabelStmt labelStmt, CxxWeaver weaver) { - super(new CxxStatement(labelStmt, weaver), weaver); - this.labelStmt = labelStmt; + super(labelStmt, weaver); } @Override - public ALabelDecl getDeclImpl() { - return CxxJoinpoints.create(labelStmt.getLabelDecl(), getWeaverEngine(), ALabelDecl.class); + public LabelStmt getNodeImpl() { + return (LabelStmt) super.getNodeImpl(); } @Override - public void setDeclImpl(ALabelDecl label) { - labelStmt.setLabelDecl((LabelDecl) label.getNode()); + public ALabelDecl getDeclImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getLabelDecl(), getWeaverEngine(), ALabelDecl.class); } @Override - public ClavaNode getNode() { - return labelStmt; + public void setDeclImpl(ALabelDecl label) { + this.getNodeImpl().setLabelDecl((LabelDecl) label.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLiteral.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLiteral.java index cfdfaaba67..4d4d1170c6 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLiteral.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLiteral.java @@ -13,24 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.Literal; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ALiteral; -public class CxxLiteral extends ALiteral { - - private final Literal literal; +public class CxxLiteral> extends ALiteral { public CxxLiteral(Literal literal, CxxWeaver weaver) { - super(new CxxExpression(literal, weaver), weaver); - - this.literal = literal; + super(literal, weaver); } @Override - public ClavaNode getNode() { - return literal; + public Literal getNodeImpl() { + return (Literal) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java index ec5356f716..917e6eaf45 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java @@ -13,12 +13,28 @@ package pt.up.fe.specs.clava.weaver.joinpoints; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaNodes; import pt.up.fe.specs.clava.ast.expr.BinaryOperator; import pt.up.fe.specs.clava.ast.expr.enums.BinaryOperatorKind; -import pt.up.fe.specs.clava.ast.stmt.*; +import pt.up.fe.specs.clava.ast.stmt.CXXForRangeStmt; +import pt.up.fe.specs.clava.ast.stmt.CompoundStmt; +import pt.up.fe.specs.clava.ast.stmt.DoStmt; +import pt.up.fe.specs.clava.ast.stmt.ForStmt; +import pt.up.fe.specs.clava.ast.stmt.LiteralStmt; +import pt.up.fe.specs.clava.ast.stmt.LoopStmt; +import pt.up.fe.specs.clava.ast.stmt.Stmt; +import pt.up.fe.specs.clava.ast.stmt.WhileStmt; import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.ast.type.enums.BuiltinKind; import pt.up.fe.specs.clava.transform.loop.LoopAnalysisUtils; @@ -26,27 +42,28 @@ import pt.up.fe.specs.clava.transform.loop.LoopTiling; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.*; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.enums.ALoopKindEnum; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ALoop; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AScope; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVarref; +import pt.up.fe.specs.clava.weaver.enums.LoopKind; import pt.up.fe.specs.clava.weaver.enums.Relation; -import pt.up.fe.specs.util.SpecsEnums; import pt.up.fe.specs.util.lazy.Lazy; import pt.up.fe.specs.util.lazy.ThreadSafeLazy; -import java.util.*; - -public class CxxLoop extends ALoop { +public class CxxLoop> extends ALoop { - private static final Lazy, ALoopKindEnum>> LOOP_TYPE = new ThreadSafeLazy<>( + private static final Lazy, LoopKind>> LOOP_TYPE = new ThreadSafeLazy<>( () -> buildLoopTypeMap()); - private static Map, ALoopKindEnum> buildLoopTypeMap() { - HashMap, ALoopKindEnum> loopTypes = new HashMap<>(); + private static Map, LoopKind> buildLoopTypeMap() { + HashMap, LoopKind> loopTypes = new HashMap<>(); - loopTypes.put(ForStmt.class, ALoopKindEnum.FOR); - loopTypes.put(WhileStmt.class, ALoopKindEnum.WHILE); - loopTypes.put(DoStmt.class, ALoopKindEnum.DOWHILE); - loopTypes.put(CXXForRangeStmt.class, ALoopKindEnum.FOREACH); + loopTypes.put(ForStmt.class, LoopKind.FOR); + loopTypes.put(WhileStmt.class, LoopKind.WHILE); + loopTypes.put(DoStmt.class, LoopKind.DOWHILE); + loopTypes.put(CXXForRangeStmt.class, LoopKind.FOREACH); return loopTypes; } @@ -54,28 +71,29 @@ private static Map, ALoopKindEnum> buildLoopTypeMap() private static final Set VALID_RELATION_OP_SETTER = EnumSet.of(BinaryOperatorKind.GT, BinaryOperatorKind.GE, BinaryOperatorKind.LT, BinaryOperatorKind.LE); - private final LoopStmt loop; - public CxxLoop(LoopStmt loop, CxxWeaver weaver) { - super(new CxxStatement(loop, weaver), weaver); + super(loop, weaver); + } - this.loop = loop; + @Override + public LoopStmt getNodeImpl() { + return (LoopStmt) super.getNodeImpl(); } @Override - public String getKindImpl() { - ALoopKindEnum loopType = LOOP_TYPE.get().get(loop.getClass()); + public LoopKind getKindImpl() { + LoopKind loopType = LOOP_TYPE.get().get(this.getNodeImpl().getClass()); Objects.requireNonNull(loopType, - () -> "Could not determine type of node '" + loop.getClass().getSimpleName() + "'"); + () -> "Could not determine type of node '" + this.getNodeImpl().getClass().getSimpleName() + "'"); - return loopType.name().toLowerCase(); + return loopType; } @Override - public Boolean getIsInnermostImpl() { + public boolean getIsInnermostImpl() { // Loop is innermost if none of its descendants is a loop - Optional anotherLoop = loop.getDescendantsStream() + Optional anotherLoop = this.getNodeImpl().getDescendantsStream() .filter(node -> node instanceof LoopStmt) .findFirst(); @@ -83,9 +101,9 @@ public Boolean getIsInnermostImpl() { } @Override - public Boolean getIsOutermostImpl() { + public boolean getIsOutermostImpl() { // Loop is outermost if none of its ancestors is a loop - Optional anotherLoop = loop.getAscendantsStream() + Optional anotherLoop = this.getNodeImpl().getAscendantsStream() .filter(node -> node instanceof LoopStmt) .findFirst(); @@ -93,9 +111,9 @@ public Boolean getIsOutermostImpl() { } @Override - public Integer getNestedLevelImpl() { + public int getNestedLevelImpl() { // Go back and count how many Loops there are - long parentLoops = loop.getAscendantsStream() + long parentLoops = this.getNodeImpl().getAscendantsStream() .filter(node -> node instanceof LoopStmt) .count(); @@ -103,10 +121,10 @@ public Integer getNestedLevelImpl() { } @Override - public AVarref getControlVarrefImpl() { + public AVarref getControlVarrefImpl() { // Only supported for loops of type 'for' - if (!(loop instanceof ForStmt forStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt forStmt)) { return null; } @@ -114,7 +132,7 @@ public AVarref getControlVarrefImpl() { if (controlVars.isEmpty()) { - ClavaLog.info("Could not find control variable for loop in location: " + loop.getLocation()); + ClavaLog.info("Could not find control variable for loop in location: " + this.getNodeImpl().getLocation()); return null; } @@ -122,7 +140,7 @@ public AVarref getControlVarrefImpl() { if (controlVars.size() > 1) { ClavaLog.info("Found more than one control variable (" + controlVars + ") for loop in location: " - + loop.getLocation()); + + this.getNodeImpl().getLocation()); } return CxxJoinpoints.create(controlVars.get(0), getWeaverEngine(), AVarref.class); @@ -142,8 +160,8 @@ public String getControlVarImpl() { } @Override - public AStatement getCondImpl() { - ClavaNode condition = loop.getStmtCondition().orElse(null); + public AStatement getCondImpl() { + ClavaNode condition = this.getNodeImpl().getStmtCondition().orElse(null); if (condition == null) { return null; @@ -153,12 +171,12 @@ public AStatement getCondImpl() { } @Override - public AStatement getStepImpl() { - if (!(loop instanceof ForStmt)) { + public AStatement getStepImpl() { + if (!(this.getNodeImpl() instanceof ForStmt)) { return null; } - Stmt inc = ((ForStmt) loop).getInc().orElse(null); + Stmt inc = ((ForStmt) this.getNodeImpl()).getInc().orElse(null); if (inc == null) { return null; @@ -168,138 +186,123 @@ public AStatement getStepImpl() { } @Override - public LoopStmt getNode() { - return loop; - } - - @Override - public int[] getRankArrayImpl() { - var rank = loop.getRank(); + public int[] getRankImpl() { + var rank = this.getNodeImpl().getRank(); return rank.stream().mapToInt(Integer::intValue).toArray(); } @Override - public Boolean getIsParallelImpl() { - return loop.isParallel(); + public boolean getIsParallelImpl() { + return this.getNodeImpl().isParallel(); } @Override public Integer getIterationsImpl() { - return loop.getIterations(); + return this.getNodeImpl().getIterations(); } @Override - public void setKindImpl(String kind) { - ALoopKindEnum loopKind = SpecsEnums.valueOf(ALoopKindEnum.class, kind.toUpperCase()); - - if (loopKind == null) { + public void setKindImpl(LoopKind kind) { + if (kind == null) { ClavaLog.warning("Unsupported loop kind:" + kind); return; } - switch (loopKind) { + switch (kind) { case WHILE: convertToWhile(); break; default: - throw new RuntimeException("Not implemented: " + loopKind); + throw new RuntimeException("Not implemented: " + kind); } } private void convertToWhile() { - if (loop instanceof WhileStmt) { + if (this.getNodeImpl() instanceof WhileStmt) { return; } - if (loop instanceof ForStmt) { - - // WhileStmt whileStmt = ClavaNodeFactory.whileStmt(loop.getInfo(), ((ForStmt) loop).getCond().orElse(null), - // loop.getBody().orElse(null)); - - // WhileStmt whileStmt = ClavaNodeFactory.whileStmt(loop.getInfo(), ((ForStmt) loop).getCond().orElse(null), - // loop.getBody()); - Stmt cond = ((ForStmt) loop).getCond().orElse(getWeaverEngine().getFactory().nullStmt()); - WhileStmt whileStmt = getWeaverEngine().getFactory().whileStmt(cond, loop.getBody()); + if (this.getNodeImpl() instanceof ForStmt) { + Stmt cond = ((ForStmt) this.getNodeImpl()).getCond().orElse(getWeaverEngine().getFactory().nullStmt()); + WhileStmt whileStmt = getWeaverEngine().getFactory().whileStmt(cond, this.getNodeImpl().getBody()); replaceWith(CxxJoinpoints.create(whileStmt, getWeaverEngine())); - return; } - throw new RuntimeException("Case not implemented:" + loop.getClass()); - + throw new RuntimeException("Case not implemented:" + this.getNodeImpl().getClass()); } @Override public void setInitImpl(String initCode) { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { return; // TODO: warn user? } var suffix = initCode.strip().endsWith(";") ? "" : ";"; LiteralStmt literalStmt = getFactory().literalStmt(initCode + suffix); - ((ForStmt) loop).setInit(literalStmt); + ((ForStmt) this.getNodeImpl()).setInit(literalStmt); } @Override public void setInitValueImpl(String initCode) { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { return; // TODO: warn user? } Type intType = getWeaverEngine().getFactory().builtinType(BuiltinKind.Int); - ((ForStmt) loop).setInitValue(getWeaverEngine().getFactory().literalExpr(initCode, intType)); + ((ForStmt) this.getNodeImpl()).setInitValue(getWeaverEngine().getFactory().literalExpr(initCode, intType)); } @Override public void setEndValueImpl(String value) { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { return; // TODO: warn user? } Type intType = getWeaverEngine().getFactory().builtinType(BuiltinKind.Int); - ((ForStmt) loop).setConditionValue(getWeaverEngine().getFactory().literalExpr(value, intType)); + ((ForStmt) this.getNodeImpl()).setConditionValue(getWeaverEngine().getFactory().literalExpr(value, intType)); } @Override public void setCondImpl(String condCode) { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { return; // TODO: warn user? } var suffix = condCode.strip().endsWith(";") ? "" : ";"; LiteralStmt literalStmt = getFactory().literalStmt(condCode + suffix); - ((ForStmt) loop).setCond(literalStmt); + ((ForStmt) this.getNodeImpl()).setCond(literalStmt); } @Override public void setStepImpl(String stepCode) { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { return; // TODO: warn user? } LiteralStmt literalStmt = getFactory().literalStmt(stepCode); - ((ForStmt) loop).setInc(literalStmt); + ((ForStmt) this.getNodeImpl()).setInc(literalStmt); } @Override public String getInitValueImpl() { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { ClavaLog.info( "$loop.initValue: Not supported for loops of kind '" + getKindImpl() + "', only 'for' loops."); return null; } - String initValue = ((ForStmt) loop).getInitValueExpr() + String initValue = ((ForStmt) this.getNodeImpl()).getInitValueExpr() .map(ClavaNode::getCode) .orElse(null); @@ -309,64 +312,21 @@ public String getInitValueImpl() { } return initValue; - /* - Optional initOpt = ((ForStmt) loop).getInit(); - - if (initOpt.isPresent()) { - - Stmt init = initOpt.get(); - - ClavaNode child = init.getChild(0); - - if (child instanceof VarDecl) { - - VarDecl decl = (VarDecl) child; - - Optional declInitOpt = decl.getInit(); - if (declInitOpt.isPresent()) { - - return declInitOpt.get().getCode(); - } - } else if (child instanceof BinaryOperator) { - - BinaryOperator binOp = (BinaryOperator) child; - if (binOp.getOp() == BinaryOperatorKind.ASSIGN) { - - return binOp.getRhs().getCode(); - } - } - } - - ClavaLog.warning( - "Could not determine the initial value of the loop. The init statement should be a variable declaration with initialization or assignment."); - return null; - */ } @Override public String getEndValueImpl() { - // Set ops = new HashSet<>(); - // ops.add(BinaryOperatorKind.LE); - // ops.add(BinaryOperatorKind.LT); - // ops.add(BinaryOperatorKind.GE); - // ops.add(BinaryOperatorKind.GT); - // ops.add(BinaryOperatorKind.NE); - - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { ClavaLog.info("Not supported for loops of kind '" + getKindImpl() + "', only 'for' loops (" + getLocationImpl() + ")."); return null; } - ForStmt forLoop = (ForStmt) loop; + ForStmt forLoop = (ForStmt) this.getNodeImpl(); String endValue = forLoop.getConditionValueExpr() .map(ClavaNode::getCode) .orElse(null); - // String endValue = forLoop.getCondOperator() - // .filter(binOp -> ops.contains(binOp.getOp())) - // .map(binOp -> binOp.getRhs().getCode()) - // .orElse(null); if (endValue == null) { ClavaLog.debug( @@ -379,27 +339,33 @@ public String getEndValueImpl() { } @Override - public String getCondRelationImpl() { + public Relation getCondRelationImpl() { BinaryOperator condOp = getConditionOp(); if (condOp == null) { return null; } - // Relation requires lowercase names - var opName = condOp.getOp().name().toLowerCase(); - - var relation = Relation.getHelper().fromNameTry(opName).map(Relation::getString).orElse(null); + // Relation enum constants use the same uppercase names as BinaryOperatorKind + var opName = condOp.getOp().name(); - if (relation == null) { - ClavaLog.warning("Could not map operation with name '" + opName + "' to a Relation. Supported names: " + Relation.getHelper().names()); + // Get Relation with the same name as the operator + Relation relation = null; + try { + relation = Relation.valueOf(opName); + } catch (IllegalArgumentException e) { + var supportedNames = Arrays.stream(Relation.values()) + .map(Relation::name) + .collect(Collectors.joining(", ")); + ClavaLog.warning("Could not map operation with name '" + opName + + "' to a Relation. Supported names: " + supportedNames); } return relation; } @Override - public Boolean getHasCondRelationImpl() { + public boolean getHasCondRelationImpl() { return getConditionOp(false) != null; } @@ -408,7 +374,7 @@ private BinaryOperator getConditionOp() { } private BinaryOperator getConditionOp(boolean showWarnings) { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { if (showWarnings) { ClavaLog.info( "Not supported for loops of kind '" + getKindImpl() + "', only 'for' loops."); @@ -417,7 +383,7 @@ private BinaryOperator getConditionOp(boolean showWarnings) { return null; } - ForStmt forLoop = (ForStmt) loop; + ForStmt forLoop = (ForStmt) this.getNodeImpl(); BinaryOperator binOp = forLoop.getCondOperator().orElse(null); if (binOp == null) { @@ -434,8 +400,8 @@ private BinaryOperator getConditionOp(boolean showWarnings) { } @Override - public void setCondRelationImpl(String operator) { - BinaryOperatorKind kind = BinaryOperatorKind.getHelper().fromValueTry(operator).orElse(null); + public void setCondRelationImpl(Relation operator) { + BinaryOperatorKind kind = BinaryOperatorKind.getHelper().fromValueTry(operator.toString()).orElse(null); if (kind == null) { ClavaLog.info("def 'condRelation': Invalid binary operator " + operator); @@ -458,13 +424,13 @@ public void setCondRelationImpl(String operator) { @Override public String getIdImpl() { - return loop.getLoopId(); + return this.getNodeImpl().getLoopId(); } @Override - public void interchangeImpl(ALoop otherLoop) { + public void interchangeImpl(ALoop otherLoop) { - Optional loopInterchange = LoopInterchange.newInstance(loop, (LoopStmt) otherLoop.getNode()); + Optional loopInterchange = LoopInterchange.newInstance(this.getNodeImpl(), (LoopStmt) otherLoop.getNodeImpl()); if (!loopInterchange.isPresent()) { ClavaLog.info("Could not interchange loops"); return; @@ -474,20 +440,20 @@ public void interchangeImpl(ALoop otherLoop) { } @Override - public Boolean isInterchangeableImpl(ALoop otherLoop) { - return LoopInterchange.test(loop, (LoopStmt) otherLoop.getNode()); + public boolean getIsInterchangeableImpl(ALoop otherLoop) { + return LoopInterchange.test(this.getNodeImpl(), (LoopStmt) otherLoop.getNodeImpl()); } @Override - public AStatement tileImpl(String blockSize, AStatement reference, Boolean useTernary) { + public AStatement tileImpl(String blockSize, AStatement reference, boolean useTernary) { LoopTiling loopTiling = new LoopTiling(getWeaverEngine().getContex()); - boolean success = loopTiling.apply(loop, (Stmt) reference.getNode(), + boolean success = loopTiling.apply(this.getNodeImpl(), (Stmt) reference.getNodeImpl(), blockSize.toString(), useTernary); if (!success) { - ClavaLog.info("Could not tile the loop: " + loop.getLocation()); + ClavaLog.info("Could not tile the loop: " + this.getNodeImpl().getLocation()); } if (loopTiling.getLastReferenceStmt() == null) { @@ -499,19 +465,19 @@ public AStatement tileImpl(String blockSize, AStatement reference, Boolean useTe } @Override - public void setIsParallelImpl(Boolean isParallel) { - loop.setParallel(isParallel); + public void setIsParallelImpl(boolean isParallel) { + this.getNodeImpl().setParallel(isParallel); } @Override - public AExpression getIterationsExprImpl() { - if (!(loop instanceof ForStmt)) { + public AExpression getIterationsExprImpl() { + if (!(this.getNodeImpl() instanceof ForStmt)) { ClavaLog.warning( "Not supported for loops of kind '" + getKindImpl() + "', only 'for' loops."); return null; } - return ((ForStmt) loop).getIterationsExpr() + return ((ForStmt) this.getNodeImpl()).getIterationsExpr() .map(expr -> CxxJoinpoints.create(expr, getWeaverEngine(), AExpression.class)) .orElse(null); @@ -519,13 +485,13 @@ public AExpression getIterationsExprImpl() { @Override public String getStepValueImpl() { - if (!(loop instanceof ForStmt)) { + if (!(this.getNodeImpl() instanceof ForStmt)) { ClavaLog.warning( "Not supported for loops of kind '" + getKindImpl() + "', only 'for' loops."); return null; } - String stepValue = ((ForStmt) loop).getStepValueExpr() + String stepValue = ((ForStmt) this.getNodeImpl()).getStepValueExpr() .map(ClavaNode::getCode) .orElse(null); @@ -538,18 +504,18 @@ public String getStepValueImpl() { } @Override - public AStatement getInitImpl() { + public AStatement getInitImpl() { - if (loop instanceof ForStmt) { - return ((ForStmt) loop).getInit() + if (this.getNodeImpl() instanceof ForStmt) { + return ((ForStmt) this.getNodeImpl()).getInit() .map(init -> CxxJoinpoints.create(init, getWeaverEngine(), AStatement.class)) .orElse(null); } // If range stmt, return begin - if (loop instanceof CXXForRangeStmt) { - return ((CXXForRangeStmt) loop).getBegin() + if (this.getNodeImpl() instanceof CXXForRangeStmt) { + return ((CXXForRangeStmt) this.getNodeImpl()).getBegin() .map(init -> CxxJoinpoints.create(init, getWeaverEngine(), AStatement.class)) .orElse(null); @@ -560,13 +526,13 @@ public AStatement getInitImpl() { } @Override - public AScope getBodyImpl() { - return CxxJoinpoints.create(loop.getBody(), getWeaverEngine(), AScope.class); + public AScope getBodyImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getBody(), getWeaverEngine(), AScope.class); } @Override - public void setBodyImpl(AScope body) { - loop.setBody((CompoundStmt) body.getNode()); + public void setBodyImpl(AScope body) { + this.getNodeImpl().setBody((CompoundStmt) body.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMarker.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMarker.java index f7e174bbcb..b50a7ee945 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMarker.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMarker.java @@ -13,11 +13,8 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import java.util.List; - import com.google.common.base.Preconditions; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.lara.LaraMarkerPragma; import pt.up.fe.specs.clava.ast.stmt.CompoundStmt; import pt.up.fe.specs.clava.weaver.CxxSelects; @@ -26,35 +23,32 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AScope; import pt.up.fe.specs.util.SpecsCollections; -public class CxxMarker extends AMarker { - - private final LaraMarkerPragma marker; +public class CxxMarker> extends AMarker { public CxxMarker(LaraMarkerPragma marker, CxxWeaver weaver) { - super(new CxxPragma(marker, weaver), weaver); - this.marker = marker; + super(marker, weaver); } @Override - public ClavaNode getNode() { - return marker; + public LaraMarkerPragma getNodeImpl() { + return (LaraMarkerPragma) super.getNodeImpl(); } @Override public String getIdImpl() { - return marker.getMarkerId(); + return this.getNodeImpl().getMarkerId(); } @Override - public AScope getContentsImpl() { - List result = CxxSelects.select(getWeaverEngine(), AScope.class, SpecsCollections.toList(marker.getTarget()), + public AScope getContentsImpl() { + AScope[] result = CxxSelects.select(getWeaverEngine(), AScope.class, SpecsCollections.toList(this.getNodeImpl().getTarget()), false, node -> node instanceof CompoundStmt && ((CompoundStmt) node).isNestedScope()); - Preconditions.checkArgument(!result.isEmpty(), - "Could not find the 'scope' associated with the marker '" + marker.getCode() + "'. Pragma target is: " - + marker.getTarget()); - Preconditions.checkArgument(result.size() == 1, "Expected just one scope, but found more than one"); + Preconditions.checkArgument(result.length > 0, + "Could not find the 'scope' associated with the marker '" + this.getNodeImpl().getCode() + "'. Pragma target is: " + + this.getNodeImpl().getTarget()); + Preconditions.checkArgument(result.length == 1, "Expected just one scope, but found more than one"); - return result.get(0); + return result[0]; } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberAccess.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberAccess.java index dbf8e4e0ef..956add16d9 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberAccess.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberAccess.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaNodes; import pt.up.fe.specs.clava.ast.expr.MemberExpr; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -22,55 +21,52 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AMemberAccess; -public class CxxMemberAccess extends AMemberAccess { - - private final MemberExpr memberExpr; +public class CxxMemberAccess> extends AMemberAccess { public CxxMemberAccess(MemberExpr memberExpr, CxxWeaver weaver) { - super(new CxxExpression(memberExpr, weaver), weaver); - this.memberExpr = memberExpr; + super(memberExpr, weaver); } @Override - public ClavaNode getNode() { - return memberExpr; + public MemberExpr getNodeImpl() { + return (MemberExpr) super.getNodeImpl(); } @Override - public AExpression getBaseImpl() { - return CxxJoinpoints.create(ClavaNodes.normalize(memberExpr.getBase()), getWeaverEngine(), AExpression.class); + public AExpression getBaseImpl() { + return CxxJoinpoints.create(ClavaNodes.normalize(this.getNodeImpl().getBase()), getWeaverEngine(), AExpression.class); } @Override public String getNameImpl() { - return memberExpr.getMemberName(); + return this.getNodeImpl().getMemberName(); } @Override - public AExpression[] getMemberChainArrayImpl() { - return memberExpr.getExprChain().stream() + public AExpression[] getMemberChainImpl() { + return this.getNodeImpl().getExprChain().stream() .map(member -> CxxJoinpoints.create(member, getWeaverEngine(), AExpression.class)) .toArray(size -> new AExpression[size]); } @Override - public String[] getMemberChainNamesArrayImpl() { - return memberExpr.getChain().toArray(new String[0]); + public String[] getMemberChainNamesImpl() { + return this.getNodeImpl().getChain().toArray(new String[0]); } @Override - public ADecl getDeclImpl() { - return CxxJoinpoints.create(memberExpr.get(MemberExpr.MEMBER_DECL), getWeaverEngine(), ADecl.class); + public ADecl getDeclImpl() { + return CxxJoinpoints.create(this.getNodeImpl().get(MemberExpr.MEMBER_DECL), getWeaverEngine(), ADecl.class); } @Override - public Boolean getArrowImpl() { - return memberExpr.get(MemberExpr.IS_ARROW); + public boolean getArrowImpl() { + return this.getNodeImpl().get(MemberExpr.IS_ARROW); } @Override - public void setArrowImpl(Boolean isArrow) { - memberExpr.set(MemberExpr.IS_ARROW, isArrow); + public void setArrowImpl(boolean isArrow) { + this.getNodeImpl().set(MemberExpr.IS_ARROW, isArrow); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberCall.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberCall.java index b556a97b20..c23ce52bd0 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberCall.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMemberCall.java @@ -19,28 +19,24 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AMemberCall; -public class CxxMemberCall extends AMemberCall { - - private final CXXMemberCallExpr memberCall; +public class CxxMemberCall> extends AMemberCall { public CxxMemberCall(CXXMemberCallExpr memberCall, CxxWeaver weaver) { - super(new CxxCall(memberCall, weaver), weaver); - - this.memberCall = memberCall; + super(memberCall, weaver); } @Override - public CXXMemberCallExpr getNode() { - return memberCall; + public CXXMemberCallExpr getNodeImpl() { + return (CXXMemberCallExpr) super.getNodeImpl(); } @Override - public AExpression getBaseImpl() { - return CxxJoinpoints.create(memberCall.getBase(), getWeaverEngine(), AExpression.class); + public AExpression getBaseImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getBase(), getWeaverEngine(), AExpression.class); } @Override - public AExpression getRootBaseImpl() { - return CxxJoinpoints.create(memberCall.getRootBase(), getWeaverEngine(), AExpression.class); + public AExpression getRootBaseImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getRootBase(), getWeaverEngine(), AExpression.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMethod.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMethod.java index b3a5c3036d..9a64bef274 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMethod.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxMethod.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.CXXMethodDecl; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; @@ -21,29 +20,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AMethod; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxMethod extends AMethod { - - private final CXXMethodDecl method; +public class CxxMethod> extends AMethod { public CxxMethod(CXXMethodDecl method, CxxWeaver weaver) { - super(new CxxFunction(method, weaver), weaver); - - this.method = method; + super(method, weaver); } @Override - public ClavaNode getNode() { - return method; + public CXXMethodDecl getNodeImpl() { + return (CXXMethodDecl) super.getNodeImpl(); } @Override - public AClass getRecordImpl() { - return method.getRecordDecl().map(record -> CxxJoinpoints.create(record, getWeaverEngine(), AClass.class)).orElse(null); + public AClass getRecordImpl() { + return this.getNodeImpl().getRecordDecl().map(record -> CxxJoinpoints.create(record, getWeaverEngine(), AClass.class)).orElse(null); } @Override public void removeRecordImpl() { - method.removeRecord(); + this.getNodeImpl().removeRecord(); } /** @@ -51,24 +46,13 @@ public void removeRecordImpl() { * this is not required */ @Override - public AType getTypeImpl() { - return CxxJoinpoints.create(method.getReturnType(), getWeaverEngine(), AType.class); + public AType getTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getReturnType(), getWeaverEngine(), AType.class); } @Override - public Boolean getIsVirtualImpl() { - return method.get(CXXMethodDecl.IS_VIRTUAL); + public boolean getIsVirtualImpl() { + return this.getNodeImpl().get(CXXMethodDecl.IS_VIRTUAL); } - /* - @Override - public void defRecordImpl(AClass value) { - method.set(CXXMethodDecl.RECORD, (CXXRecordDecl) value.getNode()); - } - - @Override - public void setRecordImpl(AClass classJp) { - defRecordImpl(classJp); - } - */ } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNamedDecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNamedDecl.java index 724f80e685..fd9df7258a 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNamedDecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNamedDecl.java @@ -24,31 +24,27 @@ import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ANamedDecl; -public class CxxNamedDecl extends ANamedDecl { - - private final NamedDecl namedDecl; +public class CxxNamedDecl> extends ANamedDecl { public CxxNamedDecl(NamedDecl namedDecl, CxxWeaver weaver) { - super(new CxxDecl(namedDecl, weaver), weaver); - - this.namedDecl = namedDecl; + super(namedDecl, weaver); } @Override - public ClavaNode getNode() { - return namedDecl; + public NamedDecl getNodeImpl() { + return (NamedDecl) super.getNodeImpl(); } @Override public String getNameImpl() { - return namedDecl.hasDeclName() ? namedDecl.getDeclName() : null; + return this.getNodeImpl().hasDeclName() ? this.getNodeImpl().getDeclName() : null; } @Override - public Boolean getIsPublicImpl() { + public boolean getIsPublicImpl() { // Search for the first AccessSpecDecl that appears before this node - int declIndex = namedDecl.indexOfSelf(); - List siblings = namedDecl.getParent().getChildren(); + int declIndex = this.getNodeImpl().indexOfSelf(); + List siblings = this.getNodeImpl().getParent().getChildren(); for (int i = declIndex - 1; i >= 0; i--) { if (siblings.get(i) instanceof AccessSpecDecl) { @@ -56,7 +52,7 @@ public Boolean getIsPublicImpl() { } } - boolean isInsideClass = namedDecl.getAncestorTry(RecordDecl.class) + boolean isInsideClass = this.getNodeImpl().getAncestorTry(RecordDecl.class) .map(recordDecl -> recordDecl.get(RecordDecl.TAG_KIND) == TagKind.CLASS) .orElse(false); @@ -66,27 +62,27 @@ public Boolean getIsPublicImpl() { @Override public void setNameImpl(String name) { - namedDecl.set(NamedDecl.DECL_NAME, name); + this.getNodeImpl().set(NamedDecl.DECL_NAME, name); } @Override public String getQualifiedPrefixImpl() { - return namedDecl.get(NamedDecl.QUALIFIED_PREFIX); + return this.getNodeImpl().get(NamedDecl.QUALIFIED_PREFIX); } @Override public String getQualifiedNameImpl() { - return namedDecl.getFullyQualifiedName(); + return this.getNodeImpl().getFullyQualifiedName(); } @Override public void setQualifiedPrefixImpl(String qualifiedPrefix) { - namedDecl.set(NamedDecl.QUALIFIED_PREFIX, qualifiedPrefix); + this.getNodeImpl().set(NamedDecl.QUALIFIED_PREFIX, qualifiedPrefix); } @Override public void setQualifiedNameImpl(String name) { - namedDecl.setQualifiedName(name); + this.getNodeImpl().setQualifiedName(name); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNewExpr.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNewExpr.java index 90297ec113..ca2dad2115 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNewExpr.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxNewExpr.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.CXXNewExpr; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ANewExpr; -public class CxxNewExpr extends ANewExpr { - - private final CXXNewExpr newExpr; +public class CxxNewExpr> extends ANewExpr { public CxxNewExpr(CXXNewExpr newExpr, CxxWeaver weaver) { - super(new CxxExpression(newExpr, weaver), weaver); - this.newExpr = newExpr; + super(newExpr, weaver); } @Override - public ClavaNode getNode() { - return newExpr; + public CXXNewExpr getNodeImpl() { + return (CXXNewExpr) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java index d82f517f06..fcdc5dcda4 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java @@ -16,7 +16,6 @@ import java.util.Arrays; import java.util.List; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.omp.OmpDirectiveKind; import pt.up.fe.specs.clava.ast.omp.OmpPragma; import pt.up.fe.specs.clava.ast.omp.clauses.OmpClauseKind; @@ -31,70 +30,53 @@ import pt.up.fe.specs.util.SpecsCollections; import pt.up.fe.specs.util.treenode.NodeInsertUtils; -public class CxxOmp extends AOmp { - - private OmpPragma ompPragma; +public class CxxOmp> extends AOmp { public CxxOmp(OmpPragma ompPragma, CxxWeaver weaver) { - super(new CxxPragma(ompPragma, weaver), weaver); - - this.ompPragma = ompPragma; + super(ompPragma, weaver); } @Override - public ClavaNode getNode() { - return ompPragma; + public OmpPragma getNodeImpl() { + return (OmpPragma) super.getNodeImpl(); } @Override public String getKindImpl() { - return ompPragma.getDirectiveKind().getString(); + return this.getNodeImpl().getDirectiveKind().getString(); } @Override public String getNumThreadsImpl() { - return ompPragma.clauses().getNumThreads().orElse(null); + return this.getNodeImpl().clauses().getNumThreads().orElse(null); } @Override public String getProcBindImpl() { - return ompPragma.clauses().getProcBind() + return this.getNodeImpl().clauses().getProcBind() .map(ProcBindKind::getKey) .orElse(null); } @Override - public Boolean hasClauseImpl(String clauseName) { + public boolean getHasClauseImpl(String clauseName) { OmpClauseKind clauseKind = parseClauseName(clauseName); - // if (clauseKind == null) { - // return false; - // } - - return ompPragma.hasClause(clauseKind); + return this.getNodeImpl().hasClause(clauseKind); } private OmpClauseKind parseClauseName(String clauseName) { return OmpClauseKind.getHelper().fromValue(clauseName); - // OmpClauseKind clauseKind = OmpClauseKind.getHelper().valueOfTry(clauseName).orElse(null); - // if (clauseKind == null) { - // - // } - // return clauseKind; } @Override - public Boolean isClauseLegalImpl(String clauseName) { + public boolean getIsClauseLegalImpl(String clauseName) { OmpClauseKind clauseKind = parseClauseName(clauseName); - // if (clauseKind == null) { - // return false; - // } - - return ompPragma.getDirectiveKind().isClauseLegal(clauseKind); + return this.getNodeImpl().getDirectiveKind().isClauseLegal(clauseKind); } @Override public void setNumThreadsImpl(String newExpr) { - ompPragma.clauses().setNumThreads(newExpr); + this.getNodeImpl().clauses().setNumThreads(newExpr); } @Override @@ -102,67 +84,44 @@ public void setProcBindImpl(String newBind) { ProcBindKind kind = ProcBindKind.getHelper().fromValueTry(newBind) .orElseThrow(() -> new RuntimeException("Can't set '" + newBind + "' as a proc bind value, valid values: " + ProcBindKind.getHelper().getAvailableValues())); - ompPragma.clauses().setProcBind(kind); - // ProcBindKind kind = ProcBindKind.getHelper().valueOfTry(newBind).orElse(null); - // if (kind == null) { - // ClavaLog.info("Can't set '" + newBind + "' as a proc bind value, valid values: " - // + ProcBindKind.getHelper().getAvailableOptions()); - // return; - // } - - // setClause(new OmpProcBindClause(kind)); - + this.getNodeImpl().clauses().setProcBind(kind); } - // private void setClause(OmpClause clause) { - // ompPragma.setClause(clause); - // } - @Override - public String[] getPrivateArrayImpl() { - return ompPragma.clauses().getPrivate().toArray(new String[0]); + public String[] getPrivateImpl() { + return this.getNodeImpl().clauses().getPrivate().toArray(new String[0]); } @Override public void setPrivateImpl(String[] newVariables) { - ompPragma.clauses().setPrivate(Arrays.asList(newVariables)); + this.getNodeImpl().clauses().setPrivate(Arrays.asList(newVariables)); } @Override - public String[] getClauseKindsArrayImpl() { - return SpecsCollections.toStringArray(ompPragma.getClauseKinds()); - - // return ompPragma.getClauseKinds().stream() - // .map(OmpClauseKind::getKey) - // .collect(Collectors.toList()) - // .toArray(new String[0]); + public String[] getClauseKindsImpl() { + return SpecsCollections.toStringArray(this.getNodeImpl().getClauseKinds()); } @Override - public String[] getReductionArrayImpl(String kind) { - return ompPragma.clauses().getReduction(kind).toArray(new String[0]); + public String[] getGetReductionImpl(String kind) { + return this.getNodeImpl().clauses().getReduction(kind).toArray(new String[0]); } @Override public void setReductionImpl(String reductionKindString, String[] newVariables) { ReductionKind reductionKind = ReductionKind.getHelper().fromValue(reductionKindString.toLowerCase()); - ompPragma.clauses().setReduction(reductionKind, Arrays.asList(newVariables)); + this.getNodeImpl().clauses().setReduction(reductionKind, Arrays.asList(newVariables)); } @Override - public String[] getReductionKindsArrayImpl() { - return SpecsCollections.toStringArray(ompPragma.clauses().getReductionKinds()); - // String[] a = SpecsCollections.toStringArray(ompPragma.clauses().getReductionKinds()); - // return ompPragma.clauses().getReductionKinds().stream() - // .map(ReductionKind::getKey) - // .collect(Collectors.toList()) - // .toArray(new String[0]); + public String[] getReductionKindsImpl() { + return SpecsCollections.toStringArray(this.getNodeImpl().clauses().getReductionKinds()); } @Override public String getDefaultImpl() { - return ompPragma.clauses().getDefault() + return this.getNodeImpl().clauses().getDefault() .map(DefaultKind::getKey) .orElse(null); } @@ -172,52 +131,52 @@ public void setDefaultImpl(String newDefault) { DefaultKind kind = DefaultKind.getHelper().fromValueTry(newDefault) .orElseThrow(() -> new RuntimeException("Can't set '" + newDefault + "' as a 'default' value, valid values: " + DefaultKind.getHelper().getAvailableValues())); - ompPragma.clauses().setDefault(kind); + this.getNodeImpl().clauses().setDefault(kind); } @Override - public String[] getFirstprivateArrayImpl() { - return ompPragma.clauses().getFirstprivate().toArray(new String[0]); + public String[] getFirstprivateImpl() { + return this.getNodeImpl().clauses().getFirstprivate().toArray(new String[0]); } @Override public void setFirstprivateImpl(String[] newVariables) { - ompPragma.clauses().setFirstprivate(Arrays.asList(newVariables)); + this.getNodeImpl().clauses().setFirstprivate(Arrays.asList(newVariables)); } @Override - public String[] getLastprivateArrayImpl() { - return ompPragma.clauses().getLastprivate().toArray(new String[0]); + public String[] getLastprivateImpl() { + return this.getNodeImpl().clauses().getLastprivate().toArray(new String[0]); } @Override public void setLastprivateImpl(String[] newVariables) { - ompPragma.clauses().setLastprivate(Arrays.asList(newVariables)); + this.getNodeImpl().clauses().setLastprivate(Arrays.asList(newVariables)); } @Override - public String[] getSharedArrayImpl() { - return ompPragma.clauses().getShared().toArray(new String[0]); + public String[] getSharedImpl() { + return this.getNodeImpl().clauses().getShared().toArray(new String[0]); } @Override public void setSharedImpl(String[] newVariables) { - ompPragma.clauses().setShared(Arrays.asList(newVariables)); + this.getNodeImpl().clauses().setShared(Arrays.asList(newVariables)); } @Override - public String[] getCopyinArrayImpl() { - return ompPragma.clauses().getCopyin().toArray(new String[0]); + public String[] getCopyinImpl() { + return this.getNodeImpl().clauses().getCopyin().toArray(new String[0]); } @Override public void setCopyinImpl(String[] newVariables) { - ompPragma.clauses().setCopyin(Arrays.asList(newVariables)); + this.getNodeImpl().clauses().setCopyin(Arrays.asList(newVariables)); } @Override public String getScheduleKindImpl() { - return ompPragma.clauses().getScheduleKind().map(ScheduleKind::getKey).orElse(null); + return this.getNodeImpl().clauses().getScheduleKind().map(ScheduleKind::getKey).orElse(null); } @Override @@ -226,17 +185,17 @@ public void setScheduleKindImpl(String scheduleKindString) { .orElseThrow(() -> new RuntimeException("Can't set '" + scheduleKindString + "' as a schedule kind, valid values: " + ScheduleKind.getHelper().getAvailableValues())); - ompPragma.clauses().setScheduleKind(kind); + this.getNodeImpl().clauses().setScheduleKind(kind); } @Override public String getScheduleChunkSizeImpl() { - return ompPragma.clauses().getScheduleChunkSize().orElse(null); + return this.getNodeImpl().clauses().getScheduleChunkSize().orElse(null); } @Override public void setScheduleChunkSizeImpl(String chunkSize) { - ompPragma.clauses().setScheduleChunkSize(chunkSize); + this.getNodeImpl().clauses().setScheduleChunkSize(chunkSize); } @Override @@ -245,24 +204,24 @@ public void setScheduleChunkSizeImpl(int chunkSize) { } @Override - public String[] getScheduleModifiersArrayImpl() { - return SpecsCollections.toStringArray(ompPragma.clauses().getScheduleModifiers()); + public String[] getScheduleModifiersImpl() { + return SpecsCollections.toStringArray(this.getNodeImpl().clauses().getScheduleModifiers()); } @Override public void setScheduleModifiersImpl(String[] modifiers) { List parsedModifiers = ScheduleModifier.getHelper().fromValue(Arrays.asList(modifiers)); - ompPragma.clauses().setScheduleModifiers(parsedModifiers); + this.getNodeImpl().clauses().setScheduleModifiers(parsedModifiers); } @Override public String getCollapseImpl() { - return ompPragma.clauses().getCollapse().orElse(null); + return this.getNodeImpl().clauses().getCollapse().orElse(null); } @Override public void setCollapseImpl(String newExpr) { - ompPragma.clauses().setCollapse(newExpr); + this.getNodeImpl().clauses().setCollapse(newExpr); } @Override @@ -272,13 +231,12 @@ public void setCollapseImpl(int newExpr) { @Override public String getOrderedImpl() { - return ompPragma.clauses().getOrdered().orElse(null); - + return this.getNodeImpl().clauses().getOrdered().orElse(null); } @Override public void setOrderedImpl(String newExpr) { - ompPragma.clauses().setOrdered(newExpr); + this.getNodeImpl().clauses().setOrdered(newExpr); } @Override @@ -288,7 +246,7 @@ public void removeClauseImpl(String clauseKindString) { + "', name is not valid. Valid clause names: " + OmpClauseKind.getHelper().getAvailableValues())); - ompPragma.removeClause(clauseKind); + this.getNodeImpl().removeClause(clauseKind); } @Override @@ -299,10 +257,10 @@ public void setKindImpl(String directiveKindString) { + OmpDirectiveKind.getHelper().getAvailableValues())); // Create new pragma based on the previous pragma - OmpPragma newOmpPragma = OmpParser.newOmpPragma(directiveKind, ompPragma); + OmpPragma newOmpPragma = OmpParser.newOmpPragma(directiveKind, this.getNodeImpl()); // Replace previous pragma - NodeInsertUtils.replace(ompPragma, newOmpPragma); + NodeInsertUtils.replace(this.getNodeImpl(), newOmpPragma); // Update join point pragma this.ompPragma = newOmpPragma; diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOp.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOp.java index df08033873..a3c49cb55b 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOp.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOp.java @@ -13,39 +13,42 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.Operator; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AOp; +import pt.up.fe.specs.clava.weaver.enums.OpKind; -public class CxxOp extends AOp { - - private final Operator op; +public class CxxOp> extends AOp { public CxxOp(Operator op, CxxWeaver weaver) { - super(new CxxExpression(op, weaver), weaver); - - this.op = op; + super(op, weaver); } @Override - public String getKindImpl() { - return op.getKindName(); + public Operator getNodeImpl() { + return (Operator) super.getNodeImpl(); } @Override - public Boolean getIsBitwiseImpl() { - return op.isBitwise(); + public OpKind getKindImpl() { + var op = this.getNodeImpl(); + + try { + return OpKind.fromDisplay(op.getKindName()); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Could not determine operator kind for operator with code '" + op.getOperatorCode() + + "' and kind name '" + op.getKindName() + "'", e); + } } @Override - public ClavaNode getNode() { - return op; + public boolean getIsBitwiseImpl() { + return this.getNodeImpl().isBitwise(); } @Override public String getOperatorImpl() { - return op.getOperatorCode(); + return this.getNodeImpl().getOperatorCode(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParam.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParam.java index 1110c244a9..525518022b 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParam.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParam.java @@ -13,27 +13,23 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.ParmVarDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AParam; -public class CxxParam extends AParam { - - private final ParmVarDecl param; +public class CxxParam> extends AParam { public CxxParam(ParmVarDecl param, CxxWeaver weaver) { - super(new CxxVardecl(param, weaver), weaver); - this.param = param; + super(param, weaver); } @Override - public ClavaNode getNode() { - return param; + public ParmVarDecl getNodeImpl() { + return (ParmVarDecl) super.getNodeImpl(); } @Override - public Boolean getIsParamImpl() { + public boolean getIsParamImpl() { return true; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParenExpr.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParenExpr.java index b459402baa..10d9e99817 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParenExpr.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxParenExpr.java @@ -13,29 +13,25 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.ParenExpr; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AParenExpr; -public class CxxParenExpr extends AParenExpr { - - private final ParenExpr parenExpr; +public class CxxParenExpr> extends AParenExpr { public CxxParenExpr(ParenExpr parenExpr, CxxWeaver weaver) { - super(new CxxExpression(parenExpr, weaver), weaver); - this.parenExpr = parenExpr; + super(parenExpr, weaver); } @Override - public ClavaNode getNode() { - return parenExpr; + public ParenExpr getNodeImpl() { + return (ParenExpr) super.getNodeImpl(); } @Override - public AExpression getSubExprImpl() { - return CxxJoinpoints.create(parenExpr.getSubExpr(), getWeaverEngine(), AExpression.class); + public AExpression getSubExprImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getSubExpr(), getWeaverEngine(), AExpression.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java index 9688d5747a..fe09bcb7a9 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java @@ -12,53 +12,48 @@ */ package pt.up.fe.specs.clava.weaver.joinpoints; - -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.pragma.Pragma; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxSelects; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.APragma; -public class CxxPragma extends APragma { - - private Pragma pragma; +public class CxxPragma> extends APragma { public CxxPragma(Pragma pragma, CxxWeaver weaver) { - super(weaver); - this.pragma = pragma; + super(pragma, weaver); } @Override - public ClavaNode getNode() { - return pragma; + public Pragma getNodeImpl() { + return (Pragma) super.getNodeImpl(); } @Override public String getNameImpl() { - return pragma.getName(); + return this.getNodeImpl().getName(); } @Override - public AJoinPoint getTargetImpl() { - return pragma.getTarget().map(target -> CxxJoinpoints.create(target, - getWeaverEngine(), AJoinPoint.class)).orElse(null); + public AJoinpoint getTargetImpl() { + return this.getNodeImpl().getTarget().map(target -> CxxJoinpoints.create(target, + getWeaverEngine(), AJoinpoint.class)).orElse(null); } @Override public String getContentImpl() { - return pragma.getContent(); + return this.getNodeImpl().getContent(); } @Override public void setContentImpl(String content) { - pragma.setContent(content); + this.getNodeImpl().setContent(content); } @Override public void setNameImpl(String name) { - pragma.setName(name); + this.getNodeImpl().setName(name); } public void setPragma(Pragma pragma) { @@ -66,8 +61,8 @@ public void setPragma(Pragma pragma) { } @Override - public AJoinPoint[] getTargetNodesArrayImpl(String endPragma) { - var pragmaNodes = pragma.getPragmaNodes(endPragma); + public AJoinpoint[] getGetTargetNodesImpl(String endPragma) { + var pragmaNodes = this.getNodeImpl().getPragmaNodes(endPragma); return CxxSelects.selectedNodesToJps(pragmaNodes.stream(), getWeaverEngine()); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java index 980895a175..956b577174 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java @@ -13,7 +13,15 @@ package pt.up.fe.specs.clava.weaver.joinpoints; +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + import org.suikasoft.jOptions.Interfaces.DataStore; + import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaOptions; @@ -26,41 +34,23 @@ import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFile; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFunction; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AProgram; import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; -import java.io.File; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -public class CxxProgram extends AProgram { +public class CxxProgram> extends AProgram { private final String name; - private final App app; - // private final File baseFolder; - - // private final List parserOptions; - - // public CxxProgram(File baseFolder, App app, List parserOptions) { - // this.baseFolder = baseFolder; - // this.app = app; - // this.parserOptions = parserOptions; - // } public CxxProgram(App app, CxxWeaver weaver) { - super(weaver); + super(app, weaver); this.name = weaver.getProgramName(); - this.app = app; } @Override - public App getNode() { - return app; + public App getNodeImpl() { + return (App) super.getNodeImpl(); } @Override @@ -81,19 +71,19 @@ public void rebuildFuzzyImpl() { } @Override - public AJoinPoint addFileImpl(AFile file) { - TranslationUnit tu = (TranslationUnit) file.getNode(); - TranslationUnit trueTu = app.addFile(tu); + public AJoinpoint addFileImpl(AFile file) { + TranslationUnit tu = (TranslationUnit) file.getNodeImpl(); + TranslationUnit trueTu = this.getNodeImpl().addFile(tu); if (tu == trueTu) { return file; } - return new CxxFile(trueTu, getWeaverEngine()); + return new CxxFile<>(trueTu, getWeaverEngine()); } @Override - public String[] getIncludeFoldersArrayImpl() { + public String[] getIncludeFoldersImpl() { Set includeFolders = getWeaverEngine().getIncludeFolders(); return includeFolders.toArray(new String[0]); @@ -110,23 +100,17 @@ public String getStdFlagImpl() { } @Override - public String[] getDefaultFlagsArrayImpl() { + public String[] getDefaultFlagsImpl() { return CxxWeaver.getDefaultFlags().toArray(new String[0]); } @Override - public String[] getUserFlagsArrayImpl() { + public String[] getUserFlagsImpl() { return getWeaverEngine().getUserFlags().toArray(new String[0]); } - // @Override - // public void messageToUserImpl(String message) { - // weaver.addMessageToUser(message); - // } - @Override public String getBaseFolderImpl() { - // ClavaLog.deprecated("attribute baseFolder should not be used, instead use file.sourcePath"); List sources = getWeaverEngine().getSources(); if (sources.isEmpty()) { SpecsLogs.warn("Expected at least program to have one source folder, found none"); @@ -139,12 +123,12 @@ public String getBaseFolderImpl() { } public DataStore getAppData() { - return app.getAppData(); + return this.getNodeImpl().getAppData(); } @Override public String getCodeImpl() { - return app.getCode(); + return this.getNodeImpl().getCode(); } @Override @@ -163,74 +147,74 @@ public String getWeavingFolderImpl() { } @Override - public Boolean getIsCxxImpl() { + public boolean getIsCxxImpl() { return getWeaverEngine().getConfig().get(ClavaOptions.STANDARD).isCxx(); } @Override - public String[] getExtraSourcesArrayImpl() { - return app.getExternalDependencies().getExtraSources().stream() + public String[] getExtraSourcesImpl() { + return this.getNodeImpl().getExternalDependencies().getExtraSources().stream() .map(File::getAbsolutePath) .collect(Collectors.toList()) .toArray(new String[0]); } @Override - public String[] getExtraIncludesArrayImpl() { - return app.getExternalDependencies().getExtraIncludes().stream() + public String[] getExtraIncludesImpl() { + return this.getNodeImpl().getExternalDependencies().getExtraIncludes().stream() .map(File::getAbsolutePath) .collect(Collectors.toList()) .toArray(new String[0]); } @Override - public String[] getExtraProjectsArrayImpl() { - return app.getExternalDependencies().getProjects().stream() + public String[] getExtraProjectsImpl() { + return this.getNodeImpl().getExternalDependencies().getProjects().stream() .map(File::getAbsolutePath) .collect(Collectors.toList()) .toArray(new String[0]); } @Override - public String[] getExtraLibsArrayImpl() { + public String[] getExtraLibsImpl() { - return app.getExternalDependencies().getLibs() + return this.getNodeImpl().getExternalDependencies().getLibs() .toArray(new String[0]); } @Override public void addExtraIncludeImpl(String path) { - app.getExternalDependencies().addInclude(new File(path)); + this.getNodeImpl().getExternalDependencies().addInclude(new File(path)); } @Override public void addExtraIncludeFromGitImpl(String gitRepository, String path) { - app.getExternalDependencies().addIncludeFromGit(gitRepository, path); + this.getNodeImpl().getExternalDependencies().addIncludeFromGit(gitRepository, path); } @Override public void addExtraSourceImpl(String path) { - app.getExternalDependencies().addSource(new File(path)); + this.getNodeImpl().getExternalDependencies().addSource(new File(path)); } @Override public void addExtraSourceFromGitImpl(String gitRepository, String path) { - app.getExternalDependencies().addSourceFromGit(gitRepository, path); + this.getNodeImpl().getExternalDependencies().addSourceFromGit(gitRepository, path); } @Override public void addExtraLibImpl(String lib) { - app.getExternalDependencies().addLib(lib); + this.getNodeImpl().getExternalDependencies().addLib(lib); } @Override public void addProjectFromGitImpl(String gitRepo, String[] libs, String path) { - app.getExternalDependencies().addProjectFromGit(gitRepo, Arrays.asList(libs), path); + this.getNodeImpl().getExternalDependencies().addProjectFromGit(gitRepo, Arrays.asList(libs), path); } @Override - public AJoinPoint addFileFromPathImpl(Object filepath) { + public AJoinpoint addFileFromPathImpl(Object filepath) { File file = getFile(filepath); if (!file.isFile()) { @@ -244,7 +228,7 @@ public AJoinPoint addFileFromPathImpl(Object filepath) { // Create file join point TranslationUnit newTu = getFactory().translationUnit(file, Arrays.asList(code)); - return addFileImpl(new CxxFile(newTu, getWeaverEngine())); + return addFileImpl(new CxxFile<>(newTu, getWeaverEngine())); } private File getFile(Object filepath) { @@ -256,22 +240,18 @@ private File getFile(Object filepath) { } @Override - public AFunction getMainImpl() { - for (TranslationUnit tunit : app.getTranslationUnits()) { + public AFunction getMainImpl() { + for (TranslationUnit tunit : this.getNodeImpl().getTranslationUnits()) { for (ClavaNode child : tunit.getChildren()) { - // ClavaLog.debug("getMain: checking if child is FunctionDecl"); if (!(child instanceof FunctionDecl)) { continue; } FunctionDecl function = (FunctionDecl) child; - // ClavaLog.debug("getMain: checking if function is main"); if (!function.getDeclName().toLowerCase().equals("main")) { continue; } - // ClavaLog.debug("getMain: checking if function '" + function.getDeclName() + "' is definition"); - // Calling isDefinition() can be expensive, specially if there are many functions, // testing name first is faster if (!function.isDefinition()) { @@ -283,26 +263,11 @@ public AFunction getMainImpl() { } return null; - /* - // Find main function - return (AFunction) app.getDescendantsStream() - // get functions - .filter(FunctionDecl.class::isInstance) - .map(FunctionDecl.class::cast) - // only definitions - .filter(FunctionDecl::isDefinition) - // the main function - .filter(fdecl -> fdecl.getDeclName().toLowerCase().equals("main")) - .map(CxxJoinpoints::create) - .findFirst() - .orElse(null); - */ } @Override - public void atexitImpl(AFunction function) { - // ClavaLog.debug("Getting main function"); - AFunction mainFunction = getMainImpl(); + public void atexitImpl(AFunction function) { + AFunction mainFunction = getMainImpl(); if (mainFunction == null) { ClavaLog.info("atexit: main() function not found, could not register function"); @@ -314,26 +279,20 @@ public void atexitImpl(AFunction function) { getFactory().builtinType("void")); // Insert call at the beginning of the main function - // ClavaLog.debug("Inserting atexit call at beginning of main"); mainFunction.getBodyImpl().insertBegin(CxxJoinpoints.create(atexitCall, getWeaverEngine())); // Add include for atexit - // ClavaLog.debug("Getting file ancestor"); - AFile file = (AFile) mainFunction.getAncestorImpl("file"); - Objects.requireNonNull(file, () -> "Expected main function to be inside a file: " + mainFunction.getNode()); - // ClavaLog.debug("Adding stdlib.h include"); + AFile file = (AFile) mainFunction.getGetAncestorImpl("file"); + Objects.requireNonNull(file, () -> "Expected main function to be inside a file: " + mainFunction.getNodeImpl()); file.addInclude("stdlib.h", true); // Add include for function - // ClavaLog.debug("Adding function include"); file.addIncludeJpImpl(function); - - // ClavaLog.debug("Finsished"); } @Override - public AFile[] getFilesArrayImpl() { - return app.getTranslationUnits().stream() + public AFile[] getFilesImpl() { + return this.getNodeImpl().getTranslationUnits().stream() .map(tunit -> CxxJoinpoints.create(tunit, getWeaverEngine(), AFile.class)) .collect(Collectors.toList()).toArray(size -> new AFile[size]); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxRecord.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxRecord.java index 22d03a0693..baf05996a2 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxRecord.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxRecord.java @@ -15,7 +15,6 @@ import java.util.stream.Collectors; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.FieldDecl; import pt.up.fe.specs.clava.ast.decl.RecordDecl; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -24,57 +23,54 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFunction; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ARecord; -public class CxxRecord extends ARecord { - - private final RecordDecl recordDecl; +public class CxxRecord> extends ARecord { public CxxRecord(RecordDecl recordDecl, CxxWeaver weaver) { - super(new CxxNamedDecl(recordDecl, weaver), weaver); - this.recordDecl = recordDecl; + super(recordDecl, weaver); } @Override - public ClavaNode getNode() { - return recordDecl; + public RecordDecl getNodeImpl() { + return (RecordDecl) super.getNodeImpl(); } @Override - public AField[] getFieldsArrayImpl() { - return recordDecl.getFields().stream() + public AField[] getFieldsImpl() { + return this.getNodeImpl().getFields().stream() .map(field -> CxxJoinpoints.create(field, getWeaverEngine(), AField.class)) - .collect(Collectors.toList()).toArray(new AField[0]); + .collect(Collectors.toList()).toArray(AField[]::new); } @Override public String getNameImpl() { - return recordDecl.getDeclName(); + return this.getNodeImpl().getDeclName(); } @Override public String getKindImpl() { - return recordDecl.getTagKind().getCode(); + return this.getNodeImpl().getTagKind().getCode(); } @Override - public AFunction[] getFunctionsArrayImpl() { - return recordDecl.getFunctions().stream() + public AFunction[] getFunctionsImpl() { + return this.getNodeImpl().getFunctions().stream() .map(function -> CxxJoinpoints.create(function, getWeaverEngine(), AFunction.class)) - .toArray(size -> new AFunction[size]); + .toArray(AFunction[]::new); } @Override - public void addFieldImpl(AField field) { - recordDecl.addField((FieldDecl) field.getNode()); + public void addFieldImpl(AField field) { + this.getNodeImpl().addField((FieldDecl) field.getNodeImpl()); } @Override - public Boolean getIsImplementationImpl() { - return recordDecl.isCompleteDefinition(); + public boolean getIsImplementationImpl() { + return this.getNodeImpl().isCompleteDefinition(); } @Override - public Boolean getIsPrototypeImpl() { - return !recordDecl.isCompleteDefinition(); + public boolean getIsPrototypeImpl() { + return !this.getNodeImpl().isCompleteDefinition(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxReturnStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxReturnStmt.java index 51f6f0fbe5..e6230a1b3f 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxReturnStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxReturnStmt.java @@ -13,44 +13,27 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.ReturnStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AReturnStmt; -public class CxxReturnStmt extends AReturnStmt { - - private final ReturnStmt returnStmt; +public class CxxReturnStmt> extends AReturnStmt { public CxxReturnStmt(ReturnStmt returnStmt, CxxWeaver weaver) { - super(new CxxStatement(returnStmt, weaver), weaver); - this.returnStmt = returnStmt; + super(returnStmt, weaver); } @Override - public ClavaNode getNode() { - return returnStmt; + public ReturnStmt getNodeImpl() { + return (ReturnStmt) super.getNodeImpl(); } @Override - public AExpression getReturnExprImpl() { - return returnStmt.getRetValue().map(retValue -> CxxJoinpoints.create(retValue, + public AExpression getReturnExprImpl() { + return this.getNodeImpl().getRetValue().map(retValue -> CxxJoinpoints.create(retValue, getWeaverEngine(), AExpression.class)).orElse(null); } - /* - @Override - public void defReturnExprImpl(AExpression value) { - - // TODO Auto-generated method stub - super.defReturnExprImpl(value); - } - - @Override - public void setReturnExprImpl(AExpression returnExpr) { - defReturnExprImpl(returnExpr); - } - */ } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java index 9bc64ef72b..6c8906ea05 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java @@ -14,6 +14,9 @@ package pt.up.fe.specs.clava.weaver.joinpoints; import java.util.List; + +import org.lara.interpreter.weaver.interf.enums.InsertPosition; + import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaNodes; @@ -30,35 +33,32 @@ import pt.up.fe.specs.clava.weaver.CxxSelects; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.Insert; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AScope; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; import pt.up.fe.specs.clava.weaver.importable.AstFactory; import pt.up.fe.specs.util.SpecsLogs; -public class CxxScope extends AScope { - - private final CompoundStmt scope; +public class CxxScope> extends AScope { public CxxScope(CompoundStmt scope, CxxWeaver weaver) { - super(new CxxStatement(scope, weaver), weaver); - this.scope = scope; + super(scope, weaver); } @Override - public ClavaNode getNode() { - return scope; + public CompoundStmt getNodeImpl() { + return (CompoundStmt) super.getNodeImpl(); } @Override - public AJoinPoint[] insertImpl(String position, String code) { + public AJoinpoint[] insertImpl(InsertPosition position, String code) { // 'body' behaviour - if (!scope.isNestedScope()) { + if (!this.getNodeImpl().isNestedScope()) { Stmt literalStmt = getWeaverEngine().getSnippetParser().parseStmt(code); - CxxActions.insertStmt(position, scope, literalStmt, getWeaverEngine()); - return new AJoinPoint[] { CxxJoinpoints.create(literalStmt, getWeaverEngine()) }; + CxxActions.insertStmt(position, this.getNodeImpl(), literalStmt, getWeaverEngine()); + return new AJoinpoint[] { CxxJoinpoints.create(literalStmt, getWeaverEngine()) }; } // Default behaviour @@ -66,88 +66,88 @@ public AJoinPoint[] insertImpl(String position, String code) { } @Override - public AJoinPoint insertBeforeImpl(AJoinPoint node) { + public AJoinpoint insertBeforeImpl(AJoinpoint node) { // 'body' behaviour - if (!scope.isNestedScope()) { + if (!this.getNodeImpl().isNestedScope()) { ClavaLog.warning("Avoid using action 'insert before' over 'body' joinpoint, use 'insertBegin' instead."); - return insertBodyImplJp("before", node.getNode()); + return insertBodyImplJp(InsertPosition.BEFORE, node.getNodeImpl()); } return super.insertBeforeImpl(node); } @Override - public AJoinPoint insertAfterImpl(AJoinPoint node) { + public AJoinpoint insertAfterImpl(AJoinpoint node) { // 'body' behaviour - if (!scope.isNestedScope()) { + if (!this.getNodeImpl().isNestedScope()) { ClavaLog.warning("Avoid using action 'insert after' over 'body' joinpoint, use 'insertEnd' instead."); - return insertBodyImplJp("after", node.getNode()); + return insertBodyImplJp(InsertPosition.AFTER, node.getNodeImpl()); } return super.insertAfterImpl(node); } @Override - public AJoinPoint replaceWithImpl(AJoinPoint node) { + public AJoinpoint replaceWithImpl(AJoinpoint node) { // 'body' behaviour - if (!scope.isNestedScope() && !(node instanceof AScope)) { + if (!this.getNodeImpl().isNestedScope() && !(node instanceof AScope)) { // Transform, if needed, the given node into a stmt - Stmt stmt = ClavaNodes.toStmt(node.getNode()); - return insertBodyImplJp("replace", stmt); + Stmt stmt = ClavaNodes.toStmt(node.getNodeImpl()); + return insertBodyImplJp(InsertPosition.REPLACE, stmt); } // Default behaviour return super.replaceWithImpl(node); } - private AJoinPoint insertBodyImplJp(String position, ClavaNode newNode) { + private AJoinpoint insertBodyImplJp(InsertPosition position, ClavaNode newNode) { - Stmt newStmt = ClavaNodes.getValidStatement(newNode, Insert.valueOf(position.toUpperCase()).toPosition()); + Stmt newStmt = ClavaNodes.getValidStatement(newNode, Insert.valueOf(position.getDisplay().toUpperCase()).toPosition()); if (newStmt == null) { return null; } - CxxActions.insertStmt(position, scope, newStmt, getWeaverEngine()); + CxxActions.insertStmt(position, this.getNodeImpl(), newStmt, getWeaverEngine()); // Body becomes the parent of this statement return CxxJoinpoints.create(newStmt, getWeaverEngine()); } @Override - public AJoinPoint insertBeginImpl(String code) { + public AJoinpoint insertBeginImpl(String code) { return insertBeginImpl(AstFactory.stmtLiteral(getWeaverEngine(), code)); } @Override - public AJoinPoint insertBeginImpl(AJoinPoint node) { - Stmt newStmt = ClavaNodes.toStmt(node.getNode()); + public AJoinpoint insertBeginImpl(AJoinpoint node) { + Stmt newStmt = ClavaNodes.toStmt(node.getNodeImpl()); - CxxActions.insertStmt("before", scope, newStmt, getWeaverEngine()); + CxxActions.insertStmt(InsertPosition.BEFORE, this.getNodeImpl(), newStmt, getWeaverEngine()); return CxxJoinpoints.create(newStmt, getWeaverEngine()); } @Override - public AJoinPoint insertEndImpl(String code) { + public AJoinpoint insertEndImpl(String code) { return insertEndImpl(AstFactory.stmtLiteral(getWeaverEngine(), code)); } @Override - public AJoinPoint insertEndImpl(AJoinPoint node) { - Stmt newStmt = ClavaNodes.toStmt(node.getNode()); + public AJoinpoint insertEndImpl(AJoinpoint node) { + Stmt newStmt = ClavaNodes.toStmt(node.getNodeImpl()); - CxxActions.insertStmt("after", scope, newStmt, getWeaverEngine()); + CxxActions.insertStmt(InsertPosition.AFTER, this.getNodeImpl(), newStmt, getWeaverEngine()); return CxxJoinpoints.create(newStmt, getWeaverEngine()); } @Override - public Long getNumStatementsImpl(Boolean flat) { - var nodesStream = flat ? scope.getChildrenStream() : scope.getDescendantsStream(); + public long getGetNumStatementsImpl(boolean flat) { + var nodesStream = flat ? this.getNodeImpl().getChildrenStream() : this.getNodeImpl().getDescendantsStream(); return nodesStream.filter(Stmt.class::isInstance) // Ignore CompoundStmt, etc @@ -157,34 +157,34 @@ public Long getNumStatementsImpl(Boolean flat) { } private List getStatements() { - return scope.toStatements(); + return this.getNodeImpl().toStatements(); } @Override public void clearImpl() { - CxxActions.removeChildren(scope, getWeaverEngine()); + CxxActions.removeChildren(this.getNodeImpl(), getWeaverEngine()); } @Override - public Boolean getNakedImpl() { - return scope.isNaked(); + public boolean getNakedImpl() { + return this.getNodeImpl().isNaked(); } @Override - public void setNakedImpl(Boolean isNaked) { - scope.setNaked(isNaked); + public void setNakedImpl(boolean isNaked) { + this.getNodeImpl().setNaked(isNaked); } @Override - public AJoinPoint addLocalImpl(String name, AJoinPoint type, String initValue) { + public AJoinpoint addLocalImpl(String name, AJoinpoint type, String initValue) { // Check if joinpoint is a CxxType if (!(type instanceof AType)) { - SpecsLogs.msgInfo("addLocal: the provided join point (" + type.getJoinPointType() + ") is not a type"); + SpecsLogs.msgInfo("addLocal: the provided join point (" + type.joinPointType() + ") is not a type"); return null; } - Type typeNode = (Type) type.getNode(); + Type typeNode = (Type) type.getNodeImpl(); // defaults as no init Expr initExpr = null; @@ -199,7 +199,7 @@ public AJoinPoint addLocalImpl(String name, AJoinPoint type, String initValue) { } varDecl.set(VarDecl.IS_USED); - AJoinPoint varDeclJp = CxxJoinpoints.create(varDecl, getWeaverEngine()); + AJoinpoint varDeclJp = CxxJoinpoints.create(varDecl, getWeaverEngine()); insertBegin(varDeclJp); @@ -207,18 +207,18 @@ public AJoinPoint addLocalImpl(String name, AJoinPoint type, String initValue) { } @Override - public AStatement[] getStmtsArrayImpl() { - return CxxJoinpoints.create(getNode().getChildren(Stmt.class), getWeaverEngine(), AStatement.class); + public AStatement[] getStmtsImpl() { + return CxxJoinpoints.create(getNodeImpl().getChildren(Stmt.class), getWeaverEngine(), AStatement.class); } @Override - public AStatement[] getAllStmtsArrayImpl() { - return CxxSelects.select(getWeaverEngine(), AStatement.class, getStatements(), true, CxxSelects::stmtFilter).toArray(new AStatement[0]); + public AStatement[] getAllStmtsImpl() { + return CxxSelects.select(getWeaverEngine(), AStatement.class, getStatements(), true, CxxSelects::stmtFilter); } @Override - public AStatement getFirstStmtImpl() { - AStatement[] stmts = getStmtsArrayImpl(); + public AStatement getFirstStmtImpl() { + AStatement[] stmts = getStmtsImpl(); if (stmts.length == 0) { return null; @@ -229,8 +229,8 @@ public AStatement getFirstStmtImpl() { } @Override - public AStatement getLastStmtImpl() { - AStatement[] stmts = getStmtsArrayImpl(); + public AStatement getLastStmtImpl() { + AStatement[] stmts = getStmtsImpl(); if (stmts.length == 0) { return null; @@ -240,14 +240,14 @@ public AStatement getLastStmtImpl() { } @Override - public AJoinPoint getOwnerImpl() { + public AJoinpoint getOwnerImpl() { // TODO: This should generically work, but corner cases have not been checked return getParentImpl(); } @Override public String cfgImpl() { - ControlFlowGraph cfg = new ControlFlowGraph(scope); + ControlFlowGraph cfg = new ControlFlowGraph(this.getNodeImpl()); var cfgDot = cfg.toDot(); ClavaLog.info(cfgDot); return cfgDot; @@ -255,19 +255,19 @@ public String cfgImpl() { @Override public String dfgImpl() { - DataFlowGraph dfg = new DataFlowGraph(scope); + DataFlowGraph dfg = new DataFlowGraph(this.getNodeImpl()); var dfgDot = dfg.toDot(); ClavaLog.info(dfgDot); return dfgDot; } @Override - public AJoinPoint insertReturnImpl(AJoinPoint code) { + public AJoinpoint insertReturnImpl(AJoinpoint code) { return CxxActions.insertReturn(this, code, getWeaverEngine()); } @Override - public AJoinPoint insertReturnImpl(String code) { + public AJoinpoint insertReturnImpl(String code) { var stmt = CxxJoinpoints.create(getWeaverEngine().getSnippetParser().parseStmt(code), getWeaverEngine()); return insertReturnImpl(stmt); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStatement.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStatement.java index b6e543ddeb..19ea4ecc6d 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStatement.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStatement.java @@ -14,56 +14,47 @@ package pt.up.fe.specs.clava.weaver.joinpoints; import java.util.List; -import pt.up.fe.specs.clava.ClavaNode; + import pt.up.fe.specs.clava.ClavaNodes; import pt.up.fe.specs.clava.ast.stmt.Stmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; import pt.up.fe.specs.util.treenode.NodeInsertUtils; -public class CxxStatement extends AStatement { - - private final Stmt stmt; +public class CxxStatement> extends AStatement { public CxxStatement(Stmt stmt, CxxWeaver weaver) { - super(weaver); - this.stmt = stmt; + super(stmt, weaver); } @Override - public ClavaNode getNode() { - return stmt; + public Stmt getNodeImpl() { + return (Stmt) super.getNodeImpl(); } @Override - public AJoinPoint replaceWithImpl(AJoinPoint node) { + public AJoinpoint replaceWithImpl(AJoinpoint node) { // First "transform" node to insert into a statement - Stmt newStmt = ClavaNodes.toStmt(node.getNode()); + Stmt newStmt = ClavaNodes.toStmt(node.getNodeImpl()); - NodeInsertUtils.replace(stmt, newStmt); + NodeInsertUtils.replace(this.getNodeImpl(), newStmt); // Return a statement joinpoint return CxxJoinpoints.create(newStmt, getWeaverEngine()); } @Override - public Boolean getIsFirstImpl() { + public boolean getIsFirstImpl() { // Get parent and check Stmt position on that list - return stmt.getParent().getChildren(Stmt.class).indexOf(stmt) == 0; - - // return stmt.indexOfSelf() == 1; - // List statementJps = parent.selectStatements(); - // Preconditions.checkArgument(!statementJps.isEmpty(), "Expected parent to "); + return this.getNodeImpl().getParent().getChildren(Stmt.class).indexOf(this.getNodeImpl()) == 0; } @Override - public Boolean getIsLastImpl() { + public boolean getIsLastImpl() { // Get parent and check Stmt position on that list - List siblings = stmt.getParent().getChildren(Stmt.class); - return siblings.indexOf(stmt) == (siblings.size() - 1); - - // return stmt.indexOfSelf() == stmt.getParentImpl().numChildren(); + List siblings = this.getNodeImpl().getParent().getChildren(Stmt.class); + return siblings.indexOf(this.getNodeImpl()) == (siblings.size() - 1); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStruct.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStruct.java index 92c90dfdc2..abdb255675 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStruct.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxStruct.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.RecordDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStruct; -public class CxxStruct extends AStruct { - - private final RecordDecl recordDecl; +public class CxxStruct> extends AStruct { public CxxStruct(RecordDecl recordDecl, CxxWeaver weaver) { - super(new CxxRecord(recordDecl, weaver), weaver); - this.recordDecl = recordDecl; + super(recordDecl, weaver); } @Override - public ClavaNode getNode() { - return recordDecl; + public RecordDecl getNodeImpl() { + return (RecordDecl) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitch.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitch.java index 5feafd8dcd..cea610cabe 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitch.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitch.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.SwitchStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; @@ -21,41 +20,38 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ASwitch; -public class CxxSwitch extends ASwitch { - - private final SwitchStmt switchStmt; +public class CxxSwitch> extends ASwitch { public CxxSwitch(SwitchStmt switchStmt, CxxWeaver weaver) { - super(new CxxStatement(switchStmt, weaver), weaver); - this.switchStmt = switchStmt; + super(switchStmt, weaver); } @Override - public ClavaNode getNode() { - return switchStmt; + public SwitchStmt getNodeImpl() { + return (SwitchStmt) super.getNodeImpl(); } @Override - public Boolean getHasDefaultCaseImpl() { - return switchStmt.hasDefaultCase(); + public boolean getHasDefaultCaseImpl() { + return this.getNodeImpl().hasDefaultCase(); } @Override - public ACase getGetDefaultCaseImpl() { - return switchStmt.getDefaultCase() + public ACase getGetDefaultCaseImpl() { + return this.getNodeImpl().getDefaultCase() .map(node -> CxxJoinpoints.create(node, getWeaverEngine(), ACase.class)) .orElse(null); } @Override - public ACase[] getCasesArrayImpl() { - return CxxJoinpoints.create(switchStmt.getCases(), getWeaverEngine(), ACase.class); + public ACase[] getCasesImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getCases(), getWeaverEngine(), ACase.class); } @Override - public AExpression getConditionImpl() { - return CxxJoinpoints.create(switchStmt.getCond(), getWeaverEngine(), AExpression.class); + public AExpression getConditionImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getCond(), getWeaverEngine(), AExpression.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitchCase.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitchCase.java index 52a86d3c92..73a4cd6dc3 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitchCase.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxSwitchCase.java @@ -1,22 +1,18 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.stmt.SwitchCase; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ASwitchCase; -public class CxxSwitchCase extends ASwitchCase { - - private final SwitchCase switchCase; +public class CxxSwitchCase> extends ASwitchCase { public CxxSwitchCase(SwitchCase switchCase, CxxWeaver weaver) { - super(new CxxStatement(switchCase, weaver), weaver); - this.switchCase = switchCase; + super(switchCase, weaver); } @Override - public ClavaNode getNode() { - return switchCase; + public SwitchCase getNodeImpl() { + return (SwitchCase) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTag.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTag.java index 0f3775652c..7f9cd1c341 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTag.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTag.java @@ -13,39 +13,37 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; +import org.lara.interpreter.weaver.interf.enums.InsertPosition; + import pt.up.fe.specs.clava.ast.lara.LaraTagPragma; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.Insert; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATag; -public class CxxTag extends ATag { - - private final LaraTagPragma tag; +public class CxxTag> extends ATag { public CxxTag(LaraTagPragma reference, CxxWeaver weaver) { - super(new CxxPragma(reference, weaver), weaver); - tag = reference; + super(reference, weaver); } @Override - public ClavaNode getNode() { - return tag; + public LaraTagPragma getNodeImpl() { + return (LaraTagPragma) super.getNodeImpl(); } @Override public String getIdImpl() { - return tag.getTagId(); + return this.getNodeImpl().getTagId(); } @Override - public AJoinPoint[] insertImpl(String position, String code) { + public AJoinpoint[] insertImpl(InsertPosition position, String code) { - Insert insert = Insert.getHelper().fromValue(position); + Insert insert = Insert.getHelper().fromValue(position.getDisplay()); if (insert == Insert.AFTER) { - return (AJoinPoint[]) getTargetImpl().insertImpl(position, code); + return (AJoinpoint[]) getTargetImpl().insertImpl(position, code); } else { return super.insertImpl(position, code); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTernaryOp.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTernaryOp.java index 7335642aa5..86a98275a0 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTernaryOp.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTernaryOp.java @@ -13,40 +13,35 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.ConditionalOperator; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATernaryOp; -public class CxxTernaryOp extends ATernaryOp { - - private final ConditionalOperator op; +public class CxxTernaryOp> extends ATernaryOp { public CxxTernaryOp(ConditionalOperator op, CxxWeaver weaver) { - super(new CxxOp(op, weaver), weaver); - - this.op = op; + super(op, weaver); } @Override - public ClavaNode getNode() { - return op; + public ConditionalOperator getNodeImpl() { + return (ConditionalOperator) super.getNodeImpl(); } @Override - public AExpression getCondImpl() { - return CxxJoinpoints.create(op.getCondition(), getWeaverEngine(), AExpression.class); + public AExpression getCondImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getCondition(), getWeaverEngine(), AExpression.class); } @Override - public AExpression getTrueExprImpl() { - return CxxJoinpoints.create(op.getTrueExpr(), getWeaverEngine(), AExpression.class); + public AExpression getTrueExprImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getTrueExpr(), getWeaverEngine(), AExpression.class); } @Override - public AExpression getFalseExprImpl() { - return CxxJoinpoints.create(op.getFalseExpr(), getWeaverEngine(), AExpression.class); + public AExpression getFalseExprImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getFalseExpr(), getWeaverEngine(), AExpression.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java index f0017c2166..31cfaf2e6f 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.CXXThisExpr; import pt.up.fe.specs.clava.ast.type.TagType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -22,34 +21,31 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.APointerType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AThis; -public class CxxThis extends AThis { - - private final CXXThisExpr thisExpr; +public class CxxThis> extends AThis { public CxxThis(CXXThisExpr thisExpr, CxxWeaver weaver) { - super(new CxxExpression(thisExpr, weaver), weaver); - this.thisExpr = thisExpr; + super(thisExpr, weaver); } @Override - public ClavaNode getNode() { - return thisExpr; + public CXXThisExpr getNodeImpl() { + return (CXXThisExpr) super.getNodeImpl(); } @Override - public ADecl getDeclImpl() { + public ADecl getDeclImpl() { // type.pointee.decl var type = getTypeImpl(); if (!(type instanceof APointerType)) { - throw new RuntimeException("Not implemented with type is " + type.getJoinPointType()); + throw new RuntimeException("Not implemented with type is " + type.joinPointType()); } // Get class type - var pointeeType = ((APointerType) type).getPointeeImpl(); + var pointeeType = ((APointerType) type).getPointeeImpl(); - var thisType = pointeeType.getNode(); + var thisType = pointeeType.getNodeImpl(); if (!(thisType instanceof TagType)) { throw new RuntimeException("Not implemented when this type is a " + thisType.getClass()); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefDecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefDecl.java index 1b4c9bf216..d23da45633 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefDecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefDecl.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.TypedefDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATypedefDecl; -public class CxxTypedefDecl extends ATypedefDecl { - - private final TypedefDecl typedefDecl; +public class CxxTypedefDecl> extends ATypedefDecl { public CxxTypedefDecl(TypedefDecl typedefDecl, CxxWeaver weaver) { - super(new CxxTypedefNameDecl(typedefDecl, weaver), weaver); - this.typedefDecl = typedefDecl; + super(typedefDecl, weaver); } @Override - public ClavaNode getNode() { - return typedefDecl; + public TypedefDecl getNodeImpl() { + return (TypedefDecl) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefNameDecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefNameDecl.java index 832ba2063c..6232b1bfe4 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefNameDecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxTypedefNameDecl.java @@ -13,23 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.TypedefNameDecl; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATypedefNameDecl; -public class CxxTypedefNameDecl extends ATypedefNameDecl { - - private final TypedefNameDecl typedefNameDecl; +public class CxxTypedefNameDecl> extends ATypedefNameDecl { public CxxTypedefNameDecl(TypedefNameDecl typedefNameDecl, CxxWeaver weaver) { - super(new CxxNamedDecl(typedefNameDecl, weaver), weaver); - this.typedefNameDecl = typedefNameDecl; + super(typedefNameDecl, weaver); } @Override - public ClavaNode getNode() { - return typedefNameDecl; + public TypedefNameDecl getNodeImpl() { + return (TypedefNameDecl) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryExprOrType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryExprOrType.java index 6baddf1182..c86cf26d2d 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryExprOrType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryExprOrType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.UnaryExprOrTypeTraitExpr; import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -23,33 +22,31 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AUnaryExprOrType; import pt.up.fe.specs.util.SpecsLogs; -public class CxxUnaryExprOrType extends AUnaryExprOrType { - - private final UnaryExprOrTypeTraitExpr expr; +public class CxxUnaryExprOrType> extends AUnaryExprOrType { public CxxUnaryExprOrType(UnaryExprOrTypeTraitExpr expr, CxxWeaver weaver) { - super(new CxxExpression(expr, weaver), weaver); - - this.expr = expr; + super(expr, weaver); } @Override - public ClavaNode getNode() { - return expr; + public UnaryExprOrTypeTraitExpr getNodeImpl() { + return (UnaryExprOrTypeTraitExpr) super.getNodeImpl(); } @Override - public Boolean getHasTypeExprImpl() { - return expr.hasTypeExpression(); + public boolean getHasTypeExprImpl() { + return this.getNodeImpl().hasTypeExpression(); } @Override - public Boolean getHasArgExprImpl() { - return expr.hasArgumentExpression(); + public boolean getHasArgExprImpl() { + return this.getNodeImpl().hasArgumentExpression(); } @Override - public AType getArgTypeImpl() { + public AType getArgTypeImpl() { + var expr = this.getNodeImpl(); + if (!expr.hasTypeExpression()) { return null; } @@ -58,7 +55,9 @@ public AType getArgTypeImpl() { } @Override - public AExpression getArgExprImpl() { + public AExpression getArgExprImpl() { + var expr = this.getNodeImpl(); + if (!expr.hasArgumentExpression()) { return null; } @@ -67,17 +66,19 @@ public AExpression getArgExprImpl() { } @Override - public void setArgTypeImpl(AType argType) { + public void setArgTypeImpl(AType argType) { + var expr = this.getNodeImpl(); + if (!expr.hasTypeExpression()) { SpecsLogs.msgInfo("UnaryExprOrType '" + expr.getUettKind() + "' does not have a type argument"); return; } - expr.setArgType((Type) argType.getNode()); + expr.setArgType((Type) argType.getNodeImpl()); } @Override public String getKindImpl() { - return expr.getUettKind().getString(); + return this.getNodeImpl().getUettKind().getString(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryOp.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryOp.java index 3eeb49fdf0..8822953268 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryOp.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxUnaryOp.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ClavaNodes; import pt.up.fe.specs.clava.ast.expr.UnaryOperator; import pt.up.fe.specs.clava.ast.expr.enums.UnaryOperatorKind; @@ -22,33 +21,30 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AUnaryOp; -public class CxxUnaryOp extends AUnaryOp { - - private final UnaryOperator unaryOp; +public class CxxUnaryOp> extends AUnaryOp { public CxxUnaryOp(UnaryOperator unaryOp, CxxWeaver weaver) { - super(new CxxOp(unaryOp, weaver), weaver); - this.unaryOp = unaryOp; + super(unaryOp, weaver); } @Override - public ClavaNode getNode() { - return unaryOp; + public UnaryOperator getNodeImpl() { + return (UnaryOperator) super.getNodeImpl(); } @Override - public AExpression getOperandImpl() { - return CxxJoinpoints.create(ClavaNodes.normalize(unaryOp.getSubExpr()), getWeaverEngine(), AExpression.class); + public AExpression getOperandImpl() { + return CxxJoinpoints.create(ClavaNodes.normalize(this.getNodeImpl().getSubExpr()), getWeaverEngine(), AExpression.class); } @Override - public Boolean getIsPointerDerefImpl() { - return unaryOp.getOp() == UnaryOperatorKind.Deref; + public boolean getIsPointerDerefImpl() { + return this.getNodeImpl().getOp() == UnaryOperatorKind.Deref; } @Override - public Boolean getIsBitwiseImpl() { - return unaryOp.getOp().isBitwise(); + public boolean getIsBitwiseImpl() { + return this.getNodeImpl().getOp().isBitwise(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVardecl.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVardecl.java index 7e310f6278..6cdda2ca33 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVardecl.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVardecl.java @@ -13,7 +13,9 @@ package pt.up.fe.specs.clava.weaver.joinpoints; -import pt.up.fe.specs.clava.ClavaNode; +import java.util.HashMap; +import java.util.Map; + import pt.up.fe.specs.clava.ast.decl.VarDecl; import pt.up.fe.specs.clava.ast.expr.Expr; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,39 +23,54 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVardecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVarref; +import pt.up.fe.specs.clava.weaver.enums.StorageClass; import pt.up.fe.specs.clava.weaver.importable.AstFactory; +import pt.up.fe.specs.util.lazy.Lazy; +import pt.up.fe.specs.util.lazy.ThreadSafeLazy; -public class CxxVardecl extends AVardecl { +public class CxxVardecl> extends AVardecl { - private final VarDecl varDecl; + private static final Lazy> STORAGE_TYPE = new ThreadSafeLazy<>( + () -> buildStorageTypeMap()); - public CxxVardecl(VarDecl varDecl, CxxWeaver weaver) { - super(new CxxDeclarator(varDecl, weaver), weaver); + private static Map buildStorageTypeMap() { + HashMap storageClasses = new HashMap<>(); + + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.None, StorageClass.NONE); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Extern, StorageClass.EXTERN); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Static, StorageClass.STATIC); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.PrivateExtern, StorageClass.PRIVATE_EXTERN); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Auto, StorageClass.AUTO); + storageClasses.put(pt.up.fe.specs.clava.ast.decl.enums.StorageClass.Register, StorageClass.REGISTER); - this.varDecl = varDecl; + return storageClasses; + } + + public CxxVardecl(VarDecl varDecl, CxxWeaver weaver) { + super(varDecl, weaver); } @Override - public ClavaNode getNode() { - return varDecl; + public VarDecl getNodeImpl() { + return (VarDecl) super.getNodeImpl(); } @Override - public Boolean getHasInitImpl() { - return varDecl.getInit().isPresent(); + public boolean getHasInitImpl() { + return this.getNodeImpl().getInit().isPresent(); } @Override - public AExpression getInitImpl() { - return varDecl.getInit().map(init -> (AExpression) CxxJoinpoints.create(init, getWeaverEngine())).orElse(null); + public AExpression getInitImpl() { + return this.getNodeImpl().getInit().map(init -> (AExpression) CxxJoinpoints.create(init, getWeaverEngine())).orElse(null); } @Override - public void setInitImpl(AExpression init) { + public void setInitImpl(AExpression init) { if (init == null) { removeInitImpl(true); } else { - varDecl.setInit((Expr) init.getNode()); + this.getNodeImpl().setInit((Expr) init.getNodeImpl()); } } @@ -63,53 +80,65 @@ public void setInitImpl(String init) { removeInitImpl(true); } - varDecl.setInit(getWeaverEngine().getFactory().literalExpr(init, varDecl.getType())); + this.getNodeImpl().setInit(getWeaverEngine().getFactory().literalExpr(init, this.getNodeImpl().getType())); } @Override public void removeInitImpl(boolean removeConst) { - varDecl.removeInit(removeConst); + this.getNodeImpl().removeInit(removeConst); } @Override - public Boolean getIsParamImpl() { + public boolean getIsParamImpl() { return false; } @Override - public String getStorageClassImpl() { - return varDecl.get(VarDecl.STORAGE_CLASS).getString(); + public StorageClass getStorageClassImpl() { + var nodeStorageClass = this.getNodeImpl().get(VarDecl.STORAGE_CLASS); + if (nodeStorageClass == null) { + throw new RuntimeException("Storage class of variable '" + getNameImpl() + "' is null"); + } + + StorageClass jpStorageClass = STORAGE_TYPE.get().get(nodeStorageClass); + if (jpStorageClass == null) { + throw new RuntimeException("Storage class '" + nodeStorageClass + "' of variable '" + getNameImpl() + + "' is not supported in the join point model"); + } + + return jpStorageClass; } @Override - public void setStorageClassImpl(String storageClass) { - varDecl.setStorageClass(storageClass); + public void setStorageClassImpl(StorageClass storageClass) { + var nodeStorageClass = STORAGE_TYPE.get().entrySet().stream() + .filter(entry -> entry.getValue() == storageClass) + .map(Map.Entry::getKey) + .findFirst() + .orElseThrow(() -> new RuntimeException( + "Storage class '" + storageClass + "' is not supported in the join point model")); + + this.getNodeImpl().setStorageClass(nodeStorageClass); } @Override - public Boolean getIsGlobalImpl() { - return varDecl.get(VarDecl.HAS_GLOBAL_STORAGE); + public boolean getIsGlobalImpl() { + return this.getNodeImpl().get(VarDecl.HAS_GLOBAL_STORAGE); } @Override public String getInitStyleImpl() { - return varDecl.get(VarDecl.INIT_STYLE).getString(); - // return InitializationStyle.valueOf(varDecl.get(VarDecl.INIT_STYLE).name()); + return this.getNodeImpl().get(VarDecl.INIT_STYLE).getString(); } @Override - public AVardecl getDefinitionImpl() { - return CxxJoinpoints.create(varDecl.getDefinition(), getWeaverEngine(), AVardecl.class); + public AVardecl getDefinitionImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getDefinition(), getWeaverEngine(), AVardecl.class); } - // @Override - // public void varrefImpl() { - // return CxxJoinpoints.create(AstFactory.varref(CxxJoinpoints.create(varDecl, AVardecl.class), AVarref.class)); - // } - @Override - public AVarref varrefImpl() { - return AstFactory.varref(getWeaverEngine(), CxxJoinpoints.create(varDecl, getWeaverEngine(), AVardecl.class)); + public AVarref varrefImpl() { + return AstFactory.varref(getWeaverEngine(), CxxJoinpoints.create(this.getNodeImpl(), getWeaverEngine(), AVardecl.class)); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVarref.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVarref.java index 34bb208124..ff38c655b6 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVarref.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxVarref.java @@ -26,56 +26,52 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVardecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVarref; -public class CxxVarref extends AVarref { - - private final DeclRefExpr refExpr; +public class CxxVarref> extends AVarref { public CxxVarref(DeclRefExpr refExpr, CxxWeaver weaver) { - super(new CxxExpression(refExpr, weaver), weaver); - - this.refExpr = refExpr; + super(refExpr, weaver); } @Override - public DeclRefExpr getNode() { - return refExpr; + public DeclRefExpr getNodeImpl() { + return (DeclRefExpr) super.getNodeImpl(); } @Override public String getNameImpl() { - return refExpr.getRefName(); + return this.getNodeImpl().getRefName(); } @Override public void setNameImpl(String name) { - refExpr.setRefName(name); + this.getNodeImpl().setRefName(name); } @Override public String getKindImpl() { - return refExpr.getKind().name().toLowerCase(); + return this.getNodeImpl().getKind().name().toLowerCase(); } @Override - public AExpression getUseExprImpl() { - return CxxJoinpoints.create(refExpr.getUseExpr(), getWeaverEngine(), AExpression.class); + public AExpression getUseExprImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getUseExpr(), getWeaverEngine(), AExpression.class); } @Override - public AVardecl getVardeclImpl() { - ADeclarator declarator = getDeclarationImpl(); + public AVardecl getVardeclImpl() { + ADeclarator declarator = getDeclarationImpl(); - return declarator instanceof AVardecl ? (AVardecl) declarator : null; + return declarator instanceof AVardecl ? (AVardecl) declarator : null; } @Override - public Boolean getIsFunctionCallImpl() { - return refExpr.isFunctionCall(); + public boolean getIsFunctionCallImpl() { + return this.getNodeImpl().isFunctionCall(); } @Override - public ADeclarator getDeclarationImpl() { - Optional declarator = refExpr.getVariableDeclaration(); + public ADeclarator getDeclarationImpl() { + Optional declarator = this.getNodeImpl().getVariableDeclaration(); if (!declarator.isPresent()) { return null; @@ -85,13 +81,13 @@ public ADeclarator getDeclarationImpl() { } @Override - public ADecl getDeclImpl() { + public ADecl getDeclImpl() { return getVardeclImpl(); } @Override public String getPropertyImpl() { - var parent = refExpr.getParent(); + var parent = this.getNodeImpl().getParent(); if (parent == null) { return null; @@ -105,13 +101,13 @@ public String getPropertyImpl() { } @Override - public Boolean getHasPropertyImpl() { - if (!refExpr.hasParent()) { + public boolean getHasPropertyImpl() { + if (!this.getNodeImpl().hasParent()) { return false; } // If parent is a MSPropertyRefExpr, this this varref has a MS-style property - return refExpr.getParent() instanceof MSPropertyRefExpr; + return this.getNodeImpl().getParent() instanceof MSPropertyRefExpr; } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxWrapperStmt.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxWrapperStmt.java index 72ad3a2e8d..dc2043a911 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxWrapperStmt.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxWrapperStmt.java @@ -19,43 +19,40 @@ import pt.up.fe.specs.clava.ast.stmt.WrapperStmt; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AWrapperStmt; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.enums.AWrapperStmtKindEnum; +import pt.up.fe.specs.clava.weaver.enums.WrapperStatementKind; -public class CxxWrapperStmt extends AWrapperStmt { - - private final WrapperStmt wrapperStmt; +public class CxxWrapperStmt> extends AWrapperStmt { public CxxWrapperStmt(WrapperStmt wrapperStmt, CxxWeaver weaver) { - super(new CxxStatement(wrapperStmt, weaver), weaver); - this.wrapperStmt = wrapperStmt; + super(wrapperStmt, weaver); } @Override - public ClavaNode getNode() { - return wrapperStmt; + public WrapperStmt getNodeImpl() { + return (WrapperStmt) super.getNodeImpl(); } @Override - public String getKindImpl() { + public WrapperStatementKind getKindImpl() { - ClavaNode wrappedNode = wrapperStmt.getWrappedNode(); + ClavaNode wrappedNode = this.getNodeImpl().getWrappedNode(); if (wrappedNode instanceof Comment) { - return AWrapperStmtKindEnum.COMMENT.getName(); + return WrapperStatementKind.COMMENT; } if (wrappedNode instanceof Pragma) { - return AWrapperStmtKindEnum.PRAGMA.getName(); + return WrapperStatementKind.PRAGMA; } throw new RuntimeException("Case not defined for wrapperStmt.kind: " + wrappedNode.getClass().getSimpleName()); } @Override - public AJoinPoint getContentImpl() { - return CxxJoinpoints.create(wrapperStmt.getWrappedNode(), getWeaverEngine()); + public AJoinpoint getContentImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getWrappedNode(), getWeaverEngine()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java index 030cad1d79..e95ab5b081 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java @@ -17,18 +17,10 @@ import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; -public class GenericJoinpoint extends ACxxWeaverJoinPoint { - - private final ClavaNode node; +public class GenericJoinpoint> extends ACxxWeaverJoinPoint { public GenericJoinpoint(ClavaNode node, CxxWeaver weaver) { - super(weaver); - this.node = node; - } - - @Override - public ClavaNode getNode() { - return node; + super(node, weaver); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkFor.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkFor.java index b935abee78..a65ff74da2 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkFor.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkFor.java @@ -13,25 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints.cilk; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.cilk.CilkFor; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ACilkFor; -import pt.up.fe.specs.clava.weaver.joinpoints.CxxLoop; -public class CxxCilkFor extends ACilkFor { - - private final CilkFor loop; +public class CxxCilkFor> extends ACilkFor { public CxxCilkFor(CilkFor loop, CxxWeaver weaver) { - super(new CxxLoop(loop, weaver), weaver); - - this.loop = loop; + super(loop, weaver); } @Override - public ClavaNode getNode() { - return loop; + public CilkFor getNodeImpl() { + return (CilkFor) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSpawn.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSpawn.java index 294e7ca369..6563a0003d 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSpawn.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSpawn.java @@ -13,25 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints.cilk; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.cilk.CilkSpawn; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ACilkSpawn; -import pt.up.fe.specs.clava.weaver.joinpoints.CxxCall; -public class CxxCilkSpawn extends ACilkSpawn { - - private final CilkSpawn spawnCall; +public class CxxCilkSpawn> extends ACilkSpawn { public CxxCilkSpawn(CilkSpawn spawnCall, CxxWeaver weaver) { - super(new CxxCall(spawnCall, weaver), weaver); - - this.spawnCall = spawnCall; + super(spawnCall, weaver); } @Override - public ClavaNode getNode() { - return spawnCall; + public CilkSpawn getNodeImpl() { + return (CilkSpawn) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSync.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSync.java index 0f66786db5..d170cb358c 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSync.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/cilk/CxxCilkSync.java @@ -13,24 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints.cilk; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.cilk.CilkSync; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ACilkSync; -import pt.up.fe.specs.clava.weaver.joinpoints.CxxStatement; -public class CxxCilkSync extends ACilkSync { - - private final CilkSync cilkSync; +public class CxxCilkSync> extends ACilkSync { public CxxCilkSync(CilkSync cilkSync, CxxWeaver weaver) { - super(new CxxStatement(cilkSync, weaver), weaver); - this.cilkSync = cilkSync; + super(cilkSync, weaver); } @Override - public ClavaNode getNode() { - return cilkSync; + public CilkSync getNodeImpl() { + return (CilkSync) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxAdjustedType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxAdjustedType.java index db1b8f34c2..4a0cd5faab 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxAdjustedType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxAdjustedType.java @@ -13,40 +13,35 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.AdjustedType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AAdjustedType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxAdjustedType extends AAdjustedType { - - private final AdjustedType adjustedType; +public class CxxAdjustedType> extends AAdjustedType { public CxxAdjustedType(AdjustedType adjustedType, CxxWeaver weaver) { - super(new CxxType(adjustedType, weaver), weaver); - - this.adjustedType = adjustedType; + super(adjustedType, weaver); } @Override - public ClavaNode getNode() { - return adjustedType; + public AdjustedType getNodeImpl() { + return (AdjustedType) super.getNodeImpl(); } @Override - public AType getOriginalTypeImpl() { - return CxxJoinpoints.create(adjustedType.get(AdjustedType.ORIGINAL_TYPE), getWeaverEngine(), AType.class); + public AType getOriginalTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().get(AdjustedType.ORIGINAL_TYPE), getWeaverEngine(), AType.class); } @Override - public int[] getArrayDimsArrayImpl() { - return getOriginalTypeImpl().getArrayDimsArrayImpl(); + public int[] getArrayDimsImpl() { + return getOriginalTypeImpl().getArrayDimsImpl(); } @Override - public Integer getArraySizeImpl() { + public int getArraySizeImpl() { return getOriginalTypeImpl().getArraySizeImpl(); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxArrayType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxArrayType.java index b1c3ae53b2..8600bf3136 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxArrayType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxArrayType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.ArrayType; import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,29 +20,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AArrayType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxArrayType extends AArrayType { - - private final ArrayType arrayType; +public class CxxArrayType> extends AArrayType { public CxxArrayType(ArrayType arrayType, CxxWeaver weaver) { - super(new CxxType(arrayType, weaver), weaver); - - this.arrayType = arrayType; + super(arrayType, weaver); } @Override - public ClavaNode getNode() { - return arrayType; + public ArrayType getNodeImpl() { + return (ArrayType) super.getNodeImpl(); } @Override - public AType getElementTypeImpl() { - return CxxJoinpoints.create(arrayType.getElementType(), getWeaverEngine(), AType.class); + public AType getElementTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getElementType(), getWeaverEngine(), AType.class); } @Override - public void setElementTypeImpl(AType arrayElementType) { - arrayType.setElementType((Type) arrayElementType.getNode()); + public void setElementTypeImpl(AType arrayElementType) { + this.getNodeImpl().setElementType((Type) arrayElementType.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxBuiltinType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxBuiltinType.java index 323de90caa..e42e40bf46 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxBuiltinType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxBuiltinType.java @@ -13,54 +13,49 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.BuiltinType; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ABuiltinType; -public class CxxBuiltinType extends ABuiltinType { - - private final BuiltinType builtinType; +public class CxxBuiltinType> extends ABuiltinType { public CxxBuiltinType(BuiltinType builtinType, CxxWeaver weaver) { - super(new CxxType(builtinType, weaver), weaver); - - this.builtinType = builtinType; + super(builtinType, weaver); } @Override - public ClavaNode getNode() { - return builtinType; + public BuiltinType getNodeImpl() { + return (BuiltinType) super.getNodeImpl(); } @Override public String getBuiltinKindImpl() { - return builtinType.get(BuiltinType.KIND).name(); + return this.getNodeImpl().get(BuiltinType.KIND).name(); } @Override - public Boolean getIsIntegerImpl() { - return builtinType.get(BuiltinType.KIND).isInteger(); + public boolean getIsIntegerImpl() { + return this.getNodeImpl().get(BuiltinType.KIND).isInteger(); } @Override - public Boolean getIsFloatImpl() { - return builtinType.get(BuiltinType.KIND).isFloatingPoint(); + public boolean getIsFloatImpl() { + return this.getNodeImpl().get(BuiltinType.KIND).isFloatingPoint(); } @Override - public Boolean getIsSignedImpl() { - return builtinType.get(BuiltinType.KIND).isSignedInteger(); + public boolean getIsSignedImpl() { + return this.getNodeImpl().get(BuiltinType.KIND).isSignedInteger(); } @Override - public Boolean getIsUnsignedImpl() { - return builtinType.get(BuiltinType.KIND).isUnsignedInteger(); + public boolean getIsUnsignedImpl() { + return this.getNodeImpl().get(BuiltinType.KIND).isUnsignedInteger(); } @Override - public Boolean getIsVoidImpl() { - return builtinType.get(BuiltinType.KIND).isVoid(); + public boolean getIsVoidImpl() { + return this.getNodeImpl().get(BuiltinType.KIND).isVoid(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxElaboratedType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxElaboratedType.java index 8d2fea7783..73738f2cd1 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxElaboratedType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxElaboratedType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.ElaboratedType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; @@ -21,37 +20,30 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; import pt.up.fe.specs.util.SpecsStrings; -public class CxxElaboratedType extends AElaboratedType { - - private final ElaboratedType elaboratedType; +public class CxxElaboratedType> extends AElaboratedType { public CxxElaboratedType(ElaboratedType elaboratedType, CxxWeaver weaver) { - super(new CxxType(elaboratedType, weaver), weaver); - - this.elaboratedType = elaboratedType; + super(elaboratedType, weaver); } @Override - public ClavaNode getNode() { - return elaboratedType; + public ElaboratedType getNodeImpl() { + return (ElaboratedType) super.getNodeImpl(); } @Override public String getQualifierImpl() { - return SpecsStrings.nullIfEmpty(elaboratedType.getQualifier()); - // String qualifier = elaboratedType.get(ElaboratedType.QUALIFIER); - // - // return qualifier.isEmpty() ? null : qualifier; + return SpecsStrings.nullIfEmpty(this.getNodeImpl().getQualifier()); } @Override public String getKeywordImpl() { - return SpecsStrings.nullIfEmpty(elaboratedType.getKeyword().getCode()); + return SpecsStrings.nullIfEmpty(this.getNodeImpl().getKeyword().getCode()); } @Override - public AType getNamedTypeImpl() { - return CxxJoinpoints.create(elaboratedType.get(ElaboratedType.NAMED_TYPE), getWeaverEngine(), AType.class); + public AType getNamedTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().get(ElaboratedType.NAMED_TYPE), getWeaverEngine(), AType.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java index ddd9418d12..fbc654a5b0 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.extra.App; import pt.up.fe.specs.clava.ast.type.EnumType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -22,28 +21,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; import pt.up.fe.specs.util.SpecsLogs; -public class CxxEnumType extends AEnumType { - private final EnumType enumType; +public class CxxEnumType> extends AEnumType { public CxxEnumType(EnumType enumType, CxxWeaver weaver) { - super(new CxxTagType(enumType, weaver), weaver); - - this.enumType = enumType; + super(enumType, weaver); } @Override - public ClavaNode getNode() { - return enumType; + public EnumType getNodeImpl() { + return (EnumType) super.getNodeImpl(); } @Override - public AType getIntegerTypeImpl() { - if (getRoot() == null) { + public AType getIntegerTypeImpl() { + if (root() == null) { SpecsLogs.msgInfo("Root not defined, is this a detached join point? -> " + this); return null; } - return CxxJoinpoints.create(enumType.getEnumDecl((App) getRootImpl().getNode()).getIntegerType(), getWeaverEngine(), AType.class); + return CxxJoinpoints.create(this.getNodeImpl().getEnumDecl((App) getRootImpl().getNodeImpl()).getIntegerType(), getWeaverEngine(), AType.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxFunctionType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxFunctionType.java index b2f215ca38..e7bece6fd4 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxFunctionType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxFunctionType.java @@ -20,43 +20,38 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AFunctionType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxFunctionType extends AFunctionType { - - private final FunctionType type; +public class CxxFunctionType> extends AFunctionType { public CxxFunctionType(FunctionType type, CxxWeaver weaver) { - super(new CxxType(type, weaver), weaver); - this.type = type; + super(type, weaver); } @Override - public Type getNode() { - return type; + public FunctionType getNodeImpl() { + return (FunctionType) super.getNodeImpl(); } @Override - public AType getReturnTypeImpl() { - return CxxJoinpoints.create(type.getReturnType(), getWeaverEngine(), AType.class); + public AType getReturnTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getReturnType(), getWeaverEngine(), AType.class); } @Override - public AType[] getParamTypesArrayImpl() { - - return type.getParamTypes().stream() - .map(paramType -> CxxJoinpoints.create(paramType, getWeaverEngine())) - .toArray(size -> new AType[size]); - + public AType[] getParamTypesImpl() { + return this.getNodeImpl().getParamTypes().stream() + .map(paramType -> CxxJoinpoints.create(paramType, getWeaverEngine(), AType.class)) + .toArray(AType[]::new); } @Override - public void setReturnTypeImpl(AType newType) { - Type newClavaType = (Type) newType.getNode(); - type.set(FunctionType.RETURN_TYPE, newClavaType); + public void setReturnTypeImpl(AType newType) { + Type newClavaType = (Type) newType.getNodeImpl(); + this.getNodeImpl().set(FunctionType.RETURN_TYPE, newClavaType); } @Override - public void setParamTypeImpl(int index, AType newType) { - type.setParamType(index, (Type) newType.getNode()); + public void setParamTypeImpl(int index, AType newType) { + this.getNodeImpl().setParamType(index, (Type) newType.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxIncompleteArrayType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxIncompleteArrayType.java index 23e1029d26..4fd5ede498 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxIncompleteArrayType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxIncompleteArrayType.java @@ -13,24 +13,19 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.IncompleteArrayType; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AIncompleteArrayType; -public class CxxIncompleteArrayType extends AIncompleteArrayType { - - private final IncompleteArrayType arrayType; +public class CxxIncompleteArrayType> extends AIncompleteArrayType { public CxxIncompleteArrayType(IncompleteArrayType arrayType, CxxWeaver weaver) { - super(new CxxArrayType(arrayType, weaver), weaver); - - this.arrayType = arrayType; + super(arrayType, weaver); } @Override - public ClavaNode getNode() { - return arrayType; + public IncompleteArrayType getNodeImpl() { + return (IncompleteArrayType) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxParenType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxParenType.java index 5c1c07265c..4e50c9af98 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxParenType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxParenType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.ParenType; import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,29 +20,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AParenType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxParenType extends AParenType { - - private final ParenType parenType; +public class CxxParenType> extends AParenType { public CxxParenType(ParenType parenType, CxxWeaver weaver) { - super(new CxxType(parenType, weaver), weaver); - - this.parenType = parenType; + super(parenType, weaver); } @Override - public ClavaNode getNode() { - return parenType; + public ParenType getNodeImpl() { + return (ParenType) super.getNodeImpl(); } @Override - public AType getInnerTypeImpl() { - return CxxJoinpoints.create(parenType.getInnerType(), getWeaverEngine(), AType.class); + public AType getInnerTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getInnerType(), getWeaverEngine(), AType.class); } @Override - public void setInnerTypeImpl(AType innerType) { - var newType = (Type) innerType.getNode(); - parenType.setInnerType(newType); + public void setInnerTypeImpl(AType innerType) { + var newType = (Type) innerType.getNodeImpl(); + this.getNodeImpl().setInnerType(newType); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxPointerType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxPointerType.java index e2a6baedf0..78bb606ca5 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxPointerType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxPointerType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.PointerType; import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,34 +20,30 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.APointerType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxPointerType extends APointerType { - - private final PointerType pointerType; +public class CxxPointerType> extends APointerType { public CxxPointerType(PointerType pointerType, CxxWeaver weaver) { - super(new CxxType(pointerType, weaver), weaver); - - this.pointerType = pointerType; + super(pointerType, weaver); } @Override - public ClavaNode getNode() { - return pointerType; + public PointerType getNodeImpl() { + return (PointerType) super.getNodeImpl(); } @Override - public AType getPointeeImpl() { - return CxxJoinpoints.create(pointerType.getPointeeType(), getWeaverEngine(), AType.class); + public AType getPointeeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getPointeeType(), getWeaverEngine(), AType.class); } @Override - public Integer getPointerLevelsImpl() { - return pointerType.getPointerLevels(); + public int getPointerLevelsImpl() { + return this.getNodeImpl().getPointerLevels(); } @Override - public void setPointeeImpl(AType pointeeType) { - pointerType.set(PointerType.POINTEE_TYPE, (Type) pointeeType.getNode()); + public void setPointeeImpl(AType pointeeType) { + this.getNodeImpl().set(PointerType.POINTEE_TYPE, (Type) pointeeType.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxQualType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxQualType.java index d9302ee271..94078ce1cb 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxQualType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxQualType.java @@ -13,35 +13,30 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.QualType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AQualType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxQualType extends AQualType { - - private final QualType qualType; +public class CxxQualType> extends AQualType { public CxxQualType(QualType qualType, CxxWeaver weaver) { - super(new CxxType(qualType, weaver), weaver); - - this.qualType = qualType; + super(qualType, weaver); } @Override - public ClavaNode getNode() { - return qualType; + public QualType getNodeImpl() { + return (QualType) super.getNodeImpl(); } @Override - public String[] getQualifiersArrayImpl() { - return qualType.getQualifierStrings().toArray(new String[0]); + public String[] getQualifiersImpl() { + return this.getNodeImpl().getQualifierStrings().toArray(new String[0]); } @Override - public AType getUnqualifiedTypeImpl() { - return CxxJoinpoints.create(qualType.getUnqualifiedType(), getWeaverEngine(), AType.class); + public AType getUnqualifiedTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getUnqualifiedType(), getWeaverEngine(), AType.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTagType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTagType.java index dcb21a6bd9..9f687653b9 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTagType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTagType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.TagDecl; import pt.up.fe.specs.clava.ast.type.TagType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,28 +20,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ADecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATagType; -public class CxxTagType extends ATagType { - private final TagType tagType; +public class CxxTagType> extends ATagType { public CxxTagType(TagType tagType, CxxWeaver weaver) { - super(new CxxType(tagType, weaver), weaver); - - this.tagType = tagType; + super(tagType, weaver); } @Override - public ClavaNode getNode() { - return tagType; + public TagType getNodeImpl() { + return (TagType) super.getNodeImpl(); } @Override public String getNameImpl() { - return tagType.get(TagType.DECL).get(TagDecl.DECL_NAME); + return this.getNodeImpl().get(TagType.DECL).get(TagDecl.DECL_NAME); } @Override - public ADecl getDeclImpl() { - return CxxJoinpoints.create(tagType.getDecl(), getWeaverEngine(), ADecl.class); + public ADecl getDeclImpl() { + return CxxJoinpoints.create(this.getNodeImpl().getDecl(), getWeaverEngine(), ADecl.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTemplateSpecializationType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTemplateSpecializationType.java index 0e14b963ff..04e0c2055a 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTemplateSpecializationType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTemplateSpecializationType.java @@ -16,7 +16,6 @@ import java.util.List; import pt.up.fe.specs.clava.ClavaLog; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.type.TemplateSpecializationType; import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -24,42 +23,38 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATemplateSpecializationType; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxTemplateSpecializationType extends ATemplateSpecializationType { - private final TemplateSpecializationType templateSpecializationType; +public class CxxTemplateSpecializationType> extends ATemplateSpecializationType { public CxxTemplateSpecializationType(TemplateSpecializationType templateSpecializationType, CxxWeaver weaver) { - - super(new CxxType(templateSpecializationType, weaver), weaver); - - this.templateSpecializationType = templateSpecializationType; + super(templateSpecializationType, weaver); } @Override - public ClavaNode getNode() { - return templateSpecializationType; + public TemplateSpecializationType getNodeImpl() { + return (TemplateSpecializationType) super.getNodeImpl(); } @Override public String getTemplateNameImpl() { - return templateSpecializationType.getTemplateName(); + return this.getNodeImpl().getTemplateName(); } @Override - public Integer getNumArgsImpl() { - return templateSpecializationType.getTemplateArguments().size(); + public int getNumArgsImpl() { + return this.getNodeImpl().getTemplateArguments().size(); } @Override - public String[] getArgsArrayImpl() { - return templateSpecializationType.getTemplateArgumentStrings(null).toArray(new String[0]); + public String[] getArgsImpl() { + return this.getNodeImpl().getTemplateArgumentStrings(null).toArray(new String[0]); } @Override - public AType getFirstArgTypeImpl() { + public AType getFirstArgTypeImpl() { ClavaLog.deprecated( "$templateSpecializationType.firstArgType is deprecated, please use $type.templateArgTypes"); - List templateArgTypes = templateSpecializationType.getTemplateArgumentTypes(); + List templateArgTypes = this.getNodeImpl().getTemplateArgumentTypes(); if (templateArgTypes.isEmpty()) { return null; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java index 33c0076d4c..4d2a820aaf 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java @@ -35,110 +35,93 @@ import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -public class CxxType extends AType { - - private final Type type; +public class CxxType> extends AType { public CxxType(Type type, CxxWeaver weaver) { - super(weaver); - this.type = type; + super(type, weaver); } @Override - public Type getNode() { - return type; + public Type getNodeImpl() { + return (Type) super.getNodeImpl(); } @Override - public Boolean getIsArrayImpl() { - return type.isArray(); - // return type instanceof ArrayType; + public boolean getIsArrayImpl() { + return this.getNodeImpl().isArray(); } @Override - public Integer getArraySizeImpl() { - if (!(type instanceof ConstantArrayType)) { + public int getArraySizeImpl() { + if (!(this.getNodeImpl() instanceof ConstantArrayType)) { return -1; } - return ((ConstantArrayType) type).getArraySize(); + return ((ConstantArrayType) this.getNodeImpl()).getArraySize(); } @Override - public int[] getArrayDimsArrayImpl() { - if (!(type instanceof ArrayType)) { + public int[] getArrayDimsImpl() { + if (!(this.getNodeImpl() instanceof ArrayType)) { return new int[0]; } - return ((ArrayType) type).getArrayDims().stream().mapToInt(Integer::intValue).toArray(); + return ((ArrayType) this.getNodeImpl()).getArrayDims().stream().mapToInt(Integer::intValue).toArray(); } - /* - @Override - public AJoinPoint getElementTypeImpl() { - if (type instanceof ArrayType) { - return CxxJoinpoints.create(((ArrayType) type).getElementType(), this); - } - - return this; - } - */ @Override - public Boolean getHasTemplateArgsImpl() { - return type.hasTemplateArgs(); + public boolean getHasTemplateArgsImpl() { + return this.getNodeImpl().hasTemplateArgs(); } @Override - public String[] getTemplateArgsStringsArrayImpl() { - return type.getTemplateArgumentStrings(null).toArray(new String[0]); + public String[] getTemplateArgsStringsImpl() { + return this.getNodeImpl().getTemplateArgumentStrings(null).toArray(new String[0]); } @Override - public Boolean getHasSugarImpl() { - // return type.getTypeData().hasSugar(); - return type.hasSugar(); - + public boolean getHasSugarImpl() { + return this.getNodeImpl().hasSugar(); } @Override - public AType getDesugarImpl() { - return CxxJoinpoints.create(type.desugar(), getWeaverEngine(), AType.class); + public AType getDesugarImpl() { + return CxxJoinpoints.create(this.getNodeImpl().desugar(), getWeaverEngine(), AType.class); } @Override - public AType getDesugarAllImpl() { - return CxxJoinpoints.create(type.desugarAll(), getWeaverEngine(), AType.class); + public AType getDesugarAllImpl() { + return CxxJoinpoints.create(this.getNodeImpl().desugarAll(), getWeaverEngine(), AType.class); } @Override - public void setDesugarImpl(AType desugaredType) { - type.setDesugar((Type) desugaredType.getNode()); + public void setDesugarImpl(AType desugaredType) { + this.getNodeImpl().setDesugar((Type) desugaredType.getNodeImpl()); } @Override - public Boolean getIsBuiltinImpl() { - return type instanceof BuiltinType; + public boolean getIsBuiltinImpl() { + return this.getNodeImpl() instanceof BuiltinType; } @Override - public Boolean getConstantImpl() { - return type.isConst(); + public boolean getConstantImpl() { + return this.getNodeImpl().isConst(); } @Override public String getKindImpl() { - return type.getNodeName(); + return this.getNodeImpl().getNodeName(); } @Override - public Boolean getIsPointerImpl() { - return type.isPointer(); - // return type instanceof PointerType; + public boolean getIsPointerImpl() { + return this.getNodeImpl().isPointer(); } @Override - public AType getUnwrapImpl() { - Type unwrappedType = Types.getSingleElement(type); + public AType getUnwrapImpl() { + Type unwrappedType = Types.getSingleElement(this.getNodeImpl()); if (unwrappedType == null) { return null; @@ -148,51 +131,51 @@ public AType getUnwrapImpl() { } @Override - public Boolean getIsTopLevelImpl() { + public boolean getIsTopLevelImpl() { // Type is top-level if it has not parent - return !type.hasParent(); + return !this.getNodeImpl().hasParent(); } @Override - public AType[] getTemplateArgsTypesArrayImpl() { - return type.getTemplateArgumentTypes().stream() + public AType[] getTemplateArgsTypesImpl() { + return this.getNodeImpl().getTemplateArgumentTypes().stream() .map(argType -> CxxJoinpoints.create(argType, getWeaverEngine(), AType.class)) - .toArray(size -> new AType[size]); + .toArray(AType[]::new); } @Override - public void setTemplateArgsTypesImpl(AType[] templateArgTypes) { + public void setTemplateArgsTypesImpl(AType[] templateArgTypes) { List argTypes = Arrays.stream( templateArgTypes) - .map(aType -> (Type) aType.getNode()) + .map(aType -> (Type) aType.getNodeImpl()) .collect(Collectors.toList()); - type.setTemplateArgumentTypes(argTypes); + this.getNodeImpl().setTemplateArgumentTypes(argTypes); } @Override - public void setTemplateArgTypeImpl(int index, AType templateArgType) { - type.setTemplateArgumentType(index, (Type) templateArgType.getNode()); + public void setTemplateArgTypeImpl(int index, AType templateArgType) { + this.getNodeImpl().setTemplateArgumentType(index, (Type) templateArgType.getNodeImpl()); } @Override - public AType getNormalizeImpl() { - return CxxJoinpoints.create(type.normalize(), getWeaverEngine(), AType.class); + public AType getNormalizeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().normalize(), getWeaverEngine(), AType.class); } @Override - public Map getTypeFieldsImpl() { - Map typeFields = new HashMap<>(); + public Map> getTypeFieldsImpl() { + Map> typeFields = new HashMap<>(); - List> keys = type.getAllKeysWithNodes(); + List> keys = this.getNodeImpl().getAllKeysWithNodes(); for (DataKey key : keys) { - if (!type.hasValue(key)) { + if (!this.getNodeImpl().hasValue(key)) { continue; } - List values = type.getClavaNode(key); + List values = this.getNodeImpl().getClavaNode(key); // Skip fields that contain more than one node if (values.size() != 1) { @@ -215,34 +198,28 @@ public boolean setTypeFieldByValueRecursiveImpl(Object currentValue, Object newV return setTypeFieldByValueRecursiveImpl(this, currentValue, newValue, new HashSet<>()); } - private static boolean setTypeFieldByValueRecursiveImpl(AType type, Object currentValue, Object newValue, + private static boolean setTypeFieldByValueRecursiveImpl(AType type, Object currentValue, Object newValue, Set checkedNodes) { // If already visited this node, return false - if (checkedNodes.contains(type.getNode())) { + if (checkedNodes.contains(type.getNodeImpl())) { return false; } // Otherwise, add current node else { - checkedNodes.add((Type) type.getNode()); + checkedNodes.add((Type) type.getNodeImpl()); } // Get keys with type fields - @SuppressWarnings("unchecked") - Map typeFields = (Map) type.getTypeFieldsImpl(); + Map> typeFields = (Map>) type.getTypeFieldsImpl(); - List visitedTypes = new ArrayList<>(); + List> visitedTypes = new ArrayList<>(); // Iterate over each type field - for (Entry entry : typeFields.entrySet()) { + for (Entry> entry : typeFields.entrySet()) { // Found value to change, change it and return if (entry.getValue().equals(currentValue)) { - // System.out.println("SETTING " + newValue.getClass() + " to " + entry); - // System.out.println( - // "1.Replacing " + entry.getKey() + " with value " + entry.getValue().getNode().toTree() - // + " with " - // + ((AType) newValue).getNode().toTree()); type.setValueImpl(entry.getKey(), newValue); return true; } @@ -252,16 +229,13 @@ private static boolean setTypeFieldByValueRecursiveImpl(AType type, Object curre // Did not find a key in the current node, call the function recursively on a copy of the visited fields // If a field is changed, update it - for (Entry entry : typeFields.entrySet()) { - AType fieldTypeCopy = (AType) entry.getValue().copy(); + for (Entry> entry : typeFields.entrySet()) { + AType fieldTypeCopy = (AType) entry.getValue().copy(); boolean changedField = setTypeFieldByValueRecursiveImpl(fieldTypeCopy, currentValue, newValue, checkedNodes); // Update field if (changedField) { - // System.out.println( - // "2.Replacing " + entry.getKey() + " with value " + entry.getValue().getNode().toTree() - // + " with " + fieldTypeCopy.getNode().toTree()); type.setValue(entry.getKey(), fieldTypeCopy); return true; } @@ -271,22 +245,22 @@ private static boolean setTypeFieldByValueRecursiveImpl(AType type, Object curre @Override public String getFieldTreeImpl() { - return type.toFieldTree(); + return this.getNodeImpl().toFieldTree(); } @Override - public AType setUnderlyingTypeImpl(AType oldValue, AType newValue) { - return CxxJoinpoints.create(type.setUnderlyingType((Type) oldValue.getNode(), (Type) newValue.getNode()), + public AType setUnderlyingTypeImpl(AType oldValue, AType newValue) { + return CxxJoinpoints.create(this.getNodeImpl().setUnderlyingType((Type) oldValue.getNodeImpl(), (Type) newValue.getNodeImpl()), getWeaverEngine(), AType.class); } @Override - public Boolean getIsAutoImpl() { - return type.isAuto(); + public boolean getIsAutoImpl() { + return this.getNodeImpl().isAuto(); } @Override - public AType asConstImpl() { - return CxxJoinpoints.create(type.asConst(), getWeaverEngine(), AType.class); + public AType asConstImpl() { + return CxxJoinpoints.create(this.getNodeImpl().asConst(), getWeaverEngine(), AType.class); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTypedefType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTypedefType.java index 8e6f07a384..879b197468 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTypedefType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxTypedefType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.decl.TypedefNameDecl; import pt.up.fe.specs.clava.ast.type.TypedefType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -22,28 +21,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATypedefNameDecl; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.ATypedefType; -public class CxxTypedefType extends ATypedefType { - - private final TypedefType typedefType; +public class CxxTypedefType> extends ATypedefType { public CxxTypedefType(TypedefType typedefType, CxxWeaver weaver) { - super(new CxxType(typedefType, weaver), weaver); - this.typedefType = typedefType; + super(typedefType, weaver); } @Override - public ATypedefNameDecl getDeclImpl() { - return CxxJoinpoints.create(typedefType.get(TypedefType.DECL), getWeaverEngine(), ATypedefNameDecl.class); + public TypedefType getNodeImpl() { + return (TypedefType) super.getNodeImpl(); } @Override - public ClavaNode getNode() { - return typedefType; + public ATypedefNameDecl getDeclImpl() { + return CxxJoinpoints.create(this.getNodeImpl().get(TypedefType.DECL), getWeaverEngine(), ATypedefNameDecl.class); } @Override - public AType getUnderlyingTypeImpl() { - return CxxJoinpoints.create(typedefType.get(TypedefType.DECL).get(TypedefNameDecl.UNDERLYING_TYPE), + public AType getUnderlyingTypeImpl() { + return CxxJoinpoints.create(this.getNodeImpl().get(TypedefType.DECL).get(TypedefNameDecl.UNDERLYING_TYPE), getWeaverEngine(), AType.class); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxUndefinedType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxUndefinedType.java index 57af710509..c78515c36d 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxUndefinedType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxUndefinedType.java @@ -14,23 +14,18 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; import pt.up.fe.specs.clava.ast.type.NullType; -import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.weaver.CxxWeaver; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AUndefinedType; -public class CxxUndefinedType extends AUndefinedType { - - private final NullType nullType; +public class CxxUndefinedType> extends AUndefinedType { public CxxUndefinedType(NullType nullType, CxxWeaver weaver) { - super(new CxxType(nullType, weaver), weaver); - - this.nullType = nullType; + super(nullType, weaver); } @Override - public Type getNode() { - return nullType; + public NullType getNodeImpl() { + return (NullType) super.getNodeImpl(); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxVariableArrayType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxVariableArrayType.java index 379f321971..b2230051e1 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxVariableArrayType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxVariableArrayType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.expr.Expr; import pt.up.fe.specs.clava.ast.type.VariableArrayType; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; @@ -21,29 +20,25 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AExpression; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AVariableArrayType; -public class CxxVariableArrayType extends AVariableArrayType { - - private final VariableArrayType arrayType; +public class CxxVariableArrayType> extends AVariableArrayType { public CxxVariableArrayType(VariableArrayType arrayType, CxxWeaver weaver) { - super(new CxxArrayType(arrayType, weaver), weaver); - - this.arrayType = arrayType; + super(arrayType, weaver); } @Override - public ClavaNode getNode() { - return arrayType; + public VariableArrayType getNodeImpl() { + return (VariableArrayType) super.getNodeImpl(); } @Override - public AExpression getSizeExprImpl() { - return CxxJoinpoints.create(arrayType.get(VariableArrayType.SIZE_EXPR), getWeaverEngine(), AExpression.class); + public AExpression getSizeExprImpl() { + return CxxJoinpoints.create(this.getNodeImpl().get(VariableArrayType.SIZE_EXPR), getWeaverEngine(), AExpression.class); } @Override - public void setSizeExprImpl(AExpression sizeExpr) { - arrayType.set(VariableArrayType.SIZE_EXPR, (Expr) sizeExpr.getNode()); + public void setSizeExprImpl(AExpression sizeExpr) { + this.getNodeImpl().set(VariableArrayType.SIZE_EXPR, (Expr) sizeExpr.getNodeImpl()); } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaDirective.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaDirective.java index 3750c25e39..a92fae203d 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaDirective.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaDirective.java @@ -13,10 +13,10 @@ package pt.up.fe.specs.clava.weaver.pragmas; -import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinPoint; +import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AJoinpoint; public interface ClavaDirective { - public void apply(AJoinPoint jp); + public void apply(AJoinpoint jp); } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaPragmas.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaPragmas.java index 87ce085984..e90d7dd30e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaPragmas.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/pragmas/ClavaPragmas.java @@ -23,7 +23,6 @@ import pt.up.fe.specs.clava.ast.pragma.Pragma; import pt.up.fe.specs.clava.weaver.CxxJoinpoints; import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; import pt.up.fe.specs.util.stringparser.StringParser; import pt.up.fe.specs.util.stringparser.StringParsers; @@ -51,7 +50,7 @@ private static void processClavaPragma(Pragma clavaPragma, CxxWeaver weaver) { return; } - ACxxWeaverJoinPoint jp = CxxJoinpoints.create(targetNode.get(), weaver); + var jp = CxxJoinpoints.create(targetNode.get(), weaver); clavaDirective.ifPresent(directive -> directive.apply(jp)); return; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/utils/ClavaAstMethods.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/utils/ClavaAstMethods.java index e178ca99b1..ec355eb567 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/utils/ClavaAstMethods.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/utils/ClavaAstMethods.java @@ -17,7 +17,7 @@ import java.util.function.Function; import org.lara.interpreter.weaver.ast.TreeNodeAstMethods; -import org.lara.interpreter.weaver.interf.JoinPoint; +import org.lara.interpreter.weaver.interf.JoinPoint2; import org.lara.interpreter.weaver.interf.WeaverEngine; import pt.up.fe.specs.clava.ClavaNode; @@ -44,7 +44,7 @@ public class ClavaAstMethods extends TreeNodeAstMethods { } public ClavaAstMethods(WeaverEngine engine, Class nodeClass, - Function toJoinPointFunction, Function toJoinPointNameFunction, + Function> toJoinPointFunction, Function toJoinPointNameFunction, Function> scopeChildrenGetter) { super(engine, nodeClass, toJoinPointFunction, toJoinPointNameFunction, scopeChildrenGetter); From 5d4d02f247aa282bd3df1ed76d708ec84090b20f Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 24 May 2026 00:31:10 +0100 Subject: [PATCH 02/99] Refactor ACxxWeaverJoinPoint into CxxJoinpoint, simplifying the hierarchy and the generator. --- .../fe/specs/clava/weaver/CxxJoinpoints.java | 4 +-- .../CxxJoinpoint.java} | 11 ++++---- .../weaver/joinpoints/GenericJoinpoint.java | 26 ------------------- 3 files changed, 8 insertions(+), 33 deletions(-) rename ClavaWeaver/src/pt/up/fe/specs/clava/weaver/{abstracts/ACxxWeaverJoinPoint.java => joinpoints/CxxJoinpoint.java} (98%) delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java index c37be1c142..4c3c7c8d1b 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java @@ -138,7 +138,7 @@ public class CxxJoinpoints { JOINPOINT_FACTORY.put(CilkFor.class, CxxCilkFor::new); JOINPOINT_FACTORY.put(CilkSync.class, CxxCilkSync::new); JOINPOINT_FACTORY.put(CilkSpawn.class, CxxCilkSpawn::new); - JOINPOINT_FACTORY.put(TagDeclVars.class, GenericJoinpoint::new); + JOINPOINT_FACTORY.put(TagDeclVars.class, CxxJoinpoint::new); JOINPOINT_FACTORY.put(ClavaNode.class, CxxJoinpoints::defaultFactory); } @@ -165,7 +165,7 @@ private static AJoinpoint compoundStmtFactory(CompoundStmt stmt, CxxWeaver we private static AJoinpoint defaultFactory(ClavaNode node, CxxWeaver weaver) { SpecsLogs.warn("Factory not defined for nodes of class '" + node.getClass().getSimpleName() + "'"); - return new GenericJoinpoint<>(node, weaver); + return new CxxJoinpoint<>(node, weaver); } public static AJoinpoint createFromLara(Object node, CxxWeaver weaver) { diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/ACxxWeaverJoinPoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java similarity index 98% rename from ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/ACxxWeaverJoinPoint.java rename to ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java index f0b6624985..de6bb7bee1 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/abstracts/ACxxWeaverJoinPoint.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java @@ -1,4 +1,4 @@ -package pt.up.fe.specs.clava.weaver.abstracts; +package pt.up.fe.specs.clava.weaver.joinpoints; import java.io.File; import java.util.ArrayList; @@ -61,9 +61,9 @@ * Abstract class which can be edited by the developer. * This class will NOT be overwritten by the generator. */ -public abstract class ACxxWeaverJoinPoint> extends AJoinpoint { +public class CxxJoinpoint> extends AJoinpoint { - protected ACxxWeaverJoinPoint(ClavaNode node, ACxxWeaver weaver) { + public CxxJoinpoint(ClavaNode node, ACxxWeaver weaver) { super(node, weaver); } @@ -269,7 +269,7 @@ public AJoinpoint[] insertImpl(InsertPosition position, String code) { } @Override - public AJoinpoint[] insertImpl(String position, JoinPoint2 JoinPoint) { + public AJoinpoint[] insertImpl(InsertPosition position, AJoinpoint node) { throw new NotImplementedException(this); } @@ -710,7 +710,8 @@ public boolean equalsImpl(Self jp) { if (!(jp instanceof AJoinpoint)) { return false; } - return getNode().equals(((AJoinpoint) obj).getNode()); + + return getNodeImpl().equals(((AJoinpoint) jp).getNodeImpl()); } @Override diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java deleted file mode 100644 index e95ab5b081..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/GenericJoinpoint.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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 pt.up.fe.specs.clava.weaver.joinpoints; - -import pt.up.fe.specs.clava.ClavaNode; -import pt.up.fe.specs.clava.weaver.CxxWeaver; -import pt.up.fe.specs.clava.weaver.abstracts.ACxxWeaverJoinPoint; - -public class GenericJoinpoint> extends ACxxWeaverJoinPoint { - - public GenericJoinpoint(ClavaNode node, CxxWeaver weaver) { - super(node, weaver); - } - -} From 8ec2c908c4613835657acdad3c88380106adf722 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 24 May 2026 00:52:29 +0100 Subject: [PATCH 03/99] Remove ACxxWeaverJoinPoint from .gitignore since it no longer exists --- ClavaWeaver/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/ClavaWeaver/.gitignore b/ClavaWeaver/.gitignore index 3ce57ad832..1170455d2e 100644 --- a/ClavaWeaver/.gitignore +++ b/ClavaWeaver/.gitignore @@ -2,7 +2,6 @@ cxx_weaver_output/ AutoParStats-default.json src/**/abstracts/ -!src/**/abstracts/ACxxWeaverJoinPoint.java src/**/exceptions/CxxWeaverException.java src/**/enums/ *.dotty From 0cf6d4ca563a27df0624b8d1040e50cee64665c1 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 24 May 2026 02:05:54 +0100 Subject: [PATCH 04/99] Update CxxSpec to use the new weaverPrefix descriptor --- ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java | 2 +- .../pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java b/ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java index b230065523..c52766bf08 100644 --- a/ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java +++ b/ClavaWeaver/src-spec/pt/up/fe/specs/clava/weaver/CxxSpec.java @@ -13,7 +13,7 @@ public class CxxSpec extends WeaverSpec { @Override public void define() { - weaverName("CxxWeaver"); + weaverPrefix("Cxx"); packageName("pt.up.fe.specs.clava.weaver"); rootJoinPoint("program"); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java index de6bb7bee1..23b769e641 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java @@ -48,7 +48,6 @@ import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AProgram; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AStatement; import pt.up.fe.specs.clava.weaver.abstracts.joinpoints.AType; -import pt.up.fe.specs.clava.weaver.abstracts.weaver.ACxxWeaver; import pt.up.fe.specs.clava.weaver.importable.AstFactory; import pt.up.fe.specs.clava.weaver.importable.LowLevelApi; import pt.up.fe.specs.util.SpecsLogs; @@ -63,7 +62,7 @@ */ public class CxxJoinpoint> extends AJoinpoint { - public CxxJoinpoint(ClavaNode node, ACxxWeaver weaver) { + public CxxJoinpoint(ClavaNode node, CxxWeaver weaver) { super(node, weaver); } From f84aaf8f0461ced4bc86df3f0770404896729fcd Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 24 May 2026 16:33:16 +0100 Subject: [PATCH 05/99] Refactor joinpoint methods to use *Impl methods for consistency --- .../specs/clava/weaver/actions/CallWrap.java | 6 ++-- .../weaver/joinpoints/CxxExpression.java | 2 +- .../clava/weaver/joinpoints/CxxFile.java | 10 +++--- .../clava/weaver/joinpoints/CxxJoinpoint.java | 36 +++++++++---------- .../clava/weaver/joinpoints/CxxLoop.java | 2 +- .../specs/clava/weaver/joinpoints/CxxOmp.java | 2 +- .../clava/weaver/joinpoints/CxxProgram.java | 4 +-- .../clava/weaver/joinpoints/CxxScope.java | 4 +-- .../clava/weaver/joinpoints/CxxThis.java | 2 +- .../weaver/joinpoints/types/CxxEnumType.java | 2 +- .../weaver/joinpoints/types/CxxType.java | 19 +++++----- 11 files changed, 43 insertions(+), 46 deletions(-) diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java index 1526bc539f..3c35eb5214 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/actions/CallWrap.java @@ -102,12 +102,12 @@ public void addWrapper(String name) { break; case NO_INCLUDE: addWrapperFunctionInPlace(name, false); - cxxCall.setName(name); // need to call this here before returning + cxxCall.setNameImpl(name); // need to call this here before returning return; case DECLARATION_IN_IMPLEMENTATION: addWrapperFunctionInPlace(name, true); - cxxCall.setName(name); + cxxCall.setNameImpl(name); return; } @@ -119,7 +119,7 @@ public void addWrapper(String name) { cxxCall.getNodeImpl().getAncestor(TranslationUnit.class).addInclude(includePath, false); // Set call name - cxxCall.setName(name); + cxxCall.setNameImpl(name); } /** diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java index 1128ef13dd..d89bbf0fe6 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxExpression.java @@ -45,7 +45,7 @@ public Expr getNodeImpl() { @Override public AVardecl getVardeclImpl() { // Get more specific join point for current node - SpecsLogs.msgInfo("attribute 'vardecl' not implemented yet for joinpoint " + joinPointType()); + SpecsLogs.msgInfo("attribute 'vardecl' not implemented yet for joinpoint " + getJoinPointTypeImpl()); return null; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java index 1c8c47faa8..0f74499892 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java @@ -111,7 +111,7 @@ public AJoinpoint insertAfterImpl(AJoinpoint node) { // Check node is a decl if (!(node.getNodeImpl() instanceof Decl)) { SpecsLogs.msgInfo( - "Can only insert Decl nodes in a file, tried to insert a '" + node.joinPointType() + "'"); + "Can only insert Decl nodes in a file, tried to insert a '" + node.getJoinPointTypeImpl() + "'"); return null; } @@ -126,7 +126,7 @@ public AJoinpoint insertBeforeImpl(AJoinpoint node) { // Check node is a decl if (node.getNodeImpl() instanceof Decl) { SpecsLogs.msgInfo( - "Can only insert Decl nodes in a file, tried to insert a '" + node.joinPointType() + "'"); + "Can only insert Decl nodes in a file, tried to insert a '" + node.getJoinPointTypeImpl() + "'"); return null; } @@ -144,7 +144,7 @@ public String getPathImpl() { public void addIncludeJpImpl(AJoinpoint jp) { // If jp is a function, include declaration if available - if (jp.instanceOf("function")) { + if (jp.instanceOfImpl("function")) { AFunction functionJp = (AFunction) jp; AJoinpoint decl = functionJp.getDeclarationJpImpl(); jp = decl != null ? decl : jp; @@ -201,7 +201,7 @@ public AVardecl addGlobalImpl(String name, AJoinpoint type, String initVal // Check if joinpoint is a CxxType if (!(type instanceof AType)) { - SpecsLogs.msgInfo("addGlobal: the provided join point (" + type.joinPointType() + ") is not a type"); + SpecsLogs.msgInfo("addGlobal: the provided join point (" + type.getJoinPointTypeImpl() + ") is not a type"); return null; } @@ -307,7 +307,7 @@ public AFile rebuildImpl() { TranslationUnit rebuiltTunit = getWeaverEngine().rebuildFile(this.getNodeImpl()); AFile rebuiltFile = CxxJoinpoints.create(rebuiltTunit, getWeaverEngine(), AFile.class); - replaceWith(rebuiltFile); + replaceWithImpl(rebuiltFile); return rebuiltFile; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java index 23b769e641..d84d34df42 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java @@ -137,7 +137,7 @@ public AJoinpoint getGetAncestorImpl(String type) { // Create join point for testing type AJoinpoint parentJp = CxxJoinpoints.create(currentNode.getParent(), getWeaverEngine()); - if (parentJp.instanceOf(type)) { + if (parentJp.instanceOfImpl(type)) { return parentJp; } @@ -151,7 +151,7 @@ public AJoinpoint getGetAncestorImpl(String type) { public AJoinpoint[] getGetDescendantsImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of descendants in attribute 'descendants'"); - return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsStream(), jp -> jp.instanceOf(type), + return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsStream(), jp -> jp.instanceOfImpl(type), getWeaverEngine()); } @@ -164,7 +164,7 @@ public AJoinpoint[] getDescendantsImpl() { public AJoinpoint[] getGetDescendantsAndSelfImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of descendants in attribute 'descendants'"); - return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsAndSelfStream(), jp -> jp.instanceOf(type), + return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsAndSelfStream(), jp -> jp.instanceOfImpl(type), getWeaverEngine()); } @@ -179,7 +179,7 @@ public AJoinpoint getGetChainAncestorImpl(String type) { AJoinpoint currentJp = this; while (currentJp.getHasParentImpl()) { var parentJp = currentJp.getParentImpl(); - if (parentJp.instanceOf(type)) { + if (parentJp.instanceOfImpl(type)) { return parentJp; } @@ -363,7 +363,7 @@ private AJoinpoint toJpToBeInserted(String code) { throw new RuntimeException( "Inserting before/after a loop header statement only support for 'declStmt' and 'exprStmt', this is a " - + joinPointType()); + + getJoinPointTypeImpl()); } @@ -392,7 +392,7 @@ public AJoinpoint replaceWithImpl(AJoinpoint[] node) { } // Remove current node from the tree - detach(); + detachImpl(); // Return the first inserted element return topInserted; @@ -410,7 +410,7 @@ public AJoinpoint replaceWithStringsImpl(String[] node) { } // Remove current node from the tree - detach(); + detachImpl(); // Return the first inserted element return topInserted; @@ -422,7 +422,7 @@ public AJoinpoint detachImpl() { if (!node.hasParent()) { SpecsLogs.msgInfo( - "action detach: could not find a parent in joinpoint of type '" + joinPointType() + "'"); + "action detach: could not find a parent in joinpoint of type '" + getJoinPointTypeImpl() + "'"); return this; } @@ -441,7 +441,7 @@ public AType getTypeImpl() { ClavaNode node = getNodeImpl(); if (!(node instanceof Typable)) { - SpecsLogs.msgInfo("Joinpoint of type '" + joinPointType() + "' with node '" + node.getNodeName() + SpecsLogs.msgInfo("Joinpoint of type '" + getJoinPointTypeImpl() + "' with node '" + node.getNodeName() + "' does not have a type"); return null; } @@ -678,7 +678,7 @@ public Object setUserFieldImpl(Map fieldNameAndValue) { Object lastPrevious = null; for (Entry entry : fieldNameAndValue.entrySet()) { - lastPrevious = setUserField(entry.getKey(), entry.getValue()); + lastPrevious = setUserFieldImpl(entry.getKey(), entry.getValue()); } return lastPrevious; @@ -697,7 +697,7 @@ public AJoinpoint getCurrentRegionImpl() { if (!currentRegionTry.isPresent()) { ClavaLog.info( - "Join point '" + joinPointType() + "'@" + getLocationImpl() + " does not support currentRegion"); + "Join point '" + getJoinPointTypeImpl() + "'@" + getLocationImpl() + " does not support currentRegion"); return null; } @@ -879,7 +879,7 @@ public String[] getKeysImpl() { public Object getGetValueImpl(String key) { var keys = getNodeImpl().getStoreDefinition(); if (!keys.hasKey(key)) { - ClavaLog.info("getValue(): key '" + key + "' not supported for join point '" + joinPointType() + "'"); + ClavaLog.info("getValue(): key '" + key + "' not supported for join point '" + getJoinPointTypeImpl() + "'"); return null; } @@ -897,13 +897,13 @@ public AJoinpoint setValueImpl(String key, Object value) { DataKey datakey = getNodeImpl().getStoreDefinition().getKeyRaw(key); // If string, use decoder - if (value instanceof String) { - value = datakey.decode((String) value); + if (value instanceof String str) { + value = datakey.decode(str); } // If join point, use underlying node - if (value instanceof AJoinpoint) { - value = ((AJoinpoint) value).getNodeImpl(); + if (value instanceof AJoinpoint jp) { + value = jp.getNodeImpl(); } // Adapt to optional, if needed @@ -982,7 +982,7 @@ public AJoinpoint setFirstChildImpl(AJoinpoint value) { // Otherwise, replace node var firstChild = getFirstChildImpl(); - firstChild.replaceWith(value); + firstChild.replaceWithImpl(value); return firstChild; } @@ -1009,7 +1009,7 @@ public AJoinpoint setLastChildImpl(AJoinpoint value) { // Otherwise, replace node var lastChild = getLastChildImpl(); - lastChild.replaceWith(value); + lastChild.replaceWithImpl(value); return lastChild; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java index 917e6eaf45..42881c9c9e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxLoop.java @@ -227,7 +227,7 @@ private void convertToWhile() { Stmt cond = ((ForStmt) this.getNodeImpl()).getCond().orElse(getWeaverEngine().getFactory().nullStmt()); WhileStmt whileStmt = getWeaverEngine().getFactory().whileStmt(cond, this.getNodeImpl().getBody()); - replaceWith(CxxJoinpoints.create(whileStmt, getWeaverEngine())); + replaceWithImpl(CxxJoinpoints.create(whileStmt, getWeaverEngine())); return; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java index fcdc5dcda4..d13809b147 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java @@ -200,7 +200,7 @@ public void setScheduleChunkSizeImpl(String chunkSize) { @Override public void setScheduleChunkSizeImpl(int chunkSize) { - setScheduleChunkSize(Integer.toString(chunkSize)); + this.setScheduleChunkSizeImpl(Integer.toString(chunkSize)); } @Override diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java index 956b577174..139d5b060a 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxProgram.java @@ -279,12 +279,12 @@ public void atexitImpl(AFunction function) { getFactory().builtinType("void")); // Insert call at the beginning of the main function - mainFunction.getBodyImpl().insertBegin(CxxJoinpoints.create(atexitCall, getWeaverEngine())); + mainFunction.getBodyImpl().insertBeginImpl(CxxJoinpoints.create(atexitCall, getWeaverEngine())); // Add include for atexit AFile file = (AFile) mainFunction.getGetAncestorImpl("file"); Objects.requireNonNull(file, () -> "Expected main function to be inside a file: " + mainFunction.getNodeImpl()); - file.addInclude("stdlib.h", true); + file.addIncludeImpl("stdlib.h", true); // Add include for function file.addIncludeJpImpl(function); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java index 6c8906ea05..2fac562db3 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxScope.java @@ -180,7 +180,7 @@ public AJoinpoint addLocalImpl(String name, AJoinpoint type, String initVa // Check if joinpoint is a CxxType if (!(type instanceof AType)) { - SpecsLogs.msgInfo("addLocal: the provided join point (" + type.joinPointType() + ") is not a type"); + SpecsLogs.msgInfo("addLocal: the provided join point (" + type.getJoinPointTypeImpl() + ") is not a type"); return null; } @@ -201,7 +201,7 @@ public AJoinpoint addLocalImpl(String name, AJoinpoint type, String initVa AJoinpoint varDeclJp = CxxJoinpoints.create(varDecl, getWeaverEngine()); - insertBegin(varDeclJp); + insertBeginImpl(varDeclJp); return varDeclJp; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java index 31cfaf2e6f..cd6e03e1f5 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxThis.java @@ -39,7 +39,7 @@ public ADecl getDeclImpl() { var type = getTypeImpl(); if (!(type instanceof APointerType)) { - throw new RuntimeException("Not implemented with type is " + type.joinPointType()); + throw new RuntimeException("Not implemented with type is " + type.getJoinPointTypeImpl()); } // Get class type diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java index fbc654a5b0..47568bfd9e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxEnumType.java @@ -34,7 +34,7 @@ public EnumType getNodeImpl() { @Override public AType getIntegerTypeImpl() { - if (root() == null) { + if (getRootImpl() == null) { SpecsLogs.msgInfo("Root not defined, is this a detached join point? -> " + this); return null; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java index 4d2a820aaf..d62b5c3eea 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java @@ -13,7 +13,6 @@ package pt.up.fe.specs.clava.weaver.joinpoints.types; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -211,32 +210,30 @@ private static boolean setTypeFieldByValueRecursiveImpl(AType type, Object cu } // Get keys with type fields - Map> typeFields = (Map>) type.getTypeFieldsImpl(); - - List> visitedTypes = new ArrayList<>(); + Map> typeFields = type.getTypeFieldsImpl(); // Iterate over each type field for (Entry> entry : typeFields.entrySet()) { // Found value to change, change it and return - if (entry.getValue().equals(currentValue)) { - type.setValueImpl(entry.getKey(), newValue); - return true; + if (currentValue instanceof CxxType cxxType){ + if (((AType)entry.getValue()).equalsImpl(cxxType)) { + type.setValueImpl(entry.getKey(), newValue); + return true; + } } - - visitedTypes.add(entry.getValue()); } // Did not find a key in the current node, call the function recursively on a copy of the visited fields // If a field is changed, update it for (Entry> entry : typeFields.entrySet()) { - AType fieldTypeCopy = (AType) entry.getValue().copy(); + AType fieldTypeCopy = (AType) entry.getValue().copyImpl(); boolean changedField = setTypeFieldByValueRecursiveImpl(fieldTypeCopy, currentValue, newValue, checkedNodes); // Update field if (changedField) { - type.setValue(entry.getKey(), fieldTypeCopy); + type.setValueImpl(entry.getKey(), fieldTypeCopy); return true; } } From 14fcfec02ca109a14579d7a23ff90c9ce0784c59 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 29 May 2026 17:51:50 +0100 Subject: [PATCH 06/99] Remove Copilot instructions document from the repository --- .github/copilot-instructions.md | 100 -------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 4c5139427c..0000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,100 +0,0 @@ -# Copilot Instructions for the Clava Repository - -## Project Overview - -Clava is a modular source-to-source compiler for C, C++, CUDA, and OpenCL, supporting advanced code analysis and transformation. It is implemented using a combination of TypeScript/JavaScript (Node.js), Java, and C++. Clava is designed for composability and reusability, and integrates with the LARA DSL for custom code transformations. - -## Development Environment & Setup - -- **Node.js Version:** 20 or 22 (required for Clava-JS) -- **Java Version:** 17+ (required for Java components) -- **Build System:** Gradle for Java modules, npm for TypeScript/JavaScript -- **IDE:** VSCode is recommended for development - -## Architecture - -- **Frontend (C++):** - The `ClangAstDumper` component extracts AST information from Clang-based codebases. -- **Middle-end (Java):** - Components like `ClangAstParser` and `ClavaWeaver` process ASTs and apply transformations. -- **API Layer (TypeScript/JavaScript):** - The `Clava-JS` module provides the main user-facing API and runtime, exposing Clava's features to Node.js environments. -- **Build Integration:** - The `CMake` package enables integration with CMake-based build systems. - -### Related Projects -- **lara-framework**: Core framework providing weaver infrastructure and JavaScript APIs -- **specs-java-libs**: Java utility libraries used throughout the project - -## Key Directories - -- `Clava-JS/`: TypeScript/JavaScript API and runtime. -- `ClavaWeaver/`: Java-based weaving engine. -- `ClangAstDumper/`: C++ AST dumper using Clang. -- `ClangAstParser/`: Java AST parser. -- `ClavaAst/`, `ClavaHls/`, `ClavaLaraApi/`, `AntarexClavaApi/`: Supporting modules for AST, HLS, LARA API, and Antarex integration. -- `CMake/`: CMake integration scripts and utilities. -- `docs/`: Documentation, tutorials, and common issues. - -## Build and Development - -- **Java Components:** Use Gradle (`gradle installDist`) to build Java modules (e.g., ClavaWeaver). -- **TypeScript/JavaScript:** Use npm scripts (`npm install`, `npm run build`) in `Clava-JS`. -- **C++ Components:** Use CMake for building and integrating the Clang AST dumper. -- **Integration:** Copy built Java binaries into `Clava-JS/java-binaries` for full functionality. - -## Usage - -- **NPM Package:** - Install globally or as a project dependency: - `npm install -g @specs-feup/clava` -- **CLI:** - Run transformations via `npx clava classic -p ""` -- **CMake Integration:** - Use the `clava_weave` CMake command to apply LARA scripts to targets. - -## Code Patterns and Conventions - -- **Visitor Patterns:** - Used extensively in AST processing (see `ClangAstDumper.h`). -- **TypeScript API:** - Modular, with clear separation between API (`src-api/`) and code (`src-code/`). -- **Java:** - Follows standard Gradle project structure. -- **C++:** - Integrates with Clang/LLVM for AST extraction. - -## Common Development Tasks - -- Add new AST node support in `ClangAstDumper` and propagate through Java and JS layers. -- Extend the TypeScript API in `Clava-JS/src-api/`. -- Create new code transformations as LARA scripts or TypeScript modules. -- Use provided test scripts and npm/Gradle test commands. - -## Dependencies - -- **Node.js 20 or 22** and **Java 17+** required. -- **Clang/LLVM** for AST extraction. -- **NPM** for JS/TS dependencies. -- **Gradle** for Java builds. -- **CMake** for build system integration. - -## Troubleshooting - -- See `docs/common_issues.md` for frequently encountered problems. -- Use the GitHub issue tracker for unresolved issues. - -## References - -- [Clava Documentation](https://specs-feup.github.io/modules/_specs_feup_clava.html) -- [Clava Project Template](https://github.com/specs-feup/clava-project-template) -- [Online Demo](https://specs.fe.up.pt/tools/clava/) -- [Main Repository](https://github.com/specs-feup/clava) - ---- - -**For LLMs:** -- Respect the modular structure and language boundaries. -- When adding features, ensure changes propagate through C++, Java, and JS layers as needed. -- Follow existing patterns for AST traversal and transformation. -- Use the provided build and test scripts for validation. From f2a967feba935198eda43093a62f9e241e40cb70 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 29 May 2026 17:52:20 +0100 Subject: [PATCH 07/99] Add LLM Skill for Clava scripts --- .codex/skills/clava-scripting/SKILL.md | 39 +++++++++++++++ .../skills/clava-scripting/agents/openai.yaml | 4 ++ .../clava-scripting/references/clava-apis.md | 33 +++++++++++++ .../clava-scripting/references/examples.md | 28 +++++++++++ .../clava-scripting/references/query-api.md | 47 +++++++++++++++++++ 5 files changed, 151 insertions(+) create mode 100644 .codex/skills/clava-scripting/SKILL.md create mode 100644 .codex/skills/clava-scripting/agents/openai.yaml create mode 100644 .codex/skills/clava-scripting/references/clava-apis.md create mode 100644 .codex/skills/clava-scripting/references/examples.md create mode 100644 .codex/skills/clava-scripting/references/query-api.md diff --git a/.codex/skills/clava-scripting/SKILL.md b/.codex/skills/clava-scripting/SKILL.md new file mode 100644 index 0000000000..b965f94e8d --- /dev/null +++ b/.codex/skills/clava-scripting/SKILL.md @@ -0,0 +1,39 @@ +--- +name: clava-scripting +description: Create, update, and explain Clava/LARA scripts in TypeScript using Clava-JS and Lara-JS APIs, including Query/Selector usage, joinpoint selection and filters, and AST transformations. Use for Clava script authoring, joinpoint queries, or refactoring code via Clava/Lara weaver APIs. +--- + +# Clava Scripting + +## Overview + +Write and modify Clava scripts in TypeScript using Clava/Lara APIs for joinpoint selection and AST transformations. + +## Quick Start + +Use ESM imports with `.js` extensions, select joinpoints with `Query`, and transform with Clava APIs. + +```ts +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import { FunctionJp } from "@specs-feup/clava/api/Joinpoints.js"; + +const $fn = Query.search(FunctionJp, { isImplementation: true }).first(); +if ($fn) $fn.clone(`${$fn.name}_clone`); +``` + +## Workflow + +1. Identify joinpoints and attributes. +Use the generated joinpoint wrappers in `@specs-feup/clava/api/Joinpoints.js` and check `Joinpoints.ts` for default attributes and available fields. + +2. Select joinpoints with Query/Selector. +Use `Query.search`, `Query.searchFrom`, `Query.searchFromInclusive`, `Query.childrenFrom`, and `Selector.scope`. Filters accept strings, regex, predicate functions, or objects keyed by attributes. `Selector` is iterable and methods like `.get()`, `.first()`, and `.chain()` consume the current selection. + +3. Transform and emit code. +Use joinpoint methods like `.clone()`, `.replaceWith()`, `.addParam()`, `.setReturnType()`, and factories in `ClavaJoinPoints` for new nodes. Use `Query.root().code` or `Clava.writeCode()` to inspect or emit output. + +## References + +- `references/query-api.md` for Query/Selector behavior and filters. +- `references/clava-apis.md` for key Clava/Lara API entry points and file locations. +- `references/examples.md` for real scripts and test patterns. diff --git a/.codex/skills/clava-scripting/agents/openai.yaml b/.codex/skills/clava-scripting/agents/openai.yaml new file mode 100644 index 0000000000..1fbab5a6f5 --- /dev/null +++ b/.codex/skills/clava-scripting/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Clava Scripting" + short_description: "Write and edit Clava/Lara scripts" + default_prompt: "Use $clava-scripting to draft a Clava TypeScript script that queries joinpoints and applies a transformation." diff --git a/.codex/skills/clava-scripting/references/clava-apis.md b/.codex/skills/clava-scripting/references/clava-apis.md new file mode 100644 index 0000000000..4661d47483 --- /dev/null +++ b/.codex/skills/clava-scripting/references/clava-apis.md @@ -0,0 +1,33 @@ +# Clava and Lara API Entry Points + +Use this to locate the right TypeScript APIs and understand where functionality lives. + +## Clava-JS APIs (this repo) + +- Joinpoint wrappers (generated): `Clava-JS/src-api/Joinpoints.ts` +- Joinpoint factories/utilities: `Clava-JS/src-api/clava/ClavaJoinPoints.ts` +- Core Clava utilities and AST stack: `Clava-JS/src-api/clava/Clava.ts` +- Common passes/opts built on Query: `Clava-JS/src-api/clava/opt`, `Clava-JS/src-api/clava/pass` + +Imports typically use: +- `@specs-feup/clava/api/Joinpoints.js` +- `@specs-feup/clava/api/clava/ClavaJoinPoints.js` +- `@specs-feup/clava/api/clava/Clava.js` + +## Lara-JS APIs (sibling repo) + +- Query API: `../lara/Lara-JS/src-api/weaver/Query.ts` +- Selector behavior and filters: `../lara/Lara-JS/src-api/weaver/Selector.ts` +- Weaver utilities: `../lara/Lara-JS/src-api/weaver/Weaver.ts` + +If the Lara-JS repo is not a sibling of Clava, search for `Lara-JS/src-api/weaver/Query.ts`. + +Imports typically use: +- `@specs-feup/lara/api/weaver/Query.js` +- `@specs-feup/lara/api/weaver/Weaver.js` + +## Notes + +- Joinpoint wrappers expose attributes and methods specific to each type. +- `ClavaJoinPoints` provides factory helpers for types, statements, expressions, and declarations. +- Use `.code` on joinpoints (or `Query.root().code`) to inspect generated code quickly. diff --git a/.codex/skills/clava-scripting/references/examples.md b/.codex/skills/clava-scripting/references/examples.md new file mode 100644 index 0000000000..79e9dd6ccc --- /dev/null +++ b/.codex/skills/clava-scripting/references/examples.md @@ -0,0 +1,28 @@ +# Script Examples and Patterns + +Use these files for concrete patterns and idioms. + +## Weaver tests (JS, but patterns apply to TS) + +- `ClavaWeaver/resources/clava/test/weaver/Function2.js` + - Select function, clone it, change return type, replace body, add param. + +- `ClavaWeaver/resources/clava/test/weaver/Clone.js` + - Clone all functions with definitions and print file code. + +- `ClavaWeaver/resources/clava/test/weaver/Field.js` + - Navigate record fields and read attributes like `isPublic`. + +- `ClavaWeaver/resources/clava/test/issues/Issue168.js` + - Normalize loops and decompose statements using `NormalizeToSubset` and `StatementDecomposer`. + +- `ClavaWeaver/resources/clava/test/issues/Issue_aiq_1.js` + - Filter loops by kind, inspect condition relation. + +## API tests + +- `ClavaWeaver/resources/clava/test/api/ClavaJoinPointsTest.js` + - Large catalog of `ClavaJoinPoints` factory helpers. + +- `Clava-JS/src-api/Query.test.ts` + - Query chaining, `.scope()`, `.chain()`, and regex selection. diff --git a/.codex/skills/clava-scripting/references/query-api.md b/.codex/skills/clava-scripting/references/query-api.md new file mode 100644 index 0000000000..1073cbecd1 --- /dev/null +++ b/.codex/skills/clava-scripting/references/query-api.md @@ -0,0 +1,47 @@ +# Query and Selector API + +Use this when writing or debugging joinpoint selection logic. + +## Primary sources + +- Query API: `../lara/Lara-JS/src-api/weaver/Query.ts` (sibling worktree) +- Selector behavior: `../lara/Lara-JS/src-api/weaver/Selector.ts` (sibling worktree) +- Query usage tests: `Clava-JS/src-api/Query.test.ts` + +If the Lara-JS repo is not a sibling of Clava, search for `Lara-JS/src-api/weaver/Query.ts`. + +## Core patterns + +- `Query.root()` returns the root joinpoint. +- `Query.search(Type, filter?, traversal?)` starts from root. +- `Query.searchFrom($base, Type?, filter?, traversal?)` searches below a base node (exclusive). +- `Query.searchFromInclusive($base, Type?, filter?, traversal?)` includes the base node. +- `Query.childrenFrom($base, Type?, filter?)` searches direct children. +- `Selector.scope(Type?, filter?)` searches inside the scope of the previously selected nodes. + +## Filters + +Filters accept: +- A string or regex applied to the default attribute for that joinpoint type. +- A predicate function `(jp) => boolean`. +- An object with attribute names as keys and values of string/regex/predicate. + +Default attributes are defined in the joinpoint wrappers and can be resolved via `Weaver.getDefaultAttribute()`. + +## Selector consumption + +`Selector` is iterable and is consumed by `for..of`, `.get()`, `.first()`, and `.chain()`. +Use `.chain()` when you need the full chain map (e.g., `loop`, `loop_0`, `loop_1`). + +## Minimal examples + +```ts +for (const $fn of Query.search(FunctionJp, { isImplementation: true })) { + // $fn is a joinpoint instance +} + +const chains = Query.search(FunctionJp, "query_loop") + .search(Loop) + .search(Loop) + .chain(); +``` From cc1852f6740aa48b259aee2585e0f1bf6b1643ad Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 29 May 2026 17:52:49 +0100 Subject: [PATCH 08/99] Refactor BatchParser to simplify error handling and remove unused ClavaException import --- Clava-JS/src-api/clava/parser/BatchParser.ts | 21 +++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Clava-JS/src-api/clava/parser/BatchParser.ts b/Clava-JS/src-api/clava/parser/BatchParser.ts index 3b0a1f2da4..bc714a3983 100644 --- a/Clava-JS/src-api/clava/parser/BatchParser.ts +++ b/Clava-JS/src-api/clava/parser/BatchParser.ts @@ -6,7 +6,7 @@ import Clava from "../Clava.js"; import ClavaJoinPoints from "../ClavaJoinPoints.js"; import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { ClavaException, FileJp } from "../../Joinpoints.js"; +import { FileJp } from "../../Joinpoints.js"; /** * Parses C/C++ files. @@ -71,27 +71,24 @@ export default class BatchParser { private rebuildFile($literalFile: FileJp) { let parsing: boolean | undefined = true; while (parsing) { - const $parsedFile = $literalFile.rebuildTry() as FileJp | ClavaException; - - // Check if it is a file - if ($parsedFile instanceof FileJp) { - return $parsedFile; + try { + return $literalFile.rebuild(); + } catch (e) { + // It is an exception + parsing = this.solveRebuildFile(e as Error, $literalFile); } - - // It is an exception - parsing = this.solveRebuildFile($parsedFile, $literalFile); } return undefined; } - private solveRebuildFile($exception: ClavaException, $literalFile: FileJp) { + private solveRebuildFile($exception: Error, $literalFile: FileJp) { // Get error message const message = $exception.message; // Check if correct type - if ($exception.exceptionType !== "ClavaParserException") { - throw $exception.exception; + if ($exception.name !== "ClavaParserException") { + throw $exception; } const lines = Strings.asLines(message); From dc3428cde936ed10ef98233c93d53dcd44350529 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 29 May 2026 19:26:29 +0100 Subject: [PATCH 09/99] rafactor: Reflect recent changes of 'instanceOf', 'equals' and 'toString' from actions into attributes. --- .../up/fe/specs/clava/weaver/joinpoints/CxxFile.java | 2 +- .../fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java | 10 +++++----- .../specs/clava/weaver/joinpoints/types/CxxType.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java index 0f74499892..e4a4bcb5cf 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxFile.java @@ -144,7 +144,7 @@ public String getPathImpl() { public void addIncludeJpImpl(AJoinpoint jp) { // If jp is a function, include declaration if available - if (jp.instanceOfImpl("function")) { + if (jp.getInstanceOfImpl("function")) { AFunction functionJp = (AFunction) jp; AJoinpoint decl = functionJp.getDeclarationJpImpl(); jp = decl != null ? decl : jp; diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java index d84d34df42..653ce34088 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java @@ -137,7 +137,7 @@ public AJoinpoint getGetAncestorImpl(String type) { // Create join point for testing type AJoinpoint parentJp = CxxJoinpoints.create(currentNode.getParent(), getWeaverEngine()); - if (parentJp.instanceOfImpl(type)) { + if (parentJp.getInstanceOfImpl(type)) { return parentJp; } @@ -151,7 +151,7 @@ public AJoinpoint getGetAncestorImpl(String type) { public AJoinpoint[] getGetDescendantsImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of descendants in attribute 'descendants'"); - return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsStream(), jp -> jp.instanceOfImpl(type), + return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsStream(), jp -> jp.getInstanceOfImpl(type), getWeaverEngine()); } @@ -164,7 +164,7 @@ public AJoinpoint[] getDescendantsImpl() { public AJoinpoint[] getGetDescendantsAndSelfImpl(String type) { Objects.requireNonNull(type, () -> "Missing type of descendants in attribute 'descendants'"); - return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsAndSelfStream(), jp -> jp.instanceOfImpl(type), + return CxxSelects.selectedNodesToJps(getNodeImpl().getDescendantsAndSelfStream(), jp -> jp.getInstanceOfImpl(type), getWeaverEngine()); } @@ -179,7 +179,7 @@ public AJoinpoint getGetChainAncestorImpl(String type) { AJoinpoint currentJp = this; while (currentJp.getHasParentImpl()) { var parentJp = currentJp.getParentImpl(); - if (parentJp.instanceOfImpl(type)) { + if (parentJp.getInstanceOfImpl(type)) { return parentJp; } @@ -705,7 +705,7 @@ public AJoinpoint getCurrentRegionImpl() { } @Override - public boolean equalsImpl(Self jp) { + public boolean getEqualsImpl(Self jp) { if (!(jp instanceof AJoinpoint)) { return false; } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java index d62b5c3eea..5e9543992f 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/types/CxxType.java @@ -217,7 +217,7 @@ private static boolean setTypeFieldByValueRecursiveImpl(AType type, Object cu // Found value to change, change it and return if (currentValue instanceof CxxType cxxType){ - if (((AType)entry.getValue()).equalsImpl(cxxType)) { + if (((AType)entry.getValue()).getEqualsImpl(cxxType)) { type.setValueImpl(entry.getKey(), newValue); return true; } From 92d8afcea2b6ffa50dfd06008673a4b33f0d36e1 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 29 May 2026 19:26:56 +0100 Subject: [PATCH 10/99] Update tsconfig.json to include "types" for node and jest --- Clava-JS/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Clava-JS/tsconfig.json b/Clava-JS/tsconfig.json index c05308dc10..17889a771f 100644 --- a/Clava-JS/tsconfig.json +++ b/Clava-JS/tsconfig.json @@ -9,7 +9,8 @@ //"checkJs": true, "sourceMap": true, "declarationMap": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, //"esModuleInterop": true + "types": ["node", "jest"] } } From 21a0b73afc0a182367b62c59cca49d64989fcf8b Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 29 May 2026 23:46:50 +0100 Subject: [PATCH 11/99] fix: update TS JP wrappers with the new version of the builder script to escape forbidden Java keywords. --- Clava-JS/src-api/Joinpoints.ts | 6 +++--- .../clava/test/issues/c/results/Issue_aiq_1.js.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Clava-JS/src-api/Joinpoints.ts b/Clava-JS/src-api/Joinpoints.ts index e670f5fca8..185b3a6498 100644 --- a/Clava-JS/src-api/Joinpoints.ts +++ b/Clava-JS/src-api/Joinpoints.ts @@ -1403,7 +1403,7 @@ export class Omp extends Pragma { /** * One of 'shared' or 'none', or undefined if no 'default' clause is defined */ - get default(): string { return wrapJoinPoint(this._javaObject.default()) } + get default(): string { return wrapJoinPoint(this._javaObject._default()) } /** * One of 'shared' or 'none', or undefined if no 'default' clause is defined */ @@ -1451,7 +1451,7 @@ export class Omp extends Pragma { /** * The variable names of all private clauses, or empty array if no private clause is defined */ - get private(): string[] { return wrapJoinPoint(this._javaObject.private()) } + get private(): string[] { return wrapJoinPoint(this._javaObject._private()) } /** * The variable names of all private clauses, or empty array if no private clause is defined */ @@ -1831,7 +1831,7 @@ export class If extends Statement { get cond(): Expression { return wrapJoinPoint(this._javaObject.cond()) } set cond(value: Expression) { this._javaObject.setCond(unwrapJoinPoint(value)); } get condDecl(): Vardecl { return wrapJoinPoint(this._javaObject.condDecl()) } - get else(): Scope { return wrapJoinPoint(this._javaObject.else()) } + get else(): Scope { return wrapJoinPoint(this._javaObject._else()) } set else(value: Statement) { this._javaObject.setElse(unwrapJoinPoint(value)); } get then(): Scope { return wrapJoinPoint(this._javaObject.then()) } set then(value: Statement) { this._javaObject.setThen(unwrapJoinPoint(value)); } diff --git a/ClavaWeaver/resources/clava/test/issues/c/results/Issue_aiq_1.js.txt b/ClavaWeaver/resources/clava/test/issues/c/results/Issue_aiq_1.js.txt index 742e8bff99..da1c2ce018 100644 --- a/ClavaWeaver/resources/clava/test/issues/c/results/Issue_aiq_1.js.txt +++ b/ClavaWeaver/resources/clava/test/issues/c/results/Issue_aiq_1.js.txt @@ -2,5 +2,5 @@ Cond: { i < len; } -lt +LT Found 1 forloops. \ No newline at end of file From 6167aaf7e4c6ea94e828997ef47e17c25c108c86 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sat, 30 May 2026 17:46:40 +0100 Subject: [PATCH 12/99] refactor: remove unused InitializationStyle enum from the project --- .../weaver/enums/InitializationStyle.java | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 ClavaWeaver/src/pt/up/fe/specs/clava/weaver/enums/InitializationStyle.java diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/enums/InitializationStyle.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/enums/InitializationStyle.java deleted file mode 100644 index 3b05d9d8a7..0000000000 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/enums/InitializationStyle.java +++ /dev/null @@ -1,46 +0,0 @@ -package pt.up.fe.specs.clava.weaver.enums; - -import org.lara.interpreter.weaver.interf.NamedEnum; -import pt.up.fe.specs.util.lazy.Lazy; -import pt.up.fe.specs.util.enums.EnumHelperWithValue; - -/** - * - * - * @author Lara C. - */ -public enum InitializationStyle implements NamedEnum{ - NO_INIT("no_init"), - CINIT("cinit"), - CALL_INIT("call_init"), - LIST_INIT("list_init"); - private String name; - private static final Lazy> ENUM_HELPER = EnumHelperWithValue.newLazyHelperWithValue(InitializationStyle.class); - - /** - * - */ - private InitializationStyle(String name){ - this.name = name; - } - /** - * - */ - public String getName() { - return this.name; - } - - /** - * - */ - public String toString() { - return getName(); - } - - /** - * - */ - public static EnumHelperWithValue getHelper() { - return ENUM_HELPER.get(); - } -} From eb9c849d32a69925dd442adcbfb1e6cb3f368c99 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 1 Jun 2026 21:21:46 +0100 Subject: [PATCH 13/99] fix: rectify CxxCudaKernelCall's class name --- .../src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java | 2 +- .../{CXXCudaKernelCall.java => CxxCudaKernelCall.java} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/{CXXCudaKernelCall.java => CxxCudaKernelCall.java} (91%) diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java index 4c3c7c8d1b..2862cc436e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxJoinpoints.java @@ -58,7 +58,7 @@ public class CxxJoinpoints { JOINPOINT_FACTORY.put(UnaryOperator.class, CxxUnaryOp::new); JOINPOINT_FACTORY.put(ConditionalOperator.class, CxxTernaryOp::new); JOINPOINT_FACTORY.put(CXXMemberCallExpr.class, CxxMemberCall::new); - JOINPOINT_FACTORY.put(CUDAKernelCallExpr.class, CXXCudaKernelCall::new); + JOINPOINT_FACTORY.put(CUDAKernelCallExpr.class, CxxCudaKernelCall::new); JOINPOINT_FACTORY.put(CallExpr.class, CxxCall::new); JOINPOINT_FACTORY.put(DeclRefExpr.class, CxxVarref::new); JOINPOINT_FACTORY.put(ArraySubscriptExpr.class, CxxArrayAccess::new); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CXXCudaKernelCall.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCudaKernelCall.java similarity index 91% rename from ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CXXCudaKernelCall.java rename to ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCudaKernelCall.java index 6cbb38b845..d3504000ae 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CXXCudaKernelCall.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxCudaKernelCall.java @@ -11,9 +11,9 @@ import pt.up.fe.specs.clava.weaver.importable.AstFactory; import pt.up.fe.specs.util.SpecsCollections; -public class CXXCudaKernelCall> extends ACudaKernelCall { +public class CxxCudaKernelCall> extends ACudaKernelCall { - public CXXCudaKernelCall(CUDAKernelCallExpr kernelCall, CxxWeaver weaver) { + public CxxCudaKernelCall(CUDAKernelCallExpr kernelCall, CxxWeaver weaver) { super(kernelCall, weaver); } From 24b7d37eec854dd432daca8e7d948488db99fa34 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 2 Jun 2026 00:07:08 +0100 Subject: [PATCH 14/99] fix: update mainClass for generateWeaver task to use the new CLI class --- ClavaWeaver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClavaWeaver/build.gradle b/ClavaWeaver/build.gradle index ef20f599d7..1ff759a7e0 100644 --- a/ClavaWeaver/build.gradle +++ b/ClavaWeaver/build.gradle @@ -63,7 +63,7 @@ tasks.register('generateWeaver', JavaExec) { group = "Execution" description = "Generates the Weaver Abstracts using WeaverGen2" classpath = configurations.weaverGeneratorRuntime + sourceSets.spec.runtimeClasspath - mainClass = 'org.lara.weavergen2.WeaverGen2' + mainClass = 'org.lara.weavergen2.cli.WeaverGen2Cli' args = [ "pt.up.fe.specs.clava.weaver.CxxSpec", "${projectDir}/src", From 7466f1458200d49c212a3686da959a0e86bf63b3 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 2 Jun 2026 17:17:50 +0100 Subject: [PATCH 15/99] fix: update generics in various classes because they are the cause of a few type casting problems. They warrant further analysis. --- .../up/fe/specs/clava/weaver/CxxSelects.java | 54 ++++++------------- .../fe/specs/clava/weaver/CxxWeaverApi.java | 2 +- .../clava/weaver/importable/AstFactory.java | 20 +++---- .../clava/weaver/joinpoints/CxxClass.java | 17 ------ .../clava/weaver/joinpoints/CxxJoinpoint.java | 15 +++++- .../specs/clava/weaver/joinpoints/CxxOmp.java | 5 +- .../clava/weaver/joinpoints/CxxPragma.java | 2 +- 7 files changed, 42 insertions(+), 73 deletions(-) diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java index dcc1b3ca88..ff6a259fb2 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxSelects.java @@ -14,7 +14,6 @@ package pt.up.fe.specs.clava.weaver; import java.util.List; -import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -40,12 +39,10 @@ public class CxxSelects { * @param directChildren * @param selectDescendents * @param filter - * @param mapper * @return */ - private static List selectPrivate(Class targetJoinpoint, - List directChildren, boolean selectDescendents, Predicate filter, - Function mapper) { + public static > T[] select(CxxWeaver weaver, Class targetJoinpoint, + List directChildren, boolean selectDescendents, Predicate filter) { Stream currentStream = directChildren.stream(); if (selectDescendents) { @@ -53,31 +50,13 @@ private static List selectPrivate(C } return currentStream.filter(filter) - .map(mapper) + .map(node -> CxxJoinpoints.create(node, weaver, targetJoinpoint)) // Filter null join points .filter(jp -> jp != null) - .collect(Collectors.toList()); + .toArray(size -> SpecsCollections.newArray(targetJoinpoint, size)); } - /** - * Selects join points. - * - * - * @param targetJoinpoint - * @param directChildren - * @param selectDescendents - * @param filter - * @return - */ - public static List select(CxxWeaver weaver, Class targetJoinpoint, - List directChildren, boolean selectDescendents, Predicate filter) { - - return selectPrivate(targetJoinpoint, directChildren, selectDescendents, filter, - node -> CxxJoinpoints.create(node, weaver, targetJoinpoint)); - - } - - public static List select(CxxWeaver weaver, Class targetJoinpoint, + public static > T[] select(CxxWeaver weaver, Class targetJoinpoint, List directChildren, boolean selectDescendents, Class filter) { return select(weaver, targetJoinpoint, directChildren, selectDescendents, filter::isInstance); @@ -112,13 +91,14 @@ public static AJoinpoint[] selectedNodesToJps(Stream sel return selectedNodesToJps(selectedNodes, jp -> true, weaverEngine); } - public static AJoinpoint[] selectedNodesToJps(Stream selectedNodes, - Predicate filter, CxxWeaver weaverEngine) { + @SuppressWarnings("unchecked") + public static > T[] selectedNodesToJps(Stream selectedNodes, + Predicate filter, CxxWeaver weaverEngine) { return selectedNodesToJpsStream(selectedNodes, filter, weaverEngine) + // Collect to list first, to avoid issues with generic array creation .collect(Collectors.toList()) - // .toArray(new AJoinpoint[0]); - .toArray(AJoinpoint[]::new); + .toArray(size -> (T[]) new AJoinpoint[size]); } public static Stream> selectedNodesToJpsStream(Stream selectedNodes, @@ -127,10 +107,11 @@ public static Stream> selectedNodesToJpsStream(Stream true, weaverEngine); } - public static Stream selectedNodesToJpsStream(Stream selectedNodes, - Predicate filter, CxxWeaver weaverEngine) { + @SuppressWarnings("unchecked") + public static > Stream selectedNodesToJpsStream(Stream selectedNodes, + Predicate filter, CxxWeaver weaverEngine) { - var selectedJps = selectedNodes + return selectedNodes // Ignore null nodes .filter(sibling -> !(sibling instanceof NullNode)) .map(node -> CxxJoinpoints.create(node, weaverEngine)) @@ -138,11 +119,8 @@ public static Stream selectedNodesToJpsStream(Stream jp != null) // Default filter .filter(CxxSelects::defaultSelectFilter) - .filter(jp -> filter.test(jp)) - // Cast back to AJoinpoint - .map(jp -> (AJoinpoint) jp); - - return selectedJps; + .map(jp -> (T) jp) + .filter(filter); } private static boolean defaultSelectFilter(AJoinpoint jp) { diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java index b154daf1a7..9c2512ea08 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaverApi.java @@ -37,7 +37,7 @@ public static void writeCode(CxxWeaver weaver, File outputFolder) { weaver.writeCode(outputFolder); } - public static List> getAvailableUserIncludes(CxxWeaver weaver) { + public static List getAvailableUserIncludes(CxxWeaver weaver) { return weaver.getAvailableIncludes().stream() .map(weaver.getFactory()::includeDecl) .map(includeDecl -> CxxJoinpoints.create(includeDecl, weaver, AInclude.class)) diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java index 45aff59c68..a0eac8c1e8 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java @@ -157,7 +157,7 @@ public static AExpression cxxConstructExpr(CxxWeaver weaver, AType type, O return cxxConstructExpr(weaver, type, SpecsCollections.asListT(AJoinpoint.class, constructorArguments)); } - public static AExpression cxxConstructExpr(CxxWeaver weaver, AType type, List> constructorArguments) { + public static AExpression cxxConstructExpr(CxxWeaver weaver, AType type, List constructorArguments) { List exprArgs = constructorArguments.stream() .map(arg -> (Expr) arg.getNodeImpl()) .collect(Collectors.toList()); @@ -169,7 +169,7 @@ public static ACall callFromFunction(CxxWeaver weaver, AFunction function, return callFromFunction(weaver, function, SpecsCollections.asListT(AJoinpoint.class, args)); } - public static ACall callFromFunction(CxxWeaver weaver, AFunction function, List> args) { + public static ACall callFromFunction(CxxWeaver weaver, AFunction function, List args) { var functionDecl = (FunctionDecl) function.getNodeImpl(); List exprArgs = args.stream() .map(arg -> (Expr) arg.getNodeImpl()) @@ -185,7 +185,7 @@ public static ACall call(CxxWeaver weaver, String functionName, AType type } - public static ACall call(CxxWeaver weaver, String functionName, AType typeJp, List> args) { + public static ACall call(CxxWeaver weaver, String functionName, AType typeJp, List args) { Type returnType = (Type) typeJp.getNodeImpl(); @@ -478,7 +478,7 @@ public static AScope scope(CxxWeaver weaver, Object[] statements) { return scope(weaver, SpecsCollections.asListT(AStatement.class, statements)); } - public static AScope scope(CxxWeaver weaver, List> statements) { + public static AScope scope(CxxWeaver weaver, List statements) { List stmtNodes = SpecsCollections.map(statements, stmt -> (Stmt) stmt.getNodeImpl()); return CxxJoinpoints.create(weaver.getFactory().compoundStmt(stmtNodes), weaver, AScope.class); } @@ -513,7 +513,7 @@ public static AFunctionType functionType(CxxWeaver weaver, AType returnTyp } - public static AFunctionType functionType(CxxWeaver weaver, AType returnTypeJp, List> argTypesJps) { + public static AFunctionType functionType(CxxWeaver weaver, AType returnTypeJp, List argTypesJps) { Type returnType = (Type) returnTypeJp.getNodeImpl(); @@ -532,7 +532,7 @@ public static AFunction functionDeclFromType(CxxWeaver weaver, String functio weaver, AFunction.class); } - public static AFunction functionDecl(CxxWeaver weaver, String functionName, AType returnTypeJp, List> namedDeclJps) { + public static AFunction functionDecl(CxxWeaver weaver, String functionName, AType returnTypeJp, List namedDeclJps) { Type returnType = (Type) returnTypeJp.getNodeImpl(); @@ -635,7 +635,7 @@ public static AExpression parenthesis(CxxWeaver weaver, AExpression expres return CxxJoinpoints.create(parenExpr, weaver, AExpression.class); } - public static AArrayAccess arrayAccess(CxxWeaver weaver, AExpression base, List> subscripts) { + public static AArrayAccess arrayAccess(CxxWeaver weaver, AExpression base, List subscripts) { var subscriptsExpr = subscripts.stream() .map(arg -> ((Expr) arg.getNodeImpl())) .collect(Collectors.toList()); @@ -648,7 +648,7 @@ public static AArrayAccess arrayAccess(CxxWeaver weaver, AExpression base, return arrayAccess(weaver, base, SpecsCollections.asListT(AExpression.class, subscripts)); } - public static AInitList initList(CxxWeaver weaver, List> values) { + public static AInitList initList(CxxWeaver weaver, List values) { var valuesExpr = values.stream() .map(arg -> ((Expr) arg.getNodeImpl())) .collect(Collectors.toList()); @@ -698,7 +698,7 @@ public static ACast cStyleCast(CxxWeaver weaver, AType type, AExpression classDecl(CxxWeaver weaver, String className, List> fields) { + public static AClass classDecl(CxxWeaver weaver, String className, List fields) { var fieldsNodes = fields.stream().map(field -> (FieldDecl) field.getNodeImpl()) .collect(Collectors.toList()); @@ -800,7 +800,7 @@ public static AExprStmt exprStmt(CxxWeaver weaver, AExpression expr) { * @param joinpoint * @return */ - public static ADeclStmt declStmt(CxxWeaver weaver, List> decls) { + public static ADeclStmt declStmt(CxxWeaver weaver, List decls) { var declNodes = decls.stream().map(decl -> (Decl) decl.getNodeImpl()) .collect(Collectors.toList()); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java index 3a597fb7b4..f678e74765 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxClass.java @@ -27,23 +27,6 @@ public CxxClass(CXXRecordDecl cxxRecordDecl, CxxWeaver weaver) { super(cxxRecordDecl, weaver); } - public Boolean isAbstract() { - return (Boolean) this.getIsAbstract(); - /* - return this.cxxRecordDecl.getMethods().stream() - .filter(method -> !(method instanceof CXXDestructorDecl)) - .anyMatch(method -> {/* - System.err.println(" -> " + method.getFullyQualifiedName() - + " " + method.get(CXXMethodDecl.IS_VIRTUAL).booleanValue() - + " " + method.get(CXXMethodDecl.IS_PURE).booleanValue()); - /** / - // System.err.println(method.getCode()); - - return method.get(CXXMethodDecl.IS_PURE).booleanValue(); - }); - */ - } - @Override public CXXRecordDecl getNodeImpl() { return (CXXRecordDecl) super.getNodeImpl(); diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java index 653ce34088..7d24619fb4 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxJoinpoint.java @@ -269,7 +269,18 @@ public AJoinpoint[] insertImpl(InsertPosition position, String code) { @Override public AJoinpoint[] insertImpl(InsertPosition position, AJoinpoint node) { - throw new NotImplementedException(this); + Insert insert = Insert.getHelper().fromValue(position.getDisplay()); + switch (insert) { + case AFTER: + return new AJoinpoint[]{insertAfterImpl(node)}; + case BEFORE: + return new AJoinpoint[]{insertBeforeImpl(node)}; + case REPLACE: + return new AJoinpoint[]{replaceWithImpl(node)}; + case AROUND: + default: + throw new NotImplementedException(insert); + } } @Override @@ -710,7 +721,7 @@ public boolean getEqualsImpl(Self jp) { return false; } - return getNodeImpl().equals(((AJoinpoint) jp).getNodeImpl()); + return this.getSameImpl(jp); } @Override diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java index d13809b147..ea2ea0bb0a 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxOmp.java @@ -263,9 +263,6 @@ public void setKindImpl(String directiveKindString) { NodeInsertUtils.replace(this.getNodeImpl(), newOmpPragma); // Update join point pragma - this.ompPragma = newOmpPragma; - - // Update parent join point - this.aPragma = new CxxPragma(ompPragma, getWeaverEngine()); + this.node = newOmpPragma; } } diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java index fe09bcb7a9..2c9d02e81e 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/joinpoints/CxxPragma.java @@ -57,7 +57,7 @@ public void setNameImpl(String name) { } public void setPragma(Pragma pragma) { - this.pragma = pragma; + this.node = pragma; } @Override From e2b06551b12bb5d177332b296ade1f24156d0df0 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 8 Jun 2026 19:23:16 +0100 Subject: [PATCH 16/99] Clava-JS: Make the project compatible with Node.js' TS parser where only erasable syntax is allowed - Removed a couple of deprecated files. - Transformed enums into "const" declarations. - Modified the tsconfig.json files - Updated to TS 6 while I was at it. --- Clava-JS/jest/ClavaLegacyTester.ts | 2 +- Clava-JS/package.json | 6 +- Clava-JS/src-api/Joinpoints.ts | 208 ++++++++++-------- Clava-JS/src-api/clava/Clava.ts | 2 +- Clava-JS/src-api/clava/ClavaCode.ts | 2 +- Clava-JS/src-api/clava/ClavaJavaTypes.ts | 2 +- Clava-JS/src-api/clava/ClavaJoinPoints.ts | 2 +- Clava-JS/src-api/clava/cmake/CMaker.ts | 2 +- Clava-JS/src-api/clava/cmake/CMakerSources.ts | 2 +- .../src-api/clava/code/SimplifyAssignment.ts | 2 +- Clava-JS/src-api/clava/gprofer/Gprofer.ts | 2 +- Clava-JS/src-api/clava/hdf5/Hdf5.ts | 56 ++--- .../src-api/clava/opencl/KernelReplacer.ts | 56 +++-- .../clava/opencl/KernelReplacerAuto.ts | 2 +- Clava-JS/src-api/clava/parser/BatchParser.ts | 2 +- Clava-JS/src-api/clava/util/ClavaDataStore.ts | 2 +- Clava-JS/src-api/clava/util/FileIterator.ts | 2 +- .../lara/benchmark/ClavaBenchmarkInstance.ts | 2 +- Clava-JS/src-api/tsconfig.json | 1 + Clava-JS/src-code/Dumper/ClangJSONDumper.ts | 2 +- Clava-JS/src-code/WeaverConfiguration.ts | 2 +- Clava-JS/src-code/tsconfig.json | 1 + Clava-JS/tsconfig.json | 7 +- .../resources/clava/test/weaver/Traversal.js | 2 +- 24 files changed, 217 insertions(+), 152 deletions(-) diff --git a/Clava-JS/jest/ClavaLegacyTester.ts b/Clava-JS/jest/ClavaLegacyTester.ts index c5a75e0ca1..8ee73374fb 100644 --- a/Clava-JS/jest/ClavaLegacyTester.ts +++ b/Clava-JS/jest/ClavaLegacyTester.ts @@ -1,5 +1,5 @@ import ClavaJavaTypes, { - ClavaJavaClasses, + type ClavaJavaClasses, } from "@specs-feup/clava/api/clava/ClavaJavaTypes.js"; import { WeaverLegacyTester } from "@specs-feup/lara/jest/WeaverLegacyTester.js"; diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 1c253d8ed6..81ca3f9f90 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -63,17 +63,17 @@ "@types/debug": "^4.1.12", "@types/java": "^0.9.6", "@types/jest": "^30.0.0", - "@types/node": "^20.14.10", + "@types/node": "^25.0.0", "@types/yargs": "^17.0.35", "cross-env": "^10.1.0", - "eslint": "^9.32.2", + "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jest": "^29.12.1", "eslint-plugin-tsdoc": "^0.5.0", "jest": "^30.2.0", "node-notifier": "^10.0.1", "ts-jest": "^29.4.6", - "typescript": "^5.9.3", + "typescript": "^6.0.3", "typescript-eslint": "^8.52.0" } } diff --git a/Clava-JS/src-api/Joinpoints.ts b/Clava-JS/src-api/Joinpoints.ts index 185b3a6498..ded340b4af 100644 --- a/Clava-JS/src-api/Joinpoints.ts +++ b/Clava-JS/src-api/Joinpoints.ts @@ -2805,92 +2805,128 @@ export class TypedefType extends Type { get underlyingType(): Type { return wrapJoinPoint(this._javaObject.underlyingType()) } } -export enum StorageClass { - NONE = "none", - AUTO = "auto", - EXTERN = "extern", - PRIVATE_EXTERN = "private_extern", - REGISTER = "register", - STATIC = "static", -} - -export enum Relation { - LE = "le", - LT = "lt", - GE = "ge", - GT = "gt", - EQ = "eq", - NE = "ne", -} - -export enum LoopKind { - for = "for", - while = "while", - dowhile = "dowhile", - foreach = "foreach", -} - -export enum ExpressionUse { - read = "read", - write = "write", - readwrite = "readwrite", -} - -export enum OpKind { - ptr_mem_d = "ptr_mem_d", - ptr_mem_i = "ptr_mem_i", - mul = "mul", - div = "div", - rem = "rem", - add = "add", - sub = "sub", - shl = "shl", - shr = "shr", - cmp = "cmp", - lt = "lt", - gt = "gt", - le = "le", - ge = "ge", - eq = "eq", - ne = "ne", - and = "and", - xor = "xor", - or = "or", - l_and = "l_and", - l_or = "l_or", - assign = "assign", - mul_assign = "mul_assign", - div_assign = "div_assign", - rem_assign = "rem_assign", - add_assign = "add_assign", - sub_assign = "sub_assign", - shl_assign = "shl_assign", - shr_assign = "shr_assign", - and_assign = "and_assign", - xor_assign = "xor_assign", - or_assign = "or_assign", - comma = "comma", - post_inc = "post_inc", - post_dec = "post_dec", - pre_inc = "pre_inc", - pre_dec = "pre_dec", - addr_of = "addr_of", - deref = "deref", - plus = "plus", - minus = "minus", - not = "not", - l_not = "l_not", - real = "real", - imag = "imag", - extension = "extension", - cowait = "cowait", - ternary = "ternary", -} - -export enum WrapperStatementKind { - comment = "comment", - pragma = "pragma", -} +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +export const StorageClass = { + NONE: "none", + AUTO: "auto", + EXTERN: "extern", + PRIVATE_EXTERN: "private_extern", + REGISTER: "register", + STATIC: "static", +} as const; +export type StorageClass = typeof StorageClass[keyof typeof StorageClass]; + +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +export const Relation = { + LE: "le", + LT: "lt", + GE: "ge", + GT: "gt", + EQ: "eq", + NE: "ne", +} as const; +export type Relation = typeof Relation[keyof typeof Relation]; + +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +export const LoopKind = { + for: "for", + while: "while", + dowhile: "dowhile", + foreach: "foreach", +} as const; +export type LoopKind = typeof LoopKind[keyof typeof LoopKind]; + +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +export const ExpressionUse = { + read: "read", + write: "write", + readwrite: "readwrite", +} as const; +export type ExpressionUse = typeof ExpressionUse[keyof typeof ExpressionUse]; + +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +export const OpKind = { + ptr_mem_d: "ptr_mem_d", + ptr_mem_i: "ptr_mem_i", + mul: "mul", + div: "div", + rem: "rem", + add: "add", + sub: "sub", + shl: "shl", + shr: "shr", + cmp: "cmp", + lt: "lt", + gt: "gt", + le: "le", + ge: "ge", + eq: "eq", + ne: "ne", + and: "and", + xor: "xor", + or: "or", + l_and: "l_and", + l_or: "l_or", + assign: "assign", + mul_assign: "mul_assign", + div_assign: "div_assign", + rem_assign: "rem_assign", + add_assign: "add_assign", + sub_assign: "sub_assign", + shl_assign: "shl_assign", + shr_assign: "shr_assign", + and_assign: "and_assign", + xor_assign: "xor_assign", + or_assign: "or_assign", + comma: "comma", + post_inc: "post_inc", + post_dec: "post_dec", + pre_inc: "pre_inc", + pre_dec: "pre_dec", + addr_of: "addr_of", + deref: "deref", + plus: "plus", + minus: "minus", + not: "not", + l_not: "l_not", + real: "real", + imag: "imag", + extension: "extension", + cowait: "cowait", + ternary: "ternary", +} as const; +export type OpKind = typeof OpKind[keyof typeof OpKind]; + +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +export const WrapperStatementKind = { + comment: "comment", + pragma: "pragma", +} as const; +export type WrapperStatementKind = typeof WrapperStatementKind[keyof typeof WrapperStatementKind]; const JoinpointMapper = { joinpoint: Joinpoint, diff --git a/Clava-JS/src-api/clava/Clava.ts b/Clava-JS/src-api/clava/Clava.ts index d6e402ca5d..3a48c219ef 100644 --- a/Clava-JS/src-api/clava/Clava.ts +++ b/Clava-JS/src-api/clava/Clava.ts @@ -1,7 +1,7 @@ import { wrapJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; import Io from "@specs-feup/lara/api/lara/Io.js"; import JavaInterop from "@specs-feup/lara/api/lara/JavaInterop.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; import Weaver from "@specs-feup/lara/api/weaver/Weaver.js"; import WeaverOptions from "@specs-feup/lara/api/weaver/WeaverOptions.js"; diff --git a/Clava-JS/src-api/clava/ClavaCode.ts b/Clava-JS/src-api/clava/ClavaCode.ts index d50230427c..292aa2e4fc 100644 --- a/Clava-JS/src-api/clava/ClavaCode.ts +++ b/Clava-JS/src-api/clava/ClavaCode.ts @@ -1,6 +1,6 @@ import Io from "@specs-feup/lara/api/lara/Io.js"; import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; import { FileJp, diff --git a/Clava-JS/src-api/clava/ClavaJavaTypes.ts b/Clava-JS/src-api/clava/ClavaJavaTypes.ts index 8e0ade6355..1dce1d31d2 100644 --- a/Clava-JS/src-api/clava/ClavaJavaTypes.ts +++ b/Clava-JS/src-api/clava/ClavaJavaTypes.ts @@ -1,5 +1,5 @@ import JavaTypes, { - JavaClasses, + type JavaClasses, } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/Clava-JS/src-api/clava/ClavaJoinPoints.ts b/Clava-JS/src-api/clava/ClavaJoinPoints.ts index 873f9181fc..332c0a23a5 100644 --- a/Clava-JS/src-api/clava/ClavaJoinPoints.ts +++ b/Clava-JS/src-api/clava/ClavaJoinPoints.ts @@ -6,7 +6,7 @@ import { arrayFromArgs, flattenArgsArray, } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import * as Joinpoints from "../Joinpoints.js"; import Clava from "./Clava.js"; import ClavaJavaTypes from "./ClavaJavaTypes.js"; diff --git a/Clava-JS/src-api/clava/cmake/CMaker.ts b/Clava-JS/src-api/clava/cmake/CMaker.ts index ee3a289716..f53c7641ae 100644 --- a/Clava-JS/src-api/clava/cmake/CMaker.ts +++ b/Clava-JS/src-api/clava/cmake/CMaker.ts @@ -5,7 +5,7 @@ import { debug, debugObject, } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import ProcessExecutor from "@specs-feup/lara/api/lara/util/ProcessExecutor.js"; import { FileJp } from "../../Joinpoints.js"; import Clava from "../Clava.js"; diff --git a/Clava-JS/src-api/clava/cmake/CMakerSources.ts b/Clava-JS/src-api/clava/cmake/CMakerSources.ts index b824a7c7ce..5d64d8c1c5 100644 --- a/Clava-JS/src-api/clava/cmake/CMakerSources.ts +++ b/Clava-JS/src-api/clava/cmake/CMakerSources.ts @@ -1,6 +1,6 @@ import Io from "@specs-feup/lara/api/lara/Io.js"; import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import Clava from "../Clava.js"; import CMakerUtils from "./CMakerUtils.js"; diff --git a/Clava-JS/src-api/clava/code/SimplifyAssignment.ts b/Clava-JS/src-api/clava/code/SimplifyAssignment.ts index fb176fefe3..0352c44461 100644 --- a/Clava-JS/src-api/clava/code/SimplifyAssignment.ts +++ b/Clava-JS/src-api/clava/code/SimplifyAssignment.ts @@ -26,7 +26,7 @@ export default function SimplifyAssignment($complexAssignment: BinaryOp): void { /** * Non-assignment counterparts of complex assignment operators (lookup table) */ -const ops = new Map([ +const ops = new Map([ [OpKind.mul_assign, OpKind.mul], [OpKind.div_assign, OpKind.div], [OpKind.rem_assign, OpKind.rem], diff --git a/Clava-JS/src-api/clava/gprofer/Gprofer.ts b/Clava-JS/src-api/clava/gprofer/Gprofer.ts index 8b12018270..08761c91d6 100644 --- a/Clava-JS/src-api/clava/gprofer/Gprofer.ts +++ b/Clava-JS/src-api/clava/gprofer/Gprofer.ts @@ -1,7 +1,7 @@ import Io from "@specs-feup/lara/api/lara/Io.js"; import Strings from "@specs-feup/lara/api/lara/Strings.js"; import JavaTypes, { - JavaClasses, + type JavaClasses, } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; import { FileJp, FunctionJp } from "../../Joinpoints.js"; diff --git a/Clava-JS/src-api/clava/hdf5/Hdf5.ts b/Clava-JS/src-api/clava/hdf5/Hdf5.ts index c626aca7c3..886262e5c3 100644 --- a/Clava-JS/src-api/clava/hdf5/Hdf5.ts +++ b/Clava-JS/src-api/clava/hdf5/Hdf5.ts @@ -9,31 +9,37 @@ import { import ClavaJoinPoints from "../ClavaJoinPoints.js"; import Format from "../Format.js"; -enum HDF5Types { - "char" = "C_S1", - "signed char" = "NATIVE_SCHAR", - "unsigned char" = "NATIVE_UCHAR", - "short" = "NATIVE_SHORT", - "unsigned short" = "NATIVE_USHORT", - "int" = "NATIVE_INT", - "unsigned int" = "NATIVE_UINT", - "long" = "NATIVE_LONG", - "unsigned long" = "NATIVE_ULONG", - "long long" = "NATIVE_LLONG", - "unsigned long long" = "NATIVE_ULLONG", - "float" = "NATIVE_FLOAT", - "double" = "NATIVE_DOUBLE", - "long double" = "NATIVE_LDOUBLE", - - "int8_t" = "NATIVE_INT8", - "uint8_t" = "NATIVE_UINT8", - "int16_t" = "NATIVE_INT16", - "uint16_t" = "NATIVE_UINT16", - "int32_t" = "NATIVE_INT32", - "uin32_t" = "NATIVE_UINT32", - "int64_t" = "NATIVE_INT64", - "uint64_t" = "NATIVE_UINT64", -} +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +const HDF5Types = { + "char": "C_S1", + "signed char": "NATIVE_SCHAR", + "unsigned char": "NATIVE_UCHAR", + "short": "NATIVE_SHORT", + "unsigned short": "NATIVE_USHORT", + "int": "NATIVE_INT", + "unsigned int": "NATIVE_UINT", + "long": "NATIVE_LONG", + "unsigned long": "NATIVE_ULONG", + "long long": "NATIVE_LLONG", + "unsigned long long": "NATIVE_ULLONG", + "float": "NATIVE_FLOAT", + "double": "NATIVE_DOUBLE", + "long double": "NATIVE_LDOUBLE", + + "int8_t": "NATIVE_INT8", + "uint8_t": "NATIVE_UINT8", + "int16_t": "NATIVE_INT16", + "uint16_t": "NATIVE_UINT16", + "int32_t": "NATIVE_INT32", + "uint32_t": "NATIVE_UINT32", + "int64_t": "NATIVE_INT64", + "uint64_t": "NATIVE_UINT64", +} as const; +type HDF5Types = typeof HDF5Types[keyof typeof HDF5Types]; /** * Utility methods related to the HDF5 library. diff --git a/Clava-JS/src-api/clava/opencl/KernelReplacer.ts b/Clava-JS/src-api/clava/opencl/KernelReplacer.ts index 3f85c2babd..bed6f67381 100644 --- a/Clava-JS/src-api/clava/opencl/KernelReplacer.ts +++ b/Clava-JS/src-api/clava/opencl/KernelReplacer.ts @@ -484,22 +484,40 @@ class Buffer { /* ------------------------------ ENUMS ----------------------------- */ -enum BufferKind { - INPUT = "CL_MEM_READ_ONLY", - OUTPUT = "CL_MEM_WRITE_ONLY", - INPUT_OUTPUT = "CL_MEM_READ_WRITE", -} - -enum DeviceType { - CL_DEVICE_TYPE_ALL = "CL_DEVICE_TYPE_ALL", - CL_DEVICE_TYPE_CPU = "CL_DEVICE_TYPE_CPU", - CL_DEVICE_TYPE_GPU = "CL_DEVICE_TYPE_GPU", - CL_DEVICE_TYPE_ACCELERATOR = "CL_DEVICE_TYPE_ACCELERATOR", - CL_DEVICE_TYPE_DEFAULT = "CL_DEVICE_TYPE_DEFAULT", -} - -enum ErrorHandling { - EXIT = 0, - RETURN = 1, - USER = 2, -} +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +const BufferKind = { + INPUT: "CL_MEM_READ_ONLY", + OUTPUT: "CL_MEM_WRITE_ONLY", + INPUT_OUTPUT: "CL_MEM_READ_WRITE", +} as const; +type BufferKind = typeof BufferKind[keyof typeof BufferKind]; + +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +const DeviceType = { + CL_DEVICE_TYPE_ALL: "CL_DEVICE_TYPE_ALL", + CL_DEVICE_TYPE_CPU: "CL_DEVICE_TYPE_CPU", + CL_DEVICE_TYPE_GPU: "CL_DEVICE_TYPE_GPU", + CL_DEVICE_TYPE_ACCELERATOR: "CL_DEVICE_TYPE_ACCELERATOR", + CL_DEVICE_TYPE_DEFAULT: "CL_DEVICE_TYPE_DEFAULT", +} as const; +type DeviceType = typeof DeviceType[keyof typeof DeviceType]; + +/** + * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. + * Revert to an enum when Node.js supports it, or when we move to a different engine that supports it. + * This and the "type" declaration below. + */ +const ErrorHandling = { + EXIT: 0, + RETURN: 1, + USER: 2, +} as const; +type ErrorHandling = typeof ErrorHandling[keyof typeof ErrorHandling]; diff --git a/Clava-JS/src-api/clava/opencl/KernelReplacerAuto.ts b/Clava-JS/src-api/clava/opencl/KernelReplacerAuto.ts index fda9724da8..a27bfc7c4b 100644 --- a/Clava-JS/src-api/clava/opencl/KernelReplacerAuto.ts +++ b/Clava-JS/src-api/clava/opencl/KernelReplacerAuto.ts @@ -3,7 +3,7 @@ import Strings from "@specs-feup/lara/api/lara/Strings.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; import { Call, FileJp, Pragma } from "../../Joinpoints.js"; import KernelReplacer, { - OpenClKernelReplacerConfiguration, + type OpenClKernelReplacerConfiguration, } from "./KernelReplacer.js"; // This aspect can be included in a library, imported and diff --git a/Clava-JS/src-api/clava/parser/BatchParser.ts b/Clava-JS/src-api/clava/parser/BatchParser.ts index bc714a3983..69301fe23e 100644 --- a/Clava-JS/src-api/clava/parser/BatchParser.ts +++ b/Clava-JS/src-api/clava/parser/BatchParser.ts @@ -4,7 +4,7 @@ import System from "@specs-feup/lara/api/lara/System.js"; import Strings from "@specs-feup/lara/api/lara/Strings.js"; import Clava from "../Clava.js"; import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; import { FileJp } from "../../Joinpoints.js"; diff --git a/Clava-JS/src-api/clava/util/ClavaDataStore.ts b/Clava-JS/src-api/clava/util/ClavaDataStore.ts index 20e0d5b648..ed672b53a3 100644 --- a/Clava-JS/src-api/clava/util/ClavaDataStore.ts +++ b/Clava-JS/src-api/clava/util/ClavaDataStore.ts @@ -1,7 +1,7 @@ import WeaverDataStore from "@specs-feup/lara/api/weaver/util/WeaverDataStore.js"; import ClavaJavaTypes from "../ClavaJavaTypes.js"; import JavaTypes, { - JavaClasses, + type JavaClasses, } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import Io from "@specs-feup/lara/api/lara/Io.js"; import DataStore from "@specs-feup/lara/api/lara/util/DataStore.js"; diff --git a/Clava-JS/src-api/clava/util/FileIterator.ts b/Clava-JS/src-api/clava/util/FileIterator.ts index 21267c0aa5..9333de784a 100644 --- a/Clava-JS/src-api/clava/util/FileIterator.ts +++ b/Clava-JS/src-api/clava/util/FileIterator.ts @@ -1,6 +1,6 @@ import Io from "@specs-feup/lara/api/lara/Io.js"; import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import { FileJp } from "../../Joinpoints.js"; import Clava from "../Clava.js"; diff --git a/Clava-JS/src-api/lara/benchmark/ClavaBenchmarkInstance.ts b/Clava-JS/src-api/lara/benchmark/ClavaBenchmarkInstance.ts index 90fe37bcbf..b31d487e58 100644 --- a/Clava-JS/src-api/lara/benchmark/ClavaBenchmarkInstance.ts +++ b/Clava-JS/src-api/lara/benchmark/ClavaBenchmarkInstance.ts @@ -1,6 +1,6 @@ import Io from "@specs-feup/lara/api/lara/Io.js"; import BenchmarkInstance from "@specs-feup/lara/api/lara/benchmark/BenchmarkInstance.js"; -import { JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; import Clava from "../..//clava/Clava.js"; import { Pragma } from "../../Joinpoints.js"; diff --git a/Clava-JS/src-api/tsconfig.json b/Clava-JS/src-api/tsconfig.json index b03a6a89ec..0607bd620a 100644 --- a/Clava-JS/src-api/tsconfig.json +++ b/Clava-JS/src-api/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "rootDir": ".", "outDir": "../api" }, "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"], diff --git a/Clava-JS/src-code/Dumper/ClangJSONDumper.ts b/Clava-JS/src-code/Dumper/ClangJSONDumper.ts index 1c7c0cf593..879a4042a6 100644 --- a/Clava-JS/src-code/Dumper/ClangJSONDumper.ts +++ b/Clava-JS/src-code/Dumper/ClangJSONDumper.ts @@ -1,4 +1,4 @@ -import AbstractDumper from "./AbstractDumper.js"; +import type AbstractDumper from "./AbstractDumper.js"; import { spawn } from "child_process"; diff --git a/Clava-JS/src-code/WeaverConfiguration.ts b/Clava-JS/src-code/WeaverConfiguration.ts index aa00ea44ab..2db66bf3b7 100644 --- a/Clava-JS/src-code/WeaverConfiguration.ts +++ b/Clava-JS/src-code/WeaverConfiguration.ts @@ -1,4 +1,4 @@ -import WeaverConfiguration from "@specs-feup/lara/code/WeaverConfiguration.js"; +import type WeaverConfiguration from "@specs-feup/lara/code/WeaverConfiguration.js"; import path from "path"; import { fileURLToPath } from "url"; diff --git a/Clava-JS/src-code/tsconfig.json b/Clava-JS/src-code/tsconfig.json index 8061f3caf7..62a2028ceb 100644 --- a/Clava-JS/src-code/tsconfig.json +++ b/Clava-JS/src-code/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "rootDir": ".", "outDir": "../code", "composite": true }, diff --git a/Clava-JS/tsconfig.json b/Clava-JS/tsconfig.json index 17889a771f..dfabb45eff 100644 --- a/Clava-JS/tsconfig.json +++ b/Clava-JS/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { + //"noEmit": true, "module": "NodeNext", - "moduleResolution": "NodeNext", "isolatedModules": true, "declaration": true, "strict": true, @@ -11,6 +11,9 @@ "declarationMap": true, "allowSyntheticDefaultImports": true, //"esModuleInterop": true - "types": ["node", "jest"] + "types": ["node", "jest"], + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true } } diff --git a/ClavaWeaver/resources/clava/test/weaver/Traversal.js b/ClavaWeaver/resources/clava/test/weaver/Traversal.js index 0b986a1259..9fb7b1278b 100644 --- a/ClavaWeaver/resources/clava/test/weaver/Traversal.js +++ b/ClavaWeaver/resources/clava/test/weaver/Traversal.js @@ -1,5 +1,5 @@ import Query from "@specs-feup/lara/api/weaver/Query.js"; -import TraversalType from "@specs-feup/lara/api/weaver/TraversalType.js"; +import { TraversalType } from "@specs-feup/lara/api/weaver/TraversalType.js"; for (const $jp of Query.search("function", "foo").search( undefined, From 9e58452c221316748f236d87202a3211896a7c34 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 8 Jun 2026 20:54:44 +0100 Subject: [PATCH 17/99] Clava-JS: Move TS files into the old JS locations. Since we do not transpile TS anymore, the sources have been moved to their old destination folders. --- .codex/skills/clava-scripting/SKILL.md | 6 +-- .../clava-scripting/references/clava-apis.md | 26 +++++------ .../clava-scripting/references/examples.md | 2 +- .../clava-scripting/references/query-api.md | 8 ++-- Clava-JS/.gitignore | 2 - Clava-JS/{src-api => api}/Issues.test.ts | 8 ++-- Clava-JS/{src-api => api}/Joinpoints.ts | 2 +- .../LegacyIntegrationTests - C.test.ts | 6 +-- .../LegacyIntegrationTests - CXX.test.ts | 6 +-- .../LegacyIntegrationTests - Issues.test.ts | 4 +- Clava-JS/{src-api => api}/Query.test.ts | 6 +-- Clava-JS/{src-api => api}/clava/Clava.ts | 19 ++++---- Clava-JS/{src-api => api}/clava/ClavaCode.ts | 12 ++--- .../{src-api => api}/clava/ClavaJavaTypes.ts | 2 +- .../clava/ClavaJoinPoints.test.ts | 8 ++-- .../{src-api => api}/clava/ClavaJoinPoints.ts | 14 +++--- Clava-JS/{src-api => api}/clava/ClavaType.ts | 6 +-- Clava-JS/{src-api => api}/clava/Format.ts | 2 +- Clava-JS/{src-api => api}/clava/MathExtra.ts | 6 +-- .../clava/analysis/Analyser.ts | 4 +- .../clava/analysis/AnalyserResult.test.ts | 4 +- .../clava/analysis/AnalyserResult.ts | 4 +- .../clava/analysis/CheckBasedAnalyser.ts | 12 ++--- .../clava/analysis/CheckResult.ts | 2 +- .../clava/analysis/Checker.ts | 4 +- .../{src-api => api}/clava/analysis/Fix.ts | 2 +- .../clava/analysis/MessageGenerator.ts | 8 ++-- .../clava/analysis/ResultFormatManager.ts | 6 +-- .../clava/analysis/ResultList.ts | 2 +- .../analysis/analysers/BoundsAnalyser.ts | 10 ++--- .../clava/analysis/analysers/BoundsResult.ts | 6 +-- .../analysis/analysers/DoubleFreeAnalyser.ts | 10 ++--- .../analysis/analysers/DoubleFreeResult.ts | 6 +-- .../clava/analysis/checkers/ChgrpChecker.ts | 6 +-- .../clava/analysis/checkers/ChmodChecker.ts | 6 +-- .../clava/analysis/checkers/ChownChecker.ts | 6 +-- .../clava/analysis/checkers/CinChecker.ts | 6 +-- .../clava/analysis/checkers/ExecChecker.ts | 6 +-- .../clava/analysis/checkers/FprintfChecker.ts | 6 +-- .../clava/analysis/checkers/FscanfChecker.ts | 6 +-- .../clava/analysis/checkers/GetsChecker.ts | 8 ++-- .../clava/analysis/checkers/LambdaChecker.ts | 6 +-- .../clava/analysis/checkers/MemcpyChecker.ts | 6 +-- .../clava/analysis/checkers/PrintfChecker.ts | 6 +-- .../clava/analysis/checkers/ScanfChecker.ts | 6 +-- .../clava/analysis/checkers/SprintfChecker.ts | 8 ++-- .../clava/analysis/checkers/StrcatChecker.ts | 8 ++-- .../clava/analysis/checkers/StrcpyChecker.ts | 12 ++--- .../clava/analysis/checkers/SyslogChecker.ts | 6 +-- .../clava/analysis/checkers/SystemChecker.ts | 6 +-- .../{src-api => api}/clava/cmake/CMaker.ts | 22 +++++----- .../clava/cmake/CMakerSources.ts | 10 ++--- .../clava/cmake/CMakerUtils.ts | 6 +-- .../clava/cmake/compilers/CMakeCompiler.ts | 0 .../cmake/compilers/GenericCMakeCompiler.ts | 2 +- .../clava/code/DecomposeResult.ts | 2 +- .../clava/code/DoToWhileStmt.ts | 4 +- .../clava/code/ForToWhileStmt.ts | 4 +- .../clava/code/GlobalVariable.ts | 4 +- .../{src-api => api}/clava/code/Inliner.ts | 10 ++--- .../{src-api => api}/clava/code/Outliner.ts | 8 ++-- .../clava/code/RemoveShadowing.ts | 2 +- .../clava/code/SimplifyAssignment.ts | 4 +- .../clava/code/SimplifyTernaryOp.ts | 4 +- .../clava/code/StatementDecomposer.ts | 8 ++-- .../{src-api => api}/clava/gprofer/Gprofer.ts | 16 +++---- .../clava/graphs/ControlFlowGraph.ts | 6 +-- .../clava/graphs/StaticCallGraph.ts | 12 ++--- .../clava/graphs/cfg/CfgBuilder.ts | 44 +++++++++---------- .../clava/graphs/cfg/CfgEdge.ts | 4 +- .../clava/graphs/cfg/CfgEdgeType.ts | 0 .../clava/graphs/cfg/CfgNodeData.ts | 6 +-- .../clava/graphs/cfg/CfgNodeType.ts | 0 .../clava/graphs/cfg/CfgUtils.ts | 8 ++-- .../clava/graphs/cfg/NextCfgNode.ts | 2 +- .../clava/graphs/cfg/nodedata/CaseData.ts | 6 +-- .../clava/graphs/cfg/nodedata/DataFactory.ts | 26 +++++------ .../clava/graphs/cfg/nodedata/GotoData.ts | 6 +-- .../clava/graphs/cfg/nodedata/HeaderData.ts | 6 +-- .../clava/graphs/cfg/nodedata/IfData.ts | 6 +-- .../graphs/cfg/nodedata/InstListNodeData.ts | 8 ++-- .../clava/graphs/cfg/nodedata/LabelData.ts | 6 +-- .../clava/graphs/cfg/nodedata/LoopData.ts | 6 +-- .../clava/graphs/cfg/nodedata/ReturnData.ts | 6 +-- .../graphs/cfg/nodedata/ScopeNodeData.ts | 6 +-- .../clava/graphs/cfg/nodedata/SwitchData.ts | 6 +-- .../clava/graphs/scg/ScgEdgeData.ts | 4 +- .../clava/graphs/scg/ScgNodeData.ts | 6 +-- .../graphs/scg/StaticCallGraphBuilder.ts | 12 ++--- Clava-JS/{src-api => api}/clava/hdf5/Hdf5.ts | 6 +-- .../clava/liveness/LivenessAnalyser.ts | 10 ++--- .../clava/liveness/LivenessAnalysis.ts | 4 +- .../clava/liveness/LivenessUtils.ts | 6 +-- .../clava/mpi/MpiAccessPattern.ts | 2 +- .../clava/mpi/MpiScatterGatherLoop.ts | 12 ++--- .../{src-api => api}/clava/mpi/MpiUtils.ts | 2 +- .../mpi/patterns/IterationVariablePattern.ts | 8 ++-- .../clava/mpi/patterns/MpiAccessPatterns.ts | 4 +- .../clava/mpi/patterns/ScalarPattern.ts | 4 +- .../clava/opencl/KernelReplacer.ts | 8 ++-- .../clava/opencl/KernelReplacerAuto.ts | 10 ++--- .../clava/opencl/OpenCLCall.ts | 10 ++--- .../clava/opencl/OpenCLCallVariables.ts | 0 .../{src-api => api}/clava/opt/Inlining.ts | 10 ++--- .../clava/opt/NormalizeToSubset.ts | 21 +++++---- Clava-JS/api/clava/opt/PrepareForInlining.ts | 8 ++++ .../clava/parser/BatchParser.ts | 18 ++++---- .../clava/pass/DecomposeDeclStmt.ts | 8 ++-- .../clava/pass/DecomposeVarDeclarations.ts | 8 ++-- .../clava/pass/LocalStaticToGlobal.ts | 10 ++--- .../clava/pass/SimplifyLoops.ts | 14 +++--- .../clava/pass/SimplifyReturnStmts.ts | 10 ++--- .../clava/pass/SimplifySelectionStmts.ts | 10 ++--- .../clava/pass/SingleReturnFunction.ts | 12 ++--- .../clava/pass/TransformSwitchToIf.ts | 12 ++--- .../{src-api => api}/clava/stats/OpsBlock.ts | 2 +- .../{src-api => api}/clava/stats/OpsCost.ts | 0 .../clava/stats/OpsCounter.ts | 12 ++--- .../clava/stats/StaticOpsCounter.ts | 8 ++-- .../clava/util/ClavaDataStore.ts | 12 ++--- .../clava/util/CodeInserter.ts | 8 ++-- .../clava/util/FileIterator.ts | 10 ++--- Clava-JS/{src-api => api}/jest.config.js | 3 +- .../lara/benchmark/ClavaBenchmarkInstance.ts | 16 +++---- Clava-JS/{src-api => api}/lara/code/Energy.ts | 14 +++--- Clava-JS/{src-api => api}/lara/code/Logger.ts | 10 ++--- Clava-JS/{src-api => api}/lara/code/Timer.ts | 16 +++---- .../lara/metrics/EnergyMetric.ts | 12 ++--- .../lara/metrics/ExecutionTimeMetric.ts | 14 +++--- Clava-JS/{src-code => api}/tsconfig.json | 4 +- .../{src-api => api}/weaver/WeaverLauncher.ts | 4 +- .../ClangPlugin/ClangPlugin.test.ts | 2 +- .../ClangPlugin/ClangPlugin.ts | 2 +- .../Dumper/AbstractDumper.ts | 0 .../Dumper/ClangJSONDumper.ts | 2 +- Clava-JS/{src-code => code}/Sandbox.test.ts | 2 +- Clava-JS/{src-code => code}/Sandbox.ts | 2 +- .../{src-code => code}/WeaverConfiguration.ts | 6 +-- Clava-JS/{src-code => code}/index.ts | 4 +- Clava-JS/{src-code => code}/jest.config.js | 0 Clava-JS/{src-code => code}/sideEffects.ts | 4 +- Clava-JS/code/tsconfig.json | 7 +++ Clava-JS/jest.config.js | 2 +- Clava-JS/jest/ClavaLegacyTester.ts | 4 +- Clava-JS/package.json | 18 ++++---- .../src-api/clava/opt/PrepareForInlining.ts | 8 ---- Clava-JS/src-api/tsconfig.json | 9 ---- Clava-JS/tsconfig.json | 2 +- Clava-JS/typedoc.config.js | 4 +- README.md | 20 ++++----- 150 files changed, 548 insertions(+), 559 deletions(-) rename Clava-JS/{src-api => api}/Issues.test.ts (87%) rename Clava-JS/{src-api => api}/Joinpoints.ts (99%) rename Clava-JS/{src-api => api}/LegacyIntegrationTests - C.test.ts (98%) rename Clava-JS/{src-api => api}/LegacyIntegrationTests - CXX.test.ts (98%) rename Clava-JS/{src-api => api}/LegacyIntegrationTests - Issues.test.ts (80%) rename Clava-JS/{src-api => api}/Query.test.ts (96%) rename Clava-JS/{src-api => api}/clava/Clava.ts (94%) rename Clava-JS/{src-api => api}/clava/ClavaCode.ts (96%) rename Clava-JS/{src-api => api}/clava/ClavaJavaTypes.ts (98%) rename Clava-JS/{src-api => api}/clava/ClavaJoinPoints.test.ts (75%) rename Clava-JS/{src-api => api}/clava/ClavaJoinPoints.ts (99%) rename Clava-JS/{src-api => api}/clava/ClavaType.ts (97%) rename Clava-JS/{src-api => api}/clava/Format.ts (99%) rename Clava-JS/{src-api => api}/clava/MathExtra.ts (94%) rename Clava-JS/{src-api => api}/clava/analysis/Analyser.ts (54%) rename Clava-JS/{src-api => api}/clava/analysis/AnalyserResult.test.ts (92%) rename Clava-JS/{src-api => api}/clava/analysis/AnalyserResult.ts (87%) rename Clava-JS/{src-api => api}/clava/analysis/CheckBasedAnalyser.ts (83%) rename Clava-JS/{src-api => api}/clava/analysis/CheckResult.ts (54%) rename Clava-JS/{src-api => api}/clava/analysis/Checker.ts (70%) rename Clava-JS/{src-api => api}/clava/analysis/Fix.ts (86%) rename Clava-JS/{src-api => api}/clava/analysis/MessageGenerator.ts (91%) rename Clava-JS/{src-api => api}/clava/analysis/ResultFormatManager.ts (84%) rename Clava-JS/{src-api => api}/clava/analysis/ResultList.ts (81%) rename Clava-JS/{src-api => api}/clava/analysis/analysers/BoundsAnalyser.ts (93%) rename Clava-JS/{src-api => api}/clava/analysis/analysers/BoundsResult.ts (83%) rename Clava-JS/{src-api => api}/clava/analysis/analysers/DoubleFreeAnalyser.ts (92%) rename Clava-JS/{src-api => api}/clava/analysis/analysers/DoubleFreeResult.ts (73%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/ChgrpChecker.ts (80%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/ChmodChecker.ts (80%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/ChownChecker.ts (80%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/CinChecker.ts (80%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/ExecChecker.ts (82%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/FprintfChecker.ts (80%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/FscanfChecker.ts (79%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/GetsChecker.ts (82%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/LambdaChecker.ts (81%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/MemcpyChecker.ts (78%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/PrintfChecker.ts (79%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/ScanfChecker.ts (81%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/SprintfChecker.ts (87%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/StrcatChecker.ts (83%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/StrcpyChecker.ts (80%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/SyslogChecker.ts (79%) rename Clava-JS/{src-api => api}/clava/analysis/checkers/SystemChecker.ts (80%) rename Clava-JS/{src-api => api}/clava/cmake/CMaker.ts (96%) rename Clava-JS/{src-api => api}/clava/cmake/CMakerSources.ts (95%) rename Clava-JS/{src-api => api}/clava/cmake/CMakerUtils.ts (92%) rename Clava-JS/{src-api => api}/clava/cmake/compilers/CMakeCompiler.ts (100%) rename Clava-JS/{src-api => api}/clava/cmake/compilers/GenericCMakeCompiler.ts (87%) rename Clava-JS/{src-api => api}/clava/code/DecomposeResult.ts (92%) rename Clava-JS/{src-api => api}/clava/code/DoToWhileStmt.ts (94%) rename Clava-JS/{src-api => api}/clava/code/ForToWhileStmt.ts (95%) rename Clava-JS/{src-api => api}/clava/code/GlobalVariable.ts (95%) rename Clava-JS/{src-api => api}/clava/code/Inliner.ts (98%) rename Clava-JS/{src-api => api}/clava/code/Outliner.ts (99%) rename Clava-JS/{src-api => api}/clava/code/RemoveShadowing.ts (90%) rename Clava-JS/{src-api => api}/clava/code/SimplifyAssignment.ts (90%) rename Clava-JS/{src-api => api}/clava/code/SimplifyTernaryOp.ts (96%) rename Clava-JS/{src-api => api}/clava/code/StatementDecomposer.ts (98%) rename Clava-JS/{src-api => api}/clava/gprofer/Gprofer.ts (93%) rename Clava-JS/{src-api => api}/clava/graphs/ControlFlowGraph.ts (93%) rename Clava-JS/{src-api => api}/clava/graphs/StaticCallGraph.ts (87%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/CfgBuilder.ts (96%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/CfgEdge.ts (94%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/CfgEdgeType.ts (100%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/CfgNodeData.ts (95%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/CfgNodeType.ts (100%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/CfgUtils.ts (96%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/NextCfgNode.ts (99%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/CaseData.ts (77%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/DataFactory.ts (81%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/GotoData.ts (69%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/HeaderData.ts (76%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/IfData.ts (72%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/InstListNodeData.ts (89%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/LabelData.ts (69%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/LoopData.ts (70%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/ReturnData.ts (72%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/ScopeNodeData.ts (66%) rename Clava-JS/{src-api => api}/clava/graphs/cfg/nodedata/SwitchData.ts (73%) rename Clava-JS/{src-api => api}/clava/graphs/scg/ScgEdgeData.ts (88%) rename Clava-JS/{src-api => api}/clava/graphs/scg/ScgNodeData.ts (86%) rename Clava-JS/{src-api => api}/clava/graphs/scg/StaticCallGraphBuilder.ts (95%) rename Clava-JS/{src-api => api}/clava/hdf5/Hdf5.ts (98%) rename Clava-JS/{src-api => api}/clava/liveness/LivenessAnalyser.ts (96%) rename Clava-JS/{src-api => api}/clava/liveness/LivenessAnalysis.ts (95%) rename Clava-JS/{src-api => api}/clava/liveness/LivenessUtils.ts (98%) rename Clava-JS/{src-api => api}/clava/mpi/MpiAccessPattern.ts (91%) rename Clava-JS/{src-api => api}/clava/mpi/MpiScatterGatherLoop.ts (97%) rename Clava-JS/{src-api => api}/clava/mpi/MpiUtils.ts (90%) rename Clava-JS/{src-api => api}/clava/mpi/patterns/IterationVariablePattern.ts (97%) rename Clava-JS/{src-api => api}/clava/mpi/patterns/MpiAccessPatterns.ts (88%) rename Clava-JS/{src-api => api}/clava/mpi/patterns/ScalarPattern.ts (85%) rename Clava-JS/{src-api => api}/clava/opencl/KernelReplacer.ts (98%) rename Clava-JS/{src-api => api}/clava/opencl/KernelReplacerAuto.ts (83%) rename Clava-JS/{src-api => api}/clava/opencl/OpenCLCall.ts (92%) rename Clava-JS/{src-api => api}/clava/opencl/OpenCLCallVariables.ts (100%) rename Clava-JS/{src-api => api}/clava/opt/Inlining.ts (74%) rename Clava-JS/{src-api => api}/clava/opt/NormalizeToSubset.ts (67%) create mode 100644 Clava-JS/api/clava/opt/PrepareForInlining.ts rename Clava-JS/{src-api => api}/clava/parser/BatchParser.ts (91%) rename Clava-JS/{src-api => api}/clava/pass/DecomposeDeclStmt.ts (89%) rename Clava-JS/{src-api => api}/clava/pass/DecomposeVarDeclarations.ts (94%) rename Clava-JS/{src-api => api}/clava/pass/LocalStaticToGlobal.ts (93%) rename Clava-JS/{src-api => api}/clava/pass/SimplifyLoops.ts (88%) rename Clava-JS/{src-api => api}/clava/pass/SimplifyReturnStmts.ts (83%) rename Clava-JS/{src-api => api}/clava/pass/SimplifySelectionStmts.ts (82%) rename Clava-JS/{src-api => api}/clava/pass/SingleReturnFunction.ts (91%) rename Clava-JS/{src-api => api}/clava/pass/TransformSwitchToIf.ts (97%) rename Clava-JS/{src-api => api}/clava/stats/OpsBlock.ts (87%) rename Clava-JS/{src-api => api}/clava/stats/OpsCost.ts (100%) rename Clava-JS/{src-api => api}/clava/stats/OpsCounter.ts (95%) rename Clava-JS/{src-api => api}/clava/stats/StaticOpsCounter.ts (98%) rename Clava-JS/{src-api => api}/clava/util/ClavaDataStore.ts (93%) rename Clava-JS/{src-api => api}/clava/util/CodeInserter.ts (93%) rename Clava-JS/{src-api => api}/clava/util/FileIterator.ts (95%) rename Clava-JS/{src-api => api}/jest.config.js (79%) rename Clava-JS/{src-api => api}/lara/benchmark/ClavaBenchmarkInstance.ts (89%) rename Clava-JS/{src-api => api}/lara/code/Energy.ts (93%) rename Clava-JS/{src-api => api}/lara/code/Logger.ts (99%) rename Clava-JS/{src-api => api}/lara/code/Timer.ts (97%) rename Clava-JS/{src-api => api}/lara/metrics/EnergyMetric.ts (82%) rename Clava-JS/{src-api => api}/lara/metrics/ExecutionTimeMetric.ts (82%) rename Clava-JS/{src-code => api}/tsconfig.json (65%) rename Clava-JS/{src-api => api}/weaver/WeaverLauncher.ts (77%) rename Clava-JS/{src-code => code}/ClangPlugin/ClangPlugin.test.ts (98%) rename Clava-JS/{src-code => code}/ClangPlugin/ClangPlugin.ts (99%) rename Clava-JS/{src-code => code}/Dumper/AbstractDumper.ts (100%) rename Clava-JS/{src-code => code}/Dumper/ClangJSONDumper.ts (92%) rename Clava-JS/{src-code => code}/Sandbox.test.ts (98%) rename Clava-JS/{src-code => code}/Sandbox.ts (99%) rename Clava-JS/{src-code => code}/WeaverConfiguration.ts (69%) rename Clava-JS/{src-code => code}/index.ts (73%) mode change 100644 => 100755 rename Clava-JS/{src-code => code}/jest.config.js (100%) rename Clava-JS/{src-code => code}/sideEffects.ts (94%) create mode 100644 Clava-JS/code/tsconfig.json delete mode 100644 Clava-JS/src-api/clava/opt/PrepareForInlining.ts delete mode 100644 Clava-JS/src-api/tsconfig.json diff --git a/.codex/skills/clava-scripting/SKILL.md b/.codex/skills/clava-scripting/SKILL.md index b965f94e8d..c6353222e1 100644 --- a/.codex/skills/clava-scripting/SKILL.md +++ b/.codex/skills/clava-scripting/SKILL.md @@ -14,8 +14,8 @@ Write and modify Clava scripts in TypeScript using Clava/Lara APIs for joinpoint Use ESM imports with `.js` extensions, select joinpoints with `Query`, and transform with Clava APIs. ```ts -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { FunctionJp } from "@specs-feup/clava/api/Joinpoints.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { FunctionJp } from "@specs-feup/clava/api/Joinpoints.ts"; const $fn = Query.search(FunctionJp, { isImplementation: true }).first(); if ($fn) $fn.clone(`${$fn.name}_clone`); @@ -24,7 +24,7 @@ if ($fn) $fn.clone(`${$fn.name}_clone`); ## Workflow 1. Identify joinpoints and attributes. -Use the generated joinpoint wrappers in `@specs-feup/clava/api/Joinpoints.js` and check `Joinpoints.ts` for default attributes and available fields. +Use the generated joinpoint wrappers in `@specs-feup/clava/api/Joinpoints.ts` and check `Joinpoints.ts` for default attributes and available fields. 2. Select joinpoints with Query/Selector. Use `Query.search`, `Query.searchFrom`, `Query.searchFromInclusive`, `Query.childrenFrom`, and `Selector.scope`. Filters accept strings, regex, predicate functions, or objects keyed by attributes. `Selector` is iterable and methods like `.get()`, `.first()`, and `.chain()` consume the current selection. diff --git a/.codex/skills/clava-scripting/references/clava-apis.md b/.codex/skills/clava-scripting/references/clava-apis.md index 4661d47483..9b9c0850c0 100644 --- a/.codex/skills/clava-scripting/references/clava-apis.md +++ b/.codex/skills/clava-scripting/references/clava-apis.md @@ -4,27 +4,27 @@ Use this to locate the right TypeScript APIs and understand where functionality ## Clava-JS APIs (this repo) -- Joinpoint wrappers (generated): `Clava-JS/src-api/Joinpoints.ts` -- Joinpoint factories/utilities: `Clava-JS/src-api/clava/ClavaJoinPoints.ts` -- Core Clava utilities and AST stack: `Clava-JS/src-api/clava/Clava.ts` -- Common passes/opts built on Query: `Clava-JS/src-api/clava/opt`, `Clava-JS/src-api/clava/pass` +- Joinpoint wrappers (generated): `Clava-JS/api/Joinpoints.ts` +- Joinpoint factories/utilities: `Clava-JS/api/clava/ClavaJoinPoints.ts` +- Core Clava utilities and AST stack: `Clava-JS/api/clava/Clava.ts` +- Common passes/opts built on Query: `Clava-JS/api/clava/opt`, `Clava-JS/api/clava/pass` Imports typically use: -- `@specs-feup/clava/api/Joinpoints.js` -- `@specs-feup/clava/api/clava/ClavaJoinPoints.js` -- `@specs-feup/clava/api/clava/Clava.js` +- `@specs-feup/clava/api/Joinpoints.ts` +- `@specs-feup/clava/api/clava/ClavaJoinPoints.ts` +- `@specs-feup/clava/api/clava/Clava.ts` ## Lara-JS APIs (sibling repo) -- Query API: `../lara/Lara-JS/src-api/weaver/Query.ts` -- Selector behavior and filters: `../lara/Lara-JS/src-api/weaver/Selector.ts` -- Weaver utilities: `../lara/Lara-JS/src-api/weaver/Weaver.ts` +- Query API: `../lara/Lara-JS/api/weaver/Query.ts` +- Selector behavior and filters: `../lara/Lara-JS/api/weaver/Selector.ts` +- Weaver utilities: `../lara/Lara-JS/api/weaver/Weaver.ts` -If the Lara-JS repo is not a sibling of Clava, search for `Lara-JS/src-api/weaver/Query.ts`. +If the Lara-JS repo is not a sibling of Clava, search for `Lara-JS/api/weaver/Query.ts`. Imports typically use: -- `@specs-feup/lara/api/weaver/Query.js` -- `@specs-feup/lara/api/weaver/Weaver.js` +- `@specs-feup/lara/api/weaver/Query.ts` +- `@specs-feup/lara/api/weaver/Weaver.ts` ## Notes diff --git a/.codex/skills/clava-scripting/references/examples.md b/.codex/skills/clava-scripting/references/examples.md index 79e9dd6ccc..b57d102de1 100644 --- a/.codex/skills/clava-scripting/references/examples.md +++ b/.codex/skills/clava-scripting/references/examples.md @@ -24,5 +24,5 @@ Use these files for concrete patterns and idioms. - `ClavaWeaver/resources/clava/test/api/ClavaJoinPointsTest.js` - Large catalog of `ClavaJoinPoints` factory helpers. -- `Clava-JS/src-api/Query.test.ts` +- `Clava-JS/api/Query.test.ts` - Query chaining, `.scope()`, `.chain()`, and regex selection. diff --git a/.codex/skills/clava-scripting/references/query-api.md b/.codex/skills/clava-scripting/references/query-api.md index 1073cbecd1..f78fd8caf2 100644 --- a/.codex/skills/clava-scripting/references/query-api.md +++ b/.codex/skills/clava-scripting/references/query-api.md @@ -4,11 +4,11 @@ Use this when writing or debugging joinpoint selection logic. ## Primary sources -- Query API: `../lara/Lara-JS/src-api/weaver/Query.ts` (sibling worktree) -- Selector behavior: `../lara/Lara-JS/src-api/weaver/Selector.ts` (sibling worktree) -- Query usage tests: `Clava-JS/src-api/Query.test.ts` +- Query API: `../lara/Lara-JS/api/weaver/Query.ts` (sibling worktree) +- Selector behavior: `../lara/Lara-JS/api/weaver/Selector.ts` (sibling worktree) +- Query usage tests: `Clava-JS/api/Query.test.ts` -If the Lara-JS repo is not a sibling of Clava, search for `Lara-JS/src-api/weaver/Query.ts`. +If the Lara-JS repo is not a sibling of Clava, search for `Lara-JS/api/weaver/Query.ts`. ## Core patterns diff --git a/Clava-JS/.gitignore b/Clava-JS/.gitignore index 60a7a887ce..d8f403bdc2 100644 --- a/Clava-JS/.gitignore +++ b/Clava-JS/.gitignore @@ -1,7 +1,5 @@ ##### User-added ##### woven_code/ -/api -/code package-lock.json java-binaries diff --git a/Clava-JS/src-api/Issues.test.ts b/Clava-JS/api/Issues.test.ts similarity index 87% rename from Clava-JS/src-api/Issues.test.ts rename to Clava-JS/api/Issues.test.ts index dd73aa1cdc..165a255610 100644 --- a/Clava-JS/src-api/Issues.test.ts +++ b/Clava-JS/api/Issues.test.ts @@ -1,10 +1,10 @@ import { registerSourceCode, registerSourceCodes, -} from "@specs-feup/lara/jest/jestHelpers.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { FunctionJp } from "@specs-feup/clava/api/Joinpoints.js"; -import ClavaJoinPoints from "@specs-feup/clava/api/clava/ClavaJoinPoints.js"; +} from "@specs-feup/lara/jest/jestHelpers.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { FunctionJp } from "./Joinpoints.ts"; +import ClavaJoinPoints from "./clava/ClavaJoinPoints.ts"; const code187 = ` int foo(int a); diff --git a/Clava-JS/src-api/Joinpoints.ts b/Clava-JS/api/Joinpoints.ts similarity index 99% rename from Clava-JS/src-api/Joinpoints.ts rename to Clava-JS/api/Joinpoints.ts index ded340b4af..b1ab847a0c 100644 --- a/Clava-JS/src-api/Joinpoints.ts +++ b/Clava-JS/api/Joinpoints.ts @@ -8,7 +8,7 @@ import { wrapJoinPoint, unwrapJoinPoint, InsertPosition, -} from "@specs-feup/lara/api/LaraJoinPoint.js"; +} from "@specs-feup/lara/api/LaraJoinPoint.ts"; type PrivateMapper = { "Joinpoint": typeof Joinpoint, diff --git a/Clava-JS/src-api/LegacyIntegrationTests - C.test.ts b/Clava-JS/api/LegacyIntegrationTests - C.test.ts similarity index 98% rename from Clava-JS/src-api/LegacyIntegrationTests - C.test.ts rename to Clava-JS/api/LegacyIntegrationTests - C.test.ts index e039f61c09..819f278223 100644 --- a/Clava-JS/src-api/LegacyIntegrationTests - C.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - C.test.ts @@ -1,6 +1,6 @@ -import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.js"; -import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import ClavaJavaTypes from "@specs-feup/clava/api/clava/ClavaJavaTypes.js"; +import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.ts"; +import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import ClavaJavaTypes from "./clava/ClavaJavaTypes.ts"; import path from "path"; const isWindows = process.platform === "win32"; diff --git a/Clava-JS/src-api/LegacyIntegrationTests - CXX.test.ts b/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts similarity index 98% rename from Clava-JS/src-api/LegacyIntegrationTests - CXX.test.ts rename to Clava-JS/api/LegacyIntegrationTests - CXX.test.ts index 5473c3bc3a..c0bef9b83d 100644 --- a/Clava-JS/src-api/LegacyIntegrationTests - CXX.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts @@ -1,6 +1,6 @@ -import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.js"; -import ClavaJavaTypes from "@specs-feup/clava/api/clava/ClavaJavaTypes.js"; -import JavaInterop from "@specs-feup/lara/api/lara/JavaInterop.js"; +import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.ts"; +import ClavaJavaTypes from "./clava/ClavaJavaTypes.ts"; +import JavaInterop from "@specs-feup/lara/api/lara/JavaInterop.ts"; import path from "path"; const isWindows = process.platform === "win32"; diff --git a/Clava-JS/src-api/LegacyIntegrationTests - Issues.test.ts b/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts similarity index 80% rename from Clava-JS/src-api/LegacyIntegrationTests - Issues.test.ts rename to Clava-JS/api/LegacyIntegrationTests - Issues.test.ts index 480a24262a..e73847c701 100644 --- a/Clava-JS/src-api/LegacyIntegrationTests - Issues.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts @@ -1,5 +1,5 @@ -import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.js"; -import ClavaJavaTypes from "@specs-feup/clava/api/clava/ClavaJavaTypes.js"; +import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.ts"; +import ClavaJavaTypes from "./clava/ClavaJavaTypes.ts"; import path from "path"; /* eslint-disable jest/expect-expect */ diff --git a/Clava-JS/src-api/Query.test.ts b/Clava-JS/api/Query.test.ts similarity index 96% rename from Clava-JS/src-api/Query.test.ts rename to Clava-JS/api/Query.test.ts index 477308a338..ca2fba6d11 100644 --- a/Clava-JS/src-api/Query.test.ts +++ b/Clava-JS/api/Query.test.ts @@ -1,6 +1,6 @@ -import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { FunctionJp, Loop } from "./Joinpoints.js"; +import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { FunctionJp, Loop } from "./Joinpoints.ts"; const code = `void query_loop() { for(int i=0; i<10; i++) { diff --git a/Clava-JS/src-api/clava/Clava.ts b/Clava-JS/api/clava/Clava.ts similarity index 94% rename from Clava-JS/src-api/clava/Clava.ts rename to Clava-JS/api/clava/Clava.ts index 3a48c219ef..3f03b49626 100644 --- a/Clava-JS/src-api/clava/Clava.ts +++ b/Clava-JS/api/clava/Clava.ts @@ -1,13 +1,12 @@ -import { wrapJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; -import Io from "@specs-feup/lara/api/lara/Io.js"; -import JavaInterop from "@specs-feup/lara/api/lara/JavaInterop.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import Weaver from "@specs-feup/lara/api/weaver/Weaver.js"; -import WeaverOptions from "@specs-feup/lara/api/weaver/WeaverOptions.js"; -import { FileJp, Include, Joinpoint, Program } from "../Joinpoints.js"; -import ClavaJavaTypes from "./ClavaJavaTypes.js"; -import ClavaDataStore from "./util/ClavaDataStore.js"; +import { wrapJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.ts"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import Weaver from "@specs-feup/lara/api/weaver/Weaver.ts"; +import WeaverOptions from "@specs-feup/lara/api/weaver/WeaverOptions.ts"; +import { FileJp, Include, Joinpoint, Program } from "../Joinpoints.ts"; +import ClavaJavaTypes from "./ClavaJavaTypes.ts"; +import ClavaDataStore from "./util/ClavaDataStore.ts"; export default class Clava { /** diff --git a/Clava-JS/src-api/clava/ClavaCode.ts b/Clava-JS/api/clava/ClavaCode.ts similarity index 96% rename from Clava-JS/src-api/clava/ClavaCode.ts rename to Clava-JS/api/clava/ClavaCode.ts index 292aa2e4fc..38b6190e59 100644 --- a/Clava-JS/src-api/clava/ClavaCode.ts +++ b/Clava-JS/api/clava/ClavaCode.ts @@ -1,7 +1,7 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { FileJp, FunctionJp, @@ -11,8 +11,8 @@ import { Statement, StorageClass, Vardecl, -} from "../Joinpoints.js"; -import Clava from "./Clava.js"; +} from "../Joinpoints.ts"; +import Clava from "./Clava.ts"; /** * Utility methods related with the source code. diff --git a/Clava-JS/src-api/clava/ClavaJavaTypes.ts b/Clava-JS/api/clava/ClavaJavaTypes.ts similarity index 98% rename from Clava-JS/src-api/clava/ClavaJavaTypes.ts rename to Clava-JS/api/clava/ClavaJavaTypes.ts index 1dce1d31d2..3f3d47692d 100644 --- a/Clava-JS/src-api/clava/ClavaJavaTypes.ts +++ b/Clava-JS/api/clava/ClavaJavaTypes.ts @@ -1,6 +1,6 @@ import JavaTypes, { type JavaClasses, -} from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +} from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace ClavaJavaClasses { diff --git a/Clava-JS/src-api/clava/ClavaJoinPoints.test.ts b/Clava-JS/api/clava/ClavaJoinPoints.test.ts similarity index 75% rename from Clava-JS/src-api/clava/ClavaJoinPoints.test.ts rename to Clava-JS/api/clava/ClavaJoinPoints.test.ts index f570a5edac..53cf209c38 100644 --- a/Clava-JS/src-api/clava/ClavaJoinPoints.test.ts +++ b/Clava-JS/api/clava/ClavaJoinPoints.test.ts @@ -1,7 +1,7 @@ -import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { ExprStmt } from "../Joinpoints.js"; -import ClavaJoinPoints from "./ClavaJoinPoints.js"; +import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { ExprStmt } from "../Joinpoints.ts"; +import ClavaJoinPoints from "./ClavaJoinPoints.ts"; const code = `int main() { int a = 0, b = 0; diff --git a/Clava-JS/src-api/clava/ClavaJoinPoints.ts b/Clava-JS/api/clava/ClavaJoinPoints.ts similarity index 99% rename from Clava-JS/src-api/clava/ClavaJoinPoints.ts rename to Clava-JS/api/clava/ClavaJoinPoints.ts index 332c0a23a5..0b4193b9a8 100644 --- a/Clava-JS/src-api/clava/ClavaJoinPoints.ts +++ b/Clava-JS/api/clava/ClavaJoinPoints.ts @@ -1,16 +1,16 @@ import { unwrapJoinPoint, wrapJoinPoint, -} from "@specs-feup/lara/api/LaraJoinPoint.js"; +} from "@specs-feup/lara/api/LaraJoinPoint.ts"; import { arrayFromArgs, flattenArgsArray, -} from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import * as Joinpoints from "../Joinpoints.js"; -import Clava from "./Clava.js"; -import ClavaJavaTypes from "./ClavaJavaTypes.js"; -import Weaver from "@specs-feup/lara/api/weaver/Weaver.js"; +} from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import * as Joinpoints from "../Joinpoints.ts"; +import Clava from "./Clava.ts"; +import ClavaJavaTypes from "./ClavaJavaTypes.ts"; +import Weaver from "@specs-feup/lara/api/weaver/Weaver.ts"; /** * Utility methods related with the creation of new join points. diff --git a/Clava-JS/src-api/clava/ClavaType.ts b/Clava-JS/api/clava/ClavaType.ts similarity index 97% rename from Clava-JS/src-api/clava/ClavaType.ts rename to Clava-JS/api/clava/ClavaType.ts index 94a5b456f8..29783ace42 100644 --- a/Clava-JS/src-api/clava/ClavaType.ts +++ b/Clava-JS/api/clava/ClavaType.ts @@ -8,9 +8,9 @@ import { Type, VariableArrayType, Varref, -} from "../Joinpoints.js"; -import ClavaJoinPoints from "./ClavaJoinPoints.js"; -import ClavaJavaTypes from "./ClavaJavaTypes.js"; +} from "../Joinpoints.ts"; +import ClavaJoinPoints from "./ClavaJoinPoints.ts"; +import ClavaJavaTypes from "./ClavaJavaTypes.ts"; /** * Utility methods related with the type join points. diff --git a/Clava-JS/src-api/clava/Format.ts b/Clava-JS/api/clava/Format.ts similarity index 99% rename from Clava-JS/src-api/clava/Format.ts rename to Clava-JS/api/clava/Format.ts index a3793873d3..a8b4beede1 100644 --- a/Clava-JS/src-api/clava/Format.ts +++ b/Clava-JS/api/clava/Format.ts @@ -1,4 +1,4 @@ -import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.js"; +import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; export default class Format { static addPrefix(str: string, prefix: string): string { diff --git a/Clava-JS/src-api/clava/MathExtra.ts b/Clava-JS/api/clava/MathExtra.ts similarity index 94% rename from Clava-JS/src-api/clava/MathExtra.ts rename to Clava-JS/api/clava/MathExtra.ts index 4a2a69272c..371ac294a9 100644 --- a/Clava-JS/src-api/clava/MathExtra.ts +++ b/Clava-JS/api/clava/MathExtra.ts @@ -1,6 +1,6 @@ -import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import { Expression } from "../Joinpoints.js"; -import ClavaJavaTypes from "./ClavaJavaTypes.js"; +import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import { Expression } from "../Joinpoints.ts"; +import ClavaJavaTypes from "./ClavaJavaTypes.ts"; export default class MathExtra { /** diff --git a/Clava-JS/src-api/clava/analysis/Analyser.ts b/Clava-JS/api/clava/analysis/Analyser.ts similarity index 54% rename from Clava-JS/src-api/clava/analysis/Analyser.ts rename to Clava-JS/api/clava/analysis/Analyser.ts index b0b382413c..4cdfff7047 100644 --- a/Clava-JS/src-api/clava/analysis/Analyser.ts +++ b/Clava-JS/api/clava/analysis/Analyser.ts @@ -1,5 +1,5 @@ -import { Joinpoint } from "../../Joinpoints.js"; -import ResultList from "./ResultList.js"; +import { Joinpoint } from "../../Joinpoints.ts"; +import ResultList from "./ResultList.ts"; export default abstract class Analyser { abstract analyse($node?: Joinpoint): ResultList | undefined; diff --git a/Clava-JS/src-api/clava/analysis/AnalyserResult.test.ts b/Clava-JS/api/clava/analysis/AnalyserResult.test.ts similarity index 92% rename from Clava-JS/src-api/clava/analysis/AnalyserResult.test.ts rename to Clava-JS/api/clava/analysis/AnalyserResult.test.ts index 7effbf0216..61870b1325 100644 --- a/Clava-JS/src-api/clava/analysis/AnalyserResult.test.ts +++ b/Clava-JS/api/clava/analysis/AnalyserResult.test.ts @@ -1,5 +1,5 @@ -import AnalyserResult from "./AnalyserResult.js"; -import ClavaJoinPoints from "@specs-feup/clava/api/clava/ClavaJoinPoints.js"; +import AnalyserResult from "./AnalyserResult.ts"; +import ClavaJoinPoints from "@specs-feup/clava/api/clava/ClavaJoinPoints.ts"; describe("AnalyserResult", () => { describe("getName", () => { diff --git a/Clava-JS/src-api/clava/analysis/AnalyserResult.ts b/Clava-JS/api/clava/analysis/AnalyserResult.ts similarity index 87% rename from Clava-JS/src-api/clava/analysis/AnalyserResult.ts rename to Clava-JS/api/clava/analysis/AnalyserResult.ts index 7c8787bc5c..5219e1e09d 100644 --- a/Clava-JS/src-api/clava/analysis/AnalyserResult.ts +++ b/Clava-JS/api/clava/analysis/AnalyserResult.ts @@ -1,5 +1,5 @@ -import { Joinpoint } from "../../Joinpoints.js"; -import Fix from "./Fix.js"; +import { Joinpoint } from "../../Joinpoints.ts"; +import Fix from "./Fix.ts"; /** * Abstract class created as a model for every result of analyser diff --git a/Clava-JS/src-api/clava/analysis/CheckBasedAnalyser.ts b/Clava-JS/api/clava/analysis/CheckBasedAnalyser.ts similarity index 83% rename from Clava-JS/src-api/clava/analysis/CheckBasedAnalyser.ts rename to Clava-JS/api/clava/analysis/CheckBasedAnalyser.ts index 9020b4bbe8..98aa2bdd83 100644 --- a/Clava-JS/src-api/clava/analysis/CheckBasedAnalyser.ts +++ b/Clava-JS/api/clava/analysis/CheckBasedAnalyser.ts @@ -1,9 +1,9 @@ -import Analyser from "./Analyser.js"; -import Checker from "./Checker.js"; -import ResultFormatManager from "./ResultFormatManager.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { FileJp, Joinpoint, Program } from "../../Joinpoints.js"; -import AnalyserResult from "./AnalyserResult.js"; +import Analyser from "./Analyser.ts"; +import Checker from "./Checker.ts"; +import ResultFormatManager from "./ResultFormatManager.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { FileJp, Joinpoint, Program } from "../../Joinpoints.ts"; +import AnalyserResult from "./AnalyserResult.ts"; type T = Program | FileJp; diff --git a/Clava-JS/src-api/clava/analysis/CheckResult.ts b/Clava-JS/api/clava/analysis/CheckResult.ts similarity index 54% rename from Clava-JS/src-api/clava/analysis/CheckResult.ts rename to Clava-JS/api/clava/analysis/CheckResult.ts index 69af292a18..8125cd3f82 100644 --- a/Clava-JS/src-api/clava/analysis/CheckResult.ts +++ b/Clava-JS/api/clava/analysis/CheckResult.ts @@ -1,3 +1,3 @@ -import AnalyserResult from "./AnalyserResult.js"; +import AnalyserResult from "./AnalyserResult.ts"; export default class CheckResult extends AnalyserResult {} diff --git a/Clava-JS/src-api/clava/analysis/Checker.ts b/Clava-JS/api/clava/analysis/Checker.ts similarity index 70% rename from Clava-JS/src-api/clava/analysis/Checker.ts rename to Clava-JS/api/clava/analysis/Checker.ts index 05520bbb8a..2c3639b83d 100644 --- a/Clava-JS/src-api/clava/analysis/Checker.ts +++ b/Clava-JS/api/clava/analysis/Checker.ts @@ -1,5 +1,5 @@ -import { Joinpoint } from "../../Joinpoints.js"; -import AnalyserResult from "./AnalyserResult.js"; +import { Joinpoint } from "../../Joinpoints.ts"; +import AnalyserResult from "./AnalyserResult.ts"; export default abstract class Checker { name: string; diff --git a/Clava-JS/src-api/clava/analysis/Fix.ts b/Clava-JS/api/clava/analysis/Fix.ts similarity index 86% rename from Clava-JS/src-api/clava/analysis/Fix.ts rename to Clava-JS/api/clava/analysis/Fix.ts index e774d87768..0c1d44a2a5 100644 --- a/Clava-JS/src-api/clava/analysis/Fix.ts +++ b/Clava-JS/api/clava/analysis/Fix.ts @@ -1,4 +1,4 @@ -import { Joinpoint } from "../../Joinpoints.js"; +import { Joinpoint } from "../../Joinpoints.ts"; export default class Fix { private node: Joinpoint; diff --git a/Clava-JS/src-api/clava/analysis/MessageGenerator.ts b/Clava-JS/api/clava/analysis/MessageGenerator.ts similarity index 91% rename from Clava-JS/src-api/clava/analysis/MessageGenerator.ts rename to Clava-JS/api/clava/analysis/MessageGenerator.ts index 25e663a2b0..260b942fc8 100644 --- a/Clava-JS/src-api/clava/analysis/MessageGenerator.ts +++ b/Clava-JS/api/clava/analysis/MessageGenerator.ts @@ -1,7 +1,7 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import Clava from "../Clava.js"; -import AnalyserResult from "./AnalyserResult.js"; -import ResultList from "./ResultList.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import Clava from "../Clava.ts"; +import AnalyserResult from "./AnalyserResult.ts"; +import ResultList from "./ResultList.ts"; // Class sorting resultLists and generating an analysis report diff --git a/Clava-JS/src-api/clava/analysis/ResultFormatManager.ts b/Clava-JS/api/clava/analysis/ResultFormatManager.ts similarity index 84% rename from Clava-JS/src-api/clava/analysis/ResultFormatManager.ts rename to Clava-JS/api/clava/analysis/ResultFormatManager.ts index 4a620b3bbc..25db7976d4 100644 --- a/Clava-JS/src-api/clava/analysis/ResultFormatManager.ts +++ b/Clava-JS/api/clava/analysis/ResultFormatManager.ts @@ -1,6 +1,6 @@ -import { FileJp, Program } from "../../Joinpoints.js"; -import AnalyserResult from "./AnalyserResult.js"; -import ResultList from "./ResultList.js"; +import { FileJp, Program } from "../../Joinpoints.ts"; +import AnalyserResult from "./AnalyserResult.ts"; +import ResultList from "./ResultList.ts"; /** * Class to format the results from the analyser into a resultList diff --git a/Clava-JS/src-api/clava/analysis/ResultList.ts b/Clava-JS/api/clava/analysis/ResultList.ts similarity index 81% rename from Clava-JS/src-api/clava/analysis/ResultList.ts rename to Clava-JS/api/clava/analysis/ResultList.ts index 366a138c6a..7f59ed5bb7 100644 --- a/Clava-JS/src-api/clava/analysis/ResultList.ts +++ b/Clava-JS/api/clava/analysis/ResultList.ts @@ -1,4 +1,4 @@ -import AnalyserResult from "./AnalyserResult.js"; +import AnalyserResult from "./AnalyserResult.ts"; export default class ResultList { fileName: string; diff --git a/Clava-JS/src-api/clava/analysis/analysers/BoundsAnalyser.ts b/Clava-JS/api/clava/analysis/analysers/BoundsAnalyser.ts similarity index 93% rename from Clava-JS/src-api/clava/analysis/analysers/BoundsAnalyser.ts rename to Clava-JS/api/clava/analysis/analysers/BoundsAnalyser.ts index 9ab20fdf63..b7987b44bb 100644 --- a/Clava-JS/src-api/clava/analysis/analysers/BoundsAnalyser.ts +++ b/Clava-JS/api/clava/analysis/analysers/BoundsAnalyser.ts @@ -1,4 +1,4 @@ -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { ArrayAccess, ArrayType, @@ -6,10 +6,10 @@ import { FunctionJp, Program, Vardecl, -} from "../../../Joinpoints.js"; -import Analyser from "../Analyser.js"; -import ResultFormatManager from "../ResultFormatManager.js"; -import BoundsResult from "./BoundsResult.js"; +} from "../../../Joinpoints.ts"; +import Analyser from "../Analyser.ts"; +import ResultFormatManager from "../ResultFormatManager.ts"; +import BoundsResult from "./BoundsResult.ts"; type T = Program | FileJp; diff --git a/Clava-JS/src-api/clava/analysis/analysers/BoundsResult.ts b/Clava-JS/api/clava/analysis/analysers/BoundsResult.ts similarity index 83% rename from Clava-JS/src-api/clava/analysis/analysers/BoundsResult.ts rename to Clava-JS/api/clava/analysis/analysers/BoundsResult.ts index edab2f4c61..aeaacdc7cc 100644 --- a/Clava-JS/src-api/clava/analysis/analysers/BoundsResult.ts +++ b/Clava-JS/api/clava/analysis/analysers/BoundsResult.ts @@ -1,6 +1,6 @@ -import { Vardecl } from "../../../Joinpoints.js"; -import AnalyserResult from "../AnalyserResult.js"; -import Fix from "../Fix.js"; +import { Vardecl } from "../../../Joinpoints.ts"; +import AnalyserResult from "../AnalyserResult.ts"; +import Fix from "../Fix.ts"; export default class BoundsResult extends AnalyserResult { arrayName: string; diff --git a/Clava-JS/src-api/clava/analysis/analysers/DoubleFreeAnalyser.ts b/Clava-JS/api/clava/analysis/analysers/DoubleFreeAnalyser.ts similarity index 92% rename from Clava-JS/src-api/clava/analysis/analysers/DoubleFreeAnalyser.ts rename to Clava-JS/api/clava/analysis/analysers/DoubleFreeAnalyser.ts index 43fd4198a1..28f62b985f 100644 --- a/Clava-JS/src-api/clava/analysis/analysers/DoubleFreeAnalyser.ts +++ b/Clava-JS/api/clava/analysis/analysers/DoubleFreeAnalyser.ts @@ -1,4 +1,4 @@ -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { BinaryOp, Call, @@ -7,10 +7,10 @@ import { Joinpoint, Program, Vardecl, -} from "../../../Joinpoints.js"; -import Analyser from "../Analyser.js"; -import ResultFormatManager from "../ResultFormatManager.js"; -import DoubleFreeResult from "./DoubleFreeResult.js"; +} from "../../../Joinpoints.ts"; +import Analyser from "../Analyser.ts"; +import ResultFormatManager from "../ResultFormatManager.ts"; +import DoubleFreeResult from "./DoubleFreeResult.ts"; type T = Program | FileJp; diff --git a/Clava-JS/src-api/clava/analysis/analysers/DoubleFreeResult.ts b/Clava-JS/api/clava/analysis/analysers/DoubleFreeResult.ts similarity index 73% rename from Clava-JS/src-api/clava/analysis/analysers/DoubleFreeResult.ts rename to Clava-JS/api/clava/analysis/analysers/DoubleFreeResult.ts index 135627f00a..a5a80cf6a5 100644 --- a/Clava-JS/src-api/clava/analysis/analysers/DoubleFreeResult.ts +++ b/Clava-JS/api/clava/analysis/analysers/DoubleFreeResult.ts @@ -1,6 +1,6 @@ -import { Joinpoint } from "../../../Joinpoints.js"; -import AnalyserResult from "../AnalyserResult.js"; -import Fix from "../Fix.js"; +import { Joinpoint } from "../../../Joinpoints.ts"; +import AnalyserResult from "../AnalyserResult.ts"; +import Fix from "../Fix.ts"; export default class DoubleFreeResult extends AnalyserResult { ptrName: string; diff --git a/Clava-JS/src-api/clava/analysis/checkers/ChgrpChecker.ts b/Clava-JS/api/clava/analysis/checkers/ChgrpChecker.ts similarity index 80% rename from Clava-JS/src-api/clava/analysis/checkers/ChgrpChecker.ts rename to Clava-JS/api/clava/analysis/checkers/ChgrpChecker.ts index 9394c59004..f9e4bdfead 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/ChgrpChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/ChgrpChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of chgrp functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/ChmodChecker.ts b/Clava-JS/api/clava/analysis/checkers/ChmodChecker.ts similarity index 80% rename from Clava-JS/src-api/clava/analysis/checkers/ChmodChecker.ts rename to Clava-JS/api/clava/analysis/checkers/ChmodChecker.ts index ebac3efb3e..eb0b7ffc00 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/ChmodChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/ChmodChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of chmod functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/ChownChecker.ts b/Clava-JS/api/clava/analysis/checkers/ChownChecker.ts similarity index 80% rename from Clava-JS/src-api/clava/analysis/checkers/ChownChecker.ts rename to Clava-JS/api/clava/analysis/checkers/ChownChecker.ts index a66b095785..dd74d7eba0 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/ChownChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/ChownChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of chown functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/CinChecker.ts b/Clava-JS/api/clava/analysis/checkers/CinChecker.ts similarity index 80% rename from Clava-JS/src-api/clava/analysis/checkers/CinChecker.ts rename to Clava-JS/api/clava/analysis/checkers/CinChecker.ts index d7edb5b30a..1fc3ef1bb2 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/CinChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/CinChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; export default class CinChecker extends Checker { private advice = diff --git a/Clava-JS/src-api/clava/analysis/checkers/ExecChecker.ts b/Clava-JS/api/clava/analysis/checkers/ExecChecker.ts similarity index 82% rename from Clava-JS/src-api/clava/analysis/checkers/ExecChecker.ts rename to Clava-JS/api/clava/analysis/checkers/ExecChecker.ts index cf4aa2a542..b0dd2cca18 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/ExecChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/ExecChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of exec family functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/FprintfChecker.ts b/Clava-JS/api/clava/analysis/checkers/FprintfChecker.ts similarity index 80% rename from Clava-JS/src-api/clava/analysis/checkers/FprintfChecker.ts rename to Clava-JS/api/clava/analysis/checkers/FprintfChecker.ts index dcc200d23c..ae842ab983 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/FprintfChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/FprintfChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of fprintf functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/FscanfChecker.ts b/Clava-JS/api/clava/analysis/checkers/FscanfChecker.ts similarity index 79% rename from Clava-JS/src-api/clava/analysis/checkers/FscanfChecker.ts rename to Clava-JS/api/clava/analysis/checkers/FscanfChecker.ts index 775ecf88d1..49b0d72365 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/FscanfChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/FscanfChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of fscanf functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/GetsChecker.ts b/Clava-JS/api/clava/analysis/checkers/GetsChecker.ts similarity index 82% rename from Clava-JS/src-api/clava/analysis/checkers/GetsChecker.ts rename to Clava-JS/api/clava/analysis/checkers/GetsChecker.ts index 6488a8bc27..786188b982 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/GetsChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/GetsChecker.ts @@ -1,7 +1,7 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; -import Fix from "../Fix.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; +import Fix from "../Fix.ts"; /** * Check for the presence of gets functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/LambdaChecker.ts b/Clava-JS/api/clava/analysis/checkers/LambdaChecker.ts similarity index 81% rename from Clava-JS/src-api/clava/analysis/checkers/LambdaChecker.ts rename to Clava-JS/api/clava/analysis/checkers/LambdaChecker.ts index 6d8d55ffc7..ea4ae4ccf7 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/LambdaChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/LambdaChecker.ts @@ -1,6 +1,6 @@ -import { Joinpoint, Vardecl } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Joinpoint, Vardecl } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of lambda objects using capture by reference diff --git a/Clava-JS/src-api/clava/analysis/checkers/MemcpyChecker.ts b/Clava-JS/api/clava/analysis/checkers/MemcpyChecker.ts similarity index 78% rename from Clava-JS/src-api/clava/analysis/checkers/MemcpyChecker.ts rename to Clava-JS/api/clava/analysis/checkers/MemcpyChecker.ts index bcf849d9fa..c89eb8d043 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/MemcpyChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/MemcpyChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of memcpy functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/PrintfChecker.ts b/Clava-JS/api/clava/analysis/checkers/PrintfChecker.ts similarity index 79% rename from Clava-JS/src-api/clava/analysis/checkers/PrintfChecker.ts rename to Clava-JS/api/clava/analysis/checkers/PrintfChecker.ts index d42cf29c52..5cd270c866 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/PrintfChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/PrintfChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of printf functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/ScanfChecker.ts b/Clava-JS/api/clava/analysis/checkers/ScanfChecker.ts similarity index 81% rename from Clava-JS/src-api/clava/analysis/checkers/ScanfChecker.ts rename to Clava-JS/api/clava/analysis/checkers/ScanfChecker.ts index 69c8d38970..65c46d42bc 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/ScanfChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/ScanfChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of scanf functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/SprintfChecker.ts b/Clava-JS/api/clava/analysis/checkers/SprintfChecker.ts similarity index 87% rename from Clava-JS/src-api/clava/analysis/checkers/SprintfChecker.ts rename to Clava-JS/api/clava/analysis/checkers/SprintfChecker.ts index 0bed50093b..9607a8ddd1 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/SprintfChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/SprintfChecker.ts @@ -1,7 +1,7 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; -import Fix from "../Fix.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; +import Fix from "../Fix.ts"; /** * Check for the presence of sprintf functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/StrcatChecker.ts b/Clava-JS/api/clava/analysis/checkers/StrcatChecker.ts similarity index 83% rename from Clava-JS/src-api/clava/analysis/checkers/StrcatChecker.ts rename to Clava-JS/api/clava/analysis/checkers/StrcatChecker.ts index 10c9aea20c..0b75676a5c 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/StrcatChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/StrcatChecker.ts @@ -1,7 +1,7 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; -import Fix from "../Fix.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; +import Fix from "../Fix.ts"; /** * Check for the presence of strcat functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/StrcpyChecker.ts b/Clava-JS/api/clava/analysis/checkers/StrcpyChecker.ts similarity index 80% rename from Clava-JS/src-api/clava/analysis/checkers/StrcpyChecker.ts rename to Clava-JS/api/clava/analysis/checkers/StrcpyChecker.ts index 9c3aef5252..f21b6f2ecf 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/StrcpyChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/StrcpyChecker.ts @@ -1,9 +1,9 @@ -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; -import Fix from "../Fix.js"; -import { Joinpoint, Call } from "../../../Joinpoints.js"; -import AnalyserResult from "../AnalyserResult.js"; -import ClavaJoinPoints from "../../ClavaJoinPoints.js"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; +import Fix from "../Fix.ts"; +import { Joinpoint, Call } from "../../../Joinpoints.ts"; +import AnalyserResult from "../AnalyserResult.ts"; +import ClavaJoinPoints from "../../ClavaJoinPoints.ts"; /*Check for the presence of strcpy functions*/ diff --git a/Clava-JS/src-api/clava/analysis/checkers/SyslogChecker.ts b/Clava-JS/api/clava/analysis/checkers/SyslogChecker.ts similarity index 79% rename from Clava-JS/src-api/clava/analysis/checkers/SyslogChecker.ts rename to Clava-JS/api/clava/analysis/checkers/SyslogChecker.ts index d418e80a69..2859e8cc79 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/SyslogChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/SyslogChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of syslog functions diff --git a/Clava-JS/src-api/clava/analysis/checkers/SystemChecker.ts b/Clava-JS/api/clava/analysis/checkers/SystemChecker.ts similarity index 80% rename from Clava-JS/src-api/clava/analysis/checkers/SystemChecker.ts rename to Clava-JS/api/clava/analysis/checkers/SystemChecker.ts index 8fc727477d..16bd5b16fc 100644 --- a/Clava-JS/src-api/clava/analysis/checkers/SystemChecker.ts +++ b/Clava-JS/api/clava/analysis/checkers/SystemChecker.ts @@ -1,6 +1,6 @@ -import { Call, Joinpoint } from "../../../Joinpoints.js"; -import Checker from "../Checker.js"; -import CheckResult from "../CheckResult.js"; +import { Call, Joinpoint } from "../../../Joinpoints.ts"; +import Checker from "../Checker.ts"; +import CheckResult from "../CheckResult.ts"; /** * Check for the presence of system functions diff --git a/Clava-JS/src-api/clava/cmake/CMaker.ts b/Clava-JS/api/clava/cmake/CMaker.ts similarity index 96% rename from Clava-JS/src-api/clava/cmake/CMaker.ts rename to Clava-JS/api/clava/cmake/CMaker.ts index f53c7641ae..142c61addf 100644 --- a/Clava-JS/src-api/clava/cmake/CMaker.ts +++ b/Clava-JS/api/clava/cmake/CMaker.ts @@ -1,18 +1,18 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import Platforms from "@specs-feup/lara/api/lara/Platforms.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import Platforms from "@specs-feup/lara/api/lara/Platforms.ts"; import { arrayFromArgs, debug, debugObject, -} from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import ProcessExecutor from "@specs-feup/lara/api/lara/util/ProcessExecutor.js"; -import { FileJp } from "../../Joinpoints.js"; -import Clava from "../Clava.js"; -import CMakerSources from "./CMakerSources.js"; -import CMakerUtils from "./CMakerUtils.js"; -import CMakeCompiler from "./compilers/CMakeCompiler.js"; -import BenchmarkCompilationEngine from "@specs-feup/lara/api/lara/benchmark/BenchmarkCompilationEngine.js"; +} from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import ProcessExecutor from "@specs-feup/lara/api/lara/util/ProcessExecutor.ts"; +import { FileJp } from "../../Joinpoints.ts"; +import Clava from "../Clava.ts"; +import CMakerSources from "./CMakerSources.ts"; +import CMakerUtils from "./CMakerUtils.ts"; +import CMakeCompiler from "./compilers/CMakeCompiler.ts"; +import BenchmarkCompilationEngine from "@specs-feup/lara/api/lara/benchmark/BenchmarkCompilationEngine.ts"; /** * Builds CMake configurations. diff --git a/Clava-JS/src-api/clava/cmake/CMakerSources.ts b/Clava-JS/api/clava/cmake/CMakerSources.ts similarity index 95% rename from Clava-JS/src-api/clava/cmake/CMakerSources.ts rename to Clava-JS/api/clava/cmake/CMakerSources.ts index 5d64d8c1c5..aa5ea4a490 100644 --- a/Clava-JS/src-api/clava/cmake/CMakerSources.ts +++ b/Clava-JS/api/clava/cmake/CMakerSources.ts @@ -1,8 +1,8 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import Clava from "../Clava.js"; -import CMakerUtils from "./CMakerUtils.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import Clava from "../Clava.ts"; +import CMakerUtils from "./CMakerUtils.ts"; /** * Contains CMaker sources diff --git a/Clava-JS/src-api/clava/cmake/CMakerUtils.ts b/Clava-JS/api/clava/cmake/CMakerUtils.ts similarity index 92% rename from Clava-JS/src-api/clava/cmake/CMakerUtils.ts rename to Clava-JS/api/clava/cmake/CMakerUtils.ts index eff7c88906..c6f21edade 100644 --- a/Clava-JS/src-api/clava/cmake/CMakerUtils.ts +++ b/Clava-JS/api/clava/cmake/CMakerUtils.ts @@ -1,6 +1,6 @@ -import ToolUtils from "@specs-feup/lara/api/lara/tool/ToolUtils.js"; -import CMakeCompiler from "./compilers/CMakeCompiler.js"; -import GenericCMakeCompiler from "./compilers/GenericCMakeCompiler.js"; +import ToolUtils from "@specs-feup/lara/api/lara/tool/ToolUtils.ts"; +import CMakeCompiler from "./compilers/CMakeCompiler.ts"; +import GenericCMakeCompiler from "./compilers/GenericCMakeCompiler.ts"; export default class CMakerUtils extends ToolUtils { private static compilerTable = { diff --git a/Clava-JS/src-api/clava/cmake/compilers/CMakeCompiler.ts b/Clava-JS/api/clava/cmake/compilers/CMakeCompiler.ts similarity index 100% rename from Clava-JS/src-api/clava/cmake/compilers/CMakeCompiler.ts rename to Clava-JS/api/clava/cmake/compilers/CMakeCompiler.ts diff --git a/Clava-JS/src-api/clava/cmake/compilers/GenericCMakeCompiler.ts b/Clava-JS/api/clava/cmake/compilers/GenericCMakeCompiler.ts similarity index 87% rename from Clava-JS/src-api/clava/cmake/compilers/GenericCMakeCompiler.ts rename to Clava-JS/api/clava/cmake/compilers/GenericCMakeCompiler.ts index 75d817f142..246ca1c289 100644 --- a/Clava-JS/src-api/clava/cmake/compilers/GenericCMakeCompiler.ts +++ b/Clava-JS/api/clava/cmake/compilers/GenericCMakeCompiler.ts @@ -1,4 +1,4 @@ -import CMakeCompiler from "./CMakeCompiler.js"; +import CMakeCompiler from "./CMakeCompiler.ts"; /** * Iterates over a list of values. diff --git a/Clava-JS/src-api/clava/code/DecomposeResult.ts b/Clava-JS/api/clava/code/DecomposeResult.ts similarity index 92% rename from Clava-JS/src-api/clava/code/DecomposeResult.ts rename to Clava-JS/api/clava/code/DecomposeResult.ts index 81648819a1..fa8edfe5e9 100644 --- a/Clava-JS/src-api/clava/code/DecomposeResult.ts +++ b/Clava-JS/api/clava/code/DecomposeResult.ts @@ -1,4 +1,4 @@ -import { Expression, Statement } from "../../Joinpoints.js"; +import { Expression, Statement } from "../../Joinpoints.ts"; export default class DecomposeResult { precedingStmts: Statement[]; diff --git a/Clava-JS/src-api/clava/code/DoToWhileStmt.ts b/Clava-JS/api/clava/code/DoToWhileStmt.ts similarity index 94% rename from Clava-JS/src-api/clava/code/DoToWhileStmt.ts rename to Clava-JS/api/clava/code/DoToWhileStmt.ts index 0c6d4b07db..0c97a1cfa1 100644 --- a/Clava-JS/src-api/clava/code/DoToWhileStmt.ts +++ b/Clava-JS/api/clava/code/DoToWhileStmt.ts @@ -1,5 +1,5 @@ -import { Joinpoint, Loop } from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +import { Joinpoint, Loop } from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; export default function DoToWhileStmt($doStmt: Loop, labelSuffix: number | string) { // do statements have an unconditional first iteration diff --git a/Clava-JS/src-api/clava/code/ForToWhileStmt.ts b/Clava-JS/api/clava/code/ForToWhileStmt.ts similarity index 95% rename from Clava-JS/src-api/clava/code/ForToWhileStmt.ts rename to Clava-JS/api/clava/code/ForToWhileStmt.ts index 50461555d1..4a9d4af209 100644 --- a/Clava-JS/src-api/clava/code/ForToWhileStmt.ts +++ b/Clava-JS/api/clava/code/ForToWhileStmt.ts @@ -1,5 +1,5 @@ -import { EmptyStmt, Joinpoint, Loop } from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +import { EmptyStmt, Joinpoint, Loop } from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; /** * Replaces for loop with an equivalent construct based on a while loop: diff --git a/Clava-JS/src-api/clava/code/GlobalVariable.ts b/Clava-JS/api/clava/code/GlobalVariable.ts similarity index 95% rename from Clava-JS/src-api/clava/code/GlobalVariable.ts rename to Clava-JS/api/clava/code/GlobalVariable.ts index ee251bc458..ef9c8718ed 100644 --- a/Clava-JS/src-api/clava/code/GlobalVariable.ts +++ b/Clava-JS/api/clava/code/GlobalVariable.ts @@ -1,5 +1,5 @@ -import { FileJp, Joinpoint, Type, Varref } from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +import { FileJp, Joinpoint, Type, Varref } from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; /** * Adds and manages global variables. diff --git a/Clava-JS/src-api/clava/code/Inliner.ts b/Clava-JS/api/clava/code/Inliner.ts similarity index 98% rename from Clava-JS/src-api/clava/code/Inliner.ts rename to Clava-JS/api/clava/code/Inliner.ts index 0ca32aa9d8..1bdeb8ac58 100644 --- a/Clava-JS/src-api/clava/code/Inliner.ts +++ b/Clava-JS/api/clava/code/Inliner.ts @@ -1,6 +1,6 @@ -import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.ts"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { BinaryOp, Call, @@ -23,8 +23,8 @@ import { Vardecl, VariableArrayType, Varref, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; export interface InlinerOptions { prefix?: string; diff --git a/Clava-JS/src-api/clava/code/Outliner.ts b/Clava-JS/api/clava/code/Outliner.ts similarity index 99% rename from Clava-JS/src-api/clava/code/Outliner.ts rename to Clava-JS/api/clava/code/Outliner.ts index 2a0ea6f954..26833ac281 100644 --- a/Clava-JS/src-api/clava/code/Outliner.ts +++ b/Clava-JS/api/clava/code/Outliner.ts @@ -1,5 +1,5 @@ -import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { AdjustedType, ArrayType, @@ -18,8 +18,8 @@ import { Statement, Vardecl, Varref, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; export default class Outliner { private verbose: boolean = true; diff --git a/Clava-JS/src-api/clava/code/RemoveShadowing.ts b/Clava-JS/api/clava/code/RemoveShadowing.ts similarity index 90% rename from Clava-JS/src-api/clava/code/RemoveShadowing.ts rename to Clava-JS/api/clava/code/RemoveShadowing.ts index 205b984013..81fb4d6a6e 100644 --- a/Clava-JS/src-api/clava/code/RemoveShadowing.ts +++ b/Clava-JS/api/clava/code/RemoveShadowing.ts @@ -1,4 +1,4 @@ -import { FunctionJp, Vardecl } from "../../Joinpoints.js"; +import { FunctionJp, Vardecl } from "../../Joinpoints.ts"; export default function RemoveShadowing($function: FunctionJp): void { const usedNames = new Set(); diff --git a/Clava-JS/src-api/clava/code/SimplifyAssignment.ts b/Clava-JS/api/clava/code/SimplifyAssignment.ts similarity index 90% rename from Clava-JS/src-api/clava/code/SimplifyAssignment.ts rename to Clava-JS/api/clava/code/SimplifyAssignment.ts index 0352c44461..7327677ea8 100644 --- a/Clava-JS/src-api/clava/code/SimplifyAssignment.ts +++ b/Clava-JS/api/clava/code/SimplifyAssignment.ts @@ -1,5 +1,5 @@ -import { BinaryOp, Expression, OpKind } from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +import { BinaryOp, Expression, OpKind } from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; /** * Simplifies assignments of the type `a += b` into the equivalent expression `a = a + b` diff --git a/Clava-JS/src-api/clava/code/SimplifyTernaryOp.ts b/Clava-JS/api/clava/code/SimplifyTernaryOp.ts similarity index 96% rename from Clava-JS/src-api/clava/code/SimplifyTernaryOp.ts rename to Clava-JS/api/clava/code/SimplifyTernaryOp.ts index 8ba7813fdb..abbd538042 100644 --- a/Clava-JS/src-api/clava/code/SimplifyTernaryOp.ts +++ b/Clava-JS/api/clava/code/SimplifyTernaryOp.ts @@ -1,5 +1,5 @@ -import { BinaryOp, ExprStmt, TernaryOp } from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +import { BinaryOp, ExprStmt, TernaryOp } from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; /** * Simplifies a statement like: diff --git a/Clava-JS/src-api/clava/code/StatementDecomposer.ts b/Clava-JS/api/clava/code/StatementDecomposer.ts similarity index 98% rename from Clava-JS/src-api/clava/code/StatementDecomposer.ts rename to Clava-JS/api/clava/code/StatementDecomposer.ts index 00ba26bcaf..675125d501 100644 --- a/Clava-JS/src-api/clava/code/StatementDecomposer.ts +++ b/Clava-JS/api/clava/code/StatementDecomposer.ts @@ -1,4 +1,4 @@ -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; import { BinaryOp, Call, @@ -18,9 +18,9 @@ import { TernaryOp, UnaryOp, Vardecl, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import DecomposeResult from "./DecomposeResult.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import DecomposeResult from "./DecomposeResult.ts"; /** * Decomposes complex statements into several simpler ones. diff --git a/Clava-JS/src-api/clava/gprofer/Gprofer.ts b/Clava-JS/api/clava/gprofer/Gprofer.ts similarity index 93% rename from Clava-JS/src-api/clava/gprofer/Gprofer.ts rename to Clava-JS/api/clava/gprofer/Gprofer.ts index 08761c91d6..35c9988490 100644 --- a/Clava-JS/src-api/clava/gprofer/Gprofer.ts +++ b/Clava-JS/api/clava/gprofer/Gprofer.ts @@ -1,13 +1,13 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import Strings from "@specs-feup/lara/api/lara/Strings.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import Strings from "@specs-feup/lara/api/lara/Strings.ts"; import JavaTypes, { type JavaClasses, -} from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { FileJp, FunctionJp } from "../../Joinpoints.js"; -import Clava from "../Clava.js"; -import CMaker from "../cmake/CMaker.js"; -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; +} from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { FileJp, FunctionJp } from "../../Joinpoints.ts"; +import Clava from "../Clava.ts"; +import CMaker from "../cmake/CMaker.ts"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; function GproferGetCxxFunction(signature: string) { return Query.search(FunctionJp, { diff --git a/Clava-JS/src-api/clava/graphs/ControlFlowGraph.ts b/Clava-JS/api/clava/graphs/ControlFlowGraph.ts similarity index 93% rename from Clava-JS/src-api/clava/graphs/ControlFlowGraph.ts rename to Clava-JS/api/clava/graphs/ControlFlowGraph.ts index dbc93107a7..335742a988 100644 --- a/Clava-JS/src-api/clava/graphs/ControlFlowGraph.ts +++ b/Clava-JS/api/clava/graphs/ControlFlowGraph.ts @@ -1,7 +1,7 @@ -import Graph from "@specs-feup/lara/api/lara/graphs/Graph.js"; +import Graph from "@specs-feup/lara/api/lara/graphs/Graph.ts"; import cytoscape from "cytoscape"; -import { Statement } from "../../Joinpoints.js"; -import CfgBuilder from "./cfg/CfgBuilder.js"; +import { Statement } from "../../Joinpoints.ts"; +import CfgBuilder from "./cfg/CfgBuilder.ts"; export default class ControlFlowGraph extends Graph { /** diff --git a/Clava-JS/src-api/clava/graphs/StaticCallGraph.ts b/Clava-JS/api/clava/graphs/StaticCallGraph.ts similarity index 87% rename from Clava-JS/src-api/clava/graphs/StaticCallGraph.ts rename to Clava-JS/api/clava/graphs/StaticCallGraph.ts index b28f2edea2..7155ef0868 100644 --- a/Clava-JS/src-api/clava/graphs/StaticCallGraph.ts +++ b/Clava-JS/api/clava/graphs/StaticCallGraph.ts @@ -1,10 +1,10 @@ -import DotFormatter from "@specs-feup/lara/api/lara/graphs/DotFormatter.js"; -import Graph from "@specs-feup/lara/api/lara/graphs/Graph.js"; -import Graphs from "@specs-feup/lara/api/lara/graphs/Graphs.js"; +import DotFormatter from "@specs-feup/lara/api/lara/graphs/DotFormatter.ts"; +import Graph from "@specs-feup/lara/api/lara/graphs/Graph.ts"; +import Graphs from "@specs-feup/lara/api/lara/graphs/Graphs.ts"; import cytoscape from "cytoscape"; -import { FunctionJp, Joinpoint } from "../../Joinpoints.js"; -import ScgNodeData from "./scg/ScgNodeData.js"; -import StaticCallGraphBuilder from "./scg/StaticCallGraphBuilder.js"; +import { FunctionJp, Joinpoint } from "../../Joinpoints.ts"; +import ScgNodeData from "./scg/ScgNodeData.ts"; +import StaticCallGraphBuilder from "./scg/StaticCallGraphBuilder.ts"; export default class StaticCallGraph extends Graph { private static dotFormatterInstance: DotFormatter | undefined = undefined; diff --git a/Clava-JS/src-api/clava/graphs/cfg/CfgBuilder.ts b/Clava-JS/api/clava/graphs/cfg/CfgBuilder.ts similarity index 96% rename from Clava-JS/src-api/clava/graphs/cfg/CfgBuilder.ts rename to Clava-JS/api/clava/graphs/cfg/CfgBuilder.ts index cf8e1a430a..0b279d7724 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/CfgBuilder.ts +++ b/Clava-JS/api/clava/graphs/cfg/CfgBuilder.ts @@ -1,7 +1,7 @@ -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import Graphs from "@specs-feup/lara/api/lara/graphs/Graphs.js"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import Graphs from "@specs-feup/lara/api/lara/graphs/Graphs.ts"; import cytoscape from "cytoscape"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { Break, Continue, @@ -13,25 +13,25 @@ import { Scope, Statement, Switch, -} from "../../../Joinpoints.js"; -import ClavaJoinPoints from "../../ClavaJoinPoints.js"; -import CfgEdge from "./CfgEdge.js"; -import CfgEdgeType from "./CfgEdgeType.js"; -import CfgNodeData from "./CfgNodeData.js"; -import CfgNodeType from "./CfgNodeType.js"; -import CfgUtils from "./CfgUtils.js"; -import NextCfgNode from "./NextCfgNode.js"; -import CaseData from "./nodedata/CaseData.js"; -import DataFactory from "./nodedata/DataFactory.js"; -import HeaderData from "./nodedata/HeaderData.js"; -import IfData from "./nodedata/IfData.js"; -import InstListNodeData from "./nodedata/InstListNodeData.js"; -import LoopData from "./nodedata/LoopData.js"; -import ScopeNodeData from "./nodedata/ScopeNodeData.js"; -import SwitchData from "./nodedata/SwitchData.js"; -import GotoData from "./nodedata/GotoData.js"; -import LabelData from "./nodedata/LabelData.js"; -import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; +} from "../../../Joinpoints.ts"; +import ClavaJoinPoints from "../../ClavaJoinPoints.ts"; +import CfgEdge from "./CfgEdge.ts"; +import CfgEdgeType from "./CfgEdgeType.ts"; +import CfgNodeData from "./CfgNodeData.ts"; +import CfgNodeType from "./CfgNodeType.ts"; +import CfgUtils from "./CfgUtils.ts"; +import NextCfgNode from "./NextCfgNode.ts"; +import CaseData from "./nodedata/CaseData.ts"; +import DataFactory from "./nodedata/DataFactory.ts"; +import HeaderData from "./nodedata/HeaderData.ts"; +import IfData from "./nodedata/IfData.ts"; +import InstListNodeData from "./nodedata/InstListNodeData.ts"; +import LoopData from "./nodedata/LoopData.ts"; +import ScopeNodeData from "./nodedata/ScopeNodeData.ts"; +import SwitchData from "./nodedata/SwitchData.ts"; +import GotoData from "./nodedata/GotoData.ts"; +import LabelData from "./nodedata/LabelData.ts"; +import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.ts"; export default class CfgBuilder { /** diff --git a/Clava-JS/src-api/clava/graphs/cfg/CfgEdge.ts b/Clava-JS/api/clava/graphs/cfg/CfgEdge.ts similarity index 94% rename from Clava-JS/src-api/clava/graphs/cfg/CfgEdge.ts rename to Clava-JS/api/clava/graphs/cfg/CfgEdge.ts index ec0c591218..543a869cb6 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/CfgEdge.ts +++ b/Clava-JS/api/clava/graphs/cfg/CfgEdge.ts @@ -1,5 +1,5 @@ -import EdgeData from "@specs-feup/lara/api/lara/graphs/EdgeData.js"; -import CfgEdgeType from "./CfgEdgeType.js"; +import EdgeData from "@specs-feup/lara/api/lara/graphs/EdgeData.ts"; +import CfgEdgeType from "./CfgEdgeType.ts"; /** * An edge of the CFG diff --git a/Clava-JS/src-api/clava/graphs/cfg/CfgEdgeType.ts b/Clava-JS/api/clava/graphs/cfg/CfgEdgeType.ts similarity index 100% rename from Clava-JS/src-api/clava/graphs/cfg/CfgEdgeType.ts rename to Clava-JS/api/clava/graphs/cfg/CfgEdgeType.ts diff --git a/Clava-JS/src-api/clava/graphs/cfg/CfgNodeData.ts b/Clava-JS/api/clava/graphs/cfg/CfgNodeData.ts similarity index 95% rename from Clava-JS/src-api/clava/graphs/cfg/CfgNodeData.ts rename to Clava-JS/api/clava/graphs/cfg/CfgNodeData.ts index c35eb7903d..628453511a 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/CfgNodeData.ts +++ b/Clava-JS/api/clava/graphs/cfg/CfgNodeData.ts @@ -1,6 +1,6 @@ -import NodeData from "@specs-feup/lara/api/lara/graphs/NodeData.js"; -import { Statement } from "../../../Joinpoints.js"; -import CfgNodeType from "./CfgNodeType.js"; +import NodeData from "@specs-feup/lara/api/lara/graphs/NodeData.ts"; +import { Statement } from "../../../Joinpoints.ts"; +import CfgNodeType from "./CfgNodeType.ts"; /** * The data of a CFG node. diff --git a/Clava-JS/src-api/clava/graphs/cfg/CfgNodeType.ts b/Clava-JS/api/clava/graphs/cfg/CfgNodeType.ts similarity index 100% rename from Clava-JS/src-api/clava/graphs/cfg/CfgNodeType.ts rename to Clava-JS/api/clava/graphs/cfg/CfgNodeType.ts diff --git a/Clava-JS/src-api/clava/graphs/cfg/CfgUtils.ts b/Clava-JS/api/clava/graphs/cfg/CfgUtils.ts similarity index 96% rename from Clava-JS/src-api/clava/graphs/cfg/CfgUtils.ts rename to Clava-JS/api/clava/graphs/cfg/CfgUtils.ts index 02507f6bec..b1e268aab9 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/CfgUtils.ts +++ b/Clava-JS/api/clava/graphs/cfg/CfgUtils.ts @@ -11,10 +11,10 @@ import { Scope, Statement, Switch, -} from "../../../Joinpoints.js"; -import CfgEdge from "./CfgEdge.js"; -import CfgEdgeType from "./CfgEdgeType.js"; -import CfgNodeType from "./CfgNodeType.js"; +} from "../../../Joinpoints.ts"; +import CfgEdge from "./CfgEdge.ts"; +import CfgEdgeType from "./CfgEdgeType.ts"; +import CfgNodeType from "./CfgNodeType.ts"; export default class CfgUtils { /** diff --git a/Clava-JS/src-api/clava/graphs/cfg/NextCfgNode.ts b/Clava-JS/api/clava/graphs/cfg/NextCfgNode.ts similarity index 99% rename from Clava-JS/src-api/clava/graphs/cfg/NextCfgNode.ts rename to Clava-JS/api/clava/graphs/cfg/NextCfgNode.ts index 09b3af62b3..c1981e3957 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/NextCfgNode.ts +++ b/Clava-JS/api/clava/graphs/cfg/NextCfgNode.ts @@ -7,7 +7,7 @@ import { LoopKind, Scope, Statement, -} from "../../../Joinpoints.js"; +} from "../../../Joinpoints.ts"; export default class NextCfgNode { /** diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/CaseData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/CaseData.ts similarity index 77% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/CaseData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/CaseData.ts index a7946bb6fc..0402b0bc69 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/CaseData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/CaseData.ts @@ -1,6 +1,6 @@ -import { Case } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { Case } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class CaseData extends CfgNodeData { constructor($stmt?: Case, id?: string) { diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/DataFactory.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/DataFactory.ts similarity index 81% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/DataFactory.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/DataFactory.ts index 3b274059fd..70e59a2fd0 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/DataFactory.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/DataFactory.ts @@ -8,19 +8,19 @@ import { Scope, Statement, Switch, -} from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; -import CaseData from "./CaseData.js"; -import GotoData from "./GotoData.js"; -import HeaderData from "./HeaderData.js"; -import IfData from "./IfData.js"; -import InstListNodeData from "./InstListNodeData.js"; -import LabelData from "./LabelData.js"; -import LoopData from "./LoopData.js"; -import ReturnData from "./ReturnData.js"; -import ScopeNodeData from "./ScopeNodeData.js"; -import SwitchData from "./SwitchData.js"; +} from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; +import CaseData from "./CaseData.ts"; +import GotoData from "./GotoData.ts"; +import HeaderData from "./HeaderData.ts"; +import IfData from "./IfData.ts"; +import InstListNodeData from "./InstListNodeData.ts"; +import LabelData from "./LabelData.ts"; +import LoopData from "./LoopData.ts"; +import ReturnData from "./ReturnData.ts"; +import ScopeNodeData from "./ScopeNodeData.ts"; +import SwitchData from "./SwitchData.ts"; export default class DataFactory { private entryPoint: Statement; diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/GotoData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/GotoData.ts similarity index 69% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/GotoData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/GotoData.ts index 82f9ed8b04..a331483cf3 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/GotoData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/GotoData.ts @@ -1,6 +1,6 @@ -import { GotoStmt } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { GotoStmt } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class GotoData extends CfgNodeData { constructor($stmt?: GotoStmt, id?: string) { diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/HeaderData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/HeaderData.ts similarity index 76% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/HeaderData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/HeaderData.ts index 909cd0821d..7c35ed28d5 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/HeaderData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/HeaderData.ts @@ -1,6 +1,6 @@ -import { Statement } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { Statement } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class HeaderData extends CfgNodeData { constructor($stmt: Statement | undefined, nodeType: CfgNodeType, id: string | undefined) { diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/IfData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/IfData.ts similarity index 72% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/IfData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/IfData.ts index 657dacd09b..492c44980b 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/IfData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/IfData.ts @@ -1,6 +1,6 @@ -import { If } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { If } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class IfData extends CfgNodeData { constructor($stmt?: If, id?: string) { diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/InstListNodeData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/InstListNodeData.ts similarity index 89% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/InstListNodeData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/InstListNodeData.ts index 8640504648..f262629f98 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/InstListNodeData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/InstListNodeData.ts @@ -1,7 +1,7 @@ -import { Statement } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; -import CfgUtils from "../CfgUtils.js"; +import { Statement } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; +import CfgUtils from "../CfgUtils.ts"; type T = Statement; diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/LabelData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/LabelData.ts similarity index 69% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/LabelData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/LabelData.ts index 86a6d06853..a9d7d1287b 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/LabelData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/LabelData.ts @@ -1,6 +1,6 @@ -import { LabelStmt } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { LabelStmt } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class LabelData extends CfgNodeData { constructor($stmt?: LabelStmt, id?: string) { diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/LoopData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/LoopData.ts similarity index 70% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/LoopData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/LoopData.ts index 876262fe56..26cce07896 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/LoopData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/LoopData.ts @@ -1,6 +1,6 @@ -import { Loop } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { Loop } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class LoopData extends CfgNodeData { constructor($stmt?: Loop, id?: string) { diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/ReturnData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/ReturnData.ts similarity index 72% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/ReturnData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/ReturnData.ts index 775b9ef6c7..e57060a73d 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/ReturnData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/ReturnData.ts @@ -1,6 +1,6 @@ -import { ReturnStmt } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { ReturnStmt } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class ReturnData extends CfgNodeData { constructor($stmt?: ReturnStmt, id?: string) { diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/ScopeNodeData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/ScopeNodeData.ts similarity index 66% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/ScopeNodeData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/ScopeNodeData.ts index c91d53c4e5..55334b4dba 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/ScopeNodeData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/ScopeNodeData.ts @@ -1,6 +1,6 @@ -import { Scope } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { Scope } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class ScopeNodeData extends CfgNodeData { private scopeStmt; diff --git a/Clava-JS/src-api/clava/graphs/cfg/nodedata/SwitchData.ts b/Clava-JS/api/clava/graphs/cfg/nodedata/SwitchData.ts similarity index 73% rename from Clava-JS/src-api/clava/graphs/cfg/nodedata/SwitchData.ts rename to Clava-JS/api/clava/graphs/cfg/nodedata/SwitchData.ts index 18c71188b2..7b28154c57 100644 --- a/Clava-JS/src-api/clava/graphs/cfg/nodedata/SwitchData.ts +++ b/Clava-JS/api/clava/graphs/cfg/nodedata/SwitchData.ts @@ -1,6 +1,6 @@ -import { Switch } from "../../../../Joinpoints.js"; -import CfgNodeData from "../CfgNodeData.js"; -import CfgNodeType from "../CfgNodeType.js"; +import { Switch } from "../../../../Joinpoints.ts"; +import CfgNodeData from "../CfgNodeData.ts"; +import CfgNodeType from "../CfgNodeType.ts"; export default class SwitchData extends CfgNodeData { constructor($stmt?: Switch, id?: string) { diff --git a/Clava-JS/src-api/clava/graphs/scg/ScgEdgeData.ts b/Clava-JS/api/clava/graphs/scg/ScgEdgeData.ts similarity index 88% rename from Clava-JS/src-api/clava/graphs/scg/ScgEdgeData.ts rename to Clava-JS/api/clava/graphs/scg/ScgEdgeData.ts index 1541821c64..fe08502a18 100644 --- a/Clava-JS/src-api/clava/graphs/scg/ScgEdgeData.ts +++ b/Clava-JS/api/clava/graphs/scg/ScgEdgeData.ts @@ -1,5 +1,5 @@ -import EdgeData from "@specs-feup/lara/api/lara/graphs/EdgeData.js"; -import { Call } from "../../../Joinpoints.js"; +import EdgeData from "@specs-feup/lara/api/lara/graphs/EdgeData.ts"; +import { Call } from "../../../Joinpoints.ts"; export default class ScgEdgeData extends EdgeData { /** diff --git a/Clava-JS/src-api/clava/graphs/scg/ScgNodeData.ts b/Clava-JS/api/clava/graphs/scg/ScgNodeData.ts similarity index 86% rename from Clava-JS/src-api/clava/graphs/scg/ScgNodeData.ts rename to Clava-JS/api/clava/graphs/scg/ScgNodeData.ts index 147227ec2a..042f2d086a 100644 --- a/Clava-JS/src-api/clava/graphs/scg/ScgNodeData.ts +++ b/Clava-JS/api/clava/graphs/scg/ScgNodeData.ts @@ -1,6 +1,6 @@ -import NodeData from "@specs-feup/lara/api/lara/graphs/NodeData.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { Call, FunctionJp } from "../../../Joinpoints.js"; +import NodeData from "@specs-feup/lara/api/lara/graphs/NodeData.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { Call, FunctionJp } from "../../../Joinpoints.ts"; export default class ScgNodeData extends NodeData { /** diff --git a/Clava-JS/src-api/clava/graphs/scg/StaticCallGraphBuilder.ts b/Clava-JS/api/clava/graphs/scg/StaticCallGraphBuilder.ts similarity index 95% rename from Clava-JS/src-api/clava/graphs/scg/StaticCallGraphBuilder.ts rename to Clava-JS/api/clava/graphs/scg/StaticCallGraphBuilder.ts index 8a00b55d35..f22959cbd7 100644 --- a/Clava-JS/src-api/clava/graphs/scg/StaticCallGraphBuilder.ts +++ b/Clava-JS/api/clava/graphs/scg/StaticCallGraphBuilder.ts @@ -1,10 +1,10 @@ -import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; -import Graphs from "@specs-feup/lara/api/lara/graphs/Graphs.js"; +import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.ts"; +import Graphs from "@specs-feup/lara/api/lara/graphs/Graphs.ts"; import cytoscape from "cytoscape"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { Call, FunctionJp, Joinpoint, Program } from "../../../Joinpoints.js"; -import ScgEdgeData from "./ScgEdgeData.js"; -import ScgNodeData from "./ScgNodeData.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { Call, FunctionJp, Joinpoint, Program } from "../../../Joinpoints.ts"; +import ScgEdgeData from "./ScgEdgeData.ts"; +import ScgNodeData from "./ScgNodeData.ts"; export default class StaticCallGraphBuilder { /** diff --git a/Clava-JS/src-api/clava/hdf5/Hdf5.ts b/Clava-JS/api/clava/hdf5/Hdf5.ts similarity index 98% rename from Clava-JS/src-api/clava/hdf5/Hdf5.ts rename to Clava-JS/api/clava/hdf5/Hdf5.ts index 886262e5c3..1a0905f0cb 100644 --- a/Clava-JS/src-api/clava/hdf5/Hdf5.ts +++ b/Clava-JS/api/clava/hdf5/Hdf5.ts @@ -5,9 +5,9 @@ import { RecordJp, TemplateSpecializationType, Type, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import Format from "../Format.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import Format from "../Format.ts"; /** * This is supposed to be an enum, but Node.js v25 does bot support TS' enums, only erasable-syntax. diff --git a/Clava-JS/src-api/clava/liveness/LivenessAnalyser.ts b/Clava-JS/api/clava/liveness/LivenessAnalyser.ts similarity index 96% rename from Clava-JS/src-api/clava/liveness/LivenessAnalyser.ts rename to Clava-JS/api/clava/liveness/LivenessAnalyser.ts index 1267b9aa7d..8ca5928b69 100644 --- a/Clava-JS/src-api/clava/liveness/LivenessAnalyser.ts +++ b/Clava-JS/api/clava/liveness/LivenessAnalyser.ts @@ -1,9 +1,9 @@ import cytoscape from "cytoscape"; -import { Case, Expression, If, Statement, Switch } from "../../Joinpoints.js"; -import ControlFlowGraph from "../graphs/ControlFlowGraph.js"; -import CfgNodeData from "../graphs/cfg/CfgNodeData.js"; -import CfgNodeType from "../graphs/cfg/CfgNodeType.js"; -import LivenessUtils from "./LivenessUtils.js"; +import { Case, Expression, If, Statement, Switch } from "../../Joinpoints.ts"; +import ControlFlowGraph from "../graphs/ControlFlowGraph.ts"; +import CfgNodeData from "../graphs/cfg/CfgNodeData.ts"; +import CfgNodeType from "../graphs/cfg/CfgNodeType.ts"; +import LivenessUtils from "./LivenessUtils.ts"; export default class LivenessAnalyser { /** diff --git a/Clava-JS/src-api/clava/liveness/LivenessAnalysis.ts b/Clava-JS/api/clava/liveness/LivenessAnalysis.ts similarity index 95% rename from Clava-JS/src-api/clava/liveness/LivenessAnalysis.ts rename to Clava-JS/api/clava/liveness/LivenessAnalysis.ts index e2c9ab7263..808cfcb807 100644 --- a/Clava-JS/src-api/clava/liveness/LivenessAnalysis.ts +++ b/Clava-JS/api/clava/liveness/LivenessAnalysis.ts @@ -1,6 +1,6 @@ import cytoscape from "cytoscape"; -import ControlFlowGraph from "../graphs/ControlFlowGraph.js"; -import LivenessAnalyser from "./LivenessAnalyser.js"; +import ControlFlowGraph from "../graphs/ControlFlowGraph.ts"; +import LivenessAnalyser from "./LivenessAnalyser.ts"; export default class LivenessAnalysis { /** diff --git a/Clava-JS/src-api/clava/liveness/LivenessUtils.ts b/Clava-JS/api/clava/liveness/LivenessUtils.ts similarity index 98% rename from Clava-JS/src-api/clava/liveness/LivenessUtils.ts rename to Clava-JS/api/clava/liveness/LivenessUtils.ts index ed65bcb7c0..d2601f36b5 100644 --- a/Clava-JS/src-api/clava/liveness/LivenessUtils.ts +++ b/Clava-JS/api/clava/liveness/LivenessUtils.ts @@ -1,13 +1,13 @@ -import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; +import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.ts"; import cytoscape from "cytoscape"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { BinaryOp, Expression, Statement, Vardecl, Varref, -} from "../../Joinpoints.js"; +} from "../../Joinpoints.ts"; export default class LivenessUtils { /** diff --git a/Clava-JS/src-api/clava/mpi/MpiAccessPattern.ts b/Clava-JS/api/clava/mpi/MpiAccessPattern.ts similarity index 91% rename from Clava-JS/src-api/clava/mpi/MpiAccessPattern.ts rename to Clava-JS/api/clava/mpi/MpiAccessPattern.ts index 652d7ec288..5573aa6cdb 100644 --- a/Clava-JS/src-api/clava/mpi/MpiAccessPattern.ts +++ b/Clava-JS/api/clava/mpi/MpiAccessPattern.ts @@ -1,4 +1,4 @@ -import { Varref } from "../../Joinpoints.js"; +import { Varref } from "../../Joinpoints.ts"; /** * Represents an MPI access pattern. diff --git a/Clava-JS/src-api/clava/mpi/MpiScatterGatherLoop.ts b/Clava-JS/api/clava/mpi/MpiScatterGatherLoop.ts similarity index 97% rename from Clava-JS/src-api/clava/mpi/MpiScatterGatherLoop.ts rename to Clava-JS/api/clava/mpi/MpiScatterGatherLoop.ts index c92d3e664a..063cfec669 100644 --- a/Clava-JS/src-api/clava/mpi/MpiScatterGatherLoop.ts +++ b/Clava-JS/api/clava/mpi/MpiScatterGatherLoop.ts @@ -1,9 +1,9 @@ -import { FileJp, FunctionJp, Loop, Varref } from "../../Joinpoints.js"; -import ClavaCode from "../ClavaCode.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import MpiAccessPattern from "./MpiAccessPattern.js"; -import MpiUtils from "./MpiUtils.js"; -import MpiAccessPatterns from "./patterns/MpiAccessPatterns.js"; +import { FileJp, FunctionJp, Loop, Varref } from "../../Joinpoints.ts"; +import ClavaCode from "../ClavaCode.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import MpiAccessPattern from "./MpiAccessPattern.ts"; +import MpiUtils from "./MpiUtils.ts"; +import MpiAccessPatterns from "./patterns/MpiAccessPatterns.ts"; /** * Applies an MPI scatter-gather strategy to loops. diff --git a/Clava-JS/src-api/clava/mpi/MpiUtils.ts b/Clava-JS/api/clava/mpi/MpiUtils.ts similarity index 90% rename from Clava-JS/src-api/clava/mpi/MpiUtils.ts rename to Clava-JS/api/clava/mpi/MpiUtils.ts index d57f4600f8..43c1950e77 100644 --- a/Clava-JS/src-api/clava/mpi/MpiUtils.ts +++ b/Clava-JS/api/clava/mpi/MpiUtils.ts @@ -1,4 +1,4 @@ -import { BuiltinType, Type } from "../../Joinpoints.js"; +import { BuiltinType, Type } from "../../Joinpoints.ts"; /** * Utility methods related to MPI. diff --git a/Clava-JS/src-api/clava/mpi/patterns/IterationVariablePattern.ts b/Clava-JS/api/clava/mpi/patterns/IterationVariablePattern.ts similarity index 97% rename from Clava-JS/src-api/clava/mpi/patterns/IterationVariablePattern.ts rename to Clava-JS/api/clava/mpi/patterns/IterationVariablePattern.ts index 49fb0b3683..2c49e27ba6 100644 --- a/Clava-JS/src-api/clava/mpi/patterns/IterationVariablePattern.ts +++ b/Clava-JS/api/clava/mpi/patterns/IterationVariablePattern.ts @@ -1,7 +1,7 @@ -import MpiAccessPattern from "../MpiAccessPattern.js"; -import MpiUtils from "../MpiUtils.js"; -import ClavaJoinPoints from "../../ClavaJoinPoints.js"; -import { ArrayType, PointerType, Type, Varref } from "../../../Joinpoints.js"; +import MpiAccessPattern from "../MpiAccessPattern.ts"; +import MpiUtils from "../MpiUtils.ts"; +import ClavaJoinPoints from "../../ClavaJoinPoints.ts"; +import { ArrayType, PointerType, Type, Varref } from "../../../Joinpoints.ts"; /** * Array that is accessed using only the iteration variable directly, without modifications. diff --git a/Clava-JS/src-api/clava/mpi/patterns/MpiAccessPatterns.ts b/Clava-JS/api/clava/mpi/patterns/MpiAccessPatterns.ts similarity index 88% rename from Clava-JS/src-api/clava/mpi/patterns/MpiAccessPatterns.ts rename to Clava-JS/api/clava/mpi/patterns/MpiAccessPatterns.ts index b7197d827b..ef50b8e1d6 100644 --- a/Clava-JS/src-api/clava/mpi/patterns/MpiAccessPatterns.ts +++ b/Clava-JS/api/clava/mpi/patterns/MpiAccessPatterns.ts @@ -1,5 +1,5 @@ -import ScalarPattern from "./ScalarPattern.js"; -import IterationVariablePattern from "./IterationVariablePattern.js"; +import ScalarPattern from "./ScalarPattern.ts"; +import IterationVariablePattern from "./IterationVariablePattern.ts"; /** diff --git a/Clava-JS/src-api/clava/mpi/patterns/ScalarPattern.ts b/Clava-JS/api/clava/mpi/patterns/ScalarPattern.ts similarity index 85% rename from Clava-JS/src-api/clava/mpi/patterns/ScalarPattern.ts rename to Clava-JS/api/clava/mpi/patterns/ScalarPattern.ts index ad0216d2d7..b6d4aa0622 100644 --- a/Clava-JS/src-api/clava/mpi/patterns/ScalarPattern.ts +++ b/Clava-JS/api/clava/mpi/patterns/ScalarPattern.ts @@ -1,5 +1,5 @@ -import { Varref } from '../../../Joinpoints.js'; -import MpiAccessPattern from '../MpiAccessPattern.js'; +import { Varref } from '../../../Joinpoints.ts'; +import MpiAccessPattern from '../MpiAccessPattern.ts'; /** * Access to a scalar variable. diff --git a/Clava-JS/src-api/clava/opencl/KernelReplacer.ts b/Clava-JS/api/clava/opencl/KernelReplacer.ts similarity index 98% rename from Clava-JS/src-api/clava/opencl/KernelReplacer.ts rename to Clava-JS/api/clava/opencl/KernelReplacer.ts index bed6f67381..1197165aba 100644 --- a/Clava-JS/src-api/clava/opencl/KernelReplacer.ts +++ b/Clava-JS/api/clava/opencl/KernelReplacer.ts @@ -1,5 +1,5 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import Strings from "@specs-feup/lara/api/lara/Strings.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import Strings from "@specs-feup/lara/api/lara/Strings.ts"; import { BuiltinType, Call, @@ -7,8 +7,8 @@ import { FunctionJp, Statement, Type, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; export interface OpenClKernelReplacerConfiguration { kernelName: string; diff --git a/Clava-JS/src-api/clava/opencl/KernelReplacerAuto.ts b/Clava-JS/api/clava/opencl/KernelReplacerAuto.ts similarity index 83% rename from Clava-JS/src-api/clava/opencl/KernelReplacerAuto.ts rename to Clava-JS/api/clava/opencl/KernelReplacerAuto.ts index a27bfc7c4b..5e75dd15b1 100644 --- a/Clava-JS/src-api/clava/opencl/KernelReplacerAuto.ts +++ b/Clava-JS/api/clava/opencl/KernelReplacerAuto.ts @@ -1,10 +1,10 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import Strings from "@specs-feup/lara/api/lara/Strings.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { Call, FileJp, Pragma } from "../../Joinpoints.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import Strings from "@specs-feup/lara/api/lara/Strings.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { Call, FileJp, Pragma } from "../../Joinpoints.ts"; import KernelReplacer, { type OpenClKernelReplacerConfiguration, -} from "./KernelReplacer.js"; +} from "./KernelReplacer.ts"; // This aspect can be included in a library, imported and // called by a user, since it needs no configuration/parameterization diff --git a/Clava-JS/src-api/clava/opencl/OpenCLCall.ts b/Clava-JS/api/clava/opencl/OpenCLCall.ts similarity index 92% rename from Clava-JS/src-api/clava/opencl/OpenCLCall.ts rename to Clava-JS/api/clava/opencl/OpenCLCall.ts index ee33f6d8c6..2fd5779552 100644 --- a/Clava-JS/src-api/clava/opencl/OpenCLCall.ts +++ b/Clava-JS/api/clava/opencl/OpenCLCall.ts @@ -1,8 +1,8 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import Platforms from "@specs-feup/lara/api/lara/Platforms.js"; -import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; -import { Call, FileJp, FunctionJp } from "../../Joinpoints.js"; -import OpenCLCallVariables from "./OpenCLCallVariables.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import Platforms from "@specs-feup/lara/api/lara/Platforms.ts"; +import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.ts"; +import { Call, FileJp, FunctionJp } from "../../Joinpoints.ts"; +import OpenCLCallVariables from "./OpenCLCallVariables.ts"; export default class OpenCLCall { $kernel: FunctionJp | undefined = undefined; diff --git a/Clava-JS/src-api/clava/opencl/OpenCLCallVariables.ts b/Clava-JS/api/clava/opencl/OpenCLCallVariables.ts similarity index 100% rename from Clava-JS/src-api/clava/opencl/OpenCLCallVariables.ts rename to Clava-JS/api/clava/opencl/OpenCLCallVariables.ts diff --git a/Clava-JS/src-api/clava/opt/Inlining.ts b/Clava-JS/api/clava/opt/Inlining.ts similarity index 74% rename from Clava-JS/src-api/clava/opt/Inlining.ts rename to Clava-JS/api/clava/opt/Inlining.ts index 0815cd4b61..f1c113c539 100644 --- a/Clava-JS/src-api/clava/opt/Inlining.ts +++ b/Clava-JS/api/clava/opt/Inlining.ts @@ -1,8 +1,8 @@ -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { FunctionJp, Joinpoint } from "../../Joinpoints.js"; -import Inliner from "../code/Inliner.js"; -import NormalizeToSubset from "./NormalizeToSubset.js"; -import PrepareForInlining from "./PrepareForInlining.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { FunctionJp, Joinpoint } from "../../Joinpoints.ts"; +import Inliner from "../code/Inliner.ts"; +import NormalizeToSubset from "./NormalizeToSubset.ts"; +import PrepareForInlining from "./PrepareForInlining.ts"; /** * diff --git a/Clava-JS/src-api/clava/opt/NormalizeToSubset.ts b/Clava-JS/api/clava/opt/NormalizeToSubset.ts similarity index 67% rename from Clava-JS/src-api/clava/opt/NormalizeToSubset.ts rename to Clava-JS/api/clava/opt/NormalizeToSubset.ts index 9d190a9adc..e1745d90d2 100644 --- a/Clava-JS/src-api/clava/opt/NormalizeToSubset.ts +++ b/Clava-JS/api/clava/opt/NormalizeToSubset.ts @@ -1,14 +1,13 @@ -import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { BinaryOp, Joinpoint } from "../../Joinpoints.js"; -import SimplifyAssignment from "../code/SimplifyAssignment.js"; -import StatementDecomposer from "../code/StatementDecomposer.js"; -import DecomposeDeclStmt from "../pass/DecomposeDeclStmt.js"; -import DecomposeVarDeclarations from "../pass/DecomposeVarDeclarations.js"; -import LocalStaticToGlobal from "../pass/LocalStaticToGlobal.js"; -import SimplifyLoops from "../pass/SimplifyLoops.js"; -import SimplifyReturnStmts from "../pass/SimplifyReturnStmts.js"; -import SimplifySelectionStmts from "../pass/SimplifySelectionStmts.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { BinaryOp, Joinpoint } from "../../Joinpoints.ts"; +import SimplifyAssignment from "../code/SimplifyAssignment.ts"; +import StatementDecomposer from "../code/StatementDecomposer.ts"; +import DecomposeDeclStmt from "../pass/DecomposeDeclStmt.ts"; +import DecomposeVarDeclarations from "../pass/DecomposeVarDeclarations.ts"; +import LocalStaticToGlobal from "../pass/LocalStaticToGlobal.ts"; +import SimplifyLoops from "../pass/SimplifyLoops.ts"; +import SimplifyReturnStmts from "../pass/SimplifyReturnStmts.ts"; +import SimplifySelectionStmts from "../pass/SimplifySelectionStmts.ts"; /** * diff --git a/Clava-JS/api/clava/opt/PrepareForInlining.ts b/Clava-JS/api/clava/opt/PrepareForInlining.ts new file mode 100644 index 0000000000..8c504edc79 --- /dev/null +++ b/Clava-JS/api/clava/opt/PrepareForInlining.ts @@ -0,0 +1,8 @@ +import { FunctionJp } from "../../Joinpoints.ts"; +import RemoveShadowing from "../code/RemoveShadowing.ts"; +import SingleReturnFunction from "../pass/SingleReturnFunction.ts"; + +export default function PrepareForInlining($function: FunctionJp) { + new SingleReturnFunction().apply($function); + RemoveShadowing($function); +} diff --git a/Clava-JS/src-api/clava/parser/BatchParser.ts b/Clava-JS/api/clava/parser/BatchParser.ts similarity index 91% rename from Clava-JS/src-api/clava/parser/BatchParser.ts rename to Clava-JS/api/clava/parser/BatchParser.ts index 69301fe23e..7abd05ce59 100644 --- a/Clava-JS/src-api/clava/parser/BatchParser.ts +++ b/Clava-JS/api/clava/parser/BatchParser.ts @@ -1,12 +1,12 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import Check from "@specs-feup/lara/api/lara/Check.js"; -import System from "@specs-feup/lara/api/lara/System.js"; -import Strings from "@specs-feup/lara/api/lara/Strings.js"; -import Clava from "../Clava.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { FileJp } from "../../Joinpoints.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import Check from "@specs-feup/lara/api/lara/Check.ts"; +import System from "@specs-feup/lara/api/lara/System.ts"; +import Strings from "@specs-feup/lara/api/lara/Strings.ts"; +import Clava from "../Clava.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import { FileJp } from "../../Joinpoints.ts"; /** * Parses C/C++ files. diff --git a/Clava-JS/src-api/clava/pass/DecomposeDeclStmt.ts b/Clava-JS/api/clava/pass/DecomposeDeclStmt.ts similarity index 89% rename from Clava-JS/src-api/clava/pass/DecomposeDeclStmt.ts rename to Clava-JS/api/clava/pass/DecomposeDeclStmt.ts index 4835cbe815..f23aaae5c4 100644 --- a/Clava-JS/src-api/clava/pass/DecomposeDeclStmt.ts +++ b/Clava-JS/api/clava/pass/DecomposeDeclStmt.ts @@ -1,7 +1,7 @@ -import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; -import { DeclStmt, Joinpoint } from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; +import { DeclStmt, Joinpoint } from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; /** * Decomposes composite declaration statements into separate statements for each variable. diff --git a/Clava-JS/src-api/clava/pass/DecomposeVarDeclarations.ts b/Clava-JS/api/clava/pass/DecomposeVarDeclarations.ts similarity index 94% rename from Clava-JS/src-api/clava/pass/DecomposeVarDeclarations.ts rename to Clava-JS/api/clava/pass/DecomposeVarDeclarations.ts index d83cf5929a..46703b2d90 100644 --- a/Clava-JS/src-api/clava/pass/DecomposeVarDeclarations.ts +++ b/Clava-JS/api/clava/pass/DecomposeVarDeclarations.ts @@ -1,12 +1,12 @@ -import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; +import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; import { ArrayType, Joinpoint, UndefinedType, Vardecl, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; /** * Decomposes the vardecl nodes that are reachable from the given join point. diff --git a/Clava-JS/src-api/clava/pass/LocalStaticToGlobal.ts b/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts similarity index 93% rename from Clava-JS/src-api/clava/pass/LocalStaticToGlobal.ts rename to Clava-JS/api/clava/pass/LocalStaticToGlobal.ts index d7663524d5..205e431aed 100644 --- a/Clava-JS/src-api/clava/pass/LocalStaticToGlobal.ts +++ b/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts @@ -1,14 +1,14 @@ -import PassTransformationError from "@specs-feup/lara/api/lara/pass/PassTransformationError.js"; -import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; +import PassTransformationError from "@specs-feup/lara/api/lara/pass/PassTransformationError.ts"; +import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; import { DeclStmt, FunctionJp, Joinpoint, StorageClass, Vardecl, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; /** * Transforms local static variables into global variables. diff --git a/Clava-JS/src-api/clava/pass/SimplifyLoops.ts b/Clava-JS/api/clava/pass/SimplifyLoops.ts similarity index 88% rename from Clava-JS/src-api/clava/pass/SimplifyLoops.ts rename to Clava-JS/api/clava/pass/SimplifyLoops.ts index c2da23b91a..cdf38e7088 100644 --- a/Clava-JS/src-api/clava/pass/SimplifyLoops.ts +++ b/Clava-JS/api/clava/pass/SimplifyLoops.ts @@ -1,10 +1,10 @@ -import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; -import { DeclStmt, ExprStmt, Joinpoint, Loop, LoopKind } from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import DoToWhileStmt from "../code/DoToWhileStmt.js"; -import ForToWhileStmt from "../code/ForToWhileStmt.js"; -import StatementDecomposer from "../code/StatementDecomposer.js"; +import Pass from "@specs-feup/lara/api/lara/pass/Pass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; +import { DeclStmt, ExprStmt, Joinpoint, Loop, LoopKind } from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import DoToWhileStmt from "../code/DoToWhileStmt.ts"; +import ForToWhileStmt from "../code/ForToWhileStmt.ts"; +import StatementDecomposer from "../code/StatementDecomposer.ts"; export default class SimplifyLoops extends Pass { protected _name = "SimplifyLoops"; diff --git a/Clava-JS/src-api/clava/pass/SimplifyReturnStmts.ts b/Clava-JS/api/clava/pass/SimplifyReturnStmts.ts similarity index 83% rename from Clava-JS/src-api/clava/pass/SimplifyReturnStmts.ts rename to Clava-JS/api/clava/pass/SimplifyReturnStmts.ts index d217f68a0d..c587d15467 100644 --- a/Clava-JS/src-api/clava/pass/SimplifyReturnStmts.ts +++ b/Clava-JS/api/clava/pass/SimplifyReturnStmts.ts @@ -1,8 +1,8 @@ -import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { Joinpoint, ReturnStmt } from "../../Joinpoints.js"; -import StatementDecomposer from "../code/StatementDecomposer.js"; +import Pass from "@specs-feup/lara/api/lara/pass/Pass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { Joinpoint, ReturnStmt } from "../../Joinpoints.ts"; +import StatementDecomposer from "../code/StatementDecomposer.ts"; // TODO: Refactor to use the SimplePass pattern export default class SimplifyReturnStmts extends Pass { diff --git a/Clava-JS/src-api/clava/pass/SimplifySelectionStmts.ts b/Clava-JS/api/clava/pass/SimplifySelectionStmts.ts similarity index 82% rename from Clava-JS/src-api/clava/pass/SimplifySelectionStmts.ts rename to Clava-JS/api/clava/pass/SimplifySelectionStmts.ts index 4f5d434c45..24c04f89d1 100644 --- a/Clava-JS/src-api/clava/pass/SimplifySelectionStmts.ts +++ b/Clava-JS/api/clava/pass/SimplifySelectionStmts.ts @@ -1,8 +1,8 @@ -import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { If, Joinpoint } from "../../Joinpoints.js"; -import StatementDecomposer from "../code/StatementDecomposer.js"; +import Pass from "@specs-feup/lara/api/lara/pass/Pass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { If, Joinpoint } from "../../Joinpoints.ts"; +import StatementDecomposer from "../code/StatementDecomposer.ts"; // TODO: Refactor to use the SimplePass pattern export default class SimplifySelectionStmts extends Pass { diff --git a/Clava-JS/src-api/clava/pass/SingleReturnFunction.ts b/Clava-JS/api/clava/pass/SingleReturnFunction.ts similarity index 91% rename from Clava-JS/src-api/clava/pass/SingleReturnFunction.ts rename to Clava-JS/api/clava/pass/SingleReturnFunction.ts index 3420317885..f3b892f932 100644 --- a/Clava-JS/src-api/clava/pass/SingleReturnFunction.ts +++ b/Clava-JS/api/clava/pass/SingleReturnFunction.ts @@ -1,15 +1,15 @@ -import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import Pass from "@specs-feup/lara/api/lara/pass/Pass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { BuiltinType, FunctionJp, Joinpoint, ReturnStmt, Vardecl, -} from "../../Joinpoints.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import DecomposeVarDeclarations from "./DecomposeVarDeclarations.js"; +} from "../../Joinpoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import DecomposeVarDeclarations from "./DecomposeVarDeclarations.ts"; export default class SingleReturnFunction extends Pass { protected _name = "SingleReturnFunctions"; diff --git a/Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts b/Clava-JS/api/clava/pass/TransformSwitchToIf.ts similarity index 97% rename from Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts rename to Clava-JS/api/clava/pass/TransformSwitchToIf.ts index c0d6df7fee..6a4cd85534 100644 --- a/Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts +++ b/Clava-JS/api/clava/pass/TransformSwitchToIf.ts @@ -1,7 +1,7 @@ -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.js"; -import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import SimplePass from "@specs-feup/lara/api/lara/pass/SimplePass.ts"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.ts"; import { Break, Case, @@ -11,8 +11,8 @@ import { OpKind, Statement, Switch, -} from "../../Joinpoints.js"; -import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js"; +} from "../../Joinpoints.ts"; +import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.ts"; /** * Transforms a switch statement into an if statement. diff --git a/Clava-JS/src-api/clava/stats/OpsBlock.ts b/Clava-JS/api/clava/stats/OpsBlock.ts similarity index 87% rename from Clava-JS/src-api/clava/stats/OpsBlock.ts rename to Clava-JS/api/clava/stats/OpsBlock.ts index ee7380138e..1f121d23ef 100644 --- a/Clava-JS/src-api/clava/stats/OpsBlock.ts +++ b/Clava-JS/api/clava/stats/OpsBlock.ts @@ -1,4 +1,4 @@ -import OpsCost from "./OpsCost.js"; +import OpsCost from "./OpsCost.ts"; export default class OpsBlock { diff --git a/Clava-JS/src-api/clava/stats/OpsCost.ts b/Clava-JS/api/clava/stats/OpsCost.ts similarity index 100% rename from Clava-JS/src-api/clava/stats/OpsCost.ts rename to Clava-JS/api/clava/stats/OpsCost.ts diff --git a/Clava-JS/src-api/clava/stats/OpsCounter.ts b/Clava-JS/api/clava/stats/OpsCounter.ts similarity index 95% rename from Clava-JS/src-api/clava/stats/OpsCounter.ts rename to Clava-JS/api/clava/stats/OpsCounter.ts index 8d3eccaf7b..ba22221e17 100644 --- a/Clava-JS/src-api/clava/stats/OpsCounter.ts +++ b/Clava-JS/api/clava/stats/OpsCounter.ts @@ -1,8 +1,8 @@ -import ClavaJoinPoints from "../ClavaJoinPoints.js"; -import GlobalVariable from "../code/GlobalVariable.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.js"; -import Logger from "../../lara/code/Logger.js"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; +import GlobalVariable from "../code/GlobalVariable.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.ts"; +import Logger from "../../lara/code/Logger.ts"; import { BuiltinType, Call, @@ -10,7 +10,7 @@ import { Joinpoint, Op, Type, -} from "../../Joinpoints.js"; +} from "../../Joinpoints.ts"; /** * Instruments an application so that it counts total operations in a region of code. diff --git a/Clava-JS/src-api/clava/stats/StaticOpsCounter.ts b/Clava-JS/api/clava/stats/StaticOpsCounter.ts similarity index 98% rename from Clava-JS/src-api/clava/stats/StaticOpsCounter.ts rename to Clava-JS/api/clava/stats/StaticOpsCounter.ts index 4a4ebd9fbe..e8842ffa25 100644 --- a/Clava-JS/src-api/clava/stats/StaticOpsCounter.ts +++ b/Clava-JS/api/clava/stats/StaticOpsCounter.ts @@ -1,5 +1,5 @@ -import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; +import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { BinaryOp, BuiltinType, @@ -17,8 +17,8 @@ import { Type, Vardecl, Varref, -} from "../../Joinpoints.js"; -import OpsBlock from "./OpsBlock.js"; +} from "../../Joinpoints.ts"; +import OpsBlock from "./OpsBlock.ts"; export default class StaticOpsCounter { // Whitelist of ops diff --git a/Clava-JS/src-api/clava/util/ClavaDataStore.ts b/Clava-JS/api/clava/util/ClavaDataStore.ts similarity index 93% rename from Clava-JS/src-api/clava/util/ClavaDataStore.ts rename to Clava-JS/api/clava/util/ClavaDataStore.ts index ed672b53a3..e8972a740f 100644 --- a/Clava-JS/src-api/clava/util/ClavaDataStore.ts +++ b/Clava-JS/api/clava/util/ClavaDataStore.ts @@ -1,11 +1,11 @@ -import WeaverDataStore from "@specs-feup/lara/api/weaver/util/WeaverDataStore.js"; -import ClavaJavaTypes from "../ClavaJavaTypes.js"; +import WeaverDataStore from "@specs-feup/lara/api/weaver/util/WeaverDataStore.ts"; +import ClavaJavaTypes from "../ClavaJavaTypes.ts"; import JavaTypes, { type JavaClasses, -} from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import Io from "@specs-feup/lara/api/lara/Io.js"; -import DataStore from "@specs-feup/lara/api/lara/util/DataStore.js"; -import { arrayFromArgs } from "@specs-feup/lara/api/lara/core/LaraCore.js"; +} from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import DataStore from "@specs-feup/lara/api/lara/util/DataStore.ts"; +import { arrayFromArgs } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; /** * DataStore used in Clava. diff --git a/Clava-JS/src-api/clava/util/CodeInserter.ts b/Clava-JS/api/clava/util/CodeInserter.ts similarity index 93% rename from Clava-JS/src-api/clava/util/CodeInserter.ts rename to Clava-JS/api/clava/util/CodeInserter.ts index f37b0c912b..b3aa88a6b6 100644 --- a/Clava-JS/src-api/clava/util/CodeInserter.ts +++ b/Clava-JS/api/clava/util/CodeInserter.ts @@ -1,7 +1,7 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import LineInserter from "@specs-feup/lara/api/lara/util/LineInserter.js"; -import { FileJp } from "../../Joinpoints.js"; -import Clava from "../Clava.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import LineInserter from "@specs-feup/lara/api/lara/util/LineInserter.ts"; +import { FileJp } from "../../Joinpoints.ts"; +import Clava from "../Clava.ts"; /** * Writes the original code of the application, with the possibility of inserting new lines of code. diff --git a/Clava-JS/src-api/clava/util/FileIterator.ts b/Clava-JS/api/clava/util/FileIterator.ts similarity index 95% rename from Clava-JS/src-api/clava/util/FileIterator.ts rename to Clava-JS/api/clava/util/FileIterator.ts index 9333de784a..592fbf70ba 100644 --- a/Clava-JS/src-api/clava/util/FileIterator.ts +++ b/Clava-JS/api/clava/util/FileIterator.ts @@ -1,8 +1,8 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import { FileJp } from "../../Joinpoints.js"; -import Clava from "../Clava.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import { debug } from "@specs-feup/lara/api/lara/core/LaraCore.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import { FileJp } from "../../Joinpoints.ts"; +import Clava from "../Clava.ts"; /** * Given a folder, collects sources in that folder, parses and returns one each time next() is called. diff --git a/Clava-JS/src-api/jest.config.js b/Clava-JS/api/jest.config.js similarity index 79% rename from Clava-JS/src-api/jest.config.js rename to Clava-JS/api/jest.config.js index c5e1ae31c2..0f0e072b60 100644 --- a/Clava-JS/src-api/jest.config.js +++ b/Clava-JS/api/jest.config.js @@ -1,4 +1,4 @@ -import { weaverConfig } from "../code/WeaverConfiguration.js"; +import { weaverConfig } from "../code/WeaverConfiguration.ts"; const config = { preset: "ts-jest/presets/default-esm", @@ -8,7 +8,6 @@ const config = { setupFiles: ["@specs-feup/lara/jest/setupFiles/sharedJavaModule.js"], setupFilesAfterEnv: ["@specs-feup/lara/jest/setupFiles/importSideEffects.js"], moduleNameMapper: { - "@specs-feup/clava/api/(.+).js": "@specs-feup/clava/src-api/$1", "(.+)\\.js": "$1", }, testEnvironmentOptions: { diff --git a/Clava-JS/src-api/lara/benchmark/ClavaBenchmarkInstance.ts b/Clava-JS/api/lara/benchmark/ClavaBenchmarkInstance.ts similarity index 89% rename from Clava-JS/src-api/lara/benchmark/ClavaBenchmarkInstance.ts rename to Clava-JS/api/lara/benchmark/ClavaBenchmarkInstance.ts index b31d487e58..66de5f2603 100644 --- a/Clava-JS/src-api/lara/benchmark/ClavaBenchmarkInstance.ts +++ b/Clava-JS/api/lara/benchmark/ClavaBenchmarkInstance.ts @@ -1,11 +1,11 @@ -import Io from "@specs-feup/lara/api/lara/Io.js"; -import BenchmarkInstance from "@specs-feup/lara/api/lara/benchmark/BenchmarkInstance.js"; -import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import Query from "@specs-feup/lara/api/weaver/Query.js"; -import Clava from "../..//clava/Clava.js"; -import { Pragma } from "../../Joinpoints.js"; -import CMaker from "../../clava/cmake/CMaker.js"; -import ClavaJoinPoints from "../../clava/ClavaJoinPoints.js"; +import Io from "@specs-feup/lara/api/lara/Io.ts"; +import BenchmarkInstance from "@specs-feup/lara/api/lara/benchmark/BenchmarkInstance.ts"; +import { type JavaClasses } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import Clava from "../..//clava/Clava.ts"; +import { Pragma } from "../../Joinpoints.ts"; +import CMaker from "../../clava/cmake/CMaker.ts"; +import ClavaJoinPoints from "../../clava/ClavaJoinPoints.ts"; /** * Instance of a Clava benchmark. diff --git a/Clava-JS/src-api/lara/code/Energy.ts b/Clava-JS/api/lara/code/Energy.ts similarity index 93% rename from Clava-JS/src-api/lara/code/Energy.ts rename to Clava-JS/api/lara/code/Energy.ts index 2fe4d6199f..02ce672fca 100644 --- a/Clava-JS/src-api/lara/code/Energy.ts +++ b/Clava-JS/api/lara/code/Energy.ts @@ -1,12 +1,12 @@ -import EnergyBase from "@specs-feup/lara/api/lara/code/EnergyBase.js"; +import EnergyBase from "@specs-feup/lara/api/lara/code/EnergyBase.ts"; -import Clava from "../../clava/Clava.js"; -import Logger from "./Logger.js"; +import Clava from "../../clava/Clava.ts"; +import Logger from "./Logger.ts"; -import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; -import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.js"; -import { FileJp, Joinpoint } from "../../Joinpoints.js"; -import { InsertPosition } from "@specs-feup/lara/api/LaraJoinPoint.js"; +import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.ts"; +import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.ts"; +import { FileJp, Joinpoint } from "../../Joinpoints.ts"; +import { InsertPosition } from "@specs-feup/lara/api/LaraJoinPoint.ts"; export default class Energy extends EnergyBase { /** diff --git a/Clava-JS/src-api/lara/code/Logger.ts b/Clava-JS/api/lara/code/Logger.ts similarity index 99% rename from Clava-JS/src-api/lara/code/Logger.ts rename to Clava-JS/api/lara/code/Logger.ts index 415a0ce678..96544fe6c8 100644 --- a/Clava-JS/src-api/lara/code/Logger.ts +++ b/Clava-JS/api/lara/code/Logger.ts @@ -1,14 +1,14 @@ -import LoggerBase from "@specs-feup/lara/api/lara/code/LoggerBase.js"; -import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; -import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.js"; +import LoggerBase from "@specs-feup/lara/api/lara/code/LoggerBase.ts"; +import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.ts"; +import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.ts"; import { Expression, FileJp, FunctionJp, Joinpoint, Scope, -} from "../../Joinpoints.js"; -import { InsertPosition } from "@specs-feup/lara/api/LaraJoinPoint.js"; +} from "../../Joinpoints.ts"; +import { InsertPosition } from "@specs-feup/lara/api/LaraJoinPoint.ts"; export default class Logger extends LoggerBase { private _useSpecsLogger: boolean; diff --git a/Clava-JS/src-api/lara/code/Timer.ts b/Clava-JS/api/lara/code/Timer.ts similarity index 97% rename from Clava-JS/src-api/lara/code/Timer.ts rename to Clava-JS/api/lara/code/Timer.ts index d224445929..a5bc93ee67 100644 --- a/Clava-JS/src-api/lara/code/Timer.ts +++ b/Clava-JS/api/lara/code/Timer.ts @@ -1,11 +1,11 @@ -import Platforms from "@specs-feup/lara/api/lara/Platforms.js"; -import TimerBase from "@specs-feup/lara/api/lara/code/TimerBase.js"; -import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.js"; -import { TimerUnit } from "@specs-feup/lara/api/lara/util/TimeUnits.js"; -import { FileJp, Joinpoint, Scope } from "../../Joinpoints.js"; -import Clava from "../../clava/Clava.js"; -import ClavaJoinPoints from "../../clava/ClavaJoinPoints.js"; -import Logger from "./Logger.js"; +import Platforms from "@specs-feup/lara/api/lara/Platforms.ts"; +import TimerBase from "@specs-feup/lara/api/lara/code/TimerBase.ts"; +import IdGenerator from "@specs-feup/lara/api/lara/util/IdGenerator.ts"; +import { TimerUnit } from "@specs-feup/lara/api/lara/util/TimeUnits.ts"; +import { FileJp, Joinpoint, Scope } from "../../Joinpoints.ts"; +import Clava from "../../clava/Clava.ts"; +import ClavaJoinPoints from "../../clava/ClavaJoinPoints.ts"; +import Logger from "./Logger.ts"; export default class Timer extends TimerBase { addedDefines: Set = new Set(); diff --git a/Clava-JS/src-api/lara/metrics/EnergyMetric.ts b/Clava-JS/api/lara/metrics/EnergyMetric.ts similarity index 82% rename from Clava-JS/src-api/lara/metrics/EnergyMetric.ts rename to Clava-JS/api/lara/metrics/EnergyMetric.ts index b049f001d1..ed14821d90 100644 --- a/Clava-JS/src-api/lara/metrics/EnergyMetric.ts +++ b/Clava-JS/api/lara/metrics/EnergyMetric.ts @@ -1,9 +1,9 @@ -import Strings from "@specs-feup/lara/api/lara/Strings.js"; -import Metric from "@specs-feup/lara/api/lara/metrics/Metric.js"; -import MetricResult from "@specs-feup/lara/api/lara/metrics/MetricResult.js"; -import ProcessExecutor from "@specs-feup/lara/api/lara/util/ProcessExecutor.js"; -import { Joinpoint } from "../../Joinpoints.js"; -import Energy from "../code/Energy.js"; +import Strings from "@specs-feup/lara/api/lara/Strings.ts"; +import Metric from "@specs-feup/lara/api/lara/metrics/Metric.ts"; +import MetricResult from "@specs-feup/lara/api/lara/metrics/MetricResult.ts"; +import ProcessExecutor from "@specs-feup/lara/api/lara/util/ProcessExecutor.ts"; +import { Joinpoint } from "../../Joinpoints.ts"; +import Energy from "../code/Energy.ts"; /** * Measures energy consumed during an application. diff --git a/Clava-JS/src-api/lara/metrics/ExecutionTimeMetric.ts b/Clava-JS/api/lara/metrics/ExecutionTimeMetric.ts similarity index 82% rename from Clava-JS/src-api/lara/metrics/ExecutionTimeMetric.ts rename to Clava-JS/api/lara/metrics/ExecutionTimeMetric.ts index 1fa7e319d4..055e7e0a5a 100644 --- a/Clava-JS/src-api/lara/metrics/ExecutionTimeMetric.ts +++ b/Clava-JS/api/lara/metrics/ExecutionTimeMetric.ts @@ -1,10 +1,10 @@ -import Strings from "@specs-feup/lara/api/lara/Strings.js"; -import Metric from "@specs-feup/lara/api/lara/metrics/Metric.js"; -import MetricResult from "@specs-feup/lara/api/lara/metrics/MetricResult.js"; -import ProcessExecutor from "@specs-feup/lara/api/lara/util/ProcessExecutor.js"; -import { TimerUnit } from "@specs-feup/lara/api/lara/util/TimeUnits.js"; -import { Joinpoint } from "../../Joinpoints.js"; -import Timer from "../code/Timer.js"; +import Strings from "@specs-feup/lara/api/lara/Strings.ts"; +import Metric from "@specs-feup/lara/api/lara/metrics/Metric.ts"; +import MetricResult from "@specs-feup/lara/api/lara/metrics/MetricResult.ts"; +import ProcessExecutor from "@specs-feup/lara/api/lara/util/ProcessExecutor.ts"; +import { TimerUnit } from "@specs-feup/lara/api/lara/util/TimeUnits.ts"; +import { Joinpoint } from "../../Joinpoints.ts"; +import Timer from "../code/Timer.ts"; /** * Measures execution time of an application. diff --git a/Clava-JS/src-code/tsconfig.json b/Clava-JS/api/tsconfig.json similarity index 65% rename from Clava-JS/src-code/tsconfig.json rename to Clava-JS/api/tsconfig.json index 62a2028ceb..c72c84e90d 100644 --- a/Clava-JS/src-code/tsconfig.json +++ b/Clava-JS/api/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "rootDir": ".", - "outDir": "../code", - "composite": true + "rootDir": "." }, "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] } diff --git a/Clava-JS/src-api/weaver/WeaverLauncher.ts b/Clava-JS/api/weaver/WeaverLauncher.ts similarity index 77% rename from Clava-JS/src-api/weaver/WeaverLauncher.ts rename to Clava-JS/api/weaver/WeaverLauncher.ts index 1faee2abae..dc7587324c 100644 --- a/Clava-JS/src-api/weaver/WeaverLauncher.ts +++ b/Clava-JS/api/weaver/WeaverLauncher.ts @@ -1,5 +1,5 @@ -import WeaverLauncherBase from "@specs-feup/lara/api/weaver/WeaverLauncherBase.js"; -import Clava from "../clava/Clava.js"; +import WeaverLauncherBase from "@specs-feup/lara/api/weaver/WeaverLauncherBase.ts"; +import Clava from "../clava/Clava.ts"; export default class WeaverLauncher extends WeaverLauncherBase { execute(args: string | any[]) { diff --git a/Clava-JS/src-code/ClangPlugin/ClangPlugin.test.ts b/Clava-JS/code/ClangPlugin/ClangPlugin.test.ts similarity index 98% rename from Clava-JS/src-code/ClangPlugin/ClangPlugin.test.ts rename to Clava-JS/code/ClangPlugin/ClangPlugin.test.ts index 4eabe5a4b9..376983f398 100644 --- a/Clava-JS/src-code/ClangPlugin/ClangPlugin.test.ts +++ b/Clava-JS/code/ClangPlugin/ClangPlugin.test.ts @@ -2,7 +2,7 @@ import { jest } from "@jest/globals"; import fs from "fs"; import path from "path"; -import ClangPlugin from "./ClangPlugin.js"; +import ClangPlugin from "./ClangPlugin.ts"; describe("ClangPlugin", () => { describe("validateClangExecutable", () => { diff --git a/Clava-JS/src-code/ClangPlugin/ClangPlugin.ts b/Clava-JS/code/ClangPlugin/ClangPlugin.ts similarity index 99% rename from Clava-JS/src-code/ClangPlugin/ClangPlugin.ts rename to Clava-JS/code/ClangPlugin/ClangPlugin.ts index 7ca3bdfd2b..4dc0c00abf 100644 --- a/Clava-JS/src-code/ClangPlugin/ClangPlugin.ts +++ b/Clava-JS/code/ClangPlugin/ClangPlugin.ts @@ -1,6 +1,6 @@ import fs from "fs"; import path from "path"; -import Sandbox from "../Sandbox.js"; +import Sandbox from "../Sandbox.ts"; import { fileURLToPath } from "url"; export default class ClangPlugin { diff --git a/Clava-JS/src-code/Dumper/AbstractDumper.ts b/Clava-JS/code/Dumper/AbstractDumper.ts similarity index 100% rename from Clava-JS/src-code/Dumper/AbstractDumper.ts rename to Clava-JS/code/Dumper/AbstractDumper.ts diff --git a/Clava-JS/src-code/Dumper/ClangJSONDumper.ts b/Clava-JS/code/Dumper/ClangJSONDumper.ts similarity index 92% rename from Clava-JS/src-code/Dumper/ClangJSONDumper.ts rename to Clava-JS/code/Dumper/ClangJSONDumper.ts index 879a4042a6..0808680984 100644 --- a/Clava-JS/src-code/Dumper/ClangJSONDumper.ts +++ b/Clava-JS/code/Dumper/ClangJSONDumper.ts @@ -1,4 +1,4 @@ -import type AbstractDumper from "./AbstractDumper.js"; +import type AbstractDumper from "./AbstractDumper.ts"; import { spawn } from "child_process"; diff --git a/Clava-JS/src-code/Sandbox.test.ts b/Clava-JS/code/Sandbox.test.ts similarity index 98% rename from Clava-JS/src-code/Sandbox.test.ts rename to Clava-JS/code/Sandbox.test.ts index e05dcc03a6..5e49d52cb9 100644 --- a/Clava-JS/src-code/Sandbox.test.ts +++ b/Clava-JS/code/Sandbox.test.ts @@ -1,4 +1,4 @@ -import Sandbox from "./Sandbox.js"; +import Sandbox from "./Sandbox.ts"; describe("Sandbox", () => { describe("sanitizeCommand", () => { diff --git a/Clava-JS/src-code/Sandbox.ts b/Clava-JS/code/Sandbox.ts similarity index 99% rename from Clava-JS/src-code/Sandbox.ts rename to Clava-JS/code/Sandbox.ts index 50b423cf0a..5025f5803c 100644 --- a/Clava-JS/src-code/Sandbox.ts +++ b/Clava-JS/code/Sandbox.ts @@ -1,5 +1,5 @@ import { ChildProcess, spawn, spawnSync } from "child_process"; -import { addActiveChildProcess } from "@specs-feup/lara/code/ChildProcessHandling.js"; +import { addActiveChildProcess } from "@specs-feup/lara/code/ChildProcessHandling.ts"; export default class Sandbox { /** diff --git a/Clava-JS/src-code/WeaverConfiguration.ts b/Clava-JS/code/WeaverConfiguration.ts similarity index 69% rename from Clava-JS/src-code/WeaverConfiguration.ts rename to Clava-JS/code/WeaverConfiguration.ts index 2db66bf3b7..f003327e0c 100644 --- a/Clava-JS/src-code/WeaverConfiguration.ts +++ b/Clava-JS/code/WeaverConfiguration.ts @@ -1,15 +1,15 @@ -import type WeaverConfiguration from "@specs-feup/lara/code/WeaverConfiguration.js"; +import type WeaverConfiguration from "@specs-feup/lara/code/WeaverConfiguration.ts"; import path from "path"; import { fileURLToPath } from "url"; export const weaverConfig: WeaverConfiguration = { weaverName: "clava", weaverPrettyName: "Clava", - weaverFileName: "@specs-feup/lara/code/Weaver.js", + weaverFileName: "@specs-feup/lara/code/Weaver.ts", jarPath: path.join( path.dirname(path.dirname(fileURLToPath(import.meta.url))), "./java-binaries/" ), javaWeaverQualifiedName: "pt.up.fe.specs.clava.weaver.CxxWeaver", - importForSideEffects: ["@specs-feup/clava/api/Joinpoints.js", "@specs-feup/clava/code/sideEffects.js"], + importForSideEffects: ["@specs-feup/clava/api/Joinpoints.ts", "@specs-feup/clava/code/sideEffects.ts"], }; diff --git a/Clava-JS/src-code/index.ts b/Clava-JS/code/index.ts old mode 100644 new mode 100755 similarity index 73% rename from Clava-JS/src-code/index.ts rename to Clava-JS/code/index.ts index c9782c286c..bdf002b8a8 --- a/Clava-JS/src-code/index.ts +++ b/Clava-JS/code/index.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import WeaverLauncher from "@specs-feup/lara/code/WeaverLauncher.js"; -import { weaverConfig } from "./WeaverConfiguration.js"; +import WeaverLauncher from "@specs-feup/lara/code/WeaverLauncher.ts"; +import { weaverConfig } from "./WeaverConfiguration.ts"; const weaverLauncher = new WeaverLauncher(weaverConfig); diff --git a/Clava-JS/src-code/jest.config.js b/Clava-JS/code/jest.config.js similarity index 100% rename from Clava-JS/src-code/jest.config.js rename to Clava-JS/code/jest.config.js diff --git a/Clava-JS/src-code/sideEffects.ts b/Clava-JS/code/sideEffects.ts similarity index 94% rename from Clava-JS/src-code/sideEffects.ts rename to Clava-JS/code/sideEffects.ts index c62eac57fa..59d7bf76ff 100644 --- a/Clava-JS/src-code/sideEffects.ts +++ b/Clava-JS/code/sideEffects.ts @@ -1,5 +1,5 @@ -import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.js"; -import Weaver from "@specs-feup/lara/api/weaver/Weaver.js"; +import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; +import Weaver from "@specs-feup/lara/api/weaver/Weaver.ts"; import path from "node:path"; import os from "node:os"; diff --git a/Clava-JS/code/tsconfig.json b/Clava-JS/code/tsconfig.json new file mode 100644 index 0000000000..c72c84e90d --- /dev/null +++ b/Clava-JS/code/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "rootDir": "." + }, + "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] +} diff --git a/Clava-JS/jest.config.js b/Clava-JS/jest.config.js index ea4524bfa8..ec1e566d57 100644 --- a/Clava-JS/jest.config.js +++ b/Clava-JS/jest.config.js @@ -12,7 +12,7 @@ const config = { moduleNameMapper: { "(.+)\\.js": "$1", }, - projects: ["src-api", "src-code"], + projects: ["api", "code"], }; export default config; diff --git a/Clava-JS/jest/ClavaLegacyTester.ts b/Clava-JS/jest/ClavaLegacyTester.ts index 8ee73374fb..a57c0c07e6 100644 --- a/Clava-JS/jest/ClavaLegacyTester.ts +++ b/Clava-JS/jest/ClavaLegacyTester.ts @@ -1,8 +1,8 @@ import ClavaJavaTypes, { type ClavaJavaClasses, -} from "@specs-feup/clava/api/clava/ClavaJavaTypes.js"; +} from "../api/clava/ClavaJavaTypes.ts"; -import { WeaverLegacyTester } from "@specs-feup/lara/jest/WeaverLegacyTester.js"; +import { WeaverLegacyTester } from "@specs-feup/lara/jest/WeaverLegacyTester.ts"; export class ClavaLegacyTester extends WeaverLegacyTester { protected readonly WORK_FOLDER: string = "cxx_weaver_output"; diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 81ca3f9f90..471286435b 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -6,8 +6,6 @@ "files": [ "api", "code", - "src-api", - "src-code", "java-binaries", ".gitignore", "eslint.config.js", @@ -20,21 +18,21 @@ "typedoc.config.js" ], "bin": { - "clava": "./code/index.js" + "clava": "./code/index.ts" }, "scripts": { - "run": "node ./code/index.js", - "build": "tsc -b src-api src-code", - "build:api": "tsc -b src-api", - "build:code": "tsc -b src-code", + "run": "node ./code/index.ts", + "build": "tsc -b api code", + "build:api": "tsc -b api", + "build:code": "tsc -b code", "build:watch": "npm run build -- --watch", "lint": "eslint .", "test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --detectOpenHandles --forceExit", - "test:api": "npm run test -- src-api", - "test:code": "npm run test -- src-code", + "test:api": "npm run test -- api", + "test:code": "npm run test -- code", "test:cov": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", - "build-interfaces": "npx lara-build-interfaces --input ../ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.json --lara @specs-feup/lara/LaraJoinPointSpecification.json --output src-api/Joinpoints.ts" + "build-interfaces": "npx lara-build-interfaces --input ../ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.json --lara @specs-feup/lara/LaraJoinPointSpecification.json --output api/Joinpoints.ts" }, "repository": { "type": "git", diff --git a/Clava-JS/src-api/clava/opt/PrepareForInlining.ts b/Clava-JS/src-api/clava/opt/PrepareForInlining.ts deleted file mode 100644 index 2ee5f63d11..0000000000 --- a/Clava-JS/src-api/clava/opt/PrepareForInlining.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { FunctionJp } from "../../Joinpoints.js"; -import RemoveShadowing from "../code/RemoveShadowing.js"; -import SingleReturnFunction from "../pass/SingleReturnFunction.js"; - -export default function PrepareForInlining($function: FunctionJp) { - new SingleReturnFunction().apply($function); - RemoveShadowing($function); -} diff --git a/Clava-JS/src-api/tsconfig.json b/Clava-JS/src-api/tsconfig.json deleted file mode 100644 index 0607bd620a..0000000000 --- a/Clava-JS/src-api/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "../api" - }, - "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"], - "references": [{ "path": "../src-code" }] -} diff --git a/Clava-JS/tsconfig.json b/Clava-JS/tsconfig.json index dfabb45eff..a9ba43705b 100644 --- a/Clava-JS/tsconfig.json +++ b/Clava-JS/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - //"noEmit": true, + "noEmit": true, "module": "NodeNext", "isolatedModules": true, "declaration": true, diff --git a/Clava-JS/typedoc.config.js b/Clava-JS/typedoc.config.js index b000de5443..527bc0db70 100644 --- a/Clava-JS/typedoc.config.js +++ b/Clava-JS/typedoc.config.js @@ -4,6 +4,6 @@ export default { extends: [ fileURLToPath(import.meta.resolve("@specs-feup/lara/typedoc.base.json")), ], - entryPoints: ["src-api/"], - tsconfig: "src-api/tsconfig.json", + entryPoints: ["api/"], + tsconfig: "api/tsconfig.json", }; diff --git a/README.md b/README.md index c7ea3845f6..0d7ae5a8fe 100644 --- a/README.md +++ b/README.md @@ -80,16 +80,16 @@ This is a non-exhaustive list of Clava documentation, usage examples and feature * [CMake integration](https://github.com/specs-feup/clava/tree/master/CMake) - Allows Clava to be used in CMake-centered compilation flows * Code transformations: * [Automatic insertion of OpenMP pragmas](https://github.com/specs-feup/clava/blob/master/ClavaLaraApi/src-lara-clava/clava/clava/autopar/Parallelize.lara) - * [Function inlining](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/opt/Inlining.ts) - * [Normalizing code](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/opt/NormalizeToSubset.ts) to a subset of the language, including: - * [Decomposition of complex statements into several, simpler statements](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/code/StatementDecomposer.ts) - * [Converting static local variables to static global variables](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/pass/LocalStaticToGlobal.ts) - * [Conversion of switch statements to ifs](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/pass/TransformSwitchToIf.ts) - * Loop conversion ([for to while](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/code/ForToWhileStmt.ts), [do to while](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/code/DoToWhileStmt.ts)) - * [Ensure there is a single return in a function](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/pass/SingleReturnFunction.ts) - * [Remove variable shadowing](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/code/RemoveShadowing.ts) - * [Simplify ternary operator](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/code/SimplifyTernaryOp.ts) - * [Simplify compound assignments](https://github.com/specs-feup/clava/blob/master/Clava-JS/src-api/clava/code/SimplifyAssignment.ts) + * [Function inlining](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/opt/Inlining.ts) + * [Normalizing code](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/opt/NormalizeToSubset.ts) to a subset of the language, including: + * [Decomposition of complex statements into several, simpler statements](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/code/StatementDecomposer.ts) + * [Converting static local variables to static global variables](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts) + * [Conversion of switch statements to ifs](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/pass/TransformSwitchToIf.ts) + * Loop conversion ([for to while](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/code/ForToWhileStmt.ts), [do to while](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/code/DoToWhileStmt.ts)) + * [Ensure there is a single return in a function](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/pass/SingleReturnFunction.ts) + * [Remove variable shadowing](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/code/RemoveShadowing.ts) + * [Simplify ternary operator](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/code/SimplifyTernaryOp.ts) + * [Simplify compound assignments](https://github.com/specs-feup/clava/blob/master/Clava-JS/api/clava/code/SimplifyAssignment.ts) * Clava [NPM libraries](https://www.npmjs.com/org/specs-feup) (not supported by the JAR file legacy distribution): Library | Description | Installation From 6ad501282e8bb8e3b42ee01a0a44a695a9d4e522 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 8 Jun 2026 22:51:42 +0100 Subject: [PATCH 18/99] Clava-JS: Run new TS version of the LARA helper scripts --- Clava-JS/api/Joinpoints.ts | 2 +- Clava-JS/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Clava-JS/api/Joinpoints.ts b/Clava-JS/api/Joinpoints.ts index b1ab847a0c..659971877f 100644 --- a/Clava-JS/api/Joinpoints.ts +++ b/Clava-JS/api/Joinpoints.ts @@ -1,5 +1,5 @@ /////////////////////////////////////////////////// -// This file is generated by build-interfaces.js // +// This file is generated by build-interfaces.ts // /////////////////////////////////////////////////// import { diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 471286435b..0789666764 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -32,7 +32,7 @@ "test:code": "npm run test -- code", "test:cov": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", - "build-interfaces": "npx lara-build-interfaces --input ../ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.json --lara @specs-feup/lara/LaraJoinPointSpecification.json --output api/Joinpoints.ts" + "build-interfaces": "lara-build-interfaces --input ../ClavaWeaver/src/pt/up/fe/specs/clava/weaver/CxxWeaver.json --lara @specs-feup/lara/LaraJoinPointSpecification.json --output api/Joinpoints.ts" }, "repository": { "type": "git", From f1a74d28cc12b4f45061c66029aa2b50e6b95ae6 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 8 Jun 2026 23:21:22 +0100 Subject: [PATCH 19/99] Clava-JS: Convert Jest configuration files to Typescript --- Clava-JS/api/{jest.config.js => jest.config.ts} | 17 ++++++++--------- Clava-JS/api/tsconfig.json | 2 +- .../code/{jest.config.js => jest.config.ts} | 8 ++++---- Clava-JS/{jest.config.js => jest.config.ts} | 8 ++++---- 4 files changed, 17 insertions(+), 18 deletions(-) rename Clava-JS/api/{jest.config.js => jest.config.ts} (54%) rename Clava-JS/code/{jest.config.js => jest.config.ts} (62%) rename Clava-JS/{jest.config.js => jest.config.ts} (84%) diff --git a/Clava-JS/api/jest.config.js b/Clava-JS/api/jest.config.ts similarity index 54% rename from Clava-JS/api/jest.config.js rename to Clava-JS/api/jest.config.ts index 0f0e072b60..64b53490b5 100644 --- a/Clava-JS/api/jest.config.js +++ b/Clava-JS/api/jest.config.ts @@ -1,18 +1,17 @@ +import { defineConfig } from "jest"; import { weaverConfig } from "../code/WeaverConfiguration.ts"; -const config = { +export default defineConfig({ preset: "ts-jest/presets/default-esm", - testEnvironment: "@specs-feup/lara/jest/jestEnvironment.js", - globalSetup: "@specs-feup/lara/jest/jestGlobalSetup.js", - globalTeardown: "@specs-feup/lara/jest/jestGlobalTeardown.js", - setupFiles: ["@specs-feup/lara/jest/setupFiles/sharedJavaModule.js"], - setupFilesAfterEnv: ["@specs-feup/lara/jest/setupFiles/importSideEffects.js"], + testEnvironment: "@specs-feup/lara/jest/jestEnvironment.ts", + globalSetup: "@specs-feup/lara/jest/jestGlobalSetup.ts", + globalTeardown: "@specs-feup/lara/jest/jestGlobalTeardown.ts", + setupFiles: ["@specs-feup/lara/jest/setupFiles/sharedJavaModule.ts"], + setupFilesAfterEnv: ["@specs-feup/lara/jest/setupFiles/importSideEffects.ts"], moduleNameMapper: { "(.+)\\.js": "$1", }, testEnvironmentOptions: { weaverConfig, }, -}; - -export default config; +}); diff --git a/Clava-JS/api/tsconfig.json b/Clava-JS/api/tsconfig.json index c72c84e90d..daa710f888 100644 --- a/Clava-JS/api/tsconfig.json +++ b/Clava-JS/api/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "rootDir": "." + "rootDirs": [".", "../code"], }, "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] } diff --git a/Clava-JS/code/jest.config.js b/Clava-JS/code/jest.config.ts similarity index 62% rename from Clava-JS/code/jest.config.js rename to Clava-JS/code/jest.config.ts index f8502043b2..76873b4a20 100644 --- a/Clava-JS/code/jest.config.js +++ b/Clava-JS/code/jest.config.ts @@ -1,9 +1,9 @@ -const config = { +import { defineConfig } from "jest"; + +export default defineConfig({ preset: "ts-jest/presets/default-esm", testEnvironment: "node", moduleNameMapper: { "(.+)\\.js": "$1", }, -}; - -export default config; +}); diff --git a/Clava-JS/jest.config.js b/Clava-JS/jest.config.ts similarity index 84% rename from Clava-JS/jest.config.js rename to Clava-JS/jest.config.ts index ec1e566d57..cfa242603e 100644 --- a/Clava-JS/jest.config.js +++ b/Clava-JS/jest.config.ts @@ -1,4 +1,6 @@ -const config = { +import { defineConfig } from "jest"; + +export default defineConfig({ preset: "ts-jest/presets/default-esm", testEnvironment: "node", notify: true, @@ -13,6 +15,4 @@ const config = { "(.+)\\.js": "$1", }, projects: ["api", "code"], -}; - -export default config; +}); From 38c94ba07276a7874d3473aada39373fbf468f37 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 8 Jun 2026 23:41:38 +0100 Subject: [PATCH 20/99] Clava-JS: Remove unused TypeScript configuration files and simplify build script in package.json --- Clava-JS/api/clava/analysis/AnalyserResult.test.ts | 2 +- Clava-JS/api/tsconfig.json | 7 ------- Clava-JS/code/tsconfig.json | 7 ------- Clava-JS/package.json | 4 +--- 4 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 Clava-JS/api/tsconfig.json delete mode 100644 Clava-JS/code/tsconfig.json diff --git a/Clava-JS/api/clava/analysis/AnalyserResult.test.ts b/Clava-JS/api/clava/analysis/AnalyserResult.test.ts index 61870b1325..990414af42 100644 --- a/Clava-JS/api/clava/analysis/AnalyserResult.test.ts +++ b/Clava-JS/api/clava/analysis/AnalyserResult.test.ts @@ -1,5 +1,5 @@ import AnalyserResult from "./AnalyserResult.ts"; -import ClavaJoinPoints from "@specs-feup/clava/api/clava/ClavaJoinPoints.ts"; +import ClavaJoinPoints from "../ClavaJoinPoints.ts"; describe("AnalyserResult", () => { describe("getName", () => { diff --git a/Clava-JS/api/tsconfig.json b/Clava-JS/api/tsconfig.json deleted file mode 100644 index daa710f888..0000000000 --- a/Clava-JS/api/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDirs": [".", "../code"], - }, - "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] -} diff --git a/Clava-JS/code/tsconfig.json b/Clava-JS/code/tsconfig.json deleted file mode 100644 index c72c84e90d..0000000000 --- a/Clava-JS/code/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": "." - }, - "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] -} diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 0789666764..81f74921d7 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -22,9 +22,7 @@ }, "scripts": { "run": "node ./code/index.ts", - "build": "tsc -b api code", - "build:api": "tsc -b api", - "build:code": "tsc -b code", + "build": "tsc", "build:watch": "npm run build -- --watch", "lint": "eslint .", "test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --detectOpenHandles --forceExit", From 44a9c00ab617ea38ce34c57811fd83c557242c36 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 9 Jun 2026 00:15:48 +0100 Subject: [PATCH 21/99] Clava-JS: Fix the linter configuration --- Clava-JS/eslint.config.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Clava-JS/eslint.config.js b/Clava-JS/eslint.config.js index 2273574c58..414d082210 100644 --- a/Clava-JS/eslint.config.js +++ b/Clava-JS/eslint.config.js @@ -13,9 +13,6 @@ export default [ js.configs.recommended, eslintConfigPrettier, ...typescriptEslint.configs.recommended, - { - ignores: ["**/*.d.ts", "**/*.config.js"], - }, { plugins: { "@typescript-eslint": typescriptEslint.plugin, @@ -28,7 +25,7 @@ export default [ sourceType: "script", parserOptions: { - project: ["./*/tsconfig.json", "./tsconfig.*.json"], + project: ["./tsconfig.json", "./tsconfig.*.json"], tsconfigRootDir: __dirname, }, }, @@ -37,10 +34,6 @@ export default [ "tsdoc/syntax": "warn", }, }, - { - ...typescriptEslint.configs.disableTypeChecked, - files: ["scripts/**/*.js"], - }, { ...jest.configs["flat/recommended"], files: ["**/*.spec.ts", "**/*.test.ts"], From 2aab56cd5cf1ad3ab382e36304eb340b48c53cd9 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 9 Jun 2026 00:43:26 +0100 Subject: [PATCH 22/99] Clava-JS: Convert ESLint configuration to TypeScript --- Clava-JS/{eslint.config.js => eslint.config.ts} | 15 ++++++++------- Clava-JS/package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) rename Clava-JS/{eslint.config.js => eslint.config.ts} (93%) diff --git a/Clava-JS/eslint.config.js b/Clava-JS/eslint.config.ts similarity index 93% rename from Clava-JS/eslint.config.js rename to Clava-JS/eslint.config.ts index 414d082210..7df9bdcf0e 100644 --- a/Clava-JS/eslint.config.js +++ b/Clava-JS/eslint.config.ts @@ -1,15 +1,16 @@ -import { fileURLToPath } from "url"; -import { dirname } from "path"; -import typescriptEslint from "typescript-eslint"; -import tsdoc from "eslint-plugin-tsdoc"; -import jest from "eslint-plugin-jest"; import js from "@eslint/js"; import eslintConfigPrettier from "eslint-config-prettier"; +import jest from "eslint-plugin-jest"; +import tsdoc from "eslint-plugin-tsdoc"; +import { defineConfig } from "eslint/config"; +import { dirname } from "path"; +import typescriptEslint from "typescript-eslint"; +import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -export default [ +export default defineConfig([ js.configs.recommended, eslintConfigPrettier, ...typescriptEslint.configs.recommended, @@ -48,4 +49,4 @@ export default [ }, }, }, -]; +]); diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 81f74921d7..7b52c598c6 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -24,7 +24,7 @@ "run": "node ./code/index.ts", "build": "tsc", "build:watch": "npm run build -- --watch", - "lint": "eslint .", + "lint": "eslint --flag unstable_native_nodejs_ts_config .", "test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --detectOpenHandles --forceExit", "test:api": "npm run test -- api", "test:code": "npm run test -- code", From 39abb8ba0c6c7a5e8925c320c66f2c8e20b250ce Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 9 Jun 2026 03:27:28 +0100 Subject: [PATCH 23/99] Clava-JS: Update Typedoc configuration --- Clava-JS/tsconfig.jest.json | 5 ----- Clava-JS/typedoc.config.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 Clava-JS/tsconfig.jest.json diff --git a/Clava-JS/tsconfig.jest.json b/Clava-JS/tsconfig.jest.json deleted file mode 100644 index dd7eedda34..0000000000 --- a/Clava-JS/tsconfig.jest.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*.spec.ts", "**/*.test.ts"], - "exclude": ["node_modules"] -} diff --git a/Clava-JS/typedoc.config.js b/Clava-JS/typedoc.config.js index 527bc0db70..357b6153bc 100644 --- a/Clava-JS/typedoc.config.js +++ b/Clava-JS/typedoc.config.js @@ -5,5 +5,5 @@ export default { fileURLToPath(import.meta.resolve("@specs-feup/lara/typedoc.base.json")), ], entryPoints: ["api/"], - tsconfig: "api/tsconfig.json", + tsconfig: "tsconfig.json", }; From 141aeb7be767215675ca045cfd21248ee0cd5539 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 9 Jun 2026 16:45:11 +0100 Subject: [PATCH 24/99] Clava-JS: Add proper exports to the package and cleanup tsconfig --- Clava-JS/package.json | 21 +++++++++++++++------ Clava-JS/tsconfig.json | 7 +------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 7b52c598c6..8a3f3bf4bb 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -3,23 +3,32 @@ "version": "3.5.1", "description": "A C/C++ source-to-source compiler written in Typescript", "type": "module", + "bin": { + "clava": "./code/index.ts" + }, + "exports": { + "./api/*.test.ts": null, + "./api/*.config.ts": null, + "./api/*": "./api/*.ts", + "./api/*.ts": "./api/*.ts", + "./code/*.test.ts": null, + "./code/*.config.ts": null, + "./code/*": "./code/*.ts", + "./code/*.ts": "./code/*.ts" + }, "files": [ "api", "code", "java-binaries", ".gitignore", - "eslint.config.js", - "jest.config.js", + "eslint.config.ts", + "jest.config.ts", "LICENSE", "package.json", "README.md", - "tsconfig.jest.json", "tsconfig.json", "typedoc.config.js" ], - "bin": { - "clava": "./code/index.ts" - }, "scripts": { "run": "node ./code/index.ts", "build": "tsc", diff --git a/Clava-JS/tsconfig.json b/Clava-JS/tsconfig.json index a9ba43705b..45c2114568 100644 --- a/Clava-JS/tsconfig.json +++ b/Clava-JS/tsconfig.json @@ -2,15 +2,10 @@ "compilerOptions": { "noEmit": true, "module": "NodeNext", - "isolatedModules": true, "declaration": true, "strict": true, - //"allowJs": true, - //"checkJs": true, - "sourceMap": true, - "declarationMap": true, + "isolatedModules": true, "allowSyntheticDefaultImports": true, - //"esModuleInterop": true "types": ["node", "jest"], "rewriteRelativeImportExtensions": true, "erasableSyntaxOnly": true, From e917ba36775de625c9127c751c1963aa26fca924 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 30 Jun 2026 03:55:41 +0100 Subject: [PATCH 25/99] Update Node.js supported versions --- .github/workflows/nightly.yml | 4 ++-- Clava-JS/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1ad36291e4..e1223a4382 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -156,7 +156,7 @@ jobs: fail-fast: false matrix: #node-version: ['latest', '22.x', '20.x'] - node-version: ['22.x', '20.x'] + node-version: ['25.x', '24.x'] os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} @@ -223,7 +223,7 @@ jobs: # Only on ubuntu-latest - name: Publish JS - if: matrix.os == 'ubuntu-latest' && matrix.node-version == '22.x' + if: matrix.os == 'ubuntu-latest' && matrix.node-version == '25.x' run: | cd clava/Clava-JS npm whoami diff --git a/Clava-JS/README.md b/Clava-JS/README.md index 0dd99989b4..0182411ffa 100644 --- a/Clava-JS/README.md +++ b/Clava-JS/README.md @@ -2,7 +2,7 @@ Clava source-to-source compiler running on top of Node.js. -Current version only works on Node 20 and 22. +Current version works on Node >=24. To test Clava-JS you can try the [Clava project template](https://github.com/specs-feup/clava-project-template). From 5d71fdb8d317c87884848f99bf8f75ed7b7b4091 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 30 Jun 2026 04:43:49 +0100 Subject: [PATCH 26/99] Fix JS CI workflow setup --- .github/workflows/nightly.yml | 23 ++++++----------------- README.md | 4 ++-- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e1223a4382..98915e2b03 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -197,7 +197,7 @@ jobs: - name: Setup js workspace run: | - echo '{ "name": "SPeCS Workspace", "type": "module", "workspaces": [ "clava/Clava-JS", "lara-framework/Lara-JS" ] }' > package.json + echo '{ "name": "SPeCS Workspace", "type": "module", "workspaces": [ "clava/Clava-JS", "lara-framework/Lara-JS" ], "overrides": { "node-gyp": "^12.1.0" } }' > package.json npm install - name: Build Lara-JS @@ -223,25 +223,14 @@ jobs: # Only on ubuntu-latest - name: Publish JS - if: matrix.os == 'ubuntu-latest' && matrix.node-version == '25.x' + if: github.event_name == 'push' && github.ref == 'refs/heads/staging' && matrix.os == 'ubuntu-latest' && matrix.node-version == '25.x' run: | cd clava/Clava-JS npm whoami echo "Attempt to publish while running on ${{ matrix.os }} and Node.js ${{matrix.node-version}}" - if [[ "${{ github.event_name }}" == "push" ]]; then - if [ "${{ github.ref }}" == "refs/heads/staging" ]; then - echo "Publishing from staging, creating timestamped prerelease and updating tag 'staging'" - TIMESTAMP=$(date +"%Y%m%d%H%M") - npm version prerelease --preid="$TIMESTAMP" - npm publish --tag staging --access public - elif [ "${{ github.ref }}" == "refs/heads/master" ]; then - echo "Publishing from main, assumes version was changed before publishing" - npm publish - else - echo "Not master or staging branches, not publishing even if it is a push event" - fi - else - echo "Not a push event, skipping publish." - fi + echo "Publishing from staging, creating timestamped prerelease and updating tag 'staging'" + TIMESTAMP=$(date +"%Y%m%d%H%M") + npm version prerelease --preid="$TIMESTAMP" + npm publish --tag staging --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/README.md b/README.md index 0d7ae5a8fe..31d2ce60d5 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ There are three distributions of Clava: ### NPM distribution (recommended) -Clava is currently distributed as an [NPM package](https://www.npmjs.com/package/@specs-feup/clava). It requires Node.js 20 or 22, and Java 17 or higher. Different OSses have different ways of installing these dependencies, but on Ubuntu you can run this: +Clava is currently distributed as an [NPM package](https://www.npmjs.com/package/@specs-feup/clava). It requires Node.js 24 or higher, and Java 17 or higher. Different OSses have different ways of installing these dependencies, but on Ubuntu you can run this: ```bash apt-get update && apt-get install -y curl openjdk-17-jdk -curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && apt-get install -y nodejs +curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && apt-get install -y nodejs ``` Now, you have two options: From a8c215e87b3621ff770603ab3c17473b15d552b0 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 16:52:43 +0100 Subject: [PATCH 27/99] Upgrade Clava-JS to TypeScript 7 --- Clava-JS/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 8a3f3bf4bb..519ef567c8 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -78,7 +78,7 @@ "jest": "^30.2.0", "node-notifier": "^10.0.1", "ts-jest": "^29.4.6", - "typescript": "^6.0.3", + "typescript": "^7.0.2", "typescript-eslint": "^8.52.0" } } From 6cf8cc2fed5044d5c0bd1cbf2fdbb9c5552e592d Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 3 Jul 2026 01:47:57 +0100 Subject: [PATCH 28/99] Update clang dumper resources to manifest release Load clang-dumper assets from the v16.0.5_3 release manifest and remove the legacy archive resource map. Update Clang 18 enum values, parser tolerance, bundled include/CUDA handling, and JS expectations needed by the new dumper. --- ClangAstParser/build.gradle | 5 + ClangAstParser/clang-dumper-release.tag | 1 + .../resources/clangast/cuda_compatibility.h | 9 + .../fe/specs/clang/ClangAstFileResource.java | 56 --- .../up/fe/specs/clang/ClangAstResource.java | 20 +- .../fe/specs/clang/ClangAstWebResource.java | 105 +++-- .../pt/up/fe/specs/clang/ClangResources.java | 432 ++++++++---------- .../up/fe/specs/clang/SupportedPlatform.java | 4 - .../fe/specs/clang/dumper/ClangAstDumper.java | 26 +- .../fe/specs/clang/dumper/ClangAstParser.java | 6 +- .../specs/clang/parsers/ClavaNodeParser.java | 28 +- .../up/fe/specs/clang/parsers/ClavaNodes.java | 66 ++- .../clang/parsers/data/DeclDataParser.java | 2 +- .../clang/parsers/data/ExprDataParser.java | 2 +- .../clang/parsers/data/StmtDataParser.java | 2 +- .../api/clava/pass/LocalStaticToGlobal.ts | 2 +- .../clava/ast/attr/enums/AttributeKind.java | 47 +- .../ast/decl/enums/InitializationStyle.java | 15 +- .../specs/clava/ast/decl/enums/Linkage.java | 52 +-- .../clava/ast/expr/enums/CharacterKind.java | 2 +- .../clava/ast/expr/enums/StringKind.java | 6 +- .../specs/clava/ast/type/ElaboratedType.java | 7 + .../ast/type/enums/CallingConvention.java | 4 +- .../ast/type/enums/ElaboratedTypeKeyword.java | 18 +- .../type/enums/UnaryTransformTypeKind.java | 15 +- .../weaver/cpp/results/TypeTemplate.js.txt | 4 +- .../clava/weaver/importable/AstFactory.java | 2 +- 27 files changed, 490 insertions(+), 448 deletions(-) create mode 100644 ClangAstParser/clang-dumper-release.tag create mode 100644 ClangAstParser/resources/clangast/cuda_compatibility.h delete mode 100644 ClangAstParser/src/pt/up/fe/specs/clang/ClangAstFileResource.java diff --git a/ClangAstParser/build.gradle b/ClangAstParser/build.gradle index 26a50196fe..81313b2d79 100644 --- a/ClangAstParser/build.gradle +++ b/ClangAstParser/build.gradle @@ -22,6 +22,7 @@ dependencies { implementation ":jOptions" implementation ":SpecsUtils" + implementation 'com.google.code.gson:gson:2.12.1' implementation 'com.google.guava:guava:33.4.0-jre' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' @@ -49,6 +50,10 @@ sourceSets { } } +processResources { + from('clang-dumper-release.tag') +} + // Test coverage configuration jacocoTestReport { reports { diff --git a/ClangAstParser/clang-dumper-release.tag b/ClangAstParser/clang-dumper-release.tag new file mode 100644 index 0000000000..0e02573bb7 --- /dev/null +++ b/ClangAstParser/clang-dumper-release.tag @@ -0,0 +1 @@ +v16.0.5_3 diff --git a/ClangAstParser/resources/clangast/cuda_compatibility.h b/ClangAstParser/resources/clangast/cuda_compatibility.h new file mode 100644 index 0000000000..f649810666 --- /dev/null +++ b/ClangAstParser/resources/clangast/cuda_compatibility.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +extern "C" __host__ cudaError_t cudaConfigureCall( + dim3 gridDim, + dim3 blockDim, + size_t sharedMem = 0, + cudaStream_t stream = 0); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstFileResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstFileResource.java deleted file mode 100644 index 6e2ac72f6c..0000000000 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstFileResource.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2018 SPeCS. - *

- * 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 - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * 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 pt.up.fe.specs.clang; - -import pt.up.fe.specs.util.providers.FileResourceProvider; - -import java.util.function.Supplier; - -public enum ClangAstFileResource implements Supplier { - - LIBC_CXX_LINUX_COMPLETE(ClangAstWebResource.LIBC_CXX_LINUX_COMPLETE), - LIBC_CXX_MACOS_COMPLETE(ClangAstWebResource.LIBC_CXX_MACOS_COMPLETE), - LIBC_CXX_WIN32_COMPLETE(ClangAstWebResource.LIBC_CXX_WIN32_COMPLETE), - OPENMP_INCLUDES(ClangAstWebResource.OPENMP_INCLUDES), - CUDA_LIB(ClangAstWebResource.CUDA_LIB), - WIN_EXE(ClangAstWebResource.WIN_EXE), - WIN_DLL1(ClangAstWebResource.WIN_DLL1), - WIN_DLL2(ClangAstWebResource.WIN_DLL2), - WIN_DLL3(ClangAstWebResource.WIN_DLL3), - WIN_DLL4(ClangAstWebResource.WIN_DLL4), - WIN_DLL5(ClangAstWebResource.WIN_DLL5), - WIN_DLL6(ClangAstWebResource.WIN_DLL6), - WIN_DLL7(ClangAstWebResource.WIN_DLL7), - WIN_DLL8(ClangAstWebResource.WIN_DLL8), - WIN_DLL9(ClangAstWebResource.WIN_DLL9), - WIN_CLANG_DLL(ClangAstWebResource.WIN_CLANG_DLL), - WIN_LLVM_DLL(ClangAstWebResource.WIN_LLVM_DLL), - LINUX_EXE(ClangAstWebResource.LINUX_EXE), - LINUX_PLUGIN(ClangAstWebResource.LINUX_PLUGIN), - LINUX_LLVM_DLL(ClangAstWebResource.LINUX_LLVM_DLL), - MAC_OS_EXE(ClangAstWebResource.MAC_OS_EXE), - MAC_OS_LLVM_DLL(ClangAstWebResource.MAC_OS_LLVM_DLL), - MAC_OS_DLL1(ClangAstWebResource.MAC_OS_DLL1); - - private final FileResourceProvider provider; - - ClangAstFileResource(FileResourceProvider provider) { - this.provider = provider; - } - - @Override - public FileResourceProvider get() { - return provider; - } -} diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java index a80188a102..8fef3eaef2 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java @@ -20,12 +20,10 @@ * */ public enum ClangAstResource implements ResourceProvider { - // BUILTIN_INCLUDES_3_8(ClangAstWebResource.BUILTIN_INCLUDES_3_8), - TEST_INCLUDES_C("test_includes.c"), - TEST_INCLUDES_CPP("test_includes.cpp"); + TEST_INCLUDES_CPP("test_includes.cpp"), + CUDA_COMPATIBILITY("cuda_compatibility.h"); - // private final WebResourceProvider webResource; private final String resource; private static final String basePackage = "clangast/"; @@ -35,24 +33,10 @@ public enum ClangAstResource implements ResourceProvider { */ private ClangAstResource(String resource) { this.resource = basePackage + resource; - // this.webResource = null; } - // private ClangAstResource(WebResourceProvider webResource) { - // this.resource = null; - // this.webResource = webResource; - // } - - /* (non-Javadoc) - * @see org.suikasoft.SharedLibrary.Interfaces.ResourceProvider#getResource() - */ @Override public String getResource() { return resource; - // if (resource != null) { - // return resource; - // } - - // return webResource.getResourceUrl(); } } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index 3a779583da..ce2a109a5a 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -13,40 +13,93 @@ package pt.up.fe.specs.clang; +import com.google.gson.Gson; +import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.providers.WebResourceProvider; -public interface ClangAstWebResource { +import java.io.File; +import java.util.List; +import java.util.Objects; +import java.util.Optional; - String ROOT_16_0_5 = "https://github.com/specs-feup/clava/releases/download/clang_ast_dumper_16.0.5/"; - String ROOT_12_0_7 = "https://github.com/specs-feup/clava/releases/download/clang_ast_dumper_v12.0.7.1/"; +public final class ClangAstWebResource { - WebResourceProvider LIBC_CXX_LINUX_COMPLETE = WebResourceProvider.newInstance(ROOT_16_0_5, "libc_cxx_linux_complete.zip", "v16.0.5"); - WebResourceProvider LIBC_CXX_MACOS_COMPLETE = WebResourceProvider.newInstance(ROOT_16_0_5, "libc_cxx_macos_complete.zip", "v16.0.6"); - WebResourceProvider LIBC_CXX_WIN32_COMPLETE = WebResourceProvider.newInstance(ROOT_16_0_5, "libc_cxx_win32_complete.zip", "v16.0.5"); + private static final String RELEASE_ROOT = "https://github.com/specs-feup/clang-dumper/releases/download/"; + private static final String RELEASE_TAG_RESOURCE = "clang-dumper-release.tag"; + private static final String MANIFEST_FILENAME = "clang-dumper-release-manifest.json"; - WebResourceProvider OPENMP_INCLUDES = WebResourceProvider.newInstance(ROOT_16_0_5, "openmp_includes.zip"); + private static final Gson GSON = new Gson(); - WebResourceProvider CUDA_LIB = WebResourceProvider.newInstance(ROOT_12_0_7, "cudalib.zip", "v11.3.0"); + private ClangAstWebResource() { + } - WebResourceProvider WIN_EXE = WebResourceProvider.newInstance(ROOT_16_0_5, "clang_ast_windows.exe", "v16.0.5_1"); - WebResourceProvider WIN_DLL1 = WebResourceProvider.newInstance(ROOT_12_0_7, "libwinpthread-1.dll"); - WebResourceProvider WIN_DLL2 = WebResourceProvider.newInstance(ROOT_12_0_7, "zlib1.dll"); - WebResourceProvider WIN_DLL3 = WebResourceProvider.newInstance(ROOT_12_0_7, "libzstd.dll"); - WebResourceProvider WIN_DLL4 = WebResourceProvider.newInstance(ROOT_12_0_7, "libstdc++-6.dll"); - WebResourceProvider WIN_DLL5 = WebResourceProvider.newInstance(ROOT_12_0_7, "libgcc_s_seh-1.dll"); - WebResourceProvider WIN_DLL6 = WebResourceProvider.newInstance(ROOT_12_0_7, "libffi-8.dll"); - WebResourceProvider WIN_DLL7 = WebResourceProvider.newInstance(ROOT_12_0_7, "libxml2-2.dll"); - WebResourceProvider WIN_DLL8 = WebResourceProvider.newInstance(ROOT_12_0_7, "liblzma-5.dll"); - WebResourceProvider WIN_DLL9 = WebResourceProvider.newInstance(ROOT_12_0_7, "libiconv-2.dll"); - WebResourceProvider WIN_LLVM_DLL = WebResourceProvider.newInstance(ROOT_16_0_5, "libLLVM-16.dll"); - WebResourceProvider WIN_CLANG_DLL = WebResourceProvider.newInstance(ROOT_16_0_5, "libclang-cpp.dll"); + public static String getReleaseTag() { + var inputStream = ClangAstWebResource.class.getClassLoader().getResourceAsStream(RELEASE_TAG_RESOURCE); - WebResourceProvider LINUX_EXE = WebResourceProvider.newInstance(ROOT_16_0_5, "clang_ast_linux", "v16.0.5"); - WebResourceProvider LINUX_PLUGIN = WebResourceProvider.newInstance(ROOT_16_0_5, "clang-plugin.so", "v16.0.5"); - WebResourceProvider LINUX_LLVM_DLL = WebResourceProvider.newInstance(ROOT_16_0_5, "libLLVM-16.so.1", "v16.0.5"); + if (inputStream == null) { + throw new RuntimeException("Could not find resource '" + RELEASE_TAG_RESOURCE + "'"); + } - WebResourceProvider MAC_OS_EXE = WebResourceProvider.newInstance(ROOT_16_0_5, "clang_ast_macos", "v16.0.5"); - WebResourceProvider MAC_OS_LLVM_DLL = WebResourceProvider.newInstance(ROOT_16_0_5, "libLLVM.dylib", "v16.0.5"); - WebResourceProvider MAC_OS_DLL1 = WebResourceProvider.newInstance(ROOT_16_0_5, "libzstd.1.dylib", "v16.0.5"); + var tag = SpecsIo.read(inputStream).trim(); + if (tag.isBlank()) { + throw new RuntimeException("Resource '" + RELEASE_TAG_RESOURCE + "' is empty"); + } + return tag; + } + + public static ClangDumperManifest getManifest(File resourceFolder) { + var manifestResource = WebResourceProvider.newInstance(getReleaseBaseUrl(), MANIFEST_FILENAME, getReleaseTag()); + var manifestFile = manifestResource.writeVersioned(resourceFolder, ClangAstWebResource.class).getFile(); + var manifest = GSON.fromJson(SpecsIo.read(manifestFile), ClangDumperManifest.class); + + if (manifest == null) { + throw new RuntimeException("Could not parse clang-dumper manifest from '" + manifestFile + "'"); + } + + manifest.validate(); + return manifest; + } + + public static WebResourceProvider getAssetResource(ClangDumperManifestAsset asset) { + return WebResourceProvider.newInstance(getReleaseBaseUrl(), asset.filename(), getReleaseTag() + "-" + asset.sha256()); + } + + private static String getReleaseBaseUrl() { + return RELEASE_ROOT + getReleaseTag() + "/"; + } + + public record ClangDumperManifest(int schema_version, List assets) { + + public void validate() { + if (schema_version != 1) { + throw new RuntimeException("Unsupported clang-dumper manifest schema version: " + schema_version); + } + + if (assets == null || assets.isEmpty()) { + throw new RuntimeException("Clang-dumper manifest does not contain assets"); + } + } + + public ClangDumperManifestAsset getAsset(String platform, String arch, String kind) { + Objects.requireNonNull(platform); + Objects.requireNonNull(arch); + Objects.requireNonNull(kind); + + Optional asset = assets.stream() + .filter(candidate -> candidate.matches(platform, arch, kind)) + .findFirst(); + + return asset.orElseThrow(() -> new RuntimeException("Could not find clang-dumper asset for platform '" + + platform + "', architecture '" + arch + "' and kind '" + kind + "'")); + } + } + + public record ClangDumperManifestAsset(String filename, String kind, String platform, String arch, int llvm_major, + String sha256) { + + public boolean matches(String platform, String arch, String kind) { + return this.platform.equals(platform) && this.arch.equals(arch) && this.kind.equals(kind); + } + } } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 3aa4b56598..e542fba19d 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -13,6 +13,8 @@ package pt.up.fe.specs.clang; +import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifest; +import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifestAsset; import pt.up.fe.specs.clang.codeparser.CodeParser; import pt.up.fe.specs.clang.dumper.ClangAstDumper; import pt.up.fe.specs.clang.parsers.TopLevelNodesParser; @@ -20,57 +22,57 @@ import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsSystem; -import pt.up.fe.specs.util.lazy.Lazy; -import pt.up.fe.specs.util.providers.FileResourceManager; -import pt.up.fe.specs.util.providers.FileResourceProvider; import pt.up.fe.specs.util.providers.FileResourceProvider.ResourceWriteData; import pt.up.fe.specs.util.system.ProcessOutputAsString; import java.io.File; -import java.util.*; +import java.io.IOException; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HexFormat; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; public class ClangResources { - private static final FileResourceManager CLANG_AST_RESOURCES = FileResourceManager - .fromEnum(ClangAstFileResource.class); - private static final Map CLANG_FILES_CACHE = new ConcurrentHashMap<>(); private final static String CLANG_FOLDERNAME = "clang_ast_exe"; - private final Lazy cudalibFolder = Lazy.newInstance(this::prepareBuiltinCudaLib); + private static final AtomicInteger HAS_LIBC = new AtomicInteger(-1); private final CodeParser options; - - private static final AtomicInteger HAS_LIBC = new AtomicInteger(-1); - public ClangResources(CodeParser options) { this.options = options; } public ClangFiles getClangFiles(String version, LibcMode libcMode) { - // Create key - var key = libcMode.name() + "_" + version + "_" + getClangResourceFolder().getAbsolutePath(); + var effectiveVersion = version.isEmpty() ? ClangAstWebResource.getReleaseTag() : version; + var key = libcMode.name() + "_" + effectiveVersion + "_" + getClangResourceFolder().getAbsolutePath(); - // Check if cached var files = CLANG_FILES_CACHE.get(key); if (files != null) { SpecsLogs.debug(() -> "Using cached version of Clang files: " + files); return files; } - File clangExecutable = prepareResources(version); - List builtinIncludes = prepareIncludes(clangExecutable, libcMode); + var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); + File clangExecutable = prepareResources(manifest); + List builtinIncludes = prepareIncludes(manifest, clangExecutable, libcMode); var newFiles = new ClangFiles(clangExecutable, builtinIncludes); SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); - // Store in cache CLANG_FILES_CACHE.put(key, newFiles); return newFiles; @@ -79,52 +81,17 @@ public ClangFiles getClangFiles(String version, LibcMode libcMode) { /** * @return path to the executable that was copied */ - private File prepareResources(String version) { - + private File prepareResources(ClangDumperManifest manifest) { File resourceFolder = getClangResourceFolder(); - SupportedPlatform platform = SupportedPlatform.getCurrentPlatform(); - FileResourceProvider executableResource = getVersionedResource(getExecutableResource(platform), version); - // Copy executable - ResourceWriteData executable = executableResource.writeVersioned(resourceFolder, ClangResources.class); + var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; + ResourceWriteData executable = downloadAsset(manifest, executableKind, resourceFolder); - // If Windows, copy additional dependencies - if (platform == SupportedPlatform.WINDOWS) { - for (FileResourceProvider resource : getWindowsResources()) { - resource.writeVersioned(resourceFolder, ClangResources.class); - } - } else if (platform == SupportedPlatform.MAC_OS) { - for (FileResourceProvider resource : getMacOSResources()) { - resource.writeVersioned(resourceFolder, ClangResources.class); - } - } else if (platform == SupportedPlatform.LINUX) { - for (FileResourceProvider resource : getLinuxResources()) { - resource.writeVersioned(resourceFolder, ClangResources.class); - } - } - - // If on Windows, preemptively unblock file, due to possible Mark-of-the-Web restrictions if (platform.isWindows()) { - var command = List.of(SpecsSystem.getWindowsPowershell(), "-NoLogo", "-NoProfile", "-NonInteractive", - "-ExecutionPolicy", "Bypass", - "-Command", - "Unblock-File", - "-Path", - "\"" + executable.getFile().getAbsolutePath() + "\"", - "-ErrorAction", - "Stop" - ); - - var output = SpecsSystem.runProcess(command, true, true); - if (output.getReturnValue() == 0) { - SpecsLogs.info("Successfully unblocked dumper executable"); - } else { - SpecsLogs.info("Could not unblock dumper executable"); - } + unblockWindowsFile(executable.getFile()); } - // If file is new and we are in a flavor of Linux or MacOS, make file executable if (executable.isNewFile() && (platform.isLinux() || platform.isMacOs())) { SpecsSystem.runProcess(Arrays.asList("chmod", "+x", executable.getFile().getAbsolutePath()), false, true); } @@ -132,16 +99,23 @@ private File prepareResources(String version) { return executable.getFile(); } - private FileResourceProvider getVersionedResource(FileResourceProvider resource, String version) { - - // If version not defined, use the latest version of the resource - if (version.isEmpty()) { - version = resource.version(); + private void unblockWindowsFile(File executable) { + var command = List.of(SpecsSystem.getWindowsPowershell(), "-NoLogo", "-NoProfile", "-NonInteractive", + "-ExecutionPolicy", "Bypass", + "-Command", + "Unblock-File", + "-Path", + "\"" + executable.getAbsolutePath() + "\"", + "-ErrorAction", + "Stop" + ); + + var output = SpecsSystem.runProcess(command, true, true); + if (output.getReturnValue() == 0) { + SpecsLogs.info("Successfully unblocked dumper executable"); + } else { + SpecsLogs.info("Could not unblock dumper executable"); } - - // ClangAst executable versions are separated by an underscore - resource = resource.createResourceVersion("_" + version); - return resource; } public File getClangResourceFolder() { @@ -152,142 +126,7 @@ public static File getDefaultTempFolder() { return SpecsIo.getTempFolder(CLANG_FOLDERNAME); } - private FileResourceProvider getExecutableResource(SupportedPlatform platform) { - switch (platform) { - case WINDOWS: - return CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_EXE); - case LINUX: - if (ClangAstDumper.usePlugin()) { - return CLANG_AST_RESOURCES.get(ClangAstFileResource.LINUX_PLUGIN); - } else { - return CLANG_AST_RESOURCES.get(ClangAstFileResource.LINUX_EXE); - } - - case MAC_OS: - return CLANG_AST_RESOURCES.get(ClangAstFileResource.MAC_OS_EXE); - default: - throw new RuntimeException("Case not defined: '" + platform + "'"); - } - } - - private List getWindowsResources() { - List windowsResources = new ArrayList<>(); - - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL1)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL2)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL3)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL4)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL5)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL6)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL7)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL8)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_DLL9)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_CLANG_DLL)); - windowsResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.WIN_LLVM_DLL)); - - return windowsResources; - } - - private List getMacOSResources() { - List macosResources = new ArrayList<>(); - - macosResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.MAC_OS_LLVM_DLL)); - macosResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.MAC_OS_DLL1)); - - return macosResources; - } - - private List getLinuxResources() { - List linuxResources = new ArrayList<>(); - - linuxResources.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.LINUX_LLVM_DLL)); - - return linuxResources; - } - - private List prepareIncludes(File clangExecutable, LibcMode libcMode) { - - // Get base resource folder - File resourceFolder = getClangResourceFolder(); - - // Create list of include zips - List includesZips = new ArrayList<>(); - - // Get libc/libcxx resources, if required - if (useBuiltinLibc(clangExecutable, libcMode)) { - - // MacOS - if (SupportedPlatform.getCurrentPlatform().isMacOs()) { - var macosBuiltinResource = CLANG_AST_RESOURCES.get(ClangAstFileResource.LIBC_CXX_MACOS_COMPLETE); - includesZips.add(getVersionedResource(macosBuiltinResource, macosBuiltinResource.version())); - } - // Linux - else if (SupportedPlatform.getCurrentPlatform().isLinux()) { - var linuxBuiltinResource = CLANG_AST_RESOURCES.get(ClangAstFileResource.LIBC_CXX_LINUX_COMPLETE); - includesZips.add(getVersionedResource(linuxBuiltinResource, linuxBuiltinResource.version())); - } - // Windows - else if (SupportedPlatform.getCurrentPlatform().isWindows()) { - var windowsBuiltinResource = CLANG_AST_RESOURCES.get(ClangAstFileResource.LIBC_CXX_WIN32_COMPLETE); - includesZips.add(getVersionedResource(windowsBuiltinResource, windowsBuiltinResource.version())); - } else { - throw new RuntimeException("Unsupported platform: " + SupportedPlatform.getCurrentPlatform()); - } - - - } - - // Always add OpenMP includes - includesZips.add(CLANG_AST_RESOURCES.get(ClangAstFileResource.OPENMP_INCLUDES)); - - // Download includes zips, later we check if any of them is new - List zipFiles = includesZips.stream() - .map(resource -> resource.writeVersioned(resourceFolder, ClangResources.class)) - .collect(Collectors.toList()); - - - var extractedFolders = new ArrayList(); - - // If a new file has been written or if folder exists but is empty, delete corresponding includes folder, and extract zip again - for (var zipFile : zipFiles) { - - // Obtain folder for zip - var zipFoldername = "include_" + SpecsIo.removeExtension(zipFile.getFile()); - var extractedFolder = SpecsIo.mkdir(resourceFolder, zipFoldername); - - // Add to extracted folders list - extractedFolders.add(extractedFolder); - - // Skip extraction if zip is not new and folder is not empty - if (!zipFile.isNewFile() && !SpecsIo.isEmptyFolder(extractedFolder)) { - continue; - } - - // Clean folder - SpecsIo.deleteFolderContents(extractedFolder); - - // Extract zip contents to folder - SpecsIo.extractZip(zipFile.getFile(), extractedFolder); - } - - // Add all folders inside extracted folders as system include - var includesFiles = new ArrayList(); - for (var extractedFolder : extractedFolders) { - var includeFolders = SpecsIo.getFolders(extractedFolder); - - includesFiles.addAll(includeFolders); - } - - - // Sort them alphabetically, by last foldername, include order is important - Collections.sort(includesFiles, Comparator.comparing(File::getName)); - SpecsLogs.debug(() -> "Includes folders: " + includesFiles); - - return includesFiles.stream().map(File::getAbsolutePath).toList(); - } - public static boolean useBuiltinLibc(File clangExecutable, LibcMode libcMode) { - return switch (libcMode) { case AUTO -> !hasLibC(clangExecutable); case BUILTIN_AND_LIBC -> true; @@ -298,7 +137,6 @@ public static boolean useBuiltinLibc(File clangExecutable, LibcMode libcMode) { private static boolean hasLibC(File clangExecutable) { var value = HAS_LIBC.get(); - // Check if initiallized if (value == -1) { var hasLibC = detectLibC(clangExecutable); value = hasLibC ? 1 : 0; @@ -316,17 +154,9 @@ private static boolean hasLibC(File clangExecutable) { throw new RuntimeException("Unexpected value: '" + value + "'"); } - /** - * Detects if the system has libc/licxx installed. - * - * @param clangExecutable - * @return - */ private static boolean detectLibC(File clangExecutable) { - File clangTest = SpecsIo.mkdir(SpecsIo.getTempFolder(), "clang_ast_test"); - // Write test files List testFiles = Arrays.asList(ClangAstResource.TEST_INCLUDES_C, ClangAstResource.TEST_INCLUDES_CPP) .stream() .map(resource -> resource.write(clangTest)) @@ -334,28 +164,23 @@ private static boolean detectLibC(File clangExecutable) { boolean needsLib = false; for (File testFile : testFiles) { - - // Invoke dumper var output = runClangAstDumper(clangExecutable, testFile); - // First check if there where no problems running the dumper if (output.getReturnValue() != 0) { - ClavaLog.info("Problems while running dumper to test in libc/libcxx is needed"); + ClavaLog.info("Problems while running dumper to test if libc/libcxx is needed"); needsLib = true; break; } - // Test files where built in such a way so that if a system include is present, it will generate code with a - // top level nodes, otherwise it generates an empty file - var topLevelNodesHeader = TopLevelNodesParser.getTopLevelNodesHeader(); - - var foundInclude = output.getOutput().contains(topLevelNodesHeader); + if (!testFile.getName().endsWith(".cpp")) { + continue; + } - if (!foundInclude) { + var topLevelNodesHeader = TopLevelNodesParser.getTopLevelNodesHeader(); + if (!output.getOutput().contains(topLevelNodesHeader)) { needsLib = true; break; } - } if (needsLib) { @@ -365,7 +190,6 @@ private static boolean detectLibC(File clangExecutable) { } return !needsLib; - } private static ProcessOutputAsString runClangAstDumper(File clangExecutable, File testFile) { @@ -373,29 +197,161 @@ private static ProcessOutputAsString runClangAstDumper(File clangExecutable, Fil return SpecsSystem.runProcess(arguments, true, false); } - public File getBuiltinCudaLib() { - return cudalibFolder.get(); + private List prepareIncludes(ClangDumperManifest manifest, File clangExecutable, LibcMode libcMode) { + if (!useBuiltinLibc(clangExecutable, libcMode)) { + return List.of(); + } + + return prepareIncludes(manifest); + } + + private List prepareIncludes(ClangDumperManifest manifest) { + var extractedFolder = prepareIncludesFolder(manifest); + var includeFolders = getIncludeFolders(extractedFolder); + SpecsLogs.debug(() -> "Includes folders: " + includeFolders); + + return includeFolders.stream().map(File::getAbsolutePath).toList(); + } + + private File prepareIncludesFolder(ClangDumperManifest manifest) { + File resourceFolder = getClangResourceFolder(); + ResourceWriteData zipFile = downloadAsset(manifest, "includes", resourceFolder); + + var zipFoldername = "include_" + SpecsIo.removeExtension(zipFile.getFile()); + var extractedFolder = SpecsIo.mkdir(resourceFolder, zipFoldername); + + if (zipFile.isNewFile() || SpecsIo.isEmptyFolder(extractedFolder)) { + SpecsIo.deleteFolderContents(extractedFolder); + SpecsIo.extractZip(zipFile.getFile(), extractedFolder); + } + + return extractedFolder; + } + + private List getIncludeFolders(File extractedFolder) { + var includeFolders = new LinkedHashSet(); + + var usrFolder = new File(extractedFolder, "usr"); + var targetFolders = SpecsIo.getFolders(usrFolder).stream() + .filter(folder -> folder.getName().contains("-")) + .sorted(Comparator.comparing(File::getName)) + .toList(); + + var cxxFolder = new File(usrFolder, "c++"); + var cxxVersionFolders = SpecsIo.getFolders(cxxFolder).stream() + .filter(folder -> folder.getName().chars().allMatch(Character::isDigit)) + .sorted(Comparator.comparing(File::getName).reversed()) + .toList(); + + addIfDirectory(includeFolders, extractedFolder, "clang"); + + if (!cxxVersionFolders.isEmpty()) { + var cxxVersionFolder = cxxVersionFolders.get(0); + includeFolders.add(cxxVersionFolder); + + for (var targetFolder : targetFolders) { + addIfDirectory(includeFolders, targetFolder, "c++/" + cxxVersionFolder.getName()); + } + } + + addIfDirectory(includeFolders, extractedFolder, "usr"); + includeFolders.addAll(targetFolders); + + addIfDirectory(includeFolders, extractedFolder, "include"); + + return new ArrayList<>(includeFolders); } - private File prepareBuiltinCudaLib() { - var fileResource = CLANG_AST_RESOURCES.get(ClangAstFileResource.CUDA_LIB); - var resourceFolder = getClangResourceFolder(); - var cudalibFolder = SpecsIo.mkdir(new File(resourceFolder, "cudalib")); + private static void addIfDirectory(LinkedHashSet folders, File parent, String relativePath) { + var folder = new File(parent, relativePath); + if (folder.isDirectory()) { + folders.add(folder); + } + } - // Download includes zips, check if any of them is new - ResourceWriteData zipFile = fileResource.writeVersioned(resourceFolder, ClangResources.class); + private ResourceWriteData downloadAsset(ClangDumperManifest manifest, String kind, File resourceFolder) { + var platform = getManifestPlatform(); + var arch = getManifestArch(platform); + var asset = manifest.getAsset(platform, arch, kind); + var resource = ClangAstWebResource.getAssetResource(asset); + var writeData = resource.writeVersioned(resourceFolder, ClangResources.class); + + if (!hasExpectedSha256(writeData.getFile(), asset)) { + SpecsLogs.info("Downloaded clang-dumper asset '" + asset.filename() + + "' does not match the expected checksum, downloading it again."); + SpecsIo.delete(writeData.getFile()); + writeData = resource.writeVersioned(resourceFolder, ClangResources.class); + } - // If a new file has been written, delete includes folder, and extract all zips again - // Extracting all because zips might have several folders and we are not determining which should be updated - if (zipFile.isNewFile()) { - // Clean folder - SpecsIo.deleteFolderContents(cudalibFolder); + if (!hasExpectedSha256(writeData.getFile(), asset)) { + throw new RuntimeException("Downloaded clang-dumper asset '" + asset.filename() + + "' does not match expected SHA-256 '" + asset.sha256() + "'"); + } - // Extract zip - SpecsIo.extractZip(zipFile.getFile(), cudalibFolder); + return writeData; + } + + private static boolean hasExpectedSha256(File file, ClangDumperManifestAsset asset) { + return asset.sha256().equalsIgnoreCase(calculateSha256(file)); + } + + private static String calculateSha256(File file) { + try { + var digest = MessageDigest.getInstance("SHA-256"); + try (var inputStream = Files.newInputStream(file.toPath())) { + inputStream.transferTo(new java.io.OutputStream() { + @Override + public void write(int b) { + digest.update((byte) b); + } + + @Override + public void write(byte[] b, int off, int len) { + digest.update(b, off, len); + } + }); + } + + return HexFormat.of().formatHex(digest.digest()); + } catch (IOException | NoSuchAlgorithmException e) { + throw new RuntimeException("Could not calculate SHA-256 for file '" + file + "'", e); } + } - // Returnb cuda lib folder - return cudalibFolder; + private static String getManifestPlatform() { + var platform = SupportedPlatform.getCurrentPlatform(); + + if (platform.isLinux()) { + return "linux"; + } + + if (platform.isMacOs()) { + return "macos"; + } + + if (platform.isWindows()) { + return "windows"; + } + + throw new RuntimeException("Unsupported platform: " + platform); + } + + private static String getManifestArch(String platform) { + var osArch = System.getProperty("os.arch").toLowerCase(); + + if (osArch.equals("amd64") || osArch.equals("x86_64")) { + return platform.equals("windows") ? "x86_64" : "x64"; + } + + if (osArch.equals("aarch64") || osArch.equals("arm64")) { + return "arm64"; + } + + throw new RuntimeException("Unsupported architecture for clang-dumper: " + osArch); + } + + public File getBuiltinCudaLib() { + var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); + return prepareIncludesFolder(manifest); } } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/SupportedPlatform.java b/ClangAstParser/src/pt/up/fe/specs/clang/SupportedPlatform.java index 094c8662e6..0564571a84 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/SupportedPlatform.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/SupportedPlatform.java @@ -74,10 +74,6 @@ private static SupportedPlatform calculateCurrentPlatform() { // Linux if (SpecsPlatforms.isLinux()) { - if (SpecsPlatforms.isLinuxArm()) { - throw new RuntimeException("ARM-based platforms are not currently supported"); - } - return LINUX; } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 0a319a6d89..d9c8551a49 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -17,6 +17,7 @@ import org.suikasoft.jOptions.JOptionsUtils; import org.suikasoft.jOptions.streamparser.LineStreamParser; import pt.up.fe.specs.clang.ClangAstKeys; +import pt.up.fe.specs.clang.ClangAstResource; import pt.up.fe.specs.clang.ClangResources; import pt.up.fe.specs.clang.cilk.CilkParser; import pt.up.fe.specs.clang.codeparser.CodeParser; @@ -194,7 +195,9 @@ private ClangAstData parsePrivate(File sourceFile, String id, Standard standard, } // Set standard to CUDA else if (isCuda && !standard.isCuda()) { - arguments.add("-std=cuda"); + arguments.add(standard.isCxx() ? standard.getFlag() : Standard.CXX17.getFlag()); + } else if (isCuda) { + arguments.add(Standard.CXX17.getFlag()); } else { arguments.add(standard.getFlag()); } @@ -223,6 +226,9 @@ else if (isCuda && !standard.isCuda()) { } // If CUDA, add corresponding flags else if (isCuda) { + arguments.add("-x"); + arguments.add("cuda"); + if (SpecsPlatforms.isWindows()) { ClavaLog.info("CUDA parsing is not supported in Windows, run at your own risk"); arguments.addAll(Arrays.asList("-fms-compatibility", "-D_MSC_VER", "-D_LIBCPP_MSVCRT")); @@ -231,12 +237,18 @@ else if (isCuda) { arguments.add("--cuda-gpu-arch=" + parserConfig.get(CodeParser.CUDA_GPU_ARCH)); var cudaPath = parserConfig.get(CodeParser.CUDA_PATH); - if (!cudaPath.isBlank()) { - - // Check if should use built-in CUDA lib - File cudaFolder = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()) - ? clangResources.getBuiltinCudaLib() - : SpecsIo.existingFolder(cudaPath); + var useBuiltinCudaLib = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()); + + if (useBuiltinCudaLib) { + arguments.add("-nocudainc"); + arguments.add("-nocudalib"); + arguments.add("-include"); + arguments.add("__clang_cuda_runtime_wrapper.h"); + arguments.add("-include"); + arguments.add(ClangAstResource.CUDA_COMPATIBILITY.write(SpecsIo.getTempFolder()).getAbsolutePath()); + } else if (!cudaPath.isBlank()) { + + File cudaFolder = SpecsIo.existingFolder(cudaPath); ClavaLog.debug("Setting --cuda-path to folder '" + cudaFolder.getAbsolutePath() + "'"); arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java index 23d6051050..3c3f1044cf 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java @@ -145,15 +145,13 @@ public TranslationUnit parseTu(File sourceFile) { if (ClavaNodes.isNullId(topLevelTypeId)) { continue; } - ClavaNode parsedNode = data.get(ClangAstData.CLAVA_NODES).get(topLevelTypeId); - Objects.requireNonNull(parsedNode, () -> "No node for type '" + topLevelTypeId + "'"); + data.get(ClangAstData.CLAVA_NODES).getOptional(topLevelTypeId); } // Parse top-level attributes for (String topLevelAttributeId : topLevelAttributes) { - ClavaNode parsedNode = data.get(ClangAstData.CLAVA_NODES).get(topLevelAttributeId); - Objects.requireNonNull(parsedNode, () -> "No node for attribute '" + topLevelAttributeId + "'"); + data.get(ClangAstData.CLAVA_NODES).getOptional(topLevelAttributeId); } // Create TU node diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java index 0cfe3473f8..b2d2d9e3c9 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java @@ -108,6 +108,10 @@ public void apply(LineStream lineStream, ClangAstData data) { ClavaNode node = parseNode(nodeId, classname, data, lineStream); + if (node == null) { + return; + } + // If UnsupportedNode, transform to DummyNode // node = transformUnsupportedNode(node); @@ -169,12 +173,11 @@ private ClavaNode parseNode(String nodeId, String classname, ClangAstData data, DataStore nodeData = data.get(ClangAstData.NODE_DATA).get(nodeId); if (nodeData == null) { - throw new RuntimeException("No ClavaData/DataStore for node '" + nodeId + "' (classname: " + classname - + "), data dumper is not being called (linestream index '" + lineStream.getLastLineIndex() + "')"); - // if (debug) - // SpecsLogs.msgInfo("No ClavaData for node '" + nodeId + "' (classname: " + classname - // + "), data dumper is not being called"); - // return new UnsupportedNode(classname, ClavaData.empty(), Collections.emptyList()); + if (debug) { + SpecsLogs.msgInfo("No ClavaData/DataStore for node '" + nodeId + "' (classname: " + classname + + "), skipping node (linestream index '" + lineStream.getLastLineIndex() + "')"); + } + return null; } // Get corresponding ClavaNode class @@ -237,11 +240,14 @@ private ClavaNode parseNode(String nodeId, String classname, ClangAstData data, // } // } - int index = i; - Objects.requireNonNull(child, - () -> "Did not find ClavaNode for child with index '" + index + "' and id '" + childId - + "' when parsing " - + clavaNodeClass.getSimpleName() + " -> " + nodeData); + if (child == null) { + if (debug) { + int index = i; + SpecsLogs.msgInfo("Did not find ClavaNode for child with index '" + index + "' and id '" + + childId + "' when parsing " + clavaNodeClass.getSimpleName() + ", skipping child"); + } + continue; + } child = processChild(child, clavaNodeClass, data); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java index 964fe3889a..fb96e68c42 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java @@ -31,6 +31,10 @@ import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.NullNodeType; +import pt.up.fe.specs.clava.ast.decl.Decl; +import pt.up.fe.specs.clava.ast.expr.Expr; +import pt.up.fe.specs.clava.ast.stmt.Stmt; +import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.context.ClavaFactory; import pt.up.fe.specs.util.SpecsCheck; import pt.up.fe.specs.util.SpecsSystem; @@ -93,7 +97,7 @@ public Optional getOptional(String nodeId) { ClavaNode clavaNode = clavaNodes.get(nodeId); if (clavaNode == null) { - ClavaLog.warning("Could not find ClavaNode with id '" + nodeId + ClavaLog.debug("Could not find ClavaNode with id '" + nodeId + "', returning an empty optional. Check if node is being visited. If parsing of includes is enabled, check that the parsing level is sufficient."); return Optional.empty(); @@ -148,10 +152,20 @@ public void queueSetNode(DataClass data, DataKey key + "', if node can be null, use queueOptional instead. Node data:\n" + data); } - // Get node - ClavaNode node = get(nodeId); - Class valueClass = key.getValueClass(); + ClavaNode node = clavaNodes.get(nodeId); + if (node == null) { + var nullNode = nullNodeFor(valueClass); + if (nullNode != null) { + data.set(key, valueClass.cast(nullNode)); + return; + } + + ClavaLog.debug("Could not find ClavaNode with id '" + nodeId + + "', skipping key '" + key.getName() + + "'. Check if node is being visited. If parsing of includes is enabled, check that the parsing level is sufficient."); + return; + } ClavaNode adaptedNode = adaptNode(node, valueClass); @@ -194,6 +208,26 @@ private ClavaNode adaptNode(ClavaNode node, Class value return node; } + private ClavaNode nullNodeFor(Class valueClass) { + if (valueClass.equals(Type.class)) { + return factory.nullType(); + } + + if (valueClass.equals(Decl.class)) { + return factory.nullDecl(); + } + + if (valueClass.equals(Stmt.class)) { + return factory.nullStmt(); + } + + if (valueClass.equals(Expr.class)) { + return factory.nullExpr(); + } + + return null; + } + public void queueSetOptionalNode(DataClass data, DataKey> key, String nodeId) { @@ -227,7 +261,29 @@ public void queueSetNodeList(DataClass data, DataKey

  • { @SuppressWarnings("unchecked") // If the nodes exist, they should be of the requested type - List nodes = nodeIds.stream().map(id -> (T) get(id)).collect(Collectors.toList()); + List nodes = nodeIds.stream() + .map(this::getOptional) + .flatMap(Optional::stream) + .map(node -> (T) node) + .collect(Collectors.toList()); + + data.set(key, nodes); + }; + + queuedActions.add(nodeToAdd); + } + + public void queueSetOptionalNodeList(DataClass data, DataKey> key, + List nodeIds) { + + Runnable nodeToAdd = () -> { + + @SuppressWarnings("unchecked") // If present, the nodes should be of the requested type + List nodes = nodeIds.stream() + .map(this::getOptional) + .flatMap(Optional::stream) + .map(node -> (T) node) + .collect(Collectors.toList()); data.set(key, nodes); }; diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java index b108271017..f7f11dc97a 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java @@ -87,7 +87,7 @@ public static DataStore parseDeclData(LineStream lines, ClangAstData dataStore) data.add(Decl.IS_INVALID_DECL, LineStreamParsers.oneOrZero(lines)); data.add(Decl.IS_MODULE_PRIVATE, LineStreamParsers.oneOrZero(lines)); - dataStore.getClavaNodes().queueSetNodeList(data, Decl.ATTRIBUTES, LineStreamParsers.stringList(lines)); + dataStore.getClavaNodes().queueSetOptionalNodeList(data, Decl.ATTRIBUTES, LineStreamParsers.stringList(lines)); // dataStore.getClavaNodes().queueSetOptionalNode(data, Decl.DECL_CONTEXT, lines.nextLine()); // dataStore.getClavaNodes().queueSetNodeList(data, Decl.DECL_CONTEXT, LineStreamParsers.stringList(lines)); // data.add(Decl.DECL_CONTEXT_IDS, LineStreamParsers.stringList(lines)); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ExprDataParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ExprDataParser.java index 3687d9e622..8582135167 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ExprDataParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ExprDataParser.java @@ -119,7 +119,7 @@ public static DataStore parseCharacterLiteralData(LineStream lines, ClangAstData DataStore data = parseLiteralData(lines, dataStore); data.add(CharacterLiteral.VALUE, LineStreamParsers.longInt(lines)); - data.add(CharacterLiteral.KIND, LineStreamParsers.enumFromInt(CharacterKind.getEnumHelper(), lines)); + data.add(CharacterLiteral.KIND, LineStreamParsers.enumFromName(CharacterKind.getEnumHelper(), lines)); return data; } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java index 43c2b2efb0..a52881b784 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java @@ -64,7 +64,7 @@ public static DataStore parseGotoStmtData(LineStream lines, ClangAstData dataSto public static DataStore parseAttributedStmtData(LineStream lines, ClangAstData dataStore) { DataStore data = parseStmtData(lines, dataStore); - dataStore.getClavaNodes().queueSetNodeList(data, AttributedStmt.STMT_ATTRIBUTES, + dataStore.getClavaNodes().queueSetOptionalNodeList(data, AttributedStmt.STMT_ATTRIBUTES, LineStreamParsers.stringList(lines)); return data; diff --git a/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts b/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts index 205e431aed..123ec0d47b 100644 --- a/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts +++ b/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts @@ -42,7 +42,7 @@ export default class LocalStaticToGlobal extends SimplePass { } // With static storage - if ($jp.storageClass !== StorageClass.STATIC) { + if ($jp.storageClass.toLowerCase() !== StorageClass.STATIC) { return false; } diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/attr/enums/AttributeKind.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/attr/enums/AttributeKind.java index 8158847f4a..5b0e3d7e71 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/attr/enums/AttributeKind.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/attr/enums/AttributeKind.java @@ -24,12 +24,19 @@ import pt.up.fe.specs.util.utilities.CachedItems; public enum AttributeKind implements StringProvider { - AddressSpace, AnnotateType, + ArmIn, + ArmInOut, ArmMveStrictPolymorphism, + ArmOut, + ArmPreserves, + ArmStreaming, + ArmStreamingCompatible, BTFTypeTag, CmseNSCall, + HLSLGroupSharedAddressSpace, + HLSLParamModifier, NoDeref, ObjCGC, ObjCInertUnsafeUnretained, @@ -49,15 +56,17 @@ public enum AttributeKind implements StringProvider { TypeNullable, TypeNullableResult, UPtr, + WebAssemblyFuncref, + CodeAlign, FallThrough, Likely, MustTail, OpenCLUnrollHint, - Suppress, Unlikely, AlwaysInline, NoInline, NoMerge, + Suppress, AArch64SVEPcs, AArch64VectorPcs, AMDGPUKernelCall, @@ -67,6 +76,7 @@ public enum AttributeKind implements StringProvider { FastCall, IntelOclBicc, LifetimeBound, + M68kRTD, MSABI, NSReturnsRetained, ObjCOwnership, @@ -94,6 +104,8 @@ public enum AttributeKind implements StringProvider { PassObjectSize, ReleaseHandle, UseHandle, + HLSLSV_DispatchThreadID, + HLSLSV_GroupIndex, AMDGPUFlatWorkGroupSize, AMDGPUNumSGPR, AMDGPUNumVGPR, @@ -116,6 +128,8 @@ public enum AttributeKind implements StringProvider { ArcWeakrefUnavailable, ArgumentWithTypeTag, ArmBuiltinAlias, + ArmLocallyStreaming, + ArmNew, Artificial, AsmLabel, AssertCapability, @@ -124,7 +138,9 @@ public enum AttributeKind implements StringProvider { AssumeAligned, Assumption, Availability, + AvailableOnlyInDefaultEvalMethod, BPFPreserveAccessIndex, + BPFPreserveStaticOffset, BTFDeclTag, Blocks, Builtin, @@ -153,6 +169,7 @@ public enum AttributeKind implements StringProvider { CapturedRecord, Cleanup, CmseNSEntry, + CodeModel, CodeSeg, Cold, Common, @@ -163,6 +180,12 @@ public enum AttributeKind implements StringProvider { ConsumableAutoCast, ConsumableSetOnRead, Convergent, + CoroDisableLifetimeBound, + CoroLifetimeBound, + CoroOnlyDestroyWhenComplete, + CoroReturnType, + CoroWrapper, + CountedBy, DLLExport, DLLExportStaticLocal, DLLImport, @@ -193,7 +216,8 @@ public enum AttributeKind implements StringProvider { GuardedVar, HIPManaged, HLSLNumThreads, - HLSLSV_GroupIndex, + HLSLResource, + HLSLResourceBinding, HLSLShader, Hot, IBAction, @@ -209,6 +233,7 @@ public enum AttributeKind implements StringProvider { M68kInterrupt, MIGServerRoutine, MSAllocator, + MSConstexpr, MSInheritance, MSNoVTable, MSP430Interrupt, @@ -216,6 +241,7 @@ public enum AttributeKind implements StringProvider { MSVtorDisp, MaxFieldAlignment, MayAlias, + MaybeUndef, MicroMips, MinSize, MinVectorWidth, @@ -227,6 +253,7 @@ public enum AttributeKind implements StringProvider { NSErrorDomain, NSReturnsAutoreleased, NSReturnsNotRetained, + NVPTXKernel, Naked, NoAlias, NoCommon, @@ -246,6 +273,7 @@ public enum AttributeKind implements StringProvider { NoThreadSafetyAnalysis, NoThrow, NoUniqueAddress, + NoUwtable, NotTailCalled, OMPAllocateDecl, OMPCaptureNoInit, @@ -288,11 +316,13 @@ public enum AttributeKind implements StringProvider { PragmaClangRodataSection, PragmaClangTextSection, PreferredName, + PreferredType, PtGuardedBy, PtGuardedVar, Pure, RISCVInterrupt, RandomizeLayout, + ReadOnlyPlacement, Reinitializes, ReleaseCapability, ReqdWorkGroupSize, @@ -313,6 +343,7 @@ public enum AttributeKind implements StringProvider { SpeculativeLoadHardening, StandaloneDebug, StrictFP, + StrictGuardStackCheck, SwiftAsync, SwiftAsyncError, SwiftAsyncName, @@ -320,12 +351,15 @@ public enum AttributeKind implements StringProvider { SwiftBridge, SwiftBridgedTypedef, SwiftError, + SwiftImportAsNonGeneric, + SwiftImportPropertyAsAccessors, SwiftName, SwiftNewType, SwiftPrivate, TLSModel, Target, TargetClones, + TargetVersion, TestTypestate, TransparentUnion, TrivialABI, @@ -334,6 +368,7 @@ public enum AttributeKind implements StringProvider { TypeVisibility, Unavailable, Uninitialized, + UnsafeBufferUsage, Unused, Used, UsingIfExists, @@ -382,6 +417,8 @@ public enum AttributeKind implements StringProvider { Overloadable, RenderScriptKernel, SwiftObjCMembers, + SwiftVersionedAddition, + SwiftVersionedRemoval, Thread, FirstAttr, LastAttr, @@ -398,7 +435,9 @@ public enum AttributeKind implements StringProvider { FirstInheritableParamAttr, LastInheritableParamAttr, FirstParameterABIAttr, - LastParameterABIAttr; + LastParameterABIAttr, + FirstHLSLAnnotationAttr, + LastHLSLAnnotationAttr; private static final Lazy> ENUM_HELPER = EnumHelperWithValue .newLazyHelperWithValue(AttributeKind.class); diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java index 21e3022c48..cae31033f3 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java @@ -24,13 +24,13 @@ import pt.up.fe.specs.util.providers.StringProvider; public enum InitializationStyle implements StringProvider { - NO_INIT, - CINIT, // C-style initialization with assignment - CALL_INIT("callinit"), // Call-style initialization (C++98) - LIST_INIT("listinit"); // Direct list-initialization (C++11) + CINIT, + CALL_INIT, + LIST_INIT, + ParenListInit; private static Lazy> ENUM_HELPER = EnumHelperWithValue - .newLazyHelperWithValue(InitializationStyle.class, NO_INIT); + .newLazyHelperWithValue(InitializationStyle.class); public static EnumHelperWithValue getHelper() { return ENUM_HELPER.get(); @@ -56,9 +56,8 @@ public String getCode(VarDecl node) { switch (this) { case CINIT: return cinitCode(node); - case NO_INIT: - return ""; case CALL_INIT: + case ParenListInit: return callInitCode(node); case LIST_INIT: return listInitCode(node); @@ -111,4 +110,4 @@ private String listInitCode(VarDecl node) { // .orElseThrow(() -> new RuntimeException()); } -} \ No newline at end of file +} diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java index 6ae4a3394d..1aa999c0fd 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java @@ -22,49 +22,11 @@ * */ public enum Linkage { - - /** - * The entity is unique and can only be referred to from within its scope. - */ - NoLinkage, - - /** - * the entity can be referred to from within the translation unit, but not other translation units. - */ - InternalLinkage, - - /** - * External linkage within a unique namespace. - * - *

    - * From the language perspective, these entities have external linkage. However, since they reside in an anonymous - * namespace, their names are unique to this translation unit, which is equivalent to having internal linkage from - * the code-generation point of view. - */ - UniqueExternalLinkage, - - /** - * No linkage according to the standard, but is visible from other translation units because of types defined in a - * inline function. - */ - VisibleNoLinkage, - - /** - * Internal linkage according to the Modules TS, but can be referred to from other translation units indirectly - * through inline functions and templates in the module interface. - * - */ - ModuleInternalLinkage, - - /** - * Module linkage, which indicates that the entity can be referred to from other translation units within the same - * module, and indirectly from arbitrary other translation units through inline functions and templates in the - * module interface. - */ - ModuleLinkage, - - /** - * The entity can be referred to from other translation units. - */ - ExternalLinkage; + Invalid, + None, + Internal, + UniqueExternal, + VisibleNone, + Module, + External; } diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java index 3e4ab11745..ee772ac786 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java @@ -18,7 +18,7 @@ import pt.up.fe.specs.util.providers.StringProvider; public enum CharacterKind implements StringProvider { - ASCII, + ORDINARY, WIDE, UTF8, UTF16, diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/StringKind.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/StringKind.java index afdcfbb29b..8f141a1a51 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/StringKind.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/StringKind.java @@ -18,12 +18,12 @@ import pt.up.fe.specs.util.exceptions.NotImplementedException; public enum StringKind { - ORDINARY, WIDE, UTF8(true), UTF16(true), - UTF32(true); + UTF32(true), + UNEVALUATED; private final boolean isUTF; @@ -47,6 +47,8 @@ public String getPrefix() { return "u"; case UTF32: return "U"; + case UNEVALUATED: + return ""; default: throw new NotImplementedException(this); } diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java index 0772dcacef..8f29b6be9e 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java @@ -50,6 +50,10 @@ public ElaboratedType(DataStore data, Collection children) @Override public Type desugar() { + if (!hasValue(NAMED_TYPE)) { + return this; + } + return getNamedType(); } @@ -63,6 +67,9 @@ public void setNamedType(Type namedType) { @Override public String getCode(ClavaNode sourceNode, String name) { + if (!hasValue(NAMED_TYPE) || getNamedType() instanceof NullType) { + return super.getCode(sourceNode, name); + } String code = getKeyword().getCode(); if (!code.isEmpty()) { diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/CallingConvention.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/CallingConvention.java index b5f58e3af6..47d8f46fbc 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/CallingConvention.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/CallingConvention.java @@ -18,7 +18,6 @@ import pt.up.fe.specs.util.providers.StringProvider; public enum CallingConvention implements StringProvider { - C, X86StdCall, X86FastCall, @@ -39,7 +38,8 @@ public enum CallingConvention implements StringProvider { PreserveAll, AArch64VectorCall, AArch64SVEPCS, - AMDGPUKernelCall; + AMDGPUKernelCall, + M68kRTD; private static final Lazy> HELPER = EnumHelperWithValue .newLazyHelperWithValue(CallingConvention.class); diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/ElaboratedTypeKeyword.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/ElaboratedTypeKeyword.java index b9a3be4baa..3827d3650c 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/ElaboratedTypeKeyword.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/ElaboratedTypeKeyword.java @@ -19,13 +19,13 @@ import pt.up.fe.specs.util.providers.StringProvider; public enum ElaboratedTypeKeyword implements StringProvider { - STRUCT, - INTERFACE, - UNION, - CLASS, - ENUM, - TYPENAME, - NONE; + Struct, + Interface, + Union, + Class, + Enum, + Typename, + None; private static final Lazy> HELPER = EnumHelperWithValue .newLazyHelperWithValue(ElaboratedTypeKeyword.class); @@ -35,7 +35,7 @@ public static EnumHelperWithValue getHelper() { } public String getCode() { - if (this == NONE) { + if (this == None) { return ""; } @@ -46,4 +46,4 @@ public String getCode() { public String getString() { return SpecsStrings.toCamelCase(name()); } -} \ No newline at end of file +} diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/UnaryTransformTypeKind.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/UnaryTransformTypeKind.java index 920be2ab40..7710528a14 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/UnaryTransformTypeKind.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/enums/UnaryTransformTypeKind.java @@ -14,8 +14,21 @@ package pt.up.fe.specs.clava.ast.type.enums; public enum UnaryTransformTypeKind { - + AddLvalueReference, + AddPointer, + AddRvalueReference, Decay, + MakeSigned, + MakeUnsigned, + RemoveAllExtents, + RemoveConst, + RemoveCV, + RemoveCVRef, + RemoveExtent, + RemovePointer, + RemoveReference, + RemoveRestrict, + RemoveVolatile, EnumUnderlyingType; } diff --git a/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt b/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt index 3030db32df..fb6efbf514 100644 --- a/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt +++ b/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt @@ -9,5 +9,5 @@ After setting int with array: int Original template args: int,float After second arg to double: int,double After setting with array [double, int]: double,int -After setting typedef_to_change: std::vector::const_iterator -After setting changed_typedef_type: std::vector::const_iterator \ No newline at end of file +After setting typedef_to_change: std::vector::const_iterator +After setting changed_typedef_type: std::vector::const_iterator diff --git a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java index a0eac8c1e8..df96d1bb6d 100644 --- a/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java +++ b/ClavaWeaver/src/pt/up/fe/specs/clava/weaver/importable/AstFactory.java @@ -680,7 +680,7 @@ public static ATypedefDecl typedefDecl(CxxWeaver weaver, AType underlyingT public static AElaboratedType structType(CxxWeaver weaver, AStruct struct) { var namedType = (Type) struct.getTypeImpl().getNodeImpl(); - var elaboratedType = weaver.getFactory().elaboratedType(ElaboratedTypeKeyword.STRUCT, namedType); + var elaboratedType = weaver.getFactory().elaboratedType(ElaboratedTypeKeyword.Struct, namedType); return CxxJoinpoints.create(elaboratedType, weaver, AElaboratedType.class); } From 604fcd99a8f9b8d653069f7bc8f8aadd7b1fea68 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 3 Jul 2026 02:52:17 +0100 Subject: [PATCH 29/99] Read clang dumper include entrypoints Use entrypoints.txt from extracted clang-dumper include archives as the ordered system include roots. Throw when entrypoints.txt is missing instead of inferring roots from archive folder names. --- .../pt/up/fe/specs/clang/ClangResources.java | 47 ++++--------------- 1 file changed, 8 insertions(+), 39 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index e542fba19d..28662bd010 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -30,11 +30,8 @@ import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.HexFormat; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -229,44 +226,16 @@ private File prepareIncludesFolder(ClangDumperManifest manifest) { } private List getIncludeFolders(File extractedFolder) { - var includeFolders = new LinkedHashSet(); - - var usrFolder = new File(extractedFolder, "usr"); - var targetFolders = SpecsIo.getFolders(usrFolder).stream() - .filter(folder -> folder.getName().contains("-")) - .sorted(Comparator.comparing(File::getName)) - .toList(); - - var cxxFolder = new File(usrFolder, "c++"); - var cxxVersionFolders = SpecsIo.getFolders(cxxFolder).stream() - .filter(folder -> folder.getName().chars().allMatch(Character::isDigit)) - .sorted(Comparator.comparing(File::getName).reversed()) - .toList(); - - addIfDirectory(includeFolders, extractedFolder, "clang"); - - if (!cxxVersionFolders.isEmpty()) { - var cxxVersionFolder = cxxVersionFolders.get(0); - includeFolders.add(cxxVersionFolder); - - for (var targetFolder : targetFolders) { - addIfDirectory(includeFolders, targetFolder, "c++/" + cxxVersionFolder.getName()); - } + var entrypointsFile = new File(extractedFolder, "entrypoints.txt"); + if (!entrypointsFile.isFile()) { + throw new RuntimeException("Could not find include archive entrypoints file '" + entrypointsFile + "'"); } - addIfDirectory(includeFolders, extractedFolder, "usr"); - includeFolders.addAll(targetFolders); - - addIfDirectory(includeFolders, extractedFolder, "include"); - - return new ArrayList<>(includeFolders); - } - - private static void addIfDirectory(LinkedHashSet folders, File parent, String relativePath) { - var folder = new File(parent, relativePath); - if (folder.isDirectory()) { - folders.add(folder); - } + return SpecsIo.read(entrypointsFile).lines() + .map(String::trim) + .filter(line -> !line.isEmpty()) + .map(line -> new File(extractedFolder, line)) + .toList(); } private ResourceWriteData downloadAsset(ClangDumperManifest manifest, String kind, File resourceFolder) { From d0def94fd696d48ae656aa7c2e0e09f56ad8ff38 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 6 Jul 2026 18:47:31 +0100 Subject: [PATCH 30/99] Cache clang dumper by release version --- .../pt/up/fe/specs/clang/ClangAstKeys.java | 10 - .../fe/specs/clang/ClangAstWebResource.java | 2 +- .../pt/up/fe/specs/clang/ClangResources.java | 211 +++++++++++++++++- .../fe/specs/clang/codeparser/CodeParser.java | 2 +- .../clang/codeparser/ParallelCodeParser.java | 4 +- Clava-JS/code/sideEffects.ts | 8 +- 6 files changed, 207 insertions(+), 30 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java index fd9907689b..ec17d8a8d6 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java @@ -27,8 +27,6 @@ public interface ClangAstKeys { - DataKey CLANGAST_VERSION = KeyFactory.string("clangast_version", ""); - /** * What libc/libcxx mode should be used. */ @@ -55,7 +53,6 @@ public static String getFlagIgnoreIncludes() { static DataStore toDataStore(List flags) { DataStore config = DataStore.newInstance(ClavaOptions.STORE_DEFINITION, false); final String stdPrefix = "-std="; - final String clangAstDumperPrefix = "-clang-dumper="; final String cilkFlag = "-fcilkplus"; // Search options @@ -78,13 +75,6 @@ static DataStore toDataStore(List flags) { continue; } - // If ClangAstDumper version, parse option - if (flag.startsWith(clangAstDumperPrefix)) { - String version = flag.substring(clangAstDumperPrefix.length()); - config.set(ClangAstKeys.CLANGAST_VERSION, version); - continue; - } - // If Cilk flag, add option if (flag.equals(cilkFlag)) { config.set(ClangAstKeys.USES_CILK); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index ce2a109a5a..5299cbaf1a 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -26,7 +26,7 @@ public final class ClangAstWebResource { private static final String RELEASE_ROOT = "https://github.com/specs-feup/clang-dumper/releases/download/"; private static final String RELEASE_TAG_RESOURCE = "clang-dumper-release.tag"; - private static final String MANIFEST_FILENAME = "clang-dumper-release-manifest.json"; + public static final String MANIFEST_FILENAME = "clang-dumper-release-manifest.json"; private static final Gson GSON = new Gson(); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 28662bd010..6ddc090cff 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -27,22 +27,34 @@ import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; +import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.HexFormat; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.Stream; public class ClangResources { private static final Map CLANG_FILES_CACHE = new ConcurrentHashMap<>(); private final static String CLANG_FOLDERNAME = "clang_ast_exe"; + private final static String INCLUDES_FOLDERNAME = "includes"; + private final static String INCLUDES_HASHES_FILENAME = "includes.sha256"; + private final static String LAST_USED_FILENAME = "last-used.txt"; + private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); private static final AtomicInteger HAS_LIBC = new AtomicInteger(-1); @@ -52,13 +64,13 @@ public ClangResources(CodeParser options) { this.options = options; } - public ClangFiles getClangFiles(String version, LibcMode libcMode) { + public ClangFiles getClangFiles(LibcMode libcMode) { - var effectiveVersion = version.isEmpty() ? ClangAstWebResource.getReleaseTag() : version; - var key = libcMode.name() + "_" + effectiveVersion + "_" + getClangResourceFolder().getAbsolutePath(); + var key = libcMode.name() + "_" + getClangResourceFolder().getAbsolutePath(); var files = CLANG_FILES_CACHE.get(key); if (files != null) { + writeLastUsed(Instant.now()); SpecsLogs.debug(() -> "Using cached version of Clang files: " + files); return files; } @@ -67,6 +79,9 @@ public ClangFiles getClangFiles(String version, LibcMode libcMode) { File clangExecutable = prepareResources(manifest); List builtinIncludes = prepareIncludes(manifest, clangExecutable, libcMode); + validateTopLevelCacheFiles(clangExecutable); + updateLastUsedAndCleanupStaleVersions(); + var newFiles = new ClangFiles(clangExecutable, builtinIncludes); SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); @@ -116,7 +131,7 @@ private void unblockWindowsFile(File executable) { } public File getClangResourceFolder() { - return options.get(CodeParser.DUMPER_FOLDER); + return SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), ClangAstWebResource.getReleaseTag()); } public static File getDefaultTempFolder() { @@ -212,14 +227,23 @@ private List prepareIncludes(ClangDumperManifest manifest) { private File prepareIncludesFolder(ClangDumperManifest manifest) { File resourceFolder = getClangResourceFolder(); - ResourceWriteData zipFile = downloadAsset(manifest, "includes", resourceFolder); + var includesAsset = getCurrentAsset(manifest, "includes"); + var extractedFolder = new File(resourceFolder, INCLUDES_FOLDERNAME); + var includesHashesFile = new File(resourceFolder, INCLUDES_HASHES_FILENAME); - var zipFoldername = "include_" + SpecsIo.removeExtension(zipFile.getFile()); - var extractedFolder = SpecsIo.mkdir(resourceFolder, zipFoldername); + if (isIncludesCacheValid(extractedFolder, includesHashesFile, includesAsset)) { + return extractedFolder; + } + + ResourceWriteData zipFile = downloadAsset(manifest, "includes", resourceFolder); - if (zipFile.isNewFile() || SpecsIo.isEmptyFolder(extractedFolder)) { + try { + SpecsIo.mkdir(extractedFolder); SpecsIo.deleteFolderContents(extractedFolder); SpecsIo.extractZip(zipFile.getFile(), extractedFolder); + writeIncludesHashes(extractedFolder, includesHashesFile, includesAsset); + } finally { + SpecsIo.delete(zipFile.getFile()); } return extractedFolder; @@ -239,9 +263,7 @@ private List getIncludeFolders(File extractedFolder) { } private ResourceWriteData downloadAsset(ClangDumperManifest manifest, String kind, File resourceFolder) { - var platform = getManifestPlatform(); - var arch = getManifestArch(platform); - var asset = manifest.getAsset(platform, arch, kind); + var asset = getCurrentAsset(manifest, kind); var resource = ClangAstWebResource.getAssetResource(asset); var writeData = resource.writeVersioned(resourceFolder, ClangResources.class); @@ -260,6 +282,173 @@ private ResourceWriteData downloadAsset(ClangDumperManifest manifest, String kin return writeData; } + private ClangDumperManifestAsset getCurrentAsset(ClangDumperManifest manifest, String kind) { + var platform = getManifestPlatform(); + var arch = getManifestArch(platform); + return manifest.getAsset(platform, arch, kind); + } + + private boolean isIncludesCacheValid(File includesFolder, File includesHashesFile, + ClangDumperManifestAsset includesAsset) { + + if (!includesFolder.isDirectory() || !includesHashesFile.isFile()) { + return false; + } + + var expectedHashes = readIncludesHashes(includesHashesFile, includesAsset); + if (expectedHashes == null) { + return false; + } + + var expectedFiles = expectedHashes.keySet(); + var actualFiles = listRegularFiles(includesFolder); + + if (!actualFiles.equals(expectedFiles)) { + SpecsLogs.info("Cached clang-dumper includes contain missing or extra files, extracting them again."); + return false; + } + + for (var entry : expectedHashes.entrySet()) { + var file = new File(includesFolder, entry.getKey()); + if (!entry.getValue().equalsIgnoreCase(calculateSha256(file))) { + SpecsLogs.info("Cached clang-dumper include file '" + file + + "' does not match the expected checksum, extracting includes again."); + return false; + } + } + + return true; + } + + private static Map readIncludesHashes(File includesHashesFile, + ClangDumperManifestAsset includesAsset) { + + var lines = SpecsIo.read(includesHashesFile).lines().toList(); + if (lines.size() < 2 || !lines.get(0).equals("# clang-dumper-includes " + includesAsset.sha256())) { + return null; + } + + Map hashes = new HashMap<>(); + for (int i = 1; i < lines.size(); i++) { + var line = lines.get(i); + if (line.isBlank()) { + continue; + } + + if (line.length() <= 65 || line.charAt(64) != ' ') { + return null; + } + + var hash = line.substring(0, 64); + var relativePath = line.substring(65); + hashes.put(relativePath, hash); + } + + return hashes; + } + + private static void writeIncludesHashes(File includesFolder, File includesHashesFile, + ClangDumperManifestAsset includesAsset) { + + var actualFiles = listRegularFiles(includesFolder).stream() + .sorted() + .toList(); + + StringBuilder hashes = new StringBuilder(); + hashes.append("# clang-dumper-includes ").append(includesAsset.sha256()).append(System.lineSeparator()); + + for (var relativePath : actualFiles) { + var file = new File(includesFolder, relativePath); + hashes.append(calculateSha256(file)) + .append(' ') + .append(relativePath) + .append(System.lineSeparator()); + } + + SpecsIo.write(includesHashesFile, hashes.toString()); + } + + private static Set listRegularFiles(File folder) { + try (Stream paths = Files.walk(folder.toPath())) { + return paths + .filter(Files::isRegularFile) + .map(folder.toPath()::relativize) + .map(Path::toString) + .map(path -> path.replace(File.separatorChar, '/')) + .collect(Collectors.toCollection(HashSet::new)); + } catch (IOException e) { + throw new UncheckedIOException("Could not list files in folder '" + folder + "'", e); + } + } + + private void validateTopLevelCacheFiles(File clangExecutable) { + File resourceFolder = getClangResourceFolder(); + Set expectedNames = new HashSet<>(); + expectedNames.add(ClangAstWebResource.MANIFEST_FILENAME); + expectedNames.add(clangExecutable.getName()); + expectedNames.add(INCLUDES_FOLDERNAME); + expectedNames.add(INCLUDES_HASHES_FILENAME); + expectedNames.add(LAST_USED_FILENAME); + + var files = resourceFolder.listFiles(); + if (files == null) { + return; + } + + for (var file : files) { + if (expectedNames.contains(file.getName())) { + continue; + } + + SpecsLogs.info("Deleting unexpected file from clang-dumper cache: " + file); + SpecsIo.delete(file); + } + } + + private void updateLastUsedAndCleanupStaleVersions() { + var now = Instant.now(); + File resourceFolder = getClangResourceFolder(); + writeLastUsed(now); + + var staleCleanup = new Thread(() -> deleteStaleVersions(now, resourceFolder), + "clang-dumper-stale-cache-cleanup"); + staleCleanup.setDaemon(true); + staleCleanup.start(); + } + + private void writeLastUsed(Instant timestamp) { + SpecsIo.write(new File(getClangResourceFolder(), LAST_USED_FILENAME), timestamp.toString()); + } + + private void deleteStaleVersions(Instant now, File currentVersionFolder) { + File cacheBaseFolder = options.get(CodeParser.DUMPER_FOLDER); + var versions = cacheBaseFolder.listFiles(File::isDirectory); + if (versions == null) { + return; + } + + for (var versionFolder : versions) { + if (versionFolder.equals(currentVersionFolder)) { + continue; + } + + var lastUsedFile = new File(versionFolder, LAST_USED_FILENAME); + if (!lastUsedFile.isFile()) { + continue; + } + + try { + var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); + if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { + SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); + SpecsIo.deleteFolder(versionFolder); + } + } catch (RuntimeException e) { + SpecsLogs.warn("Could not inspect clang-dumper cache timestamp '" + lastUsedFile + "'", e); + } + } + } + private static boolean hasExpectedSha256(File file, ClangDumperManifestAsset asset) { return asset.sha256().equalsIgnoreCase(calculateSha256(file)); } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java index 5c154cd24e..f7441ed77d 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java @@ -45,7 +45,7 @@ public abstract class CodeParser extends ADataClass { .setLabel("CUDA Path (empty: uses system installed; : uses builtin version)") .setDefaultString(""); public static final DataKey DUMPER_FOLDER = KeyFactory.folder("dumperFolder") - .setLabel("The work folder for the clang-dumper. Clava will look for it in this folder, and if not found, will download it. If not set, a temporary folder will be used.") + .setLabel("The base cache folder for the clang-dumper. Clava stores each dumper release in a versioned subfolder and downloads it if not found. If not set, a temporary folder will be used.") .setDefault(ClangResources::getDefaultTempFolder); /** diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java index 399d916211..cf71e63bc4 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java @@ -104,8 +104,6 @@ public App parse(List inputSources, List compilerOptions, ClavaCon // Standard standard = getStandard(allUserSources.values(), options); // config.getTry(ClavaOptions.STANDARD).ifPresent(standard -> arguments.add(standard.getFlag())); - // Get version for the executable - String version = options.get(ClangAstKeys.CLANGAST_VERSION); // System.out.println("PARALLEL OPTIONS: " + options); // Prepare resources before execution // ClangResources clangResources = new ClangResources(get(SHOW_CLANG_DUMP)); @@ -117,7 +115,7 @@ public App parse(List inputSources, List compilerOptions, ClavaCon ClavaLog.debug(() -> "In Linux, ClangAstDumper is a plugin. LIBC_CXX_MODE is reset to SYSTEM."); } - var clangFiles = clangResources.getClangFiles(version, get(ClangAstKeys.LIBC_CXX_MODE)); + var clangFiles = clangResources.getClangFiles(get(ClangAstKeys.LIBC_CXX_MODE)); // File clangExecutable = clangResources.prepareResources(version); // List builtinIncludes = clangResources.prepareIncludes(clangExecutable, // get(ClangAstKeys.USE_PLATFORM_INCLUDES)); diff --git a/Clava-JS/code/sideEffects.ts b/Clava-JS/code/sideEffects.ts index 59d7bf76ff..2c62d3bf47 100644 --- a/Clava-JS/code/sideEffects.ts +++ b/Clava-JS/code/sideEffects.ts @@ -18,14 +18,14 @@ const datastore = Weaver.getWeaverEngine().getData().get(); datastore.set(CxxWeaverOptions.DISABLE_CLAVA_INFO, true); datastore.set( CodeParser.DUMPER_FOLDER, - new JavaTypes.File(getVersionedCacheDir()) + new JavaTypes.File(getClangDumperCacheDir()) ); /** Code to obtain temporary folder **/ -function getVersionedCacheDir(): string { - // Use name+version to isolate different installed versions - return path.join(getCacheBaseDir(), pkg.name, pkg.version); +function getClangDumperCacheDir(): string { + // The version will be added by the installer to isolate different installed versions + return path.join(getCacheBaseDir(), pkg.name, "clang-dumper"); } function getCacheBaseDir(): string { From 3e24c44ad41f954626a79102be24219c9755e64b Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 7 Jul 2026 03:08:38 +0100 Subject: [PATCH 31/99] Throttle clang dumper include validation --- .../pt/up/fe/specs/clang/ClangResources.java | 86 ++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 6ddc090cff..19003acaf8 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -49,12 +49,15 @@ public class ClangResources { private static final Map CLANG_FILES_CACHE = new ConcurrentHashMap<>(); + private static final Set INCLUDES_DEEP_VALIDATION_CACHE = ConcurrentHashMap.newKeySet(); private final static String CLANG_FOLDERNAME = "clang_ast_exe"; private final static String INCLUDES_FOLDERNAME = "includes"; private final static String INCLUDES_HASHES_FILENAME = "includes.sha256"; + private final static String INCLUDES_VALIDATED_FILENAME = "includes-validated.txt"; private final static String LAST_USED_FILENAME = "last-used.txt"; private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); + private final static Duration INCLUDES_DEEP_VALIDATION_INTERVAL = Duration.ofDays(1); private static final AtomicInteger HAS_LIBC = new AtomicInteger(-1); @@ -230,18 +233,21 @@ private File prepareIncludesFolder(ClangDumperManifest manifest) { var includesAsset = getCurrentAsset(manifest, "includes"); var extractedFolder = new File(resourceFolder, INCLUDES_FOLDERNAME); var includesHashesFile = new File(resourceFolder, INCLUDES_HASHES_FILENAME); + var includesValidatedFile = new File(resourceFolder, INCLUDES_VALIDATED_FILENAME); if (isIncludesCacheValid(extractedFolder, includesHashesFile, includesAsset)) { + validateIncludesCacheInBackground(extractedFolder, includesHashesFile, includesValidatedFile, includesAsset); return extractedFolder; } - ResourceWriteData zipFile = downloadAsset(manifest, "includes", resourceFolder); + ResourceWriteData zipFile = downloadAsset(includesAsset, resourceFolder); try { SpecsIo.mkdir(extractedFolder); SpecsIo.deleteFolderContents(extractedFolder); SpecsIo.extractZip(zipFile.getFile(), extractedFolder); writeIncludesHashes(extractedFolder, includesHashesFile, includesAsset); + writeTimestamp(includesValidatedFile, Instant.now()); } finally { SpecsIo.delete(zipFile.getFile()); } @@ -264,6 +270,10 @@ private List getIncludeFolders(File extractedFolder) { private ResourceWriteData downloadAsset(ClangDumperManifest manifest, String kind, File resourceFolder) { var asset = getCurrentAsset(manifest, kind); + return downloadAsset(asset, resourceFolder); + } + + private ResourceWriteData downloadAsset(ClangDumperManifestAsset asset, File resourceFolder) { var resource = ClangAstWebResource.getAssetResource(asset); var writeData = resource.writeVersioned(resourceFolder, ClangResources.class); @@ -295,6 +305,63 @@ private boolean isIncludesCacheValid(File includesFolder, File includesHashesFil return false; } + if (!hasExpectedIncludesAssetHash(includesHashesFile, includesAsset)) { + return false; + } + + var entrypointsFile = new File(includesFolder, "entrypoints.txt"); + if (!entrypointsFile.isFile()) { + SpecsLogs.info("Cached clang-dumper includes are missing entrypoints, extracting them again."); + return false; + } + + return true; + } + + private void validateIncludesCacheInBackground(File includesFolder, File includesHashesFile, + File includesValidatedFile, + ClangDumperManifestAsset includesAsset) { + + if (!shouldRunIncludesDeepValidation(includesValidatedFile)) { + return; + } + + var validationKey = includesFolder.getAbsolutePath() + "_" + includesAsset.sha256(); + if (!INCLUDES_DEEP_VALIDATION_CACHE.add(validationKey)) { + return; + } + + var deepValidation = new Thread(() -> { + if (isIncludesCacheDeepValid(includesFolder, includesHashesFile, includesAsset)) { + writeTimestamp(includesValidatedFile, Instant.now()); + return; + } + + SpecsLogs.info("Invalidating clang-dumper includes cache metadata: " + includesHashesFile); + SpecsIo.delete(includesHashesFile); + SpecsIo.delete(includesValidatedFile); + }, "clang-dumper-includes-cache-validation"); + + deepValidation.setDaemon(true); + deepValidation.start(); + } + + private static boolean shouldRunIncludesDeepValidation(File includesValidatedFile) { + if (!includesValidatedFile.isFile()) { + return true; + } + + try { + var lastValidated = Instant.parse(SpecsIo.read(includesValidatedFile).trim()); + return lastValidated.isBefore(Instant.now().minus(INCLUDES_DEEP_VALIDATION_INTERVAL)); + } catch (RuntimeException e) { + return true; + } + } + + private boolean isIncludesCacheDeepValid(File includesFolder, File includesHashesFile, + ClangDumperManifestAsset includesAsset) { + var expectedHashes = readIncludesHashes(includesHashesFile, includesAsset); if (expectedHashes == null) { return false; @@ -320,6 +387,16 @@ private boolean isIncludesCacheValid(File includesFolder, File includesHashesFil return true; } + private static boolean hasExpectedIncludesAssetHash(File includesHashesFile, + ClangDumperManifestAsset includesAsset) { + + try (var reader = Files.newBufferedReader(includesHashesFile.toPath())) { + return ("# clang-dumper-includes " + includesAsset.sha256()).equals(reader.readLine()); + } catch (IOException e) { + return false; + } + } + private static Map readIncludesHashes(File includesHashesFile, ClangDumperManifestAsset includesAsset) { @@ -388,6 +465,7 @@ private void validateTopLevelCacheFiles(File clangExecutable) { expectedNames.add(clangExecutable.getName()); expectedNames.add(INCLUDES_FOLDERNAME); expectedNames.add(INCLUDES_HASHES_FILENAME); + expectedNames.add(INCLUDES_VALIDATED_FILENAME); expectedNames.add(LAST_USED_FILENAME); var files = resourceFolder.listFiles(); @@ -417,7 +495,11 @@ private void updateLastUsedAndCleanupStaleVersions() { } private void writeLastUsed(Instant timestamp) { - SpecsIo.write(new File(getClangResourceFolder(), LAST_USED_FILENAME), timestamp.toString()); + writeTimestamp(new File(getClangResourceFolder(), LAST_USED_FILENAME), timestamp); + } + + private static void writeTimestamp(File file, Instant timestamp) { + SpecsIo.write(file, timestamp.toString()); } private void deleteStaleVersions(Instant now, File currentVersionFolder) { From afb2372387a88afac4b7c0e54240cb4e593ae472 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 15 Jul 2026 18:01:17 +0100 Subject: [PATCH 32/99] Restore strict clang dumper integration --- .../pt/up/fe/specs/clang/ClangResources.java | 102 ++++++++++-------- .../fe/specs/clang/dumper/ClangAstParser.java | 7 +- .../specs/clang/parsers/ClavaNodeParser.java | 23 ++-- .../up/fe/specs/clang/parsers/ClavaNodes.java | 46 +------- .../specs/clang/parsers/LanguageParser.java | 1 + .../up/fe/specs/clang/ClangResourcesTest.java | 38 +++++++ .../api/clava/pass/LocalStaticToGlobal.ts | 2 +- .../specs/clava/ast/expr/StringLiteral.java | 2 +- .../specs/clava/ast/extra/data/Language.java | 5 + .../specs/clava/ast/type/ElaboratedType.java | 8 -- .../weaver/cpp/results/TypeTemplate.js.txt | 4 +- 11 files changed, 118 insertions(+), 120 deletions(-) create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 19003acaf8..6a03e64a6a 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -28,8 +28,10 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Duration; @@ -49,13 +51,13 @@ public class ClangResources { private static final Map CLANG_FILES_CACHE = new ConcurrentHashMap<>(); - private static final Set INCLUDES_DEEP_VALIDATION_CACHE = ConcurrentHashMap.newKeySet(); - + private static final Map CLANG_FILES_LOCKS = new ConcurrentHashMap<>(); private final static String CLANG_FOLDERNAME = "clang_ast_exe"; private final static String INCLUDES_FOLDERNAME = "includes"; private final static String INCLUDES_HASHES_FILENAME = "includes.sha256"; private final static String INCLUDES_VALIDATED_FILENAME = "includes-validated.txt"; private final static String LAST_USED_FILENAME = "last-used.txt"; + private final static String CACHE_LOCK_FILENAME = ".cache.lock"; private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); private final static Duration INCLUDES_DEEP_VALIDATION_INTERVAL = Duration.ofDays(1); @@ -69,28 +71,45 @@ public ClangResources(CodeParser options) { public ClangFiles getClangFiles(LibcMode libcMode) { - var key = libcMode.name() + "_" + getClangResourceFolder().getAbsolutePath(); + var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); + var key = libcMode.name() + "_" + useBuiltinCuda + "_" + getClangResourceFolder().getAbsolutePath(); - var files = CLANG_FILES_CACHE.get(key); - if (files != null) { + var cachedFiles = CLANG_FILES_CACHE.get(key); + if (cachedFiles != null) { writeLastUsed(Instant.now()); - SpecsLogs.debug(() -> "Using cached version of Clang files: " + files); - return files; + SpecsLogs.debug(() -> "Using cached version of Clang files: " + cachedFiles); + return cachedFiles; } - var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); - File clangExecutable = prepareResources(manifest); - List builtinIncludes = prepareIncludes(manifest, clangExecutable, libcMode); + var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(key, ignored -> new Object()); + synchronized (jvmLock) { + var files = CLANG_FILES_CACHE.get(key); + if (files != null) { + writeLastUsed(Instant.now()); + return files; + } + + var lockFile = new File(getClangResourceFolder(), CACHE_LOCK_FILENAME); + try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, + StandardOpenOption.WRITE); + var ignored = lockChannel.lock()) { - validateTopLevelCacheFiles(clangExecutable); - updateLastUsedAndCleanupStaleVersions(); + var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); + File clangExecutable = prepareResources(manifest); + List builtinIncludes = prepareIncludes(manifest, clangExecutable, libcMode); - var newFiles = new ClangFiles(clangExecutable, builtinIncludes); - SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); + validateTopLevelCacheFiles(clangExecutable); + updateLastUsedAndCleanupStaleVersions(); - CLANG_FILES_CACHE.put(key, newFiles); + var newFiles = new ClangFiles(clangExecutable, builtinIncludes); + SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); - return newFiles; + CLANG_FILES_CACHE.put(key, newFiles); + return newFiles; + } catch (IOException e) { + throw new UncheckedIOException("Could not lock clang-dumper cache '" + lockFile + "'", e); + } + } } /** @@ -213,7 +232,10 @@ private static ProcessOutputAsString runClangAstDumper(File clangExecutable, Fil } private List prepareIncludes(ClangDumperManifest manifest, File clangExecutable, LibcMode libcMode) { - if (!useBuiltinLibc(clangExecutable, libcMode)) { + var useBuiltinLibc = useBuiltinLibc(clangExecutable, libcMode); + var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); + + if (!useBuiltinLibc && !useBuiltinCuda) { return List.of(); } @@ -235,8 +257,7 @@ private File prepareIncludesFolder(ClangDumperManifest manifest) { var includesHashesFile = new File(resourceFolder, INCLUDES_HASHES_FILENAME); var includesValidatedFile = new File(resourceFolder, INCLUDES_VALIDATED_FILENAME); - if (isIncludesCacheValid(extractedFolder, includesHashesFile, includesAsset)) { - validateIncludesCacheInBackground(extractedFolder, includesHashesFile, includesValidatedFile, includesAsset); + if (isIncludesCacheValid(extractedFolder, includesHashesFile, includesValidatedFile, includesAsset)) { return extractedFolder; } @@ -298,7 +319,7 @@ private ClangDumperManifestAsset getCurrentAsset(ClangDumperManifest manifest, S return manifest.getAsset(platform, arch, kind); } - private boolean isIncludesCacheValid(File includesFolder, File includesHashesFile, + private boolean isIncludesCacheValid(File includesFolder, File includesHashesFile, File includesValidatedFile, ClangDumperManifestAsset includesAsset) { if (!includesFolder.isDirectory() || !includesHashesFile.isFile()) { @@ -315,35 +336,27 @@ private boolean isIncludesCacheValid(File includesFolder, File includesHashesFil return false; } - return true; - } - - private void validateIncludesCacheInBackground(File includesFolder, File includesHashesFile, - File includesValidatedFile, - ClangDumperManifestAsset includesAsset) { - if (!shouldRunIncludesDeepValidation(includesValidatedFile)) { - return; + return true; } - var validationKey = includesFolder.getAbsolutePath() + "_" + includesAsset.sha256(); - if (!INCLUDES_DEEP_VALIDATION_CACHE.add(validationKey)) { - return; + var validationStart = Instant.now(); + SpecsLogs.info("Validating clang-dumper includes cache: " + includesFolder); + if (isIncludesCacheDeepValid(includesFolder, includesHashesFile, includesAsset)) { + writeTimestamp(includesValidatedFile, Instant.now()); + SpecsLogs.info("Validated clang-dumper includes cache in " + + Duration.between(validationStart, Instant.now()).toMillis() + " ms"); + return true; } - var deepValidation = new Thread(() -> { - if (isIncludesCacheDeepValid(includesFolder, includesHashesFile, includesAsset)) { - writeTimestamp(includesValidatedFile, Instant.now()); - return; - } - - SpecsLogs.info("Invalidating clang-dumper includes cache metadata: " + includesHashesFile); + SpecsLogs.info("Invalidating clang-dumper includes cache metadata: " + includesHashesFile); + if (includesHashesFile.isFile()) { SpecsIo.delete(includesHashesFile); + } + if (includesValidatedFile.isFile()) { SpecsIo.delete(includesValidatedFile); - }, "clang-dumper-includes-cache-validation"); - - deepValidation.setDaemon(true); - deepValidation.start(); + } + return false; } private static boolean shouldRunIncludesDeepValidation(File includesValidatedFile) { @@ -467,6 +480,7 @@ private void validateTopLevelCacheFiles(File clangExecutable) { expectedNames.add(INCLUDES_HASHES_FILENAME); expectedNames.add(INCLUDES_VALIDATED_FILENAME); expectedNames.add(LAST_USED_FILENAME); + expectedNames.add(CACHE_LOCK_FILENAME); var files = resourceFolder.listFiles(); if (files == null) { @@ -590,8 +604,4 @@ private static String getManifestArch(String platform) { throw new RuntimeException("Unsupported architecture for clang-dumper: " + osArch); } - public File getBuiltinCudaLib() { - var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); - return prepareIncludesFolder(manifest); - } } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java index 3c3f1044cf..ccfb24385e 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstParser.java @@ -145,13 +145,14 @@ public TranslationUnit parseTu(File sourceFile) { if (ClavaNodes.isNullId(topLevelTypeId)) { continue; } - data.get(ClangAstData.CLAVA_NODES).getOptional(topLevelTypeId); - + ClavaNode parsedNode = data.get(ClangAstData.CLAVA_NODES).get(topLevelTypeId); + Objects.requireNonNull(parsedNode, () -> "No node for type '" + topLevelTypeId + "'"); } // Parse top-level attributes for (String topLevelAttributeId : topLevelAttributes) { - data.get(ClangAstData.CLAVA_NODES).getOptional(topLevelAttributeId); + ClavaNode parsedNode = data.get(ClangAstData.CLAVA_NODES).get(topLevelAttributeId); + Objects.requireNonNull(parsedNode, () -> "No node for attribute '" + topLevelAttributeId + "'"); } // Create TU node diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java index b2d2d9e3c9..19d3bfbcfb 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodeParser.java @@ -108,10 +108,6 @@ public void apply(LineStream lineStream, ClangAstData data) { ClavaNode node = parseNode(nodeId, classname, data, lineStream); - if (node == null) { - return; - } - // If UnsupportedNode, transform to DummyNode // node = transformUnsupportedNode(node); @@ -173,11 +169,8 @@ private ClavaNode parseNode(String nodeId, String classname, ClangAstData data, DataStore nodeData = data.get(ClangAstData.NODE_DATA).get(nodeId); if (nodeData == null) { - if (debug) { - SpecsLogs.msgInfo("No ClavaData/DataStore for node '" + nodeId + "' (classname: " + classname - + "), skipping node (linestream index '" + lineStream.getLastLineIndex() + "')"); - } - return null; + throw new RuntimeException("No ClavaData/DataStore for node '" + nodeId + "' (classname: " + classname + + "), data dumper is not being called (linestream index '" + lineStream.getLastLineIndex() + "')"); } // Get corresponding ClavaNode class @@ -240,14 +233,10 @@ private ClavaNode parseNode(String nodeId, String classname, ClangAstData data, // } // } - if (child == null) { - if (debug) { - int index = i; - SpecsLogs.msgInfo("Did not find ClavaNode for child with index '" + index + "' and id '" - + childId + "' when parsing " + clavaNodeClass.getSimpleName() + ", skipping child"); - } - continue; - } + int index = i; + Objects.requireNonNull(child, + () -> "Did not find ClavaNode for child with index '" + index + "' and id '" + childId + + "' when parsing " + clavaNodeClass.getSimpleName() + " -> " + nodeData); child = processChild(child, clavaNodeClass, data); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java index fb96e68c42..a0cc364dd7 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java @@ -31,10 +31,6 @@ import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaNode; import pt.up.fe.specs.clava.ast.NullNodeType; -import pt.up.fe.specs.clava.ast.decl.Decl; -import pt.up.fe.specs.clava.ast.expr.Expr; -import pt.up.fe.specs.clava.ast.stmt.Stmt; -import pt.up.fe.specs.clava.ast.type.Type; import pt.up.fe.specs.clava.context.ClavaFactory; import pt.up.fe.specs.util.SpecsCheck; import pt.up.fe.specs.util.SpecsSystem; @@ -152,20 +148,10 @@ public void queueSetNode(DataClass data, DataKey key + "', if node can be null, use queueOptional instead. Node data:\n" + data); } + ClavaNode node = Objects.requireNonNull(clavaNodes.get(nodeId), + () -> "Could not resolve required node '" + nodeId + "' for key '" + key.getName() + + "'. Node data:\n" + data); Class valueClass = key.getValueClass(); - ClavaNode node = clavaNodes.get(nodeId); - if (node == null) { - var nullNode = nullNodeFor(valueClass); - if (nullNode != null) { - data.set(key, valueClass.cast(nullNode)); - return; - } - - ClavaLog.debug("Could not find ClavaNode with id '" + nodeId - + "', skipping key '" + key.getName() - + "'. Check if node is being visited. If parsing of includes is enabled, check that the parsing level is sufficient."); - return; - } ClavaNode adaptedNode = adaptNode(node, valueClass); @@ -208,26 +194,6 @@ private ClavaNode adaptNode(ClavaNode node, Class value return node; } - private ClavaNode nullNodeFor(Class valueClass) { - if (valueClass.equals(Type.class)) { - return factory.nullType(); - } - - if (valueClass.equals(Decl.class)) { - return factory.nullDecl(); - } - - if (valueClass.equals(Stmt.class)) { - return factory.nullStmt(); - } - - if (valueClass.equals(Expr.class)) { - return factory.nullExpr(); - } - - return null; - } - public void queueSetOptionalNode(DataClass data, DataKey> key, String nodeId) { @@ -261,11 +227,7 @@ public void queueSetNodeList(DataClass data, DataKey

  • { @SuppressWarnings("unchecked") // If the nodes exist, they should be of the requested type - List nodes = nodeIds.stream() - .map(this::getOptional) - .flatMap(Optional::stream) - .map(node -> (T) node) - .collect(Collectors.toList()); + List nodes = nodeIds.stream().map(id -> (T) get(id)).collect(Collectors.toList()); data.set(key, nodes); }; diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/LanguageParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/LanguageParser.java index e366767bce..3524f18bad 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/LanguageParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/LanguageParser.java @@ -55,6 +55,7 @@ public void apply(LineStream lineStream, ClangAstData data) { .set(Language.C_PLUS_PLUS_17, LineStreamParsers.oneOrZero(lineStream)) .set(Language.C_PLUS_PLUS_20, LineStreamParsers.oneOrZero(lineStream)) .set(Language.C_PLUS_PLUS_23, LineStreamParsers.oneOrZero(lineStream)) + .set(Language.C_PLUS_PLUS_26, LineStreamParsers.oneOrZero(lineStream)) .set(Language.HAS_DIGRAPHS, LineStreamParsers.oneOrZero(lineStream)) .set(Language.IS_GNU, LineStreamParsers.oneOrZero(lineStream)) .set(Language.HEX_FLOATS, LineStreamParsers.oneOrZero(lineStream)) diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java new file mode 100644 index 0000000000..e46824d1f5 --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -0,0 +1,38 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; + +import org.junit.jupiter.api.Test; + +import pt.up.fe.specs.clang.codeparser.CodeParser; + +public class ClangResourcesTest { + + @Test + public void builtinCudaIncludesAreAvailableWithSystemLibc() { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + + var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); + var hasCudaWrapper = clangFiles.builtinIncludes().stream() + .map(folder -> new File(folder, "__clang_cuda_runtime_wrapper.h")) + .anyMatch(File::isFile); + + assertTrue(hasCudaWrapper, "Built-in CUDA must provide Clang's CUDA runtime wrapper independently of libc"); + } +} diff --git a/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts b/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts index 123ec0d47b..205e431aed 100644 --- a/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts +++ b/Clava-JS/api/clava/pass/LocalStaticToGlobal.ts @@ -42,7 +42,7 @@ export default class LocalStaticToGlobal extends SimplePass { } // With static storage - if ($jp.storageClass.toLowerCase() !== StorageClass.STATIC) { + if ($jp.storageClass !== StorageClass.STATIC) { return false; } diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java index fc9b3f5569..9cf85bec42 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java @@ -91,7 +91,7 @@ private String getStringFromBytes() { } // If ASCII, convert each byte directly - if (kind == StringKind.ORDINARY) { + if (kind == StringKind.ORDINARY || kind == StringKind.UNEVALUATED) { var literal = new StringBuilder(); for (var aByte : get(STRING_BYTES)) { diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/extra/data/Language.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/extra/data/Language.java index b0a37b3991..d930ebb620 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/extra/data/Language.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/extra/data/Language.java @@ -70,6 +70,11 @@ public class Language extends ADataClass { */ public static final DataKey C_PLUS_PLUS_23 = KeyFactory.bool("c++23"); + /** + * True if is a C++26 variant (or later). + */ + public static final DataKey C_PLUS_PLUS_26 = KeyFactory.bool("c++26"); + /** * True if supports digraphs. */ diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java index 8f29b6be9e..cb08094295 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/ElaboratedType.java @@ -50,10 +50,6 @@ public ElaboratedType(DataStore data, Collection children) @Override public Type desugar() { - if (!hasValue(NAMED_TYPE)) { - return this; - } - return getNamedType(); } @@ -67,10 +63,6 @@ public void setNamedType(Type namedType) { @Override public String getCode(ClavaNode sourceNode, String name) { - if (!hasValue(NAMED_TYPE) || getNamedType() instanceof NullType) { - return super.getCode(sourceNode, name); - } - String code = getKeyword().getCode(); if (!code.isEmpty()) { code += " "; diff --git a/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt b/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt index fb6efbf514..18167a3213 100644 --- a/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt +++ b/ClavaWeaver/resources/clava/test/weaver/cpp/results/TypeTemplate.js.txt @@ -9,5 +9,5 @@ After setting int with array: int Original template args: int,float After second arg to double: int,double After setting with array [double, int]: double,int -After setting typedef_to_change: std::vector::const_iterator -After setting changed_typedef_type: std::vector::const_iterator +After setting typedef_to_change: std::vector::const_iterator +After setting changed_typedef_type: std::vector::const_iterator From 9018763cf53a266cc10ea72607dedb188aa9b419 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 16 Jul 2026 00:07:49 +0100 Subject: [PATCH 33/99] Support testing local clang-dumper builds Allow parser tests to select a local dumper without disturbing release cache assets. Restore strict resolution of referenced nodes so incomplete dumper output fails at its source. --- .../pt/up/fe/specs/clang/ClangResources.java | 27 +++++++++++++---- .../fe/specs/clang/codeparser/CodeParser.java | 3 ++ .../up/fe/specs/clang/parsers/ClavaNodes.java | 30 +++++++------------ .../clang/parsers/data/DeclDataParser.java | 2 +- .../clang/parsers/data/StmtDataParser.java | 2 +- .../specs/clang/parser/AClangAstTester.java | 4 +++ 6 files changed, 42 insertions(+), 26 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 6a03e64a6a..2b8a38f674 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -72,7 +72,9 @@ public ClangResources(CodeParser options) { public ClangFiles getClangFiles(LibcMode libcMode) { var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); - var key = libcMode.name() + "_" + useBuiltinCuda + "_" + getClangResourceFolder().getAbsolutePath(); + var dumperExecutable = options.get(CodeParser.DUMPER_EXECUTABLE).trim(); + var key = libcMode.name() + "_" + useBuiltinCuda + "_" + dumperExecutable + "_" + + getClangResourceFolder().getAbsolutePath(); var cachedFiles = CLANG_FILES_CACHE.get(key); if (cachedFiles != null) { @@ -95,10 +97,10 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var ignored = lockChannel.lock()) { var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); - File clangExecutable = prepareResources(manifest); + File clangExecutable = getClangExecutable(manifest, dumperExecutable); List builtinIncludes = prepareIncludes(manifest, clangExecutable, libcMode); - validateTopLevelCacheFiles(clangExecutable); + validateTopLevelCacheFiles(manifest); updateLastUsedAndCleanupStaleVersions(); var newFiles = new ClangFiles(clangExecutable, builtinIncludes); @@ -115,6 +117,20 @@ public ClangFiles getClangFiles(LibcMode libcMode) { /** * @return path to the executable that was copied */ + private File getClangExecutable(ClangDumperManifest manifest, String dumperExecutable) { + if (dumperExecutable.isEmpty()) { + return prepareResources(manifest); + } + + var executable = new File(dumperExecutable).getAbsoluteFile(); + if (!executable.isFile()) { + throw new RuntimeException("Could not find custom clang-dumper executable '" + executable + "'"); + } + + SpecsLogs.info("Using custom clang-dumper executable: " + executable); + return executable; + } + private File prepareResources(ClangDumperManifest manifest) { File resourceFolder = getClangResourceFolder(); SupportedPlatform platform = SupportedPlatform.getCurrentPlatform(); @@ -471,11 +487,12 @@ private static Set listRegularFiles(File folder) { } } - private void validateTopLevelCacheFiles(File clangExecutable) { + private void validateTopLevelCacheFiles(ClangDumperManifest manifest) { File resourceFolder = getClangResourceFolder(); Set expectedNames = new HashSet<>(); expectedNames.add(ClangAstWebResource.MANIFEST_FILENAME); - expectedNames.add(clangExecutable.getName()); + var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; + expectedNames.add(getCurrentAsset(manifest, executableKind).filename()); expectedNames.add(INCLUDES_FOLDERNAME); expectedNames.add(INCLUDES_HASHES_FILENAME); expectedNames.add(INCLUDES_VALIDATED_FILENAME); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java index f7441ed77d..0d0bba4ea6 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java @@ -47,6 +47,9 @@ public abstract class CodeParser extends ADataClass { public static final DataKey DUMPER_FOLDER = KeyFactory.folder("dumperFolder") .setLabel("The base cache folder for the clang-dumper. Clava stores each dumper release in a versioned subfolder and downloads it if not found. If not set, a temporary folder will be used.") .setDefault(ClangResources::getDefaultTempFolder); + public static final DataKey DUMPER_EXECUTABLE = KeyFactory.string("dumperExecutable") + .setLabel("Optional local clang-dumper executable. Intended for development and testing; release includes are still prepared normally.") + .setDefaultString(""); /** * Execution information, such as execution time and memory used. diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java index a0cc364dd7..fc58715dd7 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java @@ -199,8 +199,11 @@ public void queueSetOptionalNode(DataClass data, DataKe Runnable nodeToAdd = () -> { + @SuppressWarnings("unchecked") Optional value = isNullId(nodeId) ? Optional.empty() - : key.getValueClass().cast(getOptional(nodeId)); + : Optional.of((T) Objects.requireNonNull(clavaNodes.get(nodeId), + () -> "Could not resolve optional node '" + nodeId + "' for key '" + key.getName() + + "'. Node data:\n" + data)); data.set(key, value); }; @@ -213,7 +216,10 @@ public void queueSetNullableNode(DataClass data, DataKe Runnable nodeToAdd = () -> { - ClavaNode value = isNullId(nodeId) ? getNullNodeType(nodeId).newNullNode(factory) : get(nodeId); + ClavaNode value = isNullId(nodeId) ? getNullNodeType(nodeId).newNullNode(factory) + : Objects.requireNonNull(clavaNodes.get(nodeId), + () -> "Could not resolve nullable node '" + nodeId + "' for key '" + key.getName() + + "'. Node data:\n" + data); data.set(key, key.getValueClass().cast(value)); }; @@ -227,24 +233,10 @@ public void queueSetNodeList(DataClass data, DataKey

  • { @SuppressWarnings("unchecked") // If the nodes exist, they should be of the requested type - List nodes = nodeIds.stream().map(id -> (T) get(id)).collect(Collectors.toList()); - - data.set(key, nodes); - }; - - queuedActions.add(nodeToAdd); - } - - public void queueSetOptionalNodeList(DataClass data, DataKey> key, - List nodeIds) { - - Runnable nodeToAdd = () -> { - - @SuppressWarnings("unchecked") // If present, the nodes should be of the requested type List nodes = nodeIds.stream() - .map(this::getOptional) - .flatMap(Optional::stream) - .map(node -> (T) node) + .map(id -> (T) Objects.requireNonNull(clavaNodes.get(id), + () -> "Could not resolve node '" + id + "' in list for key '" + key.getName() + + "'. Node data:\n" + data)) .collect(Collectors.toList()); data.set(key, nodes); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java index f7f11dc97a..b108271017 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/DeclDataParser.java @@ -87,7 +87,7 @@ public static DataStore parseDeclData(LineStream lines, ClangAstData dataStore) data.add(Decl.IS_INVALID_DECL, LineStreamParsers.oneOrZero(lines)); data.add(Decl.IS_MODULE_PRIVATE, LineStreamParsers.oneOrZero(lines)); - dataStore.getClavaNodes().queueSetOptionalNodeList(data, Decl.ATTRIBUTES, LineStreamParsers.stringList(lines)); + dataStore.getClavaNodes().queueSetNodeList(data, Decl.ATTRIBUTES, LineStreamParsers.stringList(lines)); // dataStore.getClavaNodes().queueSetOptionalNode(data, Decl.DECL_CONTEXT, lines.nextLine()); // dataStore.getClavaNodes().queueSetNodeList(data, Decl.DECL_CONTEXT, LineStreamParsers.stringList(lines)); // data.add(Decl.DECL_CONTEXT_IDS, LineStreamParsers.stringList(lines)); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java index a52881b784..43c2b2efb0 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/StmtDataParser.java @@ -64,7 +64,7 @@ public static DataStore parseGotoStmtData(LineStream lines, ClangAstData dataSto public static DataStore parseAttributedStmtData(LineStream lines, ClangAstData dataStore) { DataStore data = parseStmtData(lines, dataStore); - dataStore.getClavaNodes().queueSetOptionalNodeList(data, AttributedStmt.STMT_ATTRIBUTES, + dataStore.getClavaNodes().queueSetNodeList(data, AttributedStmt.STMT_ATTRIBUTES, LineStreamParsers.stringList(lines)); return data; diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java index eeb1c44ce1..8cc622dca5 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java @@ -92,6 +92,10 @@ public AClangAstTester(Collection resources, List comp this.outputFoldername = OUTPUT_FOLDERNAME_PREFIX + System.nanoTime() + "-" + Thread.currentThread().getId(); codeParser = CodeParser.newInstance(); + var testDumperExecutable = System.getenv("CLAVA_TEST_DUMPER_EXECUTABLE"); + if (testDumperExecutable != null && !testDumperExecutable.isBlank()) { + codeParser.set(CodeParser.DUMPER_EXECUTABLE, testDumperExecutable); + } // Set strict mode // ClangAstParser.strictMode(true); } From 8929424ffbcc090dc98a5d3802de44d8da2c1621 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 16 Jul 2026 02:50:56 +0100 Subject: [PATCH 34/99] Use canonical dumper threshold option Share the system-header threshold spelling between Clava's tool and plugin invocation paths. --- .../src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index d9c8551a49..84d85f24ae 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -53,6 +53,7 @@ public class ClangAstDumper { private final static boolean USE_PLUGIN = false; + private final static String SYSTEM_HEADER_THRESHOLD_OPTION = "-system-header-threshold="; public static boolean usePlugin() { return USE_PLUGIN; @@ -169,7 +170,7 @@ private ClangAstData parsePrivate(File sourceFile, String id, Standard standard, arguments.add("-Xclang"); arguments.add("-plugin-arg-DumpAst"); arguments.add("-Xclang"); - arguments.add("-system-threshold=" + systemIncludesThreshold); + arguments.add(SYSTEM_HEADER_THRESHOLD_OPTION + systemIncludesThreshold); } else { arguments.add(clangExecutable.getAbsolutePath()); @@ -177,7 +178,7 @@ private ClangAstData parsePrivate(File sourceFile, String id, Standard standard, arguments.add("-id=" + id); - arguments.add("-system-header-threshold=" + systemIncludesThreshold); + arguments.add(SYSTEM_HEADER_THRESHOLD_OPTION + systemIncludesThreshold); arguments.add("--"); } From 29e6e8447f92b70e12001f012dc0d8258cfbb191 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 27 Jul 2026 15:27:21 +0100 Subject: [PATCH 35/99] Update character literal kind for LLVM 18 --- .../src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java index ee772ac786..3e4ab11745 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/enums/CharacterKind.java @@ -18,7 +18,7 @@ import pt.up.fe.specs.util.providers.StringProvider; public enum CharacterKind implements StringProvider { - ORDINARY, + ASCII, WIDE, UTF8, UTF16, From 81d4c607607121b7f9a2d9956b1679f7cb9a0661 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 27 Jul 2026 15:54:30 +0100 Subject: [PATCH 36/99] Support local clang-dumper builds from tag file Treat absolute tag-file values as build directories, use their local tool or plugin without release downloads or checksum validation, and force system-provided headers. Remove the superseded executable override and cover source parsing and local tool selection. --- .../fe/specs/clang/ClangAstWebResource.java | 41 +++++++++++++-- .../src/pt/up/fe/specs/clang/ClangFiles.java | 2 +- .../pt/up/fe/specs/clang/ClangResources.java | 51 +++++++++++++------ .../fe/specs/clang/codeparser/CodeParser.java | 3 -- .../clang/codeparser/ParallelCodeParser.java | 7 +-- .../fe/specs/clang/dumper/ClangAstDumper.java | 10 ++-- .../up/fe/specs/clang/ClangResourcesTest.java | 46 +++++++++++++++++ .../specs/clang/parser/AClangAstTester.java | 4 -- 8 files changed, 130 insertions(+), 34 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index 5299cbaf1a..c48f982528 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -18,6 +18,7 @@ import pt.up.fe.specs.util.providers.WebResourceProvider; import java.io.File; +import java.nio.file.Path; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -33,19 +34,42 @@ public final class ClangAstWebResource { private ClangAstWebResource() { } - public static String getReleaseTag() { + public static DumperSource getDumperSource() { var inputStream = ClangAstWebResource.class.getClassLoader().getResourceAsStream(RELEASE_TAG_RESOURCE); if (inputStream == null) { throw new RuntimeException("Could not find resource '" + RELEASE_TAG_RESOURCE + "'"); } - var tag = SpecsIo.read(inputStream).trim(); - if (tag.isBlank()) { + var value = SpecsIo.read(inputStream).trim(); + if (value.isBlank()) { throw new RuntimeException("Resource '" + RELEASE_TAG_RESOURCE + "' is empty"); } - return tag; + return parseDumperSource(value); + } + + static DumperSource parseDumperSource(String value) { + var path = Path.of(value); + if (path.isAbsolute()) { + return new LocalBuild(path.toFile()); + } + + if (value.contains("/") || value.contains("\\") || value.equals(".") || value.equals("..")) { + throw new RuntimeException("Relative paths are not supported in resource '" + RELEASE_TAG_RESOURCE + + "': '" + value + "'"); + } + + return new Release(value); + } + + public static String getReleaseTag() { + var source = getDumperSource(); + if (source instanceof Release release) { + return release.tag(); + } + + throw new IllegalStateException("The clang-dumper resource points to a local build"); } public static ClangDumperManifest getManifest(File resourceFolder) { @@ -69,6 +93,15 @@ private static String getReleaseBaseUrl() { return RELEASE_ROOT + getReleaseTag() + "/"; } + public sealed interface DumperSource permits Release, LocalBuild { + } + + public record Release(String tag) implements DumperSource { + } + + public record LocalBuild(File folder) implements DumperSource { + } + public record ClangDumperManifest(int schema_version, List assets) { public void validate() { diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java index 731cac065f..b11d0f347e 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java @@ -16,6 +16,6 @@ import java.io.File; import java.util.List; -public record ClangFiles(File clangExecutable, List builtinIncludes) { +public record ClangFiles(File clangExecutable, List builtinIncludes, boolean useSystemHeaders) { } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 2b8a38f674..7cd3e84e17 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -15,6 +15,8 @@ import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifest; import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifestAsset; +import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; +import pt.up.fe.specs.clang.ClangAstWebResource.Release; import pt.up.fe.specs.clang.codeparser.CodeParser; import pt.up.fe.specs.clang.dumper.ClangAstDumper; import pt.up.fe.specs.clang.parsers.TopLevelNodesParser; @@ -71,14 +73,18 @@ public ClangResources(CodeParser options) { public ClangFiles getClangFiles(LibcMode libcMode) { + var source = ClangAstWebResource.getDumperSource(); var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); - var dumperExecutable = options.get(CodeParser.DUMPER_EXECUTABLE).trim(); - var key = libcMode.name() + "_" + useBuiltinCuda + "_" + dumperExecutable + "_" - + getClangResourceFolder().getAbsolutePath(); + var sourceKey = source instanceof Release + ? source + "_" + getClangResourceFolder().getAbsolutePath() + : source.toString(); + var key = libcMode.name() + "_" + useBuiltinCuda + "_" + sourceKey; var cachedFiles = CLANG_FILES_CACHE.get(key); if (cachedFiles != null) { - writeLastUsed(Instant.now()); + if (source instanceof Release) { + writeLastUsed(Instant.now()); + } SpecsLogs.debug(() -> "Using cached version of Clang files: " + cachedFiles); return cachedFiles; } @@ -87,23 +93,31 @@ public ClangFiles getClangFiles(LibcMode libcMode) { synchronized (jvmLock) { var files = CLANG_FILES_CACHE.get(key); if (files != null) { - writeLastUsed(Instant.now()); + if (source instanceof Release) { + writeLastUsed(Instant.now()); + } return files; } + if (source instanceof LocalBuild localBuild) { + var newFiles = new ClangFiles(getLocalExecutable(localBuild.folder()), List.of(), true); + CLANG_FILES_CACHE.put(key, newFiles); + return newFiles; + } + var lockFile = new File(getClangResourceFolder(), CACHE_LOCK_FILENAME); try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); var ignored = lockChannel.lock()) { var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); - File clangExecutable = getClangExecutable(manifest, dumperExecutable); + File clangExecutable = prepareResources(manifest); List builtinIncludes = prepareIncludes(manifest, clangExecutable, libcMode); validateTopLevelCacheFiles(manifest); updateLastUsedAndCleanupStaleVersions(); - var newFiles = new ClangFiles(clangExecutable, builtinIncludes); + var newFiles = new ClangFiles(clangExecutable, builtinIncludes, false); SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); CLANG_FILES_CACHE.put(key, newFiles); @@ -114,20 +128,25 @@ public ClangFiles getClangFiles(LibcMode libcMode) { } } - /** - * @return path to the executable that was copied - */ - private File getClangExecutable(ClangDumperManifest manifest, String dumperExecutable) { - if (dumperExecutable.isEmpty()) { - return prepareResources(manifest); + static File getLocalExecutable(File buildFolder) { + if (!buildFolder.isDirectory()) { + throw new RuntimeException("Local clang-dumper build directory does not exist: '" + buildFolder + "'"); + } + + String filename; + if (ClangAstDumper.usePlugin()) { + filename = System.mapLibraryName("plugin"); + } else { + filename = SupportedPlatform.getCurrentPlatform().isWindows() ? "tool.exe" : "tool"; } - var executable = new File(dumperExecutable).getAbsoluteFile(); + var executable = new File(buildFolder, filename); if (!executable.isFile()) { - throw new RuntimeException("Could not find custom clang-dumper executable '" + executable + "'"); + throw new RuntimeException("Could not find local clang-dumper " + + (ClangAstDumper.usePlugin() ? "plugin" : "tool") + " '" + executable + "'"); } - SpecsLogs.info("Using custom clang-dumper executable: " + executable); + SpecsLogs.info("Using local clang-dumper build: " + executable); return executable; } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java index 0d0bba4ea6..f7441ed77d 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java @@ -47,9 +47,6 @@ public abstract class CodeParser extends ADataClass { public static final DataKey DUMPER_FOLDER = KeyFactory.folder("dumperFolder") .setLabel("The base cache folder for the clang-dumper. Clava stores each dumper release in a versioned subfolder and downloads it if not found. If not set, a temporary folder will be used.") .setDefault(ClangResources::getDefaultTempFolder); - public static final DataKey DUMPER_EXECUTABLE = KeyFactory.string("dumperExecutable") - .setLabel("Optional local clang-dumper executable. Intended for development and testing; release includes are still prepared normally.") - .setDefaultString(""); /** * Execution information, such as execution time and memory used. diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java index cf71e63bc4..6f0d7fe238 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java @@ -145,7 +145,8 @@ public App parse(List inputSources, List compilerOptions, ClavaCon Future tUnit = executor .submit(() -> parseSource(source, id, standard, options, clangDump, - counter, parsingFolder, clangFiles.clangExecutable(), clangFiles.builtinIncludes())); + counter, parsingFolder, clangFiles.clangExecutable(), clangFiles.builtinIncludes(), + clangFiles.useSystemHeaders())); futureTUnits.add(tUnit); @@ -362,7 +363,7 @@ private Standard getStandard(Collection sources, DataStore options) { private ClangAstData parseSource(File sourceFile, String id, Standard standard, DataStore options, ConcurrentLinkedQueue clangDump, ParallelProgressCounter counter, File parsingFolder, - File clangExecutable, List builtinIncludes) { + File clangExecutable, List builtinIncludes, boolean useSystemHeaders) { // ConcurrentLinkedQueue clangDump, ConcurrentLinkedQueue workingFolders) { @@ -374,7 +375,7 @@ private ClangAstData parseSource(File sourceFile, String id, Standard standard, boolean streamConsoleOutput = !get(PARALLEL_PARSING); ClangAstDumper clangParser = new ClangAstDumper(streamConsoleOutput, clangExecutable, builtinIncludes, - this) + useSystemHeaders, this) .setBaseFolder(parsingFolder) .setSystemIncludesThreshold(get(SYSTEM_INCLUDES_THRESHOLD)); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 84d85f24ae..26c20087ae 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -87,6 +87,7 @@ public static List getTempFiles() { private File baseFolder; private File clangExecutable; private List builtinIncludes; + private final boolean useSystemHeaders; private int systemIncludesThreshold; private ClangResources clangResources; @@ -103,12 +104,14 @@ public static List getTempFiles() { * @param parserConfig */ public ClangAstDumper(boolean streamConsoleOutput, - File clangExecutable, List builtinIncludes, CodeParser parserConfig) { + File clangExecutable, List builtinIncludes, boolean useSystemHeaders, + CodeParser parserConfig) { this.streamConsoleOutput = streamConsoleOutput; this.clangExecutable = clangExecutable; this.builtinIncludes = builtinIncludes; + this.useSystemHeaders = useSystemHeaders; this.workingFolders = new ArrayList<>(); this.lastWorkingFolder = null; @@ -240,7 +243,7 @@ else if (isCuda) { var cudaPath = parserConfig.get(CodeParser.CUDA_PATH); var useBuiltinCudaLib = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()); - if (useBuiltinCudaLib) { + if (useBuiltinCudaLib && !useSystemHeaders) { arguments.add("-nocudainc"); arguments.add("-nocudalib"); arguments.add("-include"); @@ -268,7 +271,8 @@ else if (SourceType.isHeader(sourceFile)) { } // If it was determined that built-in includes will be used, disable system includes - if (ClangResources.useBuiltinLibc(clangExecutable, config.get(ClangAstKeys.LIBC_CXX_MODE))) { + if (!useSystemHeaders + && ClangResources.useBuiltinLibc(clangExecutable, config.get(ClangAstKeys.LIBC_CXX_MODE))) { arguments.add("-nostdinc"); arguments.add("-nostdinc++"); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index e46824d1f5..4008e33a2d 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -13,16 +13,62 @@ package pt.up.fe.specs.clang; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.io.IOException; +import java.nio.file.Path; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; +import pt.up.fe.specs.clang.ClangAstWebResource.Release; import pt.up.fe.specs.clang.codeparser.CodeParser; public class ClangResourcesTest { + @TempDir + Path tempFolder; + + @Test + public void releaseTagIsParsedAsRelease() { + var release = assertInstanceOf(Release.class, ClangAstWebResource.parseDumperSource("v16.0.5_3")); + + assertEquals("v16.0.5_3", release.tag()); + } + + @Test + public void absolutePathIsParsedAsLocalBuild() { + var localBuild = assertInstanceOf(LocalBuild.class, + ClangAstWebResource.parseDumperSource(tempFolder.toString())); + + assertEquals(tempFolder.toFile(), localBuild.folder()); + } + + @Test + public void relativePathIsRejected() { + assertThrows(RuntimeException.class, + () -> ClangAstWebResource.parseDumperSource("../clang-dumper/build")); + } + + @Test + public void localBuildSelectsExpectedTool() throws IOException { + var toolName = SupportedPlatform.getCurrentPlatform().isWindows() ? "tool.exe" : "tool"; + var tool = tempFolder.resolve(toolName).toFile(); + assertTrue(tool.createNewFile()); + + assertEquals(tool, ClangResources.getLocalExecutable(tempFolder.toFile())); + } + + @Test + public void localBuildRequiresExpectedTool() { + assertThrows(RuntimeException.class, () -> ClangResources.getLocalExecutable(tempFolder.toFile())); + } + @Test public void builtinCudaIncludesAreAvailableWithSystemLibc() { var parser = CodeParser.newInstance(); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java index 8cc622dca5..eeb1c44ce1 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/AClangAstTester.java @@ -92,10 +92,6 @@ public AClangAstTester(Collection resources, List comp this.outputFoldername = OUTPUT_FOLDERNAME_PREFIX + System.nanoTime() + "-" + Thread.currentThread().getId(); codeParser = CodeParser.newInstance(); - var testDumperExecutable = System.getenv("CLAVA_TEST_DUMPER_EXECUTABLE"); - if (testDumperExecutable != null && !testDumperExecutable.isBlank()) { - codeParser.set(CodeParser.DUMPER_EXECUTABLE, testDumperExecutable); - } // Set strict mode // ClangAstParser.strictMode(true); } From 9c57928ffed54364d49d0811f4db5b0b030c1120 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 27 Jul 2026 16:14:38 +0100 Subject: [PATCH 37/99] Allow unresolved unary transform types --- .../up/fe/specs/clang/parsers/data/TypeDataParser.java | 2 +- .../up/fe/specs/clava/ast/type/UnaryTransformType.java | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/TypeDataParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/TypeDataParser.java index 2dbe71624f..48d743ff2b 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/TypeDataParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/TypeDataParser.java @@ -366,7 +366,7 @@ public static DataStore parseUnaryTransformTypeData(LineStream lines, ClangAstDa DataStore data = parseTypeData(lines, parserData); data.add(UnaryTransformType.KIND, LineStreamParsers.enumFromName(UnaryTransformTypeKind.class, lines)); - parserData.getClavaNodes().queueSetNode(data, UnaryTransformType.UNDERLYING_TYPE, lines.nextLine()); + parserData.getClavaNodes().queueSetOptionalNode(data, UnaryTransformType.UNDERLYING_TYPE, lines.nextLine()); parserData.getClavaNodes().queueSetNode(data, UnaryTransformType.BASE_TYPE, lines.nextLine()); return data; diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/UnaryTransformType.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/UnaryTransformType.java index 24c1773693..827d4bf60d 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/type/UnaryTransformType.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/type/UnaryTransformType.java @@ -14,6 +14,7 @@ package pt.up.fe.specs.clava.ast.type; import java.util.Collection; +import java.util.Optional; import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.Datakey.KeyFactory; @@ -35,7 +36,11 @@ public class UnaryTransformType extends Type { public final static DataKey KIND = KeyFactory .enumeration("kind", UnaryTransformTypeKind.class); - public final static DataKey UNDERLYING_TYPE = KeyFactory.object("underlyingType", Type.class); + /** + * The transformed type, when Clang has resolved it. Dependent unary + * transforms can have no underlying type while still having a base type. + */ + public final static DataKey> UNDERLYING_TYPE = KeyFactory.optional("underlyingType"); public final static DataKey BASE_TYPE = KeyFactory.object("baseType", Type.class); @@ -49,7 +54,7 @@ public Type getBaseType() { return get(BASE_TYPE); } - public Type getUnderlyingType() { + public Optional getUnderlyingType() { return get(UNDERLYING_TYPE); } From cdb5c7d39703111b7125fa31f7138f80995da33c Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 27 Jul 2026 17:59:02 +0100 Subject: [PATCH 38/99] Test source locations across macro expansions --- Clava-JS/api/SourceLocations.test.ts | 93 ++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Clava-JS/api/SourceLocations.test.ts diff --git a/Clava-JS/api/SourceLocations.test.ts b/Clava-JS/api/SourceLocations.test.ts new file mode 100644 index 0000000000..ab6ed7463a --- /dev/null +++ b/Clava-JS/api/SourceLocations.test.ts @@ -0,0 +1,93 @@ +import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.ts"; +import Query from "@specs-feup/lara/api/weaver/Query.ts"; +import { Joinpoint, Vardecl } from "./Joinpoints.ts"; + +const code = `#define VALUE 7 +#define CAT_IMPL(left, right) left ## right +#define CAT(left, right) CAT_IMPL(left, right) +#define DECL(name) int name = VALUE; +DECL(CAT(macro_, value)) +int ordinary = 0; +int foobar = 1; +int pasted_reference = CAT(foo, bar); + +namespace std { +using uint8_t = unsigned char; +template class vector; +} + +template > +class Holder {}; +`; + +describe("source locations", () => { + registerSourceCode(code); + + it("uses real file coordinates for ordinary and macro-expanded nodes", () => { + const ordinary = Query.search(Vardecl, "ordinary").first(); + const macro = Query.search(Vardecl, "macro_value").first(); + + expect(ordinary).toBeDefined(); + expect(macro).toBeDefined(); + + if (ordinary === undefined || macro === undefined) { + return; + } + + expect(ordinary.isMacro).toBe(false); + expect(ordinary.filename).toBe("dummyFile.cpp"); + expect(ordinary.filepath).toMatch(/dummyFile\.cpp$/); + expect(ordinary.line).toBe(6); + expect(ordinary.endLine).toBe(6); + expect(ordinary.location).not.toContain(""); + + expect(macro.isMacro).toBe(true); + expect(macro.filename).toBe("dummyFile.cpp"); + expect(macro.filepath).toMatch(/dummyFile\.cpp$/); + expect(macro.line).toBe(5); + expect(macro.endLine).toBe(5); + expect(macro.column).toBe(1); + expect(macro.endColumn).toBe(24); + expect(macro.location).not.toContain(""); + + expect(macro.init.isMacro).toBe(true); + expect(macro.init.filename).toBe("dummyFile.cpp"); + expect(macro.init.line).toBe(5); + expect(macro.init.location).not.toContain(""); + + const pastedReference = Query.search(Vardecl, "pasted_reference").first(); + expect(pastedReference).toBeDefined(); + if (pastedReference === undefined) { + return; + } + + expect(pastedReference.init.isMacro).toBe(true); + expect(pastedReference.init.filename).toBe("dummyFile.cpp"); + expect(pastedReference.init.line).toBe(8); + expect(pastedReference.init.column).toBe(24); + expect(pastedReference.init.endColumn).toBe(36); + expect(pastedReference.init.location).not.toContain(""); + }); + + it("does not treat Clang's split closing angle brackets as macros", () => { + const declarations = Query.search("decl").get() as Joinpoint[]; + const templateParameter = declarations.find( + (joinpoint) => joinpoint.astName === "TemplateTypeParmDecl" + && joinpoint.line === 15 + ); + + expect(templateParameter).toBeDefined(); + if (templateParameter === undefined) { + return; + } + + expect(templateParameter.isMacro).toBe(false); + expect(templateParameter.filename).toBe("dummyFile.cpp"); + expect(templateParameter.filepath).toMatch(/dummyFile\.cpp$/); + expect(templateParameter.line).toBe(15); + expect(templateParameter.endLine).toBe(15); + expect(templateParameter.column).toBe(11); + expect(templateParameter.endColumn).toBe(54); + expect(templateParameter.location).not.toContain(""); + }); +}); From fa9382380f8e3063a0033269cfec3ae3a46d8189 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 27 Jul 2026 18:06:11 +0100 Subject: [PATCH 39/99] Test source locations in ClangAstParser --- .../test-resources/cxx/source_locations.cpp | 17 ++++ .../parser/tests/SourceLocationsTest.java | 88 +++++++++++++++++++ Clava-JS/api/SourceLocations.test.ts | 21 ++--- 3 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 ClangAstParser/test-resources/cxx/source_locations.cpp create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/SourceLocationsTest.java diff --git a/ClangAstParser/test-resources/cxx/source_locations.cpp b/ClangAstParser/test-resources/cxx/source_locations.cpp new file mode 100644 index 0000000000..a80c933e55 --- /dev/null +++ b/ClangAstParser/test-resources/cxx/source_locations.cpp @@ -0,0 +1,17 @@ +#define VALUE 7 +#define CAT_IMPL(left, right) left##right +#define CAT(left, right) CAT_IMPL(left, right) +#define DECL(name) int name = VALUE; + +DECL(CAT(macro_, value)) +int ordinary = 0; +int foobar = 1; +int pasted_reference = CAT(foo, bar); + +namespace std { +using uint8_t = unsigned char; +template class vector; +} // namespace std + +template > +class Holder {}; diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/SourceLocationsTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/SourceLocationsTest.java new file mode 100644 index 0000000000..c9e24c6c84 --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/SourceLocationsTest.java @@ -0,0 +1,88 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang.parser.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import pt.up.fe.specs.clang.codeparser.CodeParser; +import pt.up.fe.specs.clava.ClavaNode; +import pt.up.fe.specs.clava.SourceRange; +import pt.up.fe.specs.clava.ast.decl.TemplateTypeParmDecl; +import pt.up.fe.specs.clava.ast.decl.VarDecl; +import pt.up.fe.specs.clava.ast.extra.App; +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.SpecsSystem; + +public class SourceLocationsTest { + + private static final String RESOURCE = "cxx/source_locations.cpp"; + + @TempDir + Path tempFolder; + + @Test + public void sourceLocationsUseRealFileCoordinates() { + SpecsSystem.programStandardInit(); + + File sourceFile = SpecsIo.resourceCopy(RESOURCE, tempFolder.toFile(), false, true); + App app = CodeParser.newInstance().parse(List.of(sourceFile), List.of("-std=c++11")); + + VarDecl ordinary = find(app.getDescendants(VarDecl.class), varDecl -> varDecl.getDeclName().equals("ordinary")); + assertRange(ordinary, false, 7, 1, 7, 16); + + VarDecl macro = find(app.getDescendants(VarDecl.class), varDecl -> varDecl.getDeclName().equals("macro_value")); + assertRange(macro, true, 6, 1, 6, 24); + assertRange(macro.getInit().orElseThrow(), true, 6, 1, 6, 24); + + VarDecl pastedReference = find(app.getDescendants(VarDecl.class), + varDecl -> varDecl.getDeclName().equals("pasted_reference")); + assertRange(pastedReference.getInit().orElseThrow(), true, 9, 24, 9, 36); + + TemplateTypeParmDecl templateParameter = find(app.getDescendantsAndFields(TemplateTypeParmDecl.class), + parameter -> parameter.getDeclName().equals("BinaryType")); + assertRange(templateParameter, false, 16, 11, 16, 54); + } + + private static T find(List nodes, Predicate predicate) { + return nodes.stream() + .filter(predicate) + .findFirst() + .orElseThrow(); + } + + private static void assertRange(ClavaNode node, boolean isMacro, int startLine, int startColumn, int endLine, + int endColumn) { + + SourceRange location = node.getLocation(); + + assertEquals("source_locations.cpp", location.getFilename()); + assertEquals("source_locations.cpp", Path.of(location.getStartFilepath()).getFileName().toString()); + assertEquals("source_locations.cpp", Path.of(location.getEndFilepath()).getFileName().toString()); + assertEquals(startLine, location.getStartLine()); + assertEquals(startColumn, location.getStartCol()); + assertEquals(endLine, location.getEndLine()); + assertEquals(endColumn, location.getEndCol()); + assertEquals(isMacro, node.get(ClavaNode.IS_MACRO)); + assertFalse(location.toString().contains("")); + } +} diff --git a/Clava-JS/api/SourceLocations.test.ts b/Clava-JS/api/SourceLocations.test.ts index ab6ed7463a..d2d06f95ab 100644 --- a/Clava-JS/api/SourceLocations.test.ts +++ b/Clava-JS/api/SourceLocations.test.ts @@ -6,6 +6,7 @@ const code = `#define VALUE 7 #define CAT_IMPL(left, right) left ## right #define CAT(left, right) CAT_IMPL(left, right) #define DECL(name) int name = VALUE; + DECL(CAT(macro_, value)) int ordinary = 0; int foobar = 1; @@ -14,7 +15,7 @@ int pasted_reference = CAT(foo, bar); namespace std { using uint8_t = unsigned char; template class vector; -} +} // namespace std template > class Holder {}; @@ -37,22 +38,22 @@ describe("source locations", () => { expect(ordinary.isMacro).toBe(false); expect(ordinary.filename).toBe("dummyFile.cpp"); expect(ordinary.filepath).toMatch(/dummyFile\.cpp$/); - expect(ordinary.line).toBe(6); - expect(ordinary.endLine).toBe(6); + expect(ordinary.line).toBe(7); + expect(ordinary.endLine).toBe(7); expect(ordinary.location).not.toContain(""); expect(macro.isMacro).toBe(true); expect(macro.filename).toBe("dummyFile.cpp"); expect(macro.filepath).toMatch(/dummyFile\.cpp$/); - expect(macro.line).toBe(5); - expect(macro.endLine).toBe(5); + expect(macro.line).toBe(6); + expect(macro.endLine).toBe(6); expect(macro.column).toBe(1); expect(macro.endColumn).toBe(24); expect(macro.location).not.toContain(""); expect(macro.init.isMacro).toBe(true); expect(macro.init.filename).toBe("dummyFile.cpp"); - expect(macro.init.line).toBe(5); + expect(macro.init.line).toBe(6); expect(macro.init.location).not.toContain(""); const pastedReference = Query.search(Vardecl, "pasted_reference").first(); @@ -63,7 +64,7 @@ describe("source locations", () => { expect(pastedReference.init.isMacro).toBe(true); expect(pastedReference.init.filename).toBe("dummyFile.cpp"); - expect(pastedReference.init.line).toBe(8); + expect(pastedReference.init.line).toBe(9); expect(pastedReference.init.column).toBe(24); expect(pastedReference.init.endColumn).toBe(36); expect(pastedReference.init.location).not.toContain(""); @@ -73,7 +74,7 @@ describe("source locations", () => { const declarations = Query.search("decl").get() as Joinpoint[]; const templateParameter = declarations.find( (joinpoint) => joinpoint.astName === "TemplateTypeParmDecl" - && joinpoint.line === 15 + && joinpoint.line === 16 ); expect(templateParameter).toBeDefined(); @@ -84,8 +85,8 @@ describe("source locations", () => { expect(templateParameter.isMacro).toBe(false); expect(templateParameter.filename).toBe("dummyFile.cpp"); expect(templateParameter.filepath).toMatch(/dummyFile\.cpp$/); - expect(templateParameter.line).toBe(15); - expect(templateParameter.endLine).toBe(15); + expect(templateParameter.line).toBe(16); + expect(templateParameter.endLine).toBe(16); expect(templateParameter.column).toBe(11); expect(templateParameter.endColumn).toBe(54); expect(templateParameter.location).not.toContain(""); From 4a0b0e8892b5a85a1d719cabd419fd0a60da6e62 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 28 Jul 2026 13:28:26 +0100 Subject: [PATCH 40/99] Prevent races during stale dumper cache cleanup --- .../pt/up/fe/specs/clang/ClangResources.java | 41 +++++++++++++++---- .../up/fe/specs/clang/ClangResourcesTest.java | 29 +++++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 7cd3e84e17..b0e5880e6a 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -31,6 +31,8 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; @@ -552,7 +554,7 @@ private static void writeTimestamp(File file, Instant timestamp) { SpecsIo.write(file, timestamp.toString()); } - private void deleteStaleVersions(Instant now, File currentVersionFolder) { + void deleteStaleVersions(Instant now, File currentVersionFolder) { File cacheBaseFolder = options.get(CodeParser.DUMPER_FOLDER); var versions = cacheBaseFolder.listFiles(File::isDirectory); if (versions == null) { @@ -569,18 +571,41 @@ private void deleteStaleVersions(Instant now, File currentVersionFolder) { continue; } - try { - var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); - if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { - SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); - SpecsIo.deleteFolder(versionFolder); + var lockFile = new File(versionFolder, CACHE_LOCK_FILENAME); + try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, + StandardOpenOption.WRITE)) { + + var lock = tryLock(lockChannel); + if (lock == null) { + SpecsLogs.debug(() -> "Skipping locked clang-dumper cache folder: " + versionFolder); + continue; + } + + try (lock) { + if (!lastUsedFile.isFile()) { + continue; + } + + var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); + if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { + SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); + SpecsIo.deleteFolder(versionFolder); + } } - } catch (RuntimeException e) { - SpecsLogs.warn("Could not inspect clang-dumper cache timestamp '" + lastUsedFile + "'", e); + } catch (IOException | RuntimeException e) { + SpecsLogs.warn("Could not inspect clang-dumper cache folder '" + versionFolder + "'", e); } } } + private static FileLock tryLock(FileChannel lockChannel) throws IOException { + try { + return lockChannel.tryLock(); + } catch (OverlappingFileLockException e) { + return null; + } + } + private static boolean hasExpectedSha256(File file, ClangDumperManifestAsset asset) { return asset.sha256().equalsIgnoreCase(calculateSha256(file)); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 4008e33a2d..597207278e 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -14,13 +14,19 @@ package pt.up.fe.specs.clang; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.time.Duration; +import java.time.Instant; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -69,6 +75,29 @@ public void localBuildRequiresExpectedTool() { assertThrows(RuntimeException.class, () -> ClangResources.getLocalExecutable(tempFolder.toFile())); } + @Test + public void staleCacheCleanupSkipsLockedVersions() throws IOException { + var currentVersion = Files.createDirectory(tempFolder.resolve("current")).toFile(); + var staleVersion = Files.createDirectory(tempFolder.resolve("stale")).toFile(); + Files.writeString(staleVersion.toPath().resolve("last-used.txt"), + Instant.now().minus(Duration.ofDays(61)).toString()); + + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + var resources = new ClangResources(parser); + + var lockFile = staleVersion.toPath().resolve(".cache.lock"); + try (var lockChannel = FileChannel.open(lockFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE); + var ignored = lockChannel.lock()) { + + resources.deleteStaleVersions(Instant.now(), currentVersion); + assertTrue(staleVersion.isDirectory()); + } + + resources.deleteStaleVersions(Instant.now(), currentVersion); + assertFalse(staleVersion.exists()); + } + @Test public void builtinCudaIncludesAreAvailableWithSystemLibc() { var parser = CodeParser.newInstance(); From 5cbd7e2a798805e65a1af780884029bf56a830ad Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 28 Jul 2026 15:20:32 +0100 Subject: [PATCH 41/99] Refactor bundled include handling --- .../src/pt/up/fe/specs/clang/ClangFiles.java | 2 +- .../src/pt/up/fe/specs/clang/ClangResources.java | 4 ++-- .../specs/clang/codeparser/ParallelCodeParser.java | 8 +++----- .../pt/up/fe/specs/clang/dumper/ClangAstDumper.java | 13 +++++-------- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java index b11d0f347e..731cac065f 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java @@ -16,6 +16,6 @@ import java.io.File; import java.util.List; -public record ClangFiles(File clangExecutable, List builtinIncludes, boolean useSystemHeaders) { +public record ClangFiles(File clangExecutable, List builtinIncludes) { } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index b0e5880e6a..33d2781606 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -102,7 +102,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { } if (source instanceof LocalBuild localBuild) { - var newFiles = new ClangFiles(getLocalExecutable(localBuild.folder()), List.of(), true); + var newFiles = new ClangFiles(getLocalExecutable(localBuild.folder()), List.of()); CLANG_FILES_CACHE.put(key, newFiles); return newFiles; } @@ -119,7 +119,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { validateTopLevelCacheFiles(manifest); updateLastUsedAndCleanupStaleVersions(); - var newFiles = new ClangFiles(clangExecutable, builtinIncludes, false); + var newFiles = new ClangFiles(clangExecutable, builtinIncludes); SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); CLANG_FILES_CACHE.put(key, newFiles); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java index 6f0d7fe238..d16c2ce827 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java @@ -145,8 +145,7 @@ public App parse(List inputSources, List compilerOptions, ClavaCon Future tUnit = executor .submit(() -> parseSource(source, id, standard, options, clangDump, - counter, parsingFolder, clangFiles.clangExecutable(), clangFiles.builtinIncludes(), - clangFiles.useSystemHeaders())); + counter, parsingFolder, clangFiles.clangExecutable(), clangFiles.builtinIncludes())); futureTUnits.add(tUnit); @@ -363,7 +362,7 @@ private Standard getStandard(Collection sources, DataStore options) { private ClangAstData parseSource(File sourceFile, String id, Standard standard, DataStore options, ConcurrentLinkedQueue clangDump, ParallelProgressCounter counter, File parsingFolder, - File clangExecutable, List builtinIncludes, boolean useSystemHeaders) { + File clangExecutable, List builtinIncludes) { // ConcurrentLinkedQueue clangDump, ConcurrentLinkedQueue workingFolders) { @@ -374,8 +373,7 @@ private ClangAstData parseSource(File sourceFile, String id, Standard standard, // Only show output of console after parsing is done, when using parallel parsing boolean streamConsoleOutput = !get(PARALLEL_PARSING); - ClangAstDumper clangParser = new ClangAstDumper(streamConsoleOutput, clangExecutable, builtinIncludes, - useSystemHeaders, this) + ClangAstDumper clangParser = new ClangAstDumper(streamConsoleOutput, clangExecutable, builtinIncludes, this) .setBaseFolder(parsingFolder) .setSystemIncludesThreshold(get(SYSTEM_INCLUDES_THRESHOLD)); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 26c20087ae..c5597eb3fe 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -87,7 +87,6 @@ public static List getTempFiles() { private File baseFolder; private File clangExecutable; private List builtinIncludes; - private final boolean useSystemHeaders; private int systemIncludesThreshold; private ClangResources clangResources; @@ -104,14 +103,12 @@ public static List getTempFiles() { * @param parserConfig */ public ClangAstDumper(boolean streamConsoleOutput, - File clangExecutable, List builtinIncludes, boolean useSystemHeaders, - CodeParser parserConfig) { + File clangExecutable, List builtinIncludes, CodeParser parserConfig) { this.streamConsoleOutput = streamConsoleOutput; this.clangExecutable = clangExecutable; this.builtinIncludes = builtinIncludes; - this.useSystemHeaders = useSystemHeaders; this.workingFolders = new ArrayList<>(); this.lastWorkingFolder = null; @@ -243,14 +240,14 @@ else if (isCuda) { var cudaPath = parserConfig.get(CodeParser.CUDA_PATH); var useBuiltinCudaLib = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()); - if (useBuiltinCudaLib && !useSystemHeaders) { + if (useBuiltinCudaLib && !builtinIncludes.isEmpty()) { arguments.add("-nocudainc"); arguments.add("-nocudalib"); arguments.add("-include"); arguments.add("__clang_cuda_runtime_wrapper.h"); arguments.add("-include"); arguments.add(ClangAstResource.CUDA_COMPATIBILITY.write(SpecsIo.getTempFolder()).getAbsolutePath()); - } else if (!cudaPath.isBlank()) { + } else if (!useBuiltinCudaLib && !cudaPath.isBlank()) { File cudaFolder = SpecsIo.existingFolder(cudaPath); @@ -270,8 +267,8 @@ else if (SourceType.isHeader(sourceFile)) { arguments.add(standard.isCxx() ? "c++" : "c"); } - // If it was determined that built-in includes will be used, disable system includes - if (!useSystemHeaders + // Bundled include directories replace system headers when built-in libc/cxx is required. + if (!builtinIncludes.isEmpty() && ClangResources.useBuiltinLibc(clangExecutable, config.get(ClangAstKeys.LIBC_CXX_MODE))) { arguments.add("-nostdinc"); arguments.add("-nostdinc++"); From e9bf87f400ba702295203fafcf18e6bdfdd7358c Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 28 Jul 2026 18:52:32 +0100 Subject: [PATCH 42/99] Regenerate Clava-JS enum interfaces --- Clava-JS/api/Joinpoints.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Clava-JS/api/Joinpoints.ts b/Clava-JS/api/Joinpoints.ts index 659971877f..98997a143d 100644 --- a/Clava-JS/api/Joinpoints.ts +++ b/Clava-JS/api/Joinpoints.ts @@ -2811,12 +2811,12 @@ export class TypedefType extends Type { * This and the "type" declaration below. */ export const StorageClass = { - NONE: "none", - AUTO: "auto", - EXTERN: "extern", - PRIVATE_EXTERN: "private_extern", - REGISTER: "register", - STATIC: "static", + NONE: "NONE", + AUTO: "AUTO", + EXTERN: "EXTERN", + PRIVATE_EXTERN: "PRIVATE_EXTERN", + REGISTER: "REGISTER", + STATIC: "STATIC", } as const; export type StorageClass = typeof StorageClass[keyof typeof StorageClass]; @@ -2826,12 +2826,12 @@ export type StorageClass = typeof StorageClass[keyof typeof StorageClass]; * This and the "type" declaration below. */ export const Relation = { - LE: "le", - LT: "lt", - GE: "ge", - GT: "gt", - EQ: "eq", - NE: "ne", + LE: "LE", + LT: "LT", + GE: "GE", + GT: "GT", + EQ: "EQ", + NE: "NE", } as const; export type Relation = typeof Relation[keyof typeof Relation]; From bec96eae53679fc1f9ab4d23fd8a0802dec79b20 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 17:13:01 +0100 Subject: [PATCH 43/99] Update the dumper tag --- ClangAstParser/clang-dumper-release.tag | 2 +- .../ast/decl/enums/InitializationStyle.java | 6 ++-- .../specs/clava/ast/decl/enums/Linkage.java | 32 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/ClangAstParser/clang-dumper-release.tag b/ClangAstParser/clang-dumper-release.tag index 0e02573bb7..9acbd12ca8 100644 --- a/ClangAstParser/clang-dumper-release.tag +++ b/ClangAstParser/clang-dumper-release.tag @@ -1 +1 @@ -v16.0.5_3 +v18.1.8_1 diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java index cae31033f3..90f2f724a7 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java @@ -24,9 +24,9 @@ import pt.up.fe.specs.util.providers.StringProvider; public enum InitializationStyle implements StringProvider { - CINIT, - CALL_INIT, - LIST_INIT, + CINIT, // C-style initialization with assignment + CALL_INIT, // Call-style initialization (C++98) + LIST_INIT, // Direct list-initialization (C++11) ParenListInit; private static Lazy> ENUM_HELPER = EnumHelperWithValue diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java index 1aa999c0fd..f8c27a9319 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/Linkage.java @@ -23,10 +23,42 @@ */ public enum Linkage { Invalid, + + /** + * The entity is unique and can only be referred to from within its scope. + */ None, + + /** + * the entity can be referred to from within the translation unit, but not other translation units. + */ Internal, + + /** + * External linkage within a unique namespace. + * + *

    + * From the language perspective, these entities have external linkage. However, since they reside in an anonymous + * namespace, their names are unique to this translation unit, which is equivalent to having internal linkage from + * the code-generation point of view. + */ UniqueExternal, + + /** + * No linkage according to the standard, but is visible from other translation units because of types defined in a + * inline function. + */ VisibleNone, + + /** + * Module linkage, which indicates that the entity can be referred to from other translation units within the same + * module, and indirectly from arbitrary other translation units through inline functions and templates in the + * module interface. + */ Module, + + /** + * The entity can be referred to from other translation units. + */ External; } From c2c0840af0dc21d53f34fdaa08b6244a029f2fb6 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 22:30:34 +0100 Subject: [PATCH 44/99] Fix C++20 parenthesized list initialization Add support for Clang's CXXParenListInitExpr node and preserve the original parenthesized argument list during code generation. Add an integration test covering aggregate initialization under C++20. --- .../cxx/paren_list_initialization.cpp | 8 +++ .../parser/tests/InitializationStyleTest.java | 50 +++++++++++++++++++ .../ast/decl/enums/InitializationStyle.java | 13 ++++- .../clava/ast/expr/CXXParenListInitExpr.java | 30 +++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 ClangAstParser/test-resources/cxx/paren_list_initialization.cpp create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java create mode 100644 ClavaAst/src/pt/up/fe/specs/clava/ast/expr/CXXParenListInitExpr.java diff --git a/ClangAstParser/test-resources/cxx/paren_list_initialization.cpp b/ClangAstParser/test-resources/cxx/paren_list_initialization.cpp new file mode 100644 index 0000000000..8b84bb9cfb --- /dev/null +++ b/ClangAstParser/test-resources/cxx/paren_list_initialization.cpp @@ -0,0 +1,8 @@ +struct Point { + int x; + int y; +}; + +void test() { + Point point(1, 2); +} diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java new file mode 100644 index 0000000000..2860f16b17 --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java @@ -0,0 +1,50 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang.parser.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import pt.up.fe.specs.clang.codeparser.CodeParser; +import pt.up.fe.specs.clava.ast.decl.VarDecl; +import pt.up.fe.specs.clava.ast.extra.App; +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.SpecsSystem; + +public class InitializationStyleTest { + + @TempDir + Path tempFolder; + + @Test + public void parenthesizedListInitializationKeepsAllArguments() { + SpecsSystem.programStandardInit(); + + File sourceFile = SpecsIo.resourceCopy("cxx/paren_list_initialization.cpp", tempFolder.toFile(), false, true); + App app = CodeParser.newInstance().parse(List.of(sourceFile), List.of("-std=c++2a")); + + VarDecl point = app.getDescendants(VarDecl.class).stream() + .filter(varDecl -> varDecl.getDeclName().equals("point")) + .findFirst() + .orElseThrow(); + + assertEquals("Point point(1, 2)", point.getCode()); + } +} diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java index 90f2f724a7..30cf723e20 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java @@ -57,8 +57,9 @@ public String getCode(VarDecl node) { case CINIT: return cinitCode(node); case CALL_INIT: - case ParenListInit: return callInitCode(node); + case ParenListInit: + return parenListInitCode(node); case LIST_INIT: return listInitCode(node); default: @@ -97,6 +98,16 @@ private String callInitCode(VarDecl node) { return "(" + init.getCode() + ")"; } + private String parenListInitCode(VarDecl node) { + Preconditions.checkArgument(node.getNumChildren() == 1, "Expected one child"); + ClavaNode init = node.getChild(0); + + Preconditions.checkArgument(init instanceof Expr, + "Expected an Expr, got '" + init.getClass().getSimpleName() + "'"); + + return init.getCode(); + } + private String listInitCode(VarDecl node) { // Must be present Expr initList = node.getInit().get(); diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/CXXParenListInitExpr.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/CXXParenListInitExpr.java new file mode 100644 index 0000000000..92f93c195e --- /dev/null +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/CXXParenListInitExpr.java @@ -0,0 +1,30 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clava.ast.expr; + +import java.util.Collection; + +import org.suikasoft.jOptions.Interfaces.DataStore; + +import pt.up.fe.specs.clava.ClavaNode; + +/** + * A C++20 parenthesized list-initialization expression. + */ +public class CXXParenListInitExpr extends ParenListExpr { + + public CXXParenListInitExpr(DataStore data, Collection children) { + super(data, children); + } +} From 197546375fab4f606277f42220cde0b56fcb0dec Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 22:31:27 +0100 Subject: [PATCH 45/99] Synchronize clang cache initialization locks Key the in-process lock by the shared release cache lock file so concurrent libc and CUDA configurations cannot acquire different JVM locks for the same cache folder. --- .../src/pt/up/fe/specs/clang/ClangResources.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 33d2781606..deb873f1c3 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -91,7 +91,13 @@ public ClangFiles getClangFiles(LibcMode libcMode) { return cachedFiles; } - var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(key, ignored -> new Object()); + // The JVM lock must protect the same cache file as the inter-process lock. The cache key also contains the + // libc and CUDA configuration, so using it here would let two configurations acquire different JVM locks for + // the same release folder and trigger OverlappingFileLockException. + var jvmLockKey = source instanceof Release + ? new File(getClangResourceFolder(), CACHE_LOCK_FILENAME).getAbsolutePath() + : source.toString(); + var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(jvmLockKey, ignored -> new Object()); synchronized (jvmLock) { var files = CLANG_FILES_CACHE.get(key); if (files != null) { From e203ed076f36b8442037782c13226eb1d8ea010c Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 22:33:13 +0100 Subject: [PATCH 46/99] Make stale cache cleanup portable Store per-version cache locks beside the version directory so cleanup can delete the directory while holding the lock on Windows, Linux, and macOS. Update the cache cleanup test to use the shared lock-path helper. --- .../pt/up/fe/specs/clang/ClangResources.java | 18 ++++++++++++++---- .../up/fe/specs/clang/ClangResourcesTest.java | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index deb873f1c3..9c21d4b540 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -95,7 +95,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { // libc and CUDA configuration, so using it here would let two configurations acquire different JVM locks for // the same release folder and trigger OverlappingFileLockException. var jvmLockKey = source instanceof Release - ? new File(getClangResourceFolder(), CACHE_LOCK_FILENAME).getAbsolutePath() + ? getCacheLockFile(getClangResourceFolder()).getAbsolutePath() : source.toString(); var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(jvmLockKey, ignored -> new Object()); synchronized (jvmLock) { @@ -113,7 +113,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { return newFiles; } - var lockFile = new File(getClangResourceFolder(), CACHE_LOCK_FILENAME); + var lockFile = getCacheLockFile(getClangResourceFolder()); try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); var ignored = lockChannel.lock()) { @@ -524,7 +524,6 @@ private void validateTopLevelCacheFiles(ClangDumperManifest manifest) { expectedNames.add(INCLUDES_HASHES_FILENAME); expectedNames.add(INCLUDES_VALIDATED_FILENAME); expectedNames.add(LAST_USED_FILENAME); - expectedNames.add(CACHE_LOCK_FILENAME); var files = resourceFolder.listFiles(); if (files == null) { @@ -577,7 +576,7 @@ void deleteStaleVersions(Instant now, File currentVersionFolder) { continue; } - var lockFile = new File(versionFolder, CACHE_LOCK_FILENAME); + var lockFile = getCacheLockFile(versionFolder); try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { @@ -612,6 +611,17 @@ private static FileLock tryLock(FileChannel lockChannel) throws IOException { } } + /** + * Returns the lock file for a cache version. + * + *

    The lock file must be outside the version folder because stale cleanup deletes that folder while holding the + * lock. Keeping the lock inside the folder prevents deletion on Windows.

    + */ + static File getCacheLockFile(File versionFolder) { + var absoluteVersionFolder = versionFolder.getAbsoluteFile(); + return new File(absoluteVersionFolder.getParentFile(), absoluteVersionFolder.getName() + CACHE_LOCK_FILENAME); + } + private static boolean hasExpectedSha256(File file, ClangDumperManifestAsset asset) { return asset.sha256().equalsIgnoreCase(calculateSha256(file)); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 597207278e..59459a2965 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -86,8 +86,8 @@ public void staleCacheCleanupSkipsLockedVersions() throws IOException { parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); var resources = new ClangResources(parser); - var lockFile = staleVersion.toPath().resolve(".cache.lock"); - try (var lockChannel = FileChannel.open(lockFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE); + var lockFile = ClangResources.getCacheLockFile(staleVersion); + try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); var ignored = lockChannel.lock()) { resources.deleteStaleVersions(Instant.now(), currentVersion); From 048e7976072ca16c72d19f8839c6a4336a4572fd Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 22:43:21 +0100 Subject: [PATCH 47/99] Support CUDA discovery for local clang-dumper builds Discover a system CUDA toolkit from portable environment, PATH, and platform-specific locations when bundled headers are unavailable. Continue with a single warning when no toolkit is found instead of rejecting local builds. --- .../pt/up/fe/specs/clang/ClangResources.java | 72 +++++++++++++++++++ .../fe/specs/clang/dumper/ClangAstDumper.java | 13 ++++ .../up/fe/specs/clang/ClangResourcesTest.java | 23 ++++++ 3 files changed, 108 insertions(+) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 9c21d4b540..adbbf93f47 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -40,17 +40,21 @@ import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.HexFormat; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.regex.Pattern; public class ClangResources { @@ -73,6 +77,74 @@ public ClangResources(CodeParser options) { this.options = options; } + public Optional getSystemCudaInstallation() { + return findCudaInstallation( + Arrays.asList(System.getenv("CUDA_HOME"), System.getenv("CUDA_PATH"), System.getenv("CUDA_ROOT")), + System.getenv("PATH"), getConventionalCudaRoots()); + } + + static Optional findCudaInstallation(List configuredRoots, String pathValue, + List conventionalRoots) { + var candidates = new ArrayList(); + + configuredRoots.stream() + .filter(root -> root != null && !root.isBlank()) + .map(File::new) + .forEach(candidates::add); + + if (pathValue != null) { + for (var pathEntry : pathValue.split(Pattern.quote(File.pathSeparator))) { + if (pathEntry.isBlank()) { + continue; + } + + var binFolder = new File(pathEntry); + for (var executableName : List.of("nvcc", "nvcc.exe")) { + var executable = new File(binFolder, executableName); + if (executable.isFile() && binFolder.getParentFile() != null) { + candidates.add(binFolder.getParentFile()); + } + } + } + } + + candidates.addAll(conventionalRoots); + + return candidates.stream() + .map(File::getAbsoluteFile) + .filter(ClangResources::isCudaInstallation) + .findFirst(); + } + + private static boolean isCudaInstallation(File folder) { + return folder.isDirectory() && new File(folder, "include/cuda_runtime.h").isFile(); + } + + private static List getConventionalCudaRoots() { + if (!SupportedPlatform.getCurrentPlatform().isWindows()) { + return List.of( + new File("/usr/local/cuda"), + new File("/opt/cuda"), + new File("/opt/homebrew/opt/cuda")); + } + + var roots = new ArrayList(); + for (var programFiles : Arrays.asList(System.getenv("ProgramFiles"), System.getenv("ProgramFiles(x86)"))) { + if (programFiles == null || programFiles.isBlank()) { + continue; + } + + var cudaRoot = new File(programFiles, "NVIDIA GPU Computing Toolkit/CUDA"); + var versions = cudaRoot.listFiles(File::isDirectory); + if (versions != null) { + Arrays.sort(versions, Comparator.comparing(File::getName).reversed()); + roots.addAll(Arrays.asList(versions)); + } + } + + return roots; + } + public ClangFiles getClangFiles(LibcMode libcMode) { var source = ClangAstWebResource.getDumperSource(); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index c5597eb3fe..f1a9fbbadc 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -44,6 +44,7 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; /** * Calls the ClangAstDumper executable and returns the dumped information. Clava AST can be built based on this output. @@ -61,6 +62,7 @@ public static boolean usePlugin() { private final static String CLANG_DUMP_FILENAME = "clangDump.txt"; private final static String STDERR_DUMP_FILENAME = "stderr.txt"; + private static final AtomicBoolean CUDA_UNAVAILABLE_WARNING_SHOWN = new AtomicBoolean(); private static final List CLANG_AST_DUMPER_TEMP_FILES = Arrays.asList("includes.txt", CLANG_DUMP_FILENAME, // "clavaDump.txt", "nodetypes.txt", "types.txt", "is_temporary.txt", "template_args.txt", @@ -247,6 +249,17 @@ else if (isCuda) { arguments.add("__clang_cuda_runtime_wrapper.h"); arguments.add("-include"); arguments.add(ClangAstResource.CUDA_COMPATIBILITY.write(SpecsIo.getTempFolder()).getAbsolutePath()); + } else if (useBuiltinCudaLib) { + clangResources.getSystemCudaInstallation().ifPresentOrElse(cudaFolder -> { + ClavaLog.debug("Setting --cuda-path to discovered CUDA folder '" + + cudaFolder.getAbsolutePath() + "'"); + arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); + }, () -> { + if (CUDA_UNAVAILABLE_WARNING_SHOWN.compareAndSet(false, true)) { + ClavaLog.warning("CUDA parsing requested, but no system CUDA toolkit was found. " + + "Continuing without CUDA headers; CUDA parsing may fail."); + } + }); } else if (!useBuiltinCudaLib && !cudaPath.isBlank()) { File cudaFolder = SpecsIo.existingFolder(cudaPath); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 59459a2965..7f27ac5af9 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -27,6 +27,7 @@ import java.nio.file.StandardOpenOption; import java.time.Duration; import java.time.Instant; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -75,6 +76,28 @@ public void localBuildRequiresExpectedTool() { assertThrows(RuntimeException.class, () -> ClangResources.getLocalExecutable(tempFolder.toFile())); } + @Test + public void cudaInstallationIsFoundFromConfiguredRoot() throws IOException { + var cudaFolder = Files.createDirectories(tempFolder.resolve("cuda")); + Files.createDirectories(cudaFolder.resolve("include")); + Files.createFile(cudaFolder.resolve("include/cuda_runtime.h")); + + assertEquals(cudaFolder.toFile().getAbsoluteFile(), + ClangResources.findCudaInstallation(List.of(cudaFolder.toString()), "", List.of()).orElseThrow()); + } + + @Test + public void cudaInstallationIsFoundFromNvccPath() throws IOException { + var cudaFolder = Files.createDirectories(tempFolder.resolve("cuda")); + var binFolder = Files.createDirectories(cudaFolder.resolve("bin")); + Files.createDirectories(cudaFolder.resolve("include")); + Files.createFile(cudaFolder.resolve("include/cuda_runtime.h")); + Files.createFile(binFolder.resolve("nvcc")); + + assertEquals(cudaFolder.toFile().getAbsoluteFile(), + ClangResources.findCudaInstallation(List.of(), binFolder.toString(), List.of()).orElseThrow()); + } + @Test public void staleCacheCleanupSkipsLockedVersions() throws IOException { var currentVersion = Files.createDirectory(tempFolder.resolve("current")).toFile(); From 687708759561a039bb0db24415035c309534657e Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 22:57:25 +0100 Subject: [PATCH 48/99] Remove persistent clang cache lock artifacts Coordinate cache initialization and stale cleanup with a temporary directory claim that is removed on release. Keep the claim outside version folders for Windows deletion and recover claims left by terminated processes. --- .../pt/up/fe/specs/clang/ClangResources.java | 188 ++++++++++++++---- .../up/fe/specs/clang/ClangResourcesTest.java | 37 +++- 2 files changed, 184 insertions(+), 41 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index adbbf93f47..7cbe0e673f 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -30,11 +30,11 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; +import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -50,6 +50,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -65,7 +66,10 @@ public class ClangResources { private final static String INCLUDES_HASHES_FILENAME = "includes.sha256"; private final static String INCLUDES_VALIDATED_FILENAME = "includes-validated.txt"; private final static String LAST_USED_FILENAME = "last-used.txt"; - private final static String CACHE_LOCK_FILENAME = ".cache.lock"; + private final static String CACHE_LOCK_FOLDERNAME = ".cache.lock"; + private final static String CACHE_LOCK_OWNER_FILENAME = "owner"; + private final static Duration CACHE_LOCK_RETRY_INTERVAL = Duration.ofMillis(100); + private final static Duration CACHE_LOCK_STALE_MAX_AGE = Duration.ofMinutes(5); private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); private final static Duration INCLUDES_DEEP_VALIDATION_INTERVAL = Duration.ofDays(1); @@ -163,11 +167,11 @@ public ClangFiles getClangFiles(LibcMode libcMode) { return cachedFiles; } - // The JVM lock must protect the same cache file as the inter-process lock. The cache key also contains the + // The JVM lock must protect the same cache folder as the inter-process lock. The cache key also contains the // libc and CUDA configuration, so using it here would let two configurations acquire different JVM locks for - // the same release folder and trigger OverlappingFileLockException. + // the same release folder and race while initializing it. var jvmLockKey = source instanceof Release - ? getCacheLockFile(getClangResourceFolder()).getAbsolutePath() + ? getCacheLockFolder(getClangResourceFolder()).getAbsolutePath() : source.toString(); var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(jvmLockKey, ignored -> new Object()); synchronized (jvmLock) { @@ -185,10 +189,8 @@ public ClangFiles getClangFiles(LibcMode libcMode) { return newFiles; } - var lockFile = getCacheLockFile(getClangResourceFolder()); - try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, - StandardOpenOption.WRITE); - var ignored = lockChannel.lock()) { + var lockFolder = getCacheLockFolder(getClangResourceFolder()); + try (var ignored = acquireCacheLock(getClangResourceFolder())) { var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); File clangExecutable = prepareResources(manifest); @@ -203,7 +205,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { CLANG_FILES_CACHE.put(key, newFiles); return newFiles; } catch (IOException e) { - throw new UncheckedIOException("Could not lock clang-dumper cache '" + lockFile + "'", e); + throw new UncheckedIOException("Could not lock clang-dumper cache '" + lockFolder + "'", e); } } } @@ -648,26 +650,20 @@ void deleteStaleVersions(Instant now, File currentVersionFolder) { continue; } - var lockFile = getCacheLockFile(versionFolder); - try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, - StandardOpenOption.WRITE)) { - - var lock = tryLock(lockChannel); + try (var lock = tryAcquireCacheLock(versionFolder)) { if (lock == null) { SpecsLogs.debug(() -> "Skipping locked clang-dumper cache folder: " + versionFolder); continue; } - try (lock) { - if (!lastUsedFile.isFile()) { - continue; - } + if (!lastUsedFile.isFile()) { + continue; + } - var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); - if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { - SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); - SpecsIo.deleteFolder(versionFolder); - } + var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); + if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { + SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); + SpecsIo.deleteFolder(versionFolder); } } catch (IOException | RuntimeException e) { SpecsLogs.warn("Could not inspect clang-dumper cache folder '" + versionFolder + "'", e); @@ -675,23 +671,143 @@ void deleteStaleVersions(Instant now, File currentVersionFolder) { } } - private static FileLock tryLock(FileChannel lockChannel) throws IOException { - try { - return lockChannel.tryLock(); - } catch (OverlappingFileLockException e) { - return null; + static CacheLock acquireCacheLock(File versionFolder) throws IOException { + return acquireCacheLock(versionFolder, true); + } + + private static CacheLock tryAcquireCacheLock(File versionFolder) throws IOException { + return acquireCacheLock(versionFolder, false); + } + + private static CacheLock acquireCacheLock(File versionFolder, boolean wait) throws IOException { + var lockFolder = getCacheLockFolder(versionFolder); + Files.createDirectories(lockFolder.getParentFile().toPath()); + + while (true) { + try { + Files.createDirectory(lockFolder.toPath()); + } catch (FileAlreadyExistsException e) { + if (!isCacheLockStale(lockFolder)) { + if (!wait) { + return null; + } + + waitForCacheLock(); + continue; + } + + deleteCacheLock(lockFolder); + continue; + } + + var ownerFile = new File(lockFolder, CACHE_LOCK_OWNER_FILENAME); + try { + Files.writeString(ownerFile.toPath(), getProcessIdentity(), StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE); + } catch (FileAlreadyExistsException e) { + // A stale-lock recovery raced with another process that claimed this directory. + continue; + } + + return new CacheLock(lockFolder); } } /** - * Returns the lock file for a cache version. + * Returns the temporary lock folder for a cache version. * - *

    The lock file must be outside the version folder because stale cleanup deletes that folder while holding the - * lock. Keeping the lock inside the folder prevents deletion on Windows.

    + *

    The lock folder is outside the version folder because stale cleanup deletes that folder while holding the + * lock. The folder is removed when the lock is released, so normal operation leaves no lock artifact behind.

    */ - static File getCacheLockFile(File versionFolder) { + static File getCacheLockFolder(File versionFolder) { var absoluteVersionFolder = versionFolder.getAbsoluteFile(); - return new File(absoluteVersionFolder.getParentFile(), absoluteVersionFolder.getName() + CACHE_LOCK_FILENAME); + return new File(absoluteVersionFolder.getParentFile(), absoluteVersionFolder.getName() + CACHE_LOCK_FOLDERNAME); + } + + private static String getProcessIdentity() { + var process = ProcessHandle.current(); + var startTime = process.info().startInstant().map(Instant::toString).orElse(""); + return process.pid() + System.lineSeparator() + startTime; + } + + private static boolean isCacheLockStale(File lockFolder) throws IOException { + if (!lockFolder.isDirectory()) { + throw new IOException("Cache lock path is not a directory: '" + lockFolder + "'"); + } + + var ownerFile = new File(lockFolder, CACHE_LOCK_OWNER_FILENAME); + if (!ownerFile.isFile()) { + return isCacheLockOld(lockFolder); + } + + var lines = Files.readAllLines(ownerFile.toPath()); + if (lines.isEmpty()) { + return isCacheLockOld(lockFolder); + } + + try { + var pid = Long.parseLong(lines.get(0).trim()); + var process = ProcessHandle.of(pid); + if (process.isEmpty() || !process.get().isAlive()) { + return true; + } + + if (lines.size() > 1 && !lines.get(1).isBlank()) { + var processStart = process.get().info().startInstant(); + if (processStart.isPresent() && !processStart.get().toString().equals(lines.get(1).trim())) { + return true; + } + } + + return false; + } catch (NumberFormatException e) { + return isCacheLockOld(lockFolder); + } + } + + private static boolean isCacheLockOld(File lockFolder) throws IOException { + return Files.getLastModifiedTime(lockFolder.toPath()).toInstant() + .isBefore(Instant.now().minus(CACHE_LOCK_STALE_MAX_AGE)); + } + + private static void waitForCacheLock() throws IOException { + try { + Thread.sleep(CACHE_LOCK_RETRY_INTERVAL.toMillis()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted while waiting for clang-dumper cache lock", e); + } + } + + private static void deleteCacheLock(File lockFolder) throws IOException { + Files.deleteIfExists(new File(lockFolder, CACHE_LOCK_OWNER_FILENAME).toPath()); + Files.deleteIfExists(lockFolder.toPath()); + } + + static final class CacheLock implements AutoCloseable { + + private final File lockFolder; + + private CacheLock(File lockFolder) { + this.lockFolder = lockFolder; + } + + @Override + public void close() { + try { + var releasedFolder = new File(lockFolder.getParentFile(), + lockFolder.getName() + ".released-" + UUID.randomUUID()); + try { + Files.move(lockFolder.toPath(), releasedFolder.toPath(), StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + Files.move(lockFolder.toPath(), releasedFolder.toPath()); + } + + deleteCacheLock(releasedFolder); + } catch (IOException e) { + SpecsLogs.warn("Could not remove temporary clang-dumper cache lock '" + lockFolder + "'", e); + } + } } private static boolean hasExpectedSha256(File file, ClangDumperManifestAsset asset) { diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 7f27ac5af9..d3946cfb63 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -21,13 +21,14 @@ import java.io.File; import java.io.IOException; -import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; import java.time.Duration; import java.time.Instant; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -109,18 +110,44 @@ public void staleCacheCleanupSkipsLockedVersions() throws IOException { parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); var resources = new ClangResources(parser); - var lockFile = ClangResources.getCacheLockFile(staleVersion); - try (var lockChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); - var ignored = lockChannel.lock()) { + try (var ignored = ClangResources.acquireCacheLock(staleVersion)) { resources.deleteStaleVersions(Instant.now(), currentVersion); assertTrue(staleVersion.isDirectory()); } + assertFalse(ClangResources.getCacheLockFolder(staleVersion).exists()); + resources.deleteStaleVersions(Instant.now(), currentVersion); assertFalse(staleVersion.exists()); } + @Test + public void cacheLockSerializesConcurrentAcquisition() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var executor = Executors.newSingleThreadExecutor(); + ClangResources.CacheLock firstLock = ClangResources.acquireCacheLock(versionFolder); + + try { + var secondLock = executor.submit(() -> ClangResources.acquireCacheLock(versionFolder)); + assertFalse(secondLock.isDone()); + + firstLock.close(); + firstLock = null; + try (var ignored = secondLock.get(10, TimeUnit.SECONDS)) { + assertTrue(versionFolder.isDirectory()); + } + } finally { + if (firstLock != null) { + firstLock.close(); + } + executor.shutdownNow(); + executor.awaitTermination(10, TimeUnit.SECONDS); + } + + assertFalse(ClangResources.getCacheLockFolder(versionFolder).exists()); + } + @Test public void builtinCudaIncludesAreAvailableWithSystemLibc() { var parser = CodeParser.newInstance(); From 609c2935e0dec50560380cb691e2a9605c603996 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 22:58:09 +0100 Subject: [PATCH 49/99] Publish CUDA compatibility header atomically Write the shared CUDA compatibility resource to a unique temporary file and install it with an atomic move, preventing concurrent Clava instances from reading a partial header. --- .../fe/specs/clang/dumper/ClangAstDumper.java | 69 ++++++++++++++++++- .../clang/dumper/ClangAstDumperTest.java | 61 ++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index f1a9fbbadc..441ef8d579 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -39,7 +39,14 @@ import pt.up.fe.specs.util.utilities.LineStream; import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -63,6 +70,7 @@ public static boolean usePlugin() { private final static String CLANG_DUMP_FILENAME = "clangDump.txt"; private final static String STDERR_DUMP_FILENAME = "stderr.txt"; private static final AtomicBoolean CUDA_UNAVAILABLE_WARNING_SHOWN = new AtomicBoolean(); + private static final Object CUDA_COMPATIBILITY_LOCK = new Object(); private static final List CLANG_AST_DUMPER_TEMP_FILES = Arrays.asList("includes.txt", CLANG_DUMP_FILENAME, // "clavaDump.txt", "nodetypes.txt", "types.txt", "is_temporary.txt", "template_args.txt", @@ -74,6 +82,65 @@ public static List getTempFiles() { return CLANG_AST_DUMPER_TEMP_FILES; } + static File getCudaCompatibilityFile(File folder) { + var destination = new File(folder, ClangAstResource.CUDA_COMPATIBILITY.getFilename()); + if (destination.isFile()) { + return destination; + } + + synchronized (CUDA_COMPATIBILITY_LOCK) { + if (destination.isFile()) { + return destination; + } + + return writeCudaCompatibilityFile(destination); + } + } + + private static File writeCudaCompatibilityFile(File destination) { + var destinationPath = destination.toPath(); + Path temporaryPath = null; + + try { + Files.createDirectories(destinationPath.getParent()); + temporaryPath = Files.createTempFile(destinationPath.getParent(), destination.getName(), ".tmp"); + + try (var inputStream = ClangAstResource.CUDA_COMPATIBILITY.toStream()) { + if (inputStream == null) { + throw new IOException("Could not load CUDA compatibility resource"); + } + + Files.copy(inputStream, temporaryPath, StandardCopyOption.REPLACE_EXISTING); + } + + try { + Files.move(temporaryPath, destinationPath, StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + try { + Files.move(temporaryPath, destinationPath); + } catch (FileAlreadyExistsException ignored) { + // Another JVM installed the complete file first. + } + } catch (FileAlreadyExistsException ignored) { + // Another JVM installed the complete file first. + } + + return destination; + } catch (IOException e) { + throw new UncheckedIOException("Could not write CUDA compatibility resource '" + + destination + "'", e); + } finally { + if (temporaryPath != null) { + try { + Files.deleteIfExists(temporaryPath); + } catch (IOException e) { + SpecsLogs.warn("Could not delete temporary CUDA compatibility resource '" + + temporaryPath + "'", e); + } + } + } + } + /** * TODO: Not implemented yet *

    @@ -248,7 +315,7 @@ else if (isCuda) { arguments.add("-include"); arguments.add("__clang_cuda_runtime_wrapper.h"); arguments.add("-include"); - arguments.add(ClangAstResource.CUDA_COMPATIBILITY.write(SpecsIo.getTempFolder()).getAbsolutePath()); + arguments.add(getCudaCompatibilityFile(SpecsIo.getTempFolder()).getAbsolutePath()); } else if (useBuiltinCudaLib) { clangResources.getSystemCudaInstallation().ifPresentOrElse(cudaFolder -> { ClavaLog.debug("Setting --cuda-path to discovered CUDA folder '" diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java new file mode 100644 index 0000000000..7e0dbe140f --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java @@ -0,0 +1,61 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang.dumper; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import pt.up.fe.specs.clang.ClangAstResource; + +public class ClangAstDumperTest { + + @TempDir + Path tempFolder; + + @Test + public void cudaCompatibilityFileIsCompleteWhenRequestedConcurrently() throws Exception { + ExecutorService executor = Executors.newFixedThreadPool(8); + try { + var futures = new ArrayList>(); + for (int index = 0; index < 32; index++) { + futures.add(executor.submit(() -> ClangAstDumper.getCudaCompatibilityFile(tempFolder.toFile()))); + } + + Set files = new HashSet<>(); + for (var future : futures) { + files.add(future.get(10, TimeUnit.SECONDS)); + } + + var expectedFile = tempFolder.resolve(ClangAstResource.CUDA_COMPATIBILITY.getFilename()).toFile(); + assertEquals(Set.of(expectedFile), files); + assertEquals(ClangAstResource.CUDA_COMPATIBILITY.read(), Files.readString(expectedFile.toPath())); + } finally { + executor.shutdownNow(); + executor.awaitTermination(10, TimeUnit.SECONDS); + } + } +} From 24a0658626186e9f290a5211b7c7dc6a56cc4566 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 30 Jul 2026 23:19:39 +0100 Subject: [PATCH 50/99] Preserve JavaScript initialization style names Restore callinit and listinit as explicit enum values because the generated TypeScript API exposes raw strings without an enum conversion. --- .../specs/clang/parser/tests/InitializationStyleTest.java | 7 +++++++ .../fe/specs/clava/ast/decl/enums/InitializationStyle.java | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java index 2860f16b17..4a6207ae8c 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java @@ -24,6 +24,7 @@ import pt.up.fe.specs.clang.codeparser.CodeParser; import pt.up.fe.specs.clava.ast.decl.VarDecl; +import pt.up.fe.specs.clava.ast.decl.enums.InitializationStyle; import pt.up.fe.specs.clava.ast.extra.App; import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsSystem; @@ -47,4 +48,10 @@ public void parenthesizedListInitializationKeepsAllArguments() { assertEquals("Point point(1, 2)", point.getCode()); } + + @Test + public void javascriptInitializationStyleNamesRemainCompatible() { + assertEquals("callinit", InitializationStyle.CALL_INIT.getString()); + assertEquals("listinit", InitializationStyle.LIST_INIT.getString()); + } } diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java index 30cf723e20..45a8b01f6e 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/decl/enums/InitializationStyle.java @@ -25,8 +25,10 @@ public enum InitializationStyle implements StringProvider { CINIT, // C-style initialization with assignment - CALL_INIT, // Call-style initialization (C++98) - LIST_INIT, // Direct list-initialization (C++11) + // Keep these explicit names for compatibility: build-interfaces exposes strings to TS, with no TS enum to convert + // them to. + CALL_INIT("callinit"), // Call-style initialization (C++98) + LIST_INIT("listinit"), // Direct list-initialization (C++11) ParenListInit; private static Lazy> ENUM_HELPER = EnumHelperWithValue From 1068bcf3da42d326ca64b2bbe8879eb8b6b005c3 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 31 Jul 2026 14:54:02 +0100 Subject: [PATCH 51/99] test: exercise clang dumper cache races Add same-JVM, cross-JVM, multi-version, crash-recovery, stale-cleanup, and real release initialization coverage for the clang-dumper cache. Keep red diagnostic tests for reclaimed-lock release and cached last-used updates bypassing the cache lock. --- .../up/fe/specs/clang/ClangResourcesTest.java | 481 +++++++++++++++++- 1 file changed, 480 insertions(+), 1 deletion(-) diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index d3946cfb63..69136414e8 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -16,19 +16,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.FileTime; import java.time.Duration; import java.time.Instant; import java.util.List; -import java.util.concurrent.ExecutorService; +import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -39,6 +43,8 @@ public class ClangResourcesTest { + private static final Duration PROCESS_TIMEOUT = Duration.ofSeconds(10); + @TempDir Path tempFolder; @@ -148,6 +154,479 @@ public void cacheLockSerializesConcurrentAcquisition() throws Exception { assertFalse(ClangResources.getCacheLockFolder(versionFolder).exists()); } + @Test + public void cacheLockSerializesAcquisitionAcrossJvms() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var firstAcquired = tempFolder.resolve("first.acquired"); + var firstRelease = tempFolder.resolve("first.release"); + var secondAcquired = tempFolder.resolve("second.acquired"); + var secondRelease = tempFolder.resolve("second.release"); + var firstLog = tempFolder.resolve("first.log"); + var secondLog = tempFolder.resolve("second.log"); + + Process first = startCacheLockProcess(versionFolder, firstAcquired, firstRelease, firstLog); + Process second = null; + try { + assertTrue(waitForFile(firstAcquired, PROCESS_TIMEOUT)); + + second = startCacheLockProcess(versionFolder, secondAcquired, secondRelease, secondLog); + assertFalse(waitForFile(secondAcquired, Duration.ofMillis(750)), + "A second JVM acquired a cache lock that was still held"); + + Files.createFile(firstRelease); + assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT)); + + Files.createFile(secondRelease); + waitForProcess(first, firstLog); + waitForProcess(second, secondLog); + } finally { + releaseProcess(firstRelease); + releaseProcess(secondRelease); + stopProcess(first); + stopProcess(second); + } + + assertFalse(ClangResources.getCacheLockFolder(versionFolder).exists()); + } + + @Test + public void cacheLockAllowsDifferentVersionsToProceedAcrossJvms() throws Exception { + var firstVersion = Files.createDirectory(tempFolder.resolve("version-1")).toFile(); + var secondVersion = Files.createDirectory(tempFolder.resolve("version-2")).toFile(); + var firstAcquired = tempFolder.resolve("first.acquired"); + var firstRelease = tempFolder.resolve("first.release"); + var secondAcquired = tempFolder.resolve("second.acquired"); + var secondRelease = tempFolder.resolve("second.release"); + var firstLog = tempFolder.resolve("first.log"); + var secondLog = tempFolder.resolve("second.log"); + + Process first = startCacheLockProcess(firstVersion, firstAcquired, firstRelease, firstLog); + Process second = null; + try { + assertTrue(waitForFile(firstAcquired, PROCESS_TIMEOUT)); + + second = startCacheLockProcess(secondVersion, secondAcquired, secondRelease, secondLog); + assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT), + "A different dumper version was blocked by an unrelated cache lock"); + + Files.createFile(firstRelease); + Files.createFile(secondRelease); + waitForProcess(first, firstLog); + waitForProcess(second, secondLog); + } finally { + releaseProcess(firstRelease); + releaseProcess(secondRelease); + stopProcess(first); + stopProcess(second); + } + + assertFalse(ClangResources.getCacheLockFolder(firstVersion).exists()); + assertFalse(ClangResources.getCacheLockFolder(secondVersion).exists()); + } + + @Test + public void cacheLockRecoversAfterOwningJvmIsTerminated() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var firstAcquired = tempFolder.resolve("first.acquired"); + var firstRelease = tempFolder.resolve("first.release"); + var secondAcquired = tempFolder.resolve("second.acquired"); + var secondRelease = tempFolder.resolve("second.release"); + var firstLog = tempFolder.resolve("first.log"); + var secondLog = tempFolder.resolve("second.log"); + + Process first = startCacheLockProcess(versionFolder, firstAcquired, firstRelease, firstLog); + Process second = null; + try { + assertTrue(waitForFile(firstAcquired, PROCESS_TIMEOUT)); + first.destroyForcibly(); + assertTrue(first.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + + second = startCacheLockProcess(versionFolder, secondAcquired, secondRelease, secondLog); + assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT), + "A cache lock left by a terminated JVM was not recovered"); + + Files.createFile(secondRelease); + waitForProcess(second, secondLog); + } finally { + releaseProcess(firstRelease); + releaseProcess(secondRelease); + stopProcess(first); + stopProcess(second); + } + + assertFalse(ClangResources.getCacheLockFolder(versionFolder).exists()); + } + + @Test + public void cacheLockDoesNotTreatLiveOwnerAsStaleWhenDirectoryIsOld() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); + var secondAcquired = tempFolder.resolve("second.acquired"); + var secondRelease = tempFolder.resolve("second.release"); + var secondLog = tempFolder.resolve("second.log"); + + ClangResources.CacheLock first = ClangResources.acquireCacheLock(versionFolder); + Process second = null; + try { + Files.setLastModifiedTime(lockFolder, FileTime.from(Instant.now().minus(Duration.ofHours(1)))); + + second = startCacheLockProcess(versionFolder, secondAcquired, secondRelease, secondLog); + assertFalse(waitForFile(secondAcquired, Duration.ofMillis(750)), + "A live cache-lock owner was incorrectly treated as stale"); + + first.close(); + first = null; + assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT)); + Files.createFile(secondRelease); + waitForProcess(second, secondLog); + } finally { + if (first != null) { + first.close(); + } + releaseProcess(secondRelease); + stopProcess(second); + } + + assertFalse(lockFolder.toFile().exists()); + } + + @Test + public void cacheLockUsesProcessStartTimeWhenPidIsStillAlive() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); + Files.createDirectory(lockFolder); + assumeTrue(ProcessHandle.current().info().startInstant().isPresent(), + "The current platform does not expose process start times"); + Files.writeString(lockFolder.resolve("owner"), + ProcessHandle.current().pid() + System.lineSeparator() + Instant.EPOCH + System.lineSeparator()); + + var acquired = tempFolder.resolve("acquired"); + var release = tempFolder.resolve("release"); + var log = tempFolder.resolve("child.log"); + Process child = startCacheLockProcess(versionFolder, acquired, release, log); + try { + assertTrue(waitForFile(acquired, PROCESS_TIMEOUT), + "A lock with a reused PID and a different process start time was not recovered"); + Files.createFile(release); + waitForProcess(child, log); + } finally { + releaseProcess(release); + stopProcess(child); + } + + assertFalse(lockFolder.toFile().exists()); + } + + @Test + public void cacheLockRecoversAnOldOwnerlessLock() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); + Files.createDirectory(lockFolder); + Files.setLastModifiedTime(lockFolder, FileTime.from(Instant.now().minus(Duration.ofHours(1)))); + + var acquired = tempFolder.resolve("acquired"); + var release = tempFolder.resolve("release"); + var log = tempFolder.resolve("child.log"); + Process child = startCacheLockProcess(versionFolder, acquired, release, log); + try { + assertTrue(waitForFile(acquired, PROCESS_TIMEOUT), "An old ownerless cache lock was not recovered"); + Files.createFile(release); + waitForProcess(child, log); + } finally { + releaseProcess(release); + stopProcess(child); + } + + assertFalse(lockFolder.toFile().exists()); + } + + @Test + public void cacheLockDoesNotStealARecentOwnerlessLock() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); + Files.createDirectory(lockFolder); + + var acquired = tempFolder.resolve("acquired"); + var release = tempFolder.resolve("release"); + var log = tempFolder.resolve("child.log"); + Process child = startCacheLockProcess(versionFolder, acquired, release, log); + try { + assertFalse(waitForFile(acquired, Duration.ofMillis(750)), + "A lock without owner metadata was stolen before its claim was old enough"); + + Files.delete(lockFolder); + assertTrue(waitForFile(acquired, PROCESS_TIMEOUT)); + Files.createFile(release); + waitForProcess(child, log); + } finally { + releaseProcess(release); + stopProcess(child); + } + + assertFalse(lockFolder.toFile().exists()); + } + + @Test + public void staleCacheCleanupSkipsLockedVersionAcrossJvms() throws Exception { + var currentVersion = Files.createDirectory(tempFolder.resolve("current")).toFile(); + var staleVersion = Files.createDirectory(tempFolder.resolve("stale")).toFile(); + Files.writeString(staleVersion.toPath().resolve("last-used.txt"), + Instant.now().minus(Duration.ofDays(61)).toString()); + + var acquired = tempFolder.resolve("acquired"); + var release = tempFolder.resolve("release"); + var log = tempFolder.resolve("child.log"); + Process child = startCacheLockProcess(staleVersion, acquired, release, log); + try { + assertTrue(waitForFile(acquired, PROCESS_TIMEOUT)); + + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + new ClangResources(parser).deleteStaleVersions(Instant.now(), currentVersion); + assertTrue(staleVersion.isDirectory(), "Stale cleanup deleted a version locked by another JVM"); + + Files.createFile(release); + waitForProcess(child, log); + } finally { + releaseProcess(release); + stopProcess(child); + } + + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + new ClangResources(parser).deleteStaleVersions(Instant.now(), currentVersion); + assertFalse(staleVersion.exists()); + } + + @Test + public void releaseResourcesCanBeInitializedBySeparateJvms() throws Exception { + var cacheFolder = Files.createDirectory(tempFolder.resolve("cache")).toFile(); + var firstDone = tempFolder.resolve("first.done"); + var secondDone = tempFolder.resolve("second.done"); + var firstLog = tempFolder.resolve("first.log"); + var secondLog = tempFolder.resolve("second.log"); + + Process first = startResourceProcess(cacheFolder, firstDone, firstLog); + Process second = startResourceProcess(cacheFolder, secondDone, secondLog); + try { + waitForProcess(first, firstLog); + waitForProcess(second, secondLog); + } finally { + stopProcess(first); + stopProcess(second); + } + + var firstExecutable = new File(Files.readString(firstDone).trim()); + var secondExecutable = new File(Files.readString(secondDone).trim()); + assertEquals(firstExecutable.getAbsoluteFile(), secondExecutable.getAbsoluteFile()); + assertTrue(firstExecutable.isFile()); + assertFalse(cacheFolder.toPath().resolve(ClangAstWebResource.getReleaseTag() + ".cache.lock").toFile().exists()); + } + + @Test + public void cacheLockReleaseDoesNotRemoveAReclaimedLock() throws Exception { + var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); + var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); + ClangResources.CacheLock first = ClangResources.acquireCacheLock(versionFolder); + + Files.writeString(lockFolder.resolve("owner"), Long.MAX_VALUE + System.lineSeparator()); + ClangResources.CacheLock second = ClangResources.acquireCacheLock(versionFolder); + + try { + first.close(); + assertTrue(Files.isDirectory(lockFolder), + "A stale lock owner released and deleted a replacement owner's lock"); + } finally { + second.close(); + } + } + + @Test + public void cachedUseDoesNotBypassTheCacheLockBeforeUpdatingLastUsed() throws Exception { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + var resources = new ClangResources(parser); + var versionFolder = resources.getClangResourceFolder(); + var fakeExecutable = Files.createFile(tempFolder.resolve("fake-tool")).toFile(); + var cache = getClangFilesCache(); + var cacheKey = getClangFilesCacheKey(resources, LibcMode.SYSTEM); + var cachedFiles = new ClangFiles(fakeExecutable, List.of()); + cache.put(cacheKey, cachedFiles); + + ClangResources.CacheLock lock = ClangResources.acquireCacheLock(versionFolder); + var executor = Executors.newSingleThreadExecutor(); + try { + var future = executor.submit(() -> resources.getClangFiles(LibcMode.SYSTEM)); + assertThrows(TimeoutException.class, () -> future.get(750, TimeUnit.MILLISECONDS), + "A cached use updated last-used without coordinating with the cache lock"); + + lock.close(); + lock = null; + assertSame(cachedFiles, future.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + } finally { + if (lock != null) { + lock.close(); + } + executor.shutdownNow(); + executor.awaitTermination(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + cache.remove(cacheKey); + } + } + + @Test + public void sameJvmInstancesShareReleaseCacheInitialization() throws Exception { + var firstParser = CodeParser.newInstance(); + firstParser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + firstParser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + + var secondParser = CodeParser.newInstance(); + secondParser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + secondParser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + + var thirdParser = CodeParser.newInstance(); + thirdParser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + thirdParser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + + var executor = Executors.newFixedThreadPool(3); + try { + var first = executor.submit(() -> new ClangResources(firstParser).getClangFiles(LibcMode.SYSTEM)); + var second = executor.submit(() -> new ClangResources(secondParser).getClangFiles(LibcMode.SYSTEM)); + var third = executor.submit( + () -> new ClangResources(thirdParser).getClangFiles(LibcMode.BUILTIN_AND_LIBC)); + + var firstFiles = first.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + var secondFiles = second.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + var thirdFiles = third.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + + assertSame(firstFiles, secondFiles, "Same-JVM instances did not share the ClangFiles cache entry"); + assertEquals(firstFiles.clangExecutable().getAbsoluteFile(), thirdFiles.clangExecutable().getAbsoluteFile()); + assertTrue(firstFiles.clangExecutable().isFile()); + } finally { + executor.shutdownNow(); + executor.awaitTermination(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } + } + + private Process startCacheLockProcess(File versionFolder, Path acquired, Path release, Path log) + throws IOException { + + var javaExecutable = Path.of(System.getProperty("java.home"), "bin", + SupportedPlatform.getCurrentPlatform().isWindows() ? "java.exe" : "java"); + + return new ProcessBuilder( + javaExecutable.toString(), + "-cp", + System.getProperty("java.class.path"), + CacheLockProcess.class.getName(), + versionFolder.getAbsolutePath(), + acquired.toAbsolutePath().toString(), + release.toAbsolutePath().toString()) + .redirectErrorStream(true) + .redirectOutput(log.toFile()) + .start(); + } + + private Process startResourceProcess(File cacheFolder, Path done, Path log) throws IOException { + var javaExecutable = Path.of(System.getProperty("java.home"), "bin", + SupportedPlatform.getCurrentPlatform().isWindows() ? "java.exe" : "java"); + + return new ProcessBuilder( + javaExecutable.toString(), + "-cp", + System.getProperty("java.class.path"), + CacheLockProcess.class.getName(), + "resources", + cacheFolder.getAbsolutePath(), + done.toAbsolutePath().toString()) + .redirectErrorStream(true) + .redirectOutput(log.toFile()) + .start(); + } + + @SuppressWarnings("unchecked") + private Map getClangFilesCache() throws ReflectiveOperationException { + var cacheField = ClangResources.class.getDeclaredField("CLANG_FILES_CACHE"); + cacheField.setAccessible(true); + return (Map) cacheField.get(null); + } + + private String getClangFilesCacheKey(ClangResources resources, LibcMode libcMode) { + var source = ClangAstWebResource.getDumperSource(); + var sourceKey = source instanceof Release + ? source + "_" + resources.getClangResourceFolder().getAbsolutePath() + : source.toString(); + return libcMode.name() + "_false_" + sourceKey; + } + + private boolean waitForFile(Path file, Duration timeout) throws InterruptedException { + var deadline = System.nanoTime() + timeout.toNanos(); + do { + if (Files.isRegularFile(file)) { + return true; + } + + Thread.sleep(10); + } while (System.nanoTime() < deadline); + + return Files.isRegularFile(file); + } + + private void waitForProcess(Process process, Path log) throws Exception { + assertTrue(process.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS), + () -> "Child JVM did not finish. Output: " + readLog(log)); + assertEquals(0, process.exitValue(), () -> "Child JVM failed. Output: " + readLog(log)); + } + + private String readLog(Path log) { + try { + return Files.readString(log); + } catch (IOException e) { + return ""; + } + } + + private void releaseProcess(Path release) throws IOException { + if (release != null && !Files.exists(release)) { + Files.createFile(release); + } + } + + private void stopProcess(Process process) throws InterruptedException { + if (process == null || !process.isAlive()) { + return; + } + + process.destroyForcibly(); + process.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } + + public static final class CacheLockProcess { + + private CacheLockProcess() { + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("resources")) { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, new File(args[1])); + var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); + Files.writeString(Path.of(args[2]), clangFiles.clangExecutable().getAbsolutePath()); + return; + } + + var versionFolder = new File(args[0]); + var acquired = Path.of(args[1]); + var release = Path.of(args[2]); + + try (var ignored = ClangResources.acquireCacheLock(versionFolder)) { + Files.writeString(acquired, Long.toString(ProcessHandle.current().pid())); + while (!Files.exists(release)) { + Thread.sleep(10); + } + } + } + } + @Test public void builtinCudaIncludesAreAvailableWithSystemLibc() { var parser = CodeParser.newInstance(); From 954f6ef275bdf16d44a7bdc84894b3a0cae67aec Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 31 Jul 2026 15:04:50 +0100 Subject: [PATCH 52/99] test: cover stale cleanup of cached dumper paths Add a cross-JVM diagnostic proving stale cleanup can remove an executable still referenced by the in-process ClangFiles cache. --- .../up/fe/specs/clang/ClangResourcesTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 69136414e8..d4bdcface3 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -473,6 +473,35 @@ public void cachedUseDoesNotBypassTheCacheLockBeforeUpdatingLastUsed() throws Ex } } + @Test + public void staleCleanupDoesNotDeleteAnExecutableStillCachedInThisJvm() throws Exception { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + var resources = new ClangResources(parser); + var versionFolder = resources.getClangResourceFolder(); + var fakeExecutable = Files.createFile(versionFolder.toPath().resolve("fake-tool")).toFile(); + Files.writeString(versionFolder.toPath().resolve("last-used.txt"), + Instant.now().minus(Duration.ofDays(61)).toString()); + + var cache = getClangFilesCache(); + var cacheKey = getClangFilesCacheKey(resources, LibcMode.SYSTEM); + cache.put(cacheKey, new ClangFiles(fakeExecutable, List.of())); + + var currentVersion = Files.createDirectory(tempFolder.resolve("current")); + var done = tempFolder.resolve("cleanup.done"); + var log = tempFolder.resolve("cleanup.log"); + Process cleanup = startCleanupProcess(tempFolder.toFile(), currentVersion.toFile(), done, log); + try { + waitForProcess(cleanup, log); + } finally { + stopProcess(cleanup); + cache.remove(cacheKey); + } + + assertTrue(fakeExecutable.isFile(), + "A separate JVM's stale cleanup deleted an executable still held by this JVM's cache"); + } + @Test public void sameJvmInstancesShareReleaseCacheInitialization() throws Exception { var firstParser = CodeParser.newInstance(); @@ -543,6 +572,26 @@ private Process startResourceProcess(File cacheFolder, Path done, Path log) thro .start(); } + private Process startCleanupProcess(File cacheFolder, File currentVersion, Path done, Path log) + throws IOException { + + var javaExecutable = Path.of(System.getProperty("java.home"), "bin", + SupportedPlatform.getCurrentPlatform().isWindows() ? "java.exe" : "java"); + + return new ProcessBuilder( + javaExecutable.toString(), + "-cp", + System.getProperty("java.class.path"), + CacheLockProcess.class.getName(), + "cleanup", + cacheFolder.getAbsolutePath(), + currentVersion.getAbsolutePath(), + done.toAbsolutePath().toString()) + .redirectErrorStream(true) + .redirectOutput(log.toFile()) + .start(); + } + @SuppressWarnings("unchecked") private Map getClangFilesCache() throws ReflectiveOperationException { var cacheField = ClangResources.class.getDeclaredField("CLANG_FILES_CACHE"); @@ -614,6 +663,14 @@ public static void main(String[] args) throws Exception { return; } + if (args[0].equals("cleanup")) { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, new File(args[1])); + new ClangResources(parser).deleteStaleVersions(Instant.now(), new File(args[2])); + Files.writeString(Path.of(args[3]), "done"); + return; + } + var versionFolder = new File(args[0]); var acquired = Path.of(args[1]); var release = Path.of(args[2]); From 7ebcda6dc52a6ede49e1b675f740183b1cceea40 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 31 Jul 2026 16:24:56 +0100 Subject: [PATCH 53/99] fix: coordinate cached clang resource heartbeat updates --- .../pt/up/fe/specs/clang/ClangResources.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 7cbe0e673f..dff0041aea 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -158,15 +158,6 @@ public ClangFiles getClangFiles(LibcMode libcMode) { : source.toString(); var key = libcMode.name() + "_" + useBuiltinCuda + "_" + sourceKey; - var cachedFiles = CLANG_FILES_CACHE.get(key); - if (cachedFiles != null) { - if (source instanceof Release) { - writeLastUsed(Instant.now()); - } - SpecsLogs.debug(() -> "Using cached version of Clang files: " + cachedFiles); - return cachedFiles; - } - // The JVM lock must protect the same cache folder as the inter-process lock. The cache key also contains the // libc and CUDA configuration, so using it here would let two configurations acquire different JVM locks for // the same release folder and race while initializing it. @@ -178,8 +169,16 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var files = CLANG_FILES_CACHE.get(key); if (files != null) { if (source instanceof Release) { - writeLastUsed(Instant.now()); + var resourceFolder = getClangResourceFolder(); + try (var ignored = acquireCacheLock(resourceFolder)) { + writeLastUsed(Instant.now()); + } catch (IOException e) { + var lockFolder = getCacheLockFolder(resourceFolder); + throw new UncheckedIOException("Could not lock clang-dumper cache '" + lockFolder + "'", e); + } } + + SpecsLogs.debug(() -> "Using cached version of Clang files: " + files); return files; } From 7df968c04ee9e2162d7725e45b47bf2b60dda722 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sat, 1 Aug 2026 00:51:20 +0100 Subject: [PATCH 54/99] Avoid hashing cached clang-dumper includes --- .../pt/up/fe/specs/clang/ClangResources.java | 158 +----------------- .../up/fe/specs/clang/ClangResourcesTest.java | 19 +++ 2 files changed, 26 insertions(+), 151 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index dff0041aea..3919ae1a25 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -43,7 +43,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; import java.util.HexFormat; import java.util.List; @@ -54,7 +53,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import java.util.stream.Stream; import java.util.regex.Pattern; public class ClangResources { @@ -63,15 +61,12 @@ public class ClangResources { private static final Map CLANG_FILES_LOCKS = new ConcurrentHashMap<>(); private final static String CLANG_FOLDERNAME = "clang_ast_exe"; private final static String INCLUDES_FOLDERNAME = "includes"; - private final static String INCLUDES_HASHES_FILENAME = "includes.sha256"; - private final static String INCLUDES_VALIDATED_FILENAME = "includes-validated.txt"; private final static String LAST_USED_FILENAME = "last-used.txt"; private final static String CACHE_LOCK_FOLDERNAME = ".cache.lock"; private final static String CACHE_LOCK_OWNER_FILENAME = "owner"; private final static Duration CACHE_LOCK_RETRY_INTERVAL = Duration.ofMillis(100); private final static Duration CACHE_LOCK_STALE_MAX_AGE = Duration.ofMinutes(5); private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); - private final static Duration INCLUDES_DEEP_VALIDATION_INTERVAL = Duration.ofDays(1); private static final AtomicInteger HAS_LIBC = new AtomicInteger(-1); @@ -370,10 +365,8 @@ private File prepareIncludesFolder(ClangDumperManifest manifest) { File resourceFolder = getClangResourceFolder(); var includesAsset = getCurrentAsset(manifest, "includes"); var extractedFolder = new File(resourceFolder, INCLUDES_FOLDERNAME); - var includesHashesFile = new File(resourceFolder, INCLUDES_HASHES_FILENAME); - var includesValidatedFile = new File(resourceFolder, INCLUDES_VALIDATED_FILENAME); - if (isIncludesCacheValid(extractedFolder, includesHashesFile, includesValidatedFile, includesAsset)) { + if (isIncludesCacheValid(extractedFolder)) { return extractedFolder; } @@ -383,8 +376,6 @@ private File prepareIncludesFolder(ClangDumperManifest manifest) { SpecsIo.mkdir(extractedFolder); SpecsIo.deleteFolderContents(extractedFolder); SpecsIo.extractZip(zipFile.getFile(), extractedFolder); - writeIncludesHashes(extractedFolder, includesHashesFile, includesAsset); - writeTimestamp(includesValidatedFile, Instant.now()); } finally { SpecsIo.delete(zipFile.getFile()); } @@ -414,6 +405,10 @@ private ResourceWriteData downloadAsset(ClangDumperManifestAsset asset, File res var resource = ClangAstWebResource.getAssetResource(asset); var writeData = resource.writeVersioned(resourceFolder, ClangResources.class); + if (!writeData.isNewFile()) { + return writeData; + } + if (!hasExpectedSha256(writeData.getFile(), asset)) { SpecsLogs.info("Downloaded clang-dumper asset '" + asset.filename() + "' does not match the expected checksum, downloading it again."); @@ -435,14 +430,9 @@ private ClangDumperManifestAsset getCurrentAsset(ClangDumperManifest manifest, S return manifest.getAsset(platform, arch, kind); } - private boolean isIncludesCacheValid(File includesFolder, File includesHashesFile, File includesValidatedFile, - ClangDumperManifestAsset includesAsset) { - - if (!includesFolder.isDirectory() || !includesHashesFile.isFile()) { - return false; - } + static boolean isIncludesCacheValid(File includesFolder) { - if (!hasExpectedIncludesAssetHash(includesHashesFile, includesAsset)) { + if (!includesFolder.isDirectory()) { return false; } @@ -452,141 +442,9 @@ private boolean isIncludesCacheValid(File includesFolder, File includesHashesFil return false; } - if (!shouldRunIncludesDeepValidation(includesValidatedFile)) { - return true; - } - - var validationStart = Instant.now(); - SpecsLogs.info("Validating clang-dumper includes cache: " + includesFolder); - if (isIncludesCacheDeepValid(includesFolder, includesHashesFile, includesAsset)) { - writeTimestamp(includesValidatedFile, Instant.now()); - SpecsLogs.info("Validated clang-dumper includes cache in " - + Duration.between(validationStart, Instant.now()).toMillis() + " ms"); - return true; - } - - SpecsLogs.info("Invalidating clang-dumper includes cache metadata: " + includesHashesFile); - if (includesHashesFile.isFile()) { - SpecsIo.delete(includesHashesFile); - } - if (includesValidatedFile.isFile()) { - SpecsIo.delete(includesValidatedFile); - } - return false; - } - - private static boolean shouldRunIncludesDeepValidation(File includesValidatedFile) { - if (!includesValidatedFile.isFile()) { - return true; - } - - try { - var lastValidated = Instant.parse(SpecsIo.read(includesValidatedFile).trim()); - return lastValidated.isBefore(Instant.now().minus(INCLUDES_DEEP_VALIDATION_INTERVAL)); - } catch (RuntimeException e) { - return true; - } - } - - private boolean isIncludesCacheDeepValid(File includesFolder, File includesHashesFile, - ClangDumperManifestAsset includesAsset) { - - var expectedHashes = readIncludesHashes(includesHashesFile, includesAsset); - if (expectedHashes == null) { - return false; - } - - var expectedFiles = expectedHashes.keySet(); - var actualFiles = listRegularFiles(includesFolder); - - if (!actualFiles.equals(expectedFiles)) { - SpecsLogs.info("Cached clang-dumper includes contain missing or extra files, extracting them again."); - return false; - } - - for (var entry : expectedHashes.entrySet()) { - var file = new File(includesFolder, entry.getKey()); - if (!entry.getValue().equalsIgnoreCase(calculateSha256(file))) { - SpecsLogs.info("Cached clang-dumper include file '" + file - + "' does not match the expected checksum, extracting includes again."); - return false; - } - } - return true; } - private static boolean hasExpectedIncludesAssetHash(File includesHashesFile, - ClangDumperManifestAsset includesAsset) { - - try (var reader = Files.newBufferedReader(includesHashesFile.toPath())) { - return ("# clang-dumper-includes " + includesAsset.sha256()).equals(reader.readLine()); - } catch (IOException e) { - return false; - } - } - - private static Map readIncludesHashes(File includesHashesFile, - ClangDumperManifestAsset includesAsset) { - - var lines = SpecsIo.read(includesHashesFile).lines().toList(); - if (lines.size() < 2 || !lines.get(0).equals("# clang-dumper-includes " + includesAsset.sha256())) { - return null; - } - - Map hashes = new HashMap<>(); - for (int i = 1; i < lines.size(); i++) { - var line = lines.get(i); - if (line.isBlank()) { - continue; - } - - if (line.length() <= 65 || line.charAt(64) != ' ') { - return null; - } - - var hash = line.substring(0, 64); - var relativePath = line.substring(65); - hashes.put(relativePath, hash); - } - - return hashes; - } - - private static void writeIncludesHashes(File includesFolder, File includesHashesFile, - ClangDumperManifestAsset includesAsset) { - - var actualFiles = listRegularFiles(includesFolder).stream() - .sorted() - .toList(); - - StringBuilder hashes = new StringBuilder(); - hashes.append("# clang-dumper-includes ").append(includesAsset.sha256()).append(System.lineSeparator()); - - for (var relativePath : actualFiles) { - var file = new File(includesFolder, relativePath); - hashes.append(calculateSha256(file)) - .append(' ') - .append(relativePath) - .append(System.lineSeparator()); - } - - SpecsIo.write(includesHashesFile, hashes.toString()); - } - - private static Set listRegularFiles(File folder) { - try (Stream paths = Files.walk(folder.toPath())) { - return paths - .filter(Files::isRegularFile) - .map(folder.toPath()::relativize) - .map(Path::toString) - .map(path -> path.replace(File.separatorChar, '/')) - .collect(Collectors.toCollection(HashSet::new)); - } catch (IOException e) { - throw new UncheckedIOException("Could not list files in folder '" + folder + "'", e); - } - } - private void validateTopLevelCacheFiles(ClangDumperManifest manifest) { File resourceFolder = getClangResourceFolder(); Set expectedNames = new HashSet<>(); @@ -594,8 +452,6 @@ private void validateTopLevelCacheFiles(ClangDumperManifest manifest) { var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; expectedNames.add(getCurrentAsset(manifest, executableKind).filename()); expectedNames.add(INCLUDES_FOLDERNAME); - expectedNames.add(INCLUDES_HASHES_FILENAME); - expectedNames.add(INCLUDES_VALIDATED_FILENAME); expectedNames.add(LAST_USED_FILENAME); var files = resourceFolder.listFiles(); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index d4bdcface3..ffa8d53ffd 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -83,6 +83,25 @@ public void localBuildRequiresExpectedTool() { assertThrows(RuntimeException.class, () -> ClangResources.getLocalExecutable(tempFolder.toFile())); } + @Test + public void includesCacheValidationOnlyChecksRequiredFiles() throws IOException { + var includesFolder = tempFolder.resolve("includes"); + assertFalse(ClangResources.isIncludesCacheValid(includesFolder.toFile())); + + Files.createDirectory(includesFolder); + assertFalse(ClangResources.isIncludesCacheValid(includesFolder.toFile())); + + Files.createDirectories(includesFolder.resolve("builtin")); + Files.writeString(includesFolder.resolve("entrypoints.txt"), "builtin\n"); + Files.writeString(includesFolder.resolve("builtin/header.h"), "original"); + Files.writeString(includesFolder.resolve("unexpected.txt"), "extra"); + + assertTrue(ClangResources.isIncludesCacheValid(includesFolder.toFile())); + + Files.writeString(includesFolder.resolve("builtin/header.h"), "modified"); + assertTrue(ClangResources.isIncludesCacheValid(includesFolder.toFile())); + } + @Test public void cudaInstallationIsFoundFromConfiguredRoot() throws IOException { var cudaFolder = Files.createDirectories(tempFolder.resolve("cuda")); From e275a1f479ed6f177cf8810f6ec39d01478cd0c7 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sat, 1 Aug 2026 01:29:54 +0100 Subject: [PATCH 55/99] fix: simplify clang cache coordination --- .../pt/up/fe/specs/clang/ClangResources.java | 250 +++++++++++------- .../up/fe/specs/clang/ClangResourcesTest.java | 59 +---- 2 files changed, 150 insertions(+), 159 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 3919ae1a25..75d7535331 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -16,7 +16,6 @@ import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifest; import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifestAsset; import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; -import pt.up.fe.specs.clang.ClangAstWebResource.Release; import pt.up.fe.specs.clang.codeparser.CodeParser; import pt.up.fe.specs.clang.dumper.ClangAstDumper; import pt.up.fe.specs.clang.parsers.TopLevelNodesParser; @@ -30,11 +29,11 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -63,7 +62,7 @@ public class ClangResources { private final static String INCLUDES_FOLDERNAME = "includes"; private final static String LAST_USED_FILENAME = "last-used.txt"; private final static String CACHE_LOCK_FOLDERNAME = ".cache.lock"; - private final static String CACHE_LOCK_OWNER_FILENAME = "owner"; + private final static String CACHE_LOCK_OWNER_PREFIX = "owner-"; private final static Duration CACHE_LOCK_RETRY_INTERVAL = Duration.ofMillis(100); private final static Duration CACHE_LOCK_STALE_MAX_AGE = Duration.ofMinutes(5); private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); @@ -148,50 +147,37 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var source = ClangAstWebResource.getDumperSource(); var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); - var sourceKey = source instanceof Release - ? source + "_" + getClangResourceFolder().getAbsolutePath() - : source.toString(); - var key = libcMode.name() + "_" + useBuiltinCuda + "_" + sourceKey; - - // The JVM lock must protect the same cache folder as the inter-process lock. The cache key also contains the - // libc and CUDA configuration, so using it here would let two configurations acquire different JVM locks for - // the same release folder and race while initializing it. - var jvmLockKey = source instanceof Release - ? getCacheLockFolder(getClangResourceFolder()).getAbsolutePath() - : source.toString(); - var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(jvmLockKey, ignored -> new Object()); - synchronized (jvmLock) { - var files = CLANG_FILES_CACHE.get(key); - if (files != null) { - if (source instanceof Release) { - var resourceFolder = getClangResourceFolder(); - try (var ignored = acquireCacheLock(resourceFolder)) { - writeLastUsed(Instant.now()); - } catch (IOException e) { - var lockFolder = getCacheLockFolder(resourceFolder); - throw new UncheckedIOException("Could not lock clang-dumper cache '" + lockFolder + "'", e); - } - } - SpecsLogs.debug(() -> "Using cached version of Clang files: " + files); - return files; - } + if (source instanceof LocalBuild localBuild) { + var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source; + return CLANG_FILES_CACHE.computeIfAbsent(key, + ignored -> new ClangFiles(getLocalExecutable(localBuild.folder()), List.of())); + } - if (source instanceof LocalBuild localBuild) { - var newFiles = new ClangFiles(getLocalExecutable(localBuild.folder()), List.of()); - CLANG_FILES_CACHE.put(key, newFiles); - return newFiles; - } + var resourceFolder = getClangResourceFolder(); + var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" + + resourceFolder.getAbsolutePath(); + var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(resourceFolder.getAbsolutePath(), ignored -> new Object()); + synchronized (jvmLock) { + var lockFolder = getCacheLockFolder(resourceFolder); + try (var ignored = acquireCacheLock(resourceFolder)) { + var files = CLANG_FILES_CACHE.get(key); + if (files != null) { + if (files.clangExecutable().isFile()) { + writeLastUsed(resourceFolder, Instant.now()); + SpecsLogs.debug(() -> "Using cached version of Clang files: " + files); + return files; + } - var lockFolder = getCacheLockFolder(getClangResourceFolder()); - try (var ignored = acquireCacheLock(getClangResourceFolder())) { + CLANG_FILES_CACHE.remove(key, files); + } - var manifest = ClangAstWebResource.getManifest(getClangResourceFolder()); - File clangExecutable = prepareResources(manifest); - List builtinIncludes = prepareIncludes(manifest, clangExecutable, libcMode); + var manifest = ClangAstWebResource.getManifest(resourceFolder); + File clangExecutable = prepareResources(manifest, resourceFolder); + List builtinIncludes = prepareIncludes(manifest, resourceFolder, clangExecutable, libcMode); - validateTopLevelCacheFiles(manifest); - updateLastUsedAndCleanupStaleVersions(); + validateTopLevelCacheFiles(manifest, resourceFolder); + updateLastUsedAndCleanupStaleVersions(resourceFolder); var newFiles = new ClangFiles(clangExecutable, builtinIncludes); SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); @@ -226,8 +212,7 @@ static File getLocalExecutable(File buildFolder) { return executable; } - private File prepareResources(ClangDumperManifest manifest) { - File resourceFolder = getClangResourceFolder(); + private File prepareResources(ClangDumperManifest manifest, File resourceFolder) { SupportedPlatform platform = SupportedPlatform.getCurrentPlatform(); var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; @@ -342,7 +327,8 @@ private static ProcessOutputAsString runClangAstDumper(File clangExecutable, Fil return SpecsSystem.runProcess(arguments, true, false); } - private List prepareIncludes(ClangDumperManifest manifest, File clangExecutable, LibcMode libcMode) { + private List prepareIncludes(ClangDumperManifest manifest, File resourceFolder, File clangExecutable, + LibcMode libcMode) { var useBuiltinLibc = useBuiltinLibc(clangExecutable, libcMode); var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); @@ -350,19 +336,18 @@ private List prepareIncludes(ClangDumperManifest manifest, File clangExe return List.of(); } - return prepareIncludes(manifest); + return prepareIncludes(manifest, resourceFolder); } - private List prepareIncludes(ClangDumperManifest manifest) { - var extractedFolder = prepareIncludesFolder(manifest); + private List prepareIncludes(ClangDumperManifest manifest, File resourceFolder) { + var extractedFolder = prepareIncludesFolder(manifest, resourceFolder); var includeFolders = getIncludeFolders(extractedFolder); SpecsLogs.debug(() -> "Includes folders: " + includeFolders); return includeFolders.stream().map(File::getAbsolutePath).toList(); } - private File prepareIncludesFolder(ClangDumperManifest manifest) { - File resourceFolder = getClangResourceFolder(); + private File prepareIncludesFolder(ClangDumperManifest manifest, File resourceFolder) { var includesAsset = getCurrentAsset(manifest, "includes"); var extractedFolder = new File(resourceFolder, INCLUDES_FOLDERNAME); @@ -445,8 +430,7 @@ static boolean isIncludesCacheValid(File includesFolder) { return true; } - private void validateTopLevelCacheFiles(ClangDumperManifest manifest) { - File resourceFolder = getClangResourceFolder(); + private void validateTopLevelCacheFiles(ClangDumperManifest manifest, File resourceFolder) { Set expectedNames = new HashSet<>(); expectedNames.add(ClangAstWebResource.MANIFEST_FILENAME); var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; @@ -469,10 +453,9 @@ private void validateTopLevelCacheFiles(ClangDumperManifest manifest) { } } - private void updateLastUsedAndCleanupStaleVersions() { + private void updateLastUsedAndCleanupStaleVersions(File resourceFolder) { var now = Instant.now(); - File resourceFolder = getClangResourceFolder(); - writeLastUsed(now); + writeLastUsed(resourceFolder, now); var staleCleanup = new Thread(() -> deleteStaleVersions(now, resourceFolder), "clang-dumper-stale-cache-cleanup"); @@ -480,8 +463,8 @@ private void updateLastUsedAndCleanupStaleVersions() { staleCleanup.start(); } - private void writeLastUsed(Instant timestamp) { - writeTimestamp(new File(getClangResourceFolder(), LAST_USED_FILENAME), timestamp); + private static void writeLastUsed(File resourceFolder, Instant timestamp) { + writeTimestamp(new File(resourceFolder, LAST_USED_FILENAME), timestamp); } private static void writeTimestamp(File file, Instant timestamp) { @@ -496,29 +479,34 @@ void deleteStaleVersions(Instant now, File currentVersionFolder) { } for (var versionFolder : versions) { - if (versionFolder.equals(currentVersionFolder)) { + if (versionFolder.getAbsoluteFile().equals(currentVersionFolder.getAbsoluteFile())) { continue; } - var lastUsedFile = new File(versionFolder, LAST_USED_FILENAME); - if (!lastUsedFile.isFile()) { - continue; - } - - try (var lock = tryAcquireCacheLock(versionFolder)) { - if (lock == null) { - SpecsLogs.debug(() -> "Skipping locked clang-dumper cache folder: " + versionFolder); - continue; - } - - if (!lastUsedFile.isFile()) { - continue; - } + var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(versionFolder.getAbsolutePath(), ignored -> new Object()); + try { + synchronized (jvmLock) { + var lastUsedFile = new File(versionFolder, LAST_USED_FILENAME); + if (!lastUsedFile.isFile()) { + continue; + } - var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); - if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { - SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); - SpecsIo.deleteFolder(versionFolder); + try (var lock = tryAcquireCacheLock(versionFolder)) { + if (lock == null) { + SpecsLogs.debug(() -> "Skipping locked clang-dumper cache folder: " + versionFolder); + continue; + } + + if (!lastUsedFile.isFile()) { + continue; + } + + var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); + if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { + SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); + SpecsIo.deleteFolder(versionFolder); + } + } } } catch (IOException | RuntimeException e) { SpecsLogs.warn("Could not inspect clang-dumper cache folder '" + versionFolder + "'", e); @@ -551,20 +539,23 @@ private static CacheLock acquireCacheLock(File versionFolder, boolean wait) thro continue; } - deleteCacheLock(lockFolder); + recoverStaleCacheLock(lockFolder); continue; } - var ownerFile = new File(lockFolder, CACHE_LOCK_OWNER_FILENAME); + var ownerFile = new File(lockFolder, CACHE_LOCK_OWNER_PREFIX + UUID.randomUUID()); try { Files.writeString(ownerFile.toPath(), getProcessIdentity(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); - } catch (FileAlreadyExistsException e) { - // A stale-lock recovery raced with another process that claimed this directory. + } catch (NoSuchFileException e) { + // Stale-lock recovery removed the directory while this process was claiming it. continue; + } catch (IOException e) { + deleteEmptyCacheLock(lockFolder); + throw e; } - return new CacheLock(lockFolder); + return new CacheLock(lockFolder, ownerFile); } } @@ -586,16 +577,44 @@ private static String getProcessIdentity() { } private static boolean isCacheLockStale(File lockFolder) throws IOException { + if (!lockFolder.exists()) { + return true; + } + if (!lockFolder.isDirectory()) { throw new IOException("Cache lock path is not a directory: '" + lockFolder + "'"); } - var ownerFile = new File(lockFolder, CACHE_LOCK_OWNER_FILENAME); - if (!ownerFile.isFile()) { + var ownerFiles = lockFolder.listFiles(File::isFile); + if (ownerFiles == null) { + if (!lockFolder.exists()) { + return true; + } + + throw new IOException("Could not list cache lock folder: '" + lockFolder + "'"); + } + + if (ownerFiles.length == 0) { return isCacheLockOld(lockFolder); } - var lines = Files.readAllLines(ownerFile.toPath()); + for (var ownerFile : ownerFiles) { + if (!isCacheLockOwnerStale(lockFolder, ownerFile)) { + return false; + } + } + + return true; + } + + private static boolean isCacheLockOwnerStale(File lockFolder, File ownerFile) throws IOException { + List lines; + try { + lines = Files.readAllLines(ownerFile.toPath()); + } catch (NoSuchFileException e) { + return true; + } + if (lines.isEmpty()) { return isCacheLockOld(lockFolder); } @@ -621,8 +640,12 @@ private static boolean isCacheLockStale(File lockFolder) throws IOException { } private static boolean isCacheLockOld(File lockFolder) throws IOException { - return Files.getLastModifiedTime(lockFolder.toPath()).toInstant() - .isBefore(Instant.now().minus(CACHE_LOCK_STALE_MAX_AGE)); + try { + return Files.getLastModifiedTime(lockFolder.toPath()).toInstant() + .isBefore(Instant.now().minus(CACHE_LOCK_STALE_MAX_AGE)); + } catch (NoSuchFileException e) { + return true; + } } private static void waitForCacheLock() throws IOException { @@ -634,31 +657,56 @@ private static void waitForCacheLock() throws IOException { } } - private static void deleteCacheLock(File lockFolder) throws IOException { - Files.deleteIfExists(new File(lockFolder, CACHE_LOCK_OWNER_FILENAME).toPath()); - Files.deleteIfExists(lockFolder.toPath()); + private static void recoverStaleCacheLock(File lockFolder) throws IOException { + if (!lockFolder.isDirectory()) { + return; + } + + var ownerFiles = lockFolder.listFiles(File::isFile); + if (ownerFiles == null) { + return; + } + + for (var ownerFile : ownerFiles) { + if (isCacheLockOwnerStale(lockFolder, ownerFile)) { + Files.deleteIfExists(ownerFile.toPath()); + } + } + + deleteEmptyCacheLock(lockFolder); + } + + private static void deleteEmptyCacheLock(File lockFolder) throws IOException { + try { + Files.deleteIfExists(lockFolder.toPath()); + } catch (DirectoryNotEmptyException e) { + // A replacement owner claimed the lock while stale recovery was in progress. + } } + /** + * A temporary claim on one cache version. Each claim has its own owner marker, so releasing an old claim cannot + * remove a newer claim created after stale-lock recovery. + */ static final class CacheLock implements AutoCloseable { private final File lockFolder; + private final File ownerFile; - private CacheLock(File lockFolder) { + private CacheLock(File lockFolder, File ownerFile) { this.lockFolder = lockFolder; + this.ownerFile = ownerFile; + } + + File ownerFile() { + return ownerFile; } @Override public void close() { try { - var releasedFolder = new File(lockFolder.getParentFile(), - lockFolder.getName() + ".released-" + UUID.randomUUID()); - try { - Files.move(lockFolder.toPath(), releasedFolder.toPath(), StandardCopyOption.ATOMIC_MOVE); - } catch (AtomicMoveNotSupportedException e) { - Files.move(lockFolder.toPath(), releasedFolder.toPath()); - } - - deleteCacheLock(releasedFolder); + Files.deleteIfExists(ownerFile.toPath()); + deleteEmptyCacheLock(lockFolder); } catch (IOException e) { SpecsLogs.warn("Could not remove temporary clang-dumper cache lock '" + lockFolder + "'", e); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index ffa8d53ffd..03d8f9d579 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -448,7 +448,7 @@ public void cacheLockReleaseDoesNotRemoveAReclaimedLock() throws Exception { var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); ClangResources.CacheLock first = ClangResources.acquireCacheLock(versionFolder); - Files.writeString(lockFolder.resolve("owner"), Long.MAX_VALUE + System.lineSeparator()); + Files.writeString(first.ownerFile().toPath(), Long.MAX_VALUE + System.lineSeparator()); ClangResources.CacheLock second = ClangResources.acquireCacheLock(versionFolder); try { @@ -492,35 +492,6 @@ public void cachedUseDoesNotBypassTheCacheLockBeforeUpdatingLastUsed() throws Ex } } - @Test - public void staleCleanupDoesNotDeleteAnExecutableStillCachedInThisJvm() throws Exception { - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - var resources = new ClangResources(parser); - var versionFolder = resources.getClangResourceFolder(); - var fakeExecutable = Files.createFile(versionFolder.toPath().resolve("fake-tool")).toFile(); - Files.writeString(versionFolder.toPath().resolve("last-used.txt"), - Instant.now().minus(Duration.ofDays(61)).toString()); - - var cache = getClangFilesCache(); - var cacheKey = getClangFilesCacheKey(resources, LibcMode.SYSTEM); - cache.put(cacheKey, new ClangFiles(fakeExecutable, List.of())); - - var currentVersion = Files.createDirectory(tempFolder.resolve("current")); - var done = tempFolder.resolve("cleanup.done"); - var log = tempFolder.resolve("cleanup.log"); - Process cleanup = startCleanupProcess(tempFolder.toFile(), currentVersion.toFile(), done, log); - try { - waitForProcess(cleanup, log); - } finally { - stopProcess(cleanup); - cache.remove(cacheKey); - } - - assertTrue(fakeExecutable.isFile(), - "A separate JVM's stale cleanup deleted an executable still held by this JVM's cache"); - } - @Test public void sameJvmInstancesShareReleaseCacheInitialization() throws Exception { var firstParser = CodeParser.newInstance(); @@ -591,26 +562,6 @@ private Process startResourceProcess(File cacheFolder, Path done, Path log) thro .start(); } - private Process startCleanupProcess(File cacheFolder, File currentVersion, Path done, Path log) - throws IOException { - - var javaExecutable = Path.of(System.getProperty("java.home"), "bin", - SupportedPlatform.getCurrentPlatform().isWindows() ? "java.exe" : "java"); - - return new ProcessBuilder( - javaExecutable.toString(), - "-cp", - System.getProperty("java.class.path"), - CacheLockProcess.class.getName(), - "cleanup", - cacheFolder.getAbsolutePath(), - currentVersion.getAbsolutePath(), - done.toAbsolutePath().toString()) - .redirectErrorStream(true) - .redirectOutput(log.toFile()) - .start(); - } - @SuppressWarnings("unchecked") private Map getClangFilesCache() throws ReflectiveOperationException { var cacheField = ClangResources.class.getDeclaredField("CLANG_FILES_CACHE"); @@ -682,14 +633,6 @@ public static void main(String[] args) throws Exception { return; } - if (args[0].equals("cleanup")) { - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, new File(args[1])); - new ClangResources(parser).deleteStaleVersions(Instant.now(), new File(args[2])); - Files.writeString(Path.of(args[3]), "done"); - return; - } - var versionFolder = new File(args[0]); var acquired = Path.of(args[1]); var release = Path.of(args[2]); From 66e00badfd32f91758be9c7d120e09a0b7084270 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 14:31:53 +0100 Subject: [PATCH 56/99] fix: use system libc for local clang-dumper builds --- ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java | 6 +++++- ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java | 4 ++-- .../src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java | 5 ++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java index ec17d8a8d6..ad90e67c91 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java @@ -16,6 +16,7 @@ import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.Datakey.KeyFactory; import org.suikasoft.jOptions.Interfaces.DataStore; +import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaOptions; import pt.up.fe.specs.clava.language.Standard; @@ -32,7 +33,10 @@ public interface ClangAstKeys { */ DataKey LIBC_CXX_MODE = KeyFactory.enumeration("libcCxxMode", LibcMode.class) .setLabel("Libc/Libc++ mode. builtin (default): uses built-in libc/libc++; system: uses includes available in the system; auto: detects if the built-in includes are needed") - .setDefault(() -> LibcMode.BUILTIN_AND_LIBC); + .setDefault(() -> LibcMode.BUILTIN_AND_LIBC) + .setCustomGetter((mode, ignored) -> ClangAstWebResource.getDumperSource() instanceof LocalBuild + ? LibcMode.SYSTEM + : mode); DataKey USES_CILK = KeyFactory.bool("usesCilk"); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 75d7535331..e57aab8002 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -146,14 +146,14 @@ private static List getConventionalCudaRoots() { public ClangFiles getClangFiles(LibcMode libcMode) { var source = ClangAstWebResource.getDumperSource(); - var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); if (source instanceof LocalBuild localBuild) { - var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source; + var key = source.toString(); return CLANG_FILES_CACHE.computeIfAbsent(key, ignored -> new ClangFiles(getLocalExecutable(localBuild.folder()), List.of())); } + var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); var resourceFolder = getClangResourceFolder(); var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" + resourceFolder.getAbsolutePath(); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 441ef8d579..21b5ed4156 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -347,9 +347,8 @@ else if (SourceType.isHeader(sourceFile)) { arguments.add(standard.isCxx() ? "c++" : "c"); } - // Bundled include directories replace system headers when built-in libc/cxx is required. - if (!builtinIncludes.isEmpty() - && ClangResources.useBuiltinLibc(clangExecutable, config.get(ClangAstKeys.LIBC_CXX_MODE))) { + // If it was determined that built-in includes will be used, disable system includes + if (ClangResources.useBuiltinLibc(clangExecutable, config.get(ClangAstKeys.LIBC_CXX_MODE))) { arguments.add("-nostdinc"); arguments.add("-nostdinc++"); } From 41228cccc81e9106d0d2475fb15bf68d379473a0 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 14:57:32 +0100 Subject: [PATCH 57/99] refactor: simplify clang dumper resource setup --- .../pt/up/fe/specs/clang/ClangAstKeys.java | 9 +- .../fe/specs/clang/ClangAstWebResource.java | 27 ++++- .../pt/up/fe/specs/clang/ClangResources.java | 111 +++++++----------- .../fe/specs/clang/dumper/ClangAstDumper.java | 8 +- .../up/fe/specs/clang/ClangResourcesTest.java | 18 +++ 5 files changed, 90 insertions(+), 83 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java index ad90e67c91..1fef8afdf7 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java @@ -33,10 +33,7 @@ public interface ClangAstKeys { */ DataKey LIBC_CXX_MODE = KeyFactory.enumeration("libcCxxMode", LibcMode.class) .setLabel("Libc/Libc++ mode. builtin (default): uses built-in libc/libc++; system: uses includes available in the system; auto: detects if the built-in includes are needed") - .setDefault(() -> LibcMode.BUILTIN_AND_LIBC) - .setCustomGetter((mode, ignored) -> ClangAstWebResource.getDumperSource() instanceof LocalBuild - ? LibcMode.SYSTEM - : mode); + .setDefault(() -> LibcMode.BUILTIN_AND_LIBC); DataKey USES_CILK = KeyFactory.bool("usesCilk"); @@ -102,6 +99,10 @@ static DataStore toDataStore(List flags) { config.add(ClavaOptions.FLAGS_LIST, parsedFlags); + if (ClangAstWebResource.getDumperSource() instanceof LocalBuild) { + config.set(ClangAstKeys.LIBC_CXX_MODE, LibcMode.SYSTEM); + } + return config; } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index c48f982528..f1fc8775dc 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -18,6 +18,8 @@ import pt.up.fe.specs.util.providers.WebResourceProvider; import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.List; import java.util.Objects; @@ -30,18 +32,29 @@ public final class ClangAstWebResource { public static final String MANIFEST_FILENAME = "clang-dumper-release-manifest.json"; private static final Gson GSON = new Gson(); + private static final DumperSource DUMPER_SOURCE = readDumperSource(); private ClangAstWebResource() { } public static DumperSource getDumperSource() { + return DUMPER_SOURCE; + } + + private static DumperSource readDumperSource() { var inputStream = ClangAstWebResource.class.getClassLoader().getResourceAsStream(RELEASE_TAG_RESOURCE); if (inputStream == null) { throw new RuntimeException("Could not find resource '" + RELEASE_TAG_RESOURCE + "'"); } - var value = SpecsIo.read(inputStream).trim(); + String value; + try (inputStream) { + value = SpecsIo.read(inputStream).trim(); + } catch (IOException e) { + throw new UncheckedIOException("Could not read resource '" + RELEASE_TAG_RESOURCE + "'", e); + } + if (value.isBlank()) { throw new RuntimeException("Resource '" + RELEASE_TAG_RESOURCE + "' is empty"); } @@ -73,7 +86,9 @@ public static String getReleaseTag() { } public static ClangDumperManifest getManifest(File resourceFolder) { - var manifestResource = WebResourceProvider.newInstance(getReleaseBaseUrl(), MANIFEST_FILENAME, getReleaseTag()); + var releaseTag = getReleaseTag(); + var manifestResource = WebResourceProvider.newInstance(getReleaseBaseUrl(releaseTag), MANIFEST_FILENAME, + releaseTag); var manifestFile = manifestResource.writeVersioned(resourceFolder, ClangAstWebResource.class).getFile(); var manifest = GSON.fromJson(SpecsIo.read(manifestFile), ClangDumperManifest.class); @@ -86,11 +101,13 @@ public static ClangDumperManifest getManifest(File resourceFolder) { } public static WebResourceProvider getAssetResource(ClangDumperManifestAsset asset) { - return WebResourceProvider.newInstance(getReleaseBaseUrl(), asset.filename(), getReleaseTag() + "-" + asset.sha256()); + var releaseTag = getReleaseTag(); + return WebResourceProvider.newInstance(getReleaseBaseUrl(releaseTag), asset.filename(), + releaseTag + "-" + asset.sha256()); } - private static String getReleaseBaseUrl() { - return RELEASE_ROOT + getReleaseTag() + "/"; + private static String getReleaseBaseUrl(String releaseTag) { + return RELEASE_ROOT + releaseTag + "/"; } public sealed interface DumperSource permits Release, LocalBuild { diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index e57aab8002..52e504eee8 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -28,6 +28,7 @@ import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.io.UncheckedIOException; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.FileAlreadyExistsException; @@ -35,6 +36,7 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Duration; @@ -42,7 +44,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; -import java.util.HashSet; import java.util.HexFormat; import java.util.List; import java.util.Map; @@ -50,8 +51,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import java.util.regex.Pattern; public class ClangResources { @@ -67,7 +66,7 @@ public class ClangResources { private final static Duration CACHE_LOCK_STALE_MAX_AGE = Duration.ofMinutes(5); private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); - private static final AtomicInteger HAS_LIBC = new AtomicInteger(-1); + private static final Map HAS_LIBC = new ConcurrentHashMap<>(); private final CodeParser options; @@ -148,9 +147,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var source = ClangAstWebResource.getDumperSource(); if (source instanceof LocalBuild localBuild) { - var key = source.toString(); - return CLANG_FILES_CACHE.computeIfAbsent(key, - ignored -> new ClangFiles(getLocalExecutable(localBuild.folder()), List.of())); + return new ClangFiles(getLocalExecutable(localBuild.folder()), List.of()); } var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); @@ -265,65 +262,49 @@ public static boolean useBuiltinLibc(File clangExecutable, LibcMode libcMode) { } private static boolean hasLibC(File clangExecutable) { - var value = HAS_LIBC.get(); - - if (value == -1) { - var hasLibC = detectLibC(clangExecutable); - value = hasLibC ? 1 : 0; - HAS_LIBC.set(value); - } - - if (value == 0) { - return false; - } - - if (value == 1) { - return true; - } - - throw new RuntimeException("Unexpected value: '" + value + "'"); + var executableKey = SpecsIo.getCanonicalPath(clangExecutable); + return HAS_LIBC.computeIfAbsent(executableKey, ignored -> detectLibC(clangExecutable)); } private static boolean detectLibC(File clangExecutable) { - File clangTest = SpecsIo.mkdir(SpecsIo.getTempFolder(), "clang_ast_test"); + File clangTest = SpecsIo.getTempFolder("clang_ast_test_" + UUID.randomUUID()); - List testFiles = Arrays.asList(ClangAstResource.TEST_INCLUDES_C, ClangAstResource.TEST_INCLUDES_CPP) - .stream() - .map(resource -> resource.write(clangTest)) - .collect(Collectors.toList()); - - boolean needsLib = false; - for (File testFile : testFiles) { - var output = runClangAstDumper(clangExecutable, testFile); - - if (output.getReturnValue() != 0) { - ClavaLog.info("Problems while running dumper to test if libc/libcxx is needed"); - needsLib = true; - break; - } + try { + var testFiles = List.of( + ClangAstResource.TEST_INCLUDES_C.write(clangTest), + ClangAstResource.TEST_INCLUDES_CPP.write(clangTest)); + + boolean needsLib = false; + for (var testFile : testFiles) { + var output = runClangAstDumper(clangExecutable, testFile); + + if (output.getReturnValue() != 0) { + ClavaLog.info("Problems while running dumper to test if libc/libcxx is needed"); + needsLib = true; + break; + } - if (!testFile.getName().endsWith(".cpp")) { - continue; + if (testFile.getName().endsWith(".cpp") + && !output.getOutput().contains(TopLevelNodesParser.getTopLevelNodesHeader())) { + needsLib = true; + break; + } } - var topLevelNodesHeader = TopLevelNodesParser.getTopLevelNodesHeader(); - if (!output.getOutput().contains(topLevelNodesHeader)) { - needsLib = true; - break; + if (needsLib) { + ClavaLog.debug("Could not find system libc/libcxx"); + } else { + ClavaLog.debug("Detected system's libc and libcxx"); } - } - if (needsLib) { - ClavaLog.debug("Could not find system libc/libcxx"); - } else { - ClavaLog.debug("Detected system's libc and libcxx"); + return !needsLib; + } finally { + SpecsIo.deleteFolder(clangTest); } - - return !needsLib; } private static ProcessOutputAsString runClangAstDumper(File clangExecutable, File testFile) { - List arguments = Arrays.asList(clangExecutable.getAbsolutePath(), testFile.getAbsolutePath(), "--"); + List arguments = List.of(clangExecutable.getAbsolutePath(), testFile.getAbsolutePath(), "--"); return SpecsSystem.runProcess(arguments, true, false); } @@ -431,12 +412,12 @@ static boolean isIncludesCacheValid(File includesFolder) { } private void validateTopLevelCacheFiles(ClangDumperManifest manifest, File resourceFolder) { - Set expectedNames = new HashSet<>(); - expectedNames.add(ClangAstWebResource.MANIFEST_FILENAME); var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; - expectedNames.add(getCurrentAsset(manifest, executableKind).filename()); - expectedNames.add(INCLUDES_FOLDERNAME); - expectedNames.add(LAST_USED_FILENAME); + Set expectedNames = Set.of( + ClangAstWebResource.MANIFEST_FILENAME, + getCurrentAsset(manifest, executableKind).filename(), + INCLUDES_FOLDERNAME, + LAST_USED_FILENAME); var files = resourceFolder.listFiles(); if (files == null) { @@ -720,18 +701,8 @@ private static boolean hasExpectedSha256(File file, ClangDumperManifestAsset ass private static String calculateSha256(File file) { try { var digest = MessageDigest.getInstance("SHA-256"); - try (var inputStream = Files.newInputStream(file.toPath())) { - inputStream.transferTo(new java.io.OutputStream() { - @Override - public void write(int b) { - digest.update((byte) b); - } - - @Override - public void write(byte[] b, int off, int len) { - digest.update(b, off, len); - } - }); + try (var inputStream = new DigestInputStream(Files.newInputStream(file.toPath()), digest)) { + inputStream.transferTo(OutputStream.nullOutputStream()); } return HexFormat.of().formatHex(digest.digest()); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 21b5ed4156..fae30774ec 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -72,7 +72,7 @@ public static boolean usePlugin() { private static final AtomicBoolean CUDA_UNAVAILABLE_WARNING_SHOWN = new AtomicBoolean(); private static final Object CUDA_COMPATIBILITY_LOCK = new Object(); - private static final List CLANG_AST_DUMPER_TEMP_FILES = Arrays.asList("includes.txt", CLANG_DUMP_FILENAME, + private static final List CLANG_AST_DUMPER_TEMP_FILES = List.of("includes.txt", CLANG_DUMP_FILENAME, // "clavaDump.txt", "nodetypes.txt", "types.txt", "is_temporary.txt", "template_args.txt", "clavaDump.txt", "nodetypes.txt", "types.txt", "is_temporary.txt", "omp.txt", "invalid_source.txt", "enum_integer_type.txt", "consumer_order.txt", @@ -157,7 +157,7 @@ private static File writeCudaCompatibilityFile(File destination) { private File clangExecutable; private List builtinIncludes; private int systemIncludesThreshold; - private ClangResources clangResources; + private final ClangResources clangResources; private final CodeParser parserConfig; @@ -392,7 +392,7 @@ else if (SourceType.isHeader(sourceFile)) { workingFolders.add(lastWorkingFolder); output = SpecsSystem.runProcess(arguments, lastWorkingFolder, - inputStream -> this.processOutput(sourceFile, inputStream), + this::processOutput, inputStream -> this.processStdErr(inputStream, config.get(ClavaNode.CONTEXT))); if (output.isError()) { @@ -429,7 +429,7 @@ else if (SourceType.isHeader(sourceFile)) { return parsedData; } - private String processOutput(File sourceFile, InputStream inputStream) { + private String processOutput(InputStream inputStream) { StringBuilder output = new StringBuilder(); try (LineStream lines = LineStream.newInstance(inputStream, null)) { diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 03d8f9d579..8dd92e73cf 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -40,6 +40,7 @@ import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; import pt.up.fe.specs.clang.ClangAstWebResource.Release; import pt.up.fe.specs.clang.codeparser.CodeParser; +import pt.up.fe.specs.clang.parsers.TopLevelNodesParser; public class ClangResourcesTest { @@ -658,4 +659,21 @@ public void builtinCudaIncludesAreAvailableWithSystemLibc() { assertTrue(hasCudaWrapper, "Built-in CUDA must provide Clang's CUDA runtime wrapper independently of libc"); } + + @Test + public void libcDetectionIsScopedToTheExecutable() throws IOException { + assumeTrue(!SupportedPlatform.getCurrentPlatform().isWindows(), "Shell fixtures require a Unix executable"); + + var systemLibcDumper = tempFolder.resolve("system-libc-dumper"); + Files.writeString(systemLibcDumper, + "#!/bin/sh\nprintf '%s\\n' '" + TopLevelNodesParser.getTopLevelNodesHeader() + "'\n"); + assertTrue(systemLibcDumper.toFile().setExecutable(true)); + + var builtinLibcDumper = tempFolder.resolve("builtin-libc-dumper"); + Files.writeString(builtinLibcDumper, "#!/bin/sh\nexit 1\n"); + assertTrue(builtinLibcDumper.toFile().setExecutable(true)); + + assertFalse(ClangResources.useBuiltinLibc(systemLibcDumper.toFile(), LibcMode.AUTO)); + assertTrue(ClangResources.useBuiltinLibc(builtinLibcDumper.toFile(), LibcMode.AUTO)); + } } From 09a0d0ed2d5579c021fae6fed5a062ccf1d3df69 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 15:39:08 +0100 Subject: [PATCH 58/99] fix: preserve unevaluated string literal spelling Add a C++26 round-trip regression fixture covering ordinary text, simple escapes, and UCNs. Preserve Clang's source spelling for unevaluated strings because their byte payload cannot be safely re-emitted with numeric escapes, and recognize the c++2c standard used by the test. --- .../test-resources/cxx/unevaluated_strings.cpp | 3 +++ .../test-resources/cxx/unevaluated_strings.cpp.txt | 3 +++ .../pt/up/fe/specs/clang/parser/tests/CxxTest.java | 7 +++++++ .../pt/up/fe/specs/clava/ast/expr/StringLiteral.java | 10 ++++++++-- .../src/pt/up/fe/specs/clava/language/Standard.java | 12 ++++++++---- 5 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 ClangAstParser/test-resources/cxx/unevaluated_strings.cpp create mode 100644 ClangAstParser/test-resources/cxx/unevaluated_strings.cpp.txt diff --git a/ClangAstParser/test-resources/cxx/unevaluated_strings.cpp b/ClangAstParser/test-resources/cxx/unevaluated_strings.cpp new file mode 100644 index 0000000000..b808d729be --- /dev/null +++ b/ClangAstParser/test-resources/cxx/unevaluated_strings.cpp @@ -0,0 +1,3 @@ +static_assert(true, "plain message"); +static_assert(true, "line\nbreak"); +static_assert(true, "\u00e9"); diff --git a/ClangAstParser/test-resources/cxx/unevaluated_strings.cpp.txt b/ClangAstParser/test-resources/cxx/unevaluated_strings.cpp.txt new file mode 100644 index 0000000000..b808d729be --- /dev/null +++ b/ClangAstParser/test-resources/cxx/unevaluated_strings.cpp.txt @@ -0,0 +1,3 @@ +static_assert(true, "plain message"); +static_assert(true, "line\nbreak"); +static_assert(true, "\u00e9"); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxTest.java index b8cdfe5726..7de8c90424 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxTest.java @@ -287,6 +287,13 @@ public void testStrings() { new CxxTester("strings.cpp").test(); } + @Test + public void testUnevaluatedStrings() { + new CxxTester("unevaluated_strings.cpp") + .addFlags("-std=c++26") + .test(); + } + // -Xclang-ast-dump-nostdinc-nocudalib-nocudainc--cuda-gpu-arch=sm_30 // "--cuda-device-only" diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java index 9cf85bec42..10e4078fe6 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java @@ -55,6 +55,12 @@ public StringLiteral(DataStore data, Collection children) { @Override public String getLiteral() { + // C++26 unevaluated strings only allow simple escapes and UCNs. The evaluated byte payload does not retain + // enough information to reconstruct those spellings, so keep Clang's validated source spelling. + if (get(STRING_KIND) == StringKind.UNEVALUATED) { + return super.getLiteral(); + } + // Update: Unfortunately it is not possible to blindly use the source code literal // For instance, if directives and macros appear in the middle of the literal, they will also appear in the // generated source code @@ -90,8 +96,8 @@ private String getStringFromBytes() { return SpecsStrings.escapeJson(new String(bytes, kind.getCharset())); } - // If ASCII, convert each byte directly - if (kind == StringKind.ORDINARY || kind == StringKind.UNEVALUATED) { + // Ordinary strings use one-byte characters; convert each byte directly. + if (kind == StringKind.ORDINARY) { var literal = new StringBuilder(); for (var aByte : get(STRING_BYTES)) { diff --git a/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java b/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java index f05370f1bb..6240c2d93a 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java @@ -42,12 +42,16 @@ public enum Standard implements StringProvider { CXX11("c++11", true), CXX14("c++14", true), CXX17("c++17", true), - CXX2A("c++2a", true), + CXX20("c++20", true), + CXX23("c++23", true), + CXX26("c++26", true), GNUXX98("gnu++98", true), GNUXX11("gnu++11", true), GNUXX14("gnu++14", true), GNUXX17("gnu++17", true), - GNUXX2A("gnu++2a", true), + GNUXX20("gnu++20", true), + GNUXX23("gnu++23", true), + GNUXX26("gnu++26", true), OPENCL10("cl1.0"), OPENCL12("cl1.2"), OPENCL20("cl2.0"), @@ -63,8 +67,8 @@ public enum Standard implements StringProvider { private static final Lazy> ENUM_HELPER = EnumHelperWithValue .newLazyHelperWithValue(Standard.class); - private static final Set GNU_STANDARDS = SpecsCollections.asSet(GNU90, GNU99, GNU11, GNUXX98, GNUXX11, - GNUXX14); + private static final Set GNU_STANDARDS = SpecsCollections.asSet(GNU90, GNU99, GNU11, GNU17, GNUXX98, GNUXX11, + GNUXX14, GNUXX17, GNUXX20, GNUXX23, GNUXX26); public static EnumHelperWithValue getEnumHelper() { return ENUM_HELPER.get(); From ebbc2ddd09f7ded40d689dbbc80939f194a1b63e Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 16:04:00 +0100 Subject: [PATCH 59/99] fix: preserve unevaluated string source spelling Keep Clangs validated source spelling for UNEVALUATED literals so adjacent tokens and source line breaks survive code generation. Update the dependent-scope golden accordingly. --- .../test-resources/cxx/dependent_scope_decl_ref_expr.cpp.txt | 3 ++- ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ClangAstParser/test-resources/cxx/dependent_scope_decl_ref_expr.cpp.txt b/ClangAstParser/test-resources/cxx/dependent_scope_decl_ref_expr.cpp.txt index cce55922e1..5b84d42dc7 100644 --- a/ClangAstParser/test-resources/cxx/dependent_scope_decl_ref_expr.cpp.txt +++ b/ClangAstParser/test-resources/cxx/dependent_scope_decl_ref_expr.cpp.txt @@ -7,5 +7,6 @@ void compute_boundaries(FloatType value) { // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) // If v is normalized: // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) - static_assert(std::numeric_limits::is_iec559, "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + static_assert(std::numeric_limits::is_iec559, "internal error: dtoa_short requires an IEEE-754 " + "floating-point implementation"); } diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java index 10e4078fe6..8937042aee 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/StringLiteral.java @@ -55,8 +55,8 @@ public StringLiteral(DataStore data, Collection children) { @Override public String getLiteral() { - // C++26 unevaluated strings only allow simple escapes and UCNs. The evaluated byte payload does not retain - // enough information to reconstruct those spellings, so keep Clang's validated source spelling. + // Unevaluated strings can contain source-level details, such as adjacent tokens separated by a line break, that + // are lost when using the evaluated byte payload. Keep Clang's validated source spelling. if (get(STRING_KIND) == StringKind.UNEVALUATED) { return super.getLiteral(); } From 61be00d345a7b24668285b0a658427dc253d8767 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 16:40:34 +0100 Subject: [PATCH 60/99] fix: update C++ standard versions in Standard enum --- .../specs/clang/parser/tests/InitializationStyleTest.java | 2 +- ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java index 4a6207ae8c..91e1f2d286 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java @@ -39,7 +39,7 @@ public void parenthesizedListInitializationKeepsAllArguments() { SpecsSystem.programStandardInit(); File sourceFile = SpecsIo.resourceCopy("cxx/paren_list_initialization.cpp", tempFolder.toFile(), false, true); - App app = CodeParser.newInstance().parse(List.of(sourceFile), List.of("-std=c++2a")); + App app = CodeParser.newInstance().parse(List.of(sourceFile), List.of("-std=c++20")); VarDecl point = app.getDescendants(VarDecl.class).stream() .filter(varDecl -> varDecl.getDeclName().equals("point")) diff --git a/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java b/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java index 6240c2d93a..b35aca2dda 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/language/Standard.java @@ -33,10 +33,14 @@ public enum Standard implements StringProvider { C99, C11, C17, + C18, + C23, GNU90, GNU99, GNU11, GNU17, + GNU18, + GNU23, CXX98("c++98", true), CXX03("c++03", true), CXX11("c++11", true), @@ -67,8 +71,8 @@ public enum Standard implements StringProvider { private static final Lazy> ENUM_HELPER = EnumHelperWithValue .newLazyHelperWithValue(Standard.class); - private static final Set GNU_STANDARDS = SpecsCollections.asSet(GNU90, GNU99, GNU11, GNU17, GNUXX98, GNUXX11, - GNUXX14, GNUXX17, GNUXX20, GNUXX23, GNUXX26); + private static final Set GNU_STANDARDS = SpecsCollections.asSet(GNU90, GNU99, GNU11, GNU17, GNU18, GNU23, + GNUXX98, GNUXX11, GNUXX14, GNUXX17, GNUXX20, GNUXX23, GNUXX26); public static EnumHelperWithValue getEnumHelper() { return ENUM_HELPER.get(); From 58fcd62839d2c66f82be3aa21ce33df57fffb7a4 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 17:34:54 +0100 Subject: [PATCH 61/99] test: cover dependent unary transform types --- .../parser/tests/UnaryTransformTypeTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/UnaryTransformTypeTest.java diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/UnaryTransformTypeTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/UnaryTransformTypeTest.java new file mode 100644 index 0000000000..27d54bd00e --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/UnaryTransformTypeTest.java @@ -0,0 +1,85 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang.parser.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import pt.up.fe.specs.clang.codeparser.CodeParser; +import pt.up.fe.specs.clava.ast.extra.App; +import pt.up.fe.specs.clava.ast.type.Type; +import pt.up.fe.specs.clava.ast.type.UnaryTransformType; +import pt.up.fe.specs.clava.ast.type.enums.UnaryTransformTypeKind; +import pt.up.fe.specs.util.SpecsSystem; + +public class UnaryTransformTypeTest { + + private static final String SOURCE = """ + template + struct Dependent { + using type = __underlying_type(T); + }; + + enum Resolved { Value }; + using ResolvedType = __underlying_type(Resolved); + """; + + @TempDir + Path tempFolder; + + @Test + public void dependentTransformsMayHaveNoUnderlyingType() throws IOException { + SpecsSystem.programStandardInit(); + + File sourceFile = tempFolder.resolve("unary_transform_type.cpp").toFile(); + Files.writeString(sourceFile.toPath(), SOURCE); + + App app = CodeParser.newInstance().parse(List.of(sourceFile), List.of("-std=c++11")); + + List transforms = app.getDescendantsAndFields(UnaryTransformType.class).stream() + .filter(transform -> transform.get(UnaryTransformType.KIND) == UnaryTransformTypeKind.EnumUnderlyingType) + .toList(); + + assertEquals(2, transforms.size()); + + UnaryTransformType dependentTransform = transforms.stream() + .filter(transform -> transform.getUnderlyingType().isEmpty()) + .findFirst() + .orElseThrow(); + + assertNotNull(dependentTransform.getBaseType()); + assertEquals(1, dependentTransform.getNodeFields().size()); + assertTrue(dependentTransform.getNodeFields().contains(dependentTransform.getBaseType())); + + UnaryTransformType resolvedTransform = transforms.stream() + .filter(transform -> transform.getUnderlyingType().isPresent()) + .findFirst() + .orElseThrow(); + + Type resolvedUnderlyingType = resolvedTransform.getUnderlyingType().orElseThrow(); + assertNotNull(resolvedTransform.getBaseType()); + assertTrue(resolvedTransform.getNodeFields().contains(resolvedTransform.getBaseType())); + assertTrue(resolvedTransform.getNodeFields().contains(resolvedUnderlyingType)); + } +} From 611ea4a6714e2b7d4b3c10feb0c77b1941d61ab1 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 17:40:47 +0100 Subject: [PATCH 62/99] test: cover strict optional node resolution --- .../up/fe/specs/clang/parsers/ClavaNodes.java | 2 +- .../specs/clang/parsers/ClavaNodesTest.java | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/parsers/ClavaNodesTest.java diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java index fc58715dd7..30438176cc 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/ClavaNodes.java @@ -93,7 +93,7 @@ public Optional getOptional(String nodeId) { ClavaNode clavaNode = clavaNodes.get(nodeId); if (clavaNode == null) { - ClavaLog.debug("Could not find ClavaNode with id '" + nodeId + ClavaLog.warning("Could not find ClavaNode with id '" + nodeId + "', returning an empty optional. Check if node is being visited. If parsing of includes is enabled, check that the parsing level is sufficient."); return Optional.empty(); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parsers/ClavaNodesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parsers/ClavaNodesTest.java new file mode 100644 index 0000000000..f4a42736af --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parsers/ClavaNodesTest.java @@ -0,0 +1,71 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang.parsers; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.suikasoft.jOptions.Interfaces.DataStore; + +import pt.up.fe.specs.clava.ast.expr.Expr; +import pt.up.fe.specs.clava.context.ClavaContext; + +public class ClavaNodesTest { + + @Test + public void optionalNodeResolvesPresentNode() { + var context = new ClavaContext(); + var factory = context.getFactory(); + var clavaNodes = new ClavaNodes(factory); + DataStore data = factory.newDataStore(Expr.class); + var type = factory.nullType(); + + clavaNodes.getNodes().put("type-id", type); + clavaNodes.queueSetOptionalNode(data, Expr.TYPE, "type-id"); + clavaNodes.getQueuedActions().forEach(Runnable::run); + + assertSame(type, data.get(Expr.TYPE).orElseThrow()); + } + + @Test + public void optionalNodeUsesEmptyForExplicitNullId() { + var context = new ClavaContext(); + var factory = context.getFactory(); + var clavaNodes = new ClavaNodes(factory); + DataStore data = factory.newDataStore(Expr.class); + + clavaNodes.queueSetOptionalNode(data, Expr.TYPE, "nullptr_type"); + clavaNodes.getQueuedActions().forEach(Runnable::run); + + assertTrue(data.get(Expr.TYPE).isEmpty()); + } + + @Test + public void optionalNodeRejectsUnresolvedNodeId() { + var context = new ClavaContext(); + var factory = context.getFactory(); + var clavaNodes = new ClavaNodes(factory); + DataStore data = factory.newDataStore(Expr.class); + + clavaNodes.queueSetOptionalNode(data, Expr.TYPE, "missing-id"); + + var exception = assertThrows(NullPointerException.class, + () -> clavaNodes.getQueuedActions().forEach(Runnable::run)); + + assertTrue(exception.getMessage().contains("Could not resolve optional node 'missing-id'")); + assertTrue(exception.getMessage().contains("key 'type'")); + } +} From f1de83cd45ecbd7bc8342dd66ee1a02345980dea Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 18:28:50 +0100 Subject: [PATCH 63/99] test: assert parenthesized initialization style --- .../up/fe/specs/clang/parser/tests/InitializationStyleTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java index 91e1f2d286..5e1f99532b 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/InitializationStyleTest.java @@ -46,6 +46,7 @@ public void parenthesizedListInitializationKeepsAllArguments() { .findFirst() .orElseThrow(); + assertEquals(InitializationStyle.ParenListInit, point.get(VarDecl.INIT_STYLE)); assertEquals("Point point(1, 2)", point.getCode()); } From 452054ff0cf6ec728f8d51064bb4e2b2d2571bd4 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 20:13:34 +0100 Subject: [PATCH 64/99] fix: restore built-in CUDA archive parsing Use Clava's historical cudalib.zip with standard --cuda-path handling and remove the compatibility-header workaround. --- .../resources/clangast/cuda_compatibility.h | 9 --- .../up/fe/specs/clang/ClangAstResource.java | 3 +- .../fe/specs/clang/ClangAstWebResource.java | 4 + .../pt/up/fe/specs/clang/ClangResources.java | 23 ++++++ .../fe/specs/clang/dumper/ClangAstDumper.java | 79 ++----------------- .../up/fe/specs/clang/ClangResourcesTest.java | 13 +++ .../clang/dumper/ClangAstDumperTest.java | 61 -------------- .../specs/clang/parser/tests/CxxCudaTest.java | 8 +- 8 files changed, 47 insertions(+), 153 deletions(-) delete mode 100644 ClangAstParser/resources/clangast/cuda_compatibility.h delete mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java diff --git a/ClangAstParser/resources/clangast/cuda_compatibility.h b/ClangAstParser/resources/clangast/cuda_compatibility.h deleted file mode 100644 index f649810666..0000000000 --- a/ClangAstParser/resources/clangast/cuda_compatibility.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -extern "C" __host__ cudaError_t cudaConfigureCall( - dim3 gridDim, - dim3 blockDim, - size_t sharedMem = 0, - cudaStream_t stream = 0); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java index 8fef3eaef2..c0c53d5ca1 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstResource.java @@ -21,8 +21,7 @@ */ public enum ClangAstResource implements ResourceProvider { TEST_INCLUDES_C("test_includes.c"), - TEST_INCLUDES_CPP("test_includes.cpp"), - CUDA_COMPATIBILITY("cuda_compatibility.h"); + TEST_INCLUDES_CPP("test_includes.cpp"); private final String resource; diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index f1fc8775dc..6ec9d6c9b4 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -28,6 +28,10 @@ public final class ClangAstWebResource { private static final String RELEASE_ROOT = "https://github.com/specs-feup/clang-dumper/releases/download/"; + private static final String LEGACY_CUDA_RELEASE_ROOT = + "https://github.com/specs-feup/clava/releases/download/clang_ast_dumper_v12.0.7.1/"; + static final WebResourceProvider CUDA_LIB = + WebResourceProvider.newInstance(LEGACY_CUDA_RELEASE_ROOT, "cudalib.zip", "v11.3.0"); private static final String RELEASE_TAG_RESOURCE = "clang-dumper-release.tag"; public static final String MANIFEST_FILENAME = "clang-dumper-release-manifest.json"; diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 52e504eee8..ef8d0b9de7 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -23,6 +23,7 @@ import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsSystem; +import pt.up.fe.specs.util.lazy.Lazy; import pt.up.fe.specs.util.providers.FileResourceProvider.ResourceWriteData; import pt.up.fe.specs.util.system.ProcessOutputAsString; @@ -65,15 +66,35 @@ public class ClangResources { private final static Duration CACHE_LOCK_RETRY_INTERVAL = Duration.ofMillis(100); private final static Duration CACHE_LOCK_STALE_MAX_AGE = Duration.ofMinutes(5); private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); + private static final String CUDA_ARCHIVE_FILENAME = "cudalib.zip"; + private static final String CUDA_FOLDERNAME = "cudalib"; private static final Map HAS_LIBC = new ConcurrentHashMap<>(); private final CodeParser options; + private final Lazy builtinCudaLib = Lazy.newInstance(this::prepareBuiltinCudaLib); public ClangResources(CodeParser options) { this.options = options; } + public File getBuiltinCudaLib() { + return builtinCudaLib.get(); + } + + private File prepareBuiltinCudaLib() { + var resourceFolder = getClangResourceFolder(); + var cudaFolder = SpecsIo.mkdir(new File(resourceFolder, CUDA_FOLDERNAME)); + var zipFile = ClangAstWebResource.CUDA_LIB.writeVersioned(resourceFolder, ClangResources.class); + + if (zipFile.isNewFile() || !isCudaInstallation(cudaFolder)) { + SpecsIo.deleteFolderContents(cudaFolder); + SpecsIo.extractZip(zipFile.getFile(), cudaFolder); + } + + return cudaFolder; + } + public Optional getSystemCudaInstallation() { return findCudaInstallation( Arrays.asList(System.getenv("CUDA_HOME"), System.getenv("CUDA_PATH"), System.getenv("CUDA_ROOT")), @@ -417,6 +438,8 @@ private void validateTopLevelCacheFiles(ClangDumperManifest manifest, File resou ClangAstWebResource.MANIFEST_FILENAME, getCurrentAsset(manifest, executableKind).filename(), INCLUDES_FOLDERNAME, + CUDA_ARCHIVE_FILENAME, + CUDA_FOLDERNAME, LAST_USED_FILENAME); var files = resourceFolder.listFiles(); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index fae30774ec..ea31b5da86 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -17,7 +17,6 @@ import org.suikasoft.jOptions.JOptionsUtils; import org.suikasoft.jOptions.streamparser.LineStreamParser; import pt.up.fe.specs.clang.ClangAstKeys; -import pt.up.fe.specs.clang.ClangAstResource; import pt.up.fe.specs.clang.ClangResources; import pt.up.fe.specs.clang.cilk.CilkParser; import pt.up.fe.specs.clang.codeparser.CodeParser; @@ -39,14 +38,7 @@ import pt.up.fe.specs.util.utilities.LineStream; import java.io.File; -import java.io.IOException; import java.io.InputStream; -import java.io.UncheckedIOException; -import java.nio.file.AtomicMoveNotSupportedException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -70,7 +62,6 @@ public static boolean usePlugin() { private final static String CLANG_DUMP_FILENAME = "clangDump.txt"; private final static String STDERR_DUMP_FILENAME = "stderr.txt"; private static final AtomicBoolean CUDA_UNAVAILABLE_WARNING_SHOWN = new AtomicBoolean(); - private static final Object CUDA_COMPATIBILITY_LOCK = new Object(); private static final List CLANG_AST_DUMPER_TEMP_FILES = List.of("includes.txt", CLANG_DUMP_FILENAME, // "clavaDump.txt", "nodetypes.txt", "types.txt", "is_temporary.txt", "template_args.txt", @@ -82,65 +73,6 @@ public static List getTempFiles() { return CLANG_AST_DUMPER_TEMP_FILES; } - static File getCudaCompatibilityFile(File folder) { - var destination = new File(folder, ClangAstResource.CUDA_COMPATIBILITY.getFilename()); - if (destination.isFile()) { - return destination; - } - - synchronized (CUDA_COMPATIBILITY_LOCK) { - if (destination.isFile()) { - return destination; - } - - return writeCudaCompatibilityFile(destination); - } - } - - private static File writeCudaCompatibilityFile(File destination) { - var destinationPath = destination.toPath(); - Path temporaryPath = null; - - try { - Files.createDirectories(destinationPath.getParent()); - temporaryPath = Files.createTempFile(destinationPath.getParent(), destination.getName(), ".tmp"); - - try (var inputStream = ClangAstResource.CUDA_COMPATIBILITY.toStream()) { - if (inputStream == null) { - throw new IOException("Could not load CUDA compatibility resource"); - } - - Files.copy(inputStream, temporaryPath, StandardCopyOption.REPLACE_EXISTING); - } - - try { - Files.move(temporaryPath, destinationPath, StandardCopyOption.ATOMIC_MOVE); - } catch (AtomicMoveNotSupportedException e) { - try { - Files.move(temporaryPath, destinationPath); - } catch (FileAlreadyExistsException ignored) { - // Another JVM installed the complete file first. - } - } catch (FileAlreadyExistsException ignored) { - // Another JVM installed the complete file first. - } - - return destination; - } catch (IOException e) { - throw new UncheckedIOException("Could not write CUDA compatibility resource '" - + destination + "'", e); - } finally { - if (temporaryPath != null) { - try { - Files.deleteIfExists(temporaryPath); - } catch (IOException e) { - SpecsLogs.warn("Could not delete temporary CUDA compatibility resource '" - + temporaryPath + "'", e); - } - } - } - } - /** * TODO: Not implemented yet *

    @@ -310,12 +242,11 @@ else if (isCuda) { var useBuiltinCudaLib = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()); if (useBuiltinCudaLib && !builtinIncludes.isEmpty()) { - arguments.add("-nocudainc"); - arguments.add("-nocudalib"); - arguments.add("-include"); - arguments.add("__clang_cuda_runtime_wrapper.h"); - arguments.add("-include"); - arguments.add(getCudaCompatibilityFile(SpecsIo.getTempFolder()).getAbsolutePath()); + File cudaFolder = clangResources.getBuiltinCudaLib(); + + ClavaLog.debug("Setting --cuda-path to built-in CUDA folder '" + + cudaFolder.getAbsolutePath() + "'"); + arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); } else if (useBuiltinCudaLib) { clangResources.getSystemCudaInstallation().ifPresentOrElse(cudaFolder -> { ClavaLog.debug("Setting --cuda-path to discovered CUDA folder '" diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 8dd92e73cf..5fae23877f 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -660,6 +660,19 @@ public void builtinCudaIncludesAreAvailableWithSystemLibc() { assertTrue(hasCudaWrapper, "Built-in CUDA must provide Clang's CUDA runtime wrapper independently of libc"); } + @Test + public void builtinCudaArchiveHasCanonicalInstallationLayout() { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + parser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + + var cudaFolder = new ClangResources(parser).getBuiltinCudaLib(); + + assertTrue(new File(cudaFolder, "include/cuda.h").isFile()); + assertTrue(new File(cudaFolder, "include/cuda_runtime.h").isFile()); + assertTrue(new File(cudaFolder, "nvvm/libdevice/libdevice.10.bc").isFile()); + } + @Test public void libcDetectionIsScopedToTheExecutable() throws IOException { assumeTrue(!SupportedPlatform.getCurrentPlatform().isWindows(), "Shell fixtures require a Unix executable"); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java deleted file mode 100644 index 7e0dbe140f..0000000000 --- a/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2026 SPeCS. - *

    - * 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 - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * 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 pt.up.fe.specs.clang.dumper; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import pt.up.fe.specs.clang.ClangAstResource; - -public class ClangAstDumperTest { - - @TempDir - Path tempFolder; - - @Test - public void cudaCompatibilityFileIsCompleteWhenRequestedConcurrently() throws Exception { - ExecutorService executor = Executors.newFixedThreadPool(8); - try { - var futures = new ArrayList>(); - for (int index = 0; index < 32; index++) { - futures.add(executor.submit(() -> ClangAstDumper.getCudaCompatibilityFile(tempFolder.toFile()))); - } - - Set files = new HashSet<>(); - for (var future : futures) { - files.add(future.get(10, TimeUnit.SECONDS)); - } - - var expectedFile = tempFolder.resolve(ClangAstResource.CUDA_COMPATIBILITY.getFilename()).toFile(); - assertEquals(Set.of(expectedFile), files); - assertEquals(ClangAstResource.CUDA_COMPATIBILITY.read(), Files.readString(expectedFile.toPath())); - } finally { - executor.shutdownNow(); - executor.awaitTermination(10, TimeUnit.SECONDS); - } - } -} diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java index 1a6b37d3f7..4960a32dda 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java @@ -17,13 +17,7 @@ import pt.up.fe.specs.clang.parser.CxxCudaTester; -/** - * Disabled tests, they are failing in the CI server. Even when passing the --cuda-path built-in library, the parser - * fails to find the CUDA library. - * - * @author JBispo - * - */ +/** Verifies built-in CUDA parsing through Clava's historical cudalib archive. */ public class CxxCudaTest { @Test public void testAtomicAdd() { From 91f74d14a5886f63688e9570eac2af76cc1faead Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 20:20:25 +0100 Subject: [PATCH 65/99] fix: always use built-in CUDA archive Select Clava's historical CUDA archive whenever the built-in CUDA option is requested, including configurations with empty bundled libc includes. --- .../fe/specs/clang/dumper/ClangAstDumper.java | 46 +++++++---------- .../clang/dumper/ClangAstDumperTest.java | 51 +++++++++++++++++++ 2 files changed, 69 insertions(+), 28 deletions(-) create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index ea31b5da86..7554b526f6 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -43,7 +43,6 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; /** * Calls the ClangAstDumper executable and returns the dumped information. Clava AST can be built based on this output. @@ -61,7 +60,6 @@ public static boolean usePlugin() { private final static String CLANG_DUMP_FILENAME = "clangDump.txt"; private final static String STDERR_DUMP_FILENAME = "stderr.txt"; - private static final AtomicBoolean CUDA_UNAVAILABLE_WARNING_SHOWN = new AtomicBoolean(); private static final List CLANG_AST_DUMPER_TEMP_FILES = List.of("includes.txt", CLANG_DUMP_FILENAME, // "clavaDump.txt", "nodetypes.txt", "types.txt", "is_temporary.txt", "template_args.txt", @@ -239,32 +237,7 @@ else if (isCuda) { arguments.add("--cuda-gpu-arch=" + parserConfig.get(CodeParser.CUDA_GPU_ARCH)); var cudaPath = parserConfig.get(CodeParser.CUDA_PATH); - var useBuiltinCudaLib = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()); - - if (useBuiltinCudaLib && !builtinIncludes.isEmpty()) { - File cudaFolder = clangResources.getBuiltinCudaLib(); - - ClavaLog.debug("Setting --cuda-path to built-in CUDA folder '" - + cudaFolder.getAbsolutePath() + "'"); - arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); - } else if (useBuiltinCudaLib) { - clangResources.getSystemCudaInstallation().ifPresentOrElse(cudaFolder -> { - ClavaLog.debug("Setting --cuda-path to discovered CUDA folder '" - + cudaFolder.getAbsolutePath() + "'"); - arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); - }, () -> { - if (CUDA_UNAVAILABLE_WARNING_SHOWN.compareAndSet(false, true)) { - ClavaLog.warning("CUDA parsing requested, but no system CUDA toolkit was found. " - + "Continuing without CUDA headers; CUDA parsing may fail."); - } - }); - } else if (!useBuiltinCudaLib && !cudaPath.isBlank()) { - - File cudaFolder = SpecsIo.existingFolder(cudaPath); - - ClavaLog.debug("Setting --cuda-path to folder '" + cudaFolder.getAbsolutePath() + "'"); - arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); - } + addCudaPathArgument(arguments, cudaPath); // Since we only need parsing, enable host-only // Can help with errors such as "__float128 is not supported on this target" @@ -360,6 +333,23 @@ else if (SourceType.isHeader(sourceFile)) { return parsedData; } + void addCudaPathArgument(List arguments, String cudaPath) { + var useBuiltinCudaLib = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()); + + if (useBuiltinCudaLib) { + File cudaFolder = clangResources.getBuiltinCudaLib(); + + ClavaLog.debug("Setting --cuda-path to built-in CUDA folder '" + + cudaFolder.getAbsolutePath() + "'"); + arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); + } else if (!cudaPath.isBlank()) { + File cudaFolder = SpecsIo.existingFolder(cudaPath); + + ClavaLog.debug("Setting --cuda-path to folder '" + cudaFolder.getAbsolutePath() + "'"); + arguments.add("--cuda-path=" + cudaFolder.getAbsolutePath()); + } + } + private String processOutput(InputStream inputStream) { StringBuilder output = new StringBuilder(); try (LineStream lines = LineStream.newInstance(inputStream, null)) { diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java new file mode 100644 index 0000000000..967a73003b --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java @@ -0,0 +1,51 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang.dumper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import pt.up.fe.specs.clang.ClangResources; +import pt.up.fe.specs.clang.codeparser.CodeParser; + +public class ClangAstDumperTest { + + @TempDir + Path tempFolder; + + @Test + public void builtinCudaUsesArchiveWithEmptyBuiltinIncludes() { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + parser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + + var resourceFolder = new ClangResources(parser).getClangResourceFolder(); + var expectedCudaFolder = new File(resourceFolder, "cudalib"); + var dumper = new ClangAstDumper(false, tempFolder.resolve("clang").toFile(), List.of(), parser); + var arguments = new ArrayList(); + + dumper.addCudaPathArgument(arguments, parser.get(CodeParser.CUDA_PATH)); + + assertEquals(List.of("--cuda-path=" + expectedCudaFolder.getAbsolutePath()), arguments); + assertTrue(new File(expectedCudaFolder, "include/cuda_runtime.h").isFile()); + } +} From 7c4c22bcb4bec083f43b846c5b74d175efc1c33b Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 20:42:05 +0100 Subject: [PATCH 66/99] fix: simplify legacy CUDA resource handling --- .../fe/specs/clang/ClangAstWebResource.java | 5 +- .../pt/up/fe/specs/clang/ClangResources.java | 87 ++----------------- .../fe/specs/clang/dumper/ClangAstDumper.java | 20 ++--- .../up/fe/specs/clang/ClangResourcesTest.java | 24 +---- .../clang/dumper/ClangAstDumperTest.java | 51 ----------- .../specs/clang/parser/tests/CxxCudaTest.java | 10 +++ 6 files changed, 32 insertions(+), 165 deletions(-) delete mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index 6ec9d6c9b4..9689587a91 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -28,10 +28,11 @@ public final class ClangAstWebResource { private static final String RELEASE_ROOT = "https://github.com/specs-feup/clang-dumper/releases/download/"; - private static final String LEGACY_CUDA_RELEASE_ROOT = + private static final String CUDA_RELEASE_ROOT = "https://github.com/specs-feup/clava/releases/download/clang_ast_dumper_v12.0.7.1/"; + public static final String CUDA_LIB_FILENAME = "cudalib.zip"; static final WebResourceProvider CUDA_LIB = - WebResourceProvider.newInstance(LEGACY_CUDA_RELEASE_ROOT, "cudalib.zip", "v11.3.0"); + WebResourceProvider.newInstance(CUDA_RELEASE_ROOT, CUDA_LIB_FILENAME, "v11.3.0"); private static final String RELEASE_TAG_RESOURCE = "clang-dumper-release.tag"; public static final String MANIFEST_FILENAME = "clang-dumper-release-manifest.json"; diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index ef8d0b9de7..9d54bf5ebb 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -23,7 +23,6 @@ import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsSystem; -import pt.up.fe.specs.util.lazy.Lazy; import pt.up.fe.specs.util.providers.FileResourceProvider.ResourceWriteData; import pt.up.fe.specs.util.system.ProcessOutputAsString; @@ -44,15 +43,12 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.HexFormat; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; public class ClangResources { @@ -66,26 +62,19 @@ public class ClangResources { private final static Duration CACHE_LOCK_RETRY_INTERVAL = Duration.ofMillis(100); private final static Duration CACHE_LOCK_STALE_MAX_AGE = Duration.ofMinutes(5); private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); - private static final String CUDA_ARCHIVE_FILENAME = "cudalib.zip"; - private static final String CUDA_FOLDERNAME = "cudalib"; private static final Map HAS_LIBC = new ConcurrentHashMap<>(); private final CodeParser options; - private final Lazy builtinCudaLib = Lazy.newInstance(this::prepareBuiltinCudaLib); public ClangResources(CodeParser options) { this.options = options; } public File getBuiltinCudaLib() { - return builtinCudaLib.get(); - } - - private File prepareBuiltinCudaLib() { - var resourceFolder = getClangResourceFolder(); - var cudaFolder = SpecsIo.mkdir(new File(resourceFolder, CUDA_FOLDERNAME)); - var zipFile = ClangAstWebResource.CUDA_LIB.writeVersioned(resourceFolder, ClangResources.class); + var cudaResourceFolder = SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), "cuda"); + var cudaFolder = SpecsIo.mkdir(cudaResourceFolder, "cudalib"); + var zipFile = ClangAstWebResource.CUDA_LIB.writeVersioned(cudaResourceFolder, ClangResources.class); if (zipFile.isNewFile() || !isCudaInstallation(cudaFolder)) { SpecsIo.deleteFolderContents(cudaFolder); @@ -95,74 +84,10 @@ private File prepareBuiltinCudaLib() { return cudaFolder; } - public Optional getSystemCudaInstallation() { - return findCudaInstallation( - Arrays.asList(System.getenv("CUDA_HOME"), System.getenv("CUDA_PATH"), System.getenv("CUDA_ROOT")), - System.getenv("PATH"), getConventionalCudaRoots()); - } - - static Optional findCudaInstallation(List configuredRoots, String pathValue, - List conventionalRoots) { - var candidates = new ArrayList(); - - configuredRoots.stream() - .filter(root -> root != null && !root.isBlank()) - .map(File::new) - .forEach(candidates::add); - - if (pathValue != null) { - for (var pathEntry : pathValue.split(Pattern.quote(File.pathSeparator))) { - if (pathEntry.isBlank()) { - continue; - } - - var binFolder = new File(pathEntry); - for (var executableName : List.of("nvcc", "nvcc.exe")) { - var executable = new File(binFolder, executableName); - if (executable.isFile() && binFolder.getParentFile() != null) { - candidates.add(binFolder.getParentFile()); - } - } - } - } - - candidates.addAll(conventionalRoots); - - return candidates.stream() - .map(File::getAbsoluteFile) - .filter(ClangResources::isCudaInstallation) - .findFirst(); - } - private static boolean isCudaInstallation(File folder) { return folder.isDirectory() && new File(folder, "include/cuda_runtime.h").isFile(); } - private static List getConventionalCudaRoots() { - if (!SupportedPlatform.getCurrentPlatform().isWindows()) { - return List.of( - new File("/usr/local/cuda"), - new File("/opt/cuda"), - new File("/opt/homebrew/opt/cuda")); - } - - var roots = new ArrayList(); - for (var programFiles : Arrays.asList(System.getenv("ProgramFiles"), System.getenv("ProgramFiles(x86)"))) { - if (programFiles == null || programFiles.isBlank()) { - continue; - } - - var cudaRoot = new File(programFiles, "NVIDIA GPU Computing Toolkit/CUDA"); - var versions = cudaRoot.listFiles(File::isDirectory); - if (versions != null) { - Arrays.sort(versions, Comparator.comparing(File::getName).reversed()); - roots.addAll(Arrays.asList(versions)); - } - } - - return roots; - } - public ClangFiles getClangFiles(LibcMode libcMode) { var source = ClangAstWebResource.getDumperSource(); @@ -194,6 +119,10 @@ public ClangFiles getClangFiles(LibcMode libcMode) { File clangExecutable = prepareResources(manifest, resourceFolder); List builtinIncludes = prepareIncludes(manifest, resourceFolder, clangExecutable, libcMode); + if (useBuiltinCuda) { + getBuiltinCudaLib(); + } + validateTopLevelCacheFiles(manifest, resourceFolder); updateLastUsedAndCleanupStaleVersions(resourceFolder); @@ -438,8 +367,6 @@ private void validateTopLevelCacheFiles(ClangDumperManifest manifest, File resou ClangAstWebResource.MANIFEST_FILENAME, getCurrentAsset(manifest, executableKind).filename(), INCLUDES_FOLDERNAME, - CUDA_ARCHIVE_FILENAME, - CUDA_FOLDERNAME, LAST_USED_FILENAME); var files = resourceFolder.listFiles(); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 7554b526f6..829a4a31b0 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -194,10 +194,9 @@ private ClangAstData parsePrivate(File sourceFile, String id, Standard standard, arguments.add("-std=cl2.0"); } // Set standard to CUDA - else if (isCuda && !standard.isCuda()) { + else if (isCuda) { + // Clang does not accept '-std=cuda'; CUDA sources still use a C++ standard. arguments.add(standard.isCxx() ? standard.getFlag() : Standard.CXX17.getFlag()); - } else if (isCuda) { - arguments.add(Standard.CXX17.getFlag()); } else { arguments.add(standard.getFlag()); } @@ -226,12 +225,13 @@ else if (isCuda && !standard.isCuda()) { } // If CUDA, add corresponding flags else if (isCuda) { - arguments.add("-x"); - arguments.add("cuda"); - - if (SpecsPlatforms.isWindows()) { - ClavaLog.info("CUDA parsing is not supported in Windows, run at your own risk"); - arguments.addAll(Arrays.asList("-fms-compatibility", "-D_MSC_VER", "-D_LIBCPP_MSVCRT")); + if (!SpecsPlatforms.isLinux()) { + ClavaLog.info("We only officially support CUDA parsing in Linux, run at your own risk"); + arguments.add("-fms-compatibility"); + if (SpecsPlatforms.isWindows()) { + arguments.add("-D_MSC_VER"); + arguments.add("-D_LIBCPP_MSVCRT"); + } } arguments.add("--cuda-gpu-arch=" + parserConfig.get(CodeParser.CUDA_GPU_ARCH)); @@ -333,7 +333,7 @@ else if (SourceType.isHeader(sourceFile)) { return parsedData; } - void addCudaPathArgument(List arguments, String cudaPath) { + private void addCudaPathArgument(List arguments, String cudaPath) { var useBuiltinCudaLib = cudaPath.toUpperCase().equals(CodeParser.getBuiltinOption()); if (useBuiltinCudaLib) { diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 5fae23877f..7da6ca019c 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -103,28 +103,6 @@ public void includesCacheValidationOnlyChecksRequiredFiles() throws IOException assertTrue(ClangResources.isIncludesCacheValid(includesFolder.toFile())); } - @Test - public void cudaInstallationIsFoundFromConfiguredRoot() throws IOException { - var cudaFolder = Files.createDirectories(tempFolder.resolve("cuda")); - Files.createDirectories(cudaFolder.resolve("include")); - Files.createFile(cudaFolder.resolve("include/cuda_runtime.h")); - - assertEquals(cudaFolder.toFile().getAbsoluteFile(), - ClangResources.findCudaInstallation(List.of(cudaFolder.toString()), "", List.of()).orElseThrow()); - } - - @Test - public void cudaInstallationIsFoundFromNvccPath() throws IOException { - var cudaFolder = Files.createDirectories(tempFolder.resolve("cuda")); - var binFolder = Files.createDirectories(cudaFolder.resolve("bin")); - Files.createDirectories(cudaFolder.resolve("include")); - Files.createFile(cudaFolder.resolve("include/cuda_runtime.h")); - Files.createFile(binFolder.resolve("nvcc")); - - assertEquals(cudaFolder.toFile().getAbsoluteFile(), - ClangResources.findCudaInstallation(List.of(), binFolder.toString(), List.of()).orElseThrow()); - } - @Test public void staleCacheCleanupSkipsLockedVersions() throws IOException { var currentVersion = Files.createDirectory(tempFolder.resolve("current")).toFile(); @@ -668,6 +646,8 @@ public void builtinCudaArchiveHasCanonicalInstallationLayout() { var cudaFolder = new ClangResources(parser).getBuiltinCudaLib(); + assertEquals(tempFolder.resolve("cuda/cudalib").toFile().getAbsolutePath(), + cudaFolder.getAbsolutePath()); assertTrue(new File(cudaFolder, "include/cuda.h").isFile()); assertTrue(new File(cudaFolder, "include/cuda_runtime.h").isFile()); assertTrue(new File(cudaFolder, "nvvm/libdevice/libdevice.10.bc").isFile()); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java deleted file mode 100644 index 967a73003b..0000000000 --- a/ClangAstParser/test/pt/up/fe/specs/clang/dumper/ClangAstDumperTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2026 SPeCS. - *

    - * 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 - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * 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 pt.up.fe.specs.clang.dumper; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import pt.up.fe.specs.clang.ClangResources; -import pt.up.fe.specs.clang.codeparser.CodeParser; - -public class ClangAstDumperTest { - - @TempDir - Path tempFolder; - - @Test - public void builtinCudaUsesArchiveWithEmptyBuiltinIncludes() { - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - parser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); - - var resourceFolder = new ClangResources(parser).getClangResourceFolder(); - var expectedCudaFolder = new File(resourceFolder, "cudalib"); - var dumper = new ClangAstDumper(false, tempFolder.resolve("clang").toFile(), List.of(), parser); - var arguments = new ArrayList(); - - dumper.addCudaPathArgument(arguments, parser.get(CodeParser.CUDA_PATH)); - - assertEquals(List.of("--cuda-path=" + expectedCudaFolder.getAbsolutePath()), arguments); - assertTrue(new File(expectedCudaFolder, "include/cuda_runtime.h").isFile()); - } -} diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java index 4960a32dda..2a857db62a 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java @@ -15,6 +15,8 @@ import org.junit.jupiter.api.Test; +import pt.up.fe.specs.clang.ClangAstKeys; +import pt.up.fe.specs.clang.LibcMode; import pt.up.fe.specs.clang.parser.CxxCudaTester; /** Verifies built-in CUDA parsing through Clava's historical cudalib archive. */ @@ -24,6 +26,14 @@ public void testAtomicAdd() { new CxxCudaTester("atomicAdd.cu").test(); } + @Test + public void testAtomicAddWithSystemLibc() { + new CxxCudaTester("atomicAdd.cu") + .set(ClangAstKeys.LIBC_CXX_MODE, LibcMode.SYSTEM) + .onePass() + .test(); + } + @Test public void testConvolutionCache() { new CxxCudaTester("convolution_cache.cu").test(); From 3f112a9f60bd6335d0bd71e2e23afdd7cf036fc2 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 4 Aug 2026 20:50:20 +0100 Subject: [PATCH 67/99] docs: clarify CUDA standard workaround --- .../src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 829a4a31b0..ba7f29de31 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -195,7 +195,8 @@ private ClangAstData parsePrivate(File sourceFile, String id, Standard standard, } // Set standard to CUDA else if (isCuda) { - // Clang does not accept '-std=cuda'; CUDA sources still use a C++ standard. + // The LLVM 18 driver bundled with clang-dumper rejects '-std=cuda'. The .cu extension already + // selects CUDA mode, so use a C++ standard for host-side parsing. arguments.add(standard.isCxx() ? standard.getFlag() : Standard.CXX17.getFlag()); } else { arguments.add(standard.getFlag()); From 8103726ae0a30f6f653f8fc7f40988ed52b8bfae Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 5 Aug 2026 22:10:33 +0100 Subject: [PATCH 68/99] refactor: deduplicate clang-dumper include cache --- .../src/pt/up/fe/specs/clang/CacheFiles.java | 204 +++++ .../fe/specs/clang/ClangAstWebResource.java | 3 +- .../pt/up/fe/specs/clang/ClangResources.java | 594 +++++---------- .../up/fe/specs/clang/ClangResourcesTest.java | 698 ++++++------------ 4 files changed, 637 insertions(+), 862 deletions(-) create mode 100644 ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java new file mode 100644 index 0000000000..004b902c58 --- /dev/null +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java @@ -0,0 +1,204 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang; + +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.providers.FileResourceProvider; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.FileTime; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.HexFormat; + +final class CacheFiles { + + private CacheFiles() { + } + + static Path createStagingDirectory(Path parent, String prefix) { + try { + Files.createDirectories(parent); + return Files.createTempDirectory(parent, prefix); + } catch (IOException e) { + throw new UncheckedIOException("Could not create cache staging directory below '" + parent + "'", e); + } + } + + static File installFile(File destination, FileResourceProvider resource, String expectedSha256, + String description) { + if (destination.isFile()) { + return destination; + } + + Path stagingDirectory = createStagingDirectory(destination.getParentFile().toPath(), + "." + destination.getName() + ".tmp-"); + try { + File stagedFile = resource.write(stagingDirectory.toFile()); + if (stagedFile == null || !stagedFile.isFile()) { + throw new RuntimeException("Could not download " + description); + } + + if (expectedSha256 != null && !hasExpectedSha256(stagedFile, expectedSha256)) { + throw new RuntimeException("Downloaded " + description + " does not match expected SHA-256 '" + + expectedSha256 + "'"); + } + + return publish(stagedFile.toPath(), destination.toPath()).toFile(); + } finally { + deleteQuietly(stagingDirectory); + } + } + + static Path publish(Path staging, Path destination) { + try { + Files.createDirectories(destination.getParent()); + if (Files.exists(destination)) { + return destination; + } + + try { + Files.move(staging, destination, StandardCopyOption.ATOMIC_MOVE); + } catch (FileAlreadyExistsException e) { + // Another process completed the same object first. + } catch (AtomicMoveNotSupportedException e) { + try { + Files.move(staging, destination); + } catch (FileAlreadyExistsException ignored) { + // Another process completed the same object first. + } catch (FileSystemException collision) { + if (!Files.exists(destination)) { + throw collision; + } + + // Some file systems report a non-empty directory collision as a generic file-system exception. + } + } catch (FileSystemException e) { + if (!Files.exists(destination)) { + throw e; + } + + // Some file systems report a non-empty directory collision as a generic file-system exception. + } + + return destination; + } catch (IOException e) { + throw new UncheckedIOException("Could not publish cache object '" + destination + "'", e); + } + } + + static boolean hasExpectedSha256(File file, String expectedSha256) { + return expectedSha256.equalsIgnoreCase(calculateSha256(file)); + } + + static void touch(Path path) { + try { + Files.setLastModifiedTime(path, FileTime.from(Instant.now())); + } catch (IOException e) { + throw new UncheckedIOException("Could not update cache use time for '" + path + "'", e); + } + } + + static void deleteStaleDirectories(Path parent, Instant cutoff, Path excluded) { + if (!Files.isDirectory(parent)) { + return; + } + + try (DirectoryStream children = Files.newDirectoryStream(parent)) { + for (Path child : children) { + if (!Files.isDirectory(child) || child.getFileName().toString().startsWith(".")) { + continue; + } + + if (excluded != null && child.toAbsolutePath().normalize().equals(excluded.toAbsolutePath().normalize())) { + continue; + } + + if (Files.getLastModifiedTime(child).toInstant().isBefore(cutoff)) { + delete(child); + } + } + } catch (IOException e) { + throw new UncheckedIOException("Could not clean stale cache directories below '" + parent + "'", e); + } + } + + static void deleteStaleStagingDirectories(Path parent, Instant cutoff) { + if (!Files.isDirectory(parent)) { + return; + } + + try (DirectoryStream children = Files.newDirectoryStream(parent)) { + for (Path child : children) { + String name = child.getFileName().toString(); + if (!Files.isDirectory(child) || !name.startsWith(".") || !name.contains(".tmp-")) { + continue; + } + + if (Files.getLastModifiedTime(child).toInstant().isBefore(cutoff)) { + delete(child); + } + } + } catch (IOException e) { + throw new UncheckedIOException("Could not clean stale cache staging directories below '" + parent + "'", + e); + } + } + + static void delete(Path path) { + if (!Files.exists(path)) { + return; + } + + boolean deleted = path.toFile().isDirectory() + ? SpecsIo.deleteFolder(path.toFile()) + : SpecsIo.delete(path.toFile()); + if (!deleted && Files.exists(path)) { + throw new RuntimeException("Could not delete cache path '" + path + "'"); + } + } + + private static void deleteQuietly(Path path) { + try { + delete(path); + } catch (RuntimeException ignored) { + // A failed best-effort cleanup must not hide the download or extraction result. + } + } + + private static String calculateSha256(File file) { + try { + var digest = MessageDigest.getInstance("SHA-256"); + try (var inputStream = new DigestInputStream(Files.newInputStream(file.toPath()), digest)) { + inputStream.transferTo(OutputStream.nullOutputStream()); + } + + return HexFormat.of().formatHex(digest.digest()); + } catch (IOException | NoSuchAlgorithmException e) { + throw new RuntimeException("Could not calculate SHA-256 for file '" + file + "'", e); + } + } +} diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index 9689587a91..cdbc0c2a1f 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -94,7 +94,8 @@ public static ClangDumperManifest getManifest(File resourceFolder) { var releaseTag = getReleaseTag(); var manifestResource = WebResourceProvider.newInstance(getReleaseBaseUrl(releaseTag), MANIFEST_FILENAME, releaseTag); - var manifestFile = manifestResource.writeVersioned(resourceFolder, ClangAstWebResource.class).getFile(); + var manifestFile = CacheFiles.installFile(new File(resourceFolder, MANIFEST_FILENAME), manifestResource, null, + "clang-dumper release manifest"); var manifest = GSON.fromJson(SpecsIo.read(manifestFile), ClangDumperManifest.class); if (manifest == null) { diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 9d54bf5ebb..f83098dcbf 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -23,45 +23,29 @@ import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsSystem; -import pt.up.fe.specs.util.providers.FileResourceProvider.ResourceWriteData; import pt.up.fe.specs.util.system.ProcessOutputAsString; import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.UncheckedIOException; -import java.nio.file.DirectoryNotEmptyException; -import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.HexFormat; import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class ClangResources { - private static final Map CLANG_FILES_CACHE = new ConcurrentHashMap<>(); - private static final Map CLANG_FILES_LOCKS = new ConcurrentHashMap<>(); - private final static String CLANG_FOLDERNAME = "clang_ast_exe"; - private final static String INCLUDES_FOLDERNAME = "includes"; - private final static String LAST_USED_FILENAME = "last-used.txt"; - private final static String CACHE_LOCK_FOLDERNAME = ".cache.lock"; - private final static String CACHE_LOCK_OWNER_PREFIX = "owner-"; - private final static Duration CACHE_LOCK_RETRY_INTERVAL = Duration.ofMillis(100); - private final static Duration CACHE_LOCK_STALE_MAX_AGE = Duration.ofMinutes(5); - private final static Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); + private static final Map CLANG_FILES_CACHE = new ConcurrentHashMap<>(); + private static final String CLANG_FOLDERNAME = "clang_ast_exe"; + private static final String RELEASES_FOLDERNAME = "releases"; + private static final String INCLUDES_FOLDERNAME = "includes"; + private static final Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); + private static final Duration STALE_STAGING_MAX_AGE = Duration.ofHours(1); private static final Map HAS_LIBC = new ConcurrentHashMap<>(); @@ -73,19 +57,35 @@ public ClangResources(CodeParser options) { public File getBuiltinCudaLib() { var cudaResourceFolder = SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), "cuda"); - var cudaFolder = SpecsIo.mkdir(cudaResourceFolder, "cudalib"); - var zipFile = ClangAstWebResource.CUDA_LIB.writeVersioned(cudaResourceFolder, ClangResources.class); + var cudaFolder = new File(cudaResourceFolder, "cudalib"); + var zipFile = CacheFiles.installFile(new File(cudaResourceFolder, ClangAstWebResource.CUDA_LIB_FILENAME), + ClangAstWebResource.CUDA_LIB, null, "built-in CUDA archive"); - if (zipFile.isNewFile() || !isCudaInstallation(cudaFolder)) { - SpecsIo.deleteFolderContents(cudaFolder); - SpecsIo.extractZip(zipFile.getFile(), cudaFolder); + if (isCudaInstallation(cudaFolder)) { + return cudaFolder; } - return cudaFolder; + if (cudaFolder.exists()) { + CacheFiles.delete(cudaFolder.toPath()); + } + + var stagingFolder = CacheFiles.createStagingDirectory(cudaResourceFolder.toPath(), ".cudalib.tmp-"); + try { + if (!SpecsIo.extractZip(zipFile, stagingFolder.toFile()) || !isCudaInstallation(stagingFolder.toFile())) { + throw new RuntimeException("Built-in CUDA archive did not contain a valid CUDA installation"); + } + + return CacheFiles.publish(stagingFolder, cudaFolder.toPath()).toFile(); + } finally { + CacheFiles.delete(stagingFolder); + } } private static boolean isCudaInstallation(File folder) { - return folder.isDirectory() && new File(folder, "include/cuda_runtime.h").isFile(); + return folder.isDirectory() + && new File(folder, "include/cuda.h").isFile() + && new File(folder, "include/cuda_runtime.h").isFile() + && new File(folder, "nvvm/libdevice/libdevice.10.bc").isFile(); } public ClangFiles getClangFiles(LibcMode libcMode) { @@ -100,40 +100,47 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var resourceFolder = getClangResourceFolder(); var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" + resourceFolder.getAbsolutePath(); - var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(resourceFolder.getAbsolutePath(), ignored -> new Object()); - synchronized (jvmLock) { - var lockFolder = getCacheLockFolder(resourceFolder); - try (var ignored = acquireCacheLock(resourceFolder)) { - var files = CLANG_FILES_CACHE.get(key); - if (files != null) { - if (files.clangExecutable().isFile()) { - writeLastUsed(resourceFolder, Instant.now()); - SpecsLogs.debug(() -> "Using cached version of Clang files: " + files); - return files; - } - - CLANG_FILES_CACHE.remove(key, files); - } + var cached = CLANG_FILES_CACHE.get(key); + if (isUsable(cached)) { + touchUse(resourceFolder, cached.includesFolder()); + SpecsLogs.debug(() -> "Using cached version of Clang files: " + cached.files()); + return cached.files(); + } - var manifest = ClangAstWebResource.getManifest(resourceFolder); - File clangExecutable = prepareResources(manifest, resourceFolder); - List builtinIncludes = prepareIncludes(manifest, resourceFolder, clangExecutable, libcMode); + if (cached != null) { + CLANG_FILES_CACHE.remove(key, cached); + } - if (useBuiltinCuda) { - getBuiltinCudaLib(); - } + var manifest = ClangAstWebResource.getManifest(resourceFolder); + File clangExecutable = prepareResources(manifest, resourceFolder); + var includes = prepareIncludes(manifest, clangExecutable, libcMode); - validateTopLevelCacheFiles(manifest, resourceFolder); - updateLastUsedAndCleanupStaleVersions(resourceFolder); + if (useBuiltinCuda) { + getBuiltinCudaLib(); + } - var newFiles = new ClangFiles(clangExecutable, builtinIncludes); - SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + newFiles); + touchUse(resourceFolder, includes.extractedFolder()); + updateLastUsedAndCleanupStaleVersions(resourceFolder, includes.extractedFolder()); - CLANG_FILES_CACHE.put(key, newFiles); - return newFiles; - } catch (IOException e) { - throw new UncheckedIOException("Could not lock clang-dumper cache '" + lockFolder + "'", e); - } + var newFiles = new CachedClangFiles(new ClangFiles(clangExecutable, includes.folders()), + includes.extractedFolder()); + var existingFiles = CLANG_FILES_CACHE.putIfAbsent(key, newFiles); + var selectedFiles = existingFiles == null ? newFiles : existingFiles; + touchUse(resourceFolder, selectedFiles.includesFolder()); + SpecsLogs.debug(() -> "Using downloaded version of Clang files: " + selectedFiles.files()); + return selectedFiles.files(); + } + + private static boolean isUsable(CachedClangFiles cached) { + return cached != null + && cached.files().clangExecutable().isFile() + && (cached.includesFolder() == null || isIncludesCacheValid(cached.includesFolder())); + } + + private static void touchUse(File resourceFolder, File includesFolder) { + CacheFiles.touch(resourceFolder.toPath()); + if (includesFolder != null) { + CacheFiles.touch(includesFolder.toPath()); } } @@ -163,17 +170,20 @@ private File prepareResources(ClangDumperManifest manifest, File resourceFolder) SupportedPlatform platform = SupportedPlatform.getCurrentPlatform(); var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; - ResourceWriteData executable = downloadAsset(manifest, executableKind, resourceFolder); + var asset = getCurrentAsset(manifest, executableKind); + File executable = CacheFiles.installFile(new File(resourceFolder, asset.filename()), + ClangAstWebResource.getAssetResource(asset), asset.sha256(), + "clang-dumper asset '" + asset.filename() + "'"); if (platform.isWindows()) { - unblockWindowsFile(executable.getFile()); + unblockWindowsFile(executable); } - if (executable.isNewFile() && (platform.isLinux() || platform.isMacOs())) { - SpecsSystem.runProcess(Arrays.asList("chmod", "+x", executable.getFile().getAbsolutePath()), false, true); + if (platform.isLinux() || platform.isMacOs()) { + SpecsSystem.runProcess(Arrays.asList("chmod", "+x", executable.getAbsolutePath()), false, true); } - return executable.getFile(); + return executable; } private void unblockWindowsFile(File executable) { @@ -196,13 +206,31 @@ private void unblockWindowsFile(File executable) { } public File getClangResourceFolder() { - return SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), ClangAstWebResource.getReleaseTag()); + var releaseFolder = SpecsIo.mkdir(getReleasesFolder(), ClangAstWebResource.getReleaseTag()); + CacheFiles.touch(releaseFolder.toPath()); + return releaseFolder; } public static File getDefaultTempFolder() { return SpecsIo.getTempFolder(CLANG_FOLDERNAME); } + private File getReleasesFolder() { + return SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), RELEASES_FOLDERNAME); + } + + private File getIncludesRoot() { + return new File(options.get(CodeParser.DUMPER_FOLDER), INCLUDES_FOLDERNAME); + } + + private File getIncludesFolder(String sha256) { + return getSharedIncludesFolder(options.get(CodeParser.DUMPER_FOLDER), sha256); + } + + static File getSharedIncludesFolder(File cacheFolder, String sha256) { + return new File(new File(cacheFolder, INCLUDES_FOLDERNAME), sha256.toLowerCase(Locale.ROOT)); + } + public static boolean useBuiltinLibc(File clangExecutable, LibcMode libcMode) { return switch (libcMode) { case AUTO -> !hasLibC(clangExecutable); @@ -258,86 +286,105 @@ private static ProcessOutputAsString runClangAstDumper(File clangExecutable, Fil return SpecsSystem.runProcess(arguments, true, false); } - private List prepareIncludes(ClangDumperManifest manifest, File resourceFolder, File clangExecutable, - LibcMode libcMode) { + private PreparedIncludes prepareIncludes(ClangDumperManifest manifest, File clangExecutable, + LibcMode libcMode) { var useBuiltinLibc = useBuiltinLibc(clangExecutable, libcMode); var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); if (!useBuiltinLibc && !useBuiltinCuda) { - return List.of(); + return new PreparedIncludes(List.of(), null); } - return prepareIncludes(manifest, resourceFolder); - } - - private List prepareIncludes(ClangDumperManifest manifest, File resourceFolder) { - var extractedFolder = prepareIncludesFolder(manifest, resourceFolder); + var extractedFolder = prepareIncludesFolder(manifest); var includeFolders = getIncludeFolders(extractedFolder); SpecsLogs.debug(() -> "Includes folders: " + includeFolders); - return includeFolders.stream().map(File::getAbsolutePath).toList(); + return new PreparedIncludes(includeFolders.stream().map(File::getAbsolutePath).toList(), extractedFolder); } - private File prepareIncludesFolder(ClangDumperManifest manifest, File resourceFolder) { + private File prepareIncludesFolder(ClangDumperManifest manifest) { var includesAsset = getCurrentAsset(manifest, "includes"); - var extractedFolder = new File(resourceFolder, INCLUDES_FOLDERNAME); + var extractedFolder = getIncludesFolder(includesAsset.sha256()); if (isIncludesCacheValid(extractedFolder)) { return extractedFolder; } - ResourceWriteData zipFile = downloadAsset(includesAsset, resourceFolder); - + var includesRoot = getIncludesRoot(); + CacheFiles.deleteStaleStagingDirectories(includesRoot.toPath(), + Instant.now().minus(STALE_STAGING_MAX_AGE)); + var stagingFolder = CacheFiles.createStagingDirectory(includesRoot.toPath(), + "." + includesAsset.sha256() + ".tmp-"); try { - SpecsIo.mkdir(extractedFolder); - SpecsIo.deleteFolderContents(extractedFolder); - SpecsIo.extractZip(zipFile.getFile(), extractedFolder); + var downloadFolder = CacheFiles.createStagingDirectory(stagingFolder, ".download-"); + try { + var archive = ClangAstWebResource.getAssetResource(includesAsset).write(downloadFolder.toFile()); + if (archive == null || !archive.isFile()) { + throw new RuntimeException("Could not download clang-dumper includes archive '" + + includesAsset.filename() + "'"); + } + + if (!CacheFiles.hasExpectedSha256(archive, includesAsset.sha256())) { + throw new RuntimeException("Downloaded clang-dumper asset '" + includesAsset.filename() + + "' does not match expected SHA-256 '" + includesAsset.sha256() + "'"); + } + + if (!SpecsIo.extractZip(archive, stagingFolder.toFile())) { + throw new RuntimeException("Could not extract clang-dumper includes archive '" + + includesAsset.filename() + "'"); + } + } finally { + CacheFiles.delete(downloadFolder); + } + + getIncludeFolders(stagingFolder.toFile()); + if (isIncludesCacheValid(extractedFolder)) { + CacheFiles.touch(extractedFolder.toPath()); + return extractedFolder; + } + + if (extractedFolder.exists()) { + CacheFiles.delete(extractedFolder.toPath()); + } + + var publishedFolder = CacheFiles.publish(stagingFolder, extractedFolder.toPath()).toFile(); + if (!isIncludesCacheValid(publishedFolder)) { + throw new RuntimeException("Published clang-dumper includes are invalid: '" + publishedFolder + "'"); + } + + CacheFiles.touch(publishedFolder.toPath()); + return publishedFolder; } finally { - SpecsIo.delete(zipFile.getFile()); + CacheFiles.delete(stagingFolder); } - - return extractedFolder; } - private List getIncludeFolders(File extractedFolder) { + static List getIncludeFolders(File extractedFolder) { + if (!extractedFolder.isDirectory()) { + throw new RuntimeException("Could not find extracted clang-dumper includes folder '" + extractedFolder + "'"); + } + var entrypointsFile = new File(extractedFolder, "entrypoints.txt"); if (!entrypointsFile.isFile()) { throw new RuntimeException("Could not find include archive entrypoints file '" + entrypointsFile + "'"); } - return SpecsIo.read(entrypointsFile).lines() + Path root = extractedFolder.toPath().toAbsolutePath().normalize(); + var includeFolders = new ArrayList(); + var entrypoints = SpecsIo.read(entrypointsFile).lines() .map(String::trim) - .filter(line -> !line.isEmpty()) - .map(line -> new File(extractedFolder, line)) + .filter(value -> !value.isEmpty()) .toList(); - } - - private ResourceWriteData downloadAsset(ClangDumperManifest manifest, String kind, File resourceFolder) { - var asset = getCurrentAsset(manifest, kind); - return downloadAsset(asset, resourceFolder); - } - - private ResourceWriteData downloadAsset(ClangDumperManifestAsset asset, File resourceFolder) { - var resource = ClangAstWebResource.getAssetResource(asset); - var writeData = resource.writeVersioned(resourceFolder, ClangResources.class); - - if (!writeData.isNewFile()) { - return writeData; - } - - if (!hasExpectedSha256(writeData.getFile(), asset)) { - SpecsLogs.info("Downloaded clang-dumper asset '" + asset.filename() - + "' does not match the expected checksum, downloading it again."); - SpecsIo.delete(writeData.getFile()); - writeData = resource.writeVersioned(resourceFolder, ClangResources.class); - } + for (String line : entrypoints) { + Path includeFolder = root.resolve(line).normalize(); + if (!includeFolder.startsWith(root) || !Files.isDirectory(includeFolder)) { + throw new RuntimeException("Include archive entrypoint is not a usable directory: '" + line + "'"); + } - if (!hasExpectedSha256(writeData.getFile(), asset)) { - throw new RuntimeException("Downloaded clang-dumper asset '" + asset.filename() - + "' does not match expected SHA-256 '" + asset.sha256() + "'"); + includeFolders.add(includeFolder.toFile()); } - return writeData; + return includeFolders; } private ClangDumperManifestAsset getCurrentAsset(ClangDumperManifest manifest, String kind) { @@ -347,317 +394,41 @@ private ClangDumperManifestAsset getCurrentAsset(ClangDumperManifest manifest, S } static boolean isIncludesCacheValid(File includesFolder) { - - if (!includesFolder.isDirectory()) { - return false; - } - - var entrypointsFile = new File(includesFolder, "entrypoints.txt"); - if (!entrypointsFile.isFile()) { - SpecsLogs.info("Cached clang-dumper includes are missing entrypoints, extracting them again."); + try { + getIncludeFolders(includesFolder); + return true; + } catch (RuntimeException e) { + SpecsLogs.info("Cached clang-dumper includes are invalid: " + includesFolder); return false; } - - return true; - } - - private void validateTopLevelCacheFiles(ClangDumperManifest manifest, File resourceFolder) { - var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; - Set expectedNames = Set.of( - ClangAstWebResource.MANIFEST_FILENAME, - getCurrentAsset(manifest, executableKind).filename(), - INCLUDES_FOLDERNAME, - LAST_USED_FILENAME); - - var files = resourceFolder.listFiles(); - if (files == null) { - return; - } - - for (var file : files) { - if (expectedNames.contains(file.getName())) { - continue; - } - - SpecsLogs.info("Deleting unexpected file from clang-dumper cache: " + file); - SpecsIo.delete(file); - } } - private void updateLastUsedAndCleanupStaleVersions(File resourceFolder) { + private void updateLastUsedAndCleanupStaleVersions(File resourceFolder, File includesFolder) { var now = Instant.now(); - writeLastUsed(resourceFolder, now); + touchUse(resourceFolder, includesFolder); - var staleCleanup = new Thread(() -> deleteStaleVersions(now, resourceFolder), + var staleCleanup = new Thread(() -> deleteStaleVersions(now, resourceFolder, includesFolder), "clang-dumper-stale-cache-cleanup"); staleCleanup.setDaemon(true); staleCleanup.start(); } - private static void writeLastUsed(File resourceFolder, Instant timestamp) { - writeTimestamp(new File(resourceFolder, LAST_USED_FILENAME), timestamp); - } - - private static void writeTimestamp(File file, Instant timestamp) { - SpecsIo.write(file, timestamp.toString()); - } - void deleteStaleVersions(Instant now, File currentVersionFolder) { - File cacheBaseFolder = options.get(CodeParser.DUMPER_FOLDER); - var versions = cacheBaseFolder.listFiles(File::isDirectory); - if (versions == null) { - return; - } - - for (var versionFolder : versions) { - if (versionFolder.getAbsoluteFile().equals(currentVersionFolder.getAbsoluteFile())) { - continue; - } - - var jvmLock = CLANG_FILES_LOCKS.computeIfAbsent(versionFolder.getAbsolutePath(), ignored -> new Object()); - try { - synchronized (jvmLock) { - var lastUsedFile = new File(versionFolder, LAST_USED_FILENAME); - if (!lastUsedFile.isFile()) { - continue; - } - - try (var lock = tryAcquireCacheLock(versionFolder)) { - if (lock == null) { - SpecsLogs.debug(() -> "Skipping locked clang-dumper cache folder: " + versionFolder); - continue; - } - - if (!lastUsedFile.isFile()) { - continue; - } - - var lastUsed = Instant.parse(SpecsIo.read(lastUsedFile).trim()); - if (lastUsed.isBefore(now.minus(STALE_CACHE_MAX_AGE))) { - SpecsLogs.info("Deleting stale clang-dumper cache folder: " + versionFolder); - SpecsIo.deleteFolder(versionFolder); - } - } - } - } catch (IOException | RuntimeException e) { - SpecsLogs.warn("Could not inspect clang-dumper cache folder '" + versionFolder + "'", e); - } - } + deleteStaleVersions(now, currentVersionFolder, null); } - static CacheLock acquireCacheLock(File versionFolder) throws IOException { - return acquireCacheLock(versionFolder, true); - } - - private static CacheLock tryAcquireCacheLock(File versionFolder) throws IOException { - return acquireCacheLock(versionFolder, false); - } - - private static CacheLock acquireCacheLock(File versionFolder, boolean wait) throws IOException { - var lockFolder = getCacheLockFolder(versionFolder); - Files.createDirectories(lockFolder.getParentFile().toPath()); - - while (true) { - try { - Files.createDirectory(lockFolder.toPath()); - } catch (FileAlreadyExistsException e) { - if (!isCacheLockStale(lockFolder)) { - if (!wait) { - return null; - } - - waitForCacheLock(); - continue; - } - - recoverStaleCacheLock(lockFolder); - continue; - } - - var ownerFile = new File(lockFolder, CACHE_LOCK_OWNER_PREFIX + UUID.randomUUID()); - try { - Files.writeString(ownerFile.toPath(), getProcessIdentity(), StandardOpenOption.CREATE_NEW, - StandardOpenOption.WRITE); - } catch (NoSuchFileException e) { - // Stale-lock recovery removed the directory while this process was claiming it. - continue; - } catch (IOException e) { - deleteEmptyCacheLock(lockFolder); - throw e; - } - - return new CacheLock(lockFolder, ownerFile); - } - } - - /** - * Returns the temporary lock folder for a cache version. - * - *

    The lock folder is outside the version folder because stale cleanup deletes that folder while holding the - * lock. The folder is removed when the lock is released, so normal operation leaves no lock artifact behind.

    - */ - static File getCacheLockFolder(File versionFolder) { - var absoluteVersionFolder = versionFolder.getAbsoluteFile(); - return new File(absoluteVersionFolder.getParentFile(), absoluteVersionFolder.getName() + CACHE_LOCK_FOLDERNAME); - } - - private static String getProcessIdentity() { - var process = ProcessHandle.current(); - var startTime = process.info().startInstant().map(Instant::toString).orElse(""); - return process.pid() + System.lineSeparator() + startTime; - } - - private static boolean isCacheLockStale(File lockFolder) throws IOException { - if (!lockFolder.exists()) { - return true; - } - - if (!lockFolder.isDirectory()) { - throw new IOException("Cache lock path is not a directory: '" + lockFolder + "'"); - } - - var ownerFiles = lockFolder.listFiles(File::isFile); - if (ownerFiles == null) { - if (!lockFolder.exists()) { - return true; - } - - throw new IOException("Could not list cache lock folder: '" + lockFolder + "'"); - } - - if (ownerFiles.length == 0) { - return isCacheLockOld(lockFolder); - } - - for (var ownerFile : ownerFiles) { - if (!isCacheLockOwnerStale(lockFolder, ownerFile)) { - return false; - } - } - - return true; - } - - private static boolean isCacheLockOwnerStale(File lockFolder, File ownerFile) throws IOException { - List lines; - try { - lines = Files.readAllLines(ownerFile.toPath()); - } catch (NoSuchFileException e) { - return true; - } - - if (lines.isEmpty()) { - return isCacheLockOld(lockFolder); - } - + private void deleteStaleVersions(Instant now, File currentVersionFolder, File currentIncludesFolder) { + var cutoff = now.minus(STALE_CACHE_MAX_AGE); try { - var pid = Long.parseLong(lines.get(0).trim()); - var process = ProcessHandle.of(pid); - if (process.isEmpty() || !process.get().isAlive()) { - return true; - } - - if (lines.size() > 1 && !lines.get(1).isBlank()) { - var processStart = process.get().info().startInstant(); - if (processStart.isPresent() && !processStart.get().toString().equals(lines.get(1).trim())) { - return true; - } - } - - return false; - } catch (NumberFormatException e) { - return isCacheLockOld(lockFolder); - } - } - - private static boolean isCacheLockOld(File lockFolder) throws IOException { - try { - return Files.getLastModifiedTime(lockFolder.toPath()).toInstant() - .isBefore(Instant.now().minus(CACHE_LOCK_STALE_MAX_AGE)); - } catch (NoSuchFileException e) { - return true; - } - } - - private static void waitForCacheLock() throws IOException { - try { - Thread.sleep(CACHE_LOCK_RETRY_INTERVAL.toMillis()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IOException("Interrupted while waiting for clang-dumper cache lock", e); - } - } - - private static void recoverStaleCacheLock(File lockFolder) throws IOException { - if (!lockFolder.isDirectory()) { - return; - } - - var ownerFiles = lockFolder.listFiles(File::isFile); - if (ownerFiles == null) { - return; - } - - for (var ownerFile : ownerFiles) { - if (isCacheLockOwnerStale(lockFolder, ownerFile)) { - Files.deleteIfExists(ownerFile.toPath()); - } - } - - deleteEmptyCacheLock(lockFolder); - } - - private static void deleteEmptyCacheLock(File lockFolder) throws IOException { - try { - Files.deleteIfExists(lockFolder.toPath()); - } catch (DirectoryNotEmptyException e) { - // A replacement owner claimed the lock while stale recovery was in progress. - } - } - - /** - * A temporary claim on one cache version. Each claim has its own owner marker, so releasing an old claim cannot - * remove a newer claim created after stale-lock recovery. - */ - static final class CacheLock implements AutoCloseable { - - private final File lockFolder; - private final File ownerFile; - - private CacheLock(File lockFolder, File ownerFile) { - this.lockFolder = lockFolder; - this.ownerFile = ownerFile; - } - - File ownerFile() { - return ownerFile; - } - - @Override - public void close() { - try { - Files.deleteIfExists(ownerFile.toPath()); - deleteEmptyCacheLock(lockFolder); - } catch (IOException e) { - SpecsLogs.warn("Could not remove temporary clang-dumper cache lock '" + lockFolder + "'", e); - } - } - } - - private static boolean hasExpectedSha256(File file, ClangDumperManifestAsset asset) { - return asset.sha256().equalsIgnoreCase(calculateSha256(file)); - } - - private static String calculateSha256(File file) { - try { - var digest = MessageDigest.getInstance("SHA-256"); - try (var inputStream = new DigestInputStream(Files.newInputStream(file.toPath()), digest)) { - inputStream.transferTo(OutputStream.nullOutputStream()); - } - - return HexFormat.of().formatHex(digest.digest()); - } catch (IOException | NoSuchAlgorithmException e) { - throw new RuntimeException("Could not calculate SHA-256 for file '" + file + "'", e); + CacheFiles.deleteStaleDirectories(getReleasesFolder().toPath(), cutoff, currentVersionFolder.toPath()); + CacheFiles.deleteStaleDirectories(getIncludesRoot().toPath(), cutoff, + currentIncludesFolder == null ? null : currentIncludesFolder.toPath()); + CacheFiles.deleteStaleStagingDirectories(getReleasesFolder().toPath(), + now.minus(STALE_STAGING_MAX_AGE)); + CacheFiles.deleteStaleStagingDirectories(getIncludesRoot().toPath(), + now.minus(STALE_STAGING_MAX_AGE)); + } catch (RuntimeException e) { + SpecsLogs.warn("Could not clean stale clang-dumper cache resources", e); } } @@ -693,4 +464,9 @@ private static String getManifestArch(String platform) { throw new RuntimeException("Unsupported architecture for clang-dumper: " + osArch); } + private record PreparedIncludes(List folders, File extractedFolder) { + } + + private record CachedClangFiles(ClangFiles files, File includesFolder) { + } } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 7da6ca019c..e074dc952a 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -13,38 +13,45 @@ package pt.up.fe.specs.clang; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifest; +import pt.up.fe.specs.clang.ClangAstWebResource.ClangDumperManifestAsset; +import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; +import pt.up.fe.specs.clang.ClangAstWebResource.Release; +import pt.up.fe.specs.clang.codeparser.CodeParser; +import pt.up.fe.specs.clang.dumper.ClangAstDumper; +import pt.up.fe.specs.clang.parsers.TopLevelNodesParser; +import pt.up.fe.specs.util.providers.FileResourceProvider; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; -import pt.up.fe.specs.clang.ClangAstWebResource.Release; -import pt.up.fe.specs.clang.codeparser.CodeParser; -import pt.up.fe.specs.clang.parsers.TopLevelNodesParser; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ClangResourcesTest { - private static final Duration PROCESS_TIMEOUT = Duration.ofSeconds(10); + private static final Duration PROCESS_TIMEOUT = Duration.ofSeconds(30); + private static final String HELLO_SHA256 = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"; @TempDir Path tempFolder; @@ -72,7 +79,9 @@ public void relativePathIsRejected() { @Test public void localBuildSelectsExpectedTool() throws IOException { - var toolName = SupportedPlatform.getCurrentPlatform().isWindows() ? "tool.exe" : "tool"; + var toolName = ClangAstDumper.usePlugin() + ? System.mapLibraryName("plugin") + : SupportedPlatform.getCurrentPlatform().isWindows() ? "tool.exe" : "tool"; var tool = tempFolder.resolve(toolName).toFile(); assertTrue(tool.createNewFile()); @@ -85,11 +94,23 @@ public void localBuildRequiresExpectedTool() { } @Test - public void includesCacheValidationOnlyChecksRequiredFiles() throws IOException { - var includesFolder = tempFolder.resolve("includes"); - assertFalse(ClangResources.isIncludesCacheValid(includesFolder.toFile())); + public void manifestValidationAndAssetSelectionArePreserved() { + var tool = asset("tool", "tool", "linux", "x64"); + var plugin = asset("plugin", "plugin", "linux", "x64"); + var manifest = new ClangDumperManifest(1, List.of(tool, plugin)); + + assertDoesNotThrow(manifest::validate); + assertEquals(tool, manifest.getAsset("linux", "x64", "tool")); + assertEquals(plugin, manifest.getAsset("linux", "x64", "plugin")); + assertEquals(HELLO_SHA256, tool.sha256()); + assertThrows(RuntimeException.class, () -> manifest.getAsset("windows", "x64", "tool")); + assertThrows(RuntimeException.class, () -> new ClangDumperManifest(2, List.of(tool)).validate()); + assertThrows(RuntimeException.class, () -> new ClangDumperManifest(1, List.of()).validate()); + } - Files.createDirectory(includesFolder); + @Test + public void includesCacheValidationChecksEntrypointsButNotEveryFile() throws IOException { + var includesFolder = tempFolder.resolve("includes"); assertFalse(ClangResources.isIncludesCacheValid(includesFolder.toFile())); Files.createDirectories(includesFolder.resolve("builtin")); @@ -101,299 +122,140 @@ public void includesCacheValidationOnlyChecksRequiredFiles() throws IOException Files.writeString(includesFolder.resolve("builtin/header.h"), "modified"); assertTrue(ClangResources.isIncludesCacheValid(includesFolder.toFile())); + Files.writeString(includesFolder.resolve("entrypoints.txt"), "missing\n"); + assertFalse(ClangResources.isIncludesCacheValid(includesFolder.toFile())); } @Test - public void staleCacheCleanupSkipsLockedVersions() throws IOException { - var currentVersion = Files.createDirectory(tempFolder.resolve("current")).toFile(); - var staleVersion = Files.createDirectory(tempFolder.resolve("stale")).toFile(); - Files.writeString(staleVersion.toPath().resolve("last-used.txt"), - Instant.now().minus(Duration.ofDays(61)).toString()); + public void entrypointsPreserveDeclaredIncludeOrder() throws IOException { + var includesFolder = Files.createDirectories(tempFolder.resolve("includes")); + var first = Files.createDirectories(includesFolder.resolve("first")); + var second = Files.createDirectories(includesFolder.resolve("second")); + Files.writeString(includesFolder.resolve("entrypoints.txt"), "second\nfirst\n"); - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - var resources = new ClangResources(parser); - - try (var ignored = ClangResources.acquireCacheLock(staleVersion)) { - - resources.deleteStaleVersions(Instant.now(), currentVersion); - assertTrue(staleVersion.isDirectory()); - } - - assertFalse(ClangResources.getCacheLockFolder(staleVersion).exists()); - - resources.deleteStaleVersions(Instant.now(), currentVersion); - assertFalse(staleVersion.exists()); + assertEquals(List.of(second.toFile(), first.toFile()), + ClangResources.getIncludeFolders(includesFolder.toFile())); } @Test - public void cacheLockSerializesConcurrentAcquisition() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var executor = Executors.newSingleThreadExecutor(); - ClangResources.CacheLock firstLock = ClangResources.acquireCacheLock(versionFolder); - - try { - var secondLock = executor.submit(() -> ClangResources.acquireCacheLock(versionFolder)); - assertFalse(secondLock.isDone()); - - firstLock.close(); - firstLock = null; - try (var ignored = secondLock.get(10, TimeUnit.SECONDS)) { - assertTrue(versionFolder.isDirectory()); - } - } finally { - if (firstLock != null) { - firstLock.close(); - } - executor.shutdownNow(); - executor.awaitTermination(10, TimeUnit.SECONDS); - } + public void sharedIncludesAreAddressedOnlyBySha() throws IOException { + var sha = "a".repeat(64); + var sharedFolder = ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha); + Files.createDirectories(sharedFolder.toPath().resolve("builtin")); + Files.writeString(sharedFolder.toPath().resolve("entrypoints.txt"), "builtin\n"); - assertFalse(ClangResources.getCacheLockFolder(versionFolder).exists()); + var firstRelease = tempFolder.resolve("releases/v1").toFile(); + var secondRelease = tempFolder.resolve("releases/v2").toFile(); + assertNotEquals(firstRelease, secondRelease); + assertEquals(sharedFolder, ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha)); + assertTrue(ClangResources.isIncludesCacheValid(sharedFolder)); } @Test - public void cacheLockSerializesAcquisitionAcrossJvms() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var firstAcquired = tempFolder.resolve("first.acquired"); - var firstRelease = tempFolder.resolve("first.release"); - var secondAcquired = tempFolder.resolve("second.acquired"); - var secondRelease = tempFolder.resolve("second.release"); - var firstLog = tempFolder.resolve("first.log"); - var secondLog = tempFolder.resolve("second.log"); - - Process first = startCacheLockProcess(versionFolder, firstAcquired, firstRelease, firstLog); - Process second = null; - try { - assertTrue(waitForFile(firstAcquired, PROCESS_TIMEOUT)); - - second = startCacheLockProcess(versionFolder, secondAcquired, secondRelease, secondLog); - assertFalse(waitForFile(secondAcquired, Duration.ofMillis(750)), - "A second JVM acquired a cache lock that was still held"); + public void corruptNewDownloadIsRejectedWithoutRetry() throws IOException { + var source = Files.writeString(tempFolder.resolve("source"), "bad"); + var writes = new AtomicInteger(); + var destination = tempFolder.resolve("release/tool").toFile(); - Files.createFile(firstRelease); - assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT)); + assertThrows(RuntimeException.class, + () -> CacheFiles.installFile(destination, copyingResource(source, writes), HELLO_SHA256, "test asset")); - Files.createFile(secondRelease); - waitForProcess(first, firstLog); - waitForProcess(second, secondLog); - } finally { - releaseProcess(firstRelease); - releaseProcess(secondRelease); - stopProcess(first); - stopProcess(second); + assertEquals(1, writes.get()); + assertFalse(destination.exists()); + try (var children = Files.list(destination.getParentFile().toPath())) { + assertTrue(children.noneMatch(path -> path.getFileName().toString().startsWith(".tool.tmp-"))); } - - assertFalse(ClangResources.getCacheLockFolder(versionFolder).exists()); } @Test - public void cacheLockAllowsDifferentVersionsToProceedAcrossJvms() throws Exception { - var firstVersion = Files.createDirectory(tempFolder.resolve("version-1")).toFile(); - var secondVersion = Files.createDirectory(tempFolder.resolve("version-2")).toFile(); - var firstAcquired = tempFolder.resolve("first.acquired"); - var firstRelease = tempFolder.resolve("first.release"); - var secondAcquired = tempFolder.resolve("second.acquired"); - var secondRelease = tempFolder.resolve("second.release"); - var firstLog = tempFolder.resolve("first.log"); - var secondLog = tempFolder.resolve("second.log"); - - Process first = startCacheLockProcess(firstVersion, firstAcquired, firstRelease, firstLog); - Process second = null; - try { - assertTrue(waitForFile(firstAcquired, PROCESS_TIMEOUT)); - - second = startCacheLockProcess(secondVersion, secondAcquired, secondRelease, secondLog); - assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT), - "A different dumper version was blocked by an unrelated cache lock"); + public void existingReleaseResourceIsReused() throws IOException { + var destination = tempFolder.resolve("release/tool").toFile(); + Files.createDirectories(destination.toPath().getParent()); + Files.writeString(destination.toPath(), "cached"); + var writes = new AtomicInteger(); + var source = Files.writeString(tempFolder.resolve("source"), "new"); - Files.createFile(firstRelease); - Files.createFile(secondRelease); - waitForProcess(first, firstLog); - waitForProcess(second, secondLog); - } finally { - releaseProcess(firstRelease); - releaseProcess(secondRelease); - stopProcess(first); - stopProcess(second); - } - - assertFalse(ClangResources.getCacheLockFolder(firstVersion).exists()); - assertFalse(ClangResources.getCacheLockFolder(secondVersion).exists()); + assertEquals(destination, + CacheFiles.installFile(destination, copyingResource(source, writes), HELLO_SHA256, "test asset")); + assertEquals(0, writes.get()); + assertEquals("cached", Files.readString(destination.toPath())); } @Test - public void cacheLockRecoversAfterOwningJvmIsTerminated() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var firstAcquired = tempFolder.resolve("first.acquired"); - var firstRelease = tempFolder.resolve("first.release"); - var secondAcquired = tempFolder.resolve("second.acquired"); - var secondRelease = tempFolder.resolve("second.release"); - var firstLog = tempFolder.resolve("first.log"); - var secondLog = tempFolder.resolve("second.log"); + public void concurrentPublicationLeavesOneValidIncludesTree() throws Exception { + var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); + var sha = "b".repeat(64); + var finalFolder = includesRoot.resolve(sha); + var executor = Executors.newFixedThreadPool(4); + var futures = new ArrayList>(); - Process first = startCacheLockProcess(versionFolder, firstAcquired, firstRelease, firstLog); - Process second = null; try { - assertTrue(waitForFile(firstAcquired, PROCESS_TIMEOUT)); - first.destroyForcibly(); - assertTrue(first.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); - - second = startCacheLockProcess(versionFolder, secondAcquired, secondRelease, secondLog); - assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT), - "A cache lock left by a terminated JVM was not recovered"); + for (int i = 0; i < 4; i++) { + futures.add(executor.submit(() -> publishTestIncludes(includesRoot, finalFolder, sha))); + } - Files.createFile(secondRelease); - waitForProcess(second, secondLog); + for (var future : futures) { + assertEquals(finalFolder, future.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + } } finally { - releaseProcess(firstRelease); - releaseProcess(secondRelease); - stopProcess(first); - stopProcess(second); + executor.shutdownNow(); + executor.awaitTermination(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); } - assertFalse(ClangResources.getCacheLockFolder(versionFolder).exists()); - } - - @Test - public void cacheLockDoesNotTreatLiveOwnerAsStaleWhenDirectoryIsOld() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); - var secondAcquired = tempFolder.resolve("second.acquired"); - var secondRelease = tempFolder.resolve("second.release"); - var secondLog = tempFolder.resolve("second.log"); - - ClangResources.CacheLock first = ClangResources.acquireCacheLock(versionFolder); - Process second = null; - try { - Files.setLastModifiedTime(lockFolder, FileTime.from(Instant.now().minus(Duration.ofHours(1)))); - - second = startCacheLockProcess(versionFolder, secondAcquired, secondRelease, secondLog); - assertFalse(waitForFile(secondAcquired, Duration.ofMillis(750)), - "A live cache-lock owner was incorrectly treated as stale"); - - first.close(); - first = null; - assertTrue(waitForFile(secondAcquired, PROCESS_TIMEOUT)); - Files.createFile(secondRelease); - waitForProcess(second, secondLog); - } finally { - if (first != null) { - first.close(); - } - releaseProcess(secondRelease); - stopProcess(second); + assertTrue(ClangResources.isIncludesCacheValid(finalFolder.toFile())); + try (var children = Files.list(includesRoot)) { + assertTrue(children.noneMatch(path -> path.getFileName().toString().startsWith("." + sha + ".tmp-"))); } - - assertFalse(lockFolder.toFile().exists()); } @Test - public void cacheLockUsesProcessStartTimeWhenPidIsStillAlive() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); - Files.createDirectory(lockFolder); - assumeTrue(ProcessHandle.current().info().startInstant().isPresent(), - "The current platform does not expose process start times"); - Files.writeString(lockFolder.resolve("owner"), - ProcessHandle.current().pid() + System.lineSeparator() + Instant.EPOCH + System.lineSeparator()); - - var acquired = tempFolder.resolve("acquired"); - var release = tempFolder.resolve("release"); - var log = tempFolder.resolve("child.log"); - Process child = startCacheLockProcess(versionFolder, acquired, release, log); - try { - assertTrue(waitForFile(acquired, PROCESS_TIMEOUT), - "A lock with a reused PID and a different process start time was not recovered"); - Files.createFile(release); - waitForProcess(child, log); - } finally { - releaseProcess(release); - stopProcess(child); - } - - assertFalse(lockFolder.toFile().exists()); - } + public void abandonedStagingDirectoriesAreCleaned() throws IOException { + var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); + var staging = CacheFiles.createStagingDirectory(includesRoot, ".sha.tmp-"); + Files.setLastModifiedTime(staging, FileTime.from(Instant.now().minus(Duration.ofHours(2)))); - @Test - public void cacheLockRecoversAnOldOwnerlessLock() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); - Files.createDirectory(lockFolder); - Files.setLastModifiedTime(lockFolder, FileTime.from(Instant.now().minus(Duration.ofHours(1)))); - - var acquired = tempFolder.resolve("acquired"); - var release = tempFolder.resolve("release"); - var log = tempFolder.resolve("child.log"); - Process child = startCacheLockProcess(versionFolder, acquired, release, log); - try { - assertTrue(waitForFile(acquired, PROCESS_TIMEOUT), "An old ownerless cache lock was not recovered"); - Files.createFile(release); - waitForProcess(child, log); - } finally { - releaseProcess(release); - stopProcess(child); - } + CacheFiles.deleteStaleStagingDirectories(includesRoot, Instant.now().minus(Duration.ofHours(1))); - assertFalse(lockFolder.toFile().exists()); + assertFalse(Files.exists(staging)); } @Test - public void cacheLockDoesNotStealARecentOwnerlessLock() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); - Files.createDirectory(lockFolder); - - var acquired = tempFolder.resolve("acquired"); - var release = tempFolder.resolve("release"); - var log = tempFolder.resolve("child.log"); - Process child = startCacheLockProcess(versionFolder, acquired, release, log); - try { - assertFalse(waitForFile(acquired, Duration.ofMillis(750)), - "A lock without owner metadata was stolen before its claim was old enough"); + public void staleReleaseAndSharedIncludesAreRemovedAfterSixtyDays() throws IOException { + var releases = Files.createDirectories(tempFolder.resolve("releases")); + var current = Files.createDirectories(releases.resolve("current")); + var staleRelease = Files.createDirectories(releases.resolve("stale")); + var staleIncludes = Files.createDirectories( + ClangResources.getSharedIncludesFolder(tempFolder.toFile(), "c".repeat(64)).toPath().resolve("builtin")); + Files.writeString(staleIncludes.getParent().resolve("entrypoints.txt"), "builtin\n"); + var old = FileTime.from(Instant.now().minus(Duration.ofDays(61))); + Files.setLastModifiedTime(staleRelease, old); + Files.setLastModifiedTime(staleIncludes.getParent(), old); - Files.delete(lockFolder); - assertTrue(waitForFile(acquired, PROCESS_TIMEOUT)); - Files.createFile(release); - waitForProcess(child, log); - } finally { - releaseProcess(release); - stopProcess(child); - } + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + new ClangResources(parser).deleteStaleVersions(Instant.now(), current.toFile()); - assertFalse(lockFolder.toFile().exists()); + assertTrue(Files.exists(current)); + assertFalse(Files.exists(staleRelease)); + assertFalse(Files.exists(staleIncludes.getParent())); } @Test - public void staleCacheCleanupSkipsLockedVersionAcrossJvms() throws Exception { - var currentVersion = Files.createDirectory(tempFolder.resolve("current")).toFile(); - var staleVersion = Files.createDirectory(tempFolder.resolve("stale")).toFile(); - Files.writeString(staleVersion.toPath().resolve("last-used.txt"), - Instant.now().minus(Duration.ofDays(61)).toString()); - - var acquired = tempFolder.resolve("acquired"); - var release = tempFolder.resolve("release"); - var log = tempFolder.resolve("child.log"); - Process child = startCacheLockProcess(staleVersion, acquired, release, log); - try { - assertTrue(waitForFile(acquired, PROCESS_TIMEOUT)); - - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - new ClangResources(parser).deleteStaleVersions(Instant.now(), currentVersion); - assertTrue(staleVersion.isDirectory(), "Stale cleanup deleted a version locked by another JVM"); + public void usingSharedIncludesRefreshesItsLastUsedTime() throws IOException { + var shared = Files.createDirectories( + ClangResources.getSharedIncludesFolder(tempFolder.toFile(), "d".repeat(64)).toPath()); + Files.createDirectories(shared.resolve("builtin")); + Files.writeString(shared.resolve("entrypoints.txt"), "builtin\n"); + Files.setLastModifiedTime(shared, FileTime.from(Instant.now().minus(Duration.ofDays(61)))); - Files.createFile(release); - waitForProcess(child, log); - } finally { - releaseProcess(release); - stopProcess(child); - } + CacheFiles.touch(shared); var parser = CodeParser.newInstance(); parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - new ClangResources(parser).deleteStaleVersions(Instant.now(), currentVersion); - assertFalse(staleVersion.exists()); + new ClangResources(parser).deleteStaleVersions(Instant.now(), + Files.createDirectories(tempFolder.resolve("releases/current")).toFile()); + + assertTrue(Files.exists(shared)); } @Test @@ -418,110 +280,136 @@ public void releaseResourcesCanBeInitializedBySeparateJvms() throws Exception { var secondExecutable = new File(Files.readString(secondDone).trim()); assertEquals(firstExecutable.getAbsoluteFile(), secondExecutable.getAbsoluteFile()); assertTrue(firstExecutable.isFile()); - assertFalse(cacheFolder.toPath().resolve(ClangAstWebResource.getReleaseTag() + ".cache.lock").toFile().exists()); + assertTrue(cacheFolder.toPath().resolve("releases").resolve(ClangAstWebResource.getReleaseTag()).toFile().isDirectory()); } @Test - public void cacheLockReleaseDoesNotRemoveAReclaimedLock() throws Exception { - var versionFolder = Files.createDirectory(tempFolder.resolve("version")).toFile(); - var lockFolder = ClangResources.getCacheLockFolder(versionFolder).toPath(); - ClangResources.CacheLock first = ClangResources.acquireCacheLock(versionFolder); - - Files.writeString(first.ownerFile().toPath(), Long.MAX_VALUE + System.lineSeparator()); - ClangResources.CacheLock second = ClangResources.acquireCacheLock(versionFolder); + public void sameJvmInstancesReuseReleaseFilesAndRefreshSharedIncludes() throws Exception { + var firstParser = newParser(CodeParser.getBuiltinOption()); + var secondParser = newParser(CodeParser.getBuiltinOption()); + var thirdParser = newParser(CodeParser.getBuiltinOption()); + var firstResources = new ClangResources(firstParser); + var secondResources = new ClangResources(secondParser); + var thirdResources = new ClangResources(thirdParser); + var executor = Executors.newFixedThreadPool(3); try { - first.close(); - assertTrue(Files.isDirectory(lockFolder), - "A stale lock owner released and deleted a replacement owner's lock"); - } finally { - second.close(); - } - } + var first = executor.submit(() -> firstResources.getClangFiles(LibcMode.SYSTEM)); + var second = executor.submit(() -> secondResources.getClangFiles(LibcMode.SYSTEM)); + var third = executor.submit(() -> thirdResources.getClangFiles(LibcMode.BUILTIN_AND_LIBC)); - @Test - public void cachedUseDoesNotBypassTheCacheLockBeforeUpdatingLastUsed() throws Exception { - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - var resources = new ClangResources(parser); - var versionFolder = resources.getClangResourceFolder(); - var fakeExecutable = Files.createFile(tempFolder.resolve("fake-tool")).toFile(); - var cache = getClangFilesCache(); - var cacheKey = getClangFilesCacheKey(resources, LibcMode.SYSTEM); - var cachedFiles = new ClangFiles(fakeExecutable, List.of()); - cache.put(cacheKey, cachedFiles); - - ClangResources.CacheLock lock = ClangResources.acquireCacheLock(versionFolder); - var executor = Executors.newSingleThreadExecutor(); - try { - var future = executor.submit(() -> resources.getClangFiles(LibcMode.SYSTEM)); - assertThrows(TimeoutException.class, () -> future.get(750, TimeUnit.MILLISECONDS), - "A cached use updated last-used without coordinating with the cache lock"); + var firstFiles = first.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + var secondFiles = second.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + var thirdFiles = third.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); - lock.close(); - lock = null; - assertSame(cachedFiles, future.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); - } finally { - if (lock != null) { - lock.close(); + assertEquals(firstFiles, secondFiles); + assertEquals(firstFiles.clangExecutable().getAbsoluteFile(), thirdFiles.clangExecutable().getAbsoluteFile()); + assertTrue(firstFiles.clangExecutable().isFile()); + + assumeTrue(!firstFiles.builtinIncludes().isEmpty()); + var shared = new File(firstFiles.builtinIncludes().get(0)).toPath(); + while (!Files.isRegularFile(shared.resolve("entrypoints.txt"))) { + shared = shared.getParent(); } + Files.setLastModifiedTime(shared, FileTime.from(Instant.now().minus(Duration.ofDays(61)))); + firstResources.getClangFiles(LibcMode.SYSTEM); + assertTrue(Files.getLastModifiedTime(shared).toInstant().isAfter(Instant.now().minus(Duration.ofDays(1)))); + } finally { executor.shutdownNow(); executor.awaitTermination(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); - cache.remove(cacheKey); } } @Test - public void sameJvmInstancesShareReleaseCacheInitialization() throws Exception { - var firstParser = CodeParser.newInstance(); - firstParser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - firstParser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + public void builtinCudaIncludesAreAvailableWithSystemLibc() { + var parser = newParser(CodeParser.getBuiltinOption()); + var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); + var hasCudaWrapper = clangFiles.builtinIncludes().stream() + .map(folder -> new File(folder, "__clang_cuda_runtime_wrapper.h")) + .anyMatch(File::isFile); - var secondParser = CodeParser.newInstance(); - secondParser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - secondParser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + assertTrue(hasCudaWrapper, "Built-in CUDA must provide Clang's CUDA runtime wrapper independently of libc"); + } - var thirdParser = CodeParser.newInstance(); - thirdParser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - thirdParser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); + @Test + public void builtinCudaArchiveHasCanonicalInstallationLayout() { + var parser = newParser(CodeParser.getBuiltinOption()); + var cudaFolder = new ClangResources(parser).getBuiltinCudaLib(); - var executor = Executors.newFixedThreadPool(3); - try { - var first = executor.submit(() -> new ClangResources(firstParser).getClangFiles(LibcMode.SYSTEM)); - var second = executor.submit(() -> new ClangResources(secondParser).getClangFiles(LibcMode.SYSTEM)); - var third = executor.submit( - () -> new ClangResources(thirdParser).getClangFiles(LibcMode.BUILTIN_AND_LIBC)); + assertEquals(tempFolder.resolve("cuda/cudalib").toFile().getAbsolutePath(), cudaFolder.getAbsolutePath()); + assertTrue(new File(cudaFolder, "include/cuda.h").isFile()); + assertTrue(new File(cudaFolder, "include/cuda_runtime.h").isFile()); + assertTrue(new File(cudaFolder, "nvvm/libdevice/libdevice.10.bc").isFile()); + } - var firstFiles = first.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); - var secondFiles = second.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); - var thirdFiles = third.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + @Test + public void libcDetectionIsScopedToTheExecutable() throws IOException { + assumeTrue(!SupportedPlatform.getCurrentPlatform().isWindows(), "Shell fixtures require a Unix executable"); - assertSame(firstFiles, secondFiles, "Same-JVM instances did not share the ClangFiles cache entry"); - assertEquals(firstFiles.clangExecutable().getAbsoluteFile(), thirdFiles.clangExecutable().getAbsoluteFile()); - assertTrue(firstFiles.clangExecutable().isFile()); - } finally { - executor.shutdownNow(); - executor.awaitTermination(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); - } + var systemLibcDumper = tempFolder.resolve("system-libc-dumper"); + Files.writeString(systemLibcDumper, + "#!/bin/sh\nprintf '%s\\n' '" + TopLevelNodesParser.getTopLevelNodesHeader() + "'\n"); + assertTrue(systemLibcDumper.toFile().setExecutable(true)); + + var builtinLibcDumper = tempFolder.resolve("builtin-libc-dumper"); + Files.writeString(builtinLibcDumper, "#!/bin/sh\nexit 1\n"); + assertTrue(builtinLibcDumper.toFile().setExecutable(true)); + + assertFalse(ClangResources.useBuiltinLibc(systemLibcDumper.toFile(), LibcMode.AUTO)); + assertTrue(ClangResources.useBuiltinLibc(builtinLibcDumper.toFile(), LibcMode.AUTO)); } - private Process startCacheLockProcess(File versionFolder, Path acquired, Path release, Path log) - throws IOException { + private CodeParser newParser(String cudaPath) { + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + parser.set(CodeParser.CUDA_PATH, cudaPath); + return parser; + } - var javaExecutable = Path.of(System.getProperty("java.home"), "bin", - SupportedPlatform.getCurrentPlatform().isWindows() ? "java.exe" : "java"); + private static ClangDumperManifestAsset asset(String filename, String kind, String platform, String arch) { + return new ClangDumperManifestAsset(filename, kind, platform, arch, 18, HELLO_SHA256); + } - return new ProcessBuilder( - javaExecutable.toString(), - "-cp", - System.getProperty("java.class.path"), - CacheLockProcess.class.getName(), - versionFolder.getAbsolutePath(), - acquired.toAbsolutePath().toString(), - release.toAbsolutePath().toString()) - .redirectErrorStream(true) - .redirectOutput(log.toFile()) - .start(); + private static FileResourceProvider copyingResource(Path source, AtomicInteger writes) { + return new FileResourceProvider() { + @Override + public File write(File folder) { + writes.incrementAndGet(); + try { + var destination = folder.toPath().resolve(getFilename()); + Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING); + return destination.toFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String version() { + return "test"; + } + + @Override + public String getFilename() { + return source.getFileName().toString(); + } + }; + } + + private static Path publishTestIncludes(Path includesRoot, Path finalFolder, String sha) { + Path staging = CacheFiles.createStagingDirectory(includesRoot, "." + sha + ".tmp-"); + try { + try { + Files.createDirectories(staging.resolve("builtin")); + Files.writeString(staging.resolve("entrypoints.txt"), "builtin\n"); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return CacheFiles.publish(staging, finalFolder); + } finally { + CacheFiles.delete(staging); + } } private Process startResourceProcess(File cacheFolder, Path done, Path log) throws IOException { @@ -532,8 +420,7 @@ private Process startResourceProcess(File cacheFolder, Path done, Path log) thro javaExecutable.toString(), "-cp", System.getProperty("java.class.path"), - CacheLockProcess.class.getName(), - "resources", + ResourceProcess.class.getName(), cacheFolder.getAbsolutePath(), done.toAbsolutePath().toString()) .redirectErrorStream(true) @@ -541,34 +428,6 @@ private Process startResourceProcess(File cacheFolder, Path done, Path log) thro .start(); } - @SuppressWarnings("unchecked") - private Map getClangFilesCache() throws ReflectiveOperationException { - var cacheField = ClangResources.class.getDeclaredField("CLANG_FILES_CACHE"); - cacheField.setAccessible(true); - return (Map) cacheField.get(null); - } - - private String getClangFilesCacheKey(ClangResources resources, LibcMode libcMode) { - var source = ClangAstWebResource.getDumperSource(); - var sourceKey = source instanceof Release - ? source + "_" + resources.getClangResourceFolder().getAbsolutePath() - : source.toString(); - return libcMode.name() + "_false_" + sourceKey; - } - - private boolean waitForFile(Path file, Duration timeout) throws InterruptedException { - var deadline = System.nanoTime() + timeout.toNanos(); - do { - if (Files.isRegularFile(file)) { - return true; - } - - Thread.sleep(10); - } while (System.nanoTime() < deadline); - - return Files.isRegularFile(file); - } - private void waitForProcess(Process process, Path log) throws Exception { assertTrue(process.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS), () -> "Child JVM did not finish. Output: " + readLog(log)); @@ -583,12 +442,6 @@ private String readLog(Path log) { } } - private void releaseProcess(Path release) throws IOException { - if (release != null && !Files.exists(release)) { - Files.createFile(release); - } - } - private void stopProcess(Process process) throws InterruptedException { if (process == null || !process.isAlive()) { return; @@ -598,75 +451,16 @@ private void stopProcess(Process process) throws InterruptedException { process.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); } - public static final class CacheLockProcess { + public static final class ResourceProcess { - private CacheLockProcess() { + private ResourceProcess() { } public static void main(String[] args) throws Exception { - if (args[0].equals("resources")) { - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, new File(args[1])); - var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); - Files.writeString(Path.of(args[2]), clangFiles.clangExecutable().getAbsolutePath()); - return; - } - - var versionFolder = new File(args[0]); - var acquired = Path.of(args[1]); - var release = Path.of(args[2]); - - try (var ignored = ClangResources.acquireCacheLock(versionFolder)) { - Files.writeString(acquired, Long.toString(ProcessHandle.current().pid())); - while (!Files.exists(release)) { - Thread.sleep(10); - } - } + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, new File(args[0])); + var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); + Files.writeString(Path.of(args[1]), clangFiles.clangExecutable().getAbsolutePath()); } } - - @Test - public void builtinCudaIncludesAreAvailableWithSystemLibc() { - var parser = CodeParser.newInstance(); - parser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); - - var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); - var hasCudaWrapper = clangFiles.builtinIncludes().stream() - .map(folder -> new File(folder, "__clang_cuda_runtime_wrapper.h")) - .anyMatch(File::isFile); - - assertTrue(hasCudaWrapper, "Built-in CUDA must provide Clang's CUDA runtime wrapper independently of libc"); - } - - @Test - public void builtinCudaArchiveHasCanonicalInstallationLayout() { - var parser = CodeParser.newInstance(); - parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); - parser.set(CodeParser.CUDA_PATH, CodeParser.getBuiltinOption()); - - var cudaFolder = new ClangResources(parser).getBuiltinCudaLib(); - - assertEquals(tempFolder.resolve("cuda/cudalib").toFile().getAbsolutePath(), - cudaFolder.getAbsolutePath()); - assertTrue(new File(cudaFolder, "include/cuda.h").isFile()); - assertTrue(new File(cudaFolder, "include/cuda_runtime.h").isFile()); - assertTrue(new File(cudaFolder, "nvvm/libdevice/libdevice.10.bc").isFile()); - } - - @Test - public void libcDetectionIsScopedToTheExecutable() throws IOException { - assumeTrue(!SupportedPlatform.getCurrentPlatform().isWindows(), "Shell fixtures require a Unix executable"); - - var systemLibcDumper = tempFolder.resolve("system-libc-dumper"); - Files.writeString(systemLibcDumper, - "#!/bin/sh\nprintf '%s\\n' '" + TopLevelNodesParser.getTopLevelNodesHeader() + "'\n"); - assertTrue(systemLibcDumper.toFile().setExecutable(true)); - - var builtinLibcDumper = tempFolder.resolve("builtin-libc-dumper"); - Files.writeString(builtinLibcDumper, "#!/bin/sh\nexit 1\n"); - assertTrue(builtinLibcDumper.toFile().setExecutable(true)); - - assertFalse(ClangResources.useBuiltinLibc(systemLibcDumper.toFile(), LibcMode.AUTO)); - assertTrue(ClangResources.useBuiltinLibc(builtinLibcDumper.toFile(), LibcMode.AUTO)); - } } From 824dc2d9cd0a5beb565da6d18a69319a79328edf Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 5 Aug 2026 22:51:21 +0100 Subject: [PATCH 69/99] fix: enforce immutable clang-dumper include cache --- .../src/pt/up/fe/specs/clang/CacheFiles.java | 27 +++- .../pt/up/fe/specs/clang/ClangResources.java | 91 +++++------ .../up/fe/specs/clang/ClangResourcesTest.java | 150 ++++++++++++++---- 3 files changed, 183 insertions(+), 85 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java index 004b902c58..7434f04a2c 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java @@ -25,6 +25,7 @@ import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystemException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; @@ -123,6 +124,11 @@ static void touch(Path path) { } static void deleteStaleDirectories(Path parent, Instant cutoff, Path excluded) { + deleteStaleDirectories(parent, cutoff, excluded, () -> {}); + } + + static void deleteStaleDirectories(Path parent, Instant cutoff, Path excluded, + Runnable beforeRevalidation) { if (!Files.isDirectory(parent)) { return; } @@ -137,7 +143,7 @@ static void deleteStaleDirectories(Path parent, Instant cutoff, Path excluded) { continue; } - if (Files.getLastModifiedTime(child).toInstant().isBefore(cutoff)) { + if (isStaleAndUnchanged(child, cutoff, beforeRevalidation)) { delete(child); } } @@ -158,7 +164,7 @@ static void deleteStaleStagingDirectories(Path parent, Instant cutoff) { continue; } - if (Files.getLastModifiedTime(child).toInstant().isBefore(cutoff)) { + if (isStaleAndUnchanged(child, cutoff, () -> {})) { delete(child); } } @@ -168,6 +174,23 @@ static void deleteStaleStagingDirectories(Path parent, Instant cutoff) { } } + private static boolean isStaleAndUnchanged(Path path, Instant cutoff, Runnable beforeRevalidation) { + try { + var initialMtime = Files.getLastModifiedTime(path); + if (!initialMtime.toInstant().isBefore(cutoff)) { + return false; + } + + beforeRevalidation.run(); + var currentMtime = Files.getLastModifiedTime(path); + return initialMtime.equals(currentMtime); + } catch (NoSuchFileException e) { + return false; + } catch (IOException e) { + throw new UncheckedIOException("Could not inspect cache path '" + path + "'", e); + } + } + static void delete(Path path) { if (!Files.exists(path)) { return; diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index f83098dcbf..0e00129640 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -23,6 +23,7 @@ import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsSystem; +import pt.up.fe.specs.util.providers.FileResourceProvider; import pt.up.fe.specs.util.system.ProcessOutputAsString; import java.io.File; @@ -57,35 +58,19 @@ public ClangResources(CodeParser options) { public File getBuiltinCudaLib() { var cudaResourceFolder = SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), "cuda"); - var cudaFolder = new File(cudaResourceFolder, "cudalib"); - var zipFile = CacheFiles.installFile(new File(cudaResourceFolder, ClangAstWebResource.CUDA_LIB_FILENAME), - ClangAstWebResource.CUDA_LIB, null, "built-in CUDA archive"); + var cudaFolder = SpecsIo.mkdir(cudaResourceFolder, "cudalib"); + var zipFile = ClangAstWebResource.CUDA_LIB.writeVersioned(cudaResourceFolder, ClangResources.class); - if (isCudaInstallation(cudaFolder)) { - return cudaFolder; + if (zipFile.isNewFile() || !isCudaInstallation(cudaFolder)) { + SpecsIo.deleteFolderContents(cudaFolder); + SpecsIo.extractZip(zipFile.getFile(), cudaFolder); } - if (cudaFolder.exists()) { - CacheFiles.delete(cudaFolder.toPath()); - } - - var stagingFolder = CacheFiles.createStagingDirectory(cudaResourceFolder.toPath(), ".cudalib.tmp-"); - try { - if (!SpecsIo.extractZip(zipFile, stagingFolder.toFile()) || !isCudaInstallation(stagingFolder.toFile())) { - throw new RuntimeException("Built-in CUDA archive did not contain a valid CUDA installation"); - } - - return CacheFiles.publish(stagingFolder, cudaFolder.toPath()).toFile(); - } finally { - CacheFiles.delete(stagingFolder); - } + return cudaFolder; } private static boolean isCudaInstallation(File folder) { - return folder.isDirectory() - && new File(folder, "include/cuda.h").isFile() - && new File(folder, "include/cuda_runtime.h").isFile() - && new File(folder, "nvvm/libdevice/libdevice.10.bc").isFile(); + return folder.isDirectory() && new File(folder, "include/cuda_runtime.h").isFile(); } public ClangFiles getClangFiles(LibcMode libcMode) { @@ -101,8 +86,11 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" + resourceFolder.getAbsolutePath(); var cached = CLANG_FILES_CACHE.get(key); - if (isUsable(cached)) { + if (cached != null) { touchUse(resourceFolder, cached.includesFolder()); + } + + if (isUsable(cached)) { SpecsLogs.debug(() -> "Using cached version of Clang files: " + cached.files()); return cached.files(); } @@ -223,10 +211,6 @@ private File getIncludesRoot() { return new File(options.get(CodeParser.DUMPER_FOLDER), INCLUDES_FOLDERNAME); } - private File getIncludesFolder(String sha256) { - return getSharedIncludesFolder(options.get(CodeParser.DUMPER_FOLDER), sha256); - } - static File getSharedIncludesFolder(File cacheFolder, String sha256) { return new File(new File(cacheFolder, INCLUDES_FOLDERNAME), sha256.toLowerCase(Locale.ROOT)); } @@ -304,21 +288,30 @@ private PreparedIncludes prepareIncludes(ClangDumperManifest manifest, File clan private File prepareIncludesFolder(ClangDumperManifest manifest) { var includesAsset = getCurrentAsset(manifest, "includes"); - var extractedFolder = getIncludesFolder(includesAsset.sha256()); + return resolveIncludes(options.get(CodeParser.DUMPER_FOLDER), includesAsset, + ClangAstWebResource.getAssetResource(includesAsset)); + } - if (isIncludesCacheValid(extractedFolder)) { - return extractedFolder; + static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesAsset, + FileResourceProvider archiveResource) { + var extractedFolder = getSharedIncludesFolder(cacheFolder, includesAsset.sha256()); + if (extractedFolder.exists()) { + CacheFiles.touch(extractedFolder.toPath()); + if (isIncludesCacheValid(extractedFolder)) { + return extractedFolder; + } + + throw invalidIncludesCache(extractedFolder, includesAsset.sha256()); } - var includesRoot = getIncludesRoot(); - CacheFiles.deleteStaleStagingDirectories(includesRoot.toPath(), - Instant.now().minus(STALE_STAGING_MAX_AGE)); - var stagingFolder = CacheFiles.createStagingDirectory(includesRoot.toPath(), + var includesRoot = extractedFolder.getParentFile().toPath(); + CacheFiles.deleteStaleStagingDirectories(includesRoot, Instant.now().minus(STALE_STAGING_MAX_AGE)); + var stagingFolder = CacheFiles.createStagingDirectory(includesRoot, "." + includesAsset.sha256() + ".tmp-"); try { var downloadFolder = CacheFiles.createStagingDirectory(stagingFolder, ".download-"); try { - var archive = ClangAstWebResource.getAssetResource(includesAsset).write(downloadFolder.toFile()); + var archive = archiveResource.write(downloadFolder.toFile()); if (archive == null || !archive.isFile()) { throw new RuntimeException("Could not download clang-dumper includes archive '" + includesAsset.filename() + "'"); @@ -338,27 +331,33 @@ private File prepareIncludesFolder(ClangDumperManifest manifest) { } getIncludeFolders(stagingFolder.toFile()); - if (isIncludesCacheValid(extractedFolder)) { + if (extractedFolder.exists()) { CacheFiles.touch(extractedFolder.toPath()); - return extractedFolder; - } + if (isIncludesCacheValid(extractedFolder)) { + return extractedFolder; + } - if (extractedFolder.exists()) { - CacheFiles.delete(extractedFolder.toPath()); + throw invalidIncludesCache(extractedFolder, includesAsset.sha256()); } var publishedFolder = CacheFiles.publish(stagingFolder, extractedFolder.toPath()).toFile(); + CacheFiles.touch(publishedFolder.toPath()); if (!isIncludesCacheValid(publishedFolder)) { - throw new RuntimeException("Published clang-dumper includes are invalid: '" + publishedFolder + "'"); + throw invalidIncludesCache(publishedFolder, includesAsset.sha256()); } - CacheFiles.touch(publishedFolder.toPath()); return publishedFolder; } finally { CacheFiles.delete(stagingFolder); } } + private static RuntimeException invalidIncludesCache(File includesFolder, String sha256) { + return new RuntimeException("Invalid clang-dumper includes cache directory '" + + includesFolder.getAbsolutePath() + "' for SHA-256 '" + sha256 + + "'; delete this directory manually to regenerate"); + } + static List getIncludeFolders(File extractedFolder) { if (!extractedFolder.isDirectory()) { throw new RuntimeException("Could not find extracted clang-dumper includes folder '" + extractedFolder + "'"); @@ -406,11 +405,7 @@ static boolean isIncludesCacheValid(File includesFolder) { private void updateLastUsedAndCleanupStaleVersions(File resourceFolder, File includesFolder) { var now = Instant.now(); touchUse(resourceFolder, includesFolder); - - var staleCleanup = new Thread(() -> deleteStaleVersions(now, resourceFolder, includesFolder), - "clang-dumper-stale-cache-cleanup"); - staleCleanup.setDaemon(true); - staleCleanup.start(); + deleteStaleVersions(now, resourceFolder, includesFolder); } void deleteStaleVersions(Instant now, File currentVersionFolder) { diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index e074dc952a..80b7447d4b 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -26,18 +26,24 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.HexFormat; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -138,17 +144,58 @@ public void entrypointsPreserveDeclaredIncludeOrder() throws IOException { } @Test - public void sharedIncludesAreAddressedOnlyBySha() throws IOException { - var sha = "a".repeat(64); - var sharedFolder = ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha); - Files.createDirectories(sharedFolder.toPath().resolve("builtin")); - Files.writeString(sharedFolder.toPath().resolve("entrypoints.txt"), "builtin\n"); - - var firstRelease = tempFolder.resolve("releases/v1").toFile(); - var secondRelease = tempFolder.resolve("releases/v2").toFile(); + public void releasesWithTheSameIncludesShaShareOneExtraction() throws Exception { + var archive = createIncludesArchive(); + var sha = sha256(archive); + var firstAsset = new ClangDumperManifestAsset("v1-includes.zip", "includes", "linux", "x64", 18, sha); + var secondAsset = new ClangDumperManifestAsset("v2-includes.zip", "includes", "linux", "x64", 18, sha); + var firstManifest = new ClangDumperManifest(1, List.of(firstAsset)); + var secondManifest = new ClangDumperManifest(1, List.of(secondAsset)); + var firstRelease = Files.createDirectories(tempFolder.resolve("releases/v1")); + var secondRelease = Files.createDirectories(tempFolder.resolve("releases/v2")); + var firstWrites = new AtomicInteger(); + var secondWrites = new AtomicInteger(); + + firstManifest.validate(); + secondManifest.validate(); assertNotEquals(firstRelease, secondRelease); - assertEquals(sharedFolder, ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha)); - assertTrue(ClangResources.isIncludesCacheValid(sharedFolder)); + + var firstIncludes = ClangResources.resolveIncludes(tempFolder.toFile(), firstAsset, + copyingResource(archive, firstWrites)); + var secondIncludes = ClangResources.resolveIncludes(tempFolder.toFile(), secondAsset, + copyingResource(archive, secondWrites)); + + assertEquals(firstAsset, firstManifest.getAsset("linux", "x64", "includes")); + assertEquals(secondAsset, secondManifest.getAsset("linux", "x64", "includes")); + assertEquals(firstIncludes, secondIncludes); + assertEquals(1, firstWrites.get()); + assertEquals(0, secondWrites.get()); + assertTrue(ClangResources.isIncludesCacheValid(firstIncludes)); + try (var children = Files.list(tempFolder.resolve("includes"))) { + assertEquals(1, children.filter(Files::isDirectory).count()); + } + } + + @Test + public void invalidPublishedIncludesFailWithoutRepair() throws IOException { + var sha = "a".repeat(64); + var invalidFolder = ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha); + Files.createDirectories(invalidFolder.toPath()); + Files.writeString(invalidFolder.toPath().resolve("entrypoints.txt"), "missing\n"); + var writes = new AtomicInteger(); + var unusedArchive = tempFolder.resolve("unused.zip"); + + var error = assertThrows(RuntimeException.class, + () -> ClangResources.resolveIncludes(tempFolder.toFile(), + new ClangDumperManifestAsset("includes.zip", "includes", "linux", "x64", 18, sha), + copyingResource(unusedArchive, writes))); + + assertTrue(error.getMessage().contains(invalidFolder.getAbsolutePath())); + assertTrue(error.getMessage().contains(sha)); + assertTrue(error.getMessage().contains("delete this directory manually to regenerate")); + assertTrue(invalidFolder.isDirectory()); + assertEquals("missing\n", Files.readString(invalidFolder.toPath().resolve("entrypoints.txt"))); + assertEquals(0, writes.get()); } @Test @@ -182,29 +229,37 @@ public void existingReleaseResourceIsReused() throws IOException { } @Test - public void concurrentPublicationLeavesOneValidIncludesTree() throws Exception { - var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); - var sha = "b".repeat(64); - var finalFolder = includesRoot.resolve(sha); + public void concurrentInitializationLeavesOneValidIncludesTree() throws Exception { + var archive = createIncludesArchive(); + var sha = sha256(archive); + var asset = new ClangDumperManifestAsset("includes.zip", "includes", "linux", "x64", 18, sha); + var writes = new AtomicInteger(); var executor = Executors.newFixedThreadPool(4); var futures = new ArrayList>(); try { for (int i = 0; i < 4; i++) { - futures.add(executor.submit(() -> publishTestIncludes(includesRoot, finalFolder, sha))); + futures.add(executor.submit(() -> ClangResources.resolveIncludes(tempFolder.toFile(), asset, + copyingResource(archive, writes)).toPath())); } for (var future : futures) { - assertEquals(finalFolder, future.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + assertEquals(ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha).toPath(), + future.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); } } finally { executor.shutdownNow(); executor.awaitTermination(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); } - assertTrue(ClangResources.isIncludesCacheValid(finalFolder.toFile())); - try (var children = Files.list(includesRoot)) { - assertTrue(children.noneMatch(path -> path.getFileName().toString().startsWith("." + sha + ".tmp-"))); + var finalFolder = ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha); + assertTrue(ClangResources.isIncludesCacheValid(finalFolder)); + assertTrue(writes.get() >= 1); + try (var children = Files.list(finalFolder.toPath().getParent())) { + var childPaths = children.toList(); + assertEquals(1, childPaths.stream().filter(Files::isDirectory).count()); + assertTrue(childPaths.stream() + .noneMatch(path -> path.getFileName().toString().startsWith("." + sha + ".tmp-"))); } } @@ -242,13 +297,17 @@ public void staleReleaseAndSharedIncludesAreRemovedAfterSixtyDays() throws IOExc @Test public void usingSharedIncludesRefreshesItsLastUsedTime() throws IOException { - var shared = Files.createDirectories( - ClangResources.getSharedIncludesFolder(tempFolder.toFile(), "d".repeat(64)).toPath()); + var sha = "d".repeat(64); + var shared = Files.createDirectories(ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha).toPath()); Files.createDirectories(shared.resolve("builtin")); Files.writeString(shared.resolve("entrypoints.txt"), "builtin\n"); Files.setLastModifiedTime(shared, FileTime.from(Instant.now().minus(Duration.ofDays(61)))); - - CacheFiles.touch(shared); + var writes = new AtomicInteger(); + var unusedArchive = tempFolder.resolve("unused.zip"); + var asset = new ClangDumperManifestAsset("includes.zip", "includes", "linux", "x64", 18, sha); + assertEquals(shared.toFile(), ClangResources.resolveIncludes(tempFolder.toFile(), asset, + copyingResource(unusedArchive, writes))); + assertEquals(0, writes.get()); var parser = CodeParser.newInstance(); parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); @@ -258,6 +317,18 @@ public void usingSharedIncludesRefreshesItsLastUsedTime() throws IOException { assertTrue(Files.exists(shared)); } + @Test + public void cleanupRevalidationKeepsAnEntryTouchedDuringDeletionCheck() throws IOException { + var releases = Files.createDirectories(tempFolder.resolve("releases")); + var stale = Files.createDirectories(releases.resolve("stale")); + Files.setLastModifiedTime(stale, FileTime.from(Instant.now().minus(Duration.ofDays(61)))); + + CacheFiles.deleteStaleDirectories(releases, Instant.now().minus(Duration.ofDays(60)), null, + () -> CacheFiles.touch(stale)); + + assertTrue(Files.exists(stale)); + } + @Test public void releaseResourcesCanBeInitializedBySeparateJvms() throws Exception { var cacheFolder = Files.createDirectory(tempFolder.resolve("cache")).toFile(); @@ -396,19 +467,28 @@ public String getFilename() { }; } - private static Path publishTestIncludes(Path includesRoot, Path finalFolder, String sha) { - Path staging = CacheFiles.createStagingDirectory(includesRoot, "." + sha + ".tmp-"); - try { - try { - Files.createDirectories(staging.resolve("builtin")); - Files.writeString(staging.resolve("entrypoints.txt"), "builtin\n"); - } catch (IOException e) { - throw new RuntimeException(e); - } + private Path createIncludesArchive() throws IOException { + var archive = tempFolder.resolve("includes.zip"); + try (var zip = new ZipOutputStream(Files.newOutputStream(archive))) { + zip.putNextEntry(new ZipEntry("builtin/")); + zip.closeEntry(); + zip.putNextEntry(new ZipEntry("entrypoints.txt")); + zip.write("builtin\n".getBytes(StandardCharsets.UTF_8)); + zip.closeEntry(); + zip.putNextEntry(new ZipEntry("builtin/header.h")); + zip.write("header\n".getBytes(StandardCharsets.UTF_8)); + zip.closeEntry(); + } - return CacheFiles.publish(staging, finalFolder); - } finally { - CacheFiles.delete(staging); + return archive; + } + + private static String sha256(Path file) throws IOException { + try { + return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256") + .digest(Files.readAllBytes(file))); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); } } From f361e8b80f23214706687d238de301e470e48c35 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 5 Aug 2026 23:14:23 +0100 Subject: [PATCH 70/99] fix: serialize clang-dumper cache maintenance --- .../src/pt/up/fe/specs/clang/CacheFiles.java | 60 +++--- .../pt/up/fe/specs/clang/ClangResources.java | 116 +++++++---- .../up/fe/specs/clang/ClangResourcesTest.java | 185 +++++++++++++++++- 3 files changed, 295 insertions(+), 66 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java index 7434f04a2c..fb913ac04e 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.nio.channels.FileChannel; import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.DirectoryStream; import java.nio.file.FileAlreadyExistsException; @@ -28,18 +29,46 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileTime; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.HexFormat; +import java.util.function.Supplier; final class CacheFiles { + // FileChannel rejects overlapping locks in one JVM; this monitor serializes the small critical section. + private static final Object MAINTENANCE_MONITOR = new Object(); + private static final String MAINTENANCE_LOCK_FILENAME = ".maintenance.lock"; + private CacheFiles() { } + static T withMaintenanceLock(Path cacheRoot, Supplier action) { + var lockPath = cacheRoot.resolve(MAINTENANCE_LOCK_FILENAME); + synchronized (MAINTENANCE_MONITOR) { + try { + Files.createDirectories(cacheRoot); + try (var channel = FileChannel.open(lockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE); + var ignored = channel.lock()) { + return action.get(); + } + } catch (IOException e) { + throw new UncheckedIOException("Could not access cache maintenance lock '" + lockPath + "'", e); + } + } + } + + static void withMaintenanceLock(Path cacheRoot, Runnable action) { + withMaintenanceLock(cacheRoot, () -> { + action.run(); + return null; + }); + } + static Path createStagingDirectory(Path parent, String prefix) { try { Files.createDirectories(parent); @@ -123,12 +152,7 @@ static void touch(Path path) { } } - static void deleteStaleDirectories(Path parent, Instant cutoff, Path excluded) { - deleteStaleDirectories(parent, cutoff, excluded, () -> {}); - } - - static void deleteStaleDirectories(Path parent, Instant cutoff, Path excluded, - Runnable beforeRevalidation) { + static void deleteStaleDirectories(Path cacheRoot, Path parent, Instant cutoff, Path excluded) { if (!Files.isDirectory(parent)) { return; } @@ -143,16 +167,14 @@ static void deleteStaleDirectories(Path parent, Instant cutoff, Path excluded, continue; } - if (isStaleAndUnchanged(child, cutoff, beforeRevalidation)) { - delete(child); - } + withMaintenanceLock(cacheRoot, () -> deleteIfStale(child, cutoff)); } } catch (IOException e) { throw new UncheckedIOException("Could not clean stale cache directories below '" + parent + "'", e); } } - static void deleteStaleStagingDirectories(Path parent, Instant cutoff) { + static void deleteStaleStagingDirectories(Path cacheRoot, Path parent, Instant cutoff) { if (!Files.isDirectory(parent)) { return; } @@ -164,9 +186,7 @@ static void deleteStaleStagingDirectories(Path parent, Instant cutoff) { continue; } - if (isStaleAndUnchanged(child, cutoff, () -> {})) { - delete(child); - } + withMaintenanceLock(cacheRoot, () -> deleteIfStale(child, cutoff)); } } catch (IOException e) { throw new UncheckedIOException("Could not clean stale cache staging directories below '" + parent + "'", @@ -174,18 +194,14 @@ static void deleteStaleStagingDirectories(Path parent, Instant cutoff) { } } - private static boolean isStaleAndUnchanged(Path path, Instant cutoff, Runnable beforeRevalidation) { + private static void deleteIfStale(Path path, Instant cutoff) { try { - var initialMtime = Files.getLastModifiedTime(path); - if (!initialMtime.toInstant().isBefore(cutoff)) { - return false; + if (Files.isDirectory(path) + && Files.getLastModifiedTime(path).toInstant().isBefore(cutoff)) { + delete(path); } - - beforeRevalidation.run(); - var currentMtime = Files.getLastModifiedTime(path); - return initialMtime.equals(currentMtime); } catch (NoSuchFileException e) { - return false; + // Another cleanup or publisher already removed the candidate. } catch (IOException e) { throw new UncheckedIOException("Could not inspect cache path '" + path + "'", e); } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 0e00129640..57a628ae64 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -86,10 +86,6 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" + resourceFolder.getAbsolutePath(); var cached = CLANG_FILES_CACHE.get(key); - if (cached != null) { - touchUse(resourceFolder, cached.includesFolder()); - } - if (isUsable(cached)) { SpecsLogs.debug(() -> "Using cached version of Clang files: " + cached.files()); return cached.files(); @@ -119,17 +115,41 @@ public ClangFiles getClangFiles(LibcMode libcMode) { return selectedFiles.files(); } - private static boolean isUsable(CachedClangFiles cached) { - return cached != null - && cached.files().clangExecutable().isFile() - && (cached.includesFolder() == null || isIncludesCacheValid(cached.includesFolder())); - } + private boolean isUsable(CachedClangFiles cached) { + if (cached == null) { + return false; + } + + return CacheFiles.withMaintenanceLock(options.get(CodeParser.DUMPER_FOLDER).toPath(), () -> { + if (!cached.files().clangExecutable().isFile()) { + return false; + } + + var includesFolder = cached.includesFolder(); + if (includesFolder == null) { + return true; + } + + if (!includesFolder.exists()) { + return false; + } - private static void touchUse(File resourceFolder, File includesFolder) { - CacheFiles.touch(resourceFolder.toPath()); - if (includesFolder != null) { CacheFiles.touch(includesFolder.toPath()); - } + if (!isIncludesCacheValid(includesFolder)) { + throw invalidIncludesCache(includesFolder, includesFolder.getName()); + } + + return true; + }); + } + + private void touchUse(File resourceFolder, File includesFolder) { + CacheFiles.withMaintenanceLock(options.get(CodeParser.DUMPER_FOLDER).toPath(), () -> { + CacheFiles.touch(resourceFolder.toPath()); + if (includesFolder != null) { + CacheFiles.touch(includesFolder.toPath()); + } + }); } static File getLocalExecutable(File buildFolder) { @@ -194,9 +214,12 @@ private void unblockWindowsFile(File executable) { } public File getClangResourceFolder() { - var releaseFolder = SpecsIo.mkdir(getReleasesFolder(), ClangAstWebResource.getReleaseTag()); - CacheFiles.touch(releaseFolder.toPath()); - return releaseFolder; + var cacheFolder = options.get(CodeParser.DUMPER_FOLDER); + return CacheFiles.withMaintenanceLock(cacheFolder.toPath(), () -> { + var releaseFolder = SpecsIo.mkdir(getReleasesFolder(), ClangAstWebResource.getReleaseTag()); + CacheFiles.touch(releaseFolder.toPath()); + return releaseFolder; + }); } public static File getDefaultTempFolder() { @@ -295,17 +318,14 @@ private File prepareIncludesFolder(ClangDumperManifest manifest) { static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesAsset, FileResourceProvider archiveResource) { var extractedFolder = getSharedIncludesFolder(cacheFolder, includesAsset.sha256()); - if (extractedFolder.exists()) { - CacheFiles.touch(extractedFolder.toPath()); - if (isIncludesCacheValid(extractedFolder)) { - return extractedFolder; - } - - throw invalidIncludesCache(extractedFolder, includesAsset.sha256()); + var existingFolder = useExistingIncludes(cacheFolder, extractedFolder, includesAsset.sha256()); + if (existingFolder != null) { + return existingFolder; } var includesRoot = extractedFolder.getParentFile().toPath(); - CacheFiles.deleteStaleStagingDirectories(includesRoot, Instant.now().minus(STALE_STAGING_MAX_AGE)); + CacheFiles.deleteStaleStagingDirectories(cacheFolder.toPath(), includesRoot, + Instant.now().minus(STALE_STAGING_MAX_AGE)); var stagingFolder = CacheFiles.createStagingDirectory(includesRoot, "." + includesAsset.sha256() + ".tmp-"); try { @@ -331,27 +351,43 @@ static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesA } getIncludeFolders(stagingFolder.toFile()); - if (extractedFolder.exists()) { - CacheFiles.touch(extractedFolder.toPath()); - if (isIncludesCacheValid(extractedFolder)) { - return extractedFolder; - } - - throw invalidIncludesCache(extractedFolder, includesAsset.sha256()); + existingFolder = useExistingIncludes(cacheFolder, extractedFolder, includesAsset.sha256()); + if (existingFolder != null) { + return existingFolder; } var publishedFolder = CacheFiles.publish(stagingFolder, extractedFolder.toPath()).toFile(); - CacheFiles.touch(publishedFolder.toPath()); - if (!isIncludesCacheValid(publishedFolder)) { - throw invalidIncludesCache(publishedFolder, includesAsset.sha256()); + existingFolder = useExistingIncludes(cacheFolder, publishedFolder, includesAsset.sha256()); + if (existingFolder == null) { + throw new RuntimeException("Published clang-dumper includes disappeared: '" + + publishedFolder.getAbsolutePath() + "'"); } - return publishedFolder; + return existingFolder; } finally { CacheFiles.delete(stagingFolder); } } + private static File useExistingIncludes(File cacheFolder, File includesFolder, String sha256) { + if (!includesFolder.exists()) { + return null; + } + + return CacheFiles.withMaintenanceLock(cacheFolder.toPath(), () -> { + if (!includesFolder.exists()) { + return null; + } + + CacheFiles.touch(includesFolder.toPath()); + if (!isIncludesCacheValid(includesFolder)) { + throw invalidIncludesCache(includesFolder, sha256); + } + + return includesFolder; + }); + } + private static RuntimeException invalidIncludesCache(File includesFolder, String sha256) { return new RuntimeException("Invalid clang-dumper includes cache directory '" + includesFolder.getAbsolutePath() + "' for SHA-256 '" + sha256 @@ -414,13 +450,15 @@ void deleteStaleVersions(Instant now, File currentVersionFolder) { private void deleteStaleVersions(Instant now, File currentVersionFolder, File currentIncludesFolder) { var cutoff = now.minus(STALE_CACHE_MAX_AGE); + var cacheRoot = options.get(CodeParser.DUMPER_FOLDER).toPath(); try { - CacheFiles.deleteStaleDirectories(getReleasesFolder().toPath(), cutoff, currentVersionFolder.toPath()); - CacheFiles.deleteStaleDirectories(getIncludesRoot().toPath(), cutoff, + CacheFiles.deleteStaleDirectories(cacheRoot, getReleasesFolder().toPath(), cutoff, + currentVersionFolder.toPath()); + CacheFiles.deleteStaleDirectories(cacheRoot, getIncludesRoot().toPath(), cutoff, currentIncludesFolder == null ? null : currentIncludesFolder.toPath()); - CacheFiles.deleteStaleStagingDirectories(getReleasesFolder().toPath(), + CacheFiles.deleteStaleStagingDirectories(cacheRoot, getReleasesFolder().toPath(), now.minus(STALE_STAGING_MAX_AGE)); - CacheFiles.deleteStaleStagingDirectories(getIncludesRoot().toPath(), + CacheFiles.deleteStaleStagingDirectories(cacheRoot, getIncludesRoot().toPath(), now.minus(STALE_STAGING_MAX_AGE)); } catch (RuntimeException e) { SpecsLogs.warn("Could not clean stale clang-dumper cache resources", e); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 80b7447d4b..f26472c990 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -24,12 +24,16 @@ import pt.up.fe.specs.clang.parsers.TopLevelNodesParser; import pt.up.fe.specs.util.providers.FileResourceProvider; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileTime; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -39,6 +43,7 @@ import java.util.HexFormat; import java.util.List; import java.util.concurrent.Executors; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -269,7 +274,7 @@ public void abandonedStagingDirectoriesAreCleaned() throws IOException { var staging = CacheFiles.createStagingDirectory(includesRoot, ".sha.tmp-"); Files.setLastModifiedTime(staging, FileTime.from(Instant.now().minus(Duration.ofHours(2)))); - CacheFiles.deleteStaleStagingDirectories(includesRoot, Instant.now().minus(Duration.ofHours(1))); + CacheFiles.deleteStaleStagingDirectories(tempFolder, includesRoot, Instant.now().minus(Duration.ofHours(1))); assertFalse(Files.exists(staging)); } @@ -318,15 +323,60 @@ public void usingSharedIncludesRefreshesItsLastUsedTime() throws IOException { } @Test - public void cleanupRevalidationKeepsAnEntryTouchedDuringDeletionCheck() throws IOException { + public void maintenanceLockMakesUsageWinOverContendingCleanup() throws Exception { var releases = Files.createDirectories(tempFolder.resolve("releases")); var stale = Files.createDirectories(releases.resolve("stale")); Files.setLastModifiedTime(stale, FileTime.from(Instant.now().minus(Duration.ofDays(61)))); + var usageStarted = new CountDownLatch(1); + var allowUsageToFinish = new CountDownLatch(1); + var cleanupStarted = new CountDownLatch(1); + var executor = Executors.newFixedThreadPool(2); - CacheFiles.deleteStaleDirectories(releases, Instant.now().minus(Duration.ofDays(60)), null, - () -> CacheFiles.touch(stale)); + try { + var usage = executor.submit(() -> CacheFiles.withMaintenanceLock(tempFolder, () -> { + CacheFiles.touch(stale); + usageStarted.countDown(); + awaitLatch(allowUsageToFinish); + })); + assertTrue(usageStarted.await(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + + var cleanup = executor.submit(() -> { + cleanupStarted.countDown(); + CacheFiles.deleteStaleDirectories(tempFolder, releases, + Instant.now().minus(Duration.ofDays(60)), null); + }); + assertTrue(cleanupStarted.await(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + assertFalse(cleanup.isDone()); + + allowUsageToFinish.countDown(); + usage.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + cleanup.get(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + assertTrue(Files.exists(stale)); + } finally { + allowUsageToFinish.countDown(); + executor.shutdownNow(); + executor.awaitTermination(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } + } - assertTrue(Files.exists(stale)); + @Test + public void cleanupWinnerMakesSubsequentIncludesResolutionAcknowledgeTheMiss() throws Exception { + var archive = createIncludesArchive(); + var sha = sha256(archive); + var asset = new ClangDumperManifestAsset("includes.zip", "includes", "linux", "x64", 18, sha); + var shared = ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha); + Files.createDirectories(shared.toPath().resolve("builtin")); + Files.writeString(shared.toPath().resolve("entrypoints.txt"), "builtin\n"); + Files.setLastModifiedTime(shared.toPath(), FileTime.from(Instant.now().minus(Duration.ofDays(61)))); + + CacheFiles.deleteStaleDirectories(tempFolder, shared.toPath().getParent(), + Instant.now().minus(Duration.ofDays(60)), null); + assertFalse(shared.exists()); + + var writes = new AtomicInteger(); + assertEquals(shared, ClangResources.resolveIncludes(tempFolder.toFile(), asset, + copyingResource(archive, writes))); + assertEquals(1, writes.get()); } @Test @@ -354,6 +404,50 @@ public void releaseResourcesCanBeInitializedBySeparateJvms() throws Exception { assertTrue(cacheFolder.toPath().resolve("releases").resolve(ClangAstWebResource.getReleaseTag()).toFile().isDirectory()); } + @Test + public void maintenanceLockIsSharedAcrossJvmProcesses() throws Exception { + var holder = startMaintenanceProcess(MaintenanceLockHolderProcess.class, tempFolder); + var contender = (Process) null; + try (var holderOutput = new BufferedReader( + new InputStreamReader(holder.getInputStream(), StandardCharsets.UTF_8))) { + assertEquals("READY", holderOutput.readLine()); + + contender = startMaintenanceProcess(MaintenanceLockProbeProcess.class, tempFolder); + try (var contenderOutput = new BufferedReader( + new InputStreamReader(contender.getInputStream(), StandardCharsets.UTF_8))) { + assertEquals("BLOCKED", contenderOutput.readLine()); + + holder.getOutputStream().write('\n'); + holder.getOutputStream().flush(); + assertEquals("DONE", holderOutput.readLine()); + + contender.getOutputStream().write('\n'); + contender.getOutputStream().flush(); + assertEquals("ENTERED", contenderOutput.readLine()); + assertEquals("DONE", contenderOutput.readLine()); + assertTrue(contender.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + assertEquals(0, contender.exitValue()); + } + } finally { + try { + holder.getOutputStream().write('\n'); + holder.getOutputStream().flush(); + } catch (IOException ignored) { + // The holder may already have exited after the assertion path. + } + if (contender != null) { + try { + contender.getOutputStream().write('\n'); + contender.getOutputStream().flush(); + } catch (IOException ignored) { + // The contender may already have exited after the assertion path. + } + } + stopProcess(contender); + stopProcess(holder); + } + } + @Test public void sameJvmInstancesReuseReleaseFilesAndRefreshSharedIncludes() throws Exception { var firstParser = newParser(CodeParser.getBuiltinOption()); @@ -467,6 +561,17 @@ public String getFilename() { }; } + private static void awaitLatch(CountDownLatch latch) { + try { + if (!latch.await(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)) { + throw new RuntimeException("Timed out waiting for maintenance-lock test coordination"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + private Path createIncludesArchive() throws IOException { var archive = tempFolder.resolve("includes.zip"); try (var zip = new ZipOutputStream(Files.newOutputStream(archive))) { @@ -508,6 +613,19 @@ private Process startResourceProcess(File cacheFolder, Path done, Path log) thro .start(); } + private Process startMaintenanceProcess(Class processClass, Path cacheFolder) throws IOException { + var javaExecutable = Path.of(System.getProperty("java.home"), "bin", + SupportedPlatform.getCurrentPlatform().isWindows() ? "java.exe" : "java"); + + return new ProcessBuilder( + javaExecutable.toString(), + "-cp", + System.getProperty("java.class.path"), + processClass.getName(), + cacheFolder.toAbsolutePath().toString()) + .start(); + } + private void waitForProcess(Process process, Path log) throws Exception { assertTrue(process.waitFor(PROCESS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS), () -> "Child JVM did not finish. Output: " + readLog(log)); @@ -543,4 +661,61 @@ public static void main(String[] args) throws Exception { Files.writeString(Path.of(args[1]), clangFiles.clangExecutable().getAbsolutePath()); } } + + public static final class MaintenanceLockHolderProcess { + + private MaintenanceLockHolderProcess() { + } + + public static void main(String[] args) { + CacheFiles.withMaintenanceLock(Path.of(args[0]), () -> { + System.out.println("READY"); + System.out.flush(); + try { + System.in.read(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + System.out.println("DONE"); + System.out.flush(); + } + } + + public static final class MaintenanceLockProbeProcess { + + private MaintenanceLockProbeProcess() { + } + + public static void main(String[] args) { + var lockPath = Path.of(args[0], ".maintenance.lock"); + try (var channel = FileChannel.open(lockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { + var lock = channel.tryLock(); + if (lock != null) { + try (lock) { + System.out.println("ACQUIRED"); + System.out.flush(); + } + return; + } + + System.out.println("BLOCKED"); + System.out.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + try { + System.in.read(); + } catch (IOException e) { + throw new RuntimeException(e); + } + CacheFiles.withMaintenanceLock(Path.of(args[0]), () -> { + System.out.println("ENTERED"); + System.out.flush(); + }); + System.out.println("DONE"); + System.out.flush(); + } + } } From 4adea34a5e4f6064bf5c5c38a60f7bcb6c0374ee Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 5 Aug 2026 23:43:34 +0100 Subject: [PATCH 71/99] fix: lock clang-dumper staging directories --- .../src/pt/up/fe/specs/clang/CacheFiles.java | 176 ++++++++++++++++-- .../pt/up/fe/specs/clang/ClangResources.java | 32 ++-- .../up/fe/specs/clang/ClangResourcesTest.java | 27 ++- 3 files changed, 204 insertions(+), 31 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java index fb913ac04e..0b02fefb86 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java @@ -21,6 +21,8 @@ import java.io.OutputStream; import java.io.UncheckedIOException; import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.DirectoryStream; import java.nio.file.FileAlreadyExistsException; @@ -69,13 +71,123 @@ static void withMaintenanceLock(Path cacheRoot, Runnable action) { }); } - static Path createStagingDirectory(Path parent, String prefix) { + static StagingDirectory createStagingDirectory(Path parent, String prefix) { + Path lockPath; try { Files.createDirectories(parent); - return Files.createTempDirectory(parent, prefix); + lockPath = Files.createTempFile(parent, prefix, ".lock"); } catch (IOException e) { throw new UncheckedIOException("Could not create cache staging directory below '" + parent + "'", e); } + + FileChannel channel = null; + FileLock lock = null; + Path stagingPath = null; + try { + channel = FileChannel.open(lockPath, StandardOpenOption.WRITE); + lock = channel.lock(); + stagingPath = lockPath.resolveSibling(removeLockSuffix(lockPath.getFileName().toString())); + Files.createDirectory(stagingPath); + return new StagingDirectory(stagingPath, lockPath, channel, lock); + } catch (IOException e) { + cleanupStagingCreation(stagingPath, lockPath, channel, lock); + throw new UncheckedIOException("Could not create cache staging directory below '" + parent + "'", e); + } catch (RuntimeException e) { + cleanupStagingCreation(stagingPath, lockPath, channel, lock); + throw e; + } + } + + private static String removeLockSuffix(String filename) { + return filename.substring(0, filename.length() - ".lock".length()); + } + + private static void cleanupStagingCreation(Path stagingPath, Path lockPath, + FileChannel channel, FileLock lock) { + if (lock != null) { + try { + lock.release(); + } catch (IOException ignored) { + // The channel close below also releases the OS lock. + } + } + + if (channel != null) { + try { + channel.close(); + } catch (IOException ignored) { + // Best-effort cleanup after staging creation failed. + } + } + + if (stagingPath != null) { + deleteQuietly(stagingPath); + } + + try { + Files.deleteIfExists(lockPath); + } catch (IOException ignored) { + // Best-effort cleanup after staging creation failed. + } + } + + static final class StagingDirectory implements AutoCloseable { + + private final Path path; + private final Path lockPath; + private final FileChannel channel; + private final FileLock lock; + private boolean closed; + + private StagingDirectory(Path path, Path lockPath, FileChannel channel, FileLock lock) { + this.path = path; + this.lockPath = lockPath; + this.channel = channel; + this.lock = lock; + } + + Path path() { + return path; + } + + Path lockPath() { + return lockPath; + } + + @Override + public void close() { + if (closed) { + return; + } + + closed = true; + IOException failure = null; + try { + lock.release(); + } catch (IOException e) { + failure = e; + } + + try { + channel.close(); + } catch (IOException e) { + if (failure == null) { + failure = e; + } + } + + try { + Files.deleteIfExists(lockPath); + } catch (IOException e) { + if (failure == null) { + failure = e; + } + } + + if (failure != null) { + throw new UncheckedIOException("Could not close cache staging lock '" + lockPath + "'", failure); + } + } } static File installFile(File destination, FileResourceProvider resource, String expectedSha256, @@ -84,10 +196,10 @@ static File installFile(File destination, FileResourceProvider resource, String return destination; } - Path stagingDirectory = createStagingDirectory(destination.getParentFile().toPath(), + var stagingDirectory = createStagingDirectory(destination.getParentFile().toPath(), "." + destination.getName() + ".tmp-"); try { - File stagedFile = resource.write(stagingDirectory.toFile()); + File stagedFile = resource.write(stagingDirectory.path().toFile()); if (stagedFile == null || !stagedFile.isFile()) { throw new RuntimeException("Could not download " + description); } @@ -99,7 +211,11 @@ static File installFile(File destination, FileResourceProvider resource, String return publish(stagedFile.toPath(), destination.toPath()).toFile(); } finally { - deleteQuietly(stagingDirectory); + try { + deleteQuietly(stagingDirectory.path()); + } finally { + stagingDirectory.close(); + } } } @@ -174,7 +290,18 @@ static void deleteStaleDirectories(Path cacheRoot, Path parent, Instant cutoff, } } - static void deleteStaleStagingDirectories(Path cacheRoot, Path parent, Instant cutoff) { + private static void deleteIfStale(Path path, Instant cutoff) { + try { + if (Files.isDirectory(path) + && Files.getLastModifiedTime(path).toInstant().isBefore(cutoff)) { + delete(path); + } + } catch (IOException e) { + throw new UncheckedIOException("Could not inspect cache path '" + path + "'", e); + } + } + + static void deleteUnlockedStagingDirectories(Path parent) { if (!Files.isDirectory(parent)) { return; } @@ -186,24 +313,47 @@ static void deleteStaleStagingDirectories(Path cacheRoot, Path parent, Instant c continue; } - withMaintenanceLock(cacheRoot, () -> deleteIfStale(child, cutoff)); + deleteIfUnlockedStaging(child); } } catch (IOException e) { - throw new UncheckedIOException("Could not clean stale cache staging directories below '" + parent + "'", + throw new UncheckedIOException("Could not clean cache staging directories below '" + parent + "'", e); } } - private static void deleteIfStale(Path path, Instant cutoff) { + private static void deleteIfUnlockedStaging(Path stagingPath) { + var lockPath = stagingPath.resolveSibling(stagingPath.getFileName() + ".lock"); + boolean acquired = false; try { - if (Files.isDirectory(path) - && Files.getLastModifiedTime(path).toInstant().isBefore(cutoff)) { - delete(path); + try (var channel = FileChannel.open(lockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { + FileLock lock; + try { + lock = channel.tryLock(); + } catch (OverlappingFileLockException e) { + return; + } + + if (lock == null) { + return; + } + + acquired = true; + try (lock) { + delete(stagingPath); + } } } catch (NoSuchFileException e) { // Another cleanup or publisher already removed the candidate. } catch (IOException e) { - throw new UncheckedIOException("Could not inspect cache path '" + path + "'", e); + throw new UncheckedIOException("Could not inspect cache staging lock '" + lockPath + "'", e); + } finally { + if (acquired) { + try { + Files.deleteIfExists(lockPath); + } catch (IOException e) { + throw new UncheckedIOException("Could not delete cache staging lock '" + lockPath + "'", e); + } + } } } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 57a628ae64..ae7b722f6f 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -46,7 +46,6 @@ public class ClangResources { private static final String RELEASES_FOLDERNAME = "releases"; private static final String INCLUDES_FOLDERNAME = "includes"; private static final Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); - private static final Duration STALE_STAGING_MAX_AGE = Duration.ofHours(1); private static final Map HAS_LIBC = new ConcurrentHashMap<>(); @@ -324,14 +323,13 @@ static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesA } var includesRoot = extractedFolder.getParentFile().toPath(); - CacheFiles.deleteStaleStagingDirectories(cacheFolder.toPath(), includesRoot, - Instant.now().minus(STALE_STAGING_MAX_AGE)); + CacheFiles.deleteUnlockedStagingDirectories(includesRoot); var stagingFolder = CacheFiles.createStagingDirectory(includesRoot, "." + includesAsset.sha256() + ".tmp-"); try { - var downloadFolder = CacheFiles.createStagingDirectory(stagingFolder, ".download-"); + var downloadFolder = CacheFiles.createStagingDirectory(stagingFolder.path(), ".download-"); try { - var archive = archiveResource.write(downloadFolder.toFile()); + var archive = archiveResource.write(downloadFolder.path().toFile()); if (archive == null || !archive.isFile()) { throw new RuntimeException("Could not download clang-dumper includes archive '" + includesAsset.filename() + "'"); @@ -342,21 +340,25 @@ static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesA + "' does not match expected SHA-256 '" + includesAsset.sha256() + "'"); } - if (!SpecsIo.extractZip(archive, stagingFolder.toFile())) { + if (!SpecsIo.extractZip(archive, stagingFolder.path().toFile())) { throw new RuntimeException("Could not extract clang-dumper includes archive '" + includesAsset.filename() + "'"); } } finally { - CacheFiles.delete(downloadFolder); + try { + CacheFiles.delete(downloadFolder.path()); + } finally { + downloadFolder.close(); + } } - getIncludeFolders(stagingFolder.toFile()); + getIncludeFolders(stagingFolder.path().toFile()); existingFolder = useExistingIncludes(cacheFolder, extractedFolder, includesAsset.sha256()); if (existingFolder != null) { return existingFolder; } - var publishedFolder = CacheFiles.publish(stagingFolder, extractedFolder.toPath()).toFile(); + var publishedFolder = CacheFiles.publish(stagingFolder.path(), extractedFolder.toPath()).toFile(); existingFolder = useExistingIncludes(cacheFolder, publishedFolder, includesAsset.sha256()); if (existingFolder == null) { throw new RuntimeException("Published clang-dumper includes disappeared: '" @@ -365,7 +367,11 @@ static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesA return existingFolder; } finally { - CacheFiles.delete(stagingFolder); + try { + CacheFiles.delete(stagingFolder.path()); + } finally { + stagingFolder.close(); + } } } @@ -456,10 +462,8 @@ private void deleteStaleVersions(Instant now, File currentVersionFolder, File cu currentVersionFolder.toPath()); CacheFiles.deleteStaleDirectories(cacheRoot, getIncludesRoot().toPath(), cutoff, currentIncludesFolder == null ? null : currentIncludesFolder.toPath()); - CacheFiles.deleteStaleStagingDirectories(cacheRoot, getReleasesFolder().toPath(), - now.minus(STALE_STAGING_MAX_AGE)); - CacheFiles.deleteStaleStagingDirectories(cacheRoot, getIncludesRoot().toPath(), - now.minus(STALE_STAGING_MAX_AGE)); + CacheFiles.deleteUnlockedStagingDirectories(getReleasesFolder().toPath()); + CacheFiles.deleteUnlockedStagingDirectories(getIncludesRoot().toPath()); } catch (RuntimeException e) { SpecsLogs.warn("Could not clean stale clang-dumper cache resources", e); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index f26472c990..1b282e72c0 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -269,14 +269,33 @@ public void concurrentInitializationLeavesOneValidIncludesTree() throws Exceptio } @Test - public void abandonedStagingDirectoriesAreCleaned() throws IOException { + public void activelyLockedStagingDirectoriesArePreserved() throws Exception { var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); var staging = CacheFiles.createStagingDirectory(includesRoot, ".sha.tmp-"); - Files.setLastModifiedTime(staging, FileTime.from(Instant.now().minus(Duration.ofHours(2)))); + try { + CacheFiles.deleteUnlockedStagingDirectories(includesRoot); + assertTrue(Files.exists(staging.path())); + assertTrue(Files.exists(staging.lockPath())); + } finally { + staging.close(); + CacheFiles.delete(staging.path()); + } + } + + @Test + public void unlockedStagingDirectoriesAreCleaned() throws Exception { + var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); + var staging = CacheFiles.createStagingDirectory(includesRoot, ".sha.tmp-"); + var stagingPath = staging.path(); + var lockPath = staging.lockPath(); + staging.close(); + Files.createFile(lockPath); - CacheFiles.deleteStaleStagingDirectories(tempFolder, includesRoot, Instant.now().minus(Duration.ofHours(1))); + assertTrue(Files.exists(lockPath)); + CacheFiles.deleteUnlockedStagingDirectories(includesRoot); - assertFalse(Files.exists(staging)); + assertFalse(Files.exists(stagingPath)); + assertFalse(Files.exists(lockPath)); } @Test From 32b7d53dc6a07fd665c6f6c334dc75ce5268fa29 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 5 Aug 2026 23:58:29 +0100 Subject: [PATCH 72/99] simplify: clean staging locks directly --- .../src/pt/up/fe/specs/clang/CacheFiles.java | 107 ++++-------------- .../pt/up/fe/specs/clang/ClangResources.java | 16 +-- .../up/fe/specs/clang/ClangResourcesTest.java | 15 ++- 3 files changed, 44 insertions(+), 94 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java index 0b02fefb86..06a6d6e8e5 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java @@ -81,37 +81,36 @@ static StagingDirectory createStagingDirectory(Path parent, String prefix) { } FileChannel channel = null; - FileLock lock = null; Path stagingPath = null; try { channel = FileChannel.open(lockPath, StandardOpenOption.WRITE); - lock = channel.lock(); + channel.lock(); stagingPath = lockPath.resolveSibling(removeLockSuffix(lockPath.getFileName().toString())); Files.createDirectory(stagingPath); - return new StagingDirectory(stagingPath, lockPath, channel, lock); + return new StagingDirectory(stagingPath, lockPath, channel); } catch (IOException e) { - cleanupStagingCreation(stagingPath, lockPath, channel, lock); + cleanupStagingCreation(stagingPath, lockPath, channel); throw new UncheckedIOException("Could not create cache staging directory below '" + parent + "'", e); } catch (RuntimeException e) { - cleanupStagingCreation(stagingPath, lockPath, channel, lock); + cleanupStagingCreation(stagingPath, lockPath, channel); throw e; } } + static Path createTemporaryDirectory(Path parent, String prefix) { + try { + Files.createDirectories(parent); + return Files.createTempDirectory(parent, prefix); + } catch (IOException e) { + throw new UncheckedIOException("Could not create cache temporary directory below '" + parent + "'", e); + } + } + private static String removeLockSuffix(String filename) { return filename.substring(0, filename.length() - ".lock".length()); } - private static void cleanupStagingCreation(Path stagingPath, Path lockPath, - FileChannel channel, FileLock lock) { - if (lock != null) { - try { - lock.release(); - } catch (IOException ignored) { - // The channel close below also releases the OS lock. - } - } - + private static void cleanupStagingCreation(Path stagingPath, Path lockPath, FileChannel channel) { if (channel != null) { try { channel.close(); @@ -131,61 +130,20 @@ private static void cleanupStagingCreation(Path stagingPath, Path lockPath, } } - static final class StagingDirectory implements AutoCloseable { - - private final Path path; - private final Path lockPath; - private final FileChannel channel; - private final FileLock lock; - private boolean closed; - - private StagingDirectory(Path path, Path lockPath, FileChannel channel, FileLock lock) { - this.path = path; - this.lockPath = lockPath; - this.channel = channel; - this.lock = lock; - } - - Path path() { - return path; - } - - Path lockPath() { - return lockPath; - } + record StagingDirectory(Path path, Path lockPath, FileChannel channel) implements AutoCloseable { @Override public void close() { - if (closed) { - return; - } - - closed = true; - IOException failure = null; - try { - lock.release(); - } catch (IOException e) { - failure = e; - } - try { channel.close(); } catch (IOException e) { - if (failure == null) { - failure = e; - } + throw new UncheckedIOException("Could not close cache staging lock '" + lockPath + "'", e); } try { Files.deleteIfExists(lockPath); } catch (IOException e) { - if (failure == null) { - failure = e; - } - } - - if (failure != null) { - throw new UncheckedIOException("Could not close cache staging lock '" + lockPath + "'", failure); + throw new UncheckedIOException("Could not close cache staging lock '" + lockPath + "'", e); } } } @@ -301,19 +259,14 @@ private static void deleteIfStale(Path path, Instant cutoff) { } } - static void deleteUnlockedStagingDirectories(Path parent) { + static void deleteUnlockedStagingLocks(Path parent) { if (!Files.isDirectory(parent)) { return; } - try (DirectoryStream children = Files.newDirectoryStream(parent)) { - for (Path child : children) { - String name = child.getFileName().toString(); - if (!Files.isDirectory(child) || !name.startsWith(".") || !name.contains(".tmp-")) { - continue; - } - - deleteIfUnlockedStaging(child); + try (DirectoryStream locks = Files.newDirectoryStream(parent, ".*.tmp-*.lock")) { + for (Path lock : locks) { + deleteIfUnlockedStagingLock(lock); } } catch (IOException e) { throw new UncheckedIOException("Could not clean cache staging directories below '" + parent + "'", @@ -321,11 +274,9 @@ static void deleteUnlockedStagingDirectories(Path parent) { } } - private static void deleteIfUnlockedStaging(Path stagingPath) { - var lockPath = stagingPath.resolveSibling(stagingPath.getFileName() + ".lock"); - boolean acquired = false; + private static void deleteIfUnlockedStagingLock(Path lockPath) { try { - try (var channel = FileChannel.open(lockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { + try (var channel = FileChannel.open(lockPath, StandardOpenOption.WRITE)) { FileLock lock; try { lock = channel.tryLock(); @@ -337,23 +288,15 @@ private static void deleteIfUnlockedStaging(Path stagingPath) { return; } - acquired = true; try (lock) { - delete(stagingPath); + delete(lockPath.resolveSibling(removeLockSuffix(lockPath.getFileName().toString()))); } } + Files.deleteIfExists(lockPath); } catch (NoSuchFileException e) { // Another cleanup or publisher already removed the candidate. } catch (IOException e) { throw new UncheckedIOException("Could not inspect cache staging lock '" + lockPath + "'", e); - } finally { - if (acquired) { - try { - Files.deleteIfExists(lockPath); - } catch (IOException e) { - throw new UncheckedIOException("Could not delete cache staging lock '" + lockPath + "'", e); - } - } } } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index ae7b722f6f..553018493e 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -323,13 +323,13 @@ static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesA } var includesRoot = extractedFolder.getParentFile().toPath(); - CacheFiles.deleteUnlockedStagingDirectories(includesRoot); + CacheFiles.deleteUnlockedStagingLocks(includesRoot); var stagingFolder = CacheFiles.createStagingDirectory(includesRoot, "." + includesAsset.sha256() + ".tmp-"); try { - var downloadFolder = CacheFiles.createStagingDirectory(stagingFolder.path(), ".download-"); + var downloadFolder = CacheFiles.createTemporaryDirectory(stagingFolder.path(), ".download-"); try { - var archive = archiveResource.write(downloadFolder.path().toFile()); + var archive = archiveResource.write(downloadFolder.toFile()); if (archive == null || !archive.isFile()) { throw new RuntimeException("Could not download clang-dumper includes archive '" + includesAsset.filename() + "'"); @@ -345,11 +345,7 @@ static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesA + includesAsset.filename() + "'"); } } finally { - try { - CacheFiles.delete(downloadFolder.path()); - } finally { - downloadFolder.close(); - } + CacheFiles.delete(downloadFolder); } getIncludeFolders(stagingFolder.path().toFile()); @@ -462,8 +458,8 @@ private void deleteStaleVersions(Instant now, File currentVersionFolder, File cu currentVersionFolder.toPath()); CacheFiles.deleteStaleDirectories(cacheRoot, getIncludesRoot().toPath(), cutoff, currentIncludesFolder == null ? null : currentIncludesFolder.toPath()); - CacheFiles.deleteUnlockedStagingDirectories(getReleasesFolder().toPath()); - CacheFiles.deleteUnlockedStagingDirectories(getIncludesRoot().toPath()); + CacheFiles.deleteUnlockedStagingLocks(getReleasesFolder().toPath()); + CacheFiles.deleteUnlockedStagingLocks(getIncludesRoot().toPath()); } catch (RuntimeException e) { SpecsLogs.warn("Could not clean stale clang-dumper cache resources", e); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 1b282e72c0..c1a51e62c9 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -273,7 +273,7 @@ public void activelyLockedStagingDirectoriesArePreserved() throws Exception { var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); var staging = CacheFiles.createStagingDirectory(includesRoot, ".sha.tmp-"); try { - CacheFiles.deleteUnlockedStagingDirectories(includesRoot); + CacheFiles.deleteUnlockedStagingLocks(includesRoot); assertTrue(Files.exists(staging.path())); assertTrue(Files.exists(staging.lockPath())); } finally { @@ -292,12 +292,23 @@ public void unlockedStagingDirectoriesAreCleaned() throws Exception { Files.createFile(lockPath); assertTrue(Files.exists(lockPath)); - CacheFiles.deleteUnlockedStagingDirectories(includesRoot); + CacheFiles.deleteUnlockedStagingLocks(includesRoot); assertFalse(Files.exists(stagingPath)); assertFalse(Files.exists(lockPath)); } + @Test + public void orphanedStagingLocksAreCleaned() throws IOException { + var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); + var lockPath = includesRoot.resolve(".orphan.tmp-123.lock"); + Files.createFile(lockPath); + + CacheFiles.deleteUnlockedStagingLocks(includesRoot); + + assertFalse(Files.exists(lockPath)); + } + @Test public void staleReleaseAndSharedIncludesAreRemovedAfterSixtyDays() throws IOException { var releases = Files.createDirectories(tempFolder.resolve("releases")); From 2fd5934dee9c2347a480b555d5d66f2af83541c5 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 6 Aug 2026 00:11:43 +0100 Subject: [PATCH 73/99] fix: serialize staging lock registration --- .../src/pt/up/fe/specs/clang/CacheFiles.java | 14 +++++++++----- .../up/fe/specs/clang/ClangAstWebResource.java | 5 +++-- .../src/pt/up/fe/specs/clang/ClangResources.java | 11 ++++++----- .../pt/up/fe/specs/clang/ClangResourcesTest.java | 16 +++++++++------- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java index 06a6d6e8e5..3a42907535 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java @@ -71,7 +71,11 @@ static void withMaintenanceLock(Path cacheRoot, Runnable action) { }); } - static StagingDirectory createStagingDirectory(Path parent, String prefix) { + static StagingDirectory createStagingDirectory(Path cacheRoot, Path parent, String prefix) { + return withMaintenanceLock(cacheRoot, () -> createStagingDirectoryLocked(parent, prefix)); + } + + private static StagingDirectory createStagingDirectoryLocked(Path parent, String prefix) { Path lockPath; try { Files.createDirectories(parent); @@ -148,13 +152,13 @@ public void close() { } } - static File installFile(File destination, FileResourceProvider resource, String expectedSha256, + static File installFile(Path cacheRoot, File destination, FileResourceProvider resource, String expectedSha256, String description) { if (destination.isFile()) { return destination; } - var stagingDirectory = createStagingDirectory(destination.getParentFile().toPath(), + var stagingDirectory = createStagingDirectory(cacheRoot, destination.getParentFile().toPath(), "." + destination.getName() + ".tmp-"); try { File stagedFile = resource.write(stagingDirectory.path().toFile()); @@ -259,14 +263,14 @@ private static void deleteIfStale(Path path, Instant cutoff) { } } - static void deleteUnlockedStagingLocks(Path parent) { + static void deleteUnlockedStagingLocks(Path cacheRoot, Path parent) { if (!Files.isDirectory(parent)) { return; } try (DirectoryStream locks = Files.newDirectoryStream(parent, ".*.tmp-*.lock")) { for (Path lock : locks) { - deleteIfUnlockedStagingLock(lock); + withMaintenanceLock(cacheRoot, () -> deleteIfUnlockedStagingLock(lock)); } } catch (IOException e) { throw new UncheckedIOException("Could not clean cache staging directories below '" + parent + "'", diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index cdbc0c2a1f..f4e6535dfe 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -94,8 +94,9 @@ public static ClangDumperManifest getManifest(File resourceFolder) { var releaseTag = getReleaseTag(); var manifestResource = WebResourceProvider.newInstance(getReleaseBaseUrl(releaseTag), MANIFEST_FILENAME, releaseTag); - var manifestFile = CacheFiles.installFile(new File(resourceFolder, MANIFEST_FILENAME), manifestResource, null, - "clang-dumper release manifest"); + var cacheRoot = resourceFolder.toPath().getParent().getParent(); + var manifestFile = CacheFiles.installFile(cacheRoot, new File(resourceFolder, MANIFEST_FILENAME), + manifestResource, null, "clang-dumper release manifest"); var manifest = GSON.fromJson(SpecsIo.read(manifestFile), ClangDumperManifest.class); if (manifest == null) { diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 553018493e..4de8340c00 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -178,7 +178,8 @@ private File prepareResources(ClangDumperManifest manifest, File resourceFolder) var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; var asset = getCurrentAsset(manifest, executableKind); - File executable = CacheFiles.installFile(new File(resourceFolder, asset.filename()), + File executable = CacheFiles.installFile(options.get(CodeParser.DUMPER_FOLDER).toPath(), + new File(resourceFolder, asset.filename()), ClangAstWebResource.getAssetResource(asset), asset.sha256(), "clang-dumper asset '" + asset.filename() + "'"); @@ -323,8 +324,8 @@ static File resolveIncludes(File cacheFolder, ClangDumperManifestAsset includesA } var includesRoot = extractedFolder.getParentFile().toPath(); - CacheFiles.deleteUnlockedStagingLocks(includesRoot); - var stagingFolder = CacheFiles.createStagingDirectory(includesRoot, + CacheFiles.deleteUnlockedStagingLocks(cacheFolder.toPath(), includesRoot); + var stagingFolder = CacheFiles.createStagingDirectory(cacheFolder.toPath(), includesRoot, "." + includesAsset.sha256() + ".tmp-"); try { var downloadFolder = CacheFiles.createTemporaryDirectory(stagingFolder.path(), ".download-"); @@ -458,8 +459,8 @@ private void deleteStaleVersions(Instant now, File currentVersionFolder, File cu currentVersionFolder.toPath()); CacheFiles.deleteStaleDirectories(cacheRoot, getIncludesRoot().toPath(), cutoff, currentIncludesFolder == null ? null : currentIncludesFolder.toPath()); - CacheFiles.deleteUnlockedStagingLocks(getReleasesFolder().toPath()); - CacheFiles.deleteUnlockedStagingLocks(getIncludesRoot().toPath()); + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, getReleasesFolder().toPath()); + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, getIncludesRoot().toPath()); } catch (RuntimeException e) { SpecsLogs.warn("Could not clean stale clang-dumper cache resources", e); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index c1a51e62c9..4ba36b52e8 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -210,7 +210,8 @@ public void corruptNewDownloadIsRejectedWithoutRetry() throws IOException { var destination = tempFolder.resolve("release/tool").toFile(); assertThrows(RuntimeException.class, - () -> CacheFiles.installFile(destination, copyingResource(source, writes), HELLO_SHA256, "test asset")); + () -> CacheFiles.installFile(tempFolder, destination, copyingResource(source, writes), HELLO_SHA256, + "test asset")); assertEquals(1, writes.get()); assertFalse(destination.exists()); @@ -228,7 +229,8 @@ public void existingReleaseResourceIsReused() throws IOException { var source = Files.writeString(tempFolder.resolve("source"), "new"); assertEquals(destination, - CacheFiles.installFile(destination, copyingResource(source, writes), HELLO_SHA256, "test asset")); + CacheFiles.installFile(tempFolder, destination, copyingResource(source, writes), HELLO_SHA256, + "test asset")); assertEquals(0, writes.get()); assertEquals("cached", Files.readString(destination.toPath())); } @@ -271,9 +273,9 @@ public void concurrentInitializationLeavesOneValidIncludesTree() throws Exceptio @Test public void activelyLockedStagingDirectoriesArePreserved() throws Exception { var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); - var staging = CacheFiles.createStagingDirectory(includesRoot, ".sha.tmp-"); + var staging = CacheFiles.createStagingDirectory(tempFolder, includesRoot, ".sha.tmp-"); try { - CacheFiles.deleteUnlockedStagingLocks(includesRoot); + CacheFiles.deleteUnlockedStagingLocks(tempFolder, includesRoot); assertTrue(Files.exists(staging.path())); assertTrue(Files.exists(staging.lockPath())); } finally { @@ -285,14 +287,14 @@ public void activelyLockedStagingDirectoriesArePreserved() throws Exception { @Test public void unlockedStagingDirectoriesAreCleaned() throws Exception { var includesRoot = Files.createDirectories(tempFolder.resolve("includes")); - var staging = CacheFiles.createStagingDirectory(includesRoot, ".sha.tmp-"); + var staging = CacheFiles.createStagingDirectory(tempFolder, includesRoot, ".sha.tmp-"); var stagingPath = staging.path(); var lockPath = staging.lockPath(); staging.close(); Files.createFile(lockPath); assertTrue(Files.exists(lockPath)); - CacheFiles.deleteUnlockedStagingLocks(includesRoot); + CacheFiles.deleteUnlockedStagingLocks(tempFolder, includesRoot); assertFalse(Files.exists(stagingPath)); assertFalse(Files.exists(lockPath)); @@ -304,7 +306,7 @@ public void orphanedStagingLocksAreCleaned() throws IOException { var lockPath = includesRoot.resolve(".orphan.tmp-123.lock"); Files.createFile(lockPath); - CacheFiles.deleteUnlockedStagingLocks(includesRoot); + CacheFiles.deleteUnlockedStagingLocks(tempFolder, includesRoot); assertFalse(Files.exists(lockPath)); } From 33991841a26e6a87b62a7c49c6b3bcbd905f5ea4 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 6 Aug 2026 00:22:52 +0100 Subject: [PATCH 74/99] fix: clean current release staging locks --- ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 4de8340c00..228eba6b75 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -459,7 +459,7 @@ private void deleteStaleVersions(Instant now, File currentVersionFolder, File cu currentVersionFolder.toPath()); CacheFiles.deleteStaleDirectories(cacheRoot, getIncludesRoot().toPath(), cutoff, currentIncludesFolder == null ? null : currentIncludesFolder.toPath()); - CacheFiles.deleteUnlockedStagingLocks(cacheRoot, getReleasesFolder().toPath()); + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, currentVersionFolder.toPath()); CacheFiles.deleteUnlockedStagingLocks(cacheRoot, getIncludesRoot().toPath()); } catch (RuntimeException e) { SpecsLogs.warn("Could not clean stale clang-dumper cache resources", e); From f0dd0f60ade6bc696bf96d27a7ad55713612abd7 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 6 Aug 2026 02:09:43 +0100 Subject: [PATCH 75/99] feat(clang): assemble CUDA resources from NVIDIA manifest --- ClangAstParser/build.gradle | 3 + ClangAstParser/cuda-release.tag | 1 + .../src/pt/up/fe/specs/clang/CacheFiles.java | 10 + .../fe/specs/clang/ClangAstWebResource.java | 31 +- .../pt/up/fe/specs/clang/ClangResources.java | 38 +- .../pt/up/fe/specs/clang/CudaResources.java | 678 ++++++++++++++++++ .../fe/specs/clang/codeparser/CodeParser.java | 2 +- .../up/fe/specs/clang/ClangResourcesTest.java | 25 +- .../up/fe/specs/clang/CudaResourcesTest.java | 537 ++++++++++++++ .../fe/specs/clang/parser/CxxCudaTester.java | 5 +- .../specs/clang/parser/tests/CxxCudaTest.java | 2 +- Clava-JS/code/sideEffects.ts | 8 +- 12 files changed, 1293 insertions(+), 47 deletions(-) create mode 100644 ClangAstParser/cuda-release.tag create mode 100644 ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java create mode 100644 ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java diff --git a/ClangAstParser/build.gradle b/ClangAstParser/build.gradle index 81313b2d79..26f49755ed 100644 --- a/ClangAstParser/build.gradle +++ b/ClangAstParser/build.gradle @@ -24,6 +24,8 @@ dependencies { implementation 'com.google.code.gson:gson:2.12.1' implementation 'com.google.guava:guava:33.4.0-jre' + implementation 'org.apache.commons:commons-compress:1.27.1' + implementation 'org.tukaani:xz:1.9' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.0' @@ -52,6 +54,7 @@ sourceSets { processResources { from('clang-dumper-release.tag') + from('cuda-release.tag') } // Test coverage configuration diff --git a/ClangAstParser/cuda-release.tag b/ClangAstParser/cuda-release.tag new file mode 100644 index 0000000000..1701b30e16 --- /dev/null +++ b/ClangAstParser/cuda-release.tag @@ -0,0 +1 @@ +12.3.2 diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java index 3a42907535..62fc87a58f 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CacheFiles.java @@ -154,6 +154,11 @@ public void close() { static File installFile(Path cacheRoot, File destination, FileResourceProvider resource, String expectedSha256, String description) { + return installFile(cacheRoot, destination, resource, expectedSha256, -1, description); + } + + static File installFile(Path cacheRoot, File destination, FileResourceProvider resource, String expectedSha256, + long expectedSize, String description) { if (destination.isFile()) { return destination; } @@ -166,6 +171,11 @@ static File installFile(Path cacheRoot, File destination, FileResourceProvider r throw new RuntimeException("Could not download " + description); } + if (expectedSize >= 0 && stagedFile.length() != expectedSize) { + throw new RuntimeException("Downloaded " + description + " does not match expected size '" + + expectedSize + "' (actual: " + stagedFile.length() + ")"); + } + if (expectedSha256 != null && !hasExpectedSha256(stagedFile, expectedSha256)) { throw new RuntimeException("Downloaded " + description + " does not match expected SHA-256 '" + expectedSha256 + "'"); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java index f4e6535dfe..37c62f01d7 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstWebResource.java @@ -28,12 +28,8 @@ public final class ClangAstWebResource { private static final String RELEASE_ROOT = "https://github.com/specs-feup/clang-dumper/releases/download/"; - private static final String CUDA_RELEASE_ROOT = - "https://github.com/specs-feup/clava/releases/download/clang_ast_dumper_v12.0.7.1/"; - public static final String CUDA_LIB_FILENAME = "cudalib.zip"; - static final WebResourceProvider CUDA_LIB = - WebResourceProvider.newInstance(CUDA_RELEASE_ROOT, CUDA_LIB_FILENAME, "v11.3.0"); private static final String RELEASE_TAG_RESOURCE = "clang-dumper-release.tag"; + private static final String CUDA_RELEASE_TAG_RESOURCE = "cuda-release.tag"; public static final String MANIFEST_FILENAME = "clang-dumper-release-manifest.json"; private static final Gson GSON = new Gson(); @@ -47,24 +43,28 @@ public static DumperSource getDumperSource() { } private static DumperSource readDumperSource() { - var inputStream = ClangAstWebResource.class.getClassLoader().getResourceAsStream(RELEASE_TAG_RESOURCE); + return parseDumperSource(readReleaseTag(RELEASE_TAG_RESOURCE)); + } + + private static String readReleaseTag(String resourceName) { + var inputStream = ClangAstWebResource.class.getClassLoader().getResourceAsStream(resourceName); if (inputStream == null) { - throw new RuntimeException("Could not find resource '" + RELEASE_TAG_RESOURCE + "'"); + throw new RuntimeException("Could not find resource '" + resourceName + "'"); } String value; try (inputStream) { value = SpecsIo.read(inputStream).trim(); } catch (IOException e) { - throw new UncheckedIOException("Could not read resource '" + RELEASE_TAG_RESOURCE + "'", e); + throw new UncheckedIOException("Could not read resource '" + resourceName + "'", e); } if (value.isBlank()) { - throw new RuntimeException("Resource '" + RELEASE_TAG_RESOURCE + "' is empty"); + throw new RuntimeException("Resource '" + resourceName + "' is empty"); } - return parseDumperSource(value); + return value; } static DumperSource parseDumperSource(String value) { @@ -90,6 +90,17 @@ public static String getReleaseTag() { throw new IllegalStateException("The clang-dumper resource points to a local build"); } + public static String getCudaReleaseTag() { + var releaseTag = readReleaseTag(CUDA_RELEASE_TAG_RESOURCE); + if (releaseTag.equals(".") || releaseTag.equals("..") + || releaseTag.contains("/") || releaseTag.contains("\\")) { + throw new RuntimeException("Release resource '" + CUDA_RELEASE_TAG_RESOURCE + + "' must contain a single path component: '" + releaseTag + "'"); + } + + return releaseTag; + } + public static ClangDumperManifest getManifest(File resourceFolder) { var releaseTag = getReleaseTag(); var manifestResource = WebResourceProvider.newInstance(getReleaseBaseUrl(releaseTag), MANIFEST_FILENAME, diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 228eba6b75..4432867aa1 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -43,6 +43,7 @@ public class ClangResources { private static final Map CLANG_FILES_CACHE = new ConcurrentHashMap<>(); private static final String CLANG_FOLDERNAME = "clang_ast_exe"; + private static final String CLANG_CACHE_FOLDERNAME = "clang-dumper"; private static final String RELEASES_FOLDERNAME = "releases"; private static final String INCLUDES_FOLDERNAME = "includes"; private static final Duration STALE_CACHE_MAX_AGE = Duration.ofDays(60); @@ -55,21 +56,12 @@ public ClangResources(CodeParser options) { this.options = options; } - public File getBuiltinCudaLib() { - var cudaResourceFolder = SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), "cuda"); - var cudaFolder = SpecsIo.mkdir(cudaResourceFolder, "cudalib"); - var zipFile = ClangAstWebResource.CUDA_LIB.writeVersioned(cudaResourceFolder, ClangResources.class); - - if (zipFile.isNewFile() || !isCudaInstallation(cudaFolder)) { - SpecsIo.deleteFolderContents(cudaFolder); - SpecsIo.extractZip(zipFile.getFile(), cudaFolder); - } - - return cudaFolder; + public static boolean isBuiltinCudaSupported() { + return CudaResources.isSupportedPlatform(); } - private static boolean isCudaInstallation(File folder) { - return folder.isDirectory() && new File(folder, "include/cuda_runtime.h").isFile(); + public File getBuiltinCudaLib() { + return CudaResources.getBuiltinCudaLib(options.get(CodeParser.DUMPER_FOLDER).toPath()); } public ClangFiles getClangFiles(LibcMode libcMode) { @@ -119,7 +111,7 @@ private boolean isUsable(CachedClangFiles cached) { return false; } - return CacheFiles.withMaintenanceLock(options.get(CodeParser.DUMPER_FOLDER).toPath(), () -> { + return CacheFiles.withMaintenanceLock(getClangCacheRoot().toPath(), () -> { if (!cached.files().clangExecutable().isFile()) { return false; } @@ -143,7 +135,7 @@ private boolean isUsable(CachedClangFiles cached) { } private void touchUse(File resourceFolder, File includesFolder) { - CacheFiles.withMaintenanceLock(options.get(CodeParser.DUMPER_FOLDER).toPath(), () -> { + CacheFiles.withMaintenanceLock(getClangCacheRoot().toPath(), () -> { CacheFiles.touch(resourceFolder.toPath()); if (includesFolder != null) { CacheFiles.touch(includesFolder.toPath()); @@ -178,7 +170,7 @@ private File prepareResources(ClangDumperManifest manifest, File resourceFolder) var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; var asset = getCurrentAsset(manifest, executableKind); - File executable = CacheFiles.installFile(options.get(CodeParser.DUMPER_FOLDER).toPath(), + File executable = CacheFiles.installFile(getClangCacheRoot().toPath(), new File(resourceFolder, asset.filename()), ClangAstWebResource.getAssetResource(asset), asset.sha256(), "clang-dumper asset '" + asset.filename() + "'"); @@ -214,7 +206,7 @@ private void unblockWindowsFile(File executable) { } public File getClangResourceFolder() { - var cacheFolder = options.get(CodeParser.DUMPER_FOLDER); + var cacheFolder = getClangCacheRoot(); return CacheFiles.withMaintenanceLock(cacheFolder.toPath(), () -> { var releaseFolder = SpecsIo.mkdir(getReleasesFolder(), ClangAstWebResource.getReleaseTag()); CacheFiles.touch(releaseFolder.toPath()); @@ -227,11 +219,15 @@ public static File getDefaultTempFolder() { } private File getReleasesFolder() { - return SpecsIo.mkdir(options.get(CodeParser.DUMPER_FOLDER), RELEASES_FOLDERNAME); + return SpecsIo.mkdir(getClangCacheRoot(), RELEASES_FOLDERNAME); } private File getIncludesRoot() { - return new File(options.get(CodeParser.DUMPER_FOLDER), INCLUDES_FOLDERNAME); + return new File(getClangCacheRoot(), INCLUDES_FOLDERNAME); + } + + private File getClangCacheRoot() { + return new File(options.get(CodeParser.DUMPER_FOLDER), CLANG_CACHE_FOLDERNAME); } static File getSharedIncludesFolder(File cacheFolder, String sha256) { @@ -311,7 +307,7 @@ private PreparedIncludes prepareIncludes(ClangDumperManifest manifest, File clan private File prepareIncludesFolder(ClangDumperManifest manifest) { var includesAsset = getCurrentAsset(manifest, "includes"); - return resolveIncludes(options.get(CodeParser.DUMPER_FOLDER), includesAsset, + return resolveIncludes(getClangCacheRoot(), includesAsset, ClangAstWebResource.getAssetResource(includesAsset)); } @@ -453,7 +449,7 @@ void deleteStaleVersions(Instant now, File currentVersionFolder) { private void deleteStaleVersions(Instant now, File currentVersionFolder, File currentIncludesFolder) { var cutoff = now.minus(STALE_CACHE_MAX_AGE); - var cacheRoot = options.get(CodeParser.DUMPER_FOLDER).toPath(); + var cacheRoot = getClangCacheRoot().toPath(); try { CacheFiles.deleteStaleDirectories(cacheRoot, getReleasesFolder().toPath(), cutoff, currentVersionFolder.toPath()); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java new file mode 100644 index 0000000000..b1a27491ef --- /dev/null +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java @@ -0,0 +1,678 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.SpecsLogs; +import pt.up.fe.specs.util.providers.FileResourceProvider; +import pt.up.fe.specs.util.providers.WebResourceProvider; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Pattern; + +/** + * Downloads the NVIDIA redistribution packages required by Clang and assembles them into the CUDA root expected by + * the bundled dumper. + * + *

    CUDA resources are release-addressed. Archives from different CUDA releases therefore never share a cache + * destination, even when NVIDIA publishes identical bytes for both releases.

    + */ +final class CudaResources { + + static final String NVIDIA_REDIST_ROOT = "https://developer.download.nvidia.com/compute/cuda/redist/"; + static final String LINUX_X86_64_PLATFORM = "linux-x86_64"; + static final String LINUX_PPC64LE_PLATFORM = "linux-ppc64le"; + static final String LINUX_SBSA_PLATFORM = "linux-sbsa"; + static final String WINDOWS_X86_64_PLATFORM = "windows-x86_64"; + static final List REQUIRED_COMPONENTS = List.of("cuda_cudart", "cuda_nvcc", "libcurand", "cuda_cccl"); + static final String PLATFORM_FILENAME = ".platform"; + + private static final String CUDA_FOLDERNAME = "cuda"; + private static final String CUDA_LIB_FOLDERNAME = "cudalib"; + private static final String ARCHIVES_FOLDERNAME = "archives"; + private static final String MANIFEST_FILENAME_PREFIX = "redistrib_"; + private static final String MANIFEST_FILENAME_SUFFIX = ".json"; + private static final Set MANIFEST_FIELDS = Set.of("release_date", "release_label", "release_product"); + private static final Set COMPONENT_FIELDS = Set.of("name", "license", "license_path", "version"); + private static final Pattern SHA256_PATTERN = Pattern.compile("[0-9a-fA-F]{64}"); + private static final List REQUIRED_FILES = List.of( + "include/cuda.h", + "include/cuda_runtime.h", + "include/texture_fetch_functions.h", + "include/curand_mtgp32_kernel.h", + "include/nv/target", + "include/crt/host_config.h", + "nvvm/libdevice/libdevice.10.bc"); + + private CudaResources() { + } + + static File getBuiltinCudaLib(Path cacheRoot) { + var releaseTag = ClangAstWebResource.getCudaReleaseTag(); + var platform = requireSupportedPlatform(); + var platformFolder = getPlatformFolder(cacheRoot, releaseTag, platform); + var installationFolder = getInstallationFolder(platformFolder); + + // A published installation is immutable. A malformed one is an operator error, not an invitation to repair it + // in place, because doing so could race with a reader that already selected this release. + if (Files.exists(installationFolder.toPath(), LinkOption.NOFOLLOW_LINKS)) { + return useExistingInstallation(cacheRoot, platformFolder, platform, installationFolder); + } + + var manifest = getManifest(cacheRoot, platformFolder); + return install(cacheRoot, platformFolder, releaseTag, platform, manifest, CudaResources::getArchiveResource); + } + + static CudaPlatform requireSupportedPlatform() { + return getCurrentPlatform(); + } + + static boolean isSupportedPlatform() { + try { + getCurrentPlatform(); + return true; + } catch (RuntimeException e) { + return false; + } + } + + static CudaPlatform getCurrentPlatform() { + return new CudaPlatform(getManifestPlatform(SupportedPlatform.getCurrentPlatform(), System.getProperty("os.arch"))); + } + + static String getManifestPlatform(SupportedPlatform platform, String architecture) { + Objects.requireNonNull(platform, "platform"); + Objects.requireNonNull(architecture, "architecture"); + var normalizedArchitecture = architecture.toLowerCase(Locale.ROOT); + + if (platform.isWindows() && isX86_64(normalizedArchitecture)) { + return WINDOWS_X86_64_PLATFORM; + } + + if (platform.isLinux()) { + if (isX86_64(normalizedArchitecture)) { + return LINUX_X86_64_PLATFORM; + } + + if (normalizedArchitecture.equals("ppc64le")) { + return LINUX_PPC64LE_PLATFORM; + } + + if (normalizedArchitecture.equals("aarch64") || normalizedArchitecture.equals("arm64")) { + return LINUX_SBSA_PLATFORM; + } + } + + throw new RuntimeException("The CUDA manifest does not provide a supported package for platform '" + + platform + "' and architecture '" + architecture + "'"); + } + + private static boolean isX86_64(String architecture) { + return architecture.equals("amd64") || architecture.equals("x86_64"); + } + + static File getPlatformFolder(Path cacheRoot, String releaseTag, CudaPlatform platform) { + return cacheRoot.resolve(CUDA_FOLDERNAME).resolve(releaseTag).resolve(platform.manifestName()).toFile(); + } + + static File getInstallationFolder(File platformFolder) { + return new File(platformFolder, CUDA_LIB_FOLDERNAME); + } + + static File getArchiveFile(File platformFolder, CudaPackage cudaPackage) { + var relativePath = cudaPackage.archive().relativePath(); + var archiveName = relativePath.substring(relativePath.lastIndexOf('/') + 1); + return new File(new File(new File(platformFolder, ARCHIVES_FOLDERNAME), cudaPackage.component()), archiveName); + } + + static NvidiaCudaManifest getManifest(File resourceFolder) { + return getManifest(getCacheRoot(resourceFolder), resourceFolder); + } + + static NvidiaCudaManifest getManifest(Path cacheRoot, File resourceFolder) { + var releaseTag = ClangAstWebResource.getCudaReleaseTag(); + var manifestFilename = getManifestFilename(releaseTag); + var resource = WebResourceProvider.newInstance(NVIDIA_REDIST_ROOT, manifestFilename, releaseTag); + var manifestFile = CacheFiles.installFile(cacheRoot, new File(resourceFolder, manifestFilename), resource, null, + "NVIDIA CUDA redistribution manifest"); + var manifest = parseManifest(SpecsIo.read(manifestFile)); + validateManifest(manifest, releaseTag); + return manifest; + } + + private static Path getCacheRoot(File resourceFolder) { + var cacheRoot = resourceFolder.toPath(); + for (int i = 0; i < 3; i++) { + cacheRoot = cacheRoot.getParent(); + if (cacheRoot == null) { + throw new RuntimeException("CUDA resource folder is not below a cache root: '" + resourceFolder + "'"); + } + } + + return cacheRoot; + } + + static String getManifestFilename(String releaseTag) { + return MANIFEST_FILENAME_PREFIX + releaseTag + MANIFEST_FILENAME_SUFFIX; + } + + static WebResourceProvider getArchiveResource(CudaPackage cudaPackage) { + var archive = cudaPackage.archive(); + return WebResourceProvider.newInstance(NVIDIA_REDIST_ROOT, archive.relativePath(), + "cuda-" + cudaPackage.component() + "-" + archive.sha256()); + } + + static NvidiaCudaManifest parseManifest(String json) { + if (json == null || json.isBlank()) { + throw new RuntimeException("NVIDIA CUDA redistribution manifest is empty"); + } + + final JsonObject root; + try { + root = JsonParser.parseString(json).getAsJsonObject(); + } catch (RuntimeException e) { + throw new RuntimeException("Could not parse NVIDIA CUDA redistribution manifest", e); + } + + var releaseDate = getRequiredString(root, "release_date", "manifest"); + var releaseLabel = getRequiredString(root, "release_label", "manifest"); + var releaseProduct = getRequiredString(root, "release_product", "manifest"); + var components = new LinkedHashMap(); + + for (var entry : root.entrySet()) { + if (MANIFEST_FIELDS.contains(entry.getKey())) { + continue; + } + + if (!entry.getValue().isJsonObject()) { + throw new RuntimeException("NVIDIA CUDA manifest component '" + entry.getKey() + + "' is not an object"); + } + + components.put(entry.getKey(), parseComponent(entry.getKey(), entry.getValue().getAsJsonObject())); + } + + if (components.isEmpty()) { + throw new RuntimeException("NVIDIA CUDA redistribution manifest does not contain components"); + } + + return new NvidiaCudaManifest(releaseDate, releaseLabel, releaseProduct, components); + } + + static File install(Path cacheRoot, File resourceFolder, String releaseTag, CudaPlatform platform, + NvidiaCudaManifest manifest, + Function archiveResourceFactory) { + validateManifest(manifest, releaseTag); + Objects.requireNonNull(archiveResourceFactory, "archiveResourceFactory"); + + var installationFolder = getInstallationFolder(resourceFolder); + if (Files.exists(installationFolder.toPath(), LinkOption.NOFOLLOW_LINKS)) { + return useExistingInstallation(cacheRoot, resourceFolder, platform, installationFolder); + } + + var downloadedPackages = manifest.getRequiredPackages(platform.manifestName()).stream() + .map(cudaPackage -> downloadPackage(cacheRoot, resourceFolder, cudaPackage, archiveResourceFactory)) + .toList(); + + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, resourceFolder.toPath()); + var stagingDirectory = CacheFiles.createStagingDirectory(cacheRoot, resourceFolder.toPath(), ".cudalib.tmp-"); + try { + try { + assemble(stagingDirectory.path().toFile(), platform, downloadedPackages); + } catch (IOException e) { + throw new UncheckedIOException("Could not assemble CUDA resources in '" + + stagingDirectory.path() + "'", e); + } + + if (!isCudaInstallation(stagingDirectory.path().toFile(), platform.manifestName())) { + throw new RuntimeException("Assembled CUDA resources failed structural validation in '" + + stagingDirectory.path() + "'"); + } + + var publishedFolder = CacheFiles.publish(stagingDirectory.path(), installationFolder.toPath()).toFile(); + return useExistingInstallation(cacheRoot, resourceFolder, platform, publishedFolder); + } finally { + try { + CacheFiles.delete(stagingDirectory.path()); + } finally { + stagingDirectory.close(); + } + } + } + + private static CudaResources.DownloadedPackage downloadPackage(Path cacheRoot, File resourceFolder, + CudaPackage cudaPackage, + Function factory) { + var destination = getArchiveFile(resourceFolder, cudaPackage); + var archiveParent = destination.getParentFile().toPath(); + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, archiveParent); + var archive = CacheFiles.installFile(cacheRoot, destination, factory.apply(cudaPackage), + cudaPackage.archive().sha256(), cudaPackage.archive().size(), + "NVIDIA CUDA archive '" + destination.getName() + "'"); + return new DownloadedPackage(cudaPackage, archive); + } + + private static File useExistingInstallation(Path cacheRoot, File resourceFolder, CudaPlatform platform, + File installationFolder) { + var validInstallation = CacheFiles.withMaintenanceLock(cacheRoot, () -> { + if (!isCudaInstallation(installationFolder, platform.manifestName())) { + throw invalidInstallation(installationFolder, platform.manifestName()); + } + + CacheFiles.touch(resourceFolder.toPath()); + CacheFiles.touch(resourceFolder.getParentFile().toPath()); + return installationFolder; + }); + + cleanup(cacheRoot, resourceFolder); + SpecsLogs.debug(() -> "Using cached CUDA resources: " + validInstallation); + return validInstallation; + } + + private static void cleanup(Path cacheRoot, File resourceFolder) { + var cudaRoot = cacheRoot.resolve(CUDA_FOLDERNAME); + var releaseFolder = resourceFolder.toPath().getParent(); + var cutoff = Instant.now().minus(Duration.ofDays(60)); + try { + CacheFiles.deleteStaleDirectories(cacheRoot, cudaRoot, cutoff, releaseFolder); + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, releaseFolder); + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, resourceFolder.toPath()); + for (var component : REQUIRED_COMPONENTS) { + CacheFiles.deleteUnlockedStagingLocks(cacheRoot, + resourceFolder.toPath().resolve(ARCHIVES_FOLDERNAME).resolve(component)); + } + } catch (RuntimeException e) { + SpecsLogs.warn("Could not clean stale CUDA cache resources", e); + } + } + + static boolean isCudaInstallation(File folder, String platform) { + Path root = folder.toPath().toAbsolutePath().normalize(); + if (!Files.isDirectory(root, LinkOption.NOFOLLOW_LINKS) + || !Files.isDirectory(root.resolve("bin"), LinkOption.NOFOLLOW_LINKS)) { + return false; + } + + for (var requiredFile : REQUIRED_FILES) { + if (!Files.isRegularFile(root.resolve(requiredFile), LinkOption.NOFOLLOW_LINKS)) { + return false; + } + } + + var platformFile = root.resolve(PLATFORM_FILENAME); + if (!Files.isRegularFile(platformFile, LinkOption.NOFOLLOW_LINKS)) { + return false; + } + + try { + return platform.equals(Files.readString(platformFile).trim()); + } catch (IOException e) { + return false; + } + } + + private static RuntimeException invalidInstallation(File folder, String platform) { + return new RuntimeException("Invalid published CUDA installation '" + folder.getAbsolutePath() + + "' for platform '" + platform + "'; delete this directory manually to regenerate"); + } + + private static void validateManifest(NvidiaCudaManifest manifest, String releaseTag) { + Objects.requireNonNull(manifest, "manifest"); + if (!releaseTag.equals(manifest.releaseLabel())) { + throw new RuntimeException("NVIDIA CUDA manifest release label '" + manifest.releaseLabel() + + "' does not match the configured CUDA release tag '" + releaseTag + "'"); + } + + if (!"cuda".equals(manifest.releaseProduct())) { + throw new RuntimeException("NVIDIA redistribution manifest is not a CUDA manifest: '" + + manifest.releaseProduct() + "'"); + } + } + + private static NvidiaCudaComponent parseComponent(String componentName, JsonObject component) { + var name = getRequiredString(component, "name", "component '" + componentName + "'"); + var version = getRequiredString(component, "version", "component '" + componentName + "'"); + var archives = new LinkedHashMap(); + + for (var entry : component.entrySet()) { + if (COMPONENT_FIELDS.contains(entry.getKey())) { + continue; + } + + if (!entry.getValue().isJsonObject()) { + continue; + } + + var platform = entry.getKey(); + var platformObject = entry.getValue().getAsJsonObject(); + var relativePath = getRequiredString(platformObject, "relative_path", + "component '" + componentName + "', platform '" + platform + "'"); + var sha256 = getRequiredString(platformObject, "sha256", + "component '" + componentName + "', platform '" + platform + "'"); + validateSha256(sha256, componentName, platform); + var size = getRequiredLong(platformObject, "size", + "component '" + componentName + "', platform '" + platform + "'"); + validateRelativePath(relativePath, "component '" + componentName + "', platform '" + platform + "'"); + if (size < 0) { + throw new RuntimeException("NVIDIA CUDA archive size must not be negative for component '" + + componentName + "', platform '" + platform + "'"); + } + + archives.put(platform, new CudaArchive(relativePath, sha256, size)); + } + + if (archives.isEmpty()) { + throw new RuntimeException("NVIDIA CUDA component '" + componentName + "' has no platform archives"); + } + + return new NvidiaCudaComponent(name, version, archives); + } + + private static String getRequiredString(JsonObject object, String field, String owner) { + JsonElement value = object.get(field); + if (value == null || value.isJsonNull() || !value.isJsonPrimitive() + || !value.getAsJsonPrimitive().isString()) { + throw new RuntimeException("NVIDIA CUDA " + owner + " is missing string field '" + field + "'"); + } + + var stringValue = value.getAsString().trim(); + if (stringValue.isEmpty()) { + throw new RuntimeException("NVIDIA CUDA " + owner + " has an empty field '" + field + "'"); + } + + return stringValue; + } + + private static long getRequiredLong(JsonObject object, String field, String owner) { + JsonElement value = object.get(field); + if (value == null || value.isJsonNull() || !value.isJsonPrimitive()) { + throw new RuntimeException("NVIDIA CUDA " + owner + " is missing numeric field '" + field + "'"); + } + + try { + return value.getAsLong(); + } catch (RuntimeException e) { + throw new RuntimeException("NVIDIA CUDA " + owner + " has an invalid numeric field '" + field + "'", e); + } + } + + private static void validateSha256(String sha256, String component, String platform) { + if (!SHA256_PATTERN.matcher(sha256).matches()) { + throw new RuntimeException("NVIDIA CUDA archive for component '" + component + "', platform '" + + platform + "' has an invalid SHA-256: '" + sha256 + "'"); + } + } + + private static void validateRelativePath(String relativePath, String owner) { + if (relativePath.startsWith("/") || relativePath.startsWith("\\") || relativePath.contains("\\") + || hasWindowsDrivePrefix(relativePath)) { + throw new RuntimeException("NVIDIA CUDA " + owner + " has an unsafe relative path: '" + relativePath + "'"); + } + + for (var segment : relativePath.split("/", -1)) { + if (segment.isEmpty() || segment.equals(".") || segment.equals("..")) { + throw new RuntimeException("NVIDIA CUDA " + owner + " has an unsafe relative path: '" + relativePath + "'"); + } + } + } + + static void assemble(File stagingFolder, CudaPlatform platform, List packages) throws IOException { + Files.writeString(new File(stagingFolder, PLATFORM_FILENAME).toPath(), platform.manifestName()); + Files.createDirectories(new File(stagingFolder, "bin").toPath()); + + for (var downloadedPackage : packages) { + var component = downloadedPackage.cudaPackage().component(); + var sourceRoots = switch (component) { + case "cuda_cudart", "libcurand", "cuda_cccl" -> List.of("include"); + case "cuda_nvcc" -> List.of("include/crt", "nvvm/libdevice/libdevice.10.bc"); + default -> throw new RuntimeException("Unsupported NVIDIA CUDA component '" + component + "'"); + }; + + extractArchive(downloadedPackage.archiveFile(), stagingFolder, sourceRoots); + } + } + + private static void extractArchive(File archive, File destination, List sourceRoots) throws IOException { + if (archive.getName().endsWith(".zip")) { + try (InputStream input = Files.newInputStream(archive.toPath()); + var archiveInput = new ZipArchiveInputStream(input)) { + extractArchiveEntries(archiveInput, archive, destination, sourceRoots, + entry -> entry instanceof ZipArchiveEntry zipEntry && isRegularZipEntry(zipEntry)); + } + return; + } + + if (!archive.getName().endsWith(".tar.xz")) { + throw new RuntimeException("Unsupported NVIDIA CUDA archive format: '" + archive + "'"); + } + + try (InputStream input = Files.newInputStream(archive.toPath()); + var xzInput = new XZCompressorInputStream(input); + var tarInput = new TarArchiveInputStream(xzInput)) { + extractArchiveEntries(tarInput, archive, destination, sourceRoots, + entry -> entry instanceof TarArchiveEntry tarEntry && tarEntry.isFile()); + } + } + + private static void extractArchiveEntries(ArchiveInputStream archiveInput, File archive, File destination, + List sourceRoots, ArchiveEntryPolicy entryPolicy) throws IOException { + var foundRoots = new HashSet(); + String archiveRoot = null; + ArchiveEntry entry; + while ((entry = archiveInput.getNextEntry()) != null) { + var entryName = validateArchiveEntryName(entry.getName(), archive); + var topLevel = getTopLevelPath(entryName); + + if (archiveRoot == null) { + archiveRoot = topLevel; + } else if (!archiveRoot.equals(topLevel)) { + throw new RuntimeException("NVIDIA CUDA archive contains multiple top-level folders: '" + + archiveRoot + "' and '" + topLevel + "'"); + } + + var relativeName = entryName.length() == archiveRoot.length() + ? "" + : entryName.substring(archiveRoot.length() + 1); + var sourceRoot = findSourceRoot(relativeName, sourceRoots); + if (sourceRoot == null) { + continue; + } + + if (!entry.isDirectory() && !entryPolicy.isRegular(entry)) { + throw new RuntimeException("NVIDIA CUDA archive contains a non-regular selected entry: '" + + entryName + "'"); + } + + if (entry.isDirectory()) { + Files.createDirectories(destination.toPath().resolve(relativeName)); + continue; + } + + foundRoots.add(sourceRoot); + copyArchiveFile(archiveInput, destination.toPath().resolve(relativeName), entryName); + } + + if (!foundRoots.containsAll(sourceRoots)) { + var missingRoots = new ArrayList<>(sourceRoots); + missingRoots.removeAll(foundRoots); + throw new RuntimeException("NVIDIA CUDA archive '" + archive + "' is missing selected paths: " + missingRoots); + } + } + + private static boolean isRegularZipEntry(ZipArchiveEntry entry) { + if (entry.isUnixSymlink()) { + return false; + } + + var unixMode = entry.getUnixMode(); + return unixMode == 0 || (unixMode & 0170000) == 0100000; + } + + private static String validateArchiveEntryName(String entryName, File archive) { + if (entryName == null || entryName.isBlank() || entryName.startsWith("/") || entryName.contains("\\") + || hasWindowsDrivePrefix(entryName)) { + throw new RuntimeException("NVIDIA CUDA archive '" + archive + "' contains an unsafe path: '" + entryName + "'"); + } + + var normalizedName = entryName.endsWith("/") ? entryName.substring(0, entryName.length() - 1) : entryName; + if (normalizedName.isEmpty()) { + throw new RuntimeException("NVIDIA CUDA archive '" + archive + "' contains an empty path"); + } + + for (var segment : normalizedName.split("/", -1)) { + if (segment.isEmpty() || segment.equals(".") || segment.equals("..")) { + throw new RuntimeException("NVIDIA CUDA archive '" + archive + "' contains an unsafe path: '" + entryName + "'"); + } + } + + return normalizedName; + } + + private static boolean hasWindowsDrivePrefix(String path) { + return path.length() >= 2 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':'; + } + + private static String getTopLevelPath(String entryName) { + var separator = entryName.indexOf('/'); + return separator == -1 ? entryName : entryName.substring(0, separator); + } + + private static String findSourceRoot(String relativeName, List sourceRoots) { + for (var sourceRoot : sourceRoots) { + if (relativeName.equals(sourceRoot) || relativeName.startsWith(sourceRoot + "/")) { + return sourceRoot; + } + } + + return null; + } + + private static void copyArchiveFile(InputStream input, Path destination, String entryName) throws IOException { + Files.createDirectories(destination.getParent()); + var temporaryFile = Files.createTempFile(destination.getParent(), ".cuda-entry-", ".tmp"); + try { + try (OutputStream output = Files.newOutputStream(temporaryFile)) { + input.transferTo(output); + } + + if (Files.exists(destination, LinkOption.NOFOLLOW_LINKS)) { + if (!Files.isRegularFile(destination, LinkOption.NOFOLLOW_LINKS) + || Files.mismatch(destination, temporaryFile) != -1) { + throw new RuntimeException("NVIDIA CUDA archives contain conflicting files at '" + entryName + "'"); + } + return; + } + + try { + Files.move(temporaryFile, destination, StandardCopyOption.ATOMIC_MOVE); + } catch (java.nio.file.AtomicMoveNotSupportedException e) { + Files.move(temporaryFile, destination); + } catch (java.nio.file.FileAlreadyExistsException e) { + if (!Files.isRegularFile(destination, LinkOption.NOFOLLOW_LINKS) + || Files.mismatch(destination, temporaryFile) != -1) { + throw new RuntimeException("NVIDIA CUDA archives contain conflicting files at '" + entryName + "'"); + } + } + } finally { + Files.deleteIfExists(temporaryFile); + } + } + + record NvidiaCudaManifest(String releaseDate, String releaseLabel, String releaseProduct, + Map components) { + + NvidiaCudaManifest { + components = Map.copyOf(components); + } + + List getRequiredPackages(String platform) { + return REQUIRED_COMPONENTS.stream() + .map(component -> new CudaPackage(component, getComponent(component).getArchive(platform))) + .toList(); + } + + private NvidiaCudaComponent getComponent(String component) { + var value = components.get(component); + if (value == null) { + throw new RuntimeException("NVIDIA CUDA manifest is missing required component '" + component + "'"); + } + + return value; + } + } + + record NvidiaCudaComponent(String name, String version, Map archives) { + + NvidiaCudaComponent { + archives = Map.copyOf(archives); + } + + CudaArchive getArchive(String platform) { + var archive = archives.get(platform); + if (archive == null) { + throw new RuntimeException("NVIDIA CUDA component '" + name + "' has no archive for platform '" + + platform + "'"); + } + + return archive; + } + } + + record CudaPackage(String component, CudaArchive archive) { + } + + record CudaArchive(String relativePath, String sha256, long size) { + } + + record DownloadedPackage(CudaPackage cudaPackage, File archiveFile) { + } + + record CudaPlatform(String manifestName) { + } + + @FunctionalInterface + private interface ArchiveEntryPolicy { + + boolean isRegular(ArchiveEntry entry); + } +} diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java index f7441ed77d..e65c941a94 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/CodeParser.java @@ -45,7 +45,7 @@ public abstract class CodeParser extends ADataClass { .setLabel("CUDA Path (empty: uses system installed; : uses builtin version)") .setDefaultString(""); public static final DataKey DUMPER_FOLDER = KeyFactory.folder("dumperFolder") - .setLabel("The base cache folder for the clang-dumper. Clava stores each dumper release in a versioned subfolder and downloads it if not found. If not set, a temporary folder will be used.") + .setLabel("The base cache folder for Clava's downloaded resources. Clava stores each clang-dumper and CUDA release in a versioned subfolder and downloads it if not found. If not set, a temporary folder will be used.") .setDefault(ClangResources::getDefaultTempFolder); /** diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 4ba36b52e8..4b30a90673 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -313,11 +313,13 @@ public void orphanedStagingLocksAreCleaned() throws IOException { @Test public void staleReleaseAndSharedIncludesAreRemovedAfterSixtyDays() throws IOException { - var releases = Files.createDirectories(tempFolder.resolve("releases")); + var clangCacheRoot = clangCacheRoot(); + var releases = Files.createDirectories(clangCacheRoot.resolve("releases")); var current = Files.createDirectories(releases.resolve("current")); var staleRelease = Files.createDirectories(releases.resolve("stale")); var staleIncludes = Files.createDirectories( - ClangResources.getSharedIncludesFolder(tempFolder.toFile(), "c".repeat(64)).toPath().resolve("builtin")); + ClangResources.getSharedIncludesFolder(clangCacheRoot.toFile(), "c".repeat(64)).toPath() + .resolve("builtin")); Files.writeString(staleIncludes.getParent().resolve("entrypoints.txt"), "builtin\n"); var old = FileTime.from(Instant.now().minus(Duration.ofDays(61))); Files.setLastModifiedTime(staleRelease, old); @@ -335,21 +337,23 @@ public void staleReleaseAndSharedIncludesAreRemovedAfterSixtyDays() throws IOExc @Test public void usingSharedIncludesRefreshesItsLastUsedTime() throws IOException { var sha = "d".repeat(64); - var shared = Files.createDirectories(ClangResources.getSharedIncludesFolder(tempFolder.toFile(), sha).toPath()); + var clangCacheRoot = clangCacheRoot(); + var shared = Files.createDirectories( + ClangResources.getSharedIncludesFolder(clangCacheRoot.toFile(), sha).toPath()); Files.createDirectories(shared.resolve("builtin")); Files.writeString(shared.resolve("entrypoints.txt"), "builtin\n"); Files.setLastModifiedTime(shared, FileTime.from(Instant.now().minus(Duration.ofDays(61)))); var writes = new AtomicInteger(); var unusedArchive = tempFolder.resolve("unused.zip"); var asset = new ClangDumperManifestAsset("includes.zip", "includes", "linux", "x64", 18, sha); - assertEquals(shared.toFile(), ClangResources.resolveIncludes(tempFolder.toFile(), asset, + assertEquals(shared.toFile(), ClangResources.resolveIncludes(clangCacheRoot.toFile(), asset, copyingResource(unusedArchive, writes))); assertEquals(0, writes.get()); var parser = CodeParser.newInstance(); parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); new ClangResources(parser).deleteStaleVersions(Instant.now(), - Files.createDirectories(tempFolder.resolve("releases/current")).toFile()); + Files.createDirectories(clangCacheRoot.resolve("releases/current")).toFile()); assertTrue(Files.exists(shared)); } @@ -433,7 +437,8 @@ public void releaseResourcesCanBeInitializedBySeparateJvms() throws Exception { var secondExecutable = new File(Files.readString(secondDone).trim()); assertEquals(firstExecutable.getAbsoluteFile(), secondExecutable.getAbsoluteFile()); assertTrue(firstExecutable.isFile()); - assertTrue(cacheFolder.toPath().resolve("releases").resolve(ClangAstWebResource.getReleaseTag()).toFile().isDirectory()); + assertTrue(cacheFolder.toPath().resolve("clang-dumper").resolve("releases") + .resolve(ClangAstWebResource.getReleaseTag()).toFile().isDirectory()); } @Test @@ -533,7 +538,9 @@ public void builtinCudaArchiveHasCanonicalInstallationLayout() { var parser = newParser(CodeParser.getBuiltinOption()); var cudaFolder = new ClangResources(parser).getBuiltinCudaLib(); - assertEquals(tempFolder.resolve("cuda/cudalib").toFile().getAbsolutePath(), cudaFolder.getAbsolutePath()); + var cudaPlatform = CudaResources.getCurrentPlatform().manifestName(); + assertEquals(tempFolder.resolve("cuda").resolve(ClangAstWebResource.getCudaReleaseTag()) + .resolve(cudaPlatform).resolve("cudalib").toFile().getAbsolutePath(), cudaFolder.getAbsolutePath()); assertTrue(new File(cudaFolder, "include/cuda.h").isFile()); assertTrue(new File(cudaFolder, "include/cuda_runtime.h").isFile()); assertTrue(new File(cudaFolder, "nvvm/libdevice/libdevice.10.bc").isFile()); @@ -563,6 +570,10 @@ private CodeParser newParser(String cudaPath) { return parser; } + private Path clangCacheRoot() { + return tempFolder.resolve("clang-dumper"); + } + private static ClangDumperManifestAsset asset(String filename, String kind, String platform, String arch) { return new ClangDumperManifestAsset(filename, kind, platform, arch, 18, HELLO_SHA256); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java new file mode 100644 index 0000000000..f3169882a7 --- /dev/null +++ b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java @@ -0,0 +1,537 @@ +/** + * Copyright 2026 SPeCS. + *

    + * 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 + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * 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 pt.up.fe.specs.clang; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import pt.up.fe.specs.clang.codeparser.CodeParser; +import pt.up.fe.specs.util.providers.FileResourceProvider; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CudaResourcesTest { + + private static final String PLATFORM = CudaResources.LINUX_X86_64_PLATFORM; + private static final String RELEASE = "12.3.2"; + private static final Duration TEST_TIMEOUT = Duration.ofSeconds(10); + + @TempDir + Path tempFolder; + + @Test + public void manifestSelectsRequiredComponentsAndValidatesMetadata() { + var manifest = CudaResources.parseManifest(manifestJson()); + + assertEquals(RELEASE, ClangAstWebResource.getCudaReleaseTag()); + assertEquals(RELEASE, manifest.releaseLabel()); + assertEquals("cuda", manifest.releaseProduct()); + assertEquals(List.of("cuda_cudart", "cuda_nvcc", "libcurand", "cuda_cccl"), + manifest.getRequiredPackages(PLATFORM).stream() + .map(CudaResources.CudaPackage::component) + .toList()); + assertEquals(CudaResources.NVIDIA_REDIST_ROOT + "cuda_cudart/linux-x86_64/cuda_cudart.tar.xz", + CudaResources.getArchiveResource(manifest.getRequiredPackages(PLATFORM).get(0)).getUrlString()); + assertEquals("redistrib_12.3.2.json", CudaResources.getManifestFilename(RELEASE)); + + assertThrows(RuntimeException.class, () -> CudaResources.parseManifest("")); + assertThrows(RuntimeException.class, + () -> CudaResources.parseManifest(manifestJson().replace(SHA256, "not-a-sha"))); + assertThrows(RuntimeException.class, + () -> CudaResources.parseManifest(manifestJson().replace( + "cuda_cudart/linux-x86_64/cuda_cudart.tar.xz", "../cuda_cudart.tar.xz"))); + assertThrows(RuntimeException.class, + () -> CudaResources.parseManifest(manifestJson().replace("\"size\": \"1\"", "\"size\": \"-1\""))); + + var wrongRelease = CudaResources.parseManifest(manifestJson() + .replace("\"release_label\": \"12.3.2\"", "\"release_label\": \"12.3.1\"")); + var releaseError = assertThrows(RuntimeException.class, () -> CudaResources.install( + tempFolder, tempFolder.resolve("wrong-release").toFile(), RELEASE, + new CudaResources.CudaPlatform(PLATFORM), wrongRelease, ignored -> { + throw new AssertionError("Archive downloads must not start for an invalid manifest"); + })); + assertTrue(releaseError.getMessage().contains("release label")); + + var wrongProduct = CudaResources.parseManifest(manifestJson() + .replace("\"release_product\": \"cuda\"", "\"release_product\": \"other\"")); + var productError = assertThrows(RuntimeException.class, () -> CudaResources.install( + tempFolder, tempFolder.resolve("wrong-product").toFile(), RELEASE, + new CudaResources.CudaPlatform(PLATFORM), wrongProduct, ignored -> { + throw new AssertionError("Archive downloads must not start for an invalid manifest"); + })); + assertTrue(productError.getMessage().contains("not a CUDA manifest")); + + var missingComponents = new LinkedHashMap<>(manifest.components()); + missingComponents.remove("cuda_cccl"); + var missingComponent = new CudaResources.NvidiaCudaManifest( + manifest.releaseDate(), manifest.releaseLabel(), manifest.releaseProduct(), missingComponents); + assertThrows(RuntimeException.class, () -> missingComponent.getRequiredPackages(PLATFORM)); + } + + @Test + public void manifestPlatformMatchesNvidiaKeysAndRejectsUnavailablePlatforms() { + assertEquals(CudaResources.LINUX_X86_64_PLATFORM, + CudaResources.getManifestPlatform(SupportedPlatform.LINUX, "amd64")); + assertEquals(CudaResources.LINUX_PPC64LE_PLATFORM, + CudaResources.getManifestPlatform(SupportedPlatform.LINUX, "ppc64le")); + assertEquals(CudaResources.LINUX_SBSA_PLATFORM, + CudaResources.getManifestPlatform(SupportedPlatform.LINUX, "aarch64")); + assertEquals(CudaResources.WINDOWS_X86_64_PLATFORM, + CudaResources.getManifestPlatform(SupportedPlatform.WINDOWS, "x86_64")); + + assertThrows(RuntimeException.class, + () -> CudaResources.getManifestPlatform(SupportedPlatform.MAC_OS, "aarch64")); + assertThrows(RuntimeException.class, + () -> CudaResources.getManifestPlatform(SupportedPlatform.WINDOWS, "aarch64")); + } + + @Test + public void installationFetchesOnlyTheSelectedPlatformArchives() throws IOException { + var archives = createArchives(); + var manifest = addUnusedPlatforms(archives.manifest()); + var platform = new CudaResources.CudaPlatform(PLATFORM); + var platformFolder = CudaResources.getPlatformFolder(tempFolder, RELEASE, platform); + var writes = new AtomicInteger(); + + var installation = CudaResources.install(tempFolder, platformFolder, RELEASE, platform, manifest, + cudaPackage -> { + assertTrue(cudaPackage.archive().relativePath().contains("/" + PLATFORM + "/")); + var source = archives.files().get(cudaPackage.component()); + return copyingResource(source, source.getFileName().toString(), writes); + }); + + assertTrue(CudaResources.isCudaInstallation(installation, PLATFORM)); + assertEquals(CudaResources.REQUIRED_COMPONENTS.size(), writes.get()); + } + + @Test + public void archiveDownloadsRequireBothExpectedSizeAndSha256() throws IOException { + var source = Files.writeString(tempFolder.resolve("cuda_cudart.tar.xz"), "archive"); + var actualSize = Files.size(source); + var actualSha = sha256(source); + var platformFolder = CudaResources.getPlatformFolder(tempFolder, RELEASE, new CudaResources.CudaPlatform(PLATFORM)); + + var wrongSize = manifestForSingleArchive(source, actualSha, actualSize + 1); + var sizeError = assertThrows(RuntimeException.class, + () -> install(wrongSize, platformFolder, source, new AtomicInteger())); + assertTrue(sizeError.getMessage().contains("expected size")); + assertFalse(CudaResources.getArchiveFile(platformFolder, + wrongSize.getRequiredPackages(PLATFORM).get(0)).isFile()); + + var wrongSha = manifestForSingleArchive(source, "0".repeat(64), actualSize); + var shaError = assertThrows(RuntimeException.class, + () -> install(wrongSha, platformFolder, source, new AtomicInteger())); + assertTrue(shaError.getMessage().contains("expected SHA-256")); + } + + @Test + public void assembleSupportsTarXzAndZipPackages() throws IOException { + var archives = createArchives(); + var stagingFolder = Files.createDirectory(tempFolder.resolve("cudalib")); + + CudaResources.assemble(stagingFolder.toFile(), new CudaResources.CudaPlatform(PLATFORM), + downloadedPackages(archives)); + + assertEquals(PLATFORM, Files.readString(stagingFolder.resolve(CudaResources.PLATFORM_FILENAME))); + assertTrue(Files.isDirectory(stagingFolder.resolve("bin"))); + assertEquals("cuda.h", Files.readString(stagingFolder.resolve("include/cuda.h"))); + assertEquals("cuda_runtime.h", Files.readString(stagingFolder.resolve("include/cuda_runtime.h"))); + assertEquals("texture", Files.readString(stagingFolder.resolve("include/texture_fetch_functions.h"))); + assertEquals("curand", Files.readString(stagingFolder.resolve("include/curand_mtgp32_kernel.h"))); + assertEquals("target", Files.readString(stagingFolder.resolve("include/nv/target"))); + assertEquals("host_config", Files.readString(stagingFolder.resolve("include/crt/host_config.h"))); + assertEquals("libdevice", Files.readString(stagingFolder.resolve("nvvm/libdevice/libdevice.10.bc"))); + assertFalse(Files.exists(stagingFolder.resolve("bin/discarded"))); + assertFalse(Files.exists(stagingFolder.resolve("bin/discarded.exe"))); + assertTrue(CudaResources.isCudaInstallation(stagingFolder.toFile(), PLATFORM)); + } + + @Test + public void extractionRejectsTraversalEntries() throws IOException { + var traversalArchive = tempFolder.resolve("traversal.tar.xz"); + writeTarXz(traversalArchive, Map.of( + "cuda_cudart/include/../escape.h", bytes("escape"))); + var traversalPackage = new CudaResources.DownloadedPackage( + new CudaResources.CudaPackage("cuda_cudart", + new CudaResources.CudaArchive("cuda_cudart/linux-x86_64/cuda_cudart.tar.xz", SHA256, 1)), + traversalArchive.toFile()); + + assertThrows(RuntimeException.class, () -> CudaResources.assemble( + Files.createDirectory(tempFolder.resolve("traversal-out")).toFile(), + new CudaResources.CudaPlatform(PLATFORM), List.of(traversalPackage))); + assertFalse(Files.exists(tempFolder.resolve("escape.h"))); + + var driveArchive = tempFolder.resolve("drive.tar.xz"); + writeTarXz(driveArchive, Map.of("C:/escape.h", bytes("escape"))); + var drivePackage = new CudaResources.DownloadedPackage( + new CudaResources.CudaPackage("cuda_cudart", + new CudaResources.CudaArchive("cuda_cudart/linux-x86_64/cuda_cudart.tar.xz", SHA256, 1)), + driveArchive.toFile()); + assertThrows(RuntimeException.class, () -> CudaResources.assemble( + Files.createDirectory(tempFolder.resolve("drive-out")).toFile(), + new CudaResources.CudaPlatform(PLATFORM), List.of(drivePackage))); + } + + @Test + public void requiredComponentsAreStoredPerReleaseWithoutDeduplication() throws IOException { + var archives = createArchives(); + var firstRelease = CudaResources.getPlatformFolder(tempFolder, RELEASE, + new CudaResources.CudaPlatform(PLATFORM)); + var secondRelease = CudaResources.getPlatformFolder(tempFolder, "13.3.1", + new CudaResources.CudaPlatform(PLATFORM)); + + var first = install(archives, firstRelease, new AtomicInteger()); + var second = install(archives, secondRelease, new AtomicInteger()); + + assertTrue(CudaResources.isCudaInstallation(first, PLATFORM)); + assertTrue(CudaResources.isCudaInstallation(second, PLATFORM)); + assertNotEquals(CudaResources.getArchiveFile(firstRelease, + archives.manifest().getRequiredPackages(PLATFORM).get(0)).toPath(), + CudaResources.getArchiveFile(secondRelease, + archives.manifest().getRequiredPackages(PLATFORM).get(0)).toPath()); + } + + @Test + public void existingValidInstallationIsReusedAndUsageIsRefreshed() throws IOException { + var platform = new CudaResources.CudaPlatform(hostPlatform()); + var platformFolder = CudaResources.getPlatformFolder(tempFolder, RELEASE, platform); + var installation = CudaResources.getInstallationFolder(platformFolder); + writeValidInstallation(installation.toPath(), PLATFORM); + var old = FileTime.from(Instant.now().minus(Duration.ofDays(61))); + Files.setLastModifiedTime(platformFolder.toPath().getParent(), old); + + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + var result = new ClangResources(parser).getBuiltinCudaLib(); + + assertEquals(installation.getAbsoluteFile(), result.getAbsoluteFile()); + assertTrue(Files.getLastModifiedTime(platformFolder.toPath().getParent()).toInstant() + .isAfter(Instant.now().minus(Duration.ofDays(1)))); + } + + @Test + public void invalidPublishedInstallationFailsWithoutRepair() throws IOException { + var platform = new CudaResources.CudaPlatform(hostPlatform()); + var platformFolder = CudaResources.getPlatformFolder(tempFolder, RELEASE, platform); + var installation = CudaResources.getInstallationFolder(platformFolder); + Files.createDirectories(installation.toPath()); + Files.writeString(installation.toPath().resolve("sentinel"), "do not repair"); + + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + var error = assertThrows(RuntimeException.class, () -> new ClangResources(parser).getBuiltinCudaLib()); + + assertTrue(error.getMessage().contains(installation.getAbsolutePath())); + assertTrue(error.getMessage().contains("delete this directory manually to regenerate")); + assertEquals("do not repair", Files.readString(installation.toPath().resolve("sentinel"))); + } + + @Test + public void concurrentPublicationLeavesOneValidInstallation() throws Exception { + var archives = createArchives(); + var platform = new CudaResources.CudaPlatform(PLATFORM); + var platformFolder = CudaResources.getPlatformFolder(tempFolder, RELEASE, platform); + var writes = new AtomicInteger(); + var executor = Executors.newFixedThreadPool(4); + var futures = new ArrayList>(); + + try { + for (int i = 0; i < 4; i++) { + futures.add(executor.submit(() -> install(archives, platformFolder, writes))); + } + + for (var future : futures) { + assertEquals(CudaResources.getInstallationFolder(platformFolder).getAbsoluteFile(), + future.get(TEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS).getAbsoluteFile()); + } + } finally { + executor.shutdownNow(); + executor.awaitTermination(TEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } + + assertTrue(CudaResources.isCudaInstallation(CudaResources.getInstallationFolder(platformFolder), PLATFORM)); + try (var children = Files.list(platformFolder.toPath())) { + assertTrue(children.noneMatch(path -> path.getFileName().toString().startsWith(".cudalib.tmp-"))); + } + try (var children = Files.list(platformFolder.toPath().resolve("archives/cuda_cudart"))) { + assertTrue(children.noneMatch(path -> path.getFileName().toString().startsWith(".cuda_cudart"))); + } + assertTrue(writes.get() >= 4); + } + + @Test + public void staleCudaReleasesAreRemovedAfterSixtyDays() throws IOException { + var platform = new CudaResources.CudaPlatform(hostPlatform()); + var currentFolder = CudaResources.getPlatformFolder(tempFolder, RELEASE, platform); + var currentInstallation = CudaResources.getInstallationFolder(currentFolder); + writeValidInstallation(currentInstallation.toPath(), PLATFORM); + + var staleFolder = CudaResources.getPlatformFolder(tempFolder, "11.8.0", platform); + Files.createDirectories(staleFolder.toPath()); + Files.setLastModifiedTime(staleFolder.toPath().getParent(), + FileTime.from(Instant.now().minus(Duration.ofDays(61)))); + + var parser = CodeParser.newInstance(); + parser.set(CodeParser.DUMPER_FOLDER, tempFolder.toFile()); + new ClangResources(parser).getBuiltinCudaLib(); + + assertTrue(currentInstallation.isDirectory()); + assertFalse(staleFolder.getParentFile().exists()); + } + + private File install(CudaResources.NvidiaCudaManifest manifest, File platformFolder, Path source, + AtomicInteger writes) { + return CudaResources.install(tempFolder, platformFolder, RELEASE, + new CudaResources.CudaPlatform(PLATFORM), manifest, + cudaPackage -> copyingResource(source, + source.getFileName().toString(), writes)); + } + + private File install(ArchiveSet archives, File platformFolder, AtomicInteger writes) { + return CudaResources.install(tempFolder, platformFolder, RELEASE, + new CudaResources.CudaPlatform(PLATFORM), archives.manifest(), + cudaPackage -> { + var source = archives.files().get(cudaPackage.component()); + return copyingResource(source, source.getFileName().toString(), writes); + }); + } + + private ArchiveSet createArchives() throws IOException { + var files = new LinkedHashMap(); + var cudart = tempFolder.resolve("cuda_cudart.tar.xz"); + writeTarXz(cudart, Map.of( + "cuda_cudart/include/cuda.h", bytes("cuda.h"), + "cuda_cudart/include/cuda_runtime.h", bytes("cuda_runtime.h"), + "cuda_cudart/include/texture_fetch_functions.h", bytes("texture"), + "cuda_cudart/bin/discarded", bytes("discarded"))); + files.put("cuda_cudart", cudart); + + var nvcc = tempFolder.resolve("cuda_nvcc.zip"); + writeZip(nvcc, Map.of( + "cuda_nvcc/include/crt/host_config.h", bytes("host_config"), + "cuda_nvcc/nvvm/libdevice/libdevice.10.bc", bytes("libdevice"), + "cuda_nvcc/bin/discarded.exe", bytes("discarded"))); + files.put("cuda_nvcc", nvcc); + + var curand = tempFolder.resolve("libcurand.tar.xz"); + writeTarXz(curand, Map.of( + "libcurand/include/curand_mtgp32_kernel.h", bytes("curand"))); + files.put("libcurand", curand); + + var cccl = tempFolder.resolve("cuda_cccl.zip"); + writeZip(cccl, Map.of("cuda_cccl/include/nv/target", bytes("target"))); + files.put("cuda_cccl", cccl); + + var components = new LinkedHashMap(); + for (var component : CudaResources.REQUIRED_COMPONENTS) { + var archive = files.get(component); + var archivePath = component + "/linux-x86_64/" + archive.getFileName(); + var cudaArchive = new CudaResources.CudaArchive(archivePath, sha256(archive), Files.size(archive)); + components.put(component, new CudaResources.NvidiaCudaComponent(component, RELEASE, + Map.of(PLATFORM, cudaArchive))); + } + + return new ArchiveSet(new CudaResources.NvidiaCudaManifest("2024-01-02", RELEASE, "cuda", components), files); + } + + private List downloadedPackages(ArchiveSet archives) { + return archives.manifest().getRequiredPackages(PLATFORM).stream() + .map(cudaPackage -> new CudaResources.DownloadedPackage(cudaPackage, + archives.files().get(cudaPackage.component()).toFile())) + .toList(); + } + + private CudaResources.NvidiaCudaManifest manifestForSingleArchive(Path source, String sha256, long size) { + var components = new LinkedHashMap(); + for (var component : CudaResources.REQUIRED_COMPONENTS) { + var archiveName = component.equals("cuda_cudart") ? "cuda_cudart.tar.xz" : source.getFileName().toString(); + var relativePath = component + "/linux-x86_64/" + archiveName; + components.put(component, new CudaResources.NvidiaCudaComponent(component, RELEASE, + Map.of(PLATFORM, new CudaResources.CudaArchive(relativePath, sha256, size)))); + } + + return new CudaResources.NvidiaCudaManifest("2024-01-02", RELEASE, "cuda", components); + } + + private CudaResources.NvidiaCudaManifest addUnusedPlatforms(CudaResources.NvidiaCudaManifest manifest) { + var components = new LinkedHashMap(); + for (var entry : manifest.components().entrySet()) { + var selectedArchive = entry.getValue().archives().get(PLATFORM); + var archives = new LinkedHashMap<>(entry.getValue().archives()); + for (var unusedPlatform : List.of(CudaResources.LINUX_PPC64LE_PLATFORM, + CudaResources.LINUX_SBSA_PLATFORM, CudaResources.WINDOWS_X86_64_PLATFORM)) { + archives.put(unusedPlatform, new CudaResources.CudaArchive( + entry.getKey() + "/" + unusedPlatform + "/unused.tar.xz", + selectedArchive.sha256(), selectedArchive.size())); + } + components.put(entry.getKey(), new CudaResources.NvidiaCudaComponent( + entry.getValue().name(), entry.getValue().version(), archives)); + } + + return new CudaResources.NvidiaCudaManifest(manifest.releaseDate(), manifest.releaseLabel(), + manifest.releaseProduct(), components); + } + + private void writeValidInstallation(Path installation, String platform) throws IOException { + Files.createDirectories(installation.resolve("bin")); + Files.writeString(installation.resolve(CudaResources.PLATFORM_FILENAME), platform); + for (var requiredFile : List.of( + "include/cuda.h", + "include/cuda_runtime.h", + "include/texture_fetch_functions.h", + "include/curand_mtgp32_kernel.h", + "include/nv/target", + "include/crt/host_config.h", + "nvvm/libdevice/libdevice.10.bc")) { + var file = installation.resolve(requiredFile); + Files.createDirectories(file.getParent()); + Files.writeString(file, requiredFile); + } + } + + private FileResourceProvider copyingResource(Path source, String filename, AtomicInteger writes) { + return new FileResourceProvider() { + @Override + public File write(java.io.File folder) { + writes.incrementAndGet(); + try { + var destination = folder.toPath().resolve(filename); + Files.copy(source, destination); + return destination.toFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String version() { + return "test"; + } + + @Override + public String getFilename() { + return filename; + } + }; + } + + private static String hostPlatform() { + return CudaResources.getCurrentPlatform().manifestName(); + } + + private static void writeTarXz(Path archive, Map files) throws IOException { + try (OutputStream output = Files.newOutputStream(archive); + var xzOutput = new XZCompressorOutputStream(output); + var tarOutput = new TarArchiveOutputStream(xzOutput)) { + tarOutput.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + for (var file : files.entrySet()) { + var entry = new TarArchiveEntry(file.getKey()); + entry.setSize(file.getValue().length); + tarOutput.putArchiveEntry(entry); + tarOutput.write(file.getValue()); + tarOutput.closeArchiveEntry(); + } + } + } + + private static void writeZip(Path archive, Map files) throws IOException { + try (OutputStream output = Files.newOutputStream(archive); + var zipOutput = new ZipArchiveOutputStream(output)) { + for (var file : files.entrySet()) { + var entry = new ZipArchiveEntry(file.getKey()); + zipOutput.putArchiveEntry(entry); + zipOutput.write(file.getValue()); + zipOutput.closeArchiveEntry(); + } + } + } + + private static String manifestJson() { + return """ + { + "release_date": "2024-01-02", + "release_label": "12.3.2", + "release_product": "cuda", + "cuda_cudart": %s, + "cuda_nvcc": %s, + "libcurand": %s, + "cuda_cccl": %s + } + """.formatted( + component("CUDA Runtime", "cuda_cudart/linux-x86_64/cuda_cudart.tar.xz"), + component("CUDA NVCC", "cuda_nvcc/linux-x86_64/cuda_nvcc.tar.xz"), + component("cuRAND", "libcurand/linux-x86_64/libcurand.tar.xz"), + component("CCCL", "cuda_cccl/linux-x86_64/cuda_cccl.tar.xz")); + } + + private static String component(String name, String relativePath) { + return """ + { + "name": "%s", + "version": "12.3.101", + "linux-x86_64": { + "relative_path": "%s", + "sha256": "%s", + "size": "1" + } + } + """.formatted(name, relativePath, SHA256); + } + + private static byte[] bytes(String value) { + return value.getBytes(StandardCharsets.UTF_8); + } + + private static String sha256(Path file) throws IOException { + try { + return java.util.HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256") + .digest(Files.readAllBytes(file))); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private static final String SHA256 = "0".repeat(64); + + private record ArchiveSet(CudaResources.NvidiaCudaManifest manifest, Map files) { + } +} diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/CxxCudaTester.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/CxxCudaTester.java index 45fdfffce8..fde8a56bcc 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/CxxCudaTester.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/CxxCudaTester.java @@ -16,7 +16,7 @@ import java.util.Arrays; import java.util.List; -import pt.up.fe.specs.lang.SpecsPlatforms; +import pt.up.fe.specs.clang.ClangResources; public class CxxCudaTester extends AClangAstTester { @@ -28,8 +28,7 @@ public CxxCudaTester(List files) { // super("cxx/cuda", files, Arrays.asList("-std=cuda")); super("cxx/cuda", files); - // Windows currently not supported - if (SpecsPlatforms.isWindows()) { + if (!ClangResources.isBuiltinCudaSupported()) { doNotRun(); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java index 2a857db62a..a0e13c0289 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java @@ -19,7 +19,7 @@ import pt.up.fe.specs.clang.LibcMode; import pt.up.fe.specs.clang.parser.CxxCudaTester; -/** Verifies built-in CUDA parsing through Clava's historical cudalib archive. */ +/** Verifies built-in CUDA parsing through the pinned NVIDIA redistribution packages. */ public class CxxCudaTest { @Test public void testAtomicAdd() { diff --git a/Clava-JS/code/sideEffects.ts b/Clava-JS/code/sideEffects.ts index 2c62d3bf47..090da8743b 100644 --- a/Clava-JS/code/sideEffects.ts +++ b/Clava-JS/code/sideEffects.ts @@ -18,14 +18,14 @@ const datastore = Weaver.getWeaverEngine().getData().get(); datastore.set(CxxWeaverOptions.DISABLE_CLAVA_INFO, true); datastore.set( CodeParser.DUMPER_FOLDER, - new JavaTypes.File(getClangDumperCacheDir()) + new JavaTypes.File(getClavaCacheDir()) ); /** Code to obtain temporary folder **/ -function getClangDumperCacheDir(): string { - // The version will be added by the installer to isolate different installed versions - return path.join(getCacheBaseDir(), pkg.name, "clang-dumper"); +function getClavaCacheDir(): string { + // Java adds separate namespaces for the dumper and CUDA resources. + return path.join(getCacheBaseDir(), pkg.name); } function getCacheBaseDir(): string { From c202bfcf5f260c01a2e5e3772aa72398bb32338b Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 6 Aug 2026 02:27:35 +0100 Subject: [PATCH 76/99] fix(clang): protect initializing CUDA releases --- .../pt/up/fe/specs/clang/CudaResources.java | 21 +++++++ .../up/fe/specs/clang/CudaResourcesTest.java | 55 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java index b1a27491ef..1e06725525 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java @@ -99,10 +99,31 @@ static File getBuiltinCudaLib(Path cacheRoot) { return useExistingInstallation(cacheRoot, platformFolder, platform, installationFolder); } + claimInUse(cacheRoot, platformFolder); var manifest = getManifest(cacheRoot, platformFolder); return install(cacheRoot, platformFolder, releaseTag, platform, manifest, CudaResources::getArchiveResource); } + static void claimInUse(Path cacheRoot, File platformFolder) { + CacheFiles.withMaintenanceLock(cacheRoot, () -> { + var platformPath = platformFolder.toPath(); + var releasePath = platformPath.getParent(); + if (releasePath == null) { + throw new RuntimeException("CUDA platform folder is not below a release folder: '" + + platformFolder + "'"); + } + + try { + Files.createDirectories(platformPath); + } catch (IOException e) { + throw new UncheckedIOException("Could not create CUDA platform folder '" + platformPath + "'", e); + } + + CacheFiles.touch(releasePath); + CacheFiles.touch(platformPath); + }); + } + static CudaPlatform requireSupportedPlatform() { return getCurrentPlatform(); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java index f3169882a7..c51f893c9a 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java @@ -38,6 +38,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -265,6 +266,49 @@ public void invalidPublishedInstallationFailsWithoutRepair() throws IOException assertEquals("do not repair", Files.readString(installation.toPath().resolve("sentinel"))); } + @Test + public void oldPartialReleaseIsProtectedWhileInitializationContinues() throws Exception { + var platformFolder = CudaResources.getPlatformFolder(tempFolder, RELEASE, + new CudaResources.CudaPlatform(PLATFORM)); + var releaseFolder = platformFolder.toPath().getParent(); + Files.createDirectories(platformFolder.toPath().resolve("partial")); + Files.writeString(platformFolder.toPath().resolve("partial/manifest-download"), "in progress"); + var old = FileTime.from(Instant.now().minus(Duration.ofDays(61))); + Files.setLastModifiedTime(releaseFolder, old); + Files.setLastModifiedTime(platformFolder.toPath(), old); + + var claimed = new CountDownLatch(1); + var allowInitializationToFinish = new CountDownLatch(1); + var executor = Executors.newFixedThreadPool(2); + + try { + var initialization = executor.submit(() -> { + CudaResources.claimInUse(tempFolder, platformFolder); + claimed.countDown(); + awaitLatch(allowInitializationToFinish); + }); + assertTrue(claimed.await(TEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)); + + var cleanup = executor.submit(() -> CacheFiles.deleteStaleDirectories(tempFolder, + tempFolder.resolve("cuda"), Instant.now().minus(Duration.ofDays(60)), null)); + cleanup.get(TEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + + assertTrue(Files.isDirectory(releaseFolder)); + assertTrue(Files.isDirectory(platformFolder.toPath())); + assertTrue(Files.getLastModifiedTime(releaseFolder).toInstant() + .isAfter(Instant.now().minus(Duration.ofDays(1)))); + assertTrue(Files.getLastModifiedTime(platformFolder.toPath()).toInstant() + .isAfter(Instant.now().minus(Duration.ofDays(1)))); + + allowInitializationToFinish.countDown(); + initialization.get(TEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } finally { + allowInitializationToFinish.countDown(); + executor.shutdownNow(); + executor.awaitTermination(TEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + } + } + @Test public void concurrentPublicationLeavesOneValidInstallation() throws Exception { var archives = createArchives(); @@ -532,6 +576,17 @@ private static String sha256(Path file) throws IOException { private static final String SHA256 = "0".repeat(64); + private static void awaitLatch(CountDownLatch latch) { + try { + if (!latch.await(TEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)) { + throw new AssertionError("Timed out waiting for CUDA initialization test coordination"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + private record ArchiveSet(CudaResources.NvidiaCudaManifest manifest, Map files) { } } From 5b32ebe6e5dab58ad5de4be96d34f7cb4e61c2a4 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 6 Aug 2026 16:44:10 +0100 Subject: [PATCH 77/99] fix(clang): select CUDA platform from NVIDIA manifest Keep the clang-dumper includes archive limited to built-in libc/libc++ and configure the system Clang resource directory for SYSTEM-mode CUDA parsing. Built-in CUDA supplies only the NVIDIA cudalib. Select a host-compatible CUDA platform from the intersection of all required NVIDIA manifest components, then reuse that parsed manifest for installation. Tests: CudaResourcesTest, ClangResourcesTest, and CxxCudaTest --- ClangAstParser/clang-dumper-release.tag | 2 +- .../src/pt/up/fe/specs/clang/ClangFiles.java | 2 +- .../pt/up/fe/specs/clang/ClangResources.java | 73 ++++++++- .../pt/up/fe/specs/clang/CudaResources.java | 145 ++++++++++++++---- .../clang/codeparser/ParallelCodeParser.java | 9 +- .../fe/specs/clang/dumper/ClangAstDumper.java | 10 +- .../up/fe/specs/clang/ClangResourcesTest.java | 29 ++-- .../up/fe/specs/clang/CudaResourcesTest.java | 70 ++++++--- .../specs/clang/parser/tests/CxxCudaTest.java | 6 +- 9 files changed, 269 insertions(+), 77 deletions(-) diff --git a/ClangAstParser/clang-dumper-release.tag b/ClangAstParser/clang-dumper-release.tag index 9acbd12ca8..73e74e98ff 100644 --- a/ClangAstParser/clang-dumper-release.tag +++ b/ClangAstParser/clang-dumper-release.tag @@ -1 +1 @@ -v18.1.8_1 +v18.1.8_2 diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java index 731cac065f..1fcbf321bc 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java @@ -16,6 +16,6 @@ import java.io.File; import java.util.List; -public record ClangFiles(File clangExecutable, List builtinIncludes) { +public record ClangFiles(File clangExecutable, List builtinIncludes, File systemResourceDir) { } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 4432867aa1..c7fcffe77e 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -67,12 +67,16 @@ public File getBuiltinCudaLib() { public ClangFiles getClangFiles(LibcMode libcMode) { var source = ClangAstWebResource.getDumperSource(); + var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); if (source instanceof LocalBuild localBuild) { - return new ClangFiles(getLocalExecutable(localBuild.folder()), List.of()); + var clangExecutable = getLocalExecutable(localBuild.folder()); + var systemResourceDir = libcMode == LibcMode.SYSTEM && useBuiltinCuda + ? findSystemClangResourceDir(null) + : null; + return new ClangFiles(clangExecutable, List.of(), systemResourceDir); } - var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); var resourceFolder = getClangResourceFolder(); var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" + resourceFolder.getAbsolutePath(); @@ -89,6 +93,9 @@ public ClangFiles getClangFiles(LibcMode libcMode) { var manifest = ClangAstWebResource.getManifest(resourceFolder); File clangExecutable = prepareResources(manifest, resourceFolder); var includes = prepareIncludes(manifest, clangExecutable, libcMode); + var systemResourceDir = libcMode == LibcMode.SYSTEM && useBuiltinCuda + ? prepareSystemClangResourceDir(manifest) + : null; if (useBuiltinCuda) { getBuiltinCudaLib(); @@ -97,7 +104,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { touchUse(resourceFolder, includes.extractedFolder()); updateLastUsedAndCleanupStaleVersions(resourceFolder, includes.extractedFolder()); - var newFiles = new CachedClangFiles(new ClangFiles(clangExecutable, includes.folders()), + var newFiles = new CachedClangFiles(new ClangFiles(clangExecutable, includes.folders(), systemResourceDir), includes.extractedFolder()); var existingFiles = CLANG_FILES_CACHE.putIfAbsent(key, newFiles); var selectedFiles = existingFiles == null ? newFiles : existingFiles; @@ -116,6 +123,11 @@ private boolean isUsable(CachedClangFiles cached) { return false; } + if (cached.files().systemResourceDir() != null + && !cached.files().systemResourceDir().isDirectory()) { + return false; + } + var includesFolder = cached.includesFolder(); if (includesFolder == null) { return true; @@ -292,9 +304,8 @@ private static ProcessOutputAsString runClangAstDumper(File clangExecutable, Fil private PreparedIncludes prepareIncludes(ClangDumperManifest manifest, File clangExecutable, LibcMode libcMode) { var useBuiltinLibc = useBuiltinLibc(clangExecutable, libcMode); - var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); - if (!useBuiltinLibc && !useBuiltinCuda) { + if (!useBuiltinLibc) { return new PreparedIncludes(List.of(), null); } @@ -305,6 +316,58 @@ private PreparedIncludes prepareIncludes(ClangDumperManifest manifest, File clan return new PreparedIncludes(includeFolders.stream().map(File::getAbsolutePath).toList(), extractedFolder); } + private File prepareSystemClangResourceDir(ClangDumperManifest manifest) { + var executableKind = ClangAstDumper.usePlugin() ? "plugin" : "tool"; + var llvmMajor = getCurrentAsset(manifest, executableKind).llvm_major(); + return findSystemClangResourceDir(llvmMajor); + } + + private File findSystemClangResourceDir(Integer llvmMajor) { + var commandNames = getSystemClangCommandNames(llvmMajor); + for (var commandName : commandNames) { + final ProcessOutputAsString output; + try { + output = SpecsSystem.runProcess(List.of(commandName, "-print-resource-dir"), true, false); + } catch (RuntimeException e) { + continue; + } + + if (output.getReturnValue() != 0 || output.getStdOut() == null) { + continue; + } + + var resourceDir = new File(output.getStdOut().trim()); + if (isSystemClangResourceDir(resourceDir, llvmMajor)) { + SpecsLogs.debug(() -> "Using system Clang resource directory '" + + resourceDir.getAbsolutePath() + "'"); + return resourceDir; + } + } + + var expectedVersion = llvmMajor == null ? "the local clang-dumper build's version" + : "LLVM " + llvmMajor; + throw new RuntimeException("Could not find a system Clang resource directory for SYSTEM mode with built-in CUDA" + + " on host '" + SupportedPlatform.getCurrentPlatform() + "' (expected " + expectedVersion + + "). Tried: " + commandNames); + } + + private static List getSystemClangCommandNames(Integer llvmMajor) { + var suffix = SupportedPlatform.getCurrentPlatform().isWindows() ? ".exe" : ""; + if (llvmMajor == null) { + return List.of("clang++" + suffix); + } + + return List.of("clang++-" + llvmMajor + suffix, "clang++" + suffix); + } + + private static boolean isSystemClangResourceDir(File resourceDir, Integer llvmMajor) { + if (!resourceDir.isDirectory() || !new File(resourceDir, "include").isDirectory()) { + return false; + } + + return llvmMajor == null || resourceDir.getName().equals(Integer.toString(llvmMajor)); + } + private File prepareIncludesFolder(ClangDumperManifest manifest) { var includesAsset = getCurrentAsset(manifest, "includes"); return resolveIncludes(getClangCacheRoot(), includesAsset, diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java index 1e06725525..4cf8718d98 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java @@ -42,6 +42,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -60,10 +61,6 @@ final class CudaResources { static final String NVIDIA_REDIST_ROOT = "https://developer.download.nvidia.com/compute/cuda/redist/"; - static final String LINUX_X86_64_PLATFORM = "linux-x86_64"; - static final String LINUX_PPC64LE_PLATFORM = "linux-ppc64le"; - static final String LINUX_SBSA_PLATFORM = "linux-sbsa"; - static final String WINDOWS_X86_64_PLATFORM = "windows-x86_64"; static final List REQUIRED_COMPONENTS = List.of("cuda_cudart", "cuda_nvcc", "libcurand", "cuda_cccl"); static final String PLATFORM_FILENAME = ".platform"; @@ -89,7 +86,10 @@ private CudaResources() { static File getBuiltinCudaLib(Path cacheRoot) { var releaseTag = ClangAstWebResource.getCudaReleaseTag(); - var platform = requireSupportedPlatform(); + var releaseFolder = getReleaseFolder(cacheRoot, releaseTag); + claimReleaseInUse(cacheRoot, releaseFolder); + var manifest = getManifest(cacheRoot, releaseFolder); + var platform = requireSupportedPlatform(manifest); var platformFolder = getPlatformFolder(cacheRoot, releaseTag, platform); var installationFolder = getInstallationFolder(platformFolder); @@ -100,10 +100,21 @@ static File getBuiltinCudaLib(Path cacheRoot) { } claimInUse(cacheRoot, platformFolder); - var manifest = getManifest(cacheRoot, platformFolder); return install(cacheRoot, platformFolder, releaseTag, platform, manifest, CudaResources::getArchiveResource); } + private static void claimReleaseInUse(Path cacheRoot, File releaseFolder) { + CacheFiles.withMaintenanceLock(cacheRoot, () -> { + try { + Files.createDirectories(releaseFolder.toPath()); + } catch (IOException e) { + throw new UncheckedIOException("Could not create CUDA release folder '" + releaseFolder + "'", e); + } + + CacheFiles.touch(releaseFolder.toPath()); + }); + } + static void claimInUse(Path cacheRoot, File platformFolder) { CacheFiles.withMaintenanceLock(cacheRoot, () -> { var platformPath = platformFolder.toPath(); @@ -128,9 +139,17 @@ static CudaPlatform requireSupportedPlatform() { return getCurrentPlatform(); } + static CudaPlatform requireSupportedPlatform(NvidiaCudaManifest manifest) { + return getCurrentPlatform(manifest); + } + static boolean isSupportedPlatform() { + return isSupportedPlatform(ClangResources.getDefaultTempFolder().toPath()); + } + + static boolean isSupportedPlatform(Path cacheRoot) { try { - getCurrentPlatform(); + getCurrentPlatform(cacheRoot); return true; } catch (RuntimeException e) { return false; @@ -138,42 +157,114 @@ static boolean isSupportedPlatform() { } static CudaPlatform getCurrentPlatform() { - return new CudaPlatform(getManifestPlatform(SupportedPlatform.getCurrentPlatform(), System.getProperty("os.arch"))); + return getCurrentPlatform(ClangResources.getDefaultTempFolder().toPath()); + } + + static CudaPlatform getCurrentPlatform(Path cacheRoot) { + var releaseTag = ClangAstWebResource.getCudaReleaseTag(); + var releaseFolder = getReleaseFolder(cacheRoot, releaseTag); + claimReleaseInUse(cacheRoot, releaseFolder); + return getCurrentPlatform(getManifest(cacheRoot, releaseFolder)); } - static String getManifestPlatform(SupportedPlatform platform, String architecture) { + static CudaPlatform getCurrentPlatform(NvidiaCudaManifest manifest) { + return new CudaPlatform(getManifestPlatform(manifest, SupportedPlatform.getCurrentPlatform(), + System.getProperty("os.arch"))); + } + + static String getManifestPlatform(NvidiaCudaManifest manifest, SupportedPlatform platform, String architecture) { + Objects.requireNonNull(manifest, "manifest"); Objects.requireNonNull(platform, "platform"); Objects.requireNonNull(architecture, "architecture"); - var normalizedArchitecture = architecture.toLowerCase(Locale.ROOT); - - if (platform.isWindows() && isX86_64(normalizedArchitecture)) { - return WINDOWS_X86_64_PLATFORM; - } - if (platform.isLinux()) { - if (isX86_64(normalizedArchitecture)) { - return LINUX_X86_64_PLATFORM; + var commonPlatforms = new LinkedHashSet(); + var missingComponents = new ArrayList(); + var firstComponent = true; + for (var componentName : REQUIRED_COMPONENTS) { + var component = manifest.components().get(componentName); + if (component == null) { + missingComponents.add(componentName); + continue; } - if (normalizedArchitecture.equals("ppc64le")) { - return LINUX_PPC64LE_PLATFORM; + if (firstComponent) { + commonPlatforms.addAll(component.archives().keySet()); + firstComponent = false; + } else { + commonPlatforms.retainAll(component.archives().keySet()); } + } - if (normalizedArchitecture.equals("aarch64") || normalizedArchitecture.equals("arm64")) { - return LINUX_SBSA_PLATFORM; - } + var selectedPlatform = commonPlatforms.stream() + .filter(candidate -> isCompatiblePlatform(candidate, platform, architecture)) + .findFirst(); + if (missingComponents.isEmpty() && selectedPlatform.isPresent()) { + return selectedPlatform.get(); + } + + var reason = missingComponents.isEmpty() + ? "no platform key is present in all required components and is compatible with this host" + : "the manifest is missing required components " + missingComponents; + throw new RuntimeException("Built-in CUDA is unsupported for host '" + platform + " (" + architecture + + ")': " + reason + ". Available manifest platform keys: " + + getAvailablePlatformKeys(manifest)); + } + + private static boolean isCompatiblePlatform(String manifestPlatform, SupportedPlatform hostPlatform, + String hostArchitecture) { + var separator = manifestPlatform.indexOf('-'); + if (separator <= 0 || separator == manifestPlatform.length() - 1) { + return false; + } + + var manifestOs = normalizeOs(manifestPlatform.substring(0, separator)); + var manifestArchitecture = normalizeArchitecture(manifestPlatform.substring(separator + 1)); + return manifestOs.equals(normalizeOs(hostPlatform)) + && manifestArchitecture.equals(normalizeArchitecture(hostArchitecture)); + } + + private static String normalizeOs(SupportedPlatform platform) { + return switch (platform) { + case WINDOWS -> "windows"; + case LINUX -> "linux"; + case MAC_OS -> "macos"; + }; + } + + private static String normalizeOs(String os) { + var normalized = os.toLowerCase(Locale.ROOT).replace("_", "").replace("-", ""); + return switch (normalized) { + case "darwin", "mac" -> "macos"; + default -> normalized; + }; + } + + private static String normalizeArchitecture(String architecture) { + var normalized = architecture.toLowerCase(Locale.ROOT).replace("_", "").replace("-", ""); + return switch (normalized) { + case "amd64", "x8664", "x64" -> "x8664"; + case "aarch64", "arm64", "armv8", "armv8l", "sbsa" -> "arm64"; + default -> normalized; + }; + } + + private static Map> getAvailablePlatformKeys(NvidiaCudaManifest manifest) { + var available = new LinkedHashMap>(); + for (var componentName : REQUIRED_COMPONENTS) { + var component = manifest.components().get(componentName); + var platforms = component == null ? List.of() : component.archives().keySet().stream().sorted().toList(); + available.put(componentName, platforms); } - throw new RuntimeException("The CUDA manifest does not provide a supported package for platform '" - + platform + "' and architecture '" + architecture + "'"); + return available; } - private static boolean isX86_64(String architecture) { - return architecture.equals("amd64") || architecture.equals("x86_64"); + private static File getReleaseFolder(Path cacheRoot, String releaseTag) { + return cacheRoot.resolve(CUDA_FOLDERNAME).resolve(releaseTag).toFile(); } static File getPlatformFolder(Path cacheRoot, String releaseTag, CudaPlatform platform) { - return cacheRoot.resolve(CUDA_FOLDERNAME).resolve(releaseTag).resolve(platform.manifestName()).toFile(); + return getReleaseFolder(cacheRoot, releaseTag).toPath().resolve(platform.manifestName()).toFile(); } static File getInstallationFolder(File platformFolder) { diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java index d16c2ce827..9313859bda 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java @@ -89,6 +89,7 @@ public App parse(List inputSources, List compilerOptions, ClavaCon ConcurrentLinkedQueue clangDump = new ConcurrentLinkedQueue<>(); DataStore options = ClangAstKeys.toDataStore(compilerOptions); + options.set(ClangAstKeys.LIBC_CXX_MODE, get(ClangAstKeys.LIBC_CXX_MODE)); // Add context to config // ClavaContext context = new ClavaContext(); @@ -145,7 +146,8 @@ public App parse(List inputSources, List compilerOptions, ClavaCon Future tUnit = executor .submit(() -> parseSource(source, id, standard, options, clangDump, - counter, parsingFolder, clangFiles.clangExecutable(), clangFiles.builtinIncludes())); + counter, parsingFolder, clangFiles.clangExecutable(), clangFiles.builtinIncludes(), + clangFiles.systemResourceDir())); futureTUnits.add(tUnit); @@ -362,7 +364,7 @@ private Standard getStandard(Collection sources, DataStore options) { private ClangAstData parseSource(File sourceFile, String id, Standard standard, DataStore options, ConcurrentLinkedQueue clangDump, ParallelProgressCounter counter, File parsingFolder, - File clangExecutable, List builtinIncludes) { + File clangExecutable, List builtinIncludes, File systemResourceDir) { // ConcurrentLinkedQueue clangDump, ConcurrentLinkedQueue workingFolders) { @@ -373,7 +375,8 @@ private ClangAstData parseSource(File sourceFile, String id, Standard standard, // Only show output of console after parsing is done, when using parallel parsing boolean streamConsoleOutput = !get(PARALLEL_PARSING); - ClangAstDumper clangParser = new ClangAstDumper(streamConsoleOutput, clangExecutable, builtinIncludes, this) + ClangAstDumper clangParser = new ClangAstDumper(streamConsoleOutput, clangExecutable, builtinIncludes, + systemResourceDir, this) .setBaseFolder(parsingFolder) .setSystemIncludesThreshold(get(SYSTEM_INCLUDES_THRESHOLD)); diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index ba7f29de31..8b56c1c291 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -86,6 +86,7 @@ public static List getTempFiles() { private File baseFolder; private File clangExecutable; private List builtinIncludes; + private File systemResourceDir; private int systemIncludesThreshold; private final ClangResources clangResources; @@ -99,15 +100,18 @@ public static List getTempFiles() { * @param streamConsoleOutput * @param clangExecutable * @param builtinIncludes + * @param systemResourceDir * @param parserConfig */ public ClangAstDumper(boolean streamConsoleOutput, - File clangExecutable, List builtinIncludes, CodeParser parserConfig) { + File clangExecutable, List builtinIncludes, File systemResourceDir, + CodeParser parserConfig) { this.streamConsoleOutput = streamConsoleOutput; this.clangExecutable = clangExecutable; this.builtinIncludes = builtinIncludes; + this.systemResourceDir = systemResourceDir; this.workingFolders = new ArrayList<>(); this.lastWorkingFolder = null; @@ -252,6 +256,10 @@ else if (SourceType.isHeader(sourceFile)) { arguments.add(standard.isCxx() ? "c++" : "c"); } + if (systemResourceDir != null) { + arguments.add("-resource-dir=" + systemResourceDir.getAbsolutePath()); + } + // If it was determined that built-in includes will be used, disable system includes if (ClangResources.useBuiltinLibc(clangExecutable, config.get(ClangAstKeys.LIBC_CXX_MODE))) { arguments.add("-nostdinc"); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index 4b30a90673..bdd8f23799 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -55,6 +55,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -486,10 +487,10 @@ public void maintenanceLockIsSharedAcrossJvmProcesses() throws Exception { } @Test - public void sameJvmInstancesReuseReleaseFilesAndRefreshSharedIncludes() throws Exception { - var firstParser = newParser(CodeParser.getBuiltinOption()); - var secondParser = newParser(CodeParser.getBuiltinOption()); - var thirdParser = newParser(CodeParser.getBuiltinOption()); + public void sameJvmInstancesReuseReleaseFilesAndPrepareIncludesOnlyForBuiltinLibc() throws Exception { + var firstParser = newParser(""); + var secondParser = newParser(""); + var thirdParser = newParser(""); var firstResources = new ClangResources(firstParser); var secondResources = new ClangResources(secondParser); var thirdResources = new ClangResources(thirdParser); @@ -507,14 +508,16 @@ public void sameJvmInstancesReuseReleaseFilesAndRefreshSharedIncludes() throws E assertEquals(firstFiles, secondFiles); assertEquals(firstFiles.clangExecutable().getAbsoluteFile(), thirdFiles.clangExecutable().getAbsoluteFile()); assertTrue(firstFiles.clangExecutable().isFile()); + assertTrue(firstFiles.builtinIncludes().isEmpty()); + assertTrue(secondFiles.builtinIncludes().isEmpty()); + assertFalse(thirdFiles.builtinIncludes().isEmpty()); - assumeTrue(!firstFiles.builtinIncludes().isEmpty()); - var shared = new File(firstFiles.builtinIncludes().get(0)).toPath(); + var shared = new File(thirdFiles.builtinIncludes().get(0)).toPath(); while (!Files.isRegularFile(shared.resolve("entrypoints.txt"))) { shared = shared.getParent(); } Files.setLastModifiedTime(shared, FileTime.from(Instant.now().minus(Duration.ofDays(61)))); - firstResources.getClangFiles(LibcMode.SYSTEM); + thirdResources.getClangFiles(LibcMode.BUILTIN_AND_LIBC); assertTrue(Files.getLastModifiedTime(shared).toInstant().isAfter(Instant.now().minus(Duration.ofDays(1)))); } finally { executor.shutdownNow(); @@ -523,14 +526,14 @@ public void sameJvmInstancesReuseReleaseFilesAndRefreshSharedIncludes() throws E } @Test - public void builtinCudaIncludesAreAvailableWithSystemLibc() { + public void builtinCudaUsesSystemClangResourceWithSystemLibc() { var parser = newParser(CodeParser.getBuiltinOption()); var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); - var hasCudaWrapper = clangFiles.builtinIncludes().stream() - .map(folder -> new File(folder, "__clang_cuda_runtime_wrapper.h")) - .anyMatch(File::isFile); - assertTrue(hasCudaWrapper, "Built-in CUDA must provide Clang's CUDA runtime wrapper independently of libc"); + assertTrue(clangFiles.builtinIncludes().isEmpty()); + assertNotNull(clangFiles.systemResourceDir()); + assertTrue(clangFiles.systemResourceDir().isDirectory()); + assertFalse(Files.exists(clangCacheRoot().resolve("includes"))); } @Test @@ -538,7 +541,7 @@ public void builtinCudaArchiveHasCanonicalInstallationLayout() { var parser = newParser(CodeParser.getBuiltinOption()); var cudaFolder = new ClangResources(parser).getBuiltinCudaLib(); - var cudaPlatform = CudaResources.getCurrentPlatform().manifestName(); + var cudaPlatform = cudaFolder.getParentFile().getName(); assertEquals(tempFolder.resolve("cuda").resolve(ClangAstWebResource.getCudaReleaseTag()) .resolve(cudaPlatform).resolve("cudalib").toFile().getAbsolutePath(), cudaFolder.getAbsolutePath()); assertTrue(new File(cudaFolder, "include/cuda.h").isFile()); diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java index c51f893c9a..3a2576bbee 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java @@ -52,7 +52,7 @@ public class CudaResourcesTest { - private static final String PLATFORM = CudaResources.LINUX_X86_64_PLATFORM; + private static final String PLATFORM = "linux-x86_64"; private static final String RELEASE = "12.3.2"; private static final Duration TEST_TIMEOUT = Duration.ofSeconds(10); @@ -109,20 +109,39 @@ public void manifestSelectsRequiredComponentsAndValidatesMetadata() { } @Test - public void manifestPlatformMatchesNvidiaKeysAndRejectsUnavailablePlatforms() { - assertEquals(CudaResources.LINUX_X86_64_PLATFORM, - CudaResources.getManifestPlatform(SupportedPlatform.LINUX, "amd64")); - assertEquals(CudaResources.LINUX_PPC64LE_PLATFORM, - CudaResources.getManifestPlatform(SupportedPlatform.LINUX, "ppc64le")); - assertEquals(CudaResources.LINUX_SBSA_PLATFORM, - CudaResources.getManifestPlatform(SupportedPlatform.LINUX, "aarch64")); - assertEquals(CudaResources.WINDOWS_X86_64_PLATFORM, - CudaResources.getManifestPlatform(SupportedPlatform.WINDOWS, "x86_64")); + public void manifestAcceptsHostWhenAllRequiredComponentsExposeACompatiblePlatform() { + var manifest = CudaResources.parseManifest(manifestJson()); - assertThrows(RuntimeException.class, - () -> CudaResources.getManifestPlatform(SupportedPlatform.MAC_OS, "aarch64")); - assertThrows(RuntimeException.class, - () -> CudaResources.getManifestPlatform(SupportedPlatform.WINDOWS, "aarch64")); + assertEquals(PLATFORM, + CudaResources.getManifestPlatform(manifest, SupportedPlatform.LINUX, "amd64")); + } + + @Test + public void manifestRejectsHostWhenARequiredComponentLacksThePlatform() { + var manifest = CudaResources.parseManifest(manifestJson()); + var missingPlatformComponent = manifest.components().get("cuda_cccl"); + var archives = new LinkedHashMap<>(missingPlatformComponent.archives()); + archives.remove(PLATFORM); + var components = new LinkedHashMap<>(manifest.components()); + components.put("cuda_cccl", new CudaResources.NvidiaCudaComponent( + missingPlatformComponent.name(), missingPlatformComponent.version(), archives)); + var incompleteManifest = new CudaResources.NvidiaCudaManifest(manifest.releaseDate(), manifest.releaseLabel(), + manifest.releaseProduct(), components); + + var error = assertThrows(RuntimeException.class, + () -> CudaResources.getManifestPlatform(incompleteManifest, SupportedPlatform.LINUX, "amd64")); + assertTrue(error.getMessage().contains("linux (amd64)")); + assertTrue(error.getMessage().contains(PLATFORM)); + assertTrue(error.getMessage().contains("cuda_cccl")); + } + + @Test + public void additionalCompatibleManifestPlatformNeedsNoJavaSupportWhitelist() { + var additionalPlatform = "linux-riscv64"; + var manifest = CudaResources.parseManifest(manifestJson(additionalPlatform)); + + assertEquals(additionalPlatform, + CudaResources.getManifestPlatform(manifest, SupportedPlatform.LINUX, "riscv64")); } @Test @@ -441,8 +460,7 @@ private CudaResources.NvidiaCudaManifest addUnusedPlatforms(CudaResources.Nvidia for (var entry : manifest.components().entrySet()) { var selectedArchive = entry.getValue().archives().get(PLATFORM); var archives = new LinkedHashMap<>(entry.getValue().archives()); - for (var unusedPlatform : List.of(CudaResources.LINUX_PPC64LE_PLATFORM, - CudaResources.LINUX_SBSA_PLATFORM, CudaResources.WINDOWS_X86_64_PLATFORM)) { + for (var unusedPlatform : List.of("linux-riscv64", "windows-x86_64")) { archives.put(unusedPlatform, new CudaResources.CudaArchive( entry.getKey() + "/" + unusedPlatform + "/unused.tar.xz", selectedArchive.sha256(), selectedArchive.size())); @@ -499,7 +517,7 @@ public String getFilename() { } private static String hostPlatform() { - return CudaResources.getCurrentPlatform().manifestName(); + return PLATFORM; } private static void writeTarXz(Path archive, Map files) throws IOException { @@ -530,6 +548,10 @@ private static void writeZip(Path archive, Map files) throws IOE } private static String manifestJson() { + return manifestJson(PLATFORM); + } + + private static String manifestJson(String platform) { return """ { "release_date": "2024-01-02", @@ -541,24 +563,24 @@ private static String manifestJson() { "cuda_cccl": %s } """.formatted( - component("CUDA Runtime", "cuda_cudart/linux-x86_64/cuda_cudart.tar.xz"), - component("CUDA NVCC", "cuda_nvcc/linux-x86_64/cuda_nvcc.tar.xz"), - component("cuRAND", "libcurand/linux-x86_64/libcurand.tar.xz"), - component("CCCL", "cuda_cccl/linux-x86_64/cuda_cccl.tar.xz")); + component("CUDA Runtime", platform, "cuda_cudart/" + platform + "/cuda_cudart.tar.xz"), + component("CUDA NVCC", platform, "cuda_nvcc/" + platform + "/cuda_nvcc.tar.xz"), + component("cuRAND", platform, "libcurand/" + platform + "/libcurand.tar.xz"), + component("CCCL", platform, "cuda_cccl/" + platform + "/cuda_cccl.tar.xz")); } - private static String component(String name, String relativePath) { + private static String component(String name, String platform, String relativePath) { return """ { "name": "%s", "version": "12.3.101", - "linux-x86_64": { + "%s": { "relative_path": "%s", "sha256": "%s", "size": "1" } } - """.formatted(name, relativePath, SHA256); + """.formatted(name, platform, relativePath, SHA256); } private static byte[] bytes(String value) { diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java index a0e13c0289..7d45f6770f 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/parser/tests/CxxCudaTest.java @@ -22,8 +22,10 @@ /** Verifies built-in CUDA parsing through the pinned NVIDIA redistribution packages. */ public class CxxCudaTest { @Test - public void testAtomicAdd() { - new CxxCudaTester("atomicAdd.cu").test(); + public void testAtomicAddWithBuiltinLibc() { + new CxxCudaTester("atomicAdd.cu") + .set(ClangAstKeys.LIBC_CXX_MODE, LibcMode.BUILTIN_AND_LIBC) + .test(); } @Test From 60462ff006aa09df8ad26c7b51f57accca169148 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 7 Aug 2026 21:02:14 +0100 Subject: [PATCH 78/99] fix(clang): resolve libc mode and CUDA support Resolve AUTO once after the clang-dumper executable is selected and carry the concrete mode through resource preparation, cache state, and per-translation-unit configuration. Keep local and plugin paths on SYSTEM, including the system Clang resource directory for built-in CUDA. Make CUDA support detection return false only for a validated manifest with no compatible host platform; propagate download, cache, parse, validation, and component failures. Tests: CudaResourcesTest (16), ClangResourcesTest (27), CxxCudaTest (6) --- .../pt/up/fe/specs/clang/ClangAstKeys.java | 5 -- .../src/pt/up/fe/specs/clang/ClangFiles.java | 10 ++- .../pt/up/fe/specs/clang/ClangResources.java | 43 +++++++++---- .../pt/up/fe/specs/clang/CudaResources.java | 61 ++++++++++++------- .../clang/codeparser/ParallelCodeParser.java | 13 ++-- .../fe/specs/clang/dumper/ClangAstDumper.java | 5 +- .../up/fe/specs/clang/ClangResourcesTest.java | 43 ++++++++++--- .../up/fe/specs/clang/CudaResourcesTest.java | 39 ++++++++++++ 8 files changed, 158 insertions(+), 61 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java index 1fef8afdf7..ec17d8a8d6 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangAstKeys.java @@ -16,7 +16,6 @@ import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.Datakey.KeyFactory; import org.suikasoft.jOptions.Interfaces.DataStore; -import pt.up.fe.specs.clang.ClangAstWebResource.LocalBuild; import pt.up.fe.specs.clava.ClavaLog; import pt.up.fe.specs.clava.ClavaOptions; import pt.up.fe.specs.clava.language.Standard; @@ -99,10 +98,6 @@ static DataStore toDataStore(List flags) { config.add(ClavaOptions.FLAGS_LIST, parsedFlags); - if (ClangAstWebResource.getDumperSource() instanceof LocalBuild) { - config.set(ClangAstKeys.LIBC_CXX_MODE, LibcMode.SYSTEM); - } - return config; } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java index 1fcbf321bc..d250a9ab85 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangFiles.java @@ -15,7 +15,15 @@ import java.io.File; import java.util.List; +import java.util.Objects; -public record ClangFiles(File clangExecutable, List builtinIncludes, File systemResourceDir) { +public record ClangFiles(File clangExecutable, List builtinIncludes, File systemResourceDir, + LibcMode libcMode) { + public ClangFiles { + Objects.requireNonNull(libcMode, "libcMode"); + if (libcMode == LibcMode.AUTO) { + throw new IllegalArgumentException("Clang files must use a concrete libc mode"); + } + } } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index c7fcffe77e..041f66e569 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -64,22 +65,26 @@ public File getBuiltinCudaLib() { return CudaResources.getBuiltinCudaLib(options.get(CodeParser.DUMPER_FOLDER).toPath()); } - public ClangFiles getClangFiles(LibcMode libcMode) { + public ClangFiles getClangFiles(LibcMode requestedLibcMode) { var source = ClangAstWebResource.getDumperSource(); var useBuiltinCuda = options.get(CodeParser.CUDA_PATH).equalsIgnoreCase(CodeParser.getBuiltinOption()); + var forceSystemLibc = source instanceof LocalBuild || ClangAstDumper.usePlugin(); if (source instanceof LocalBuild localBuild) { var clangExecutable = getLocalExecutable(localBuild.folder()); + var libcMode = resolveLibcMode(clangExecutable, requestedLibcMode, forceSystemLibc); var systemResourceDir = libcMode == LibcMode.SYSTEM && useBuiltinCuda ? findSystemClangResourceDir(null) : null; - return new ClangFiles(clangExecutable, List.of(), systemResourceDir); + return new ClangFiles(clangExecutable, List.of(), systemResourceDir, libcMode); } var resourceFolder = getClangResourceFolder(); - var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" - + resourceFolder.getAbsolutePath(); + var manifest = ClangAstWebResource.getManifest(resourceFolder); + File clangExecutable = prepareResources(manifest, resourceFolder); + var libcMode = resolveLibcMode(clangExecutable, requestedLibcMode, forceSystemLibc); + var key = libcMode.name() + "_" + useBuiltinCuda + "_" + source + "_" + resourceFolder.getAbsolutePath(); var cached = CLANG_FILES_CACHE.get(key); if (isUsable(cached)) { SpecsLogs.debug(() -> "Using cached version of Clang files: " + cached.files()); @@ -90,9 +95,7 @@ public ClangFiles getClangFiles(LibcMode libcMode) { CLANG_FILES_CACHE.remove(key, cached); } - var manifest = ClangAstWebResource.getManifest(resourceFolder); - File clangExecutable = prepareResources(manifest, resourceFolder); - var includes = prepareIncludes(manifest, clangExecutable, libcMode); + var includes = prepareIncludes(manifest, libcMode); var systemResourceDir = libcMode == LibcMode.SYSTEM && useBuiltinCuda ? prepareSystemClangResourceDir(manifest) : null; @@ -104,7 +107,8 @@ public ClangFiles getClangFiles(LibcMode libcMode) { touchUse(resourceFolder, includes.extractedFolder()); updateLastUsedAndCleanupStaleVersions(resourceFolder, includes.extractedFolder()); - var newFiles = new CachedClangFiles(new ClangFiles(clangExecutable, includes.folders(), systemResourceDir), + var newFiles = new CachedClangFiles(new ClangFiles(clangExecutable, includes.folders(), systemResourceDir, + libcMode), includes.extractedFolder()); var existingFiles = CLANG_FILES_CACHE.putIfAbsent(key, newFiles); var selectedFiles = existingFiles == null ? newFiles : existingFiles; @@ -113,6 +117,22 @@ public ClangFiles getClangFiles(LibcMode libcMode) { return selectedFiles.files(); } + static LibcMode resolveLibcMode(File clangExecutable, LibcMode requestedLibcMode, boolean forceSystem) { + Objects.requireNonNull(clangExecutable, "clangExecutable"); + Objects.requireNonNull(requestedLibcMode, "requestedLibcMode"); + + if (forceSystem) { + return LibcMode.SYSTEM; + } + + return switch (requestedLibcMode) { + case AUTO -> useBuiltinLibc(clangExecutable, requestedLibcMode) + ? LibcMode.BUILTIN_AND_LIBC + : LibcMode.SYSTEM; + case BUILTIN_AND_LIBC, SYSTEM -> requestedLibcMode; + }; + } + private boolean isUsable(CachedClangFiles cached) { if (cached == null) { return false; @@ -301,11 +321,8 @@ private static ProcessOutputAsString runClangAstDumper(File clangExecutable, Fil return SpecsSystem.runProcess(arguments, true, false); } - private PreparedIncludes prepareIncludes(ClangDumperManifest manifest, File clangExecutable, - LibcMode libcMode) { - var useBuiltinLibc = useBuiltinLibc(clangExecutable, libcMode); - - if (!useBuiltinLibc) { + private PreparedIncludes prepareIncludes(ClangDumperManifest manifest, LibcMode libcMode) { + if (libcMode == LibcMode.SYSTEM) { return new PreparedIncludes(List.of(), null); } diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java index 4cf8718d98..93afe53dd5 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java @@ -47,6 +47,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.regex.Pattern; @@ -140,7 +141,10 @@ static CudaPlatform requireSupportedPlatform() { } static CudaPlatform requireSupportedPlatform(NvidiaCudaManifest manifest) { - return getCurrentPlatform(manifest); + var platform = SupportedPlatform.getCurrentPlatform(); + var architecture = System.getProperty("os.arch"); + return findSupportedPlatform(manifest) + .orElseThrow(() -> unsupportedPlatform(manifest, platform, architecture)); } static boolean isSupportedPlatform() { @@ -148,12 +152,7 @@ static boolean isSupportedPlatform() { } static boolean isSupportedPlatform(Path cacheRoot) { - try { - getCurrentPlatform(cacheRoot); - return true; - } catch (RuntimeException e) { - return false; - } + return findSupportedPlatform(getCurrentManifest(cacheRoot)).isPresent(); } static CudaPlatform getCurrentPlatform() { @@ -161,18 +160,25 @@ static CudaPlatform getCurrentPlatform() { } static CudaPlatform getCurrentPlatform(Path cacheRoot) { - var releaseTag = ClangAstWebResource.getCudaReleaseTag(); - var releaseFolder = getReleaseFolder(cacheRoot, releaseTag); - claimReleaseInUse(cacheRoot, releaseFolder); - return getCurrentPlatform(getManifest(cacheRoot, releaseFolder)); + return requireSupportedPlatform(getCurrentManifest(cacheRoot)); } static CudaPlatform getCurrentPlatform(NvidiaCudaManifest manifest) { - return new CudaPlatform(getManifestPlatform(manifest, SupportedPlatform.getCurrentPlatform(), - System.getProperty("os.arch"))); + return requireSupportedPlatform(manifest); } static String getManifestPlatform(NvidiaCudaManifest manifest, SupportedPlatform platform, String architecture) { + return findManifestPlatform(manifest, platform, architecture) + .orElseThrow(() -> unsupportedPlatform(manifest, platform, architecture)); + } + + private static Optional findSupportedPlatform(NvidiaCudaManifest manifest) { + return findManifestPlatform(manifest, SupportedPlatform.getCurrentPlatform(), + System.getProperty("os.arch")).map(CudaPlatform::new); + } + + private static Optional findManifestPlatform(NvidiaCudaManifest manifest, SupportedPlatform platform, + String architecture) { Objects.requireNonNull(manifest, "manifest"); Objects.requireNonNull(platform, "platform"); Objects.requireNonNull(architecture, "architecture"); @@ -195,19 +201,28 @@ static String getManifestPlatform(NvidiaCudaManifest manifest, SupportedPlatform } } - var selectedPlatform = commonPlatforms.stream() + if (!missingComponents.isEmpty()) { + throw new RuntimeException("NVIDIA CUDA manifest is missing required components " + missingComponents + + ". Available manifest platform keys: " + getAvailablePlatformKeys(manifest)); + } + + return commonPlatforms.stream() .filter(candidate -> isCompatiblePlatform(candidate, platform, architecture)) .findFirst(); - if (missingComponents.isEmpty() && selectedPlatform.isPresent()) { - return selectedPlatform.get(); - } + } - var reason = missingComponents.isEmpty() - ? "no platform key is present in all required components and is compatible with this host" - : "the manifest is missing required components " + missingComponents; - throw new RuntimeException("Built-in CUDA is unsupported for host '" + platform + " (" + architecture - + ")': " + reason + ". Available manifest platform keys: " - + getAvailablePlatformKeys(manifest)); + private static RuntimeException unsupportedPlatform(NvidiaCudaManifest manifest, SupportedPlatform platform, + String architecture) { + return new RuntimeException("Built-in CUDA is unsupported for host '" + platform + " (" + architecture + + ")': no platform key is present in all required components and is compatible with this host" + + ". Available manifest platform keys: " + getAvailablePlatformKeys(manifest)); + } + + private static NvidiaCudaManifest getCurrentManifest(Path cacheRoot) { + var releaseTag = ClangAstWebResource.getCudaReleaseTag(); + var releaseFolder = getReleaseFolder(cacheRoot, releaseTag); + claimReleaseInUse(cacheRoot, releaseFolder); + return getManifest(cacheRoot, releaseFolder); } private static boolean isCompatiblePlatform(String manifestPlatform, SupportedPlatform hostPlatform, diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java index 9313859bda..91b7ca5b98 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/codeparser/ParallelCodeParser.java @@ -18,7 +18,6 @@ import org.suikasoft.jOptions.Interfaces.DataStore; import pt.up.fe.specs.clang.ClangAstKeys; import pt.up.fe.specs.clang.ClangResources; -import pt.up.fe.specs.clang.LibcMode; import pt.up.fe.specs.clang.dumper.ClangAstData; import pt.up.fe.specs.clang.dumper.ClangAstDumper; import pt.up.fe.specs.clang.dumper.ClangAstParser; @@ -89,7 +88,6 @@ public App parse(List inputSources, List compilerOptions, ClavaCon ConcurrentLinkedQueue clangDump = new ConcurrentLinkedQueue<>(); DataStore options = ClangAstKeys.toDataStore(compilerOptions); - options.set(ClangAstKeys.LIBC_CXX_MODE, get(ClangAstKeys.LIBC_CXX_MODE)); // Add context to config // ClavaContext context = new ClavaContext(); @@ -110,13 +108,8 @@ public App parse(List inputSources, List compilerOptions, ClavaCon // ClangResources clangResources = new ClangResources(get(SHOW_CLANG_DUMP)); ClangResources clangResources = new ClangResources(this); - - if (ClangAstDumper.usePlugin()) { - set(ClangAstKeys.LIBC_CXX_MODE, LibcMode.SYSTEM); - ClavaLog.debug(() -> "In Linux, ClangAstDumper is a plugin. LIBC_CXX_MODE is reset to SYSTEM."); - } - var clangFiles = clangResources.getClangFiles(get(ClangAstKeys.LIBC_CXX_MODE)); + options.set(ClangAstKeys.LIBC_CXX_MODE, clangFiles.libcMode()); // File clangExecutable = clangResources.prepareResources(version); // List builtinIncludes = clangResources.prepareIncludes(clangExecutable, // get(ClangAstKeys.USE_PLATFORM_INCLUDES)); @@ -246,7 +239,9 @@ public App parse(List inputSources, List compilerOptions, ClavaCon app.getContext().pushApp(app); app.setSourcesFromStrings(allSources); - app.addConfig(ClangAstKeys.toDataStore(compilerOptions)); + DataStore appConfig = ClangAstKeys.toDataStore(compilerOptions); + appConfig.set(ClangAstKeys.LIBC_CXX_MODE, clangFiles.libcMode()); + app.addConfig(appConfig); // Applies several passes to make the tree resemble more the original code, e.g., remove implicit nodes from // original clang tree diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java index 8b56c1c291..b0c1463f08 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/dumper/ClangAstDumper.java @@ -18,6 +18,7 @@ import org.suikasoft.jOptions.streamparser.LineStreamParser; import pt.up.fe.specs.clang.ClangAstKeys; import pt.up.fe.specs.clang.ClangResources; +import pt.up.fe.specs.clang.LibcMode; import pt.up.fe.specs.clang.cilk.CilkParser; import pt.up.fe.specs.clang.codeparser.CodeParser; import pt.up.fe.specs.clang.codeparser.ParallelCodeParser; @@ -260,8 +261,8 @@ else if (SourceType.isHeader(sourceFile)) { arguments.add("-resource-dir=" + systemResourceDir.getAbsolutePath()); } - // If it was determined that built-in includes will be used, disable system includes - if (ClangResources.useBuiltinLibc(clangExecutable, config.get(ClangAstKeys.LIBC_CXX_MODE))) { + // The parser has already resolved the libc policy before creating this per-file configuration. + if (config.get(ClangAstKeys.LIBC_CXX_MODE) == LibcMode.BUILTIN_AND_LIBC) { arguments.add("-nostdinc"); arguments.add("-nostdinc++"); } diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java index bdd8f23799..3169d7b778 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/ClangResourcesTest.java @@ -526,13 +526,18 @@ public void sameJvmInstancesReuseReleaseFilesAndPrepareIncludesOnlyForBuiltinLib } @Test - public void builtinCudaUsesSystemClangResourceWithSystemLibc() { + public void builtinCudaAutoSystemLibcUsesSameResourcePathAsExplicitSystem() { var parser = newParser(CodeParser.getBuiltinOption()); - var clangFiles = new ClangResources(parser).getClangFiles(LibcMode.SYSTEM); - - assertTrue(clangFiles.builtinIncludes().isEmpty()); - assertNotNull(clangFiles.systemResourceDir()); - assertTrue(clangFiles.systemResourceDir().isDirectory()); + var resources = new ClangResources(parser); + var autoFiles = resources.getClangFiles(LibcMode.AUTO); + var systemFiles = resources.getClangFiles(LibcMode.SYSTEM); + + assertEquals(LibcMode.SYSTEM, autoFiles.libcMode()); + assertEquals(LibcMode.SYSTEM, systemFiles.libcMode()); + assertTrue(autoFiles.builtinIncludes().isEmpty()); + assertEquals(systemFiles.systemResourceDir(), autoFiles.systemResourceDir()); + assertNotNull(autoFiles.systemResourceDir()); + assertTrue(autoFiles.systemResourceDir().isDirectory()); assertFalse(Files.exists(clangCacheRoot().resolve("includes"))); } @@ -562,8 +567,30 @@ public void libcDetectionIsScopedToTheExecutable() throws IOException { Files.writeString(builtinLibcDumper, "#!/bin/sh\nexit 1\n"); assertTrue(builtinLibcDumper.toFile().setExecutable(true)); - assertFalse(ClangResources.useBuiltinLibc(systemLibcDumper.toFile(), LibcMode.AUTO)); - assertTrue(ClangResources.useBuiltinLibc(builtinLibcDumper.toFile(), LibcMode.AUTO)); + assertEquals(LibcMode.SYSTEM, + ClangResources.resolveLibcMode(systemLibcDumper.toFile(), LibcMode.AUTO, false)); + assertEquals(LibcMode.BUILTIN_AND_LIBC, + ClangResources.resolveLibcMode(builtinLibcDumper.toFile(), LibcMode.AUTO, false)); + assertEquals(LibcMode.SYSTEM, + ClangResources.resolveLibcMode(systemLibcDumper.toFile(), LibcMode.SYSTEM, false)); + assertEquals(LibcMode.BUILTIN_AND_LIBC, + ClangResources.resolveLibcMode(builtinLibcDumper.toFile(), LibcMode.BUILTIN_AND_LIBC, false)); + } + + @Test + public void forcedBuildAndPluginModesResolveToSystemWithoutAutoState() throws IOException { + assumeTrue(!SupportedPlatform.getCurrentPlatform().isWindows(), "Shell fixtures require a Unix executable"); + + var dumper = tempFolder.resolve("dumper"); + Files.writeString(dumper, "#!/bin/sh\nexit 1\n"); + assertTrue(dumper.toFile().setExecutable(true)); + + assertEquals(LibcMode.SYSTEM, + ClangResources.resolveLibcMode(dumper.toFile(), LibcMode.BUILTIN_AND_LIBC, true)); + assertEquals(LibcMode.SYSTEM, + ClangResources.resolveLibcMode(dumper.toFile(), LibcMode.AUTO, true)); + assertThrows(IllegalArgumentException.class, + () -> new ClangFiles(dumper.toFile(), List.of(), null, LibcMode.AUTO)); } private CodeParser newParser(String cudaPath) { diff --git a/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java index 3a2576bbee..09f27952d9 100644 --- a/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java +++ b/ClangAstParser/test/pt/up/fe/specs/clang/CudaResourcesTest.java @@ -135,6 +135,35 @@ public void manifestRejectsHostWhenARequiredComponentLacksThePlatform() { assertTrue(error.getMessage().contains("cuda_cccl")); } + @Test + public void supportDetectionReturnsFalseOnlyForAValidatedUnsupportedHost() throws IOException { + var unsupportedPlatform = SupportedPlatform.getCurrentPlatform().isWindows() + ? "linux-riscv64" + : "windows-x86_64"; + writeCachedManifest(manifestJson(unsupportedPlatform)); + + assertFalse(CudaResources.isSupportedPlatform(tempFolder)); + } + + @Test + public void supportDetectionPropagatesManifestAndCacheFailures() throws IOException { + writeCachedManifest("not-json"); + assertThrows(RuntimeException.class, () -> CudaResources.isSupportedPlatform(tempFolder)); + + var invalidManifestRoot = Files.createDirectory(tempFolder.resolve("invalid")).toAbsolutePath(); + writeCachedManifest(invalidManifestRoot, manifestJson() + .replace("\"release_product\": \"cuda\"", "\"release_product\": \"other\"")); + assertThrows(RuntimeException.class, () -> CudaResources.isSupportedPlatform(invalidManifestRoot)); + + var missingComponentRoot = Files.createDirectory(tempFolder.resolve("missing")).toAbsolutePath(); + writeCachedManifest(missingComponentRoot, manifestJson().replace("\"cuda_cccl\":", "\"missing\":")); + assertThrows(RuntimeException.class, () -> CudaResources.isSupportedPlatform(missingComponentRoot)); + + var cacheFile = tempFolder.resolve("cache-file"); + Files.writeString(cacheFile, "not-a-directory"); + assertThrows(RuntimeException.class, () -> CudaResources.isSupportedPlatform(cacheFile)); + } + @Test public void additionalCompatibleManifestPlatformNeedsNoJavaSupportWhitelist() { var additionalPlatform = "linux-riscv64"; @@ -520,6 +549,16 @@ private static String hostPlatform() { return PLATFORM; } + private void writeCachedManifest(String json) throws IOException { + writeCachedManifest(tempFolder, json); + } + + private void writeCachedManifest(Path cacheRoot, String json) throws IOException { + var releaseFolder = cacheRoot.resolve("cuda").resolve(ClangAstWebResource.getCudaReleaseTag()); + Files.createDirectories(releaseFolder); + Files.writeString(releaseFolder.resolve(CudaResources.getManifestFilename(RELEASE)), json); + } + private static void writeTarXz(Path archive, Map files) throws IOException { try (OutputStream output = Files.newOutputStream(archive); var xzOutput = new XZCompressorOutputStream(output); From 8919c363a506be3b5bbd4916311e727511bb5b11 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Mon, 6 Jul 2026 18:43:15 +0100 Subject: [PATCH 79/99] Add aggregate Clava build and JS coverage Introduce a root Clava Gradle aggregate for Java and Clava-JS build/test tasks, validate the Clava-JS java-binaries symlink against ClavaWeaver installDist, and wire Clava-JS Jest runs to JaCoCo so Node-hosted JVM coverage is merged with Java test coverage. --- Clava-JS/README.md | 16 ++- Clava-JS/api/jest.config.ts | 4 +- Clava-JS/jest/jestGlobalSetup.ts | 40 ++++++ Clava-JS/jest/jestGlobalTeardown.ts | 20 +++ build.gradle | 182 ++++++++++++++++++++++++++++ settings.gradle | 8 ++ 6 files changed, 259 insertions(+), 11 deletions(-) create mode 100644 Clava-JS/jest/jestGlobalSetup.ts create mode 100644 Clava-JS/jest/jestGlobalTeardown.ts create mode 100644 build.gradle create mode 100644 settings.gradle diff --git a/Clava-JS/README.md b/Clava-JS/README.md index 0182411ffa..56da27ab09 100644 --- a/Clava-JS/README.md +++ b/Clava-JS/README.md @@ -35,21 +35,19 @@ Starting from the `workspace` directory, execute the following commands to build ```bash npm install -npm run build -w lara-framework/Lara-JS -npm run build -w clava/Clava-JS -npm install -cd clava/ClavaWeaver -gradle installDist -cd ../.. +gradle -p clava clavaJsBuild ``` -Finally, copy the JARs in the folder `./clava/ClavaWeaver/build/install/ClavaWeaver/lib` into a new folder called java-binaries in `./clava/Clava-JS`: +The `clavaJsBuild` task builds Lara-JS, runs `ClavaWeaver:installDist`, and checks that `./clava/Clava-JS/java-binaries` is the symlink to `../ClavaWeaver/build/install/ClavaWeaver`. Keep this symlink instead of copying the JARs, otherwise Clava-JS can silently run against stale Java binaries. + +To run the Java and Clava-JS tests from one place and generate the merged Java coverage report: ```bash -mkdir clava/Clava-JS/java-binaries -cp -r ./clava/ClavaWeaver/build/install/ClavaWeaver/lib ./clava/Clava-JS/java-binaries +gradle -p clava check clavaMergedJacocoReport ``` +The merged report is written to `./clava/build/reports/jacoco/clavaMergedJacocoReport/html/index.html`. It combines normal Gradle JaCoCo execution data with the JaCoCo data captured from the JVM embedded in Clava-JS tests. + Install the package globally: ```bash diff --git a/Clava-JS/api/jest.config.ts b/Clava-JS/api/jest.config.ts index 64b53490b5..de47ada83a 100644 --- a/Clava-JS/api/jest.config.ts +++ b/Clava-JS/api/jest.config.ts @@ -4,8 +4,8 @@ import { weaverConfig } from "../code/WeaverConfiguration.ts"; export default defineConfig({ preset: "ts-jest/presets/default-esm", testEnvironment: "@specs-feup/lara/jest/jestEnvironment.ts", - globalSetup: "@specs-feup/lara/jest/jestGlobalSetup.ts", - globalTeardown: "@specs-feup/lara/jest/jestGlobalTeardown.ts", + globalSetup: "/../jest/jestGlobalSetup.ts", + globalTeardown: "/../jest/jestGlobalTeardown.ts", setupFiles: ["@specs-feup/lara/jest/setupFiles/sharedJavaModule.ts"], setupFilesAfterEnv: ["@specs-feup/lara/jest/setupFiles/importSideEffects.ts"], moduleNameMapper: { diff --git a/Clava-JS/jest/jestGlobalSetup.ts b/Clava-JS/jest/jestGlobalSetup.ts new file mode 100644 index 0000000000..37d08fd10f --- /dev/null +++ b/Clava-JS/jest/jestGlobalSetup.ts @@ -0,0 +1,40 @@ +import laraGlobalSetup from "@specs-feup/lara/jest/jestGlobalSetup.ts"; +import java from "java"; + +export default async function (...args: Parameters) { + applyJavaOptions(); + await laraGlobalSetup(...args); +} + +function applyJavaOptions(): void { + const rawJavaOptions = process.env.CLAVA_JS_JAVA_OPTIONS; + + if (rawJavaOptions === undefined || rawJavaOptions.trim() === "") { + return; + } + + const javaOptions = parseJavaOptions(rawJavaOptions); + + for (const javaOption of javaOptions) { + if (!java.options.includes(javaOption)) { + java.options.push(javaOption); + } + } +} + +function parseJavaOptions(rawJavaOptions: string): string[] { + try { + const parsed = JSON.parse(rawJavaOptions); + + if ( + Array.isArray(parsed) && + parsed.every((javaOption) => typeof javaOption === "string") + ) { + return parsed; + } + } catch { + // Fall back to whitespace splitting for ad-hoc local use. + } + + return rawJavaOptions.split(/\s+/).filter((javaOption) => javaOption !== ""); +} diff --git a/Clava-JS/jest/jestGlobalTeardown.ts b/Clava-JS/jest/jestGlobalTeardown.ts new file mode 100644 index 0000000000..82a7b8121e --- /dev/null +++ b/Clava-JS/jest/jestGlobalTeardown.ts @@ -0,0 +1,20 @@ +import laraGlobalTeardown from "@specs-feup/lara/jest/jestGlobalTeardown.ts"; +import java from "java"; + +export default async function () { + dumpJacocoCoverage(); + await laraGlobalTeardown(); +} + +function dumpJacocoCoverage(): void { + if (process.env.CLAVA_JS_JAVA_OPTIONS === undefined) { + return; + } + + try { + const JacocoRuntime = java.import("org.jacoco.agent.rt.RT"); + JacocoRuntime.getAgent().dump(false); + } catch (error) { + console.warn(`Could not dump Clava-JS JaCoCo coverage: ${error}`); + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..54282f8153 --- /dev/null +++ b/build.gradle @@ -0,0 +1,182 @@ +import groovy.json.JsonOutput + +plugins { + id 'base' + id 'jacoco' +} + +jacoco { + toolVersion = '0.8.13' +} + +repositories { + mavenCentral() +} + +configurations { + jacocoRuntimeAgent +} + +dependencies { + jacocoRuntimeAgent("org.jacoco:org.jacoco.agent:${jacoco.toolVersion}") { + artifact { + classifier = 'runtime' + extension = 'jar' + } + transitive = false + } +} + +def workspaceRoot = layout.projectDirectory.dir('..') +def clavaJsDir = layout.projectDirectory.dir('Clava-JS') +def clavaWeaverInstallDir = layout.projectDirectory.dir('ClavaWeaver/build/install/ClavaWeaver') +def clavaJsJavaBinaries = clavaJsDir.file('java-binaries') +def clavaJsJacocoExec = layout.buildDirectory.file('jacoco/clava-js.exec') + +def clavaJavaBuilds = [ + 'AntarexClavaApi', + 'ClangAstParser', + 'ClavaAst', + 'ClavaLaraApi', + 'ClavaOptionsManager', + 'ClavaWeaver' +] + +def clavaCoverageProjects = [ + [name: 'AntarexClavaApi', sourceDirs: ['src-java']], + [name: 'ClangAstParser', sourceDirs: ['src']], + [name: 'ClavaAst', sourceDirs: ['src']], + [name: 'ClavaLaraApi', sourceDirs: ['src-java']], + [name: 'ClavaOptionsManager', sourceDirs: ['src']], + [name: 'ClavaWeaver', sourceDirs: ['src', 'src-spec']] +] + +tasks.register('clavaJavaAssemble') { + group = 'build' + description = 'Assembles all Clava Java projects through the aggregate build.' + dependsOn clavaJavaBuilds.collect { gradle.includedBuild(it).task(':assemble') } +} + +tasks.register('clavaJavaTest') { + group = 'verification' + description = 'Runs the Java test tasks for all Clava Java projects.' + dependsOn clavaJavaBuilds.collect { gradle.includedBuild(it).task(':test') } + finalizedBy 'clavaMergedJacocoReport' +} + +tasks.register('installClavaWeaver') { + group = 'distribution' + description = 'Installs the ClavaWeaver distribution used by Clava-JS.' + dependsOn gradle.includedBuild('ClavaWeaver').task(':installDist') +} + +tasks.register('validateClavaJsJavaBinariesSymlink') { + group = 'verification' + description = 'Checks that Clava-JS/java-binaries points at the current ClavaWeaver installDist output.' + dependsOn 'installClavaWeaver' + + doLast { + def link = clavaJsJavaBinaries.asFile.toPath() + def expectedTarget = java.nio.file.Paths.get('../ClavaWeaver/build/install/ClavaWeaver') + + if (!java.nio.file.Files.isSymbolicLink(link)) { + throw new GradleException("${clavaJsJavaBinaries.asFile} must be a symbolic link to ${expectedTarget}") + } + + def actualTarget = java.nio.file.Files.readSymbolicLink(link) + if (actualTarget != expectedTarget) { + throw new GradleException("${clavaJsJavaBinaries.asFile} points to ${actualTarget}, expected ${expectedTarget}") + } + + if (!clavaWeaverInstallDir.file('lib').asFile.isDirectory()) { + throw new GradleException("ClavaWeaver installDist output is missing: ${clavaWeaverInstallDir.file('lib').asFile}") + } + } +} + +tasks.register('npmInstall', Exec) { + group = 'build setup' + description = 'Installs npm workspace dependencies when node_modules is missing.' + workingDir = workspaceRoot.asFile + commandLine 'npm', 'install' + onlyIf { + !workspaceRoot.dir('node_modules').asFile.isDirectory() + } +} + +tasks.register('laraJsBuild', Exec) { + group = 'build' + description = 'Builds Lara-JS through the npm workspace.' + dependsOn 'npmInstall' + workingDir = workspaceRoot.asFile + commandLine 'npm', 'run', 'build', '-w', 'lara-framework/Lara-JS' +} + +tasks.register('clavaJsBuild', Exec) { + group = 'build' + description = 'Builds Clava-JS after refreshing the Java distribution symlink target.' + dependsOn 'validateClavaJsJavaBinariesSymlink', 'laraJsBuild' + workingDir = workspaceRoot.asFile + commandLine 'npm', 'run', 'build', '-w', 'clava/Clava-JS' +} + +tasks.register('clavaJsTest', Exec) { + group = 'verification' + description = 'Runs Clava-JS tests with JaCoCo attached to the embedded JVM.' + dependsOn 'clavaJsBuild' + finalizedBy 'clavaMergedJacocoReport' + workingDir = workspaceRoot.asFile + commandLine 'npm', 'run', 'test', '-w', 'clava/Clava-JS' + + doFirst { + def execFile = clavaJsJacocoExec.get().asFile + execFile.parentFile.mkdirs() + if (execFile.isFile()) { + execFile.delete() + } + + def agentFile = configurations.jacocoRuntimeAgent.singleFile + def includes = 'pt.up.fe.specs.*:org.lara.*:org.suikasoft.*:larai.*' + def agentOption = "-javaagent:${agentFile.absolutePath}=destfile=${execFile.absolutePath},append=false,dumponexit=true,jmx=true,includes=${includes}" + environment 'CLAVA_JS_JAVA_OPTIONS', JsonOutput.toJson([agentOption]) + } +} + +tasks.register('clavaMergedJacocoReport', JacocoReport) { + group = 'verification' + description = 'Generates a merged Java coverage report from Gradle tests and Clava-JS tests.' + + executionData.from( + fileTree(layout.projectDirectory.asFile) { + include '*/build/jacoco/*.exec' + include '*/build/jacoco/**/*.exec' + }, + clavaJsJacocoExec + ) + + sourceDirectories.from(clavaCoverageProjects.collectMany { projectInfo -> + projectInfo.sourceDirs.collect { layout.projectDirectory.dir("${projectInfo.name}/${it}") } + }) + + classDirectories.from(clavaCoverageProjects.collect { projectInfo -> + layout.projectDirectory.dir("${projectInfo.name}/build/classes/java") + }) + + reports { + xml.required = true + html.required = true + csv.required = false + } + + onlyIf { + executionData.files.any { it.isFile() } + } +} + +tasks.named('assemble') { + dependsOn 'clavaJavaAssemble', 'clavaJsBuild' +} + +tasks.named('check') { + dependsOn 'clavaJavaTest', 'clavaJsTest' +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..10e329e795 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,8 @@ +rootProject.name = 'clava' + +includeBuild('AntarexClavaApi') +includeBuild('ClangAstParser') +includeBuild('ClavaAst') +includeBuild('ClavaLaraApi') +includeBuild('ClavaOptionsManager') +includeBuild('ClavaWeaver') From d94f2033af08dc0b177f748535919a0399350d87 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 17:33:04 +0100 Subject: [PATCH 80/99] test: run Jest serially without forced exit --- Clava-JS/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 519ef567c8..47309ab81d 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -34,7 +34,7 @@ "build": "tsc", "build:watch": "npm run build -- --watch", "lint": "eslint --flag unstable_native_nodejs_ts_config .", - "test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --detectOpenHandles --forceExit", + "test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --runInBand", "test:api": "npm run test -- api", "test:code": "npm run test -- code", "test:cov": "npm run test -- --coverage", From d77441d5ef7dee4dd0e633bf36835fe43b927af0 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 17:49:48 +0100 Subject: [PATCH 81/99] migrate tests to vitest Adopt the Lara-JS shared lint and formatter configuration, replace Jest test setup with Vitest, and run TypeScript tests through Node natively. --- Clava-JS/api/Issues.test.ts | 2 +- .../api/LegacyIntegrationTests - C.test.ts | 4 +- .../api/LegacyIntegrationTests - CXX.test.ts | 4 +- .../LegacyIntegrationTests - Issues.test.ts | 4 +- Clava-JS/api/Query.test.ts | 2 +- Clava-JS/api/clava/ClavaJoinPoints.test.ts | 2 +- Clava-JS/api/jest.config.ts | 17 ------ Clava-JS/api/vitest.config.ts | 19 +++++++ Clava-JS/code/ClangPlugin/ClangPlugin.test.ts | 8 +-- Clava-JS/code/jest.config.ts | 9 ---- Clava-JS/code/vitest.config.ts | 18 +++++++ Clava-JS/eslint.config.ts | 53 +------------------ Clava-JS/jest.config.ts | 18 ------- Clava-JS/jest/ClavaLegacyTester.ts | 44 --------------- Clava-JS/jest/jestGlobalTeardown.ts | 20 ------- Clava-JS/package.json | 21 +++----- Clava-JS/prettier.config.ts | 3 ++ Clava-JS/tsconfig.json | 2 +- Clava-JS/vitest.config.ts | 13 +++++ Clava-JS/vitest/ClavaLegacyTester.ts | 44 +++++++++++++++ .../setupClavaWeaver.ts} | 15 +++--- 21 files changed, 127 insertions(+), 195 deletions(-) delete mode 100644 Clava-JS/api/jest.config.ts create mode 100644 Clava-JS/api/vitest.config.ts delete mode 100644 Clava-JS/code/jest.config.ts create mode 100644 Clava-JS/code/vitest.config.ts delete mode 100644 Clava-JS/jest.config.ts delete mode 100644 Clava-JS/jest/ClavaLegacyTester.ts delete mode 100644 Clava-JS/jest/jestGlobalTeardown.ts create mode 100644 Clava-JS/prettier.config.ts create mode 100644 Clava-JS/vitest.config.ts create mode 100644 Clava-JS/vitest/ClavaLegacyTester.ts rename Clava-JS/{jest/jestGlobalSetup.ts => vitest/setupClavaWeaver.ts} (65%) diff --git a/Clava-JS/api/Issues.test.ts b/Clava-JS/api/Issues.test.ts index 165a255610..d6e3a89fdb 100644 --- a/Clava-JS/api/Issues.test.ts +++ b/Clava-JS/api/Issues.test.ts @@ -1,7 +1,7 @@ import { registerSourceCode, registerSourceCodes, -} from "@specs-feup/lara/jest/jestHelpers.ts"; +} from "@specs-feup/lara/vitest/weaverTestHelpers.ts"; import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { FunctionJp } from "./Joinpoints.ts"; import ClavaJoinPoints from "./clava/ClavaJoinPoints.ts"; diff --git a/Clava-JS/api/LegacyIntegrationTests - C.test.ts b/Clava-JS/api/LegacyIntegrationTests - C.test.ts index 819f278223..dbd27bf31e 100644 --- a/Clava-JS/api/LegacyIntegrationTests - C.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - C.test.ts @@ -1,4 +1,4 @@ -import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.ts"; +import { ClavaLegacyTester } from "../vitest/ClavaLegacyTester.ts"; import JavaTypes from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; import ClavaJavaTypes from "./clava/ClavaJavaTypes.ts"; import path from "path"; @@ -6,7 +6,7 @@ import path from "path"; const isWindows = process.platform === "win32"; const isMacOS = process.platform === "darwin"; -/* eslint-disable jest/expect-expect */ +/* eslint-disable vitest/expect-expect */ describe("CTest", () => { function newTester() { return new ClavaLegacyTester( diff --git a/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts b/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts index c0bef9b83d..54d143a814 100644 --- a/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts @@ -1,4 +1,4 @@ -import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.ts"; +import { ClavaLegacyTester } from "../vitest/ClavaLegacyTester.ts"; import ClavaJavaTypes from "./clava/ClavaJavaTypes.ts"; import JavaInterop from "@specs-feup/lara/api/lara/JavaInterop.ts"; import path from "path"; @@ -6,7 +6,7 @@ import path from "path"; const isWindows = process.platform === "win32"; const isMacOS = process.platform === "darwin"; -/* eslint-disable jest/expect-expect */ +/* eslint-disable vitest/expect-expect */ describe("CxxTest", () => { function newTester() { return new ClavaLegacyTester( diff --git a/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts b/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts index e73847c701..11b6447f7f 100644 --- a/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts @@ -1,8 +1,8 @@ -import { ClavaLegacyTester } from "../jest/ClavaLegacyTester.ts"; +import { ClavaLegacyTester } from "../vitest/ClavaLegacyTester.ts"; import ClavaJavaTypes from "./clava/ClavaJavaTypes.ts"; import path from "path"; -/* eslint-disable jest/expect-expect */ +/* eslint-disable vitest/expect-expect */ describe("IssuesTest", () => { function newTester() { return new ClavaLegacyTester( diff --git a/Clava-JS/api/Query.test.ts b/Clava-JS/api/Query.test.ts index ca2fba6d11..f937f5a1fb 100644 --- a/Clava-JS/api/Query.test.ts +++ b/Clava-JS/api/Query.test.ts @@ -1,4 +1,4 @@ -import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.ts"; +import { registerSourceCode } from "@specs-feup/lara/vitest/weaverTestHelpers.ts"; import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { FunctionJp, Loop } from "./Joinpoints.ts"; diff --git a/Clava-JS/api/clava/ClavaJoinPoints.test.ts b/Clava-JS/api/clava/ClavaJoinPoints.test.ts index 53cf209c38..ae34056103 100644 --- a/Clava-JS/api/clava/ClavaJoinPoints.test.ts +++ b/Clava-JS/api/clava/ClavaJoinPoints.test.ts @@ -1,4 +1,4 @@ -import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.ts"; +import { registerSourceCode } from "@specs-feup/lara/vitest/weaverTestHelpers.ts"; import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { ExprStmt } from "../Joinpoints.ts"; import ClavaJoinPoints from "./ClavaJoinPoints.ts"; diff --git a/Clava-JS/api/jest.config.ts b/Clava-JS/api/jest.config.ts deleted file mode 100644 index de47ada83a..0000000000 --- a/Clava-JS/api/jest.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from "jest"; -import { weaverConfig } from "../code/WeaverConfiguration.ts"; - -export default defineConfig({ - preset: "ts-jest/presets/default-esm", - testEnvironment: "@specs-feup/lara/jest/jestEnvironment.ts", - globalSetup: "/../jest/jestGlobalSetup.ts", - globalTeardown: "/../jest/jestGlobalTeardown.ts", - setupFiles: ["@specs-feup/lara/jest/setupFiles/sharedJavaModule.ts"], - setupFilesAfterEnv: ["@specs-feup/lara/jest/setupFiles/importSideEffects.ts"], - moduleNameMapper: { - "(.+)\\.js": "$1", - }, - testEnvironmentOptions: { - weaverConfig, - }, -}); diff --git a/Clava-JS/api/vitest.config.ts b/Clava-JS/api/vitest.config.ts new file mode 100644 index 0000000000..0e97e83fab --- /dev/null +++ b/Clava-JS/api/vitest.config.ts @@ -0,0 +1,19 @@ +import { defineProject } from "vitest/config"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.dirname(fileURLToPath(import.meta.url)); + +export default defineProject({ + root, + test: { + experimental: { + viteModuleRunner: false, + }, + fileParallelism: false, + globals: true, + name: "api", + pool: "forks", + setupFiles: ["../vitest/setupClavaWeaver.ts"], + }, +}); diff --git a/Clava-JS/code/ClangPlugin/ClangPlugin.test.ts b/Clava-JS/code/ClangPlugin/ClangPlugin.test.ts index 376983f398..cea1531967 100644 --- a/Clava-JS/code/ClangPlugin/ClangPlugin.test.ts +++ b/Clava-JS/code/ClangPlugin/ClangPlugin.test.ts @@ -1,4 +1,4 @@ -import { jest } from "@jest/globals"; +import { vi } from "vitest"; import fs from "fs"; import path from "path"; @@ -76,17 +76,17 @@ describe("ClangPlugin", () => { const actualMap = await ClangPlugin.getAvailablePlugins(); expect(actualMap).toEqual(expectedMap); - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); it("should throw an error when the clang-plugin-binaries directory does not exist", async () => { - jest.spyOn(fs, "existsSync").mockClear().mockReturnValue(false); + vi.spyOn(fs, "existsSync").mockClear().mockReturnValue(false); await expect(ClangPlugin.getAvailablePlugins()).rejects.toThrow( "Could not find 'clang-plugin-binaries' directory" ); - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); }); */ diff --git a/Clava-JS/code/jest.config.ts b/Clava-JS/code/jest.config.ts deleted file mode 100644 index 76873b4a20..0000000000 --- a/Clava-JS/code/jest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig } from "jest"; - -export default defineConfig({ - preset: "ts-jest/presets/default-esm", - testEnvironment: "node", - moduleNameMapper: { - "(.+)\\.js": "$1", - }, -}); diff --git a/Clava-JS/code/vitest.config.ts b/Clava-JS/code/vitest.config.ts new file mode 100644 index 0000000000..c1ba777db6 --- /dev/null +++ b/Clava-JS/code/vitest.config.ts @@ -0,0 +1,18 @@ +import { defineProject } from "vitest/config"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.dirname(fileURLToPath(import.meta.url)); + +export default defineProject({ + root, + test: { + experimental: { + viteModuleRunner: false, + }, + fileParallelism: false, + globals: true, + name: "code", + pool: "forks", + }, +}); diff --git a/Clava-JS/eslint.config.ts b/Clava-JS/eslint.config.ts index 7df9bdcf0e..a8f4237a32 100644 --- a/Clava-JS/eslint.config.ts +++ b/Clava-JS/eslint.config.ts @@ -1,52 +1,3 @@ -import js from "@eslint/js"; -import eslintConfigPrettier from "eslint-config-prettier"; -import jest from "eslint-plugin-jest"; -import tsdoc from "eslint-plugin-tsdoc"; -import { defineConfig } from "eslint/config"; -import { dirname } from "path"; -import typescriptEslint from "typescript-eslint"; -import { fileURLToPath } from "url"; +import { createEslintConfig } from "@specs-feup/lara/eslint.config.ts"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -export default defineConfig([ - js.configs.recommended, - eslintConfigPrettier, - ...typescriptEslint.configs.recommended, - { - plugins: { - "@typescript-eslint": typescriptEslint.plugin, - tsdoc, - }, - - languageOptions: { - parser: typescriptEslint.parser, - ecmaVersion: 5, - sourceType: "script", - - parserOptions: { - project: ["./tsconfig.json", "./tsconfig.*.json"], - tsconfigRootDir: __dirname, - }, - }, - - rules: { - "tsdoc/syntax": "warn", - }, - }, - { - ...jest.configs["flat/recommended"], - files: ["**/*.spec.ts", "**/*.test.ts"], - - plugins: { - jest, - }, - - languageOptions: { - globals: { - ...jest.environments.globals.globals, - }, - }, - }, -]); +export default createEslintConfig(import.meta.dirname); diff --git a/Clava-JS/jest.config.ts b/Clava-JS/jest.config.ts deleted file mode 100644 index cfa242603e..0000000000 --- a/Clava-JS/jest.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from "jest"; - -export default defineConfig({ - preset: "ts-jest/presets/default-esm", - testEnvironment: "node", - notify: true, - notifyMode: "always", - //verbose: true, - collectCoverage: false, - coverageDirectory: "coverage", - coverageReporters: ["text", "lcov"], - collectCoverageFrom: ["**/*[^.d].(t|j)s"], - coverageProvider: "v8", - moduleNameMapper: { - "(.+)\\.js": "$1", - }, - projects: ["api", "code"], -}); diff --git a/Clava-JS/jest/ClavaLegacyTester.ts b/Clava-JS/jest/ClavaLegacyTester.ts deleted file mode 100644 index a57c0c07e6..0000000000 --- a/Clava-JS/jest/ClavaLegacyTester.ts +++ /dev/null @@ -1,44 +0,0 @@ -import ClavaJavaTypes, { - type ClavaJavaClasses, -} from "../api/clava/ClavaJavaTypes.ts"; - -import { WeaverLegacyTester } from "@specs-feup/lara/jest/WeaverLegacyTester.ts"; - -export class ClavaLegacyTester extends WeaverLegacyTester { - protected readonly WORK_FOLDER: string = "cxx_weaver_output"; - private readonly standard: ClavaJavaClasses.Standard; - private readonly compilerFlags: string; - - public constructor( - basePackage: string, - standard: ClavaJavaClasses.Standard, - compilerFlags: string = "" - ) { - super(basePackage); - this.standard = standard; - this.compilerFlags = compilerFlags; - - this.set(ClavaJavaTypes.ClavaOptions.FLAGS, this.compilerFlags); - } - - public async test( - laraResource: string, - ...codeResources: string[] - ): Promise { - if (this.standard != null) { - this.set(ClavaJavaTypes.ClavaOptions.STANDARD, this.standard); - } - - this.set( - ClavaJavaTypes.CxxWeaverOption.CHECK_SYNTAX, - this.checkWovenCodeSyntax - ); - this.set(ClavaJavaTypes.CxxWeaverOption.DISABLE_CLAVA_INFO, true); - this.set(ClavaJavaTypes.CxxWeaverOption.DISABLE_CODE_GENERATION); - - // Enable parallel parsing - //this.set(ClavaJavaTypes.ParallelCodeParser.PARALLEL_PARSING); - - await super.test(laraResource, ...codeResources); - } -} diff --git a/Clava-JS/jest/jestGlobalTeardown.ts b/Clava-JS/jest/jestGlobalTeardown.ts deleted file mode 100644 index 82a7b8121e..0000000000 --- a/Clava-JS/jest/jestGlobalTeardown.ts +++ /dev/null @@ -1,20 +0,0 @@ -import laraGlobalTeardown from "@specs-feup/lara/jest/jestGlobalTeardown.ts"; -import java from "java"; - -export default async function () { - dumpJacocoCoverage(); - await laraGlobalTeardown(); -} - -function dumpJacocoCoverage(): void { - if (process.env.CLAVA_JS_JAVA_OPTIONS === undefined) { - return; - } - - try { - const JacocoRuntime = java.import("org.jacoco.agent.rt.RT"); - JacocoRuntime.getAgent().dump(false); - } catch (error) { - console.warn(`Could not dump Clava-JS JaCoCo coverage: ${error}`); - } -} diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 47309ab81d..5a9d99100b 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -9,10 +9,12 @@ "exports": { "./api/*.test.ts": null, "./api/*.config.ts": null, + "./api/*.js": "./api/*.ts", "./api/*": "./api/*.ts", "./api/*.ts": "./api/*.ts", "./code/*.test.ts": null, "./code/*.config.ts": null, + "./code/*.js": "./code/*.ts", "./code/*": "./code/*.ts", "./code/*.ts": "./code/*.ts" }, @@ -22,7 +24,8 @@ "java-binaries", ".gitignore", "eslint.config.ts", - "jest.config.ts", + "prettier.config.ts", + "vitest.config.ts", "LICENSE", "package.json", "README.md", @@ -34,7 +37,9 @@ "build": "tsc", "build:watch": "npm run build -- --watch", "lint": "eslint --flag unstable_native_nodejs_ts_config .", - "test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --runInBand", + "format": "prettier --write .", + "format:check": "prettier --check .", + "test": "vitest run", "test:api": "npm run test -- api", "test:code": "npm run test -- code", "test:cov": "npm run test -- --coverage", @@ -64,21 +69,11 @@ "cytoscape": "^3.33.1" }, "devDependencies": { - "@jest/globals": "^30.2.0", "@types/debug": "^4.1.12", "@types/java": "^0.9.6", - "@types/jest": "^30.0.0", "@types/node": "^25.0.0", "@types/yargs": "^17.0.35", - "cross-env": "^10.1.0", "eslint": "^9.39.2", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jest": "^29.12.1", - "eslint-plugin-tsdoc": "^0.5.0", - "jest": "^30.2.0", - "node-notifier": "^10.0.1", - "ts-jest": "^29.4.6", - "typescript": "^7.0.2", - "typescript-eslint": "^8.52.0" + "vitest": "^4.1.10" } } diff --git a/Clava-JS/prettier.config.ts b/Clava-JS/prettier.config.ts new file mode 100644 index 0000000000..f4c5fe7217 --- /dev/null +++ b/Clava-JS/prettier.config.ts @@ -0,0 +1,3 @@ +import config from "@specs-feup/lara/prettier.config.ts"; + +export default config; diff --git a/Clava-JS/tsconfig.json b/Clava-JS/tsconfig.json index 45c2114568..416c109338 100644 --- a/Clava-JS/tsconfig.json +++ b/Clava-JS/tsconfig.json @@ -6,7 +6,7 @@ "strict": true, "isolatedModules": true, "allowSyntheticDefaultImports": true, - "types": ["node", "jest"], + "types": ["node", "vitest/globals"], "rewriteRelativeImportExtensions": true, "erasableSyntaxOnly": true, "verbatimModuleSyntax": true diff --git a/Clava-JS/vitest.config.ts b/Clava-JS/vitest.config.ts new file mode 100644 index 0000000000..3b9aced4d0 --- /dev/null +++ b/Clava-JS/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + coverage: { + include: ["**/*[^.d].(t|j)s"], + provider: "v8", + reporter: ["text", "lcov"], + }, + maxWorkers: 1, + projects: ["api/vitest.config.ts", "code/vitest.config.ts"], + }, +}); diff --git a/Clava-JS/vitest/ClavaLegacyTester.ts b/Clava-JS/vitest/ClavaLegacyTester.ts new file mode 100644 index 0000000000..3b038339e5 --- /dev/null +++ b/Clava-JS/vitest/ClavaLegacyTester.ts @@ -0,0 +1,44 @@ +import ClavaJavaTypes, { + type ClavaJavaClasses, +} from "../api/clava/ClavaJavaTypes.ts"; + +import { WeaverLegacyTester } from "@specs-feup/lara/vitest/WeaverLegacyTester.ts"; + +export class ClavaLegacyTester extends WeaverLegacyTester { + protected readonly WORK_FOLDER: string = "cxx_weaver_output"; + private readonly standard: ClavaJavaClasses.Standard; + private readonly compilerFlags: string; + + public constructor( + basePackage: string, + standard: ClavaJavaClasses.Standard, + compilerFlags: string = "", + ) { + super(basePackage); + this.standard = standard; + this.compilerFlags = compilerFlags; + + this.set(ClavaJavaTypes.ClavaOptions.FLAGS, this.compilerFlags); + } + + public async test( + laraResource: string, + ...codeResources: string[] + ): Promise { + if (this.standard != null) { + this.set(ClavaJavaTypes.ClavaOptions.STANDARD, this.standard); + } + + this.set( + ClavaJavaTypes.CxxWeaverOption.CHECK_SYNTAX, + this.checkWovenCodeSyntax, + ); + this.set(ClavaJavaTypes.CxxWeaverOption.DISABLE_CLAVA_INFO, true); + this.set(ClavaJavaTypes.CxxWeaverOption.DISABLE_CODE_GENERATION); + + // Enable parallel parsing + //this.set(ClavaJavaTypes.ParallelCodeParser.PARALLEL_PARSING); + + await super.test(laraResource, ...codeResources); + } +} diff --git a/Clava-JS/jest/jestGlobalSetup.ts b/Clava-JS/vitest/setupClavaWeaver.ts similarity index 65% rename from Clava-JS/jest/jestGlobalSetup.ts rename to Clava-JS/vitest/setupClavaWeaver.ts index 37d08fd10f..7d2a3e6398 100644 --- a/Clava-JS/jest/jestGlobalSetup.ts +++ b/Clava-JS/vitest/setupClavaWeaver.ts @@ -1,10 +1,9 @@ -import laraGlobalSetup from "@specs-feup/lara/jest/jestGlobalSetup.ts"; +import { weaverConfig } from "../code/WeaverConfiguration.ts"; +import { setupWeaver } from "@specs-feup/lara/vitest/setupWeaver.ts"; import java from "java"; -export default async function (...args: Parameters) { - applyJavaOptions(); - await laraGlobalSetup(...args); -} +applyJavaOptions(); +setupWeaver(weaverConfig); function applyJavaOptions(): void { const rawJavaOptions = process.env.CLAVA_JS_JAVA_OPTIONS; @@ -13,9 +12,7 @@ function applyJavaOptions(): void { return; } - const javaOptions = parseJavaOptions(rawJavaOptions); - - for (const javaOption of javaOptions) { + for (const javaOption of parseJavaOptions(rawJavaOptions)) { if (!java.options.includes(javaOption)) { java.options.push(javaOption); } @@ -24,7 +21,7 @@ function applyJavaOptions(): void { function parseJavaOptions(rawJavaOptions: string): string[] { try { - const parsed = JSON.parse(rawJavaOptions); + const parsed: unknown = JSON.parse(rawJavaOptions); if ( Array.isArray(parsed) && From 781d89b7a57c2b0daaba9211b4f9744ca753000d Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 17:50:19 +0100 Subject: [PATCH 82/99] restore clava TypeScript dependency Keep TypeScript explicitly declared for Clava-JS type checking. --- Clava-JS/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 5a9d99100b..048be4580f 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -74,6 +74,7 @@ "@types/node": "^25.0.0", "@types/yargs": "^17.0.35", "eslint": "^9.39.2", + "typescript": "^7.0.2", "vitest": "^4.1.10" } } From aaebc42dc139c8a575eef21e45c583b783da4c10 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 18:02:33 +0100 Subject: [PATCH 83/99] set integration test timeouts Allow the known long-running InlineNasLu and Inliner tests up to ten seconds without changing the suite-wide timeout. --- Clava-JS/api/LegacyIntegrationTests - C.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Clava-JS/api/LegacyIntegrationTests - C.test.ts b/Clava-JS/api/LegacyIntegrationTests - C.test.ts index dbd27bf31e..f131c405d2 100644 --- a/Clava-JS/api/LegacyIntegrationTests - C.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - C.test.ts @@ -101,7 +101,7 @@ describe("CTest", () => { await newTester() .checkExpectedOutput(false) .test("InlineNasLu.js", "inline_nas_lu.c"); - }); + }, 10_000); it("InlineNasFt", async () => { await newTester() @@ -340,7 +340,7 @@ describe("CApiTest", () => { } await tester.test("InlinerTest.js", "inliner.c"); - }); + }, 10_000); it("StatementDecomposer", async () => { await newTester().test( From aeb5abf2fe6fcad15994c421e59f9a76a1818afd Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 18:03:34 +0100 Subject: [PATCH 84/99] extend inliner test timeout Give the long-running Inliner integration test a fifteen-second timeout while retaining InlineNasLu at ten seconds. --- Clava-JS/api/LegacyIntegrationTests - C.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clava-JS/api/LegacyIntegrationTests - C.test.ts b/Clava-JS/api/LegacyIntegrationTests - C.test.ts index f131c405d2..9421c6d19d 100644 --- a/Clava-JS/api/LegacyIntegrationTests - C.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - C.test.ts @@ -340,7 +340,7 @@ describe("CApiTest", () => { } await tester.test("InlinerTest.js", "inliner.c"); - }, 10_000); + }, 15_000); it("StatementDecomposer", async () => { await newTester().test( From 9abce8dce7eba7cdcfb8e502162cc258c00c59bb Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 18:30:43 +0100 Subject: [PATCH 85/99] build: migrate Clava-JS checks to Oxc Adopt the shared Oxlint configuration and Oxfmt scripts, retaining lint exclusions with Oxlint directives. --- Clava-JS/api/LegacyIntegrationTests - C.test.ts | 2 +- Clava-JS/api/LegacyIntegrationTests - CXX.test.ts | 2 +- Clava-JS/api/LegacyIntegrationTests - Issues.test.ts | 2 +- Clava-JS/api/clava/ClavaJavaTypes.ts | 6 +++--- Clava-JS/eslint.config.ts | 3 --- Clava-JS/oxlint.config.ts | 6 ++++++ Clava-JS/package.json | 12 ++++++------ Clava-JS/prettier.config.ts | 3 --- 8 files changed, 18 insertions(+), 18 deletions(-) delete mode 100644 Clava-JS/eslint.config.ts create mode 100644 Clava-JS/oxlint.config.ts delete mode 100644 Clava-JS/prettier.config.ts diff --git a/Clava-JS/api/LegacyIntegrationTests - C.test.ts b/Clava-JS/api/LegacyIntegrationTests - C.test.ts index 9421c6d19d..db3f36a05a 100644 --- a/Clava-JS/api/LegacyIntegrationTests - C.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - C.test.ts @@ -6,7 +6,7 @@ import path from "path"; const isWindows = process.platform === "win32"; const isMacOS = process.platform === "darwin"; -/* eslint-disable vitest/expect-expect */ +/* oxlint-disable vitest/expect-expect */ describe("CTest", () => { function newTester() { return new ClavaLegacyTester( diff --git a/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts b/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts index 54d143a814..1ffcb5fbf7 100644 --- a/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - CXX.test.ts @@ -6,7 +6,7 @@ import path from "path"; const isWindows = process.platform === "win32"; const isMacOS = process.platform === "darwin"; -/* eslint-disable vitest/expect-expect */ +/* oxlint-disable vitest/expect-expect */ describe("CxxTest", () => { function newTester() { return new ClavaLegacyTester( diff --git a/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts b/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts index 11b6447f7f..c4782a44ec 100644 --- a/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts +++ b/Clava-JS/api/LegacyIntegrationTests - Issues.test.ts @@ -2,7 +2,7 @@ import { ClavaLegacyTester } from "../vitest/ClavaLegacyTester.ts"; import ClavaJavaTypes from "./clava/ClavaJavaTypes.ts"; import path from "path"; -/* eslint-disable vitest/expect-expect */ +/* oxlint-disable vitest/expect-expect */ describe("IssuesTest", () => { function newTester() { return new ClavaLegacyTester( diff --git a/Clava-JS/api/clava/ClavaJavaTypes.ts b/Clava-JS/api/clava/ClavaJavaTypes.ts index 3f3d47692d..e617cf3b43 100644 --- a/Clava-JS/api/clava/ClavaJavaTypes.ts +++ b/Clava-JS/api/clava/ClavaJavaTypes.ts @@ -2,9 +2,9 @@ import JavaTypes, { type JavaClasses, } from "@specs-feup/lara/api/lara/util/JavaTypes.ts"; -// eslint-disable-next-line @typescript-eslint/no-namespace +// oxlint-disable-next-line typescript/no-namespace export namespace ClavaJavaClasses { - /* eslint-disable @typescript-eslint/no-empty-object-type */ + /* oxlint-disable typescript/no-empty-object-type */ export interface ClavaNodes extends JavaClasses.JavaClass {} export interface ClavaNode extends JavaClasses.JavaClass {} export interface CxxJoinpoints extends JavaClasses.JavaClass {} @@ -19,7 +19,7 @@ export namespace ClavaJavaClasses { export interface CxxWeaverOption extends JavaClasses.JavaClass {} export interface ClavaOptions extends JavaClasses.JavaClass {} export interface CodeParser extends JavaClasses.JavaClass {} - /* eslint-enable @typescript-eslint/no-empty-object-type */ + /* oxlint-enable typescript/no-empty-object-type */ } /** diff --git a/Clava-JS/eslint.config.ts b/Clava-JS/eslint.config.ts deleted file mode 100644 index a8f4237a32..0000000000 --- a/Clava-JS/eslint.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createEslintConfig } from "@specs-feup/lara/eslint.config.ts"; - -export default createEslintConfig(import.meta.dirname); diff --git a/Clava-JS/oxlint.config.ts b/Clava-JS/oxlint.config.ts new file mode 100644 index 0000000000..2f6883cbde --- /dev/null +++ b/Clava-JS/oxlint.config.ts @@ -0,0 +1,6 @@ +import config from "@specs-feup/lara/oxlint.config.ts"; +import { defineConfig } from "oxlint"; + +export default defineConfig({ + extends: [config], +}); diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 048be4580f..7c4630b0bd 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -23,8 +23,7 @@ "code", "java-binaries", ".gitignore", - "eslint.config.ts", - "prettier.config.ts", + "oxlint.config.ts", "vitest.config.ts", "LICENSE", "package.json", @@ -36,9 +35,9 @@ "run": "node ./code/index.ts", "build": "tsc", "build:watch": "npm run build -- --watch", - "lint": "eslint --flag unstable_native_nodejs_ts_config .", - "format": "prettier --write .", - "format:check": "prettier --check .", + "lint": "oxlint .", + "format": "oxfmt .", + "format:check": "oxfmt --check .", "test": "vitest run", "test:api": "npm run test -- api", "test:code": "npm run test -- code", @@ -73,7 +72,8 @@ "@types/java": "^0.9.6", "@types/node": "^25.0.0", "@types/yargs": "^17.0.35", - "eslint": "^9.39.2", + "oxfmt": "^0.59.0", + "oxlint": "^1.74.0", "typescript": "^7.0.2", "vitest": "^4.1.10" } diff --git a/Clava-JS/prettier.config.ts b/Clava-JS/prettier.config.ts deleted file mode 100644 index f4c5fe7217..0000000000 --- a/Clava-JS/prettier.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import config from "@specs-feup/lara/prettier.config.ts"; - -export default config; From 18cfd4441e1a92624b868afedda1236c72b7db49 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 18:55:42 +0100 Subject: [PATCH 86/99] Declare Clava development tool dependencies --- Clava-JS/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 7c4630b0bd..1fcb5e31c9 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -72,6 +72,8 @@ "@types/java": "^0.9.6", "@types/node": "^25.0.0", "@types/yargs": "^17.0.35", + "@vitest/coverage-v8": "^4.1.10", + "eslint-plugin-tsdoc": "^0.5.0", "oxfmt": "^0.59.0", "oxlint": "^1.74.0", "typescript": "^7.0.2", From 62301f814471cfd618196219870193ca1235d704 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 14 Jul 2026 21:47:53 +0100 Subject: [PATCH 87/99] test: share Lara Vitest lifecycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run API and code tests in one Vitest configuration backed by Lara’s shared Weaver lifecycle helper, while preserving package test filters. --- Clava-JS/api/vitest.config.ts | 19 --------------- Clava-JS/code/vitest.config.ts | 18 -------------- Clava-JS/vitest.config.ts | 15 ++++-------- Clava-JS/vitest/setupClavaWeaver.ts | 37 ----------------------------- 4 files changed, 4 insertions(+), 85 deletions(-) delete mode 100644 Clava-JS/api/vitest.config.ts delete mode 100644 Clava-JS/code/vitest.config.ts delete mode 100644 Clava-JS/vitest/setupClavaWeaver.ts diff --git a/Clava-JS/api/vitest.config.ts b/Clava-JS/api/vitest.config.ts deleted file mode 100644 index 0e97e83fab..0000000000 --- a/Clava-JS/api/vitest.config.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { defineProject } from "vitest/config"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const root = path.dirname(fileURLToPath(import.meta.url)); - -export default defineProject({ - root, - test: { - experimental: { - viteModuleRunner: false, - }, - fileParallelism: false, - globals: true, - name: "api", - pool: "forks", - setupFiles: ["../vitest/setupClavaWeaver.ts"], - }, -}); diff --git a/Clava-JS/code/vitest.config.ts b/Clava-JS/code/vitest.config.ts deleted file mode 100644 index c1ba777db6..0000000000 --- a/Clava-JS/code/vitest.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineProject } from "vitest/config"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const root = path.dirname(fileURLToPath(import.meta.url)); - -export default defineProject({ - root, - test: { - experimental: { - viteModuleRunner: false, - }, - fileParallelism: false, - globals: true, - name: "code", - pool: "forks", - }, -}); diff --git a/Clava-JS/vitest.config.ts b/Clava-JS/vitest.config.ts index 3b9aced4d0..dd321932fb 100644 --- a/Clava-JS/vitest.config.ts +++ b/Clava-JS/vitest.config.ts @@ -1,13 +1,6 @@ -import { defineConfig } from "vitest/config"; +import { createWeaverVitestConfig } from "@specs-feup/lara/vitest/weaverVitestConfig.ts"; +import { weaverConfig } from "./code/WeaverConfiguration.ts"; -export default defineConfig({ - test: { - coverage: { - include: ["**/*[^.d].(t|j)s"], - provider: "v8", - reporter: ["text", "lcov"], - }, - maxWorkers: 1, - projects: ["api/vitest.config.ts", "code/vitest.config.ts"], - }, +export default createWeaverVitestConfig(weaverConfig, { + javaOptionsEnvironmentVariable: "CLAVA_JS_JAVA_OPTIONS", }); diff --git a/Clava-JS/vitest/setupClavaWeaver.ts b/Clava-JS/vitest/setupClavaWeaver.ts deleted file mode 100644 index 7d2a3e6398..0000000000 --- a/Clava-JS/vitest/setupClavaWeaver.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { weaverConfig } from "../code/WeaverConfiguration.ts"; -import { setupWeaver } from "@specs-feup/lara/vitest/setupWeaver.ts"; -import java from "java"; - -applyJavaOptions(); -setupWeaver(weaverConfig); - -function applyJavaOptions(): void { - const rawJavaOptions = process.env.CLAVA_JS_JAVA_OPTIONS; - - if (rawJavaOptions === undefined || rawJavaOptions.trim() === "") { - return; - } - - for (const javaOption of parseJavaOptions(rawJavaOptions)) { - if (!java.options.includes(javaOption)) { - java.options.push(javaOption); - } - } -} - -function parseJavaOptions(rawJavaOptions: string): string[] { - try { - const parsed: unknown = JSON.parse(rawJavaOptions); - - if ( - Array.isArray(parsed) && - parsed.every((javaOption) => typeof javaOption === "string") - ) { - return parsed; - } - } catch { - // Fall back to whitespace splitting for ad-hoc local use. - } - - return rawJavaOptions.split(/\s+/).filter((javaOption) => javaOption !== ""); -} From f37fb126884d3fbecac5e8a077f6eab72f414511 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 15 Jul 2026 02:26:22 +0100 Subject: [PATCH 88/99] fix: sync Java binaries after installDist Materialize ClavaWeaver's distribution for every installDist, make aggregate JS builds depend on the synchronized output, and validate npm package contents through Lara's shared helper. --- Clava-JS/README.md | 4 +++- Clava-JS/package.json | 1 + ClavaWeaver/build.gradle | 33 +++++++++++++++++++++++++++++++++ build.gradle | 35 ++++------------------------------- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/Clava-JS/README.md b/Clava-JS/README.md index 56da27ab09..156f76e401 100644 --- a/Clava-JS/README.md +++ b/Clava-JS/README.md @@ -38,7 +38,9 @@ npm install gradle -p clava clavaJsBuild ``` -The `clavaJsBuild` task builds Lara-JS, runs `ClavaWeaver:installDist`, and checks that `./clava/Clava-JS/java-binaries` is the symlink to `../ClavaWeaver/build/install/ClavaWeaver`. Keep this symlink instead of copying the JARs, otherwise Clava-JS can silently run against stale Java binaries. +The `clavaJsBuild` task builds Lara-JS, runs `ClavaWeaver:installDist`, and synchronizes its complete distribution into `./clava/Clava-JS/java-binaries`. The generated directory is refreshed automatically, including when `installDist` is invoked directly, so do not copy or edit its contents manually. + +Clava-JS tests invoked through the combined Gradle build always run after this synchronization. Local `npm pack` and `npm publish` also validate that `java-binaries` is a real, populated directory before creating a package. To run the Java and Clava-JS tests from one place and generate the merged Java coverage report: diff --git a/Clava-JS/package.json b/Clava-JS/package.json index 1fcb5e31c9..f0a50ffae2 100644 --- a/Clava-JS/package.json +++ b/Clava-JS/package.json @@ -38,6 +38,7 @@ "lint": "oxlint .", "format": "oxfmt .", "format:check": "oxfmt --check .", + "prepack": "node --input-type=module --eval \"import { validateJavaBinaries } from '@specs-feup/lara/scripts/validateJavaBinaries.js'; validateJavaBinaries();\"", "test": "vitest run", "test:api": "npm run test -- api", "test:code": "npm run test -- code", diff --git a/ClavaWeaver/build.gradle b/ClavaWeaver/build.gradle index 1ff759a7e0..91128a6e1c 100644 --- a/ClavaWeaver/build.gradle +++ b/ClavaWeaver/build.gradle @@ -73,3 +73,36 @@ tasks.register('generateWeaver', JavaExec) { } compileJava.dependsOn generateWeaver + +def clavaWeaverInstallDir = layout.buildDirectory.dir('install/ClavaWeaver') +def clavaJsJavaBinaries = layout.projectDirectory.dir('../Clava-JS/java-binaries') + +def syncClavaJsJavaBinaries = tasks.register('syncClavaJsJavaBinaries', Sync) { + group = 'distribution' + description = 'Synchronizes the ClavaWeaver installDist output used by Clava-JS.' + dependsOn tasks.named('installDist') + from clavaWeaverInstallDir + into clavaJsJavaBinaries + + // The destination is also used by npm packaging and must not retain files + // that are no longer part of installDist. + outputs.upToDateWhen { false } + + doFirst { + def installLib = clavaWeaverInstallDir.get().dir('lib').asFile + if (!installLib.isDirectory()) { + throw new GradleException("ClavaWeaver installDist output is missing: ${installLib}") + } + + def destination = clavaJsJavaBinaries.asFile.toPath() + if (java.nio.file.Files.isSymbolicLink(destination)) { + java.nio.file.Files.delete(destination) + } else if (java.nio.file.Files.exists(destination) && !java.nio.file.Files.isDirectory(destination)) { + throw new GradleException("Clava-JS java-binaries is not a directory: ${destination}") + } + } +} + +tasks.named('installDist') { + finalizedBy syncClavaJsJavaBinaries +} diff --git a/build.gradle b/build.gradle index 54282f8153..e1da408aa6 100644 --- a/build.gradle +++ b/build.gradle @@ -28,9 +28,6 @@ dependencies { } def workspaceRoot = layout.projectDirectory.dir('..') -def clavaJsDir = layout.projectDirectory.dir('Clava-JS') -def clavaWeaverInstallDir = layout.projectDirectory.dir('ClavaWeaver/build/install/ClavaWeaver') -def clavaJsJavaBinaries = clavaJsDir.file('java-binaries') def clavaJsJacocoExec = layout.buildDirectory.file('jacoco/clava-js.exec') def clavaJavaBuilds = [ @@ -66,32 +63,8 @@ tasks.register('clavaJavaTest') { tasks.register('installClavaWeaver') { group = 'distribution' - description = 'Installs the ClavaWeaver distribution used by Clava-JS.' - dependsOn gradle.includedBuild('ClavaWeaver').task(':installDist') -} - -tasks.register('validateClavaJsJavaBinariesSymlink') { - group = 'verification' - description = 'Checks that Clava-JS/java-binaries points at the current ClavaWeaver installDist output.' - dependsOn 'installClavaWeaver' - - doLast { - def link = clavaJsJavaBinaries.asFile.toPath() - def expectedTarget = java.nio.file.Paths.get('../ClavaWeaver/build/install/ClavaWeaver') - - if (!java.nio.file.Files.isSymbolicLink(link)) { - throw new GradleException("${clavaJsJavaBinaries.asFile} must be a symbolic link to ${expectedTarget}") - } - - def actualTarget = java.nio.file.Files.readSymbolicLink(link) - if (actualTarget != expectedTarget) { - throw new GradleException("${clavaJsJavaBinaries.asFile} points to ${actualTarget}, expected ${expectedTarget}") - } - - if (!clavaWeaverInstallDir.file('lib').asFile.isDirectory()) { - throw new GradleException("ClavaWeaver installDist output is missing: ${clavaWeaverInstallDir.file('lib').asFile}") - } - } + description = 'Installs ClavaWeaver and synchronizes the distribution used by Clava-JS.' + dependsOn gradle.includedBuild('ClavaWeaver').task(':syncClavaJsJavaBinaries') } tasks.register('npmInstall', Exec) { @@ -114,8 +87,8 @@ tasks.register('laraJsBuild', Exec) { tasks.register('clavaJsBuild', Exec) { group = 'build' - description = 'Builds Clava-JS after refreshing the Java distribution symlink target.' - dependsOn 'validateClavaJsJavaBinariesSymlink', 'laraJsBuild' + description = 'Builds Clava-JS after synchronizing its Java distribution.' + dependsOn 'installClavaWeaver', 'laraJsBuild' workingDir = workspaceRoot.asFile commandLine 'npm', 'run', 'build', '-w', 'clava/Clava-JS' } From 70ac726bd8fb43f1dbdb54bbef3933d1a5816568 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 23 Jul 2026 14:58:47 +0100 Subject: [PATCH 89/99] ci: resolve dependencies through branch ancestry --- .github/scripts/resolve-dependency-refs.sh | 129 +++++++++++++++++++++ .github/workflows/copilot-setup-steps.yml | 50 ++------ .github/workflows/nightly.yml | 50 ++------ 3 files changed, 143 insertions(+), 86 deletions(-) create mode 100644 .github/scripts/resolve-dependency-refs.sh diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh new file mode 100644 index 0000000000..efb4229608 --- /dev/null +++ b/.github/scripts/resolve-dependency-refs.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if (( $# < 3 || $# % 2 == 0 )); then + echo "Usage: $0 SOURCE_DIRECTORY OUTPUT_PREFIX DEPENDENCY_REPOSITORY [...]" >&2 + exit 2 +fi + +source_directory=$1 +shift + +source_branch=${SOURCE_BRANCH:?SOURCE_BRANCH must name the branch being tested} +integration_branch=${INTEGRATION_BRANCH:-staging} +root_branch=${ROOT_BRANCH:-master} + +declare -a candidates=() +declare -A seen_candidates=() + +add_candidate() { + local branch=$1 + + if [[ -n $branch && -z ${seen_candidates[$branch]+yes} ]]; then + candidates+=("$branch") + seen_candidates[$branch]=1 + fi +} + +source_ref=HEAD +if git -C "$source_directory" show-ref --verify --quiet "refs/remotes/origin/${source_branch}"; then + source_ref="refs/remotes/origin/${source_branch}" +elif git -C "$source_directory" show-ref --verify --quiet "refs/heads/${source_branch}"; then + source_ref="refs/heads/${source_branch}" +fi + +add_candidate "$source_branch" + +# Branch creation has no durable metadata in Git. Branch tips that still occur +# on the source branch's first-parent history are the reliable part of the +# stack. Sort them by their distance from the source tip. +declare -A first_parent_distance=() +distance=0 +root_ref= +if git -C "$source_directory" show-ref --verify --quiet "refs/remotes/origin/${root_branch}"; then + root_ref="refs/remotes/origin/${root_branch}" +elif git -C "$source_directory" show-ref --verify --quiet "refs/heads/${root_branch}"; then + root_ref="refs/heads/${root_branch}" +fi + +while IFS= read -r commit; do + first_parent_distance[$commit]=$distance + ((distance += 1)) + + # The root branch is allowed to advance independently. Stop at the first + # first-parent commit contained in its current history, not at its tip. + if [[ -n $root_ref ]] && + git -C "$source_directory" merge-base --is-ancestor "$commit" "$root_ref"; then + break + fi +done < <(git -C "$source_directory" rev-list --first-parent "$source_ref") + +ancestry_candidates=$(mktemp) +trap 'rm -f "$ancestry_candidates"' EXIT + +while IFS=$'\t' read -r ref_name object_name; do + branch=${ref_name#origin/} + + [[ $ref_name == origin || $branch == HEAD ]] && continue + [[ $branch == "$source_branch" ]] && continue + [[ $branch == "$integration_branch" ]] && continue + [[ $branch == "$root_branch" ]] && continue + [[ -n ${first_parent_distance[$object_name]+yes} ]] || continue + + printf '%s\t%s\n' "${first_parent_distance[$object_name]}" "$branch" \ + >> "$ancestry_candidates" +done < <( + git -C "$source_directory" for-each-ref \ + --format='%(refname:short)%09%(objectname)' \ + refs/remotes/origin +) + +while IFS=$'\t' read -r _ branch; do + add_candidate "$branch" +done < <(sort -k1,1n -k2,2 -u "$ancestry_candidates") + +if [[ $source_branch != "$root_branch" ]]; then + add_candidate "$integration_branch" +fi +add_candidate "$root_branch" + +printf -v candidate_chain '%s -> ' "${candidates[@]}" +candidate_chain=${candidate_chain% -> } +echo "Dependency branch candidates: ${candidate_chain}" + +while (( $# )); do + prefix=$1 + repository=$2 + shift 2 + + url="https://github.com/${repository}.git" + default_branch=$( + git ls-remote --symref "$url" HEAD | + awk '/^ref:/ {sub("refs/heads/", "", $2); print $2; exit}' + ) + + declare -A dependency_branches=() + while IFS=$'\t' read -r _ ref; do + dependency_branches[${ref#refs/heads/}]=1 + done < <(git ls-remote --heads "$url") + + selected= + for branch in "${candidates[@]}"; do + if [[ -n ${dependency_branches[$branch]+yes} ]]; then + selected=$branch + break + fi + done + + if [[ -z $selected ]]; then + echo "None of the candidate branches exists in ${repository}" >&2 + exit 1 + fi + + echo "Using '${selected}' for ${repository}" + { + echo "${prefix}_ref=${selected}" + echo "${prefix}_default=${default_branch}" + } >> "${GITHUB_OUTPUT:?GITHUB_OUTPUT is not set}" +done diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index d6a802310e..89f28b84e7 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -46,59 +46,23 @@ jobs: uses: actions/checkout@v6 with: path: clava + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Determine repository refs id: repo-refs shell: bash env: - BRANCH_NAME: ${{ env.BRANCH_NAME }} - BASE_BRANCH: ${{ github.base_ref }} + SOURCE_BRANCH: ${{ env.BRANCH_NAME }} run: | - set -euo pipefail - - # For each dependency repository, determine which branch to checkout. - # Priority order: - # 1. A branch with the same name as the current branch - # 2. If this is a PR, the target branch (base_ref) - # 3. The default branch of the repository - - determine_ref() { - local prefix=$1 - local repo=$2 - local url="https://github.com/${repo}.git" - - # Get the default branch - local default_branch - default_branch=$(git ls-remote --symref "$url" HEAD | awk '/^ref:/ {print $2}' | sed 's@refs/heads/@@') - echo "${prefix}_default=${default_branch}" >> "$GITHUB_OUTPUT" - echo "Default branch for ${repo} is '${default_branch}'" - - local ref_to_use="" - - # Priority 1: Same branch name - if [ -n "$(git ls-remote --heads "$url" "refs/heads/${BRANCH_NAME}")" ]; then - ref_to_use="${BRANCH_NAME}" - echo "Using matching branch '${BRANCH_NAME}' in ${repo}" - # Priority 2: PR target branch (if this is a PR) - elif [ -n "${BASE_BRANCH}" ] && [ -n "$(git ls-remote --heads "$url" "refs/heads/${BASE_BRANCH}")" ]; then - ref_to_use="${BASE_BRANCH}" - echo "Using PR target branch '${BASE_BRANCH}' in ${repo}" - # Priority 3: Default branch - else - ref_to_use="${default_branch}" - echo "Using default branch '${default_branch}' for ${repo}" - fi - - echo "${prefix}_ref=${ref_to_use}" >> "$GITHUB_OUTPUT" - } - - determine_ref "lara" "specs-feup/lara-framework" - determine_ref "specs" "specs-feup/specs-java-libs" + bash clava/.github/scripts/resolve-dependency-refs.sh \ + clava \ + lara specs-feup/lara-framework \ + specs specs-feup/specs-java-libs - name: Echo checks run: | echo "Weaver branch: ${{ env.BRANCH_NAME }}" - echo "PR target branch (if any): ${{ github.base_ref }}" echo "Lara framework ref: ${{ steps.repo-refs.outputs.lara_ref }}" echo "Lara framework default: ${{ steps.repo-refs.outputs.lara_default }}" echo "Specs-java-libs ref: ${{ steps.repo-refs.outputs.specs_ref }}" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 98915e2b03..9849ececc3 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -46,59 +46,23 @@ jobs: uses: actions/checkout@v6 with: path: clava + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Determine repository refs id: repo-refs shell: bash env: - BRANCH_NAME: ${{ env.BRANCH_NAME }} - BASE_BRANCH: ${{ github.base_ref }} + SOURCE_BRANCH: ${{ env.BRANCH_NAME }} run: | - set -euo pipefail - - # For each dependency repository, determine which branch to checkout. - # Priority order: - # 1. A branch with the same name as the current branch - # 2. If this is a PR, the target branch (base_ref) - # 3. The default branch of the repository - - determine_ref() { - local prefix=$1 - local repo=$2 - local url="https://github.com/${repo}.git" - - # Get the default branch - local default_branch - default_branch=$(git ls-remote --symref "$url" HEAD | awk '/^ref:/ {print $2}' | sed 's@refs/heads/@@') - echo "${prefix}_default=${default_branch}" >> "$GITHUB_OUTPUT" - echo "Default branch for ${repo} is '${default_branch}'" - - local ref_to_use="" - - # Priority 1: Same branch name - if [ -n "$(git ls-remote --heads "$url" "refs/heads/${BRANCH_NAME}")" ]; then - ref_to_use="${BRANCH_NAME}" - echo "Using matching branch '${BRANCH_NAME}' in ${repo}" - # Priority 2: PR target branch (if this is a PR) - elif [ -n "${BASE_BRANCH}" ] && [ -n "$(git ls-remote --heads "$url" "refs/heads/${BASE_BRANCH}")" ]; then - ref_to_use="${BASE_BRANCH}" - echo "Using PR target branch '${BASE_BRANCH}' in ${repo}" - # Priority 3: Default branch - else - ref_to_use="${default_branch}" - echo "Using default branch '${default_branch}' for ${repo}" - fi - - echo "${prefix}_ref=${ref_to_use}" >> "$GITHUB_OUTPUT" - } - - determine_ref "lara" "specs-feup/lara-framework" - determine_ref "specs" "specs-feup/specs-java-libs" + bash clava/.github/scripts/resolve-dependency-refs.sh \ + clava \ + lara specs-feup/lara-framework \ + specs specs-feup/specs-java-libs - name: Echo checks run: | echo "Weaver branch: ${{ env.BRANCH_NAME }}" - echo "PR target branch (if any): ${{ github.base_ref }}" echo "Lara framework ref: ${{ steps.repo-refs.outputs.lara_ref }}" echo "Lara framework default: ${{ steps.repo-refs.outputs.lara_default }}" echo "Specs-java-libs ref: ${{ steps.repo-refs.outputs.specs_ref }}" From 7f29b390245ffae1b0e1e77f24a8754155b79ddd Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 23 Jul 2026 15:19:25 +0100 Subject: [PATCH 90/99] test: harden dependency branch resolution --- .github/scripts/resolve-dependency-refs.sh | 24 ++++- .../scripts/test-resolve-dependency-refs.sh | 100 ++++++++++++++++++ .github/workflows/copilot-setup-steps.yml | 3 + .github/workflows/nightly.yml | 3 + 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/test-resolve-dependency-refs.sh diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh index efb4229608..5e9cd53972 100644 --- a/.github/scripts/resolve-dependency-refs.sh +++ b/.github/scripts/resolve-dependency-refs.sh @@ -32,6 +32,7 @@ if git -C "$source_directory" show-ref --verify --quiet "refs/remotes/origin/${s elif git -C "$source_directory" show-ref --verify --quiet "refs/heads/${source_branch}"; then source_ref="refs/heads/${source_branch}" fi +git -C "$source_directory" rev-parse --verify "${source_ref}^{commit}" >/dev/null add_candidate "$source_branch" @@ -47,6 +48,12 @@ elif git -C "$source_directory" show-ref --verify --quiet "refs/heads/${root_bra root_ref="refs/heads/${root_branch}" fi +if [[ -z $root_ref ]]; then + echo "Cannot find the root branch '${root_branch}' in ${source_directory}" >&2 + exit 1 +fi + +reached_root=false while IFS= read -r commit; do first_parent_distance[$commit]=$distance ((distance += 1)) @@ -55,10 +62,16 @@ while IFS= read -r commit; do # first-parent commit contained in its current history, not at its tip. if [[ -n $root_ref ]] && git -C "$source_directory" merge-base --is-ancestor "$commit" "$root_ref"; then + reached_root=true break fi done < <(git -C "$source_directory" rev-list --first-parent "$source_ref") +if [[ $reached_root != true ]]; then + echo "The first-parent history of '${source_branch}' does not reach '${root_branch}'" >&2 + exit 1 +fi + ancestry_candidates=$(mktemp) trap 'rm -f "$ancestry_candidates"' EXIT @@ -97,11 +110,20 @@ while (( $# )); do repository=$2 shift 2 - url="https://github.com/${repository}.git" + if [[ $repository == *://* || $repository == /* ]]; then + url=$repository + else + url="https://github.com/${repository}.git" + fi + default_branch=$( git ls-remote --symref "$url" HEAD | awk '/^ref:/ {sub("refs/heads/", "", $2); print $2; exit}' ) + if [[ -z $default_branch ]]; then + echo "Cannot determine the default branch for ${repository}" >&2 + exit 1 + fi declare -A dependency_branches=() while IFS=$'\t' read -r _ ref; do diff --git a/.github/scripts/test-resolve-dependency-refs.sh b/.github/scripts/test-resolve-dependency-refs.sh new file mode 100644 index 0000000000..d0670ddd76 --- /dev/null +++ b/.github/scripts/test-resolve-dependency-refs.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +set -euo pipefail + +script_directory=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +resolver=${1:-"${script_directory}/resolve-dependency-refs.sh"} +test_directory=$(mktemp -d) +source_repository="${test_directory}/source" + +git init --quiet --initial-branch=master "$source_repository" +git -C "$source_repository" config user.name "CI Resolver Test" +git -C "$source_repository" config user.email "ci-resolver@example.invalid" + +commit() { + local message=$1 + git -C "$source_repository" commit --quiet --allow-empty -m "$message" +} + +commit "master base" + +git -C "$source_repository" switch --quiet -c staging +commit "staging base" + +git -C "$source_repository" switch --quiet -c lmsousa +commit "branch A" +lmsousa_tip=$(git -C "$source_repository" rev-parse HEAD) + +git -C "$source_repository" switch --quiet -c vitest +commit "branch B" +vitest_tip=$(git -C "$source_repository" rev-parse HEAD) + +git -C "$source_repository" switch --quiet -c java-deprecation +commit "branch C" +java_deprecation_tip=$(git -C "$source_repository" rev-parse HEAD) + +git -C "$source_repository" switch --quiet -c workflow-fix +commit "current branch" +workflow_fix_tip=$(git -C "$source_repository" rev-parse HEAD) + +# Both long-lived branches advance after the stack was created. +git -C "$source_repository" switch --quiet staging +commit "advanced staging" +staging_tip=$(git -C "$source_repository" rev-parse HEAD) + +git -C "$source_repository" switch --quiet master +commit "advanced master" +master_tip=$(git -C "$source_repository" rev-parse HEAD) + +for branch in workflow-fix java-deprecation vitest lmsousa staging master; do + branch_variable=${branch//-/_}_tip + git -C "$source_repository" update-ref \ + "refs/remotes/origin/${branch}" "${!branch_variable}" +done + +make_dependency() { + local name=$1 + shift + local repository="${test_directory}/${name}.git" + + git clone --quiet --bare "$source_repository" "$repository" + while IFS= read -r ref; do + git -C "$repository" update-ref -d "$ref" + done < <(git -C "$repository" for-each-ref --format='%(refname)' refs/heads) + + git -C "$repository" update-ref refs/heads/master "$master_tip" + for branch in "$@"; do + branch_variable=${branch//-/_}_tip + git -C "$repository" update-ref "refs/heads/${branch}" "${!branch_variable}" + done + git -C "$repository" symbolic-ref HEAD refs/heads/master + echo "$repository" +} + +current_dependency=$(make_dependency current workflow-fix lmsousa) +middle_dependency=$(make_dependency middle java-deprecation lmsousa) +older_dependency=$(make_dependency older vitest lmsousa) +integration_dependency=$(make_dependency integration staging) +root_dependency=$(make_dependency root) + +output_file="${test_directory}/github-output" +log_file="${test_directory}/resolver.log" +SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="$output_file" \ + bash "$resolver" "$source_repository" \ + current "$current_dependency" \ + middle "$middle_dependency" \ + older "$older_dependency" \ + integration "$integration_dependency" \ + root "$root_dependency" | + tee "$log_file" + +grep -Fqx \ + "Dependency branch candidates: workflow-fix -> java-deprecation -> vitest -> lmsousa -> staging -> master" \ + "$log_file" +grep -Fqx "current_ref=workflow-fix" "$output_file" +grep -Fqx "middle_ref=java-deprecation" "$output_file" +grep -Fqx "older_ref=vitest" "$output_file" +grep -Fqx "integration_ref=staging" "$output_file" +grep -Fqx "root_ref=master" "$output_file" + +echo "All dependency ref resolver tests passed" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 89f28b84e7..bd7058737f 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -49,6 +49,9 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Test dependency ref resolver + run: bash clava/.github/scripts/test-resolve-dependency-refs.sh + - name: Determine repository refs id: repo-refs shell: bash diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 9849ececc3..63632034c4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -49,6 +49,9 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Test dependency ref resolver + run: bash clava/.github/scripts/test-resolve-dependency-refs.sh + - name: Determine repository refs id: repo-refs shell: bash From 7a3295b5bbb88525cdde49a99288eebda741682d Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 23 Jul 2026 15:26:14 +0100 Subject: [PATCH 91/99] fix: resolve stack from workflow event commit --- .github/scripts/resolve-dependency-refs.sh | 5 ----- .../scripts/test-resolve-dependency-refs.sh | 18 ++++++++++++++++++ .github/workflows/copilot-setup-steps.yml | 4 ++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh index 5e9cd53972..e48aaa7b5d 100644 --- a/.github/scripts/resolve-dependency-refs.sh +++ b/.github/scripts/resolve-dependency-refs.sh @@ -27,11 +27,6 @@ add_candidate() { } source_ref=HEAD -if git -C "$source_directory" show-ref --verify --quiet "refs/remotes/origin/${source_branch}"; then - source_ref="refs/remotes/origin/${source_branch}" -elif git -C "$source_directory" show-ref --verify --quiet "refs/heads/${source_branch}"; then - source_ref="refs/heads/${source_branch}" -fi git -C "$source_directory" rev-parse --verify "${source_ref}^{commit}" >/dev/null add_candidate "$source_branch" diff --git a/.github/scripts/test-resolve-dependency-refs.sh b/.github/scripts/test-resolve-dependency-refs.sh index d0670ddd76..fd9b333452 100644 --- a/.github/scripts/test-resolve-dependency-refs.sh +++ b/.github/scripts/test-resolve-dependency-refs.sh @@ -46,11 +46,25 @@ git -C "$source_repository" switch --quiet master commit "advanced master" master_tip=$(git -C "$source_repository" rev-parse HEAD) +# Simulate a later push after the workflow event was created. Resolution must +# use the detached event commit, never the newer remote source branch tip. +git -C "$source_repository" switch --quiet workflow-fix +commit "post-event parent branch" +later_parent_tip=$(git -C "$source_repository" rev-parse HEAD) +git -C "$source_repository" branch later-parent +commit "post-event source update" +post_event_source_tip=$(git -C "$source_repository" rev-parse HEAD) +git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" + for branch in workflow-fix java-deprecation vitest lmsousa staging master; do branch_variable=${branch//-/_}_tip git -C "$source_repository" update-ref \ "refs/remotes/origin/${branch}" "${!branch_variable}" done +git -C "$source_repository" update-ref \ + refs/remotes/origin/workflow-fix "$post_event_source_tip" +git -C "$source_repository" update-ref \ + refs/remotes/origin/later-parent "$later_parent_tip" make_dependency() { local name=$1 @@ -91,6 +105,10 @@ SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="$output_file" \ grep -Fqx \ "Dependency branch candidates: workflow-fix -> java-deprecation -> vitest -> lmsousa -> staging -> master" \ "$log_file" +if grep -Fq "later-parent" "$log_file"; then + echo "Resolver used a branch created after the workflow event" >&2 + exit 1 +fi grep -Fqx "current_ref=workflow-fix" "$output_file" grep -Fqx "middle_ref=java-deprecation" "$output_file" grep -Fqx "older_ref=vitest" "$output_file" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index bd7058737f..fc1c4778d0 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -9,9 +9,13 @@ on: push: paths: - .github/workflows/copilot-setup-steps.yml + - .github/scripts/resolve-dependency-refs.sh + - .github/scripts/test-resolve-dependency-refs.sh pull_request: paths: - .github/workflows/copilot-setup-steps.yml + - .github/scripts/resolve-dependency-refs.sh + - .github/scripts/test-resolve-dependency-refs.sh env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} From ec43dfa9fb4340b05e8e82472b415a4830f36e93 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 23 Jul 2026 20:43:00 +0100 Subject: [PATCH 92/99] ci: infer branch order across repositories --- .github/scripts/resolve-dependency-refs.sh | 323 +++++++++++++----- .../scripts/test-resolve-dependency-refs.sh | 75 +++- .github/workflows/copilot-setup-steps.yml | 6 +- .github/workflows/nightly.yml | 6 +- 4 files changed, 318 insertions(+), 92 deletions(-) diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh index e48aaa7b5d..413239c703 100644 --- a/.github/scripts/resolve-dependency-refs.sh +++ b/.github/scripts/resolve-dependency-refs.sh @@ -14,82 +14,138 @@ source_branch=${SOURCE_BRANCH:?SOURCE_BRANCH must name the branch being tested} integration_branch=${INTEGRATION_BRANCH:-staging} root_branch=${ROOT_BRANCH:-master} +declare -a dependency_prefixes=() +declare -a dependency_repositories=() +while (( $# )); do + dependency_prefixes+=("$1") + dependency_repositories+=("$2") + shift 2 +done + +repository_url() { + local repository=$1 + if [[ $repository == *://* || $repository == /* ]]; then + echo "$repository" + else + echo "https://github.com/${repository}.git" + fi +} + +evidence_directory=$(mktemp -d) +declare -a evidence_paths=("$source_directory") +declare -a evidence_ref_prefixes=("refs/remotes/origin/") +declare -A cloned_paths=() + +clone_evidence_repository() { + local repository=$1 + + if [[ -n ${cloned_paths[$repository]+yes} ]]; then + return + fi + + local clone_path="${evidence_directory}/repository-${#cloned_paths[@]}.git" + git clone --quiet --bare --filter=blob:none \ + "$(repository_url "$repository")" "$clone_path" + cloned_paths[$repository]=$clone_path + evidence_paths+=("$clone_path") + evidence_ref_prefixes+=("refs/heads/") +} + +for repository in "${dependency_repositories[@]}"; do + clone_evidence_repository "$repository" +done + +if [[ -n ${EVIDENCE_REPOSITORIES:-} ]]; then + read -r -a additional_evidence <<< "$EVIDENCE_REPOSITORIES" + for repository in "${additional_evidence[@]}"; do + clone_evidence_repository "$repository" + done +fi + declare -a candidates=() declare -A seen_candidates=() add_candidate() { local branch=$1 - if [[ -n $branch && -z ${seen_candidates[$branch]+yes} ]]; then + seen_candidates[$branch]=${#candidates[@]} candidates+=("$branch") - seen_candidates[$branch]=1 fi } -source_ref=HEAD -git -C "$source_directory" rev-parse --verify "${source_ref}^{commit}" >/dev/null - add_candidate "$source_branch" -# Branch creation has no durable metadata in Git. Branch tips that still occur -# on the source branch's first-parent history are the reliable part of the -# stack. Sort them by their distance from the source tip. -declare -A first_parent_distance=() -distance=0 -root_ref= -if git -C "$source_directory" show-ref --verify --quiet "refs/remotes/origin/${root_branch}"; then - root_ref="refs/remotes/origin/${root_branch}" -elif git -C "$source_directory" show-ref --verify --quiet "refs/heads/${root_branch}"; then - root_ref="refs/heads/${root_branch}" -fi - -if [[ -z $root_ref ]]; then - echo "Cannot find the root branch '${root_branch}' in ${source_directory}" >&2 - exit 1 -fi +collect_candidates() { + local repository_path=$1 + local ref_prefix=$2 + local start_ref=$3 + local required=$4 + local root_ref="${ref_prefix}${root_branch}" -reached_root=false -while IFS= read -r commit; do - first_parent_distance[$commit]=$distance - ((distance += 1)) - - # The root branch is allowed to advance independently. Stop at the first - # first-parent commit contained in its current history, not at its tip. - if [[ -n $root_ref ]] && - git -C "$source_directory" merge-base --is-ancestor "$commit" "$root_ref"; then - reached_root=true - break + if ! git -C "$repository_path" rev-parse --verify "${start_ref}^{commit}" >/dev/null 2>&1; then + if [[ $required == true ]]; then + echo "Cannot resolve the workflow commit in ${repository_path}" >&2 + exit 1 + fi + return + fi + if ! git -C "$repository_path" rev-parse --verify "${root_ref}^{commit}" >/dev/null 2>&1; then + echo "Cannot find root branch '${root_branch}' in ${repository_path}" >&2 + exit 1 fi -done < <(git -C "$source_directory" rev-list --first-parent "$source_ref") -if [[ $reached_root != true ]]; then - echo "The first-parent history of '${source_branch}' does not reach '${root_branch}'" >&2 - exit 1 -fi + declare -A distance_by_commit=() + local distance=0 + local reached_root=false + local commit + while IFS= read -r commit; do + distance_by_commit[$commit]=$distance + ((distance += 1)) + if git -C "$repository_path" merge-base --is-ancestor "$commit" "$root_ref"; then + reached_root=true + break + fi + done < <(git -C "$repository_path" rev-list --first-parent "$start_ref") -ancestry_candidates=$(mktemp) -trap 'rm -f "$ancestry_candidates"' EXIT + if [[ $reached_root != true ]]; then + if [[ $required == true ]]; then + echo "The workflow commit's first-parent history does not reach '${root_branch}'" >&2 + exit 1 + fi + return + fi -while IFS=$'\t' read -r ref_name object_name; do - branch=${ref_name#origin/} + local ordered_refs="${evidence_directory}/candidate-refs-${#candidates[@]}-${distance}" + : > "$ordered_refs" + local ref object branch + while IFS=$'\t' read -r ref object; do + branch=${ref#"$ref_prefix"} + [[ $branch == HEAD ]] && continue + [[ $branch == "$source_branch" ]] && continue + [[ $branch == "$integration_branch" ]] && continue + [[ $branch == "$root_branch" ]] && continue + [[ -n ${distance_by_commit[$object]+yes} ]] || continue + printf '%s\t%s\n' "${distance_by_commit[$object]}" "$branch" >> "$ordered_refs" + done < <( + git -C "$repository_path" for-each-ref \ + --format='%(refname)%09%(objectname)' "${ref_prefix%/}" + ) - [[ $ref_name == origin || $branch == HEAD ]] && continue - [[ $branch == "$source_branch" ]] && continue - [[ $branch == "$integration_branch" ]] && continue - [[ $branch == "$root_branch" ]] && continue - [[ -n ${first_parent_distance[$object_name]+yes} ]] || continue + while IFS=$'\t' read -r _ branch; do + add_candidate "$branch" + done < <(sort -k1,1n -k2,2 -u "$ordered_refs") +} - printf '%s\t%s\n' "${first_parent_distance[$object_name]}" "$branch" \ - >> "$ancestry_candidates" -done < <( - git -C "$source_directory" for-each-ref \ - --format='%(refname:short)%09%(objectname)' \ - refs/remotes/origin -) +# HEAD is the immutable event commit. Never substitute the mutable remote tip. +collect_candidates "$source_directory" "refs/remotes/origin/" HEAD true -while IFS=$'\t' read -r _ branch; do - add_candidate "$branch" -done < <(sort -k1,1n -k2,2 -u "$ancestry_candidates") +for ((repository_index = 1; repository_index < ${#evidence_paths[@]}; repository_index++)); do + repository_path=${evidence_paths[$repository_index]} + source_ref="refs/heads/${source_branch}" + if git -C "$repository_path" show-ref --verify --quiet "$source_ref"; then + collect_candidates "$repository_path" "refs/heads/" "$source_ref" false + fi +done if [[ $source_branch != "$root_branch" ]]; then add_candidate "$integration_branch" @@ -100,47 +156,150 @@ printf -v candidate_chain '%s -> ' "${candidates[@]}" candidate_chain=${candidate_chain% -> } echo "Dependency branch candidates: ${candidate_chain}" -while (( $# )); do - prefix=$1 - repository=$2 - shift 2 +candidate_count=${#candidates[@]} +declare -A edges=() - if [[ $repository == *://* || $repository == /* ]]; then - url=$repository - else - url="https://github.com/${repository}.git" +add_edge() { + local newer=$1 + local older=$2 + [[ $newer == "$older" ]] || edges["${newer},${older}"]=1 +} + +is_first_parent_ancestor() { + local repository_path=$1 + local older=$2 + local newer=$3 + local commit + + while IFS= read -r commit; do + [[ $commit == "$older" ]] && return 0 + done < <(git -C "$repository_path" rev-list --first-parent "$newer") + return 1 +} + +# The event branch is newest by definition. staging and master are the two +# conventional terminal levels, even when their tips have advanced. +for ((i = 0; i < candidate_count; i++)); do + branch=${candidates[$i]} + if [[ $branch != "$source_branch" ]]; then + add_edge 0 "$i" + fi + if [[ $source_branch != "$root_branch" && + $branch != "$integration_branch" && $branch != "$root_branch" ]]; then + integration_index=${seen_candidates[$integration_branch]} + add_edge "$i" "$integration_index" fi +done +root_index=${seen_candidates[$root_branch]} +if [[ $source_branch != "$root_branch" ]]; then + integration_index=${seen_candidates[$integration_branch]} + add_edge "$integration_index" "$root_index" +fi - default_branch=$( - git ls-remote --symref "$url" HEAD | - awk '/^ref:/ {sub("refs/heads/", "", $2); print $2; exit}' - ) - if [[ -z $default_branch ]]; then - echo "Cannot determine the default branch for ${repository}" >&2 +# Each repository contributes only strict first-parent evidence. Equal refs +# are neutral; missing or diverged refs contribute no ordering. +for ((repository_index = 0; repository_index < ${#evidence_paths[@]}; repository_index++)); do + repository_path=${evidence_paths[$repository_index]} + ref_prefix=${evidence_ref_prefixes[$repository_index]} + + for ((i = 0; i < candidate_count; i++)); do + left_branch=${candidates[$i]} + [[ $left_branch == "$source_branch" || $left_branch == "$integration_branch" || $left_branch == "$root_branch" ]] && continue + left_ref="${ref_prefix}${left_branch}" + left_sha=$(git -C "$repository_path" rev-parse --verify "${left_ref}^{commit}" 2>/dev/null || true) + [[ -n $left_sha ]] || continue + + for ((j = i + 1; j < candidate_count; j++)); do + right_branch=${candidates[$j]} + [[ $right_branch == "$source_branch" || $right_branch == "$integration_branch" || $right_branch == "$root_branch" ]] && continue + right_ref="${ref_prefix}${right_branch}" + right_sha=$(git -C "$repository_path" rev-parse --verify "${right_ref}^{commit}" 2>/dev/null || true) + [[ -n $right_sha && $left_sha != "$right_sha" ]] || continue + + if is_first_parent_ancestor "$repository_path" "$right_sha" "$left_sha"; then + add_edge "$i" "$j" + elif is_first_parent_ancestor "$repository_path" "$left_sha" "$right_sha"; then + add_edge "$j" "$i" + fi + done + done +done + +# Compute transitive closure and reject contradictory evidence. +for ((k = 0; k < candidate_count; k++)); do + for ((i = 0; i < candidate_count; i++)); do + [[ -n ${edges["${i},${k}"]+yes} ]] || continue + for ((j = 0; j < candidate_count; j++)); do + if [[ -n ${edges["${k},${j}"]+yes} ]]; then + edges["${i},${j}"]=1 + fi + done + done +done + +for ((i = 0; i < candidate_count; i++)); do + if [[ -n ${edges["${i},${i}"]+yes} ]]; then + echo "Contradictory branch ordering evidence involves '${candidates[$i]}'" >&2 exit 1 fi +done - declare -A dependency_branches=() - while IFS=$'\t' read -r _ ref; do - dependency_branches[${ref#refs/heads/}]=1 - done < <(git ls-remote --heads "$url") +for ((dependency_index = 0; dependency_index < ${#dependency_repositories[@]}; dependency_index++)); do + prefix=${dependency_prefixes[$dependency_index]} + repository=${dependency_repositories[$dependency_index]} + repository_path=${cloned_paths[$repository]} - selected= - for branch in "${candidates[@]}"; do - if [[ -n ${dependency_branches[$branch]+yes} ]]; then - selected=$branch - break + default_branch=$(git -C "$repository_path" symbolic-ref --short HEAD) + default_branch=${default_branch#refs/heads/} + + declare -a available_indices=() + declare -A available_shas=() + for ((i = 0; i < candidate_count; i++)); do + branch=${candidates[$i]} + sha=$(git -C "$repository_path" rev-parse --verify "refs/heads/${branch}^{commit}" 2>/dev/null || true) + if [[ -n $sha ]]; then + available_indices+=("$i") + available_shas[$i]=$sha fi done - if [[ -z $selected ]]; then + declare -a newest_indices=() + for i in "${available_indices[@]}"; do + dominated=false + for j in "${available_indices[@]}"; do + if [[ $i != "$j" && -n ${edges["${j},${i}"]+yes} ]]; then + dominated=true + break + fi + done + [[ $dominated == true ]] || newest_indices+=("$i") + done + + if (( ${#newest_indices[@]} == 0 )); then echo "None of the candidate branches exists in ${repository}" >&2 exit 1 fi - echo "Using '${selected}' for ${repository}" + selected_index=${newest_indices[0]} + selected_sha=${available_shas[$selected_index]} + if (( ${#newest_indices[@]} > 1 )); then + ambiguous=() + for i in "${newest_indices[@]}"; do + ambiguous+=("${candidates[$i]}") + if [[ ${available_shas[$i]} != "$selected_sha" ]]; then + printf -v ambiguous_list '%s, ' "${ambiguous[@]}" + ambiguous_list=${ambiguous_list%, } + echo "Ambiguous newest branches in ${repository}: ${ambiguous_list}" >&2 + exit 1 + fi + done + fi + + selected_branch=${candidates[$selected_index]} + echo "Using '${selected_branch}' (${selected_sha}) for ${repository}" { - echo "${prefix}_ref=${selected}" + echo "${prefix}_ref=${selected_sha}" + echo "${prefix}_branch=${selected_branch}" echo "${prefix}_default=${default_branch}" } >> "${GITHUB_OUTPUT:?GITHUB_OUTPUT is not set}" done diff --git a/.github/scripts/test-resolve-dependency-refs.sh b/.github/scripts/test-resolve-dependency-refs.sh index fd9b333452..3cf7625978 100644 --- a/.github/scripts/test-resolve-dependency-refs.sh +++ b/.github/scripts/test-resolve-dependency-refs.sh @@ -65,6 +65,10 @@ git -C "$source_repository" update-ref \ refs/remotes/origin/workflow-fix "$post_event_source_tip" git -C "$source_repository" update-ref \ refs/remotes/origin/later-parent "$later_parent_tip" +# Clava's vitest and java-deprecation refs are intentionally equal. Another +# repository must provide their strict ordering. +git -C "$source_repository" update-ref \ + refs/remotes/origin/vitest "$java_deprecation_tip" make_dependency() { local name=$1 @@ -86,10 +90,21 @@ make_dependency() { } current_dependency=$(make_dependency current workflow-fix lmsousa) -middle_dependency=$(make_dependency middle java-deprecation lmsousa) +middle_dependency=$(make_dependency middle java-deprecation vitest lmsousa) older_dependency=$(make_dependency older vitest lmsousa) integration_dependency=$(make_dependency integration staging) root_dependency=$(make_dependency root) +equal_dependency=$(make_dependency equal java-deprecation vitest lmsousa) +git -C "$equal_dependency" update-ref \ + refs/heads/vitest "$java_deprecation_tip" +ambiguous_dependency=$(make_dependency ambiguous java-deprecation vitest lmsousa) +git -C "$ambiguous_dependency" update-ref \ + refs/heads/vitest "$staging_tip" +reverse_dependency=$(make_dependency reverse java-deprecation vitest lmsousa) +git -C "$reverse_dependency" update-ref \ + refs/heads/java-deprecation "$vitest_tip" +git -C "$reverse_dependency" update-ref \ + refs/heads/vitest "$java_deprecation_tip" output_file="${test_directory}/github-output" log_file="${test_directory}/resolver.log" @@ -109,10 +124,58 @@ if grep -Fq "later-parent" "$log_file"; then echo "Resolver used a branch created after the workflow event" >&2 exit 1 fi -grep -Fqx "current_ref=workflow-fix" "$output_file" -grep -Fqx "middle_ref=java-deprecation" "$output_file" -grep -Fqx "older_ref=vitest" "$output_file" -grep -Fqx "integration_ref=staging" "$output_file" -grep -Fqx "root_ref=master" "$output_file" +grep -Fqx "current_branch=workflow-fix" "$output_file" +grep -Fqx "current_ref=${workflow_fix_tip}" "$output_file" +grep -Fqx "middle_branch=java-deprecation" "$output_file" +grep -Fqx "middle_ref=${java_deprecation_tip}" "$output_file" +grep -Fqx "older_branch=vitest" "$output_file" +grep -Fqx "older_ref=${vitest_tip}" "$output_file" +grep -Fqx "integration_branch=staging" "$output_file" +grep -Fqx "integration_ref=${staging_tip}" "$output_file" +grep -Fqx "root_branch=master" "$output_file" +grep -Fqx "root_ref=${master_tip}" "$output_file" + +# Equal, unordered names are safe when they resolve to the same target commit. +equal_output="${test_directory}/equal-output" +SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="$equal_output" \ + bash "$resolver" "$source_repository" \ + equal "$equal_dependency" >/dev/null +grep -Fqx "equal_ref=${java_deprecation_tip}" "$equal_output" + +# Diverged candidates with no ordering evidence must fail rather than guess. +if SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/ambiguous-output" \ + bash "$resolver" "$source_repository" \ + ambiguous "$ambiguous_dependency" \ + >"${test_directory}/ambiguous.log" 2>&1; then + echo "Resolver accepted ambiguous, different dependency refs" >&2 + exit 1 +fi +grep -Fq "Ambiguous newest branches" "${test_directory}/ambiguous.log" + +# Opposite strict orderings from two repositories are contradictory. +if SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/contradictory-output" \ + bash "$resolver" "$source_repository" \ + forward "$middle_dependency" \ + reverse "$reverse_dependency" \ + >"${test_directory}/contradictory.log" 2>&1; then + echo "Resolver accepted contradictory repository evidence" >&2 + exit 1 +fi +grep -Fq "Contradictory branch ordering evidence" \ + "${test_directory}/contradictory.log" + +# Terminal branch workflows must not create a staging/master ordering cycle. +git -C "$source_repository" switch --quiet --detach "$staging_tip" +SOURCE_BRANCH=staging GITHUB_OUTPUT="${test_directory}/staging-output" \ + bash "$resolver" "$source_repository" \ + integration "$integration_dependency" >/dev/null +grep -Fqx "integration_ref=${staging_tip}" \ + "${test_directory}/staging-output" + +git -C "$source_repository" switch --quiet --detach "$master_tip" +SOURCE_BRANCH=master GITHUB_OUTPUT="${test_directory}/master-output" \ + bash "$resolver" "$source_repository" \ + root "$root_dependency" >/dev/null +grep -Fqx "root_ref=${master_tip}" "${test_directory}/master-output" echo "All dependency ref resolver tests passed" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index fc1c4778d0..b6c2c1cff3 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -70,9 +70,11 @@ jobs: - name: Echo checks run: | echo "Weaver branch: ${{ env.BRANCH_NAME }}" - echo "Lara framework ref: ${{ steps.repo-refs.outputs.lara_ref }}" + echo "Lara framework branch: ${{ steps.repo-refs.outputs.lara_branch }}" + echo "Lara framework commit: ${{ steps.repo-refs.outputs.lara_ref }}" echo "Lara framework default: ${{ steps.repo-refs.outputs.lara_default }}" - echo "Specs-java-libs ref: ${{ steps.repo-refs.outputs.specs_ref }}" + echo "Specs-java-libs branch: ${{ steps.repo-refs.outputs.specs_branch }}" + echo "Specs-java-libs commit: ${{ steps.repo-refs.outputs.specs_ref }}" echo "Specs-java-libs default: ${{ steps.repo-refs.outputs.specs_default }}" - name: Checkout lara-framework diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 63632034c4..36a14f898b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -66,9 +66,11 @@ jobs: - name: Echo checks run: | echo "Weaver branch: ${{ env.BRANCH_NAME }}" - echo "Lara framework ref: ${{ steps.repo-refs.outputs.lara_ref }}" + echo "Lara framework branch: ${{ steps.repo-refs.outputs.lara_branch }}" + echo "Lara framework commit: ${{ steps.repo-refs.outputs.lara_ref }}" echo "Lara framework default: ${{ steps.repo-refs.outputs.lara_default }}" - echo "Specs-java-libs ref: ${{ steps.repo-refs.outputs.specs_ref }}" + echo "Specs-java-libs branch: ${{ steps.repo-refs.outputs.specs_branch }}" + echo "Specs-java-libs commit: ${{ steps.repo-refs.outputs.specs_ref }}" echo "Specs-java-libs default: ${{ steps.repo-refs.outputs.specs_default }}" - name: Checkout lara-framework From 0c1a841655771048ad9554c9ea2de23ab8dd18bc Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 23 Jul 2026 20:50:22 +0100 Subject: [PATCH 93/99] fix: retain advanced stack branches --- .github/scripts/resolve-dependency-refs.sh | 35 +++++++++++++++++-- .../scripts/test-resolve-dependency-refs.sh | 35 +++++++++++++++++++ .github/workflows/nightly.yml | 1 + 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh index 413239c703..de7e88f229 100644 --- a/.github/scripts/resolve-dependency-refs.sh +++ b/.github/scripts/resolve-dependency-refs.sh @@ -62,6 +62,23 @@ if [[ -n ${EVIDENCE_REPOSITORIES:-} ]]; then done fi +# A moved branch is relevant only when its name is shared by repositories. +# Count each repository once so private one-off branches do not pollute the +# global candidate set. +declare -A branch_presence=() +for ((repository_index = 0; repository_index < ${#evidence_paths[@]}; repository_index++)); do + repository_path=${evidence_paths[$repository_index]} + ref_prefix=${evidence_ref_prefixes[$repository_index]} + while IFS= read -r ref; do + branch=${ref#"$ref_prefix"} + [[ $branch == HEAD ]] && continue + branch_presence[$branch]=$(( ${branch_presence[$branch]:-0} + 1 )) + done < <( + git -C "$repository_path" for-each-ref \ + --format='%(refname)' "${ref_prefix%/}" + ) +done + declare -a candidates=() declare -A seen_candidates=() @@ -114,6 +131,7 @@ collect_candidates() { fi return fi + local root_distance=$((distance - 1)) local ordered_refs="${evidence_directory}/candidate-refs-${#candidates[@]}-${distance}" : > "$ordered_refs" @@ -124,8 +142,21 @@ collect_candidates() { [[ $branch == "$source_branch" ]] && continue [[ $branch == "$integration_branch" ]] && continue [[ $branch == "$root_branch" ]] && continue - [[ -n ${distance_by_commit[$object]+yes} ]] || continue - printf '%s\t%s\n' "${distance_by_commit[$object]}" "$branch" >> "$ordered_refs" + + if [[ -n ${distance_by_commit[$object]+yes} ]]; then + printf '%s\t%s\n' "${distance_by_commit[$object]}" "$branch" >> "$ordered_refs" + continue + fi + + # The branch may have advanced after a child was created. Its merge-base + # still identifies the old tip on the event's stack. Requiring the name in + # multiple repositories and a fork point newer than the root boundary + # avoids treating every branch from master as part of this stack. + [[ ${branch_presence[$branch]:-0} -ge 2 ]] || continue + merge_base=$(git -C "$repository_path" merge-base "$start_ref" "$ref" 2>/dev/null || true) + [[ -n $merge_base && -n ${distance_by_commit[$merge_base]+yes} ]] || continue + [[ ${distance_by_commit[$merge_base]} -lt $root_distance ]] || continue + printf '%s\t%s\n' "${distance_by_commit[$merge_base]}" "$branch" >> "$ordered_refs" done < <( git -C "$repository_path" for-each-ref \ --format='%(refname)%09%(objectname)' "${ref_prefix%/}" diff --git a/.github/scripts/test-resolve-dependency-refs.sh b/.github/scripts/test-resolve-dependency-refs.sh index 3cf7625978..c9663a945d 100644 --- a/.github/scripts/test-resolve-dependency-refs.sh +++ b/.github/scripts/test-resolve-dependency-refs.sh @@ -56,6 +56,16 @@ commit "post-event source update" post_event_source_tip=$(git -C "$source_repository" rev-parse HEAD) git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" +git -C "$source_repository" switch --quiet lmsousa +commit "advanced parent branch" +advanced_lmsousa_tip=$(git -C "$source_repository" rev-parse HEAD) + +git -C "$source_repository" switch --quiet staging +git -C "$source_repository" switch --quiet -c rebased-parent-fixture +commit "rebased parent branch" +rebased_lmsousa_tip=$(git -C "$source_repository" rev-parse HEAD) +git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" + for branch in workflow-fix java-deprecation vitest lmsousa staging master; do branch_variable=${branch//-/_}_tip git -C "$source_repository" update-ref \ @@ -105,6 +115,12 @@ git -C "$reverse_dependency" update-ref \ refs/heads/java-deprecation "$vitest_tip" git -C "$reverse_dependency" update-ref \ refs/heads/vitest "$java_deprecation_tip" +advanced_dependency=$(make_dependency advanced lmsousa staging) +git -C "$advanced_dependency" update-ref \ + refs/heads/lmsousa "$advanced_lmsousa_tip" +rebased_dependency=$(make_dependency rebased lmsousa staging) +git -C "$rebased_dependency" update-ref \ + refs/heads/lmsousa "$rebased_lmsousa_tip" output_file="${test_directory}/github-output" log_file="${test_directory}/resolver.log" @@ -178,4 +194,23 @@ SOURCE_BRANCH=master GITHUB_OUTPUT="${test_directory}/master-output" \ root "$root_dependency" >/dev/null grep -Fqx "root_ref=${master_tip}" "${test_directory}/master-output" +# A shared parent remains a candidate when its tip advances or is rebased, +# provided its merge-base still identifies this post-master stack. +git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" +git -C "$source_repository" update-ref \ + refs/remotes/origin/lmsousa "$advanced_lmsousa_tip" +SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/advanced-output" \ + bash "$resolver" "$source_repository" \ + advanced "$advanced_dependency" >/dev/null +grep -Fqx "advanced_ref=${advanced_lmsousa_tip}" \ + "${test_directory}/advanced-output" + +git -C "$source_repository" update-ref \ + refs/remotes/origin/lmsousa "$rebased_lmsousa_tip" +SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/rebased-output" \ + bash "$resolver" "$source_repository" \ + rebased "$rebased_dependency" >/dev/null +grep -Fqx "rebased_ref=${rebased_lmsousa_tip}" \ + "${test_directory}/rebased-output" + echo "All dependency ref resolver tests passed" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 36a14f898b..f4e09490e2 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -7,6 +7,7 @@ name: nightly on: push: + pull_request: # Daily at midnight schedule: From db0df0046f5f8db5833e9f800092c302d9f1e857 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 23 Jul 2026 20:55:50 +0100 Subject: [PATCH 94/99] fix: reject unproven sibling branches --- .github/scripts/resolve-dependency-refs.sh | 58 +++++++++++++++++-- .../scripts/test-resolve-dependency-refs.sh | 25 +++++++- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh index de7e88f229..561f86d965 100644 --- a/.github/scripts/resolve-dependency-refs.sh +++ b/.github/scripts/resolve-dependency-refs.sh @@ -81,12 +81,17 @@ done declare -a candidates=() declare -A seen_candidates=() +declare -A candidate_proven=() add_candidate() { local branch=$1 + local proven=${2:-true} if [[ -n $branch && -z ${seen_candidates[$branch]+yes} ]]; then seen_candidates[$branch]=${#candidates[@]} + candidate_proven[${#candidates[@]}]=$proven candidates+=("$branch") + elif [[ -n $branch && $proven == true ]]; then + candidate_proven[${seen_candidates[$branch]}]=true fi } @@ -144,7 +149,7 @@ collect_candidates() { [[ $branch == "$root_branch" ]] && continue if [[ -n ${distance_by_commit[$object]+yes} ]]; then - printf '%s\t%s\n' "${distance_by_commit[$object]}" "$branch" >> "$ordered_refs" + printf '%s\t%s\ttrue\n' "${distance_by_commit[$object]}" "$branch" >> "$ordered_refs" continue fi @@ -156,14 +161,14 @@ collect_candidates() { merge_base=$(git -C "$repository_path" merge-base "$start_ref" "$ref" 2>/dev/null || true) [[ -n $merge_base && -n ${distance_by_commit[$merge_base]+yes} ]] || continue [[ ${distance_by_commit[$merge_base]} -lt $root_distance ]] || continue - printf '%s\t%s\n' "${distance_by_commit[$merge_base]}" "$branch" >> "$ordered_refs" + printf '%s\t%s\tfalse\n' "${distance_by_commit[$merge_base]}" "$branch" >> "$ordered_refs" done < <( git -C "$repository_path" for-each-ref \ --format='%(refname)%09%(objectname)' "${ref_prefix%/}" ) - while IFS=$'\t' read -r _ branch; do - add_candidate "$branch" + while IFS=$'\t' read -r _ branch proven; do + add_candidate "$branch" "$proven" done < <(sort -k1,1n -k2,2 -u "$ordered_refs") } @@ -189,6 +194,7 @@ echo "Dependency branch candidates: ${candidate_chain}" candidate_count=${#candidates[@]} declare -A edges=() +declare -A strict_edges=() add_edge() { local newer=$1 @@ -249,13 +255,36 @@ for ((repository_index = 0; repository_index < ${#evidence_paths[@]}; repository if is_first_parent_ancestor "$repository_path" "$right_sha" "$left_sha"; then add_edge "$i" "$j" + strict_edges["${i},${j}"]=1 elif is_first_parent_ancestor "$repository_path" "$left_sha" "$right_sha"; then add_edge "$j" "$i" + strict_edges["${j},${i}"]=1 fi done done done +# Strict evidence validates an uncertain candidate only when it connects to a +# branch already observed directly on a stack. Propagate that proof through a +# chain of strict first-parent relationships. +proof_changed=true +while [[ $proof_changed == true ]]; do + proof_changed=false + for edge in "${!strict_edges[@]}"; do + newer_index=${edge%,*} + older_index=${edge#*,} + if [[ ${candidate_proven[$newer_index]:-false} == true && + ${candidate_proven[$older_index]:-false} != true ]]; then + candidate_proven[$older_index]=true + proof_changed=true + elif [[ ${candidate_proven[$older_index]:-false} == true && + ${candidate_proven[$newer_index]:-false} != true ]]; then + candidate_proven[$newer_index]=true + proof_changed=true + fi + done +done + # Compute transitive closure and reject contradictory evidence. for ((k = 0; k < candidate_count; k++)); do for ((i = 0; i < candidate_count; i++)); do @@ -295,6 +324,27 @@ for ((dependency_index = 0; dependency_index < ${#dependency_repositories[@]}; d done declare -a newest_indices=() + + # A merge-base-only branch may be a moved parent or an unrelated sibling. + # Without strict evidence elsewhere, selecting its distinct commit would be + # a guess. Equal target SHAs remain harmless aliases. + for i in "${available_indices[@]}"; do + [[ ${candidate_proven[$i]:-false} == true ]] && continue + harmless_uncertain=false + for j in "${available_indices[@]}"; do + if [[ ${candidate_proven[$j]:-false} == true && + ( ${available_shas[$j]} == "${available_shas[$i]}" || + -n ${edges["${j},${i}"]+yes} ) ]]; then + harmless_uncertain=true + break + fi + done + if [[ $harmless_uncertain != true ]]; then + echo "Cannot place shared branch '${candidates[$i]}' in the stack for ${repository}" >&2 + exit 1 + fi + done + for i in "${available_indices[@]}"; do dominated=false for j in "${available_indices[@]}"; do diff --git a/.github/scripts/test-resolve-dependency-refs.sh b/.github/scripts/test-resolve-dependency-refs.sh index c9663a945d..81bb36d0be 100644 --- a/.github/scripts/test-resolve-dependency-refs.sh +++ b/.github/scripts/test-resolve-dependency-refs.sh @@ -64,6 +64,11 @@ git -C "$source_repository" switch --quiet staging git -C "$source_repository" switch --quiet -c rebased-parent-fixture commit "rebased parent branch" rebased_lmsousa_tip=$(git -C "$source_repository" rev-parse HEAD) + +git -C "$source_repository" switch --quiet --detach "$lmsousa_tip" +git -C "$source_repository" switch --quiet -c unrelated-sibling +commit "unrelated sibling branch" +unrelated_sibling_tip=$(git -C "$source_repository" rev-parse HEAD) git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" for branch in workflow-fix java-deprecation vitest lmsousa staging master; do @@ -121,6 +126,7 @@ git -C "$advanced_dependency" update-ref \ rebased_dependency=$(make_dependency rebased lmsousa staging) git -C "$rebased_dependency" update-ref \ refs/heads/lmsousa "$rebased_lmsousa_tip" +sibling_dependency=$(make_dependency sibling unrelated-sibling staging) output_file="${test_directory}/github-output" log_file="${test_directory}/resolver.log" @@ -199,7 +205,8 @@ grep -Fqx "root_ref=${master_tip}" "${test_directory}/master-output" git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" git -C "$source_repository" update-ref \ refs/remotes/origin/lmsousa "$advanced_lmsousa_tip" -SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/advanced-output" \ +SOURCE_BRANCH=workflow-fix EVIDENCE_REPOSITORIES="$middle_dependency" \ + GITHUB_OUTPUT="${test_directory}/advanced-output" \ bash "$resolver" "$source_repository" \ advanced "$advanced_dependency" >/dev/null grep -Fqx "advanced_ref=${advanced_lmsousa_tip}" \ @@ -207,10 +214,24 @@ grep -Fqx "advanced_ref=${advanced_lmsousa_tip}" \ git -C "$source_repository" update-ref \ refs/remotes/origin/lmsousa "$rebased_lmsousa_tip" -SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/rebased-output" \ +SOURCE_BRANCH=workflow-fix EVIDENCE_REPOSITORIES="$middle_dependency" \ + GITHUB_OUTPUT="${test_directory}/rebased-output" \ bash "$resolver" "$source_repository" \ rebased "$rebased_dependency" >/dev/null grep -Fqx "rebased_ref=${rebased_lmsousa_tip}" \ "${test_directory}/rebased-output" +# A shared sibling with the same fork point is not a proven stack member. +git -C "$source_repository" update-ref \ + refs/remotes/origin/unrelated-sibling "$unrelated_sibling_tip" +if SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/sibling-output" \ + bash "$resolver" "$source_repository" \ + sibling "$sibling_dependency" \ + >"${test_directory}/sibling.log" 2>&1; then + echo "Resolver selected an unproven sibling branch" >&2 + exit 1 +fi +grep -Fq "Cannot place shared branch 'unrelated-sibling'" \ + "${test_directory}/sibling.log" + echo "All dependency ref resolver tests passed" From 1c21976371847794ea78796c6c58e579fe84be0f Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Thu, 23 Jul 2026 20:57:32 +0100 Subject: [PATCH 95/99] fix: propagate stack proof toward ancestors --- .github/scripts/resolve-dependency-refs.sh | 4 ---- .github/scripts/test-resolve-dependency-refs.sh | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh index 561f86d965..f0adaebfe8 100644 --- a/.github/scripts/resolve-dependency-refs.sh +++ b/.github/scripts/resolve-dependency-refs.sh @@ -277,10 +277,6 @@ while [[ $proof_changed == true ]]; do ${candidate_proven[$older_index]:-false} != true ]]; then candidate_proven[$older_index]=true proof_changed=true - elif [[ ${candidate_proven[$older_index]:-false} == true && - ${candidate_proven[$newer_index]:-false} != true ]]; then - candidate_proven[$newer_index]=true - proof_changed=true fi done done diff --git a/.github/scripts/test-resolve-dependency-refs.sh b/.github/scripts/test-resolve-dependency-refs.sh index 81bb36d0be..a235f1c8ef 100644 --- a/.github/scripts/test-resolve-dependency-refs.sh +++ b/.github/scripts/test-resolve-dependency-refs.sh @@ -222,6 +222,8 @@ grep -Fqx "rebased_ref=${rebased_lmsousa_tip}" \ "${test_directory}/rebased-output" # A shared sibling with the same fork point is not a proven stack member. +git -C "$source_repository" update-ref \ + refs/remotes/origin/lmsousa "$lmsousa_tip" git -C "$source_repository" update-ref \ refs/remotes/origin/unrelated-sibling "$unrelated_sibling_tip" if SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/sibling-output" \ From a50683e7b8e533ef918235309183a6708330238d Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sat, 22 Aug 2026 20:20:35 +0100 Subject: [PATCH 96/99] fix(test): use Vitest helper instead of removed Jest helper SourceLocations.test.ts still imported @specs-feup/lara/jest/jestHelpers.ts, which no longer exists after the Vitest migration, breaking tsc on all platforms. --- Clava-JS/api/SourceLocations.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clava-JS/api/SourceLocations.test.ts b/Clava-JS/api/SourceLocations.test.ts index d2d06f95ab..c805d530c0 100644 --- a/Clava-JS/api/SourceLocations.test.ts +++ b/Clava-JS/api/SourceLocations.test.ts @@ -1,4 +1,4 @@ -import { registerSourceCode } from "@specs-feup/lara/jest/jestHelpers.ts"; +import { registerSourceCode } from "@specs-feup/lara/vitest/weaverTestHelpers.ts"; import Query from "@specs-feup/lara/api/weaver/Query.ts"; import { Joinpoint, Vardecl } from "./Joinpoints.ts"; From 5a8f872892647d6fa8b6cd7f72299a046b3aa924 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 1 Sep 2026 19:41:14 +0100 Subject: [PATCH 97/99] ci: consume specs-feup/branch-resolver action for dependency refs Replaces the vendored copies of resolve-dependency-refs.sh and its test suite with the specs-feup/branch-resolver@v1 composite action, which exports _ref/_branch/_default as environment variables. --- .github/scripts/resolve-dependency-refs.sh | 382 ------------------ .../scripts/test-resolve-dependency-refs.sh | 239 ----------- .github/workflows/copilot-setup-steps.yml | 36 +- .github/workflows/nightly.yml | 36 +- 4 files changed, 28 insertions(+), 665 deletions(-) delete mode 100644 .github/scripts/resolve-dependency-refs.sh delete mode 100644 .github/scripts/test-resolve-dependency-refs.sh diff --git a/.github/scripts/resolve-dependency-refs.sh b/.github/scripts/resolve-dependency-refs.sh deleted file mode 100644 index f0adaebfe8..0000000000 --- a/.github/scripts/resolve-dependency-refs.sh +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -if (( $# < 3 || $# % 2 == 0 )); then - echo "Usage: $0 SOURCE_DIRECTORY OUTPUT_PREFIX DEPENDENCY_REPOSITORY [...]" >&2 - exit 2 -fi - -source_directory=$1 -shift - -source_branch=${SOURCE_BRANCH:?SOURCE_BRANCH must name the branch being tested} -integration_branch=${INTEGRATION_BRANCH:-staging} -root_branch=${ROOT_BRANCH:-master} - -declare -a dependency_prefixes=() -declare -a dependency_repositories=() -while (( $# )); do - dependency_prefixes+=("$1") - dependency_repositories+=("$2") - shift 2 -done - -repository_url() { - local repository=$1 - if [[ $repository == *://* || $repository == /* ]]; then - echo "$repository" - else - echo "https://github.com/${repository}.git" - fi -} - -evidence_directory=$(mktemp -d) -declare -a evidence_paths=("$source_directory") -declare -a evidence_ref_prefixes=("refs/remotes/origin/") -declare -A cloned_paths=() - -clone_evidence_repository() { - local repository=$1 - - if [[ -n ${cloned_paths[$repository]+yes} ]]; then - return - fi - - local clone_path="${evidence_directory}/repository-${#cloned_paths[@]}.git" - git clone --quiet --bare --filter=blob:none \ - "$(repository_url "$repository")" "$clone_path" - cloned_paths[$repository]=$clone_path - evidence_paths+=("$clone_path") - evidence_ref_prefixes+=("refs/heads/") -} - -for repository in "${dependency_repositories[@]}"; do - clone_evidence_repository "$repository" -done - -if [[ -n ${EVIDENCE_REPOSITORIES:-} ]]; then - read -r -a additional_evidence <<< "$EVIDENCE_REPOSITORIES" - for repository in "${additional_evidence[@]}"; do - clone_evidence_repository "$repository" - done -fi - -# A moved branch is relevant only when its name is shared by repositories. -# Count each repository once so private one-off branches do not pollute the -# global candidate set. -declare -A branch_presence=() -for ((repository_index = 0; repository_index < ${#evidence_paths[@]}; repository_index++)); do - repository_path=${evidence_paths[$repository_index]} - ref_prefix=${evidence_ref_prefixes[$repository_index]} - while IFS= read -r ref; do - branch=${ref#"$ref_prefix"} - [[ $branch == HEAD ]] && continue - branch_presence[$branch]=$(( ${branch_presence[$branch]:-0} + 1 )) - done < <( - git -C "$repository_path" for-each-ref \ - --format='%(refname)' "${ref_prefix%/}" - ) -done - -declare -a candidates=() -declare -A seen_candidates=() -declare -A candidate_proven=() - -add_candidate() { - local branch=$1 - local proven=${2:-true} - if [[ -n $branch && -z ${seen_candidates[$branch]+yes} ]]; then - seen_candidates[$branch]=${#candidates[@]} - candidate_proven[${#candidates[@]}]=$proven - candidates+=("$branch") - elif [[ -n $branch && $proven == true ]]; then - candidate_proven[${seen_candidates[$branch]}]=true - fi -} - -add_candidate "$source_branch" - -collect_candidates() { - local repository_path=$1 - local ref_prefix=$2 - local start_ref=$3 - local required=$4 - local root_ref="${ref_prefix}${root_branch}" - - if ! git -C "$repository_path" rev-parse --verify "${start_ref}^{commit}" >/dev/null 2>&1; then - if [[ $required == true ]]; then - echo "Cannot resolve the workflow commit in ${repository_path}" >&2 - exit 1 - fi - return - fi - if ! git -C "$repository_path" rev-parse --verify "${root_ref}^{commit}" >/dev/null 2>&1; then - echo "Cannot find root branch '${root_branch}' in ${repository_path}" >&2 - exit 1 - fi - - declare -A distance_by_commit=() - local distance=0 - local reached_root=false - local commit - while IFS= read -r commit; do - distance_by_commit[$commit]=$distance - ((distance += 1)) - if git -C "$repository_path" merge-base --is-ancestor "$commit" "$root_ref"; then - reached_root=true - break - fi - done < <(git -C "$repository_path" rev-list --first-parent "$start_ref") - - if [[ $reached_root != true ]]; then - if [[ $required == true ]]; then - echo "The workflow commit's first-parent history does not reach '${root_branch}'" >&2 - exit 1 - fi - return - fi - local root_distance=$((distance - 1)) - - local ordered_refs="${evidence_directory}/candidate-refs-${#candidates[@]}-${distance}" - : > "$ordered_refs" - local ref object branch - while IFS=$'\t' read -r ref object; do - branch=${ref#"$ref_prefix"} - [[ $branch == HEAD ]] && continue - [[ $branch == "$source_branch" ]] && continue - [[ $branch == "$integration_branch" ]] && continue - [[ $branch == "$root_branch" ]] && continue - - if [[ -n ${distance_by_commit[$object]+yes} ]]; then - printf '%s\t%s\ttrue\n' "${distance_by_commit[$object]}" "$branch" >> "$ordered_refs" - continue - fi - - # The branch may have advanced after a child was created. Its merge-base - # still identifies the old tip on the event's stack. Requiring the name in - # multiple repositories and a fork point newer than the root boundary - # avoids treating every branch from master as part of this stack. - [[ ${branch_presence[$branch]:-0} -ge 2 ]] || continue - merge_base=$(git -C "$repository_path" merge-base "$start_ref" "$ref" 2>/dev/null || true) - [[ -n $merge_base && -n ${distance_by_commit[$merge_base]+yes} ]] || continue - [[ ${distance_by_commit[$merge_base]} -lt $root_distance ]] || continue - printf '%s\t%s\tfalse\n' "${distance_by_commit[$merge_base]}" "$branch" >> "$ordered_refs" - done < <( - git -C "$repository_path" for-each-ref \ - --format='%(refname)%09%(objectname)' "${ref_prefix%/}" - ) - - while IFS=$'\t' read -r _ branch proven; do - add_candidate "$branch" "$proven" - done < <(sort -k1,1n -k2,2 -u "$ordered_refs") -} - -# HEAD is the immutable event commit. Never substitute the mutable remote tip. -collect_candidates "$source_directory" "refs/remotes/origin/" HEAD true - -for ((repository_index = 1; repository_index < ${#evidence_paths[@]}; repository_index++)); do - repository_path=${evidence_paths[$repository_index]} - source_ref="refs/heads/${source_branch}" - if git -C "$repository_path" show-ref --verify --quiet "$source_ref"; then - collect_candidates "$repository_path" "refs/heads/" "$source_ref" false - fi -done - -if [[ $source_branch != "$root_branch" ]]; then - add_candidate "$integration_branch" -fi -add_candidate "$root_branch" - -printf -v candidate_chain '%s -> ' "${candidates[@]}" -candidate_chain=${candidate_chain% -> } -echo "Dependency branch candidates: ${candidate_chain}" - -candidate_count=${#candidates[@]} -declare -A edges=() -declare -A strict_edges=() - -add_edge() { - local newer=$1 - local older=$2 - [[ $newer == "$older" ]] || edges["${newer},${older}"]=1 -} - -is_first_parent_ancestor() { - local repository_path=$1 - local older=$2 - local newer=$3 - local commit - - while IFS= read -r commit; do - [[ $commit == "$older" ]] && return 0 - done < <(git -C "$repository_path" rev-list --first-parent "$newer") - return 1 -} - -# The event branch is newest by definition. staging and master are the two -# conventional terminal levels, even when their tips have advanced. -for ((i = 0; i < candidate_count; i++)); do - branch=${candidates[$i]} - if [[ $branch != "$source_branch" ]]; then - add_edge 0 "$i" - fi - if [[ $source_branch != "$root_branch" && - $branch != "$integration_branch" && $branch != "$root_branch" ]]; then - integration_index=${seen_candidates[$integration_branch]} - add_edge "$i" "$integration_index" - fi -done -root_index=${seen_candidates[$root_branch]} -if [[ $source_branch != "$root_branch" ]]; then - integration_index=${seen_candidates[$integration_branch]} - add_edge "$integration_index" "$root_index" -fi - -# Each repository contributes only strict first-parent evidence. Equal refs -# are neutral; missing or diverged refs contribute no ordering. -for ((repository_index = 0; repository_index < ${#evidence_paths[@]}; repository_index++)); do - repository_path=${evidence_paths[$repository_index]} - ref_prefix=${evidence_ref_prefixes[$repository_index]} - - for ((i = 0; i < candidate_count; i++)); do - left_branch=${candidates[$i]} - [[ $left_branch == "$source_branch" || $left_branch == "$integration_branch" || $left_branch == "$root_branch" ]] && continue - left_ref="${ref_prefix}${left_branch}" - left_sha=$(git -C "$repository_path" rev-parse --verify "${left_ref}^{commit}" 2>/dev/null || true) - [[ -n $left_sha ]] || continue - - for ((j = i + 1; j < candidate_count; j++)); do - right_branch=${candidates[$j]} - [[ $right_branch == "$source_branch" || $right_branch == "$integration_branch" || $right_branch == "$root_branch" ]] && continue - right_ref="${ref_prefix}${right_branch}" - right_sha=$(git -C "$repository_path" rev-parse --verify "${right_ref}^{commit}" 2>/dev/null || true) - [[ -n $right_sha && $left_sha != "$right_sha" ]] || continue - - if is_first_parent_ancestor "$repository_path" "$right_sha" "$left_sha"; then - add_edge "$i" "$j" - strict_edges["${i},${j}"]=1 - elif is_first_parent_ancestor "$repository_path" "$left_sha" "$right_sha"; then - add_edge "$j" "$i" - strict_edges["${j},${i}"]=1 - fi - done - done -done - -# Strict evidence validates an uncertain candidate only when it connects to a -# branch already observed directly on a stack. Propagate that proof through a -# chain of strict first-parent relationships. -proof_changed=true -while [[ $proof_changed == true ]]; do - proof_changed=false - for edge in "${!strict_edges[@]}"; do - newer_index=${edge%,*} - older_index=${edge#*,} - if [[ ${candidate_proven[$newer_index]:-false} == true && - ${candidate_proven[$older_index]:-false} != true ]]; then - candidate_proven[$older_index]=true - proof_changed=true - fi - done -done - -# Compute transitive closure and reject contradictory evidence. -for ((k = 0; k < candidate_count; k++)); do - for ((i = 0; i < candidate_count; i++)); do - [[ -n ${edges["${i},${k}"]+yes} ]] || continue - for ((j = 0; j < candidate_count; j++)); do - if [[ -n ${edges["${k},${j}"]+yes} ]]; then - edges["${i},${j}"]=1 - fi - done - done -done - -for ((i = 0; i < candidate_count; i++)); do - if [[ -n ${edges["${i},${i}"]+yes} ]]; then - echo "Contradictory branch ordering evidence involves '${candidates[$i]}'" >&2 - exit 1 - fi -done - -for ((dependency_index = 0; dependency_index < ${#dependency_repositories[@]}; dependency_index++)); do - prefix=${dependency_prefixes[$dependency_index]} - repository=${dependency_repositories[$dependency_index]} - repository_path=${cloned_paths[$repository]} - - default_branch=$(git -C "$repository_path" symbolic-ref --short HEAD) - default_branch=${default_branch#refs/heads/} - - declare -a available_indices=() - declare -A available_shas=() - for ((i = 0; i < candidate_count; i++)); do - branch=${candidates[$i]} - sha=$(git -C "$repository_path" rev-parse --verify "refs/heads/${branch}^{commit}" 2>/dev/null || true) - if [[ -n $sha ]]; then - available_indices+=("$i") - available_shas[$i]=$sha - fi - done - - declare -a newest_indices=() - - # A merge-base-only branch may be a moved parent or an unrelated sibling. - # Without strict evidence elsewhere, selecting its distinct commit would be - # a guess. Equal target SHAs remain harmless aliases. - for i in "${available_indices[@]}"; do - [[ ${candidate_proven[$i]:-false} == true ]] && continue - harmless_uncertain=false - for j in "${available_indices[@]}"; do - if [[ ${candidate_proven[$j]:-false} == true && - ( ${available_shas[$j]} == "${available_shas[$i]}" || - -n ${edges["${j},${i}"]+yes} ) ]]; then - harmless_uncertain=true - break - fi - done - if [[ $harmless_uncertain != true ]]; then - echo "Cannot place shared branch '${candidates[$i]}' in the stack for ${repository}" >&2 - exit 1 - fi - done - - for i in "${available_indices[@]}"; do - dominated=false - for j in "${available_indices[@]}"; do - if [[ $i != "$j" && -n ${edges["${j},${i}"]+yes} ]]; then - dominated=true - break - fi - done - [[ $dominated == true ]] || newest_indices+=("$i") - done - - if (( ${#newest_indices[@]} == 0 )); then - echo "None of the candidate branches exists in ${repository}" >&2 - exit 1 - fi - - selected_index=${newest_indices[0]} - selected_sha=${available_shas[$selected_index]} - if (( ${#newest_indices[@]} > 1 )); then - ambiguous=() - for i in "${newest_indices[@]}"; do - ambiguous+=("${candidates[$i]}") - if [[ ${available_shas[$i]} != "$selected_sha" ]]; then - printf -v ambiguous_list '%s, ' "${ambiguous[@]}" - ambiguous_list=${ambiguous_list%, } - echo "Ambiguous newest branches in ${repository}: ${ambiguous_list}" >&2 - exit 1 - fi - done - fi - - selected_branch=${candidates[$selected_index]} - echo "Using '${selected_branch}' (${selected_sha}) for ${repository}" - { - echo "${prefix}_ref=${selected_sha}" - echo "${prefix}_branch=${selected_branch}" - echo "${prefix}_default=${default_branch}" - } >> "${GITHUB_OUTPUT:?GITHUB_OUTPUT is not set}" -done diff --git a/.github/scripts/test-resolve-dependency-refs.sh b/.github/scripts/test-resolve-dependency-refs.sh deleted file mode 100644 index a235f1c8ef..0000000000 --- a/.github/scripts/test-resolve-dependency-refs.sh +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -script_directory=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) -resolver=${1:-"${script_directory}/resolve-dependency-refs.sh"} -test_directory=$(mktemp -d) -source_repository="${test_directory}/source" - -git init --quiet --initial-branch=master "$source_repository" -git -C "$source_repository" config user.name "CI Resolver Test" -git -C "$source_repository" config user.email "ci-resolver@example.invalid" - -commit() { - local message=$1 - git -C "$source_repository" commit --quiet --allow-empty -m "$message" -} - -commit "master base" - -git -C "$source_repository" switch --quiet -c staging -commit "staging base" - -git -C "$source_repository" switch --quiet -c lmsousa -commit "branch A" -lmsousa_tip=$(git -C "$source_repository" rev-parse HEAD) - -git -C "$source_repository" switch --quiet -c vitest -commit "branch B" -vitest_tip=$(git -C "$source_repository" rev-parse HEAD) - -git -C "$source_repository" switch --quiet -c java-deprecation -commit "branch C" -java_deprecation_tip=$(git -C "$source_repository" rev-parse HEAD) - -git -C "$source_repository" switch --quiet -c workflow-fix -commit "current branch" -workflow_fix_tip=$(git -C "$source_repository" rev-parse HEAD) - -# Both long-lived branches advance after the stack was created. -git -C "$source_repository" switch --quiet staging -commit "advanced staging" -staging_tip=$(git -C "$source_repository" rev-parse HEAD) - -git -C "$source_repository" switch --quiet master -commit "advanced master" -master_tip=$(git -C "$source_repository" rev-parse HEAD) - -# Simulate a later push after the workflow event was created. Resolution must -# use the detached event commit, never the newer remote source branch tip. -git -C "$source_repository" switch --quiet workflow-fix -commit "post-event parent branch" -later_parent_tip=$(git -C "$source_repository" rev-parse HEAD) -git -C "$source_repository" branch later-parent -commit "post-event source update" -post_event_source_tip=$(git -C "$source_repository" rev-parse HEAD) -git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" - -git -C "$source_repository" switch --quiet lmsousa -commit "advanced parent branch" -advanced_lmsousa_tip=$(git -C "$source_repository" rev-parse HEAD) - -git -C "$source_repository" switch --quiet staging -git -C "$source_repository" switch --quiet -c rebased-parent-fixture -commit "rebased parent branch" -rebased_lmsousa_tip=$(git -C "$source_repository" rev-parse HEAD) - -git -C "$source_repository" switch --quiet --detach "$lmsousa_tip" -git -C "$source_repository" switch --quiet -c unrelated-sibling -commit "unrelated sibling branch" -unrelated_sibling_tip=$(git -C "$source_repository" rev-parse HEAD) -git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" - -for branch in workflow-fix java-deprecation vitest lmsousa staging master; do - branch_variable=${branch//-/_}_tip - git -C "$source_repository" update-ref \ - "refs/remotes/origin/${branch}" "${!branch_variable}" -done -git -C "$source_repository" update-ref \ - refs/remotes/origin/workflow-fix "$post_event_source_tip" -git -C "$source_repository" update-ref \ - refs/remotes/origin/later-parent "$later_parent_tip" -# Clava's vitest and java-deprecation refs are intentionally equal. Another -# repository must provide their strict ordering. -git -C "$source_repository" update-ref \ - refs/remotes/origin/vitest "$java_deprecation_tip" - -make_dependency() { - local name=$1 - shift - local repository="${test_directory}/${name}.git" - - git clone --quiet --bare "$source_repository" "$repository" - while IFS= read -r ref; do - git -C "$repository" update-ref -d "$ref" - done < <(git -C "$repository" for-each-ref --format='%(refname)' refs/heads) - - git -C "$repository" update-ref refs/heads/master "$master_tip" - for branch in "$@"; do - branch_variable=${branch//-/_}_tip - git -C "$repository" update-ref "refs/heads/${branch}" "${!branch_variable}" - done - git -C "$repository" symbolic-ref HEAD refs/heads/master - echo "$repository" -} - -current_dependency=$(make_dependency current workflow-fix lmsousa) -middle_dependency=$(make_dependency middle java-deprecation vitest lmsousa) -older_dependency=$(make_dependency older vitest lmsousa) -integration_dependency=$(make_dependency integration staging) -root_dependency=$(make_dependency root) -equal_dependency=$(make_dependency equal java-deprecation vitest lmsousa) -git -C "$equal_dependency" update-ref \ - refs/heads/vitest "$java_deprecation_tip" -ambiguous_dependency=$(make_dependency ambiguous java-deprecation vitest lmsousa) -git -C "$ambiguous_dependency" update-ref \ - refs/heads/vitest "$staging_tip" -reverse_dependency=$(make_dependency reverse java-deprecation vitest lmsousa) -git -C "$reverse_dependency" update-ref \ - refs/heads/java-deprecation "$vitest_tip" -git -C "$reverse_dependency" update-ref \ - refs/heads/vitest "$java_deprecation_tip" -advanced_dependency=$(make_dependency advanced lmsousa staging) -git -C "$advanced_dependency" update-ref \ - refs/heads/lmsousa "$advanced_lmsousa_tip" -rebased_dependency=$(make_dependency rebased lmsousa staging) -git -C "$rebased_dependency" update-ref \ - refs/heads/lmsousa "$rebased_lmsousa_tip" -sibling_dependency=$(make_dependency sibling unrelated-sibling staging) - -output_file="${test_directory}/github-output" -log_file="${test_directory}/resolver.log" -SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="$output_file" \ - bash "$resolver" "$source_repository" \ - current "$current_dependency" \ - middle "$middle_dependency" \ - older "$older_dependency" \ - integration "$integration_dependency" \ - root "$root_dependency" | - tee "$log_file" - -grep -Fqx \ - "Dependency branch candidates: workflow-fix -> java-deprecation -> vitest -> lmsousa -> staging -> master" \ - "$log_file" -if grep -Fq "later-parent" "$log_file"; then - echo "Resolver used a branch created after the workflow event" >&2 - exit 1 -fi -grep -Fqx "current_branch=workflow-fix" "$output_file" -grep -Fqx "current_ref=${workflow_fix_tip}" "$output_file" -grep -Fqx "middle_branch=java-deprecation" "$output_file" -grep -Fqx "middle_ref=${java_deprecation_tip}" "$output_file" -grep -Fqx "older_branch=vitest" "$output_file" -grep -Fqx "older_ref=${vitest_tip}" "$output_file" -grep -Fqx "integration_branch=staging" "$output_file" -grep -Fqx "integration_ref=${staging_tip}" "$output_file" -grep -Fqx "root_branch=master" "$output_file" -grep -Fqx "root_ref=${master_tip}" "$output_file" - -# Equal, unordered names are safe when they resolve to the same target commit. -equal_output="${test_directory}/equal-output" -SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="$equal_output" \ - bash "$resolver" "$source_repository" \ - equal "$equal_dependency" >/dev/null -grep -Fqx "equal_ref=${java_deprecation_tip}" "$equal_output" - -# Diverged candidates with no ordering evidence must fail rather than guess. -if SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/ambiguous-output" \ - bash "$resolver" "$source_repository" \ - ambiguous "$ambiguous_dependency" \ - >"${test_directory}/ambiguous.log" 2>&1; then - echo "Resolver accepted ambiguous, different dependency refs" >&2 - exit 1 -fi -grep -Fq "Ambiguous newest branches" "${test_directory}/ambiguous.log" - -# Opposite strict orderings from two repositories are contradictory. -if SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/contradictory-output" \ - bash "$resolver" "$source_repository" \ - forward "$middle_dependency" \ - reverse "$reverse_dependency" \ - >"${test_directory}/contradictory.log" 2>&1; then - echo "Resolver accepted contradictory repository evidence" >&2 - exit 1 -fi -grep -Fq "Contradictory branch ordering evidence" \ - "${test_directory}/contradictory.log" - -# Terminal branch workflows must not create a staging/master ordering cycle. -git -C "$source_repository" switch --quiet --detach "$staging_tip" -SOURCE_BRANCH=staging GITHUB_OUTPUT="${test_directory}/staging-output" \ - bash "$resolver" "$source_repository" \ - integration "$integration_dependency" >/dev/null -grep -Fqx "integration_ref=${staging_tip}" \ - "${test_directory}/staging-output" - -git -C "$source_repository" switch --quiet --detach "$master_tip" -SOURCE_BRANCH=master GITHUB_OUTPUT="${test_directory}/master-output" \ - bash "$resolver" "$source_repository" \ - root "$root_dependency" >/dev/null -grep -Fqx "root_ref=${master_tip}" "${test_directory}/master-output" - -# A shared parent remains a candidate when its tip advances or is rebased, -# provided its merge-base still identifies this post-master stack. -git -C "$source_repository" switch --quiet --detach "$workflow_fix_tip" -git -C "$source_repository" update-ref \ - refs/remotes/origin/lmsousa "$advanced_lmsousa_tip" -SOURCE_BRANCH=workflow-fix EVIDENCE_REPOSITORIES="$middle_dependency" \ - GITHUB_OUTPUT="${test_directory}/advanced-output" \ - bash "$resolver" "$source_repository" \ - advanced "$advanced_dependency" >/dev/null -grep -Fqx "advanced_ref=${advanced_lmsousa_tip}" \ - "${test_directory}/advanced-output" - -git -C "$source_repository" update-ref \ - refs/remotes/origin/lmsousa "$rebased_lmsousa_tip" -SOURCE_BRANCH=workflow-fix EVIDENCE_REPOSITORIES="$middle_dependency" \ - GITHUB_OUTPUT="${test_directory}/rebased-output" \ - bash "$resolver" "$source_repository" \ - rebased "$rebased_dependency" >/dev/null -grep -Fqx "rebased_ref=${rebased_lmsousa_tip}" \ - "${test_directory}/rebased-output" - -# A shared sibling with the same fork point is not a proven stack member. -git -C "$source_repository" update-ref \ - refs/remotes/origin/lmsousa "$lmsousa_tip" -git -C "$source_repository" update-ref \ - refs/remotes/origin/unrelated-sibling "$unrelated_sibling_tip" -if SOURCE_BRANCH=workflow-fix GITHUB_OUTPUT="${test_directory}/sibling-output" \ - bash "$resolver" "$source_repository" \ - sibling "$sibling_dependency" \ - >"${test_directory}/sibling.log" 2>&1; then - echo "Resolver selected an unproven sibling branch" >&2 - exit 1 -fi -grep -Fq "Cannot place shared branch 'unrelated-sibling'" \ - "${test_directory}/sibling.log" - -echo "All dependency ref resolver tests passed" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index b6c2c1cff3..aeb4868657 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -9,13 +9,9 @@ on: push: paths: - .github/workflows/copilot-setup-steps.yml - - .github/scripts/resolve-dependency-refs.sh - - .github/scripts/test-resolve-dependency-refs.sh pull_request: paths: - .github/workflows/copilot-setup-steps.yml - - .github/scripts/resolve-dependency-refs.sh - - .github/scripts/test-resolve-dependency-refs.sh env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} @@ -53,43 +49,37 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: Test dependency ref resolver - run: bash clava/.github/scripts/test-resolve-dependency-refs.sh - - name: Determine repository refs - id: repo-refs - shell: bash - env: - SOURCE_BRANCH: ${{ env.BRANCH_NAME }} - run: | - bash clava/.github/scripts/resolve-dependency-refs.sh \ - clava \ - lara specs-feup/lara-framework \ + uses: specs-feup/branch-resolver@v1 + with: + source-directory: clava + dependencies: | + lara specs-feup/lara-framework specs specs-feup/specs-java-libs - name: Echo checks run: | echo "Weaver branch: ${{ env.BRANCH_NAME }}" - echo "Lara framework branch: ${{ steps.repo-refs.outputs.lara_branch }}" - echo "Lara framework commit: ${{ steps.repo-refs.outputs.lara_ref }}" - echo "Lara framework default: ${{ steps.repo-refs.outputs.lara_default }}" - echo "Specs-java-libs branch: ${{ steps.repo-refs.outputs.specs_branch }}" - echo "Specs-java-libs commit: ${{ steps.repo-refs.outputs.specs_ref }}" - echo "Specs-java-libs default: ${{ steps.repo-refs.outputs.specs_default }}" + echo "Lara framework branch: ${{ env.lara_branch }}" + echo "Lara framework commit: ${{ env.lara_ref }}" + echo "Lara framework default: ${{ env.lara_default }}" + echo "Specs-java-libs branch: ${{ env.specs_branch }}" + echo "Specs-java-libs commit: ${{ env.specs_ref }}" + echo "Specs-java-libs default: ${{ env.specs_default }}" - name: Checkout lara-framework uses: actions/checkout@v6 with: repository: specs-feup/lara-framework path: lara-framework - ref: ${{ steps.repo-refs.outputs.lara_ref }} + ref: ${{ env.lara_ref }} - name: Checkout specs-java-libs uses: actions/checkout@v6 with: repository: specs-feup/specs-java-libs path: specs-java-libs - ref: ${{ steps.repo-refs.outputs.specs_ref }} + ref: ${{ env.specs_ref }} - name: Build Weaver run: | diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index f4e09490e2..b37ef75abc 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -27,8 +27,8 @@ jobs: runs-on: ubuntu-latest outputs: - lara_ref: ${{ steps.repo-refs.outputs.lara_ref }} - specs_ref: ${{ steps.repo-refs.outputs.specs_ref }} + lara_ref: ${{ env.lara_ref }} + specs_ref: ${{ env.specs_ref }} steps: - name: Setup Java @@ -50,43 +50,37 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: Test dependency ref resolver - run: bash clava/.github/scripts/test-resolve-dependency-refs.sh - - name: Determine repository refs - id: repo-refs - shell: bash - env: - SOURCE_BRANCH: ${{ env.BRANCH_NAME }} - run: | - bash clava/.github/scripts/resolve-dependency-refs.sh \ - clava \ - lara specs-feup/lara-framework \ + uses: specs-feup/branch-resolver@v1 + with: + source-directory: clava + dependencies: | + lara specs-feup/lara-framework specs specs-feup/specs-java-libs - name: Echo checks run: | echo "Weaver branch: ${{ env.BRANCH_NAME }}" - echo "Lara framework branch: ${{ steps.repo-refs.outputs.lara_branch }}" - echo "Lara framework commit: ${{ steps.repo-refs.outputs.lara_ref }}" - echo "Lara framework default: ${{ steps.repo-refs.outputs.lara_default }}" - echo "Specs-java-libs branch: ${{ steps.repo-refs.outputs.specs_branch }}" - echo "Specs-java-libs commit: ${{ steps.repo-refs.outputs.specs_ref }}" - echo "Specs-java-libs default: ${{ steps.repo-refs.outputs.specs_default }}" + echo "Lara framework branch: ${{ env.lara_branch }}" + echo "Lara framework commit: ${{ env.lara_ref }}" + echo "Lara framework default: ${{ env.lara_default }}" + echo "Specs-java-libs branch: ${{ env.specs_branch }}" + echo "Specs-java-libs commit: ${{ env.specs_ref }}" + echo "Specs-java-libs default: ${{ env.specs_default }}" - name: Checkout lara-framework uses: actions/checkout@v6 with: repository: specs-feup/lara-framework path: lara-framework - ref: ${{ steps.repo-refs.outputs.lara_ref }} + ref: ${{ env.lara_ref }} - name: Checkout specs-java-libs uses: actions/checkout@v6 with: repository: specs-feup/specs-java-libs path: specs-java-libs - ref: ${{ steps.repo-refs.outputs.specs_ref }} + ref: ${{ env.specs_ref }} - name: Build with Gradle run: | From bb3c807d991797d6852cd72dd5dd099ff07f39f7 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 2 Sep 2026 17:20:59 +0100 Subject: [PATCH 98/99] ci: check out the same PR revision in build-js as build-java build-js had no ref, so pull_request runs tested the synthetic merge commit while build-java built the PR head; JS tests then consumed Java binaries from a different revision. Pin both jobs to github.event.pull_request.head.sha || github.sha. --- .github/workflows/nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b37ef75abc..2a1749ba4a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -151,6 +151,7 @@ jobs: uses: actions/checkout@v6 with: path: clava + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Checkout lara-framework uses: actions/checkout@v6 From 43e7b81a1fa247a76ccfc52561b81236c3828735 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 2 Sep 2026 17:30:22 +0100 Subject: [PATCH 99/99] ci: restrict push trigger to master and staging A push to a feature branch with an open PR fired both the push run and the pull_request synchronize run for the same commit. Feature branches are covered by pull_request; push now builds only the long-lived branches. --- .github/workflows/copilot-setup-steps.yml | 1 + .github/workflows/nightly.yml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index aeb4868657..93c29172c4 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -7,6 +7,7 @@ name: "Copilot Setup Steps" on: workflow_dispatch: push: + branches: [master, staging] paths: - .github/workflows/copilot-setup-steps.yml pull_request: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2a1749ba4a..270e13a5c0 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -7,6 +7,9 @@ name: nightly on: push: + # Feature branches are covered by pull_request; push only builds the + # long-lived branches to avoid duplicate runs when a PR is open. + branches: [master, staging] pull_request: # Daily at midnight