1/* 2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { global } from '../static/global'; 17import { isNumber, throwError, getEnumName } from '../../utils'; 18import { KNativePointer, KInt, nullptr, withStringResult } from '@koalaui/interop'; 19import { passNode, passString, unpackNodeArray, unpackNonNullableNode } from './private'; 20import { isFunctionDeclaration, isMemberExpression, isMethodDefinition, isNumberLiteral } from '../factory/nodeTests'; 21import { 22 Es2pandaContextState, 23 Es2pandaMethodDefinitionKind, 24 Es2pandaModifierFlags, 25} from '../../generated/Es2pandaEnums'; 26import type { AstNode } from '../peers/AstNode'; 27import { 28 ClassDefinition, 29 ClassProperty, 30 ETSImportDeclaration, 31 ImportSpecifier, 32 isClassDefinition, 33 isIdentifier, 34 isObjectExpression, 35 isProperty, 36 isScriptFunction, 37 isTSInterfaceDeclaration, 38 Property, 39 type AnnotationUsage, 40} from '../../generated'; 41import { Program } from '../peers/Program'; 42import { clearNodeCache } from '../class-by-peer'; 43import { SourcePosition } from '../peers/SourcePosition'; 44import { MemberExpression } from '../to-be-generated/MemberExpression'; 45 46export function proceedToState(state: Es2pandaContextState, forceDtsEmit = false): void { 47 console.log('[TS WRAPPER] PROCEED TO STATE: ', getEnumName(Es2pandaContextState, state)); 48 if (global.es2panda._ContextState(global.context) === Es2pandaContextState.ES2PANDA_STATE_ERROR) { 49 processErrorState(state, forceDtsEmit); 50 } 51 if (state <= global.es2panda._ContextState(global.context)) { 52 console.log('[TS WRAPPER] PROCEED TO STATE: SKIPPING'); 53 return; 54 } 55 clearNodeCache(); 56 global.es2panda._ProceedToState(global.context, state); 57 processErrorState(state, forceDtsEmit); 58} 59 60function processErrorState(state: Es2pandaContextState, forceDtsEmit = false): void { 61 try { 62 if ( 63 global.es2panda._ContextState(global.context) === Es2pandaContextState.ES2PANDA_STATE_ERROR && 64 !forceDtsEmit 65 ) { 66 const errorMessage = withStringResult(global.es2panda._ContextErrorMessage(global.context)); 67 if (errorMessage === undefined) { 68 throwError(`Could not get ContextErrorMessage`); 69 } 70 const allErrorMessages = withStringResult(global.es2panda._GetAllErrorMessages(global.context)); 71 if (allErrorMessages === undefined) { 72 throwError(`Could not get AllErrorMessages`); 73 } 74 throwError([`Failed to proceed to ${Es2pandaContextState[state]}`, errorMessage, allErrorMessages].join(`\n`)); 75 } 76 } catch (e) { 77 global.es2panda._DestroyContext(global.context); 78 throw e; 79 } 80} 81 82export function startChecker(): boolean { 83 return global.es2panda._CheckerStartChecker(global.context); 84} 85 86export function recheckSubtree(node: AstNode): void { 87 global.es2panda._AstNodeRecheck(global.context, node.peer); 88} 89 90export function rebindSubtree(node: AstNode): void { 91 global.es2panda._AstNodeRebind(global.context, node.peer); 92} 93 94export function getDecl(node: AstNode): AstNode | undefined { 95 if (isMemberExpression(node)) { 96 return getDeclFromArrayOrObjectMember(node); 97 } 98 if (isObjectExpression(node)) { 99 return getPeerObjectDecl(passNode(node)); 100 } 101 const decl = getPeerDecl(passNode(node)); 102 if (!!decl) { 103 return decl; 104 } 105 if (isProperty(node.parent)) { 106 return getDeclFromProperty(node.parent); 107 } 108 return undefined; 109} 110 111function getDeclFromProperty(node: Property): AstNode | undefined { 112 if (!node.key) { 113 return undefined; 114 } 115 if (!isObjectExpression(node.parent)) { 116 return getPeerDecl(passNode(node.key)); 117 } 118 return getDeclFromObjectExpressionProperty(node); 119} 120 121function getDeclFromObjectExpressionProperty(node: Property): AstNode | undefined { 122 const declNode = getPeerObjectDecl(passNode(node.parent)); 123 if (!declNode || !node.key || !isIdentifier(node.key)) { 124 return undefined; 125 } 126 let body: readonly AstNode[] = []; 127 if (isClassDefinition(declNode)) { 128 body = declNode.body; 129 } else if (isTSInterfaceDeclaration(declNode)) { 130 body = declNode.body?.body ?? []; 131 } 132 return body.find( 133 (statement) => 134 isMethodDefinition(statement) && 135 statement.kind === Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET && 136 !!statement.name && 137 !!node.key && 138 isIdentifier(node.key) && 139 statement.name.name === node.key.name 140 ); 141} 142 143function getDeclFromArrayOrObjectMember(node: MemberExpression): AstNode | undefined { 144 if (isNumberLiteral(node.property)) { 145 return getDecl(node.object); 146 } 147 return getDecl(node.property); 148} 149 150export function getPeerDecl(peer: KNativePointer): AstNode | undefined { 151 const decl = global.es2panda._DeclarationFromIdentifier(global.context, peer); 152 if (decl === nullptr) { 153 return undefined; 154 } 155 return unpackNonNullableNode(decl); 156} 157 158export function getPeerObjectDecl(peer: KNativePointer): AstNode | undefined { 159 const decl = global.es2panda._ClassVariableDeclaration(global.context, peer); 160 if (decl === nullptr) { 161 return undefined; 162 } 163 return unpackNonNullableNode(decl); 164} 165 166export function getAnnotations(node: AstNode): readonly AnnotationUsage[] { 167 if (!isFunctionDeclaration(node) && !isScriptFunction(node) && !isClassDefinition(node)) { 168 throwError('for now annotations allowed only for: functionDeclaration, scriptFunction, classDefinition'); 169 } 170 return unpackNodeArray(global.es2panda._AnnotationAllowedAnnotations(global.context, node.peer, nullptr)); 171} 172 173export function getOriginalNode(node: AstNode): AstNode { 174 if (node === undefined) { 175 // TODO: fix this 176 throwError('there is no arkts pair of ts node (unable to getOriginalNode)'); 177 } 178 if (node.originalPeer === nullptr) { 179 return node; 180 } 181 return unpackNonNullableNode(node.originalPeer); 182} 183 184export function getFileName(): string { 185 return global.filePath; 186} 187 188export function classDefinitionSetFromStructModifier(node: ClassDefinition): void { 189 global.es2panda._ClassDefinitionSetFromStructModifier(global.context, node.peer); 190} 191 192export function classDefinitionIsFromStructConst(node: ClassDefinition): boolean { 193 return global.es2panda._ClassDefinitionIsFromStructConst(global.context, node.peer); 194} 195 196export function ImportSpecifierSetRemovable(node: ImportSpecifier): void { 197 global.es2panda._ImportSpecifierSetRemovable(global.context, node.peer); 198} 199 200export function ImportSpecifierIsRemovableConst(node: ImportSpecifier): boolean { 201 return global.es2panda._ImportSpecifierIsRemovableConst(global.context, node.peer); 202} 203 204// TODO: It seems like Definition overrides AstNode modifiers 205// with it's own modifiers which is completely unrelated set of flags. 206// Use this function if you need 207// the language level modifiers: public, declare, export, etc. 208export function classDefinitionFlags(node: ClassDefinition): Es2pandaModifierFlags { 209 return global.generatedEs2panda._AstNodeModifiers(global.context, node.peer); 210} 211 212// TODO: Import statements should be inserted to the statements 213export function importDeclarationInsert(node: ETSImportDeclaration, program: Program): void { 214 global.es2panda._InsertETSImportDeclarationAndParse(global.context, program.peer, node.peer); 215} 216 217export function hasModifierFlag(node: AstNode, flag: Es2pandaModifierFlags): boolean { 218 if (!node) return false; 219 220 let modifiers; 221 if (isClassDefinition(node)) { 222 modifiers = classDefinitionFlags(node); 223 } else { 224 modifiers = node.modifiers; 225 } 226 return (modifiers & flag) === flag; 227} 228 229// TODO: ClassProperty's optional flag is set by AstNode's modifiers flags. 230export function classPropertySetOptional(node: ClassProperty, value: boolean): ClassProperty { 231 if (value) { 232 node.modifiers |= Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; 233 } else { 234 node.modifiers &= Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; 235 } 236 return node; 237} 238 239export function modifiersToString(modifiers: Es2pandaModifierFlags): string { 240 return Object.values(Es2pandaModifierFlags) 241 .filter(isNumber) 242 .map((it) => { 243 console.log(it.valueOf(), Es2pandaModifierFlags[it], modifiers.valueOf() & it); 244 return (modifiers.valueOf() & it) === it ? Es2pandaModifierFlags[it] : ''; 245 }) 246 .join(' '); 247} 248export function destroyConfig(config: KNativePointer): void { 249 global.es2panda._DestroyConfig(config); 250 global.resetConfig(); 251} 252 253export function setAllParents(ast: AstNode) { 254 global.es2panda._AstNodeUpdateAll(global.context, ast.peer); 255} 256 257export function generateTsDeclarationsFromContext( 258 outputDeclEts: string, 259 outputEts: string, 260 exportAll: boolean, 261 recordFile: string 262): KInt { 263 return global.es2panda._GenerateTsDeclarationsFromContext( 264 global.context, 265 passString(outputDeclEts), 266 passString(outputEts), 267 exportAll, 268 passString(recordFile) 269 ); 270} 271 272export function isDefaultAccessModifierClassProperty(property: ClassProperty): boolean { 273 return global.es2panda._ClassPropertyIsDefaultAccessModifierConst(global.context, property.peer); 274} 275 276export function getStartPosition(node: AstNode): SourcePosition { 277 return new SourcePosition(global.es2panda._AstNodeStartConst(global.context, node.peer)); 278} 279 280export function getEndPosition(node: AstNode): SourcePosition { 281 return new SourcePosition(global.es2panda._AstNodeEndConst(global.context, node.peer)); 282} 283