loader.go 9.0 KB
Newer Older
chai2010's avatar
chai2010 已提交
1 2 3 4 5 6
// 版权 @2021 凹语言 作者。保留所有权利。

package loader

import (
	"io/fs"
chai2010's avatar
chai2010 已提交
7
	"os"
chai2010's avatar
chai2010 已提交
8
	"path/filepath"
chai2010's avatar
chai2010 已提交
9 10 11 12 13 14 15 16 17 18 19 20 21
	"sort"
	"strings"

	"github.com/wa-lang/wa/internal/ast"
	"github.com/wa-lang/wa/internal/config"
	"github.com/wa-lang/wa/internal/logger"
	"github.com/wa-lang/wa/internal/parser"
	"github.com/wa-lang/wa/internal/ssa"
	"github.com/wa-lang/wa/internal/token"
	"github.com/wa-lang/wa/internal/types"
	"github.com/wa-lang/wa/internal/waroot"
)

chai2010's avatar
chai2010 已提交
22
type _Loader struct {
chai2010's avatar
chai2010 已提交
23 24
	cfg  config.Config
	vfs  config.PkgVFS
25
	prog *Program
chai2010's avatar
chai2010 已提交
26 27
}

28
func newLoader(cfg *config.Config) *_Loader {
chai2010's avatar
chai2010 已提交
29
	return &_Loader{
chai2010's avatar
chai2010 已提交
30
		cfg: *cfg.Clone(),
31
		prog: &Program{
chai2010's avatar
chai2010 已提交
32 33 34 35 36
			Pkgs: make(map[string]*Package),
		},
	}
}

chai2010's avatar
chai2010 已提交
37 38 39 40 41 42 43 44 45 46 47
func (p *_Loader) LoadProgramFile(filename string, src interface{}) (*Program, error) {
	logger.Tracef(&config.EnableTrace_loader, "cfg: %+v", p.cfg)
	logger.Tracef(&config.EnableTrace_loader, "filename: %v", filename)

	vfs, manifest, err := loadProgramFileMeta(&p.cfg, filename, src)
	if err != nil {
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
		return nil, err
	}

	return p.loadProgram(vfs, manifest)
chai2010's avatar
chai2010 已提交
48 49
}

50 51
func (p *_Loader) LoadProgram(appPath string) (*Program, error) {
	logger.Tracef(&config.EnableTrace_loader, "cfg: %+v", p.cfg)
chai2010's avatar
chai2010 已提交
52 53
	logger.Tracef(&config.EnableTrace_loader, "appPath: %s", appPath)

chai2010's avatar
chai2010 已提交
54 55 56 57 58 59 60 61 62 63
	if isWaFile(appPath) {
		vfs, manifest, err := loadProgramFileMeta(&p.cfg, appPath, nil)
		if err != nil {
			logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
			return nil, err
		}

		return p.loadProgram(vfs, manifest)
	}

chai2010's avatar
chai2010 已提交
64 65 66 67 68 69 70 71 72 73
	vfs, manifest, err := loadProgramMeta(&p.cfg, appPath)
	if err != nil {
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
		return nil, err
	}

	return p.loadProgram(vfs, manifest)
}

func (p *_Loader) LoadProgramVFS(vfs *config.PkgVFS, appPath string) (*Program, error) {
chai2010's avatar
chai2010 已提交
74
	manifest, err := config.LoadManifest(vfs.App, appPath)
chai2010's avatar
chai2010 已提交
75 76 77 78 79
	if err != nil {
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
		return nil, err
	}

chai2010's avatar
chai2010 已提交
80 81 82 83 84 85
	return p.loadProgram(vfs, manifest)
}

