[release-branch.go1.8] all: merge master into release-branch.go1.8

add1aac0 static: don't use the jQuery func for looking up based on hash
608c3b09 present: fix typo
0d047c8d cmd/godex: handle printing of type aliases
de557280 go/gcimporter15: update import/export to handle type aliases
5d76f8ce static: detect platform to hide/show unix/windows instructions

Change-Id: I327abdef58298a72d2c2cdcd24da6bfd096bd262
This commit is contained in:
Chris Broadfoot 2017-01-19 12:44:19 -08:00
commit 8187b5f3e6
17 changed files with 259 additions and 172 deletions

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
// This file implements access to gc-generated export data. // This file implements access to gc-generated export data.
package main package main

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
// This file implements access to gccgo-generated export data. // This file implements access to gccgo-generated export data.
package main package main

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
package main package main
import ( import (

13
cmd/godex/isAlias18.go Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.9
package main
import "go/types"
func isAlias(obj *types.TypeName) bool {
return false // there are no type aliases before Go 1.9
}

13
cmd/godex/isAlias19.go Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package main
import "go/types"
func isAlias(obj *types.TypeName) bool {
return obj.IsAlias()
}

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
package main package main
import ( import (
@ -143,7 +141,13 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
p.printDecl("type", len(typez), func() { p.printDecl("type", len(typez), func() {
for _, obj := range typez { for _, obj := range typez {
p.printf("%s ", obj.Name()) p.printf("%s ", obj.Name())
p.writeType(p.pkg, obj.Type().Underlying()) typ := obj.Type()
if isAlias(obj) {
p.print("= ")
p.writeType(p.pkg, typ)
} else {
p.writeType(p.pkg, typ.Underlying())
}
p.print("\n") p.print("\n")
} }
}) })

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
// This file implements access to export data from source. // This file implements access to export data from source.
package main package main

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
// This file implements writing of types. The functionality is lifted // This file implements writing of types. The functionality is lifted
// directly from go/types, but now contains various modifications for // directly from go/types, but now contains various modifications for
// nicer output. // nicer output.

View File

@ -39,11 +39,12 @@ const debugFormat = false // default: false
const trace = false // default: false const trace = false // default: false
// Current export format version. Increase with each format change. // Current export format version. Increase with each format change.
// 3: added aliasTag and export of aliases // 4: type name objects support type aliases, uses aliasTag
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
// 2: removed unused bool in ODCL export (compiler only) // 2: removed unused bool in ODCL export (compiler only)
// 1: header format change (more regular), export package for _ struct fields // 1: header format change (more regular), export package for _ struct fields
// 0: Go1.7 encoding // 0: Go1.7 encoding
const exportVersion = 3 const exportVersion = 4
// trackAllTypes enables cycle tracking for all types, not just named // trackAllTypes enables cycle tracking for all types, not just named
// types. The existing compiler invariants assume that unnamed types // types. The existing compiler invariants assume that unnamed types
@ -65,9 +66,6 @@ type exporter struct {
pkgIndex map[*types.Package]int pkgIndex map[*types.Package]int
typIndex map[types.Type]int typIndex map[types.Type]int
// track objects that we've reexported already
reexported map[types.Object]bool
// position encoding // position encoding
posInfoFormat bool posInfoFormat bool
prevFile string prevFile string
@ -86,7 +84,6 @@ func BExportData(fset *token.FileSet, pkg *types.Package) []byte {
strIndex: map[string]int{"": 0}, // empty string is mapped to 0 strIndex: map[string]int{"": 0}, // empty string is mapped to 0
pkgIndex: make(map[*types.Package]int), pkgIndex: make(map[*types.Package]int),
typIndex: make(map[types.Type]int), typIndex: make(map[types.Type]int),
reexported: make(map[types.Object]bool),
posInfoFormat: true, // TODO(gri) might become a flag, eventually posInfoFormat: true, // TODO(gri) might become a flag, eventually
} }
@ -188,7 +185,13 @@ func (p *exporter) obj(obj types.Object) {
p.value(obj.Val()) p.value(obj.Val())
case *types.TypeName: case *types.TypeName:
p.tag(typeTag) if isAlias(obj) {
p.tag(aliasTag)
p.pos(obj)
p.qualifiedName(obj)
} else {
p.tag(typeTag)
}
p.typ(obj.Type()) p.typ(obj.Type())
case *types.Var: case *types.Var:
@ -205,21 +208,6 @@ func (p *exporter) obj(obj types.Object) {
p.paramList(sig.Params(), sig.Variadic()) p.paramList(sig.Params(), sig.Variadic())
p.paramList(sig.Results(), false) p.paramList(sig.Results(), false)
// Alias-related code. Keep for now.
// case *types_Alias:
// // make sure the original is exported before the alias
// // (if the alias declaration was invalid, orig will be nil)
// orig := original(obj)
// if orig != nil && !p.reexported[orig] {
// p.obj(orig)
// p.reexported[orig] = true
// }
// p.tag(aliasTag)
// p.pos(obj)
// p.string(obj.Name())
// p.qualifiedName(orig)
default: default:
log.Fatalf("gcimporter: unexpected object %v (%T)", obj, obj) log.Fatalf("gcimporter: unexpected object %v (%T)", obj, obj)
} }
@ -279,10 +267,6 @@ func commonPrefixLen(a, b string) int {
} }
func (p *exporter) qualifiedName(obj types.Object) { func (p *exporter) qualifiedName(obj types.Object) {
if obj == nil {
p.string("")
return
}
p.string(obj.Name()) p.string(obj.Name())
p.pkg(obj.Pkg(), false) p.pkg(obj.Pkg(), false)
} }
@ -482,28 +466,45 @@ func (p *exporter) method(m *types.Func) {
p.paramList(sig.Results(), false) p.paramList(sig.Results(), false)
} }
// fieldName is like qualifiedName but it doesn't record the package for exported names.
func (p *exporter) fieldName(f *types.Var) { func (p *exporter) fieldName(f *types.Var) {
name := f.Name() name := f.Name()
// anonymous field with unexported base type name: use "?" as field name
// (bname != "" per spec, but we are conservative in case of errors)
if f.Anonymous() { if f.Anonymous() {
base := f.Type() // anonymous field - we distinguish between 3 cases:
if ptr, ok := base.(*types.Pointer); ok { // 1) field name matches base type name and is exported
base = ptr.Elem() // 2) field name matches base type name and is not exported
} // 3) field name doesn't match base type name (alias name)
if named, ok := base.(*types.Named); ok && !named.Obj().Exported() { bname := basetypeName(f.Type())
// anonymous field with unexported base type name if name == bname {
name = "?" // unexported name to force export of package if ast.IsExported(name) {
name = "" // 1) we don't need to know the field name or package
} else {
name = "?" // 2) use unexported name "?" to force package export
}
} else {
// 3) indicate alias and export name as is
// (this requires an extra "@" but this is a rare case)
p.string("@")
} }
} }
p.string(name) p.string(name)
if !f.Exported() { if name != "" && !ast.IsExported(name) {
p.pkg(f.Pkg(), false) p.pkg(f.Pkg(), false)
} }
} }
func basetypeName(typ types.Type) string {
switch typ := deref(typ).(type) {
case *types.Basic:
return typ.Name()
case *types.Named:
return typ.Obj().Name()
default:
return "" // unnamed type
}
}
func (p *exporter) paramList(params *types.Tuple, variadic bool) { func (p *exporter) paramList(params *types.Tuple, variadic bool) {
// use negative length to indicate unnamed parameters // use negative length to indicate unnamed parameters
// (look at the first parameter only since either all // (look at the first parameter only since either all
@ -797,10 +798,10 @@ func (p *exporter) tracef(format string, args ...interface{}) {
// Debugging support. // Debugging support.
// (tagString is only used when tracing is enabled) // (tagString is only used when tracing is enabled)
var tagString = [...]string{ var tagString = [...]string{
// Packages: // Packages
-packageTag: "package", -packageTag: "package",
// Types: // Types
-namedTag: "named type", -namedTag: "named type",
-arrayTag: "array", -arrayTag: "array",
-sliceTag: "slice", -sliceTag: "slice",
@ -812,7 +813,7 @@ var tagString = [...]string{
-mapTag: "map", -mapTag: "map",
-chanTag: "chan", -chanTag: "chan",
// Values: // Values
-falseTag: "false", -falseTag: "false",
-trueTag: "true", -trueTag: "true",
-int64Tag: "int64", -int64Tag: "int64",
@ -821,4 +822,7 @@ var tagString = [...]string{
-complexTag: "complex", -complexTag: "complex",
-stringTag: "string", -stringTag: "string",
-unknownTag: "unknown", -unknownTag: "unknown",
// Type aliases
-aliasTag: "alias",
} }

View File

@ -1,60 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Alias-related code. Keep for now.
// +build ignore
package gcimporter_test
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"testing"
gcimporter "golang.org/x/tools/go/gcimporter15"
)
func disabledTestInvalidAlias(t *testing.T) {
// parse and typecheck
const src = "package p; func InvalidAlias => foo.f"
fset1 := token.NewFileSet()
f, err := parser.ParseFile(fset1, "p.go", src, 0)
if err != nil {
t.Fatal(err)
}
var conf types.Config
pkg, err := conf.Check("p", fset1, []*ast.File{f}, nil)
if err == nil {
t.Fatal("invalid source type-checked without error")
}
if pkg == nil {
t.Fatal("nil package returned")
}
// export
exportdata := gcimporter.BExportData(fset1, pkg)
// import
imports := make(map[string]*types.Package)
fset2 := token.NewFileSet()
_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
if err != nil {
t.Fatalf("BImportData(%s): %v", pkg.Path(), err)
}
// pkg2 must contain InvalidAlias as an invalid Alias
obj := pkg2.Scope().Lookup("InvalidAlias")
if obj == nil {
t.Fatal("InvalidAlias not found")
}
alias, ok := obj.(*types.Alias)
if !ok {
t.Fatalf("got %v; want alias", alias)
}
if alias.Type() != types.Typ[types.Invalid] || alias.Orig() != nil {
t.Fatalf("got %v (orig = %v); want invalid alias", alias, alias.Orig())
}
}

View File

@ -0,0 +1,93 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package gcimporter_test
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"testing"
gcimporter "golang.org/x/tools/go/gcimporter15"
)
const src = `
package p
type (
T0 = int32
T1 = struct{}
T2 = struct{ T1 }
Invalid = foo // foo is undeclared
)
`
func checkPkg(t *testing.T, pkg *types.Package, label string) {
T1 := types.NewStruct(nil, nil)
T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
for _, test := range []struct {
name string
typ types.Type
}{
{"T0", types.Typ[types.Int32]},
{"T1", T1},
{"T2", T2},
{"Invalid", types.Typ[types.Invalid]},
} {
obj := pkg.Scope().Lookup(test.name)
if obj == nil {
t.Errorf("%s: %s not found", label, test.name)
continue
}
tname, _ := obj.(*types.TypeName)
if tname == nil {
t.Errorf("%s: %v not a type name", label, obj)
continue
}
if !tname.IsAlias() {
t.Errorf("%s: %v: not marked as alias", label, tname)
continue
}
if got := tname.Type(); !types.Identical(got, test.typ) {
t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
}
}
}
func TestTypeAliases(t *testing.T) {
// parse and typecheck
fset1 := token.NewFileSet()
f, err := parser.ParseFile(fset1, "p.go", src, 0)
if err != nil {
t.Fatal(err)
}
var conf types.Config
pkg1, err := conf.Check("p", fset1, []*ast.File{f}, nil)
if err == nil {
// foo in undeclared in src; we should see an error
t.Fatal("invalid source type-checked without error")
}
if pkg1 == nil {
// despite incorrect src we should see a (partially) type-checked package
t.Fatal("nil package returned")
}
checkPkg(t, pkg1, "export")
// export
exportdata := gcimporter.BExportData(fset1, pkg1)
// import
imports := make(map[string]*types.Package)
fset2 := token.NewFileSet()
_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg1.Path())
if err != nil {
t.Fatalf("BImportData(%s): %v", pkg1.Path(), err)
}
checkPkg(t, pkg2, "import")
}

View File

@ -100,10 +100,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
// read version specific flags - extend as necessary // read version specific flags - extend as necessary
switch p.version { switch p.version {
// case 4: // case 5:
// ... // ...
// fallthrough // fallthrough
case 3, 2, 1: case 4, 3, 2, 1:
p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
p.trackAllTypes = p.int() != 0 p.trackAllTypes = p.int() != 0
p.posInfoFormat = p.int() != 0 p.posInfoFormat = p.int() != 0
@ -210,7 +210,6 @@ func (p *importer) pkg() *types.Package {
} }
// objTag returns the tag value for each object kind. // objTag returns the tag value for each object kind.
// obj must not be a *types.Alias.
func objTag(obj types.Object) int { func objTag(obj types.Object) int {
switch obj.(type) { switch obj.(type) {
case *types.Const: case *types.Const:
@ -221,7 +220,6 @@ func objTag(obj types.Object) int {
return varTag return varTag
case *types.Func: case *types.Func:
return funcTag return funcTag
// Aliases are not exported multiple times, thus we should not see them here.
default: default:
errorf("unexpected object: %v (%T)", obj, obj) // panics errorf("unexpected object: %v (%T)", obj, obj) // panics
panic("unreachable") panic("unreachable")
@ -239,14 +237,14 @@ func (p *importer) declare(obj types.Object) {
pkg := obj.Pkg() pkg := obj.Pkg()
if alt := pkg.Scope().Insert(obj); alt != nil { if alt := pkg.Scope().Insert(obj); alt != nil {
// This can only trigger if we import a (non-type) object a second time. // This can only trigger if we import a (non-type) object a second time.
// Excluding aliases, this cannot happen because 1) we only import a package // Excluding type aliases, this cannot happen because 1) we only import a package
// once; and b) we ignore compiler-specific export data which may contain // once; and b) we ignore compiler-specific export data which may contain
// functions whose inlined function bodies refer to other functions that // functions whose inlined function bodies refer to other functions that
// were already imported. // were already imported.
// However, aliases require reexporting the original object, so we need // However, type aliases require reexporting the original type, so we need
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
// method importer.obj, switch case importing functions). // method importer.obj, switch case importing functions).
// Note that the original itself cannot be an alias. // TODO(gri) review/update this comment once the gc compiler handles type aliases.
if !sameObj(obj, alt) { if !sameObj(obj, alt) {
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
} }
@ -262,6 +260,13 @@ func (p *importer) obj(tag int) {
val := p.value() val := p.value()
p.declare(types.NewConst(pos, pkg, name, typ, val)) p.declare(types.NewConst(pos, pkg, name, typ, val))
case aliasTag:
// TODO(gri) verify type alias hookup is correct
pos := p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil)
p.declare(types.NewTypeName(pos, pkg, name, typ))
case typeTag: case typeTag:
p.typ(nil) p.typ(nil)
@ -279,19 +284,6 @@ func (p *importer) obj(tag int) {
sig := types.NewSignature(nil, params, result, isddd) sig := types.NewSignature(nil, params, result, isddd)
p.declare(types.NewFunc(pos, pkg, name, sig)) p.declare(types.NewFunc(pos, pkg, name, sig))
case aliasTag:
pos := p.pos()
name := p.string()
var orig types.Object
if pkg, name := p.qualifiedName(); pkg != nil {
orig = pkg.Scope().Lookup(name)
}
// Alias-related code. Keep for now.
_ = pos
_ = name
_ = orig
// p.declare(types.NewAlias(pos, p.pkgList[0], name, orig))
default: default:
errorf("unexpected object tag %d", tag) errorf("unexpected object tag %d", tag)
} }
@ -351,9 +343,7 @@ var (
func (p *importer) qualifiedName() (pkg *types.Package, name string) { func (p *importer) qualifiedName() (pkg *types.Package, name string) {
name = p.string() name = p.string()
if name != "" { pkg = p.pkg()
pkg = p.pkg()
}
return return
} }
@ -558,17 +548,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
fields = make([]*types.Var, n) fields = make([]*types.Var, n)
tags = make([]string, n) tags = make([]string, n)
for i := range fields { for i := range fields {
fields[i] = p.field(parent) fields[i], tags[i] = p.field(parent)
tags[i] = p.string()
} }
} }
return return
} }
func (p *importer) field(parent *types.Package) *types.Var { func (p *importer) field(parent *types.Package) (*types.Var, string) {
pos := p.pos() pos := p.pos()
pkg, name := p.fieldName(parent) pkg, name, alias := p.fieldName(parent)
typ := p.typ(parent) typ := p.typ(parent)
tag := p.string()
anonymous := false anonymous := false
if name == "" { if name == "" {
@ -580,12 +570,15 @@ func (p *importer) field(parent *types.Package) *types.Var {
case *types.Named: case *types.Named:
name = typ.Obj().Name() name = typ.Obj().Name()
default: default:
errorf("anonymous field expected") errorf("named base type expected")
} }
anonymous = true anonymous = true
} else if alias {
// anonymous field: we have an explicit name because it's an alias
anonymous = true
} }
return types.NewField(pos, pkg, name, typ, anonymous) return types.NewField(pos, pkg, name, typ, anonymous), tag
} }
func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
@ -600,45 +593,42 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
func (p *importer) method(parent *types.Package) *types.Func { func (p *importer) method(parent *types.Package) *types.Func {
pos := p.pos() pos := p.pos()
pkg, name := p.fieldName(parent) pkg, name, _ := p.fieldName(parent)
params, isddd := p.paramList() params, isddd := p.paramList()
result, _ := p.paramList() result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd) sig := types.NewSignature(nil, params, result, isddd)
return types.NewFunc(pos, pkg, name, sig) return types.NewFunc(pos, pkg, name, sig)
} }
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) { func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
name := p.string() name = p.string()
pkg := parent pkg = parent
if pkg == nil { if pkg == nil {
// use the imported package instead // use the imported package instead
pkg = p.pkgList[0] pkg = p.pkgList[0]
} }
if p.version == 0 && name == "_" { if p.version == 0 && name == "_" {
// version 0 didn't export a package for _ fields // version 0 didn't export a package for _ fields
// see issue #15514 return
// For bug-compatibility with gc, pretend all imported
// blank fields belong to the same dummy package.
// This avoids spurious "cannot assign A to B" errors
// from go/types caused by types changing as they are
// re-exported.
const blankpkg = "<_>"
pkg := p.imports[blankpkg]
if pkg == nil {
pkg = types.NewPackage(blankpkg, blankpkg)
p.imports[blankpkg] = pkg
}
return pkg, name
} }
if name != "" && !exported(name) { switch name {
if name == "?" { case "":
name = "" // 1) field name matches base type name and is exported: nothing to do
} case "?":
// 2) field name matches base type name and is not exported: need package
name = ""
pkg = p.pkg() pkg = p.pkg()
case "@":
// 3) field name doesn't match type name (alias)
name = p.string()
alias = true
fallthrough
default:
if !exported(name) {
pkg = p.pkg()
}
} }
return pkg, name return
} }
func (p *importer) paramList() (*types.Tuple, bool) { func (p *importer) paramList() (*types.Tuple, bool) {
@ -909,7 +899,7 @@ const (
nilTag // only used by gc (appears in exported inlined function bodies) nilTag // only used by gc (appears in exported inlined function bodies)
unknownTag // not used by gc (only appears in packages with errors) unknownTag // not used by gc (only appears in packages with errors)
// Aliases // Type aliases
aliasTag aliasTag
) )
@ -933,7 +923,7 @@ var predeclared = []types.Type{
types.Typ[types.Complex128], types.Typ[types.Complex128],
types.Typ[types.String], types.Typ[types.String],
// aliases // basic type aliases
types.Universe.Lookup("byte").Type(), types.Universe.Lookup("byte").Type(),
types.Universe.Lookup("rune").Type(), types.Universe.Lookup("rune").Type(),

View File

@ -0,0 +1,13 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.9
package gcimporter
import "go/types"
func isAlias(obj *types.TypeName) bool {
return false // there are no type aliases before Go 1.9
}

View File

@ -0,0 +1,13 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package gcimporter
import "go/types"
func isAlias(obj *types.TypeName) bool {
return obj.IsAlias()
}

View File

@ -247,7 +247,7 @@ function fixFocus() {
function toggleHash() { function toggleHash() {
// Open all of the toggles for a particular hash. // Open all of the toggles for a particular hash.
var els = $(document.getElementById(window.location.hash.substring(1)), var els = $(document.getElementById(window.location.hash.substring(1)),
$("a[name='" + window.location.hash.substring(1) + "']")); $.find("a[name='" + window.location.hash.substring(1) + "']"));
while (els.length) { while (els.length) {
for (var i = 0; i < els.length; i++) { for (var i = 0; i < els.length; i++) {
var el = $(els[i]); var el = $(els[i]);
@ -263,7 +263,14 @@ function personalizeInstallInstructions() {
var prefix = '?download='; var prefix = '?download=';
var s = window.location.search; var s = window.location.search;
if (s.indexOf(prefix) != 0) { if (s.indexOf(prefix) != 0) {
// No 'download' query string; bail. // No 'download' query string; detect "test" instructions from User Agent.
if (navigator.platform.indexOf('Win') != -1) {
$('.testUnix').hide();
$('.testWindows').show();
} else {
$('.testUnix').show();
$('.testWindows').hide();
}
return; return;
} }

View File

@ -833,7 +833,14 @@ function personalizeInstallInstructions() {
var prefix = '?download='; var prefix = '?download=';
var s = window.location.search; var s = window.location.search;
if (s.indexOf(prefix) != 0) { if (s.indexOf(prefix) != 0) {
// No 'download' query string; bail. // No 'download' query string; detect "test" instructions from User Agent.
if (navigator.platform.indexOf('Win') != -1) {
$('.testUnix').hide();
$('.testWindows').show();
} else {
$('.testUnix').show();
$('.testWindows').hide();
}
return; return;
} }

View File

@ -39,7 +39,7 @@ func parseIframe(ctx *Context, fileName string, lineno int, text string) (Elem,
i.Width = v i.Width = v
} }
default: default:
return nil, fmt.Errorf("incorrect image invocation: %q", text) return nil, fmt.Errorf("incorrect iframe invocation: %q", text)
} }
return i, nil return i, nil
} }