diff --git a/ui2abc/libarkts/native/src/bridges.cc b/ui2abc/libarkts/native/src/bridges.cc index 382bb5b2f71ec2ed45715428d9cc18b84e5f40e1..0d454e70d08fc2de3d2940be7474e4b8f2da7053 100644 --- a/ui2abc/libarkts/native/src/bridges.cc +++ b/ui2abc/libarkts/native/src/bridges.cc @@ -224,6 +224,16 @@ static KNativePointer impl_ProgramExternalSources(KNativePointer contextPtr, KNa } KOALA_INTEROP_2(ProgramExternalSources, KNativePointer, KNativePointer, KNativePointer); +static KNativePointer impl_ProgramDirectExternalSources(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto&& instance = reinterpret_cast(instancePtr); + std::size_t source_len = 0; + auto external_sources = GetImpl()->ProgramDirectExternalSources(context, instance, &source_len); + return StageArena::cloneVector(external_sources, source_len); +} +KOALA_INTEROP_2(ProgramDirectExternalSources, KNativePointer, KNativePointer, KNativePointer); + static KNativePointer impl_ProgramSourceFilePath(KNativePointer contextPtr, KNativePointer instancePtr) { auto context = reinterpret_cast(contextPtr); @@ -332,3 +342,29 @@ KNativePointer impl_CreateSourceRange(KNativePointer context, KNativePointer sta return GetImpl()->CreateSourceRange(_context_, _start_, _end_); } KOALA_INTEROP_3(CreateSourceRange, KNativePointer, KNativePointer, KNativePointer, KNativePointer); + + +static es2panda_Context* currentContext = nullptr; +static es2panda_AstNode* currentNode = nullptr; + +static es2panda_AstNode* cloneSubtree(es2panda_Context* context, es2panda_AstNode* oldCurrent, es2panda_AstNode* newParent) { + es2panda_AstNode* newCurrent = GetImpl()->AstNodeClone(context, oldCurrent, newParent); + currentContext = context; + currentNode = newCurrent; + GetImpl()->AstNodeIterateConst(context, oldCurrent, [] (es2panda_AstNode* oldChild) -> void { + auto* newChild = cloneSubtree(currentContext, oldChild, currentNode); + //GetImpl()->AstNodeAddChild(currentContext, currentNode, newChild); + }); + currentContext = nullptr; + currentNode = nullptr; + return newCurrent; +} +KNativePointer impl_AstNodeDeepClone(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + auto result = GetImpl()->AstNodeClone(_context, _receiver, _receiver); // cloneSubtree(_context, _receiver, nullptr); + //printf("CLONED %s\n", GetImpl()->AstNodeDumpJSONConst(_context, result)); + return result; +} +KOALA_INTEROP_2(AstNodeDeepClone, KNativePointer, KNativePointer, KNativePointer); diff --git a/ui2abc/libarkts/src-host/es2panda.ts b/ui2abc/libarkts/src-host/es2panda.ts index 3e7b89fdef2d8e71395da142605d9c2fd84ecc75..a0471a814cf9ac197c543ab7739627a289056bec 100644 --- a/ui2abc/libarkts/src-host/es2panda.ts +++ b/ui2abc/libarkts/src-host/es2panda.ts @@ -78,7 +78,7 @@ function insertPlugin( console.log(filterSource(script.dumpSrc())) } - + global.profiler.curPlugin = pluginName global.profiler.transformStarted() @@ -93,7 +93,7 @@ function insertPlugin( return !restart } }) - + global.profiler.transformEnded(state, pluginName) global.profiler.curPlugin = "" diff --git a/ui2abc/libarkts/src/Es2pandaNativeModule.ts b/ui2abc/libarkts/src/Es2pandaNativeModule.ts index d96eaff104e467d73f17cdfb6943f9bb133fd8de..429903418d182b2e6ef195062bf32c22a9065de8 100644 --- a/ui2abc/libarkts/src/Es2pandaNativeModule.ts +++ b/ui2abc/libarkts/src/Es2pandaNativeModule.ts @@ -40,6 +40,9 @@ export class Es2pandaNativeModule { _AstNodeRecheck(context: KPtr, node: KPtr): void { throw new Error("Not implemented") } + _AstNodeDeepClone(context: KPtr, node: KPtr): KPtr { + throw new Error("Not implemented") + } _ContextState(context: KPtr): KInt { throw new Error("Not implemented") } @@ -103,6 +106,9 @@ export class Es2pandaNativeModule { _ProgramExternalSources(context: KNativePointer, instance: KNativePointer): KNativePointer { throw new Error("Not implemented"); } + _ProgramDirectExternalSources(context: KNativePointer, instance: KNativePointer): KNativePointer { + throw new Error("Not implemented"); + } _ExternalSourceName(instance: KNativePointer): KNativePointer { throw new Error("Not implemented"); } diff --git a/ui2abc/libarkts/src/arkts-api/ProgramProvider.ts b/ui2abc/libarkts/src/arkts-api/ProgramProvider.ts index f73298098df1dbaa1b686b0a4fa05052ae7fd844..5480ee61609d9d53403b65c4ea00b87fcfa21bed 100644 --- a/ui2abc/libarkts/src/arkts-api/ProgramProvider.ts +++ b/ui2abc/libarkts/src/arkts-api/ProgramProvider.ts @@ -15,6 +15,8 @@ import { KNativePointer } from "@koalaui/interop" import { defaultFilter, listPrograms, ProgramWithName } from "./plugins" +import { Program } from "../generated" +import { programGetDirectExternalSources, programGetExternalSources } from "./node-utilities/Program" export class ProgramProvider { // TODO: migrate to wrappers instead of pointers @@ -26,6 +28,31 @@ export class ProgramProvider { this.queue = [mainProgram] } + static dumpDeps(program: Program): string { + let seen = new Set() + let queue: Program[] = [] + let result = `digraph "Dependencies of ${program.absoluteName}" {` + queue.push(program) + while (queue.length > 0) { + const current = queue.shift()! + const listed = programGetExternalSources(current) + listed.forEach(source => { + if (!defaultFilter(source.getName())) return + source.programs.forEach(program => { + if (program.peer != current.peer) { + result += `"${current.fileName}" -> "${program.fileName}";\n` + if (!seen.has(program.peer)) { + seen.add(program.peer) + queue.push(program) + } + } + }) + }) + } + result += "}" + return result + } + updateQueue() { const listed = listPrograms(this.mainProgram.program, this.filter) for (const program of listed) { diff --git a/ui2abc/libarkts/src/arkts-api/node-utilities/Program.ts b/ui2abc/libarkts/src/arkts-api/node-utilities/Program.ts index 4e80fbb30fda591091c9df43a81543867f571f2c..a9c7bad9816dca31b6d007fa5f89cad037c1b321 100644 --- a/ui2abc/libarkts/src/arkts-api/node-utilities/Program.ts +++ b/ui2abc/libarkts/src/arkts-api/node-utilities/Program.ts @@ -25,3 +25,10 @@ export function programGetExternalSources(program: Program): ExternalSource[] { (instance: KNativePointer) => new ExternalSource(instance) ) } + +export function programGetDirectExternalSources(program: Program): ExternalSource[] { + return acceptNativeObjectArrayResult( + global.es2panda._ProgramDirectExternalSources(global.context, program.peer), + (instance: KNativePointer) => new ExternalSource(instance) + ) +} diff --git a/ui2abc/libarkts/src/arkts-api/peers/AstNode.ts b/ui2abc/libarkts/src/arkts-api/peers/AstNode.ts index 55fbcf5a0bf6046a026cdc1f29e35ff879572011..c00f242496fe50474d0ed315849deda78b53d942 100644 --- a/ui2abc/libarkts/src/arkts-api/peers/AstNode.ts +++ b/ui2abc/libarkts/src/arkts-api/peers/AstNode.ts @@ -95,8 +95,13 @@ export abstract class AstNode extends ArktsObject { return unpackString(global.es2panda._AstNodeDumpModifiers(global.context, this.peer)) } - public clone(): this { - return unpackNonNullableNode(global.generatedEs2panda._AstNodeClone(global.context, this.peer, this.parent.peer)); + public clone(parent: KNativePointer = nullptr): this { + return unpackNonNullableNode(global.generatedEs2panda._AstNodeClone( + global.context, this.peer, parent)) + } + + public deepClone(): this { + return unpackNonNullableNode(global.es2panda._AstNodeDeepClone(global.context, this.peer)) } public get parent(): AstNode { diff --git a/ui2abc/libarkts/src/plugin-utils.ts b/ui2abc/libarkts/src/plugin-utils.ts index 1f33c3c4c47ee33d2a92cf3ca2f484f388c74a30..4339bf1ee3aaac10a7a7887be15c57e3410bb8c8 100644 --- a/ui2abc/libarkts/src/plugin-utils.ts +++ b/ui2abc/libarkts/src/plugin-utils.ts @@ -24,9 +24,13 @@ import { Program, inferVoidReturnType, ETSModule, - ProgramProvider + ProgramProvider, + BlockStatement, + AbstractVisitor, + isBlockStatement } from "./arkts-api" -import { AstNode } from "./reexport-for-generated" +import { AstNode, KNativePointer } from "./reexport-for-generated" +import { factory } from "./arkts-api/factory/nodeFactory" export interface RunTransformerContext { isMainProgram: boolean @@ -37,11 +41,57 @@ export interface RunTransformerHooks { onProgramTransformEnd?(ctx: RunTransformerContext): boolean } +class CloneVisitor extends AbstractVisitor { + visitor(node: AstNode, options?: object): AstNode { + return this.visitEachChild(node) + } +} + +function deepClone(ast: AstNode): AstNode { + if (true) + return ast.clone() + else + return new CloneVisitor().visitor(ast) +} + +class ASTCacheEntry { + programToTree = new Map() + constructor() {} + find(program: Program): AstNode|undefined { + return this.programToTree.get(program.peer) + } + update(program: Program, ast: AstNode) { + this.programToTree.set(program.peer, deepClone(ast)) + } +} +class ASTCache { + cache = new Map() + find(transformer: ProgramTransformer, program: Program) : AstNode|undefined { + return this.cache.get(transformer)?.find(program) + } + update(transformer: ProgramTransformer, program: Program, ast: AstNode) { + let entry = this.cache.get(transformer) + if (!entry) { + entry = new ASTCacheEntry() + this.cache.set(transformer, entry) + } + entry.update(program, ast) + } +} + +let verbose = false + export function runTransformer(prog: Program, state: Es2pandaContextState, transform: ProgramTransformer, pluginContext: PluginContext, hooks: RunTransformerHooks = {}) { + if (verbose) console.log(`RUN TRANSFORMER on ${prog.absoluteName}`) + // Visit deps const provider = new ProgramProvider({ program: prog, name: `${arktsGlobal.packageName}.${arktsGlobal.filePathFromPackageRoot}` }) let currentProgram = provider.next() - + let cache = pluginContext.parameter("ASTCache") + if (!cache) { + cache = new ASTCache() + pluginContext.setParameter("ASTCache", cache) + } while (currentProgram) { const runCtx: RunTransformerContext = { isMainProgram: currentProgram.program.peer == prog.peer @@ -53,8 +103,18 @@ export function runTransformer(prog: Program, state: Es2pandaContextState, trans const importStorage = new ImportStorage(currentProgram.program, state == Es2pandaContextState.ES2PANDA_STATE_PARSED) stageSpecificPreFilters(ast, state) - transform?.(currentProgram.program, { isMainProgram: runCtx.isMainProgram, name: currentProgram.name, stage: state }, pluginContext) + if (verbose) + console.log(`TRANSFORM on ${currentProgram.program.absoluteName} main is ${runCtx.isMainProgram}`) + let transformed = cache.find(transform, currentProgram.program) + if (transformed) { + console.log(`REUSE CACHED FOR ${currentProgram.program.absoluteName}`) + currentProgram.program.setAst(deepClone(transformed) as BlockStatement) + } else { + console.log(`NO CACHE FOR ${currentProgram.program.absoluteName}`) + transform(currentProgram.program, { isMainProgram: runCtx.isMainProgram, name: currentProgram.name, stage: state }, pluginContext) + cache.update(transform, currentProgram.program, currentProgram.program.ast) + } stageSpecificPostFilters(ast, state) setBaseOverloads(ast) importStorage.update() diff --git a/ui2abc/ui-plugins/src/checked-stage-plugin.ts b/ui2abc/ui-plugins/src/checked-stage-plugin.ts index 9e80c4e0b8597d0e4a6725f71e3a1763fd3eb7dd..1d3f0b45ff457045ca513da97db3cb69e234bace 100644 --- a/ui2abc/ui-plugins/src/checked-stage-plugin.ts +++ b/ui2abc/ui-plugins/src/checked-stage-plugin.ts @@ -28,9 +28,7 @@ export default function checkedTransformer( userPluginOptions?: TransformerOptions ): arkts.ProgramTransformer { return (program: arkts.Program, _compilationOptions: arkts.CompilationOptions, context: arkts.PluginContext) => { - const structsResolver = context.parameter("structsTable")! - const importer = context.parameter("importer")!; - + const structsResolver = context.parameter("structsTable")!; [ new InstantiateFactoryHelper(), new EtsFirstArgTransformer(),