// 加载程序
func (p *_Loader) loadProgram(vfs *config.PkgVFS, manifest *config.Manifest) (*Program, error) {
	logger.DumpFS(&config.EnableTrace_loader, "vfs.app", vfs.App, ".")
chai2010's avatar
chai2010 已提交
86
	logger.Tracef(&config.EnableTrace_loader, "manifest: %s", manifest.JSONString())
chai2010's avatar
chai2010 已提交
87

chai2010's avatar
chai2010 已提交
88
	p.vfs = *vfs
chai2010's avatar
chai2010 已提交
89
	p.prog.Cfg = &p.cfg
90 91 92
	p.prog.Manifest = manifest
	p.prog.Fset = token.NewFileSet()

chai2010's avatar
chai2010 已提交
93 94 95 96 97 98 99 100
	if p.vfs.Std == nil {
		if p.cfg.WaRoot != "" {
			p.vfs.Std = os.DirFS(filepath.Join(p.cfg.WaRoot, "src"))
		} else {
			p.vfs.Std = waroot.GetFS()
		}
	}

chai2010's avatar
chai2010 已提交
101 102 103 104 105 106 107 108
	// import "runtime"
	logger.Trace(&config.EnableTrace_loader, "import runtime")
	if _, err := p.Import("runtime"); err != nil {
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
		return nil, err
	}

	// import "main"
chai2010's avatar
chai2010 已提交
109 110
	logger.Trace(&config.EnableTrace_loader, "import "+manifest.MainPkg)
	if _, err := p.Import(manifest.MainPkg); err != nil {
chai2010's avatar
chai2010 已提交
111 112 113 114 115
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
		return nil, err
	}

	// 转为 SSA
116
	p.prog.SSAProgram = ssa.NewProgram(p.prog.Fset, ssa.SanityCheckFunctions)
chai2010's avatar
chai2010 已提交
117

118
	for pkgpath, pkg := range p.prog.Pkgs {
chai2010's avatar
chai2010 已提交
119 120 121
		logger.Tracef(&config.EnableTrace_loader, "build SSA; pkgpath: %v", pkgpath)

		if err := p.buildSSA(pkgpath); err != nil {
chai2010's avatar
chai2010 已提交
122
			logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
123
			return p.prog, err
chai2010's avatar
chai2010 已提交
124 125
		}

chai2010's avatar
chai2010 已提交
126
		if pkgpath == manifest.MainPkg {
127
			p.prog.SSAMainPkg = pkg.SSAPkg
chai2010's avatar
chai2010 已提交
128 129 130
		}
	}

chai2010's avatar
chai2010 已提交
131
	logger.Trace(&config.EnableTrace_loader, "return ok")
132
	return p.prog, nil
chai2010's avatar
chai2010 已提交
133 134
}

chai2010's avatar
chai2010 已提交
135
func (p *_Loader) buildSSA(pkgpath string) error {
136
	pkg := p.prog.Pkgs[pkgpath]
chai2010's avatar
chai2010 已提交
137 138 139 140 141
	if pkg.SSAPkg != nil {
		return nil
	}

	for _, importPkg := range pkg.Pkg.Imports() {
142
		if p.prog.Pkgs[importPkg.Path()].SSAPkg == nil {
chai2010's avatar
chai2010 已提交
143
			if err := p.buildSSA(importPkg.Path()); err != nil {
chai2010's avatar
chai2010 已提交
144
				logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
chai2010's avatar
chai2010 已提交
145 146 147 148 149
				return err
			}
		}
	}

150
	pkg.SSAPkg = p.prog.SSAProgram.CreatePackage(pkg.Pkg, pkg.Files, pkg.Info, true)
chai2010's avatar
chai2010 已提交
151 152 153 154 155
	pkg.SSAPkg.Build()

	return nil
}

