Skip to content

Commit d466fe3

Browse files
Merge pull request #1380 from Workiva/typeOrderWip
[generics] Grouping by declaration kinds and adding $finishSetup
2 parents 1e45615 + 6c9aa83 commit d466fe3

File tree

27 files changed

+456
-70
lines changed

27 files changed

+456
-70
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88

99
permissions:
1010
contents: read
11+
pull-requests: read
1112

1213
concurrency:
1314
group: ci-${{ github.ref }}

compiler/compiler.go

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter, goVersion string) err
159159
}
160160
}
161161

162-
if _, err := w.Write([]byte("$synthesizeMethods();\n$initAllLinknames();\nvar $mainPkg = $packages[\"" + string(mainPkg.ImportPath) + "\"];\n$packages[\"runtime\"].$init();\n$go($mainPkg.$init, []);\n$flushConsole();\n\n}).call(this);\n")); err != nil {
162+
if _, err := w.Write([]byte("$callForAllPackages(\"$finishSetup\");\n$synthesizeMethods();\n$callForAllPackages(\"$initLinknames\");\nvar $mainPkg = $packages[\"" + string(mainPkg.ImportPath) + "\"];\n$packages[\"runtime\"].$init();\n$go($mainPkg.$init, []);\n$flushConsole();\n\n}).call(this);\n")); err != nil {
163163
return err
164164
}
165165
return nil
@@ -183,13 +183,68 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, gls linkname.Go
183183
filteredDecls = append(filteredDecls, d)
184184
}
185185
}
186+
// Write variable names
186187
if _, err := w.Write(removeWhitespace([]byte(fmt.Sprintf("\tvar %s;\n", strings.Join(vars, ", "))), minify)); err != nil {
187188
return err
188189
}
190+
// Write imports
189191
for _, d := range filteredDecls {
190-
if _, err := w.Write(d.DeclCode); err != nil {
192+
if _, err := w.Write(d.ImportCode); err != nil {
191193
return err
192194
}
195+
}
196+
// Write named type declarations
197+
for _, d := range filteredDecls {
198+
if _, err := w.Write(d.TypeDeclCode); err != nil {
199+
return err
200+
}
201+
}
202+
// Write exports for named type declarations
203+
for _, d := range filteredDecls {
204+
if _, err := w.Write(d.ExportTypeCode); err != nil {
205+
return err
206+
}
207+
}
208+
209+
// The following parts have to be run after all packages have been added
210+
// to handle generics that use named types defined in a package that
211+
// is defined after this package has been defined.
212+
if _, err := w.Write(removeWhitespace([]byte("\t$pkg.$finishSetup = function() {\n"), minify)); err != nil {
213+
return err
214+
}
215+
216+
// Write anonymous type declarations
217+
for _, d := range filteredDecls {
218+
if _, err := w.Write(d.AnonTypeDeclCode); err != nil {
219+
return err
220+
}
221+
}
222+
// Write function declarations
223+
for _, d := range filteredDecls {
224+
if _, err := w.Write(d.FuncDeclCode); err != nil {
225+
return err
226+
}
227+
}
228+
// Write exports for function declarations
229+
for _, d := range filteredDecls {
230+
if _, err := w.Write(d.ExportFuncCode); err != nil {
231+
return err
232+
}
233+
}
234+
// Write reflection metadata for types' methods
235+
for _, d := range filteredDecls {
236+
if _, err := w.Write(d.MethodListCode); err != nil {
237+
return err
238+
}
239+
}
240+
// Write the calls to finish initialization of types
241+
for _, d := range filteredDecls {
242+
if _, err := w.Write(d.TypeInitCode); err != nil {
243+
return err
244+
}
245+
}
246+
247+
for _, d := range filteredDecls {
193248
if gls.IsImplementation(d.LinkingName) {
194249
// This decl is referenced by a go:linkname directive, expose it to external
195250
// callers via $linkname object (declared in prelude). We are not using
@@ -205,16 +260,6 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, gls linkname.Go
205260
}
206261
}
207262
}
208-
for _, d := range filteredDecls {
209-
if _, err := w.Write(d.MethodListCode); err != nil {
210-
return err
211-
}
212-
}
213-
for _, d := range filteredDecls {
214-
if _, err := w.Write(d.TypeInitCode); err != nil {
215-
return err
216-
}
217-
}
218263

