github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/compiler/compiler.go (about) 1 package compiler 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/gob" 7 "encoding/json" 8 "fmt" 9 "go/token" 10 "go/types" 11 "io" 12 "strings" 13 14 "github.com/goplusjs/gopherjs/compiler/prelude" 15 "golang.org/x/tools/go/gcexportdata" 16 ) 17 18 var sizes32 = &types.StdSizes{WordSize: 4, MaxAlign: 8} 19 var reservedKeywords = make(map[string]bool) 20 21 func init() { 22 for _, keyword := range []string{"abstract", "arguments", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "eval", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", "undefined", "var", "void", "volatile", "while", "with", "yield"} { 23 reservedKeywords[keyword] = true 24 } 25 } 26 27 type ErrorList []error 28 29 func (err ErrorList) Error() string { 30 return err[0].Error() 31 } 32 33 type Archive struct { 34 ImportPath string 35 Name string 36 Version string 37 Imports []string 38 ExportData []byte 39 Declarations []*Decl 40 IncJSCode []byte 41 FileSet []byte 42 Minified bool 43 } 44 45 type Decl struct { 46 FullName string 47 Vars []string 48 DeclCode []byte 49 MethodListCode []byte 50 TypeInitCode []byte 51 InitCode []byte 52 DceObjectFilter string 53 DceMethodFilter string 54 DceDeps []string 55 Blocking bool 56 } 57 58 type Dependency struct { 59 Pkg string 60 Type string 61 Method string 62 } 63 64 func ImportDependencies(archive *Archive, importPkg func(string) (*Archive, error)) ([]*Archive, error) { 65 var deps []*Archive 66 paths := make(map[string]bool) 67 var collectDependencies func(path string) error 68 collectDependencies = func(path string) error { 69 if paths[path] { 70 return nil 71 } 72 dep, err := importPkg(path) 73 if err != nil { 74 return err 75 } 76 for _, imp := range dep.Imports { 77 if err := collectDependencies(imp); err != nil { 78 return err 79 } 80 } 81 deps = append(deps, dep) 82 paths[dep.ImportPath] = true 83 return nil 84 } 85 86 if err := collectDependencies("runtime"); err != nil { 87 return nil, err 88 } 89 for _, imp := range archive.Imports { 90 if err := collectDependencies(imp); err != nil { 91 return nil, err 92 } 93 } 94 95 deps = append(deps, archive) 96 return deps, nil 97 } 98 99 type dceInfo struct { 100 decl *Decl 101 objectFilter string 102 methodFilter string 103 } 104 105 func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error { 106 mainPkg := pkgs[len(pkgs)-1] 107 minify := mainPkg.Minified 108 109 byFilter := make(map[string][]*dceInfo) 110 var pendingDecls []*Decl 111 for _, pkg := range pkgs { 112 for _, d := range pkg.Declarations { 113 if d.DceObjectFilter == "" && d.DceMethodFilter == "" { 114 pendingDecls = append(pendingDecls, d) 115 continue 116 } 117 info := &dceInfo{decl: d} 118 if d.DceObjectFilter != "" { 119 info.objectFilter = pkg.ImportPath + "." + d.DceObjectFilter 120 byFilter[info.objectFilter] = append(byFilter[info.objectFilter], info) 121 } 122 if d.DceMethodFilter != "" { 123 info.methodFilter = pkg.ImportPath + "." + d.DceMethodFilter 124 byFilter[info.methodFilter] = append(byFilter[info.methodFilter], info) 125 } 126 } 127 } 128 129 dceSelection := make(map[*Decl]struct{}) 130 for len(pendingDecls) != 0 { 131 d := pendingDecls[len(pendingDecls)-1] 132 pendingDecls = pendingDecls[:len(pendingDecls)-1] 133 134 dceSelection[d] = struct{}{} 135 136 for _, dep := range d.DceDeps { 137 if infos, ok := byFilter[dep]; ok { 138 delete(byFilter, dep) 139 for _, info := range infos { 140 if info.objectFilter == dep { 141 info.objectFilter = "" 142 } 143 if info.methodFilter == dep { 144 info.methodFilter = "" 145 } 146 if info.objectFilter == "" && info.methodFilter == "" { 147 pendingDecls = append(pendingDecls, info.decl) 148 } 149 } 150 } 151 } 152 } 153 154 if _, err := w.Write([]byte("\"use strict\";\n(function() {\n\n")); err != nil { 155 return err 156 } 157 preludeJS := prelude.Prelude 158 if minify { 159 preludeJS = prelude.Minified 160 } 161 if _, err := io.WriteString(w, preludeJS); err != nil { 162 return err 163 } 164 if _, err := w.Write([]byte("\n")); err != nil { 165 return err 166 } 167 168 // write packages 169 for _, pkg := range pkgs { 170 if err := WritePkgCode(pkg, dceSelection, minify, w); err != nil { 171 return err 172 } 173 } 174 175 if _, err := w.Write([]byte("$synthesizeMethods();\nvar $mainPkg = $packages[\"" + string(mainPkg.ImportPath) + "\"];\n$packages[\"runtime\"].$init();\n$go($mainPkg.$init, []);\n$flushConsole();\n\n}).call(this);\n")); err != nil { 176 return err 177 } 178 179 return nil 180 } 181 182 func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, minify bool, w *SourceMapFilter) error { 183 if w.MappingCallback != nil && pkg.FileSet != nil { 184 w.fileSet = token.NewFileSet() 185 if err := w.fileSet.Read(json.NewDecoder(bytes.NewReader(pkg.FileSet)).Decode); err != nil { 186 panic(err) 187 } 188 } 189 if _, err := w.Write(pkg.IncJSCode); err != nil { 190 return err 191 } 192 if _, err := w.Write(removeWhitespace([]byte(fmt.Sprintf("$packages[\"%s\"] = (function() {\n", pkg.ImportPath)), minify)); err != nil { 193 return err 194 } 195 vars := []string{"$pkg = {}", "$init"} 196 var filteredDecls []*Decl 197 for _, d := range pkg.Declarations { 198 if _, ok := dceSelection[d]; ok { 199 vars = append(vars, d.Vars...) 200 filteredDecls = append(filteredDecls, d) 201 } 202 } 203 if _, err := w.Write(removeWhitespace([]byte(fmt.Sprintf("\tvar %s;\n", strings.Join(vars, ", "))), minify)); err != nil { 204 return err 205 } 206 for _, d := range filteredDecls { 207 if _, err := w.Write(d.DeclCode); err != nil { 208 return err 209 } 210 } 211 for _, d := range filteredDecls { 212 if _, err := w.Write(d.MethodListCode); err != nil { 213 return err 214 } 215 } 216 for _, d := range filteredDecls { 217 if _, err := w.Write(d.TypeInitCode); err != nil { 218 return err 219 } 220 } 221 222 if _, err := w.Write(removeWhitespace([]byte("\t$init = function() {\n\t\t$pkg.$init = function() {};\n\t\t/* */ var $f, $c = false, $s = 0, $r; if (this !== undefined && this.$blk !== undefined) { $f = this; $c = true; $s = $f.$s; $r = $f.$r; } s: while (true) { switch ($s) { case 0:\n"), minify)); err != nil { 223 return err 224 } 225 for _, d := range filteredDecls { 226 if _, err := w.Write(d.InitCode); err != nil { 227 return err 228 } 229 } 230 if _, err := w.Write(removeWhitespace([]byte("\t\t/* */ } return; } if ($f === undefined) { $f = { $blk: $init }; } $f.$s = $s; $f.$r = $r; return $f;\n\t};\n\t$pkg.$init = $init;\n\treturn $pkg;\n})();"), minify)); err != nil { 231 return err 232 } 233 if _, err := w.Write([]byte("\n")); err != nil { // keep this \n even when minified 234 return err 235 } 236 return nil 237 } 238 239 func ReadArchive(filename, path string, r io.Reader, packages map[string]*types.Package) (*Archive, error) { 240 var a Archive 241 if err := gob.NewDecoder(r).Decode(&a); err != nil { 242 return nil, err 243 } 244 245 var err error 246 packages[path], err = gcexportdata.Read(bytes.NewReader(a.ExportData), token.NewFileSet(), packages, path) 247 if err != nil { 248 return nil, err 249 } 250 251 return &a, nil 252 } 253 254 func WriteArchive(a *Archive, w io.Writer) error { 255 return gob.NewEncoder(w).Encode(a) 256 } 257 258 type SourceMapFilter struct { 259 Writer io.Writer 260 MappingCallback func(generatedLine, generatedColumn int, originalPos token.Position) 261 line int 262 column int 263 fileSet *token.FileSet 264 } 265 266 func (f *SourceMapFilter) Write(p []byte) (n int, err error) { 267 var n2 int 268 for { 269 i := bytes.IndexByte(p, '\b') 270 w := p 271 if i != -1 { 272 w = p[:i] 273 } 274 275 n2, err = f.Writer.Write(w) 276 n += n2 277 for { 278 i := bytes.IndexByte(w, '\n') 279 if i == -1 { 280 f.column += len(w) 281 break 282 } 283 f.line++ 284 f.column = 0 285 w = w[i+1:] 286 } 287 288 if err != nil || i == -1 { 289 return 290 } 291 if f.MappingCallback != nil { 292 f.MappingCallback(f.line+1, f.column, f.fileSet.Position(token.Pos(binary.BigEndian.Uint32(p[i+1:i+5])))) 293 } 294 p = p[i+5:] 295 n += 5 296 } 297 }