chai2010's avatar
chai2010 已提交
156
func (p *_Loader) Import(pkgpath string) (*types.Package, error) {
chai2010's avatar
chai2010 已提交
157 158
	logger.Tracef(&config.EnableTrace_loader, "pkgpath: %v", pkgpath)

159
	if pkg, ok := p.prog.Pkgs[pkgpath]; ok {
chai2010's avatar
chai2010 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
		return pkg.Pkg, nil
	}

	var err error
	var pkg Package

	// 解析当前包到 AST
	pkg.Files, err = p.ParseDir(pkgpath)
	if err != nil {
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
		return nil, err
	}

	// 修复 pkg 名称(wa 是可选)
	for _, f := range pkg.Files {
		if f.Name.Name == "" {
176
			if pkgpath == p.prog.Manifest.MainPkg {
chai2010's avatar
chai2010 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
				f.Name.Name = "main"
			} else {
				pkgname := pkgpath
				if idx := strings.LastIndex(pkgname, "/"); idx != -1 {
					pkgname = pkgname[idx+1:]
				}
				f.Name.Name = pkgname
			}
		}
	}

	pkg.Info = &types.Info{
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Implicits:  make(map[ast.Node]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
		Scopes:     make(map[ast.Node]*types.Scope),
	}

	conf := types.Config{
		Importer: p,
		Sizes:    p.getSizes(),
	}
201
	pkg.Pkg, err = conf.Check(pkgpath, p.prog.Fset, pkg.Files, pkg.Info)
chai2010's avatar
chai2010 已提交
202 203 204 205 206 207 208
	if err != nil {
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
		return nil, err
	}

	logger.Tracef(&config.EnableTrace_loader, "save pkgpath: %v", pkgpath)

209
	p.prog.Pkgs[pkgpath] = &pkg
chai2010's avatar
chai2010 已提交
210 211 212
	return pkg.Pkg, nil
}

chai2010's avatar
chai2010 已提交
213
func (p *_Loader) ParseDir(pkgpath string) ([]*ast.File, error) {
chai2010's avatar
chai2010 已提交
214 215 216 217 218 219 220 221 222 223
	logger.Tracef(&config.EnableTrace_loader, "pkgpath: %v", pkgpath)

	var (
		filenames []string
		datas     [][]byte
		err       error
	)

	switch {
	case p.isStdPkg(pkgpath):
chai2010's avatar
chai2010 已提交
224
		logger.Tracef(&config.EnableTrace_loader, "isStdPkg; pkgpath: %v", pkgpath)
chai2010's avatar
chai2010 已提交
225

chai2010's avatar
chai2010 已提交
226
		filenames, datas, err = p.readDirFiles(p.vfs.Std, pkgpath)
chai2010's avatar
chai2010 已提交
227
		if err != nil {
chai2010's avatar
chai2010 已提交
228
			logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
chai2010's avatar
chai2010 已提交
229 230 231
			return nil, err
		}
	case p.isSelfPkg(pkgpath):
chai2010's avatar
chai2010 已提交
232 233 234
		relpkg := strings.TrimPrefix(pkgpath, p.prog.Manifest.Pkg.Pkgpath)
		if relpkg == "" {
			relpkg = "."
chai2010's avatar
chai2010 已提交
235 236
		}

chai2010's avatar
chai2010 已提交
237
		logger.Tracef(&config.EnableTrace_loader, "isSelfPkg; pkgpath=%v, relpkg=%v", pkgpath, relpkg)
chai2010's avatar
chai2010 已提交
238

chai2010's avatar
chai2010 已提交
239
		filenames, datas, err = p.readDirFiles(p.vfs.App, relpkg)
chai2010's avatar
chai2010 已提交
240
		if err != nil {
chai2010's avatar
chai2010 已提交
241
			logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
chai2010's avatar
chai2010 已提交
242 243 244 245 246 247
			return nil, err
		}

		logger.Trace(&config.EnableTrace_loader, "isSelfPkg; return ok")

	default: // vendor
chai2010's avatar
chai2010 已提交
248 249 250
		logger.Tracef(&config.EnableTrace_loader, "vendorPkg; pkgpath: %v", pkgpath)

		filenames, datas, err = p.readDirFiles(p.vfs.Vendor, pkgpath)
chai2010's avatar
chai2010 已提交
251
		if err != nil {
chai2010's avatar
chai2010 已提交
252
			logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
chai2010's avatar
chai2010 已提交
253 254 255 256 257 258 259 260
			return nil, err
		}
	}

	logger.Tracef(&config.EnableTrace_loader, "filenames: %v", filenames)

	var files []*ast.File
	for i, filename := range filenames {
261
		f, err := parser.ParseFile(nil, p.prog.Fset, filename, datas[i], parser.AllErrors)
chai2010's avatar
chai2010 已提交
262 263 264
		if err != nil {
			logger.Tracef(&config.EnableTrace_loader, "filename: %v", filename)
			logger.Tracef(&config.EnableTrace_loader, "datas[i]: %s", datas[i])
chai2010's avatar
chai2010 已提交
265
			logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
chai2010's avatar
chai2010 已提交
266 267 268 269 270 271 272 273 274

			return nil, err
		}
		files = append(files, f)
	}

	return files, nil
}

chai2010's avatar
chai2010 已提交
275
func (p *_Loader) readDirFiles(fileSystem fs.FS, path string) (filenames []string, datas [][]byte, err error) {
chai2010's avatar
chai2010 已提交
276 277
	path = strings.TrimPrefix(path, "/")

chai2010's avatar
chai2010 已提交
278 279 280 281
	logger.Tracef(&config.EnableTrace_loader, "path: %v", path)

	dirEntries, err := fs.ReadDir(fileSystem, path)
	if err != nil {
chai2010's avatar
chai2010 已提交
282 283
		logger.Tracef(&config.EnableTrace_loader, "path: %v", path)
		logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
chai2010's avatar
chai2010 已提交
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
		return nil, nil, err
	}

	for i, entry := range dirEntries {
		logger.Tracef(&config.EnableTrace_loader, "%d: path=%v", i, entry.Name())

		if entry.IsDir() {
			continue
		}
		if strings.HasPrefix(entry.Name(), "_") {
			continue
		}
		if !p.hasExt(entry.Name(), ".go", ".ugo", ".wa") {
			continue
		}

		filenames = append(filenames, entry.Name())
	}

	logger.Tracef(&config.EnableTrace_loader, "filenames=%v", filenames)

	sort.Strings(filenames)
	for _, name := range filenames {
chai2010's avatar
chai2010 已提交
307 308 309 310 311 312 313 314
		var fpath string
		if path != "" && path != "." {
			fpath = strings.TrimPrefix(filepath.Join(path, name), "/")
		} else {
			fpath = strings.TrimPrefix(name, "/")
		}

		data, err := fs.ReadFile(fileSystem, fpath)
chai2010's avatar
chai2010 已提交
315
		if err != nil {
chai2010's avatar
chai2010 已提交
316
			logger.Tracef(&config.EnableTrace_loader, "fpath: %v", fpath)
chai2010's avatar
chai2010 已提交
317 318 319 320 321 322 323 324 325 326 327
			logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
			return nil, nil, err
		}
		datas = append(datas, data)
	}

	logger.Trace(&config.EnableTrace_loader, "return ok")

	return filenames, datas, nil
}

chai2010's avatar
chai2010 已提交
328
func (p *_Loader) hasExt(name string, extensions ...string) bool {
chai2010's avatar
chai2010 已提交
329 330 331 332 333 334 335 336
	for _, ext := range extensions {
		if strings.HasSuffix(name, ext) {
			return true
		}
	}
	return false
}

chai2010's avatar
chai2010 已提交
337
func (p *_Loader) isStdPkg(pkgpath string) bool {
chai2010's avatar
chai2010 已提交
338 339 340
	return waroot.IsStdPkg(pkgpath)
}

chai2010's avatar
chai2010 已提交
341
func (p *_Loader) isSelfPkg(pkgpath string) bool {
342
	if pkgpath == p.prog.Manifest.Pkg.Pkgpath {
chai2010's avatar
chai2010 已提交
343 344
		return true
	}
345
	if strings.HasPrefix(pkgpath, p.prog.Manifest.Pkg.Pkgpath+"/") {
chai2010's avatar
chai2010 已提交
346 347 348 349 350
		return true
	}
	return false
}

chai2010's avatar
chai2010 已提交
351
func (p *_Loader) getSizes() types.Sizes {
chai2010's avatar
chai2010 已提交
352
	var zero config.StdSizes
353 354
	if p == nil || p.cfg.WaSizes == zero {
		return types.SizesFor(p.cfg.WaArch)
chai2010's avatar
chai2010 已提交
355 356
	} else {
		return &types.StdSizes{
357 358
			WordSize: p.cfg.WaSizes.WordSize,
			MaxAlign: p.cfg.WaSizes.MaxAlign,
chai2010's avatar
chai2010 已提交
359 360 361
		}
	}
}