diff --git a/arkoala-arkts/libarkts/native/src/bridges.cc b/arkoala-arkts/libarkts/native/src/bridges.cc index d7d313b737c2436367d0fb52153c8878cc3cb090..6f57ec0681e1bd8257dc05f2bf50f0c3cc8f856a 100644 --- a/arkoala-arkts/libarkts/native/src/bridges.cc +++ b/arkoala-arkts/libarkts/native/src/bridges.cc @@ -329,3 +329,30 @@ KNativePointer impl_CreateClassDefinition1(KNativePointer context, KNativePointe return result; } KOALA_INTEROP_6(CreateClassDefinition1, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt, KInt); + +KNativePointer impl_CallExpressionSignature(KNativePointer context, KNativePointer classInstance) +{ + const auto _context = reinterpret_cast(context); + const auto _classInstance = reinterpret_cast(classInstance); + const auto result = GetImpl()->CallExpressionSignature(_context, _classInstance); + return result; +} +KOALA_INTEROP_2(CallExpressionSignature, KNativePointer, KNativePointer, KNativePointer) + +KNativePointer impl_SignatureFunction(KNativePointer context, KNativePointer classInstance) +{ + const auto _context = reinterpret_cast(context); + const auto _classInstance = reinterpret_cast(classInstance); + const auto result = GetImpl()->SignatureFunction(_context, _classInstance); + return result; +} +KOALA_INTEROP_2(SignatureFunction, KNativePointer, KNativePointer, KNativePointer) + +KNativePointer impl_DeclarationFromIdentifier(KNativePointer context, KNativePointer identifier) +{ + const auto _context = reinterpret_cast(context); + const auto _identifier = reinterpret_cast(identifier); + const auto result = GetImpl()->DeclarationFromIdentifier(_context, _identifier); + return result; +} +KOALA_INTEROP_2(DeclarationFromIdentifier, KNativePointer, KNativePointer, KNativePointer) diff --git a/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts b/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts index 78b2555cd0a58d9c8ee0c58ab859a261ad37804d..e62809f69870d79fb9ddeab09c92f0c51464216b 100644 --- a/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts +++ b/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts @@ -541,6 +541,16 @@ export class Es2pandaNativeModule { _ScopeSetParent(context: KPtr, ast: KPtr, scope: KPtr): void { throw new Error("Not implemented") } + + _CallExpressionSignature(context: KPtr, classInstance: KPtr): KPtr { + throw new Error("Not implemented") + } + _SignatureFunction(context: KPtr, classInstance: KPtr): KPtr { + throw new Error("Not implemented") + } + _DeclarationFromIdentifier(context: KPtr, identifier: KPtr): KPtr { + throw new Error("Not implemented") + } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/arkoala-arkts/libarkts/src/arkts-api/to-be-generated/MemberExpression.ts b/arkoala-arkts/libarkts/src/arkts-api/to-be-generated/MemberExpression.ts index 790405568ed25d9f921e999fb9b5d3100a96e783..f760db27aae26ba2f6e15dc5598e0bb4dd1943e4 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/to-be-generated/MemberExpression.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/to-be-generated/MemberExpression.ts @@ -61,4 +61,12 @@ export class MemberExpression extends AstNode { get kind(): Es2pandaMemberExpressionKind { return global.generatedEs2panda._MemberExpressionKindConst(global.context, this.peer) } + + get computed(): boolean { + return global.generatedEs2panda._MemberExpressionIsComputedConst(global.context, this.peer) + } + + get optional(): boolean { + return false // todo: no corresponding method in es2panda + } } \ No newline at end of file diff --git a/arkoala-arkts/libarkts/src/arkts-api/types.ts b/arkoala-arkts/libarkts/src/arkts-api/types.ts index a9bf80f46e63fa2f2755bc2a57f484af28ed97c7..d83d0709658f2378f9a8bf90924864e7f4977cb0 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/types.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/types.ts @@ -45,6 +45,7 @@ import { AstNode } from "./peers/AstNode" import { ArktsObject } from "./peers/ArktsObject" import { Config } from "./peers/Config" import { Context } from "./peers/Context" +import { factory } from "@koalaui/ets-tsc" export const arrayOfNullptr = new BigUint64Array([nullptr]) @@ -333,7 +334,7 @@ export class ETSUnionType extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE) super(peer) - this.types = unpackNodeArray(global.generatedEs2panda._ETSUnionTypeIrTypesConst(global.context, this.peer)) + this.types = unpackNodeArray(global.generatedEs2panda._TSUnionTypeTypesConst(global.context, this.peer)) } static create( @@ -504,7 +505,7 @@ export class ScriptFunction extends AstNode { this.parameters = unpackNodeArray(global.generatedEs2panda._ScriptFunctionParams(global.context, this.peer)) this.typeParamsDecl = unpackNode(global.generatedEs2panda._ScriptFunctionTypeParams(global.context, this.peer)) this.body = unpackNode(global.generatedEs2panda._ScriptFunctionBody(global.context, this.peer)) - // this.signature = unpackNonNullableObject(FunctionSignature, nativeModule._ScriptFunctionSignature(global.context, this.peer)) + // this.signature = unpackNode(global.generatedEs2panda._ScriptFunctionSignature(global.context, this.peer)) // this.declare = global.generatedEs2panda._ScriptFunctionDeclareConst(global.context, this.peer) this.ident = unpackNode(global.generatedEs2panda._ScriptFunctionId(global.context, this.peer)) @@ -513,16 +514,21 @@ export class ScriptFunction extends AstNode { static create( body: AstNode | undefined, - signature: FunctionSignature, + signature: FunctionSignature | undefined, functionFlags: Es2pandaScriptFunctionFlags, modifierFlags: Es2pandaModifierFlags, declare: boolean, - ident: Identifier | undefined + ident: Identifier | undefined, + parameters?: ETSParameterExpression[], ): ScriptFunction { const peer = global.generatedEs2panda._CreateScriptFunction( global.context, passNode(body), - signature.peer, + signature?.peer ?? FunctionSignature.create( + undefined, + parameters!, + undefined + ).peer, functionFlags, modifierFlags ) @@ -568,7 +574,7 @@ export class ScriptFunction extends AstNode { return (this.modifiers & Es2pandaModifierFlags.MODIFIER_FLAGS_ASYNC) !== 0 } - readonly parameters: readonly ETSParameterExpression[] + readonly parameters: ETSParameterExpression[] readonly typeParamsDecl?: TSTypeParameterDeclaration readonly body?: BlockStatement // readonly signature: FunctionSignature @@ -1072,7 +1078,9 @@ export class EtsImportDeclaration extends AstNode { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION) super(peer) this.importSource = unpackNonNullableNode(global.generatedEs2panda._ETSImportDeclarationSourceConst(global.context, this.peer)) + this.importSpecifiers = unpackNodeArray(global.generatedEs2panda._ImportDeclarationSpecifiersConst(global.context, this.peer)) this.resolvedSource = unpackNonNullableNode(global.generatedEs2panda._ETSImportDeclarationResolvedSource(global.context, this.peer)) + this.importKind = global.generatedEs2panda._ImportDeclarationIsTypeKindConst(global.context, this.peer) this.hasDecl = global.generatedEs2panda._ETSImportDeclarationHasDeclConst(global.context, this.peer) } @@ -1091,13 +1099,15 @@ export class EtsImportDeclaration extends AstNode { ).peer, passNodeArray(specifiers), specifiers.length, - importKind + +importKind ) ) } readonly importSource: StringLiteral readonly resolvedSource: StringLiteral + readonly importSpecifiers: readonly ImportSpecifier[] + readonly importKind: Es2pandaImportKinds readonly hasDecl: boolean } diff --git a/arkoala-arkts/libarkts/src/arkts-api/utilities/private.ts b/arkoala-arkts/libarkts/src/arkts-api/utilities/private.ts index ea90f728200cf6841f6ac1144e7adc6c80614663..c6ec7acdc97ad40f98c9c6c5eadeed19372adf3c 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/utilities/private.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/utilities/private.ts @@ -44,7 +44,7 @@ export function passNode(node: AstNode | undefined): KNativePointer { } // meaning unpackNonNullableNodeArray -export function unpackNodeArray(nodesPtr: KNativePointer): readonly T[] { +export function unpackNodeArray(nodesPtr: KNativePointer): T[] { if (nodesPtr === nullptr) { throwError('nodesPtr is NULLPTR (maybe you should use unpackNodeArray)') } diff --git a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts index ec5c19bfa5e4ffb4ddaad4114e1bf88a715772a0..dbc9a8b880859f3551e96d8343845893d7a37e4b 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts @@ -16,11 +16,12 @@ import { global } from "../static/global" import { throwError } from "../../utils" import { KNativePointer, nullptr, withStringResult } from "@koalaui/interop" -import { AnnotationUsageIr } from "../types" -import { unpackNodeArray, unpackNonNullableNode } from "./private" +import { AnnotationUsageIr, MethodDefinition } from "../types" +import { passNode, unpackNodeArray, unpackNonNullableNode } from "./private" import { isClassDefinition, isFunctionDeclaration, isScriptFunction } from "../factory/nodeTests" import { Es2pandaContextState } from "../../generated/Es2pandaEnums" import { AstNode } from "../peers/AstNode" +import { Identifier } from "../types" export function proceedToState(state: Es2pandaContextState): void { if (state <= global.es2panda._ContextState(global.context)) { @@ -54,17 +55,7 @@ export function rebindSubtree(node: AstNode): void { } export function getDecl(node: AstNode): AstNode | undefined { - let decl: KNativePointer = node.peer - - decl = global.es2panda._AstNodeVariableConst(global.context, decl) - if (decl === nullptr) { - return undefined - } - decl = global.es2panda._VariableDeclaration(global.context, decl) - if (decl === nullptr) { - return undefined - } - decl = global.es2panda._DeclNode(global.context, decl) + const decl = global.es2panda._DeclarationFromIdentifier(global.context, passNode(node)) if (decl === nullptr) { return undefined } diff --git a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts index 7838dd0ddc3f8ce046f94449f00cd1bcf4987d1a..7c32de72275a859c85b41cc601d361d950e77302 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts @@ -19,11 +19,13 @@ import { CallExpression, ClassDeclaration, ClassDefinition, + EtsImportDeclaration, EtsScript, ExpressionStatement, FunctionDeclaration, FunctionSignature, MethodDefinition, + ScriptFunction, StructDeclaration } from "./types" import { @@ -33,6 +35,7 @@ import { } from "../generated/Es2pandaEnums" import { nullptr } from "@koalaui/interop" import { AstNode } from "./peers/AstNode" +import { MemberExpression } from "../reexport-for-generated" type Visitor = (node: AstNode) => AstNode @@ -142,6 +145,37 @@ export function visitEachChild( false ) } + if (node instanceof ScriptFunction) { + return factory.updateScriptFunction( + node, + nodeVisitor(node.body, visitor), + nullptr, + node.scriptFunctionFlags, + Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + false, + nodeVisitor(node.ident, visitor), + nodesVisitor(node.parameters, visitor) + ) + } + if (node instanceof EtsImportDeclaration) { + return factory.updateImportDeclaration( + node, + nodeVisitor(node.importSource, visitor), + nodesVisitor(node.importSpecifiers, visitor), + node.importKind, + node.hasDecl + ) + } + if (node instanceof MemberExpression) { + return factory.updateMemberExpression( + node, + nodeVisitor(node.object, visitor), + nodeVisitor(node.property, visitor), + node.kind, + node.computed, + node.optional + ) + } // TODO return node diff --git a/arkoala-arkts/memo-plugin/demo/arktsconfig-unmemoized.json b/arkoala-arkts/memo-plugin/demo/arktsconfig-unmemoized.json new file mode 100644 index 0000000000000000000000000000000000000000..6f7c6369ba6ce38f9c095f6d3b3447ca1d9d2b23 --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/arktsconfig-unmemoized.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "package": "@koalaui/runtime", + "outDir": "build/abc", + "baseUrl": "build/unmemoized", + "paths": { + "@koalaui/compat": [ + "../../../../../incremental/compat/src/arkts" + ], + "@koalaui/common": [ + "../../../../../incremental/common/src" + ], + "@koalaui/runtime": [ + "../../../../../incremental/runtime/build/unmemoized/src" + ], + "@koalaui/interop": [ + "../../../../../interop/src/arkts" + ] + } + }, + "include": ["./build/unmemoized/stub.ts"] +} diff --git a/arkoala-arkts/memo-plugin/demo/arktsconfig.json b/arkoala-arkts/memo-plugin/demo/arktsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..24a22d2626eeee91d13fbc5c9459c19fb6820a81 --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/arktsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "package": "@koalaui/runtime", + "outDir": "build/abc", + "baseUrl": ".", + "paths": { + "@koalaui/compat": [ + "../../../incremental/compat/src/arkts" + ], + "@koalaui/common": [ + "../../../incremental/common/src" + ], + "@koalaui/runtime": [ + "../runtime-api" + ], + "@koalaui/interop": [ + "../../../interop/src/arkts" + ] + }, + "plugins": [ + { + "transform": "..", + "stage": "checked" + } + ] + }, + "include": ["./demo.sts"] +} diff --git a/arkoala-arkts/memo-plugin/demo/demo.sts b/arkoala-arkts/memo-plugin/demo/demo.sts new file mode 100644 index 0000000000000000000000000000000000000000..74bb5f28f4ae17e937de5e98fd7b8599d0638d30 --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/demo.sts @@ -0,0 +1,16 @@ +import { GlobalStateManager, memoEntry, StateContext, memo } from "@koalaui/runtime" +import { memo_foo } from "./stub" + +@memo +function foo_wrapper() { + memo_foo("hello") +} + +function main() { + const manager = GlobalStateManager.instance + const state = manager.computableState((context: StateContext): Int => { + memoEntry(context, 0, ETSGLOBAL.foo_wrapper) + return 20 + }) + console.log(state.value) +} diff --git a/arkoala-arkts/memo-plugin/demo/demo.ts b/arkoala-arkts/memo-plugin/demo/demo.ts new file mode 100644 index 0000000000000000000000000000000000000000..a5990afd4aa5eb6169fbdf225bfaed04e26f2701 --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/demo.ts @@ -0,0 +1,18 @@ +// This file is needed to check how unmemoized (by ts plugin) should look like +// It is not actually executed + +import { GlobalStateManager, memoEntry, StateContext } from "@koalaui/runtime" +import { memo_foo } from "./stub" + +/** @memo */ +function foo_wrapper() { + memo_foo("hello") +} + +function main() { + const manager = GlobalStateManager.instance + const state = manager.computableState((context: StateContext) => { + memoEntry(context, 0, foo_wrapper) + }) + console.log(state.value) +} diff --git a/arkoala-arkts/memo-plugin/demo/package.json b/arkoala-arkts/memo-plugin/demo/package.json new file mode 100644 index 0000000000000000000000000000000000000000..863ce63477b5cfe718a24b97b84ca656fec9db72 --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/package.json @@ -0,0 +1,19 @@ +{ + "name": "@koalaui/sts-memo-plugin", + "version": "", + "description": "", + "scripts": { + "clean": "rimraf build", + "unmemoize": "ets-tsc -p tsconfig-unmemoize.json", + "ts-like:compile": "npm run unmemoize && fast-arktsc --input-files ./arktsconfig-unmemoized.json --output-dir ./build --compiler ../../../incremental/tools/panda/arkts/arktsc --link-name stub && ninja ${NINJA_OPTIONS} -f build/build.ninja", + "arkts-like:compile": "../../../incremental/tools/panda/arkts/arktsc --arktsconfig ./arktsconfig.json --output ./build/demo.abc ./demo.sts", + "arkts-like:compile:capi": "../../../incremental/tools/panda/arkts/arktsc-capi --arktsconfig ./arktsconfig.json --output ./build/demo.abc --file ./demo.sts --dump-plugin-ast", + "run": "../../../incremental/tools/panda/node_modules/@panda/sdk/linux_host_tools/bin/ark --load-runtimes=ets --boot-panda-files=../../../incremental/tools/panda/node_modules/@panda/sdk/ets/etsstdlib.abc:../../../incremental/runtime/build/incremental.abc:build/stub.abc ./build/demo.abc @koalaui.runtime.demo.ETSGLOBAL::main", + "run:unmemoized": "npm run unmemoize && npm run ts-like:compile && ../../../incremental/tools/panda/node_modules/@panda/sdk/linux_host_tools/bin/ark --load-runtimes=ets --boot-panda-files=../../../incremental/tools/panda/node_modules/@panda/sdk/ets/etsstdlib.abc:../../../incremental/runtime/build/incremental.abc ./build/stub.abc @koalaui.runtime.demo.ETSGLOBAL::main", + "disasm": "../../../incremental/tools/panda/node_modules/@panda/sdk/linux_host_tools/bin/ark_disasm build/stub.abc build/stub.disasm" + }, + "devDependencies": { + "@koalaui/ets-tsc": "4.9.5-r4", + "@koalaui/fast-arktsc": "next" + } +} \ No newline at end of file diff --git a/arkoala-arkts/memo-plugin/demo/stub.sts b/arkoala-arkts/memo-plugin/demo/stub.sts new file mode 100644 index 0000000000000000000000000000000000000000..e444446f64cb815255aa51557b27f87961bc80c0 --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/stub.sts @@ -0,0 +1,4 @@ +import { memo, __memo_context_type, __memo_id_type } from "@koalaui/runtime" + +@memo export declare function memo_foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, s: String): void +@memo export declare function memo_foo(s: String): void diff --git a/arkoala-arkts/memo-plugin/demo/stub.ts b/arkoala-arkts/memo-plugin/demo/stub.ts new file mode 100644 index 0000000000000000000000000000000000000000..d036fa342baab8b02585f07280f3c542084c8eba --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/stub.ts @@ -0,0 +1,4 @@ +/** @memo */ +export function memo_foo(s: string) { + console.log(`MEMO FUNCTION FOO CALLED`) +} diff --git a/arkoala-arkts/memo-plugin/demo/tsconfig-unmemoize.json b/arkoala-arkts/memo-plugin/demo/tsconfig-unmemoize.json new file mode 100644 index 0000000000000000000000000000000000000000..afa892abb3b8dfe26d92c70e60440da23166f68a --- /dev/null +++ b/arkoala-arkts/memo-plugin/demo/tsconfig-unmemoize.json @@ -0,0 +1,17 @@ +{ + "extends": "@koalaui/build-common/tsconfig.json", + "compilerOptions": { + "outDir": "build/junk", + "rootDir" : ".", + "module": "CommonJS", + "plugins": [ + { + "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", + "trace": false, + "only_unmemoize": true, + "unmemoizeDir": "build/unmemoized" + } + ] + }, + "include": ["./demo.ts", "./stub.ts"] +} diff --git a/arkoala-arkts/memo-plugin/package.json b/arkoala-arkts/memo-plugin/package.json new file mode 100644 index 0000000000000000000000000000000000000000..83de11ca6bc80b882a51ae4cf7f3b9770835d74c --- /dev/null +++ b/arkoala-arkts/memo-plugin/package.json @@ -0,0 +1,10 @@ +{ + "name": "@koalaui/memo-plugin", + "main": "build/src/memo-transformer.js", + "scripts": { + "compile": "tsc -b .", + "compile:libarkts": "npm run compile --prefix ../libarkts", + "demo:run": "npm run clean --prefix demo && npm run compile && npm run ts-like:compile --prefix demo && npm run arkts-like:compile:capi --prefix demo && npm run run --prefix demo", + "demo:disasm": "npm run disasm --prefix demo" + } +} \ No newline at end of file diff --git a/arkoala-arkts/memo-plugin/runtime-api/common.sts b/arkoala-arkts/memo-plugin/runtime-api/common.sts new file mode 100644 index 0000000000000000000000000000000000000000..f8a1d055d837444674815032bf99e4d4e0e455bd --- /dev/null +++ b/arkoala-arkts/memo-plugin/runtime-api/common.sts @@ -0,0 +1 @@ +export type KoalaCallsiteKey = int diff --git a/arkoala-arkts/memo-plugin/runtime-api/index.sts b/arkoala-arkts/memo-plugin/runtime-api/index.sts new file mode 100644 index 0000000000000000000000000000000000000000..90d3cc3aacccbcdd51797cc51d47a28a1b1385df --- /dev/null +++ b/arkoala-arkts/memo-plugin/runtime-api/index.sts @@ -0,0 +1,10 @@ +import { KoalaCallsiteKey } from "./common" +import { StateContext } from "./states/State" + +export { memoEntry } from "./memo/entry" +export { GlobalStateManager } from "./states/GlobalStateManager" +export { StateManager, State, Disposable, ComputableState, StateContext } from "./states/State" + +export @interface memo {} +export type __memo_context_type = StateContext +export type __memo_id_type = KoalaCallsiteKey diff --git a/arkoala-arkts/memo-plugin/runtime-api/memo/entry.sts b/arkoala-arkts/memo-plugin/runtime-api/memo/entry.sts new file mode 100644 index 0000000000000000000000000000000000000000..0c75014655b81ac76167ad32470d82cf11872a72 --- /dev/null +++ b/arkoala-arkts/memo-plugin/runtime-api/memo/entry.sts @@ -0,0 +1,4 @@ +import { KoalaCallsiteKey } from "../common" +import { StateContext } from "../states/State" + +export declare function memoEntry(__memo_context: StateContext, __memo_id: KoalaCallsiteKey, entry: (__memo_context: StateContext, __memo_id: KoalaCallsiteKey) => R): R diff --git a/arkoala-arkts/memo-plugin/runtime-api/states/GlobalStateManager.sts b/arkoala-arkts/memo-plugin/runtime-api/states/GlobalStateManager.sts new file mode 100644 index 0000000000000000000000000000000000000000..bc62a899279be20d05d918901244acf4c927f47b --- /dev/null +++ b/arkoala-arkts/memo-plugin/runtime-api/states/GlobalStateManager.sts @@ -0,0 +1,5 @@ +import { StateManager } from "./State" + +export declare class GlobalStateManager { + static get instance(): StateManager +} diff --git a/arkoala-arkts/memo-plugin/runtime-api/states/State.sts b/arkoala-arkts/memo-plugin/runtime-api/states/State.sts new file mode 100644 index 0000000000000000000000000000000000000000..361be38504d6237800663b04478815d01d446e0d --- /dev/null +++ b/arkoala-arkts/memo-plugin/runtime-api/states/State.sts @@ -0,0 +1,13 @@ +export declare interface StateManager extends StateContext { } +export declare interface State { + modified: boolean + value: Value +} +export interface Disposable { + disposed: boolean + dispose(): void +} +export declare interface ComputableState extends Disposable, State { } +export declare interface StateContext { + computableState(compute: (context: StateContext) => Value, cleanup?: (context: StateContext, value: Value | undefined) => void): ComputableState +} diff --git a/arkoala-arkts/memo-plugin/src/AbstractVisitor.ts b/arkoala-arkts/memo-plugin/src/AbstractVisitor.ts new file mode 100644 index 0000000000000000000000000000000000000000..90c6c1665def145ee60363ef6b284e054ff022a2 --- /dev/null +++ b/arkoala-arkts/memo-plugin/src/AbstractVisitor.ts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * 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. + */ + +import * as arkts from "@koalaui/libarkts" + +export abstract class AbstractVisitor { + constructor( + ) {} + + indentation = 0 + + withIndentation(exec: () => T) { + this.indentation++ + const result = exec() + this.indentation-- + return result + } + + abstract visitor(node: arkts.AstNode): arkts.AstNode + + visitEachChild(node: arkts.AstNode): arkts.AstNode { + return this.withIndentation(() => + arkts.visitEachChild( + node, + it => this.visitor(it) + ) + ) + } +} diff --git a/arkoala-arkts/memo-plugin/src/import-transformer.ts b/arkoala-arkts/memo-plugin/src/import-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..4037bcf65925b2c390206004259a29a62b21840b --- /dev/null +++ b/arkoala-arkts/memo-plugin/src/import-transformer.ts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * 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. + */ + +import * as arkts from "@koalaui/libarkts" +import { AbstractVisitor } from "./AbstractVisitor" +import { createContextTypeImportSpecifier, createIdTypeImportSpecifier, RuntimeNames } from "./utils" + +function createContextTypesImportDeclaration(): arkts.EtsImportDeclaration { + return arkts.factory.createImportDeclaration( + arkts.factory.createStringLiteral(RuntimeNames.CONTEXT_TYPE_DEFAULT_IMPORT), + [createContextTypeImportSpecifier(), createIdTypeImportSpecifier()], + arkts.Es2pandaImportKinds.IMPORT_KINDS_TYPE, + true, + ) +} + +export class ImportTransformer extends AbstractVisitor { + visitor(node: arkts.AstNode): arkts.AstNode { + if (node instanceof arkts.EtsScript) { + return arkts.factory.updateEtsScript( + node, + [ + ...node.getChildren().filter(it => it instanceof arkts.EtsImportDeclaration), + createContextTypesImportDeclaration(), + ...node.getChildren().filter(it => !(it instanceof arkts.EtsImportDeclaration)), + ] + ) + } + return node + } +} diff --git a/arkoala-arkts/memo-plugin/src/memo-detector.ts b/arkoala-arkts/memo-plugin/src/memo-detector.ts new file mode 100644 index 0000000000000000000000000000000000000000..db447f07c7899ab623c433fff44a9ab8148193d8 --- /dev/null +++ b/arkoala-arkts/memo-plugin/src/memo-detector.ts @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * 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. + */ + +import * as arkts from "@koalaui/libarkts" +import { AbstractVisitor } from "./AbstractVisitor" +import { createContextParameter, createIdParameter, RuntimeNames } from "./utils" + +export function hasMemoAnnotation(node: arkts.ScriptFunction) { + return arkts.getAnnotations(node).some((it) => + arkts.isIdentifier(it.expr) && it.expr.name === RuntimeNames.ANNOTATION + ) +} + +export function createHiddenParameters(withType: boolean): arkts.ETSParameterExpression[] { + return [createContextParameter(withType), createIdParameter(withType)] +} + +export class MemoDetector extends AbstractVisitor { + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + // TODO: Remove (currently annotations are lost on visitor) + const methodDefinitionHasMemoAnnotation = + beforeChildren instanceof arkts.MethodDefinition && hasMemoAnnotation(beforeChildren.scriptFunction) + + const node = this.visitEachChild(beforeChildren) + console.log(" ".repeat(this.indentation) + node.constructor.name) + if (node instanceof arkts.MethodDefinition) { + if (methodDefinitionHasMemoAnnotation) { + console.log("MEMO SCRIPT FUNCTION") + // TODO: fix + const updatedNode = arkts.factory.updateMethodDefinition( + node, + 2, + node.name, + arkts.factory.createFunctionExpression( + arkts.factory.updateScriptFunction( + node.scriptFunction, + node.scriptFunction.body, + undefined, + node.scriptFunction.scriptFunctionFlags, + node.scriptFunction.modifiers, + false, + node.scriptFunction.ident, + [...createHiddenParameters(true), ...node.scriptFunction.parameters] + ), + ), + node.modifiers, + false + ) + console.log("END") + return updatedNode + } + } + if (node instanceof arkts.CallExpression) { + const expr = node.expression + const decl = arkts.getDecl(expr) + if (decl instanceof arkts.MethodDefinition && hasMemoAnnotation(decl.scriptFunction)) { + return arkts.factory.updateCallExpression( + node, + node.expression, + undefined, + [...createHiddenParameters(false), ...node.arguments] + ) + } + } + return node + } +} diff --git a/arkoala-arkts/memo-plugin/src/memo-transformer.ts b/arkoala-arkts/memo-plugin/src/memo-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..1975ec2be9003f2eaceb87b1068b505c9bf0a22c --- /dev/null +++ b/arkoala-arkts/memo-plugin/src/memo-transformer.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * 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. + */ + +import * as arkts from "@koalaui/libarkts" +import { MemoDetector } from "./memo-detector" +import { ImportTransformer } from "./import-transformer" + +export interface TransformerOptions { + trace?: boolean, +} + +export default function memoTransformer( + userPluginOptions?: TransformerOptions +) { + return (node: arkts.EtsScript) => { + const memoDetector = new MemoDetector() + memoDetector.visitor(node) + const importTransformer = new ImportTransformer() + return importTransformer.visitor(node) + } +} diff --git a/arkoala-arkts/memo-plugin/src/utils.ts b/arkoala-arkts/memo-plugin/src/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e3c4b6065f8430afde49f2e41ae556d53bb8e91 --- /dev/null +++ b/arkoala-arkts/memo-plugin/src/utils.ts @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * 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. + */ + +import * as arkts from "@koalaui/libarkts" + +export enum RuntimeNames { + __CONTEXT = "__context", + __ID = "__id", + ANNOTATION = "memo", + CONTEXT = "__memo_context", + CONTEXT_TYPE = "__memo_context_type", + CONTEXT_TYPE_DEFAULT_IMPORT = "@koalaui/runtime", + ID = "__memo_id", + ID_TYPE = "__memo_id_type", +} + +export function createContextTypeImportSpecifier(): arkts.ImportSpecifier { + return arkts.factory.createImportSpecifier( + arkts.factory.createIdentifier(RuntimeNames.CONTEXT_TYPE), + arkts.factory.createIdentifier(RuntimeNames.CONTEXT_TYPE), + ) +} + +export function createIdTypeImportSpecifier(): arkts.ImportSpecifier { + return arkts.factory.createImportSpecifier( + arkts.factory.createIdentifier(RuntimeNames.ID_TYPE), + arkts.factory.createIdentifier(RuntimeNames.ID_TYPE), + ) +} + +export function createContextParameter(withType: boolean): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(RuntimeNames.CONTEXT, + withType ? arkts.factory.createIdentifier(RuntimeNames.CONTEXT_TYPE) : undefined + ), + undefined + ) +} + +export function createIdParameter(withType: boolean): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(RuntimeNames.ID, + withType ? arkts.factory.createIdentifier(RuntimeNames.ID_TYPE) : undefined + ), + undefined + ) +} diff --git a/arkoala-arkts/memo-plugin/tsconfig.json b/arkoala-arkts/memo-plugin/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..ef0efa64d715c743770f9853c3923a6cb73e92ea --- /dev/null +++ b/arkoala-arkts/memo-plugin/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@koalaui/build-common/tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "baseUrl": ".", + "outDir": "./build", + "module": "CommonJS" + }, + "include": [ + "./src/**/*.ts", + ] +}