219264
{
220265
// Set up all functions which package declares, but which implementation
@@ -229,16 +274,23 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, gls linkname.Go
229274
if !found {
230275
continue // The symbol is not affected by a go:linkname directive.
231276
}
232-
lines = append(lines, fmt.Sprintf("\t\t%s = $linknames[%q];\n", d.RefExpr, impl.String()))
277+
lines = append(lines, fmt.Sprintf("\t\t\t%s = $linknames[%q];\n", d.RefExpr, impl.String()))
233278
}
234279
if len(lines) > 0 {
235-
code := fmt.Sprintf("\t$pkg.$initLinknames = function() {\n%s};\n", strings.Join(lines, ""))
280+
code := fmt.Sprintf("\t\t$pkg.$initLinknames = function() {\n%s};\n", strings.Join(lines, ""))
236281
if _, err := w.Write(removeWhitespace([]byte(code), minify)); err != nil {
237282
return err
238283
}
239284
}
240285
}
241286

287+
// Write the end of the `$finishSetup` function.
288+
if _, err := w.Write(removeWhitespace([]byte("\t};\n"), minify)); err != nil {
289+
return err
290+
}
291+
292+
// Write the initialization function that will initialize this package
293+
// (e.g. initialize package-level variable value).
242294
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 {
243295
return err
244296
}

compiler/compiler_test.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,11 +569,11 @@ func TestLengthParenthesizingIssue841(t *testing.T) {
569569
goodRegex := regexp.MustCompile(`\(a\s*\+\s*b\)\.length`)
570570
goodFound := false
571571
for i, decl := range mainPkg.Declarations {
572-
if badRegex.Match(decl.DeclCode) {
572+
if badRegex.Match(decl.FuncDeclCode) {
573573
t.Errorf("found length issue in decl #%d: %s", i, decl.FullName)
574-
t.Logf("decl code:\n%s", string(decl.DeclCode))
574+
t.Logf("decl code:\n%s", string(decl.FuncDeclCode))
575575
}
576-
if goodRegex.Match(decl.DeclCode) {
576+
if goodRegex.Match(decl.FuncDeclCode) {
577577
goodFound = true
578578
}
579579
}
@@ -1121,13 +1121,17 @@ func collectDeclInstances(t *testing.T, pkg *Archive) []string {
11211121

11221122
// Collect all instances of generics (e.g. `Foo[bar] @ 2`) written to the decl code.
11231123
insts := []string{}
1124-
for _, decl := range pkg.Declarations {
1125-
if match := rex.FindAllStringSubmatch(string(decl.DeclCode), 1); len(match) > 0 {
1124+
checkCode := func(code []byte) {
1125+
if match := rex.FindAllStringSubmatch(string(code), 1); len(match) > 0 {
11261126
instance := match[0][1] + `[` + strings.TrimSpace(match[0][3]) + `]`
11271127
instance = strings.ReplaceAll(instance, `command-line-arguments`, pkg.Name)
11281128
insts = append(insts, instance)
11291129
}
11301130
}
1131+
for _, decl := range pkg.Declarations {
1132+
checkCode(decl.TypeDeclCode)
1133+
checkCode(decl.FuncDeclCode)
1134+
}
11311135
sort.Strings(insts)
11321136
return insts
11331137
}

compiler/decls.go

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,30 @@ type Decl struct {
4444
RefExpr string
4545
// NamedRecvType is method named recv declare.
4646
NamedRecvType string
47-
// JavaScript code that declares basic information about a symbol. For a type
48-
// it configures basic information about the type and its identity. For a function
49-
// or method it contains its compiled body.
50-
DeclCode []byte
47+
// JavaScript code that declares a local variable for an imported package.
48+
ImportCode []byte
49+
// JavaScript code that declares basic information about a named type symbol.
50+
// It configures basic information about the type and its identity.
51+
TypeDeclCode []byte
52+
// JavaScript code that assigns exposed named types to the package.
53+
ExportTypeCode []byte
54+
// JavaScript code that declares basic information about an anonymous types.
55+
// It configures basic information about the type.
56+
// This is added to the finish setup phase to has access to all packages.
57+
AnonTypeDeclCode []byte
58+
// JavaScript code that declares basic information about a function or
59+
// method symbol. This contains the function's or method's compiled body.
60+
// This is added to the finish setup phase to has access to all packages.
61+
FuncDeclCode []byte
62+
// JavaScript code that assigns exposed functions to the package.
63+
// This is added to the finish setup phase to has access to all packages.
64+
ExportFuncCode []byte
5165
// JavaScript code that initializes reflection metadata about type's method list.
66+
// This is added to the finish setup phase to has access to all packages.
5267
MethodListCode []byte
5368
// JavaScript code that initializes the rest of reflection metadata about a type
5469
// (e.g. struct fields, array type sizes, element types, etc.).
70+
// This is added to the finish setup phase to has access to all packages.
5571
TypeInitCode []byte
5672
// JavaScript code that needs to be executed during the package init phase to
5773
// set the symbol up (e.g. initialize package-level variable value).
@@ -68,7 +84,12 @@ type Decl struct {
6884
// minify returns a copy of Decl with unnecessary whitespace removed from the
6985
// JS code.
7086
func (d Decl) minify() Decl {
71-
d.DeclCode = removeWhitespace(d.DeclCode, true)
87+
d.ImportCode = removeWhitespace(d.ImportCode, true)
88+
d.TypeDeclCode = removeWhitespace(d.TypeDeclCode, true)
89+
d.ExportTypeCode = removeWhitespace(d.ExportTypeCode, true)
90+
d.AnonTypeDeclCode = removeWhitespace(d.AnonTypeDeclCode, true)
91+
d.FuncDeclCode = removeWhitespace(d.FuncDeclCode, true)
92+
d.ExportFuncCode = removeWhitespace(d.ExportFuncCode, true)
7293
d.MethodListCode = removeWhitespace(d.MethodListCode, true)
7394
d.TypeInitCode = removeWhitespace(d.TypeInitCode, true)
7495
d.InitCode = removeWhitespace(d.InitCode, true)
@@ -164,10 +185,10 @@ func (fc *funcContext) importDecls() (importedPaths []string, importDecls []*Dec
164185
func (fc *funcContext) newImportDecl(importedPkg *types.Package) *Decl {
165186
pkgVar := fc.importedPkgVar(importedPkg)
166187
d := &Decl{
167-
FullName: importDeclFullName(importedPkg),
168-
Vars: []string{pkgVar},
169-
DeclCode: []byte(fmt.Sprintf("\t%s = $packages[\"%s\"];\n", pkgVar, importedPkg.Path())),
170-
InitCode: fc.CatchOutput(1, func() { fc.translateStmt(fc.importInitializer(importedPkg.Path()), nil) }),
188+
FullName: importDeclFullName(importedPkg),
189+
Vars: []string{pkgVar},
190+
ImportCode: []byte(fmt.Sprintf("\t%s = $packages[\"%s\"];\n", pkgVar, importedPkg.Path())),
191+
InitCode: fc.CatchOutput(1, func() { fc.translateStmt(fc.importInitializer(importedPkg.Path()), nil) }),
171192
}
172193
d.Dce().SetAsAlive()
173194
return d
@@ -293,17 +314,19 @@ func (fc *funcContext) funcDecls(functions []*ast.FuncDecl) ([]*Decl, error) {
293314
// package-level variable by which they all are referenced,
294315
// e.g. init functions and instances of generic functions.
295316
objName := fc.objectName(o)
317+
generic := len(instances) > 1 || !instances[0].IsTrivial()
318+
varName := objName
319+
if generic {
320+
varName += ` = []`
321+
}
296322
varDecl := &Decl{
297323
FullName: funcVarDeclFullName(o),
298-
Vars: []string{objName},
324+
Vars: []string{varName},
299325
}
300326
varDecl.Dce().SetName(o, nil, nil)
301-
if len(instances) > 1 || !instances[0].IsTrivial() {
302-
varDecl.DeclCode = fc.CatchOutput(0, func() {
303-
fc.Printf("%s = {};", objName)
304-
if o.Exported() {
305-
fc.Printf("$pkg.%s = %s;", encodeIdent(fun.Name.Name), objName)
306-
}
327+
if generic && o.Exported() {
328+
varDecl.ExportFuncCode = fc.CatchOutput(1, func() {
329+
fc.Printf("$pkg.%s = %s;", encodeIdent(fun.Name.Name), objName)
307330
})
308331
}
309332
funcDecls = append(funcDecls, varDecl)
@@ -359,7 +382,7 @@ func (fc *funcContext) newFuncDecl(fun *ast.FuncDecl, inst typeparams.Instance)
359382
}
360383

361384
fc.pkgCtx.CollectDCEDeps(d, func() {
362-
d.DeclCode = fc.namedFuncContext(inst).translateTopLevelFunction(fun)
385+
d.FuncDeclCode = fc.namedFuncContext(inst).translateTopLevelFunction(fun)
363386
})
364387
return d
365388
}
@@ -438,18 +461,18 @@ func (fc *funcContext) namedTypeDecls(typeNames typesutil.TypeNames) ([]*Decl, e
438461
// the type definition directly.
439462
func (fc *funcContext) newNamedTypeVarDecl(obj *types.TypeName) *Decl {
440463
name := fc.objectName(obj)
464+
generic := fc.pkgCtx.instanceSet.Pkg(obj.Pkg()).ObjHasInstances(obj)
465+
varName := name
466+
if generic {
467+
varName += ` = []`
468+
}
441469
varDecl := &Decl{
442470
FullName: typeVarDeclFullName(obj),
443-
Vars: []string{name},
471+
Vars: []string{varName},
444472
}
445473
varDecl.Dce().SetName(obj, nil, nil)
446-
if fc.pkgCtx.instanceSet.Pkg(obj.Pkg()).ObjHasInstances(obj) {
447-
varDecl.DeclCode = fc.CatchOutput(0, func() {
448-
fc.Printf("%s = {};", name)
449-
})
450-
}
451474
if isPkgLevel(obj) {
452-
varDecl.TypeInitCode = fc.CatchOutput(0, func() {
475+
varDecl.ExportTypeCode = fc.CatchOutput(0, func() {
453476
fc.Printf("$pkg.%s = %s;", encodeIdent(obj.Name()), name)
454477
})
455478
}
@@ -483,7 +506,7 @@ func (fc *funcContext) newNamedTypeInstDecl(inst typeparams.Instance) (*Decl, er
483506
d.Dce().SetName(inst.Object, inst.TNest, inst.TArgs)
484507
fc.pkgCtx.CollectDCEDeps(d, func() {
485508
// Code that declares a JS type (i.e. prototype) for each Go type.
486-
d.DeclCode = fc.CatchOutput(0, func() {
509+
d.TypeDeclCode = fc.CatchOutput(0, func() {
487510
size := int64(0)
488511
constructor := "null"
489512

@@ -505,7 +528,7 @@ func (fc *funcContext) newNamedTypeInstDecl(inst typeparams.Instance) (*Decl, er
505528
})
506529

507530
// Reflection metadata about methods the type has.
508-
d.MethodListCode = fc.CatchOutput(0, func() {
531+
d.MethodListCode = fc.CatchOutput(1, func() {
509532
if _, ok := underlying.(*types.Interface); ok {
510533
return
511534
}
@@ -531,7 +554,7 @@ func (fc *funcContext) newNamedTypeInstDecl(inst typeparams.Instance) (*Decl, er
531554
// initialize themselves.
532555
switch t := underlying.(type) {
533556
case *types.Array, *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Slice, *types.Signature, *types.Struct:
534-
d.TypeInitCode = fc.CatchOutput(0, func() {
557+
d.TypeInitCode = fc.CatchOutput(1, func() {
535558
fc.Printf("%s.init(%s);", fc.instName(inst), fc.initArgs(t))
536559
})
537560
}
@@ -541,6 +564,10 @@ func (fc *funcContext) newNamedTypeInstDecl(inst typeparams.Instance) (*Decl, er
541564

542565
// structConstructor returns JS constructor function for a struct type.
543566
func (fc *funcContext) structConstructor(t *types.Struct) string {
567+
if t.NumFields() == 0 {
568+
return `function() { this.$val = this; }`
569+
}
570+
544571
constructor := &strings.Builder{}
545572

546573
ctrArgs := make([]string, t.NumFields())
@@ -607,7 +634,7 @@ func (fc *funcContext) anonTypeDecls(anonTypes []*types.TypeName) []*Decl {
607634
}
608635
d.Dce().SetName(t, nil, nil)
609636
fc.pkgCtx.CollectDCEDeps(d, func() {
610-
d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), fc.initArgs(t.Type())))
637+
d.AnonTypeDeclCode = []byte(fmt.Sprintf("\t\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), fc.initArgs(t.Type())))
611638
})
612639
decls = append(decls, d)
613640
}

0 commit comments

Comments
 (0)