Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Next Next commit
build: Allow overriden functions calls
Before this commit, every overriden standard library function was renamed
'_'. Now, if there is the following directive in the function comment:
"//gopherjs:keep_overridden", they are prefixed with "_gopherjs_overridden_"
so that they can be called from the overriding function. This is typically
useful to use the standard library implementation as a fallback.
  • Loading branch information
r-l-x committed Apr 20, 2018
commit 24a2d2dc0863bf03db1cdf15941a8f6e2635f787
52 changes: 41 additions & 11 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,23 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
return &PackageData{Package: pkg, JSFiles: jsFiles}, nil
}

// Test if we find the '//gopherjs:keep_overridden' comment
func findKeepOverridenComment(doc *ast.CommentGroup) bool {
if doc == nil {
return false
}
for _, comment := range doc.List {
text := comment.Text
if i := strings.Index(text, " "); i >= 0 {
text = text[:i]
}
if text == "//gopherjs:keep_overridden" {
return true
}
}
return false
}

// parseAndAugment parses and returns all .go files of given pkg.
// Standard Go library packages are augmented with files in compiler/natives folder.
// If isTest is true and pkg.ImportPath has no _test suffix, package is built for running internal tests.
Expand All @@ -195,12 +212,19 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
// The native packages are augmented by the contents of natives.FS in the following way.
// The file names do not matter except the usual `_test` suffix. The files for
// native overrides get added to the package (even if they have the same name
// as an existing file from the standard library). For all identifiers that exist
// in the original AND the overrides, the original identifier in the AST gets
// replaced by `_`. New identifiers that don't exist in original package get added.
// as an existing file from the standard library). For function identifiers that exist
// in the original AND the overrides AND that include the following directive in their comment:
// //gopherjs:keep_overridden, the original identifier in the AST gets prefixed by
// `_gopherjs_overridden_`. For other identifiers that exist in the original AND the overrides,
// the original identifier gets replaced by `_`. New identifiers that don't exist in original
// package get added.
func parseAndAugment(pkg *build.Package, isTest bool, fileSet *token.FileSet) ([]*ast.File, error) {
var files []*ast.File
replacedDeclNames := make(map[string]bool)

type overrideInfo struct {
keepOverriden bool
}
replacedDeclNames := make(map[string]overrideInfo)
funcName := func(d *ast.FuncDecl) string {
if d.Recv == nil || len(d.Recv.List) == 0 {
return d.Name.Name
Expand Down Expand Up @@ -279,17 +303,17 @@ func parseAndAugment(pkg *build.Package, isTest bool, fileSet *token.FileSet) ([
for _, decl := range file.Decls {
switch d := decl.(type) {
case *ast.FuncDecl:
replacedDeclNames[funcName(d)] = true
replacedDeclNames[funcName(d)] = overrideInfo{keepOverriden: findKeepOverridenComment(d.Doc)}
case *ast.GenDecl:
switch d.Tok {
case token.TYPE:
for _, spec := range d.Specs {
replacedDeclNames[spec.(*ast.TypeSpec).Name.Name] = true
replacedDeclNames[spec.(*ast.TypeSpec).Name.Name] = overrideInfo{}
}
case token.VAR, token.CONST:
for _, spec := range d.Specs {
for _, name := range spec.(*ast.ValueSpec).Names {
replacedDeclNames[name.Name] = true
replacedDeclNames[name.Name] = overrideInfo{}
}
}
}
Expand Down Expand Up @@ -341,23 +365,29 @@ func parseAndAugment(pkg *build.Package, isTest bool, fileSet *token.FileSet) ([
for _, decl := range file.Decls {
switch d := decl.(type) {
case *ast.FuncDecl:
if replacedDeclNames[funcName(d)] {
d.Name = ast.NewIdent("_")
if info, ok := replacedDeclNames[funcName(d)]; ok {
if info.keepOverriden {
// Allow overridden function calls
// The standard library implementation of foo() becomes _gopherjs_overridden_foo()
d.Name.Name = "_gopherjs_overridden_" + d.Name.Name
} else {
d.Name = ast.NewIdent("_")
}
}
case *ast.GenDecl:
switch d.Tok {
Copy link
Member

@flimzy flimzy Mar 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm mentioning this again so it doesn't get lost in the shuffle, now that GitHub has hidden my original note as "outdated".

The comment for this function is outdated by the changes you've made. In particular, this text is no longer correct:

 // The native packages are augmented by the contents of natives.FS in the following way.
 // The file names do not matter except the usual `_test` suffix. The files for
 // native overrides get added to the package (even if they have the same name
 // as an existing file from the standard library). For all identifiers that exist
 // in the original AND the overrides, the original identifier in the AST gets
 // replaced by `_`. New identifiers that don't exist in original package get added.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, here is the updated comment: febab31

case token.TYPE:
for _, spec := range d.Specs {
s := spec.(*ast.TypeSpec)
if replacedDeclNames[s.Name.Name] {
if _, ok := replacedDeclNames[s.Name.Name]; ok {
s.Name = ast.NewIdent("_")
}
}
case token.VAR, token.CONST:
for _, spec := range d.Specs {
s := spec.(*ast.ValueSpec)
for i, name := range s.Names {
if replacedDeclNames[name.Name] {
if _, ok := replacedDeclNames[name.Name]; ok {
s.Names[i] = ast.NewIdent("_")
}
}
Expand Down