Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getWidget in ispx #765

Closed
nighca opened this issue Aug 16, 2024 · 2 comments · Fixed by #767
Closed

getWidget in ispx #765

nighca opened this issue Aug 16, 2024 · 2 comments · Fixed by #767
Assignees

Comments

@nighca
Copy link
Collaborator

nighca commented Aug 16, 2024

#676 (comment)

Widget 目前有已知问题如下:

getWidget 的定义使用了泛型(见 Gopt_Game_Gopx_GetWidget),ispx 这里使用 qexp -outdir pkg github.com/goplus/spx 来生成 package spx 的导出信息供运行时使用;但 qexp 目前默认会跳过基于泛型的函数定义

➜ go generate
2024/08/15 20:12:28 process [github.com/goplus/spx]
2024/08/15 20:12:34 skip typeparam func github.com/goplus/spx.Gopt_Game_Gopx_GetWidget[T any](game interface{}, name string) *T
2024/08/15 20:12:34 export github.com/goplus/spx: pkg/github.com/goplus/spx/export.go
2024/08/15 20:12:34 process [github.com/hajimehoshi/ebiten/v2]
2024/08/15 20:12:39 export github.com/hajimehoshi/ebiten/v2: pkg/github.com/hajimehoshi/ebiten/v2/export.go

因此游戏的代码中无法调用 getWidget

qexp 目前默认会跳过基于泛型的函数定义

这个可能可以通过指定 --src 来避免跳过,但那样会有新的问题,暂时还没有找到解决办法

@nighca nighca self-assigned this Aug 16, 2024
@nighca
Copy link
Collaborator Author

nighca commented Aug 23, 2024

通过 --src 导出 spx 的问题

可以通过指定 --src 来避免跳过,但那样会有新的问题,暂时还没有找到解决办法

这里“新的问题”包括:

通过 --src 导出 package github.com/goplus/spx 意味着 igop 在 import 这个包的时候会基于源代码进行 parse & type-check 来得到 ast、types info 等并执行,因此 github.com/goplus/spx 依赖的其他 package 需要被提前导出并预加载(就像我们本来对 github.com/goplus/spx 做的那样)

github.com/goplus/spx 依赖的所有 package)

WeChatWorkScreenshot_8cb02ed1-7d7b-457d-ac2c-7789a6c4e104

如上图,github.com/goplus/spx 依赖的 package 很多,分别处理时本身会遇到其他的问题,比如路径中带 /internal/ 的包不可以被 igop 导出

方案 1

跟七叶讨论后,有一个方案是这样的:

考虑 igop 做的事情大体可以分为两个步骤:

  1. 将原 Go+ 代码转换为 Go 代码(这里会做类型检查)
  2. 执行转换后的 Go 代码(这里会做类型检查,也会真的执行)

可以继续使用不指定 -src 的导出(这时 github.com/goplus/spx 的导出中没有 Gopt_Game_Gopx_GetWidget 对应的类型声明),然后在 ispx 这里做步骤 1 的时候向 github.com/goplus/spx 对应的 types 中添加手工构造的 Gopt_Game_Gopx_GetWidget 类型信息(通过实现并替换 types loader);接着构造一个“虚拟的 package”(如 github.com/goplus/spx/widget),这个 package 的内容仅包含 Gopt_Game_Gopx_GetWidget 的代码,将其通过类似 AddImportFile 这样的方法添加进 ctx,并对步骤 1 的产物(Go 代码)进行处理,将其中对原函数(github.com/goplus/spx.Gopt_Game_Gopx_GetWidget)的调用替换为对 github.com/goplus/spx/widget.Gopt_Game_Gopx_GetWidget 的调用;新增的虚拟 package 会走基于源代码进行解析并执行的逻辑,但因为它很简单,因此不会遇到上面提到的问题

这个方案尝试后可以跑通,但是也有问题:ispx 这里的维护成本高;对于 spx 暴露的一个带泛型函数(如 Gopt_Game_Gopx_GetWidget),在 ispx 这里需要对应地维护两份内容:

  1. 构造 Gopt_Game_Gopx_GetWidget 类型信息的代码
  2. Gopt_Game_Gopx_GetWidget 挪到单独的 package 后的实现代码

方案 2

在跟 1 类似的逻辑基础上,可以做这样的优化:

  1. 向 igop 添加逻辑,基于导出时拿到的 Gopt_Game_Gopx_GetWidget 类型信息,对应地生成构造该类型信息的代码(见 Support type for generic functions igop#274 中的 GenericFuncTypeConstructors),输出到导出的产物中, 在 igop 运行时再通过 GenericFuncTypeConstructors 还原出类型信息并添加到 package types 中,这样可以让上面提到的步骤 1 通过

  2. 在 ispx 中通过 igop.RegisterExternalGopt_Game_Gopx_GetWidget 的实现添加进去,这样上面提到的步骤 2 可以通过,见 https://github.com/goplus/builder/pull/767/files#diff-4278954e9dad98c6a035f9a3060fa13f2ffbcc198094dec7a0ab1f4426b9c9a0

    igop.RegisterExternal 有已知的缺陷,作为函数实现被传入的 Gopt_Game_Gopx_GetWidget 需要先被(类型)具体化后才能传入,因此在这里需要遍历泛型参数可能的所有取值,分别 register 一遍;这个对 spx 目前的 Gopt_Game_Gopx_GetWidget 不是大问题,因为它的泛型参数支持的取值是可数且确定的(当前只有 spx.Monitor 一个可能的取值);但是这个对 igop 来说大概率不是一个具有通用性的方案

@nighca
Copy link
Collaborator Author

nighca commented Aug 28, 2024

方案 3

七叶给 igop 添加了 RegisterPackagePatch 以支持向已有的 package 添加内容,详见 goplus/igop#275 。这样我们可以:

  1. 以不指定 -src 的方式导出(这时 github.com/goplus/spx 的导出中没有 Gopt_Game_Gopx_GetWidget 对应的类型声明)
  2. 通过 RegisterPackagePatchgithub.com/goplus/spx 中以源码的方式添加 Gopt_Game_Gopx_GetWidget(其类型与实现均会被添加)
  3. 接着做原先的操作(包括类型检查、代码转换与执行)

这样我们在 ispx 这里维护一份 Gopt_Game_Gopx_GetWidget 的源码即可(这份源码以字符串格式被传入 RegisterPackagePatch

我们可以接着调整 spx 中 Gopt_Game_Gopx_GetWidget 的实现,把它的主要逻辑拆到一个不带泛型的函数(如 GetWidget_)中实现,这样 Gopt_Game_Gopx_GetWidget 的函数体就会比较简单,长期来看 ispx 的维护负担会低一些。

该方案的维护成本是可接受的,因为它跟 ispx 中现有的 Gopt_Game_MainGopt_Game_Run 的维护成本差不多是一个水平

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant