expressions working
Signed-off-by: Casey Lee <cplee@nektos.com>
This commit is contained in:
parent
409060c847
commit
e40ab0145f
114 changed files with 32361 additions and 0 deletions
5
go.mod
5
go.mod
|
@ -2,6 +2,7 @@ module github.com/nektos/act
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||||
|
github.com/MichaelTJones/walk v0.0.0-20161122175330-4748e29d5718 // indirect
|
||||||
github.com/Microsoft/go-winio v0.4.11 // indirect
|
github.com/Microsoft/go-winio v0.4.11 // indirect
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||||
github.com/andreaskoch/go-fswatch v1.0.0
|
github.com/andreaskoch/go-fswatch v1.0.0
|
||||||
|
@ -17,11 +18,13 @@ require (
|
||||||
github.com/gorilla/mux v1.7.0 // indirect
|
github.com/gorilla/mux v1.7.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
||||||
|
github.com/mgutz/str v1.2.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
github.com/opencontainers/runc v0.1.1 // indirect
|
github.com/opencontainers/runc v0.1.1 // indirect
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
|
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
|
||||||
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94
|
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94
|
||||||
github.com/sirupsen/logrus v1.3.0
|
github.com/sirupsen/logrus v1.3.0
|
||||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect
|
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect
|
||||||
|
@ -38,7 +41,9 @@ require (
|
||||||
google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca // indirect
|
google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca // indirect
|
||||||
google.golang.org/grpc v1.18.0 // indirect
|
google.golang.org/grpc v1.18.0 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
|
gopkg.in/godo.v2 v2.0.9
|
||||||
gopkg.in/ini.v1 v1.41.0 // indirect
|
gopkg.in/ini.v1 v1.41.0 // indirect
|
||||||
|
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||||
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
|
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
|
||||||
gopkg.in/src-d/go-git-fixtures.v3 v3.3.0 // indirect
|
gopkg.in/src-d/go-git-fixtures.v3 v3.3.0 // indirect
|
||||||
gopkg.in/src-d/go-git.v4 v4.9.1
|
gopkg.in/src-d/go-git.v4 v4.9.1
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -1,6 +1,8 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
|
github.com/MichaelTJones/walk v0.0.0-20161122175330-4748e29d5718 h1:FSsoaa1q4jAaeiAUxf9H0PgFP7eA/UL6c3PdJH+nMN4=
|
||||||
|
github.com/MichaelTJones/walk v0.0.0-20161122175330-4748e29d5718/go.mod h1:VVwKsx9Dc8rNG55BWqogoJzGubjKnRoXdUvpGbWqeCc=
|
||||||
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||||
|
@ -67,6 +69,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||||
|
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
@ -85,6 +89,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff h1:+6NUiITWwE5q1KO6SAfUX918c+Tab0+tGAM/mtdlUyA=
|
||||||
|
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
|
||||||
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 h1:G04eS0JkAIVZfaJLjla9dNxkJCPiKIGZlw9AfOhzOD0=
|
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 h1:G04eS0JkAIVZfaJLjla9dNxkJCPiKIGZlw9AfOhzOD0=
|
||||||
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ=
|
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
|
@ -159,8 +165,12 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/godo.v2 v2.0.9 h1:jnbznTzXVk0JDKOxN3/LJLDPYJzIl0734y+Z0cEJb4A=
|
||||||
|
gopkg.in/godo.v2 v2.0.9/go.mod h1:wgvPPKLsWN0hPIJ4JyxvFGGbIW3fJMSrXhdvSuZ1z/8=
|
||||||
gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE=
|
gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE=
|
||||||
gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
||||||
|
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||||
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
|
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
|
||||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
|
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
|
||||||
|
|
171
pkg/runner/expression.go
Normal file
171
pkg/runner/expression.go
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/godo.v2/glob"
|
||||||
|
)
|
||||||
|
|
||||||
|
const prefix = "${{"
|
||||||
|
const suffix = "}}"
|
||||||
|
|
||||||
|
var pattern *regexp.Regexp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pattern = regexp.MustCompile(fmt.Sprintf("\\%s.+?%s", prefix, suffix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExpressionEvaluator creates a new evaluator
|
||||||
|
func (rc *RunContext) NewExpressionEvaluator() ExpressionEvaluator {
|
||||||
|
vm := rc.newVM()
|
||||||
|
return &expressionEvaluator{
|
||||||
|
vm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpressionEvaluator is the interface for evaluating expressions
|
||||||
|
type ExpressionEvaluator interface {
|
||||||
|
Evaluate(string) (string, error)
|
||||||
|
Interpolate(string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type expressionEvaluator struct {
|
||||||
|
vm *otto.Otto
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ee *expressionEvaluator) Evaluate(in string) (string, error) {
|
||||||
|
val, err := ee.vm.Run(in)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return val.ToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ee *expressionEvaluator) Interpolate(in string) (string, error) {
|
||||||
|
errList := make([]error, 0)
|
||||||
|
out := pattern.ReplaceAllStringFunc(in, func(match string) string {
|
||||||
|
expression := strings.TrimPrefix(strings.TrimSuffix(match, suffix), prefix)
|
||||||
|
evaluated, err := ee.Evaluate(expression)
|
||||||
|
if err != nil {
|
||||||
|
errList = append(errList, err)
|
||||||
|
}
|
||||||
|
return evaluated
|
||||||
|
})
|
||||||
|
if len(errList) > 0 {
|
||||||
|
return "", fmt.Errorf("Unable to interpolate string '%s' - %v", in, errList)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RunContext) newVM() *otto.Otto {
|
||||||
|
configers := []func(*otto.Otto){
|
||||||
|
vmContains,
|
||||||
|
vmStartsWith,
|
||||||
|
vmEndsWith,
|
||||||
|
vmFormat,
|
||||||
|
vmJoin,
|
||||||
|
vmToJSON,
|
||||||
|
vmHashFiles(rc.Config.Workdir),
|
||||||
|
}
|
||||||
|
vm := otto.New()
|
||||||
|
for _, configer := range configers {
|
||||||
|
configer(vm)
|
||||||
|
}
|
||||||
|
return vm
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmContains(vm *otto.Otto) {
|
||||||
|
vm.Set("contains", func(searchString interface{}, searchValue string) bool {
|
||||||
|
if searchStringString, ok := searchString.(string); ok {
|
||||||
|
return strings.Contains(strings.ToLower(searchStringString), strings.ToLower(searchValue))
|
||||||
|
} else if searchStringArray, ok := searchString.([]string); ok {
|
||||||
|
for _, s := range searchStringArray {
|
||||||
|
if strings.ToLower(s) == strings.ToLower(searchValue) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmStartsWith(vm *otto.Otto) {
|
||||||
|
vm.Set("startsWith", func(searchString string, searchValue string) bool {
|
||||||
|
return strings.HasPrefix(strings.ToLower(searchString), strings.ToLower(searchValue))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmEndsWith(vm *otto.Otto) {
|
||||||
|
vm.Set("endsWith", func(searchString string, searchValue string) bool {
|
||||||
|
return strings.HasSuffix(strings.ToLower(searchString), strings.ToLower(searchValue))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmFormat(vm *otto.Otto) {
|
||||||
|
vm.Set("format", func(s string, vals ...string) string {
|
||||||
|
for i, v := range vals {
|
||||||
|
s = strings.ReplaceAll(s, fmt.Sprintf("{%d}", i), v)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmJoin(vm *otto.Otto) {
|
||||||
|
vm.Set("join", func(element interface{}, optionalElem string) string {
|
||||||
|
slist := make([]string, 0)
|
||||||
|
if elementString, ok := element.(string); ok {
|
||||||
|
slist = append(slist, elementString)
|
||||||
|
} else if elementArray, ok := element.([]string); ok {
|
||||||
|
slist = append(slist, elementArray...)
|
||||||
|
}
|
||||||
|
if optionalElem != "" {
|
||||||
|
slist = append(slist, optionalElem)
|
||||||
|
}
|
||||||
|
return strings.Join(slist, " ")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmToJSON(vm *otto.Otto) {
|
||||||
|
vm.Set("toJSON", func(o interface{}) string {
|
||||||
|
rtn, err := json.MarshalIndent(o, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Unable to marsal: %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(rtn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmHashFiles(workdir string) func(*otto.Otto) {
|
||||||
|
return func(vm *otto.Otto) {
|
||||||
|
vm.Set("hashFiles", func(path string) string {
|
||||||
|
files, _, err := glob.Glob([]string{filepath.Join(workdir, path)})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
hasher := sha256.New()
|
||||||
|
for _, file := range files {
|
||||||
|
f, err := os.Open(file.Path)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if _, err := io.Copy(hasher, f); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
69
pkg/runner/expression_test.go
Normal file
69
pkg/runner/expression_test.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEvaluate(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
rc := &RunContext{
|
||||||
|
Config: &Config{
|
||||||
|
Workdir: ".",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ee := rc.NewExpressionEvaluator()
|
||||||
|
|
||||||
|
tables := []struct {
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
errMesg string
|
||||||
|
}{
|
||||||
|
{" 1 ", "1", ""},
|
||||||
|
{"1 + 3", "4", ""},
|
||||||
|
{"(1 + 3) * -2", "-8", ""},
|
||||||
|
{"'my text'", "my text", ""},
|
||||||
|
{"contains('my text', 'te')", "true", ""},
|
||||||
|
{"contains('my TEXT', 'te')", "true", ""},
|
||||||
|
{"contains(['my text'], 'te')", "false", ""},
|
||||||
|
{"contains(['foo','bar'], 'bar')", "true", ""},
|
||||||
|
{"startsWith('hello world', 'He')", "true", ""},
|
||||||
|
{"endsWith('hello world', 'ld')", "true", ""},
|
||||||
|
{"format('0:{0} 2:{2} 1:{1}', 'zero', 'one', 'two')", "0:zero 2:two 1:one", ""},
|
||||||
|
{"join(['hello'],'octocat')", "hello octocat", ""},
|
||||||
|
{"join(['hello','mona','the'],'octocat')", "hello mona the octocat", ""},
|
||||||
|
{"join('hello','mona')", "hello mona", ""},
|
||||||
|
{"toJSON({'foo':'bar'})", "{\n \"foo\": \"bar\"\n}", ""},
|
||||||
|
{"hashFiles('**/package-lock.json')", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
table := table
|
||||||
|
t.Run(table.in, func(t *testing.T) {
|
||||||
|
out, err := ee.Evaluate(table.in)
|
||||||
|
if table.errMesg == "" {
|
||||||
|
assert.NoError(err, table.in)
|
||||||
|
assert.Equal(table.out, out)
|
||||||
|
} else {
|
||||||
|
assert.Error(err)
|
||||||
|
assert.Equal(table.errMesg, err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInterpolate(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
rc := &RunContext{
|
||||||
|
Config: &Config{
|
||||||
|
Workdir: ".",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ee := rc.NewExpressionEvaluator()
|
||||||
|
|
||||||
|
out, err := ee.Interpolate(" ${{1}} to ${{2}} ")
|
||||||
|
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(" 1 to 2 ", out)
|
||||||
|
}
|
25
vendor/github.com/MichaelTJones/walk/README.md
generated
vendored
Normal file
25
vendor/github.com/MichaelTJones/walk/README.md
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
walk
|
||||||
|
====
|
||||||
|
|
||||||
|
Fast parallel version of golang filepath.Walk()
|
||||||
|
|
||||||
|
Performs traversals in parallel so set GOMAXPROCS appropriately. Vaues of 8 to 16 seem to work best on my
|
||||||
|
4-CPU plus 4 SMT pseudo-CPU MacBookPro. The result is about 4x-6x the traversal rate of the standard Walk().
|
||||||
|
The two are not identical since we are walking the file system in a tumult of asynchronous walkFunc calls by
|
||||||
|
a number of goroutines. So, take note of the following:
|
||||||
|
|
||||||
|
1. This walk honors all of the walkFunc error semantics but as multiple user-supplied walkFuncs may simultaneously encounter a traversal error or generate one to stop traversal, only the FIRST of these will be returned as the Walk() result.
|
||||||
|
|
||||||
|
2. Further, since there may be a few files in flight at the instant of error discovery, a few more walkFunc calls may happen after the first error-generating call has signaled its desire to stop. In general this is a non-issue but it could matter so pay attention when designing your walkFunc. (For example, if you accumulate results then you need to have your own means to know to stop accumulating once you signal an error.)
|
||||||
|
|
||||||
|
3. Because the walkFunc is called concurrently in multiple goroutines, it needs to be careful about what it does with external data to avoid collisions. Results may be printed using fmt, but generally the best plan is to send results over a channel or accumulate counts using a locked mutex.
|
||||||
|
|
||||||
|
These issues are illustrated/handled in the simple traversal programs supplied with walk. There is also a test file that is just the tests from filepath in the Go language's standard library. Walk passes these tests when run in single process mode, and passes most of them in concurrent mode (GOMAXPROCS > 1). The problem is not a real problem, but one of the test expecting a specific number of errors to be found based on presumed sequential traversals.
|
||||||
|
|
||||||
|
Copyright (c) 2016 Michael T Jones
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
vendor/github.com/MichaelTJones/walk/path_plan9.go
generated
vendored
Normal file
30
vendor/github.com/MichaelTJones/walk/path_plan9.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
package walk
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// IsAbs returns true if the path is absolute.
|
||||||
|
func IsAbs(path string) bool {
|
||||||
|
return strings.HasPrefix(path, "/") || strings.HasPrefix(path, "#")
|
||||||
|
}
|
||||||
|
|
||||||
|
// volumeNameLen returns length of the leading volume name on Windows.
|
||||||
|
// It returns 0 elsewhere.
|
||||||
|
func volumeNameLen(path string) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPrefix exists for historical compatibility and should not be used.
|
||||||
|
func HasPrefix(p, prefix string) bool {
|
||||||
|
return strings.HasPrefix(p, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitList(path string) []string {
|
||||||
|
if path == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return strings.Split(path, string(ListSeparator))
|
||||||
|
}
|
32
vendor/github.com/MichaelTJones/walk/path_unix.go
generated
vendored
Normal file
32
vendor/github.com/MichaelTJones/walk/path_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2010 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 darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||||
|
|
||||||
|
package walk
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// IsAbs returns true if the path is absolute.
|
||||||
|
func IsAbs(path string) bool {
|
||||||
|
return strings.HasPrefix(path, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// volumeNameLen returns length of the leading volume name on Windows.
|
||||||
|
// It returns 0 elsewhere.
|
||||||
|
func volumeNameLen(path string) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPrefix exists for historical compatibility and should not be used.
|
||||||
|
func HasPrefix(p, prefix string) bool {
|
||||||
|
return strings.HasPrefix(p, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitList(path string) []string {
|
||||||
|
if path == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return strings.Split(path, string(ListSeparator))
|
||||||
|
}
|
105
vendor/github.com/MichaelTJones/walk/path_windows.go
generated
vendored
Normal file
105
vendor/github.com/MichaelTJones/walk/path_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
package walk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isSlash(c uint8) bool {
|
||||||
|
return c == '\\' || c == '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAbs returns true if the path is absolute.
|
||||||
|
func IsAbs(path string) (b bool) {
|
||||||
|
l := volumeNameLen(path)
|
||||||
|
if l == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
path = path[l:]
|
||||||
|
if path == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isSlash(path[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// volumeNameLen returns length of the leading volume name on Windows.
|
||||||
|
// It returns 0 elsewhere.
|
||||||
|
func volumeNameLen(path string) int {
|
||||||
|
if len(path) < 2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// with drive letter
|
||||||
|
c := path[0]
|
||||||
|
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
// is it UNC
|
||||||
|
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
|
||||||
|
!isSlash(path[2]) && path[2] != '.' {
|
||||||
|
// first, leading `\\` and next shouldn't be `\`. its server name.
|
||||||
|
for n := 3; n < l-1; n++ {
|
||||||
|
// second, next '\' shouldn't be repeated.
|
||||||
|
if isSlash(path[n]) {
|
||||||
|
n++
|
||||||
|
// third, following something characters. its share name.
|
||||||
|
if !isSlash(path[n]) {
|
||||||
|
if path[n] == '.' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for ; n < l; n++ {
|
||||||
|
if isSlash(path[n]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPrefix exists for historical compatibility and should not be used.
|
||||||
|
func HasPrefix(p, prefix string) bool {
|
||||||
|
if strings.HasPrefix(p, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return strings.HasPrefix(strings.ToLower(p), strings.ToLower(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitList(path string) []string {
|
||||||
|
// The same implementation is used in LookPath in os/exec;
|
||||||
|
// consider changing os/exec when changing this.
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split path, respecting but preserving quotes.
|
||||||
|
list := []string{}
|
||||||
|
start := 0
|
||||||
|
quo := false
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
switch c := path[i]; {
|
||||||
|
case c == '"':
|
||||||
|
quo = !quo
|
||||||
|
case c == ListSeparator && !quo:
|
||||||
|
list = append(list, path[start:i])
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list = append(list, path[start:])
|
||||||
|
|
||||||
|
// Remove quotes.
|
||||||
|
for i, s := range list {
|
||||||
|
if strings.Contains(s, `"`) {
|
||||||
|
list[i] = strings.Replace(s, `"`, ``, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
67
vendor/github.com/MichaelTJones/walk/symlink.go
generated
vendored
Normal file
67
vendor/github.com/MichaelTJones/walk/symlink.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2012 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 !windows
|
||||||
|
|
||||||
|
package walk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func evalSymlinks(path string) (string, error) {
|
||||||
|
const maxIter = 255
|
||||||
|
originalPath := path
|
||||||
|
// consume path by taking each frontmost path element,
|
||||||
|
// expanding it if it's a symlink, and appending it to b
|
||||||
|
var b bytes.Buffer
|
||||||
|
for n := 0; path != ""; n++ {
|
||||||
|
if n > maxIter {
|
||||||
|
return "", errors.New("EvalSymlinks: too many links in " + originalPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find next path component, p
|
||||||
|
i := strings.IndexRune(path, Separator)
|
||||||
|
var p string
|
||||||
|
if i == -1 {
|
||||||
|
p, path = path, ""
|
||||||
|
} else {
|
||||||
|
p, path = path[:i], path[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == "" {
|
||||||
|
if b.Len() == 0 {
|
||||||
|
// must be absolute path
|
||||||
|
b.WriteRune(Separator)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Lstat(b.String() + p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSymlink == 0 {
|
||||||
|
b.WriteString(p)
|
||||||
|
if path != "" {
|
||||||
|
b.WriteRune(Separator)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's a symlink, put it at the front of path
|
||||||
|
dest, err := os.Readlink(b.String() + p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if IsAbs(dest) {
|
||||||
|
b.Reset()
|
||||||
|
}
|
||||||
|
path = dest + string(Separator) + path
|
||||||
|
}
|
||||||
|
return Clean(b.String()), nil
|
||||||
|
}
|
69
vendor/github.com/MichaelTJones/walk/symlink_windows.go
generated
vendored
Normal file
69
vendor/github.com/MichaelTJones/walk/symlink_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
package walk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func toShort(path string) (string, error) {
|
||||||
|
p, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := p // GetShortPathName says we can reuse buffer
|
||||||
|
n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n > uint32(len(b)) {
|
||||||
|
b = make([]uint16, n)
|
||||||
|
n, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syscall.UTF16ToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLong(path string) (string, error) {
|
||||||
|
p, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := p // GetLongPathName says we can reuse buffer
|
||||||
|
n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n > uint32(len(b)) {
|
||||||
|
b = make([]uint16, n)
|
||||||
|
n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b = b[:n]
|
||||||
|
return syscall.UTF16ToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalSymlinks(path string) (string, error) {
|
||||||
|
p, err := toShort(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
p, err = toLong(p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// syscall.GetLongPathName does not change the case of the drive letter,
|
||||||
|
// but the result of EvalSymlinks must be unique, so we have
|
||||||
|
// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
|
||||||
|
// Make drive letter upper case.
|
||||||
|
if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
|
||||||
|
p = string(p[0]+'A'-'a') + p[1:]
|
||||||
|
}
|
||||||
|
return Clean(p), nil
|
||||||
|
}
|
444
vendor/github.com/MichaelTJones/walk/walk.go
generated
vendored
Normal file
444
vendor/github.com/MichaelTJones/walk/walk.go
generated
vendored
Normal file
|
@ -0,0 +1,444 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// Package filepath implements utility routines for manipulating filename paths
|
||||||
|
// in a way compatible with the target operating system-defined file paths.
|
||||||
|
package walk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SkipDir is used as a return value from WalkFuncs to indicate that
|
||||||
|
// the directory named in the call is to be skipped. It is not returned
|
||||||
|
// as an error by any function.
|
||||||
|
var SkipDir = errors.New("skip this directory")
|
||||||
|
|
||||||
|
// WalkFunc is the type of the function called for each file or directory
|
||||||
|
// visited by Walk. The path argument contains the argument to Walk as a
|
||||||
|
// prefix; that is, if Walk is called with "dir", which is a directory
|
||||||
|
// containing the file "a", the walk function will be called with argument
|
||||||
|
// "dir/a". The info argument is the os.FileInfo for the named path.
|
||||||
|
//
|
||||||
|
// If there was a problem walking to the file or directory named by path, the
|
||||||
|
// incoming error will describe the problem and the function can decide how
|
||||||
|
// to handle that error (and Walk will not descend into that directory). If
|
||||||
|
// an error is returned, processing stops. The sole exception is that if path
|
||||||
|
// is a directory and the function returns the special value SkipDir, the
|
||||||
|
// contents of the directory are skipped and processing continues as usual on
|
||||||
|
// the next file.
|
||||||
|
type WalkFunc func(path string, info os.FileInfo, err error) error
|
||||||
|
|
||||||
|
var lstat = os.Lstat // for testing
|
||||||
|
var LstatP = &lstat
|
||||||
|
|
||||||
|
type VisitData struct {
|
||||||
|
path string
|
||||||
|
info os.FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type WalkState struct {
|
||||||
|
walkFn WalkFunc
|
||||||
|
v chan VisitData // files to be processed
|
||||||
|
active sync.WaitGroup // number of files to process
|
||||||
|
lock sync.RWMutex
|
||||||
|
firstError error // accessed using lock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *WalkState) terminated() bool {
|
||||||
|
ws.lock.RLock()
|
||||||
|
done := ws.firstError != nil
|
||||||
|
ws.lock.RUnlock()
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *WalkState) setTerminated(err error) {
|
||||||
|
ws.lock.Lock()
|
||||||
|
if ws.firstError == nil {
|
||||||
|
ws.firstError = err
|
||||||
|
}
|
||||||
|
ws.lock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *WalkState) visitChannel() {
|
||||||
|
for file := range ws.v {
|
||||||
|
ws.visitFile(file)
|
||||||
|
ws.active.Add(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *WalkState) visitFile(file VisitData) {
|
||||||
|
if ws.terminated() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ws.walkFn(file.path, file.info, nil)
|
||||||
|
if err != nil {
|
||||||
|
if !(file.info.IsDir() && err == SkipDir) {
|
||||||
|
ws.setTerminated(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !file.info.IsDir() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
names, err := readDirNames(file.path)
|
||||||
|
if err != nil {
|
||||||
|
err = ws.walkFn(file.path, file.info, err)
|
||||||
|
if err != nil {
|
||||||
|
ws.setTerminated(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
here := file.path
|
||||||
|
for _, name := range names {
|
||||||
|
file.path = Join(here, name)
|
||||||
|
file.info, err = lstat(file.path)
|
||||||
|
if err != nil {
|
||||||
|
err = ws.walkFn(file.path, file.info, err)
|
||||||
|
if err != nil && (!file.info.IsDir() || err != SkipDir) {
|
||||||
|
ws.setTerminated(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch file.info.IsDir() {
|
||||||
|
case true:
|
||||||
|
ws.active.Add(1) // presume channel send will succeed
|
||||||
|
select {
|
||||||
|
case ws.v <- file:
|
||||||
|
// push directory info to queue for concurrent traversal
|
||||||
|
default:
|
||||||
|
// undo increment when send fails and handle now
|
||||||
|
ws.active.Add(-1)
|
||||||
|
ws.visitFile(file)
|
||||||
|
}
|
||||||
|
case false:
|
||||||
|
err = ws.walkFn(file.path, file.info, nil)
|
||||||
|
if err != nil {
|
||||||
|
ws.setTerminated(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
||||||
|
// directory in the tree, including root. All errors that arise visiting files
|
||||||
|
// and directories are filtered by walkFn. The files are walked in a random
|
||||||
|
// order. Walk does not follow symbolic links.
|
||||||
|
|
||||||
|
func Walk(root string, walkFn WalkFunc) error {
|
||||||
|
info, err := os.Lstat(root)
|
||||||
|
if err != nil {
|
||||||
|
return walkFn(root, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws := &WalkState{
|
||||||
|
walkFn: walkFn,
|
||||||
|
v: make(chan VisitData, 1024),
|
||||||
|
}
|
||||||
|
defer close(ws.v)
|
||||||
|
|
||||||
|
ws.active.Add(1)
|
||||||
|
ws.v <- VisitData{root, info}
|
||||||
|
|
||||||
|
walkers := 16
|
||||||
|
for i := 0; i < walkers; i++ {
|
||||||
|
go ws.visitChannel()
|
||||||
|
}
|
||||||
|
ws.active.Wait()
|
||||||
|
|
||||||
|
return ws.firstError
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// THE REMAINDER IS UNCHANGED FROM THE ORGINAL GO LIBRARY ORIGINAL
|
||||||
|
//
|
||||||
|
|
||||||
|
// readDirNames reads the directory named by dirname and returns
|
||||||
|
// a sorted list of directory entries.
|
||||||
|
func readDirNames(dirname string) ([]string, error) {
|
||||||
|
f, err := os.Open(dirname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
names, err := f.Readdirnames(-1)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sort.Strings(names) // omit sort to save 1-2%
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A lazybuf is a lazily constructed path buffer.
|
||||||
|
// It supports append, reading previously appended bytes,
|
||||||
|
// and retrieving the final string. It does not allocate a buffer
|
||||||
|
// to hold the output until that output diverges from s.
|
||||||
|
type lazybuf struct {
|
||||||
|
path string
|
||||||
|
buf []byte
|
||||||
|
w int
|
||||||
|
volAndPath string
|
||||||
|
volLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *lazybuf) index(i int) byte {
|
||||||
|
if b.buf != nil {
|
||||||
|
return b.buf[i]
|
||||||
|
}
|
||||||
|
return b.path[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *lazybuf) append(c byte) {
|
||||||
|
if b.buf == nil {
|
||||||
|
if b.w < len(b.path) && b.path[b.w] == c {
|
||||||
|
b.w++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.buf = make([]byte, len(b.path))
|
||||||
|
copy(b.buf, b.path[:b.w])
|
||||||
|
}
|
||||||
|
b.buf[b.w] = c
|
||||||
|
b.w++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *lazybuf) string() string {
|
||||||
|
if b.buf == nil {
|
||||||
|
return b.volAndPath[:b.volLen+b.w]
|
||||||
|
}
|
||||||
|
return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Separator = os.PathSeparator
|
||||||
|
ListSeparator = os.PathListSeparator
|
||||||
|
)
|
||||||
|
|
||||||
|
// Clean returns the shortest path name equivalent to path
|
||||||
|
// by purely lexical processing. It applies the following rules
|
||||||
|
// iteratively until no further processing can be done:
|
||||||
|
//
|
||||||
|
// 1. Replace multiple Separator elements with a single one.
|
||||||
|
// 2. Eliminate each . path name element (the current directory).
|
||||||
|
// 3. Eliminate each inner .. path name element (the parent directory)
|
||||||
|
// along with the non-.. element that precedes it.
|
||||||
|
// 4. Eliminate .. elements that begin a rooted path:
|
||||||
|
// that is, replace "/.." by "/" at the beginning of a path,
|
||||||
|
// assuming Separator is '/'.
|
||||||
|
//
|
||||||
|
// The returned path ends in a slash only if it represents a root directory,
|
||||||
|
// such as "/" on Unix or `C:\` on Windows.
|
||||||
|
//
|
||||||
|
// If the result of this process is an empty string, Clean
|
||||||
|
// returns the string ".".
|
||||||
|
//
|
||||||
|
// See also Rob Pike, ``Lexical File Names in Plan 9 or
|
||||||
|
// Getting Dot-Dot Right,''
|
||||||
|
// http://plan9.bell-labs.com/sys/doc/lexnames.html
|
||||||
|
func Clean(path string) string {
|
||||||
|
originalPath := path
|
||||||
|
volLen := volumeNameLen(path)
|
||||||
|
path = path[volLen:]
|
||||||
|
if path == "" {
|
||||||
|
if volLen > 1 && originalPath[1] != ':' {
|
||||||
|
// should be UNC
|
||||||
|
return FromSlash(originalPath)
|
||||||
|
}
|
||||||
|
return originalPath + "."
|
||||||
|
}
|
||||||
|
rooted := os.IsPathSeparator(path[0])
|
||||||
|
|
||||||
|
// Invariants:
|
||||||
|
// reading from path; r is index of next byte to process.
|
||||||
|
// writing to buf; w is index of next byte to write.
|
||||||
|
// dotdot is index in buf where .. must stop, either because
|
||||||
|
// it is the leading slash or it is a leading ../../.. prefix.
|
||||||
|
n := len(path)
|
||||||
|
out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
|
||||||
|
r, dotdot := 0, 0
|
||||||
|
if rooted {
|
||||||
|
out.append(Separator)
|
||||||
|
r, dotdot = 1, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for r < n {
|
||||||
|
switch {
|
||||||
|
case os.IsPathSeparator(path[r]):
|
||||||
|
// empty path element
|
||||||
|
r++
|
||||||
|
case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
|
||||||
|
// . element
|
||||||
|
r++
|
||||||
|
case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
|
||||||
|
// .. element: remove to last separator
|
||||||
|
r += 2
|
||||||
|
switch {
|
||||||
|
case out.w > dotdot:
|
||||||
|
// can backtrack
|
||||||
|
out.w--
|
||||||
|
for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) {
|
||||||
|
out.w--
|
||||||
|
}
|
||||||
|
case !rooted:
|
||||||
|
// cannot backtrack, but not rooted, so append .. element.
|
||||||
|
if out.w > 0 {
|
||||||
|
out.append(Separator)
|
||||||
|
}
|
||||||
|
out.append('.')
|
||||||
|
out.append('.')
|
||||||
|
dotdot = out.w
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// real path element.
|
||||||
|
// add slash if needed
|
||||||
|
if rooted && out.w != 1 || !rooted && out.w != 0 {
|
||||||
|
out.append(Separator)
|
||||||
|
}
|
||||||
|
// copy element
|
||||||
|
for ; r < n && !os.IsPathSeparator(path[r]); r++ {
|
||||||
|
out.append(path[r])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn empty string into "."
|
||||||
|
if out.w == 0 {
|
||||||
|
out.append('.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return FromSlash(out.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSlash returns the result of replacing each separator character
|
||||||
|
// in path with a slash ('/') character. Multiple separators are
|
||||||
|
// replaced by multiple slashes.
|
||||||
|
func ToSlash(path string) string {
|
||||||
|
if Separator == '/' {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return strings.Replace(path, string(Separator), "/", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromSlash returns the result of replacing each slash ('/') character
|
||||||
|
// in path with a separator character. Multiple slashes are replaced
|
||||||
|
// by multiple separators.
|
||||||
|
func FromSlash(path string) string {
|
||||||
|
if Separator == '/' {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return strings.Replace(path, "/", string(Separator), -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join joins any number of path elements into a single path, adding
|
||||||
|
// a Separator if necessary. The result is Cleaned, in particular
|
||||||
|
// all empty strings are ignored.
|
||||||
|
func Join(elem ...string) string {
|
||||||
|
for i, e := range elem {
|
||||||
|
if e != "" {
|
||||||
|
return Clean(strings.Join(elem[i:], string(Separator)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rel returns a relative path that is lexically equivalent to targpath when
|
||||||
|
// joined to basepath with an intervening separator. That is,
|
||||||
|
// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
|
||||||
|
// On success, the returned path will always be relative to basepath,
|
||||||
|
// even if basepath and targpath share no elements.
|
||||||
|
// An error is returned if targpath can't be made relative to basepath or if
|
||||||
|
// knowing the current working directory would be necessary to compute it.
|
||||||
|
func Rel(basepath, targpath string) (string, error) {
|
||||||
|
baseVol := VolumeName(basepath)
|
||||||
|
targVol := VolumeName(targpath)
|
||||||
|
base := Clean(basepath)
|
||||||
|
targ := Clean(targpath)
|
||||||
|
if targ == base {
|
||||||
|
return ".", nil
|
||||||
|
}
|
||||||
|
base = base[len(baseVol):]
|
||||||
|
targ = targ[len(targVol):]
|
||||||
|
if base == "." {
|
||||||
|
base = ""
|
||||||
|
}
|
||||||
|
// Can't use IsAbs - `\a` and `a` are both relative in Windows.
|
||||||
|
baseSlashed := len(base) > 0 && base[0] == Separator
|
||||||
|
targSlashed := len(targ) > 0 && targ[0] == Separator
|
||||||
|
if baseSlashed != targSlashed || baseVol != targVol {
|
||||||
|
return "", errors.New("Rel: can't make " + targ + " relative to " + base)
|
||||||
|
}
|
||||||
|
// Position base[b0:bi] and targ[t0:ti] at the first differing elements.
|
||||||
|
bl := len(base)
|
||||||
|
tl := len(targ)
|
||||||
|
var b0, bi, t0, ti int
|
||||||
|
for {
|
||||||
|
for bi < bl && base[bi] != Separator {
|
||||||
|
bi++
|
||||||
|
}
|
||||||
|
for ti < tl && targ[ti] != Separator {
|
||||||
|
ti++
|
||||||
|
}
|
||||||
|
if targ[t0:ti] != base[b0:bi] {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if bi < bl {
|
||||||
|
bi++
|
||||||
|
}
|
||||||
|
if ti < tl {
|
||||||
|
ti++
|
||||||
|
}
|
||||||
|
b0 = bi
|
||||||
|
t0 = ti
|
||||||
|
}
|
||||||
|
if base[b0:bi] == ".." {
|
||||||
|
return "", errors.New("Rel: can't make " + targ + " relative to " + base)
|
||||||
|
}
|
||||||
|
if b0 != bl {
|
||||||
|
// Base elements left. Must go up before going down.
|
||||||
|
seps := strings.Count(base[b0:bl], string(Separator))
|
||||||
|
size := 2 + seps*3
|
||||||
|
if tl != t0 {
|
||||||
|
size += 1 + tl - t0
|
||||||
|
}
|
||||||
|
buf := make([]byte, size)
|
||||||
|
n := copy(buf, "..")
|
||||||
|
for i := 0; i < seps; i++ {
|
||||||
|
buf[n] = Separator
|
||||||
|
copy(buf[n+1:], "..")
|
||||||
|
n += 3
|
||||||
|
}
|
||||||
|
if t0 != tl {
|
||||||
|
buf[n] = Separator
|
||||||
|
copy(buf[n+1:], targ[t0:])
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
return targ[t0:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeName returns leading volume name.
|
||||||
|
// Given "C:\foo\bar" it returns "C:" under windows.
|
||||||
|
// Given "\\host\share\foo" it returns "\\host\share".
|
||||||
|
// On other platforms it returns "".
|
||||||
|
func VolumeName(path string) (v string) {
|
||||||
|
return path[:volumeNameLen(path)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalSymlinks returns the path name after the evaluation of any symbolic
|
||||||
|
// links.
|
||||||
|
// If path is relative the result will be relative to the current directory,
|
||||||
|
// unless one of the components is an absolute symbolic link.
|
||||||
|
func EvalSymlinks(path string) (string, error) {
|
||||||
|
return evalSymlinks(path)
|
||||||
|
}
|
7
vendor/github.com/mgutz/str/.gitignore
generated
vendored
Normal file
7
vendor/github.com/mgutz/str/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
tmp/
|
||||||
|
*.log
|
||||||
|
_*
|
||||||
|
node_modules
|
||||||
|
example/dist
|
||||||
|
/Gododir/godobin*
|
||||||
|
/Gododir/Gododir
|
5
vendor/github.com/mgutz/str/CREDITS
generated
vendored
Normal file
5
vendor/github.com/mgutz/str/CREDITS
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
* [string.js](http://stringjs.com) - I contributed several
|
||||||
|
functions to this project.
|
||||||
|
|
||||||
|
* [bbgen.net](http://bbgen.net/blog/2011/06/string-to-argc-argv/)
|
||||||
|
|
21
vendor/github.com/mgutz/str/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mgutz/str/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2014 Mario L. Gutierrez <mario@mgutz.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
649
vendor/github.com/mgutz/str/README.md
generated
vendored
Normal file
649
vendor/github.com/mgutz/str/README.md
generated
vendored
Normal file
|
@ -0,0 +1,649 @@
|
||||||
|
# str
|
||||||
|
|
||||||
|
import "github.com/mgutz/str"
|
||||||
|
|
||||||
|
Package str is a comprehensive set of string functions to build more Go
|
||||||
|
awesomeness. Str complements Go's standard packages and does not duplicate
|
||||||
|
functionality found in `strings` or `strconv`.
|
||||||
|
|
||||||
|
Str is based on plain functions instead of object-based methods, consistent with
|
||||||
|
Go standard string packages.
|
||||||
|
|
||||||
|
str.Between("<a>foo</a>", "<a>", "</a>") == "foo"
|
||||||
|
|
||||||
|
Str supports pipelining instead of chaining
|
||||||
|
|
||||||
|
s := str.Pipe("\nabcdef\n", Clean, BetweenF("a", "f"), ChompLeftF("bc"))
|
||||||
|
|
||||||
|
User-defined filters can be added to the pipeline by inserting a function or
|
||||||
|
closure that returns a function with this signature
|
||||||
|
|
||||||
|
func(string) string
|
||||||
|
|
||||||
|
### Index
|
||||||
|
|
||||||
|
* [Variables](#variables)
|
||||||
|
* [func Between](#func
|
||||||
|
[godoc](https://godoc.org/github.com/mgutz/str)
|
||||||
|
between)
|
||||||
|
* [func BetweenF](#func--betweenf)
|
||||||
|
* [func Camelize](#func--camelize)
|
||||||
|
* [func Capitalize](#func--capitalize)
|
||||||
|
* [func CharAt](#func--charat)
|
||||||
|
* [func CharAtF](#func--charatf)
|
||||||
|
* [func ChompLeft](#func--chompleft)
|
||||||
|
* [func ChompLeftF](#func--chompleftf)
|
||||||
|
* [func ChompRight](#func--chompright)
|
||||||
|
* [func ChompRightF](#func--chomprightf)
|
||||||
|
* [func Classify](#func--classify)
|
||||||
|
* [func ClassifyF](#func--classifyf)
|
||||||
|
* [func Clean](#func--clean)
|
||||||
|
* [func Dasherize](#func--dasherize)
|
||||||
|
* [func DecodeHTMLEntities](#func--decodehtmlentities)
|
||||||
|
* [func EnsurePrefix](#func--ensureprefix)
|
||||||
|
* [func EnsurePrefixF](#func--ensureprefixf)
|
||||||
|
* [func EnsureSuffix](#func--ensuresuffix)
|
||||||
|
* [func EnsureSuffixF](#func--ensuresuffixf)
|
||||||
|
* [func EscapeHTML](#func--escapehtml)
|
||||||
|
* [func Humanize](#func--humanize)
|
||||||
|
* [func Iif](#func--iif)
|
||||||
|
* [func IndexOf](#func--indexof)
|
||||||
|
* [func IsAlpha](#func--isalpha)
|
||||||
|
* [func IsAlphaNumeric](#func--isalphanumeric)
|
||||||
|
* [func IsEmpty](#func--isempty)
|
||||||
|
* [func IsLower](#func--islower)
|
||||||
|
* [func IsNumeric](#func--isnumeric)
|
||||||
|
* [func IsUpper](#func--isupper)
|
||||||
|
* [func Left](#func--left)
|
||||||
|
* [func LeftF](#func--leftf)
|
||||||
|
* [func LeftOf](#func--leftof)
|
||||||
|
* [func Letters](#func--letters)
|
||||||
|
* [func Lines](#func--lines)
|
||||||
|
* [func Map](#func--map)
|
||||||
|
* [func Match](#func--match)
|
||||||
|
* [func Pad](#func--pad)
|
||||||
|
* [func PadF](#func--padf)
|
||||||
|
* [func PadLeft](#func--padleft)
|
||||||
|
* [func PadLeftF](#func--padleftf)
|
||||||
|
* [func PadRight](#func--padright)
|
||||||
|
* [func PadRightF](#func--padrightf)
|
||||||
|
* [func Pipe](#func--pipe)
|
||||||
|
* [func QuoteItems](#func--quoteitems)
|
||||||
|
* [func ReplaceF](#func--replacef)
|
||||||
|
* [func ReplacePattern](#func--replacepattern)
|
||||||
|
* [func ReplacePatternF](#func--replacepatternf)
|
||||||
|
* [func Reverse](#func--reverse)
|
||||||
|
* [func Right](#func--right)
|
||||||
|
* [func RightF](#func--rightf)
|
||||||
|
* [func RightOf](#func--rightof)
|
||||||
|
* [func SetTemplateDelimiters](#func--settemplatedelimiters)
|
||||||
|
* [func Slice](#func--slice)
|
||||||
|
* [func SliceContains](#func--slicecontains)
|
||||||
|
* [func SliceF](#func--slicef)
|
||||||
|
* [func SliceIndexOf](#func--sliceindexof)
|
||||||
|
* [func Slugify](#func--slugify)
|
||||||
|
* [func StripPunctuation](#func--strippunctuation)
|
||||||
|
* [func StripTags](#func--striptags)
|
||||||
|
* [func Substr](#func--substr)
|
||||||
|
* [func SubstrF](#func--substrf)
|
||||||
|
* [func Template](#func--template)
|
||||||
|
* [func TemplateDelimiters](#func--templatedelimiters)
|
||||||
|
* [func TemplateWithDelimiters](#func--templatewithdelimiters)
|
||||||
|
* [func ToArgv](#func--toargv)
|
||||||
|
* [func ToBool](#func--tobool)
|
||||||
|
* [func ToBoolOr](#func--toboolor)
|
||||||
|
* [func ToFloat32Or](#func--tofloat32or)
|
||||||
|
* [func ToFloat64Or](#func--tofloat64or)
|
||||||
|
* [func ToIntOr](#func--tointor)
|
||||||
|
* [func Underscore](#func--underscore)
|
||||||
|
* [func UnescapeHTML](#func--unescapehtml)
|
||||||
|
* [func WrapHTML](#func--wraphtml)
|
||||||
|
* [func WrapHTMLF](#func--wraphtmlf)
|
||||||
|
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
```go
|
||||||
|
var ToFloatOr = ToFloat64Or
|
||||||
|
```
|
||||||
|
ToFloatOr parses as a float64 or returns defaultValue.
|
||||||
|
|
||||||
|
```go
|
||||||
|
var Verbose = false
|
||||||
|
```
|
||||||
|
Verbose flag enables console output for those functions that have counterparts
|
||||||
|
in Go's excellent stadard packages.
|
||||||
|
|
||||||
|
#### func [Between](#between)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Between(s, left, right string) string
|
||||||
|
```
|
||||||
|
Between extracts a string between left and right strings.
|
||||||
|
|
||||||
|
#### func [BetweenF](#betweenf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func BetweenF(left, right string) func(string) string
|
||||||
|
```
|
||||||
|
BetweenF is the filter form for Between.
|
||||||
|
|
||||||
|
#### func [Camelize](#camelize)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Camelize(s string) string
|
||||||
|
```
|
||||||
|
Camelize return new string which removes any underscores or dashes and convert a
|
||||||
|
string into camel casing.
|
||||||
|
|
||||||
|
#### func [Capitalize](#capitalize)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Capitalize(s string) string
|
||||||
|
```
|
||||||
|
Capitalize uppercases the first char of s and lowercases the rest.
|
||||||
|
|
||||||
|
#### func [CharAt](#charat)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CharAt(s string, index int) string
|
||||||
|
```
|
||||||
|
CharAt returns a string from the character at the specified position.
|
||||||
|
|
||||||
|
#### func [CharAtF](#charatf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CharAtF(index int) func(string) string
|
||||||
|
```
|
||||||
|
CharAtF is the filter form of CharAt.
|
||||||
|
|
||||||
|
#### func [ChompLeft](#chompleft)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ChompLeft(s, prefix string) string
|
||||||
|
```
|
||||||
|
ChompLeft removes prefix at the start of a string.
|
||||||
|
|
||||||
|
#### func [ChompLeftF](#chompleftf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ChompLeftF(prefix string) func(string) string
|
||||||
|
```
|
||||||
|
ChompLeftF is the filter form of ChompLeft.
|
||||||
|
|
||||||
|
#### func [ChompRight](#chompright)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ChompRight(s, suffix string) string
|
||||||
|
```
|
||||||
|
ChompRight removes suffix from end of s.
|
||||||
|
|
||||||
|
#### func [ChompRightF](#chomprightf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ChompRightF(suffix string) func(string) string
|
||||||
|
```
|
||||||
|
ChompRightF is the filter form of ChompRight.
|
||||||
|
|
||||||
|
#### func [Classify](#classify)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Classify(s string) string
|
||||||
|
```
|
||||||
|
Classify returns a camelized string with the first letter upper cased.
|
||||||
|
|
||||||
|
#### func [ClassifyF](#classifyf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ClassifyF(s string) func(string) string
|
||||||
|
```
|
||||||
|
ClassifyF is the filter form of Classify.
|
||||||
|
|
||||||
|
#### func [Clean](#clean)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Clean(s string) string
|
||||||
|
```
|
||||||
|
Clean compresses all adjacent whitespace to a single space and trims s.
|
||||||
|
|
||||||
|
#### func [Dasherize](#dasherize)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Dasherize(s string) string
|
||||||
|
```
|
||||||
|
Dasherize converts a camel cased string into a string delimited by dashes.
|
||||||
|
|
||||||
|
#### func [DecodeHTMLEntities](#decodehtmlentities)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func DecodeHTMLEntities(s string) string
|
||||||
|
```
|
||||||
|
DecodeHTMLEntities decodes HTML entities into their proper string
|
||||||
|
representation. DecodeHTMLEntities is an alias for html.UnescapeString
|
||||||
|
|
||||||
|
#### func [EnsurePrefix](#ensureprefix)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func EnsurePrefix(s, prefix string) string
|
||||||
|
```
|
||||||
|
EnsurePrefix ensures s starts with prefix.
|
||||||
|
|
||||||
|
#### func [EnsurePrefixF](#ensureprefixf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func EnsurePrefixF(prefix string) func(string) string
|
||||||
|
```
|
||||||
|
EnsurePrefixF is the filter form of EnsurePrefix.
|
||||||
|
|
||||||
|
#### func [EnsureSuffix](#ensuresuffix)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func EnsureSuffix(s, suffix string) string
|
||||||
|
```
|
||||||
|
EnsureSuffix ensures s ends with suffix.
|
||||||
|
|
||||||
|
#### func [EnsureSuffixF](#ensuresuffixf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func EnsureSuffixF(suffix string) func(string) string
|
||||||
|
```
|
||||||
|
EnsureSuffixF is the filter form of EnsureSuffix.
|
||||||
|
|
||||||
|
#### func [EscapeHTML](#escapehtml)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func EscapeHTML(s string) string
|
||||||
|
```
|
||||||
|
EscapeHTML is alias for html.EscapeString.
|
||||||
|
|
||||||
|
#### func [Humanize](#humanize)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Humanize(s string) string
|
||||||
|
```
|
||||||
|
Humanize transforms s into a human friendly form.
|
||||||
|
|
||||||
|
#### func [Iif](#iif)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Iif(condition bool, truthy string, falsey string) string
|
||||||
|
```
|
||||||
|
Iif is short for immediate if. If condition is true return truthy else falsey.
|
||||||
|
|
||||||
|
#### func [IndexOf](#indexof)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IndexOf(s string, needle string, start int) int
|
||||||
|
```
|
||||||
|
IndexOf finds the index of needle in s starting from start.
|
||||||
|
|
||||||
|
#### func [IsAlpha](#isalpha)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsAlpha(s string) bool
|
||||||
|
```
|
||||||
|
IsAlpha returns true if a string contains only letters from ASCII (a-z,A-Z).
|
||||||
|
Other letters from other languages are not supported.
|
||||||
|
|
||||||
|
#### func [IsAlphaNumeric](#isalphanumeric)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsAlphaNumeric(s string) bool
|
||||||
|
```
|
||||||
|
IsAlphaNumeric returns true if a string contains letters and digits.
|
||||||
|
|
||||||
|
#### func [IsEmpty](#isempty)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsEmpty(s string) bool
|
||||||
|
```
|
||||||
|
IsEmpty returns true if the string is solely composed of whitespace.
|
||||||
|
|
||||||
|
#### func [IsLower](#islower)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsLower(s string) bool
|
||||||
|
```
|
||||||
|
IsLower returns true if s comprised of all lower case characters.
|
||||||
|
|
||||||
|
#### func [IsNumeric](#isnumeric)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsNumeric(s string) bool
|
||||||
|
```
|
||||||
|
IsNumeric returns true if a string contains only digits from 0-9. Other digits
|
||||||
|
not in Latin (such as Arabic) are not currently supported.
|
||||||
|
|
||||||
|
#### func [IsUpper](#isupper)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsUpper(s string) bool
|
||||||
|
```
|
||||||
|
IsUpper returns true if s contains all upper case chracters.
|
||||||
|
|
||||||
|
#### func [Left](#left)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Left(s string, n int) string
|
||||||
|
```
|
||||||
|
Left returns the left substring of length n.
|
||||||
|
|
||||||
|
#### func [LeftF](#leftf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LeftF(n int) func(string) string
|
||||||
|
```
|
||||||
|
LeftF is the filter form of Left.
|
||||||
|
|
||||||
|
#### func [LeftOf](#leftof)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LeftOf(s string, needle string) string
|
||||||
|
```
|
||||||
|
LeftOf returns the substring left of needle.
|
||||||
|
|
||||||
|
#### func [Letters](#letters)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Letters(s string) []string
|
||||||
|
```
|
||||||
|
Letters returns an array of runes as strings so it can be indexed into.
|
||||||
|
|
||||||
|
#### func [Lines](#lines)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Lines(s string) []string
|
||||||
|
```
|
||||||
|
Lines convert windows newlines to unix newlines then convert to an Array of
|
||||||
|
lines.
|
||||||
|
|
||||||
|
#### func [Map](#map)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Map(arr []string, iterator func(string) string) []string
|
||||||
|
```
|
||||||
|
Map maps an array's iitem through an iterator.
|
||||||
|
|
||||||
|
#### func [Match](#match)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Match(s, pattern string) bool
|
||||||
|
```
|
||||||
|
Match returns true if patterns matches the string
|
||||||
|
|
||||||
|
#### func [Pad](#pad)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Pad(s, c string, n int) string
|
||||||
|
```
|
||||||
|
Pad pads string s on both sides with c until it has length of n.
|
||||||
|
|
||||||
|
#### func [PadF](#padf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func PadF(c string, n int) func(string) string
|
||||||
|
```
|
||||||
|
PadF is the filter form of Pad.
|
||||||
|
|
||||||
|
#### func [PadLeft](#padleft)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func PadLeft(s, c string, n int) string
|
||||||
|
```
|
||||||
|
PadLeft pads s on left side with c until it has length of n.
|
||||||
|
|
||||||
|
#### func [PadLeftF](#padleftf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func PadLeftF(c string, n int) func(string) string
|
||||||
|
```
|
||||||
|
PadLeftF is the filter form of PadLeft.
|
||||||
|
|
||||||
|
#### func [PadRight](#padright)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func PadRight(s, c string, n int) string
|
||||||
|
```
|
||||||
|
PadRight pads s on right side with c until it has length of n.
|
||||||
|
|
||||||
|
#### func [PadRightF](#padrightf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func PadRightF(c string, n int) func(string) string
|
||||||
|
```
|
||||||
|
PadRightF is the filter form of Padright
|
||||||
|
|
||||||
|
#### func [Pipe](#pipe)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Pipe(s string, funcs ...func(string) string) string
|
||||||
|
```
|
||||||
|
Pipe pipes s through one or more string filters.
|
||||||
|
|
||||||
|
#### func [QuoteItems](#quoteitems)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func QuoteItems(arr []string) []string
|
||||||
|
```
|
||||||
|
QuoteItems quotes all items in array, mostly for debugging.
|
||||||
|
|
||||||
|
#### func [ReplaceF](#replacef)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ReplaceF(old, new string, n int) func(string) string
|
||||||
|
```
|
||||||
|
ReplaceF is the filter form of strings.Replace.
|
||||||
|
|
||||||
|
#### func [ReplacePattern](#replacepattern)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ReplacePattern(s, pattern, repl string) string
|
||||||
|
```
|
||||||
|
ReplacePattern replaces string with regexp string. ReplacePattern returns a copy
|
||||||
|
of src, replacing matches of the Regexp with the replacement string repl. Inside
|
||||||
|
repl, $ signs are interpreted as in Expand, so for instance $1 represents the
|
||||||
|
text of the first submatch.
|
||||||
|
|
||||||
|
#### func [ReplacePatternF](#replacepatternf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ReplacePatternF(pattern, repl string) func(string) string
|
||||||
|
```
|
||||||
|
ReplacePatternF is the filter form of ReplaceRegexp.
|
||||||
|
|
||||||
|
#### func [Reverse](#reverse)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Reverse(s string) string
|
||||||
|
```
|
||||||
|
Reverse a string
|
||||||
|
|
||||||
|
#### func [Right](#right)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Right(s string, n int) string
|
||||||
|
```
|
||||||
|
Right returns the right substring of length n.
|
||||||
|
|
||||||
|
#### func [RightF](#rightf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RightF(n int) func(string) string
|
||||||
|
```
|
||||||
|
RightF is the Filter version of Right.
|
||||||
|
|
||||||
|
#### func [RightOf](#rightof)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RightOf(s string, prefix string) string
|
||||||
|
```
|
||||||
|
RightOf returns the substring to the right of prefix.
|
||||||
|
|
||||||
|
#### func [SetTemplateDelimiters](#settemplatedelimiters)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SetTemplateDelimiters(opening, closing string)
|
||||||
|
```
|
||||||
|
SetTemplateDelimiters sets the delimiters for Template function. Defaults to
|
||||||
|
"{{" and "}}"
|
||||||
|
|
||||||
|
#### func [Slice](#slice)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Slice(s string, start, end int) string
|
||||||
|
```
|
||||||
|
Slice slices a string. If end is negative then it is the from the end of the
|
||||||
|
string.
|
||||||
|
|
||||||
|
#### func [SliceContains](#slicecontains)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SliceContains(slice []string, val string) bool
|
||||||
|
```
|
||||||
|
SliceContains determines whether val is an element in slice.
|
||||||
|
|
||||||
|
#### func [SliceF](#slicef)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SliceF(start, end int) func(string) string
|
||||||
|
```
|
||||||
|
SliceF is the filter for Slice.
|
||||||
|
|
||||||
|
#### func [SliceIndexOf](#sliceindexof)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SliceIndexOf(slice []string, val string) int
|
||||||
|
```
|
||||||
|
SliceIndexOf gets the indx of val in slice. Returns -1 if not found.
|
||||||
|
|
||||||
|
#### func [Slugify](#slugify)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Slugify(s string) string
|
||||||
|
```
|
||||||
|
Slugify converts s into a dasherized string suitable for URL segment.
|
||||||
|
|
||||||
|
#### func [StripPunctuation](#strippunctuation)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func StripPunctuation(s string) string
|
||||||
|
```
|
||||||
|
StripPunctuation strips puncation from string.
|
||||||
|
|
||||||
|
#### func [StripTags](#striptags)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func StripTags(s string, tags ...string) string
|
||||||
|
```
|
||||||
|
StripTags strips all of the html tags or tags specified by the parameters
|
||||||
|
|
||||||
|
#### func [Substr](#substr)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Substr(s string, index int, n int) string
|
||||||
|
```
|
||||||
|
Substr returns a substring of s starting at index of length n.
|
||||||
|
|
||||||
|
#### func [SubstrF](#substrf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SubstrF(index, n int) func(string) string
|
||||||
|
```
|
||||||
|
SubstrF is the filter form of Substr.
|
||||||
|
|
||||||
|
#### func [Template](#template)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Template(s string, values map[string]interface{}) string
|
||||||
|
```
|
||||||
|
Template is a string template which replaces template placeholders delimited by
|
||||||
|
"{{" and "}}" with values from map. The global delimiters may be set with
|
||||||
|
SetTemplateDelimiters.
|
||||||
|
|
||||||
|
#### func [TemplateDelimiters](#templatedelimiters)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TemplateDelimiters() (opening string, closing string)
|
||||||
|
```
|
||||||
|
TemplateDelimiters is the getter for the opening and closing delimiters for
|
||||||
|
Template.
|
||||||
|
|
||||||
|
#### func [TemplateWithDelimiters](#templatewithdelimiters)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TemplateWithDelimiters(s string, values map[string]interface{}, opening, closing string) string
|
||||||
|
```
|
||||||
|
TemplateWithDelimiters is string template with user-defineable opening and
|
||||||
|
closing delimiters.
|
||||||
|
|
||||||
|
#### func [ToArgv](#toargv)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToArgv(s string) []string
|
||||||
|
```
|
||||||
|
ToArgv converts string s into an argv for exec.
|
||||||
|
|
||||||
|
#### func [ToBool](#tobool)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToBool(s string) bool
|
||||||
|
```
|
||||||
|
ToBool fuzzily converts truthy values.
|
||||||
|
|
||||||
|
#### func [ToBoolOr](#toboolor)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToBoolOr(s string, defaultValue bool) bool
|
||||||
|
```
|
||||||
|
ToBoolOr parses s as a bool or returns defaultValue.
|
||||||
|
|
||||||
|
#### func [ToFloat32Or](#tofloat32or)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToFloat32Or(s string, defaultValue float32) float32
|
||||||
|
```
|
||||||
|
ToFloat32Or parses as a float32 or returns defaultValue on error.
|
||||||
|
|
||||||
|
#### func [ToFloat64Or](#tofloat64or)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToFloat64Or(s string, defaultValue float64) float64
|
||||||
|
```
|
||||||
|
ToFloat64Or parses s as a float64 or returns defaultValue.
|
||||||
|
|
||||||
|
#### func [ToIntOr](#tointor)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToIntOr(s string, defaultValue int) int
|
||||||
|
```
|
||||||
|
ToIntOr parses s as an int or returns defaultValue.
|
||||||
|
|
||||||
|
#### func [Underscore](#underscore)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Underscore(s string) string
|
||||||
|
```
|
||||||
|
Underscore returns converted camel cased string into a string delimited by
|
||||||
|
underscores.
|
||||||
|
|
||||||
|
#### func [UnescapeHTML](#unescapehtml)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UnescapeHTML(s string) string
|
||||||
|
```
|
||||||
|
UnescapeHTML is an alias for html.UnescapeString.
|
||||||
|
|
||||||
|
#### func [WrapHTML](#wraphtml)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func WrapHTML(s string, tag string, attrs map[string]string) string
|
||||||
|
```
|
||||||
|
WrapHTML wraps s within HTML tag having attributes attrs. Note, WrapHTML does
|
||||||
|
not escape s value.
|
||||||
|
|
||||||
|
#### func [WrapHTMLF](#wraphtmlf)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func WrapHTMLF(tag string, attrs map[string]string) func(string) string
|
||||||
|
```
|
||||||
|
WrapHTMLF is the filter form of WrapHTML.
|
1
vendor/github.com/mgutz/str/VERSION
generated
vendored
Normal file
1
vendor/github.com/mgutz/str/VERSION
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
1.1.0
|
19
vendor/github.com/mgutz/str/doc.go
generated
vendored
Normal file
19
vendor/github.com/mgutz/str/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// Package str is a comprehensive set of string functions to build more
|
||||||
|
// Go awesomeness. Str complements Go's standard packages and does not duplicate
|
||||||
|
// functionality found in `strings` or `strconv`.
|
||||||
|
//
|
||||||
|
// Str is based on plain functions instead of object-based methods,
|
||||||
|
// consistent with Go standard string packages.
|
||||||
|
//
|
||||||
|
// str.Between("<a>foo</a>", "<a>", "</a>") == "foo"
|
||||||
|
//
|
||||||
|
// Str supports pipelining instead of chaining
|
||||||
|
//
|
||||||
|
// s := str.Pipe("\nabcdef\n", Clean, BetweenF("a", "f"), ChompLeftF("bc"))
|
||||||
|
//
|
||||||
|
// User-defined filters can be added to the pipeline by inserting a function
|
||||||
|
// or closure that returns a function with this signature
|
||||||
|
//
|
||||||
|
// func(string) string
|
||||||
|
//
|
||||||
|
package str
|
337
vendor/github.com/mgutz/str/funcsAO.go
generated
vendored
Normal file
337
vendor/github.com/mgutz/str/funcsAO.go
generated
vendored
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
package str
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
//"log"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verbose flag enables console output for those functions that have
|
||||||
|
// counterparts in Go's excellent stadard packages.
|
||||||
|
var Verbose = false
|
||||||
|
var templateOpen = "{{"
|
||||||
|
var templateClose = "}}"
|
||||||
|
|
||||||
|
var beginEndSpacesRe = regexp.MustCompile("^\\s+|\\s+$")
|
||||||
|
var camelizeRe = regexp.MustCompile(`(\-|_|\s)+(.)?`)
|
||||||
|
var camelizeRe2 = regexp.MustCompile(`(\-|_|\s)+`)
|
||||||
|
var capitalsRe = regexp.MustCompile("([A-Z])")
|
||||||
|
var dashSpaceRe = regexp.MustCompile(`[-\s]+`)
|
||||||
|
var dashesRe = regexp.MustCompile("-+")
|
||||||
|
var isAlphaNumericRe = regexp.MustCompile(`[^0-9a-z\xC0-\xFF]`)
|
||||||
|
var isAlphaRe = regexp.MustCompile(`[^a-z\xC0-\xFF]`)
|
||||||
|
var nWhitespaceRe = regexp.MustCompile(`\s+`)
|
||||||
|
var notDigitsRe = regexp.MustCompile(`[^0-9]`)
|
||||||
|
var slugifyRe = regexp.MustCompile(`[^\w\s\-]`)
|
||||||
|
var spaceUnderscoreRe = regexp.MustCompile("[_\\s]+")
|
||||||
|
var spacesRe = regexp.MustCompile("[\\s\\xA0]+")
|
||||||
|
var stripPuncRe = regexp.MustCompile(`[^\w\s]|_`)
|
||||||
|
var templateRe = regexp.MustCompile(`([\-\[\]()*\s])`)
|
||||||
|
var templateRe2 = regexp.MustCompile(`\$`)
|
||||||
|
var underscoreRe = regexp.MustCompile(`([a-z\d])([A-Z]+)`)
|
||||||
|
var whitespaceRe = regexp.MustCompile(`^[\s\xa0]*$`)
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Between extracts a string between left and right strings.
|
||||||
|
func Between(s, left, right string) string {
|
||||||
|
l := len(left)
|
||||||
|
startPos := strings.Index(s, left)
|
||||||
|
if startPos < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
endPos := IndexOf(s, right, startPos+l)
|
||||||
|
//log.Printf("%s: left %s right %s start %d end %d", s, left, right, startPos+l, endPos)
|
||||||
|
if endPos < 0 {
|
||||||
|
return ""
|
||||||
|
} else if right == "" {
|
||||||
|
return s[endPos:]
|
||||||
|
} else {
|
||||||
|
return s[startPos+l : endPos]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BetweenF is the filter form for Between.
|
||||||
|
func BetweenF(left, right string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return Between(s, left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camelize return new string which removes any underscores or dashes and convert a string into camel casing.
|
||||||
|
func Camelize(s string) string {
|
||||||
|
return camelizeRe.ReplaceAllStringFunc(s, func(val string) string {
|
||||||
|
val = strings.ToUpper(val)
|
||||||
|
val = camelizeRe2.ReplaceAllString(val, "")
|
||||||
|
return val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capitalize uppercases the first char of s and lowercases the rest.
|
||||||
|
func Capitalize(s string) string {
|
||||||
|
return strings.ToUpper(s[0:1]) + strings.ToLower(s[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// CharAt returns a string from the character at the specified position.
|
||||||
|
func CharAt(s string, index int) string {
|
||||||
|
l := len(s)
|
||||||
|
shortcut := index < 0 || index > l-1 || l == 0
|
||||||
|
if shortcut {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s[index : index+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CharAtF is the filter form of CharAt.
|
||||||
|
func CharAtF(index int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return CharAt(s, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChompLeft removes prefix at the start of a string.
|
||||||
|
func ChompLeft(s, prefix string) string {
|
||||||
|
if strings.HasPrefix(s, prefix) {
|
||||||
|
return s[len(prefix):]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChompLeftF is the filter form of ChompLeft.
|
||||||
|
func ChompLeftF(prefix string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return ChompLeft(s, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChompRight removes suffix from end of s.
|
||||||
|
func ChompRight(s, suffix string) string {
|
||||||
|
if strings.HasSuffix(s, suffix) {
|
||||||
|
return s[:len(s)-len(suffix)]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChompRightF is the filter form of ChompRight.
|
||||||
|
func ChompRightF(suffix string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return ChompRight(s, suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Classify returns a camelized string with the first letter upper cased.
|
||||||
|
func Classify(s string) string {
|
||||||
|
return Camelize("-" + s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClassifyF is the filter form of Classify.
|
||||||
|
func ClassifyF(s string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return Classify(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean compresses all adjacent whitespace to a single space and trims s.
|
||||||
|
func Clean(s string) string {
|
||||||
|
s = spacesRe.ReplaceAllString(s, " ")
|
||||||
|
s = beginEndSpacesRe.ReplaceAllString(s, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dasherize converts a camel cased string into a string delimited by dashes.
|
||||||
|
func Dasherize(s string) string {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
s = spaceUnderscoreRe.ReplaceAllString(s, "-")
|
||||||
|
s = capitalsRe.ReplaceAllString(s, "-$1")
|
||||||
|
s = dashesRe.ReplaceAllString(s, "-")
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// EscapeHTML is alias for html.EscapeString.
|
||||||
|
func EscapeHTML(s string) string {
|
||||||
|
if Verbose {
|
||||||
|
fmt.Println("Use html.EscapeString instead of EscapeHTML")
|
||||||
|
}
|
||||||
|
return html.EscapeString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeHTMLEntities decodes HTML entities into their proper string representation.
|
||||||
|
// DecodeHTMLEntities is an alias for html.UnescapeString
|
||||||
|
func DecodeHTMLEntities(s string) string {
|
||||||
|
if Verbose {
|
||||||
|
fmt.Println("Use html.UnescapeString instead of DecodeHTMLEntities")
|
||||||
|
}
|
||||||
|
return html.UnescapeString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsurePrefix ensures s starts with prefix.
|
||||||
|
func EnsurePrefix(s, prefix string) string {
|
||||||
|
if strings.HasPrefix(s, prefix) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return prefix + s
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsurePrefixF is the filter form of EnsurePrefix.
|
||||||
|
func EnsurePrefixF(prefix string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return EnsurePrefix(s, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureSuffix ensures s ends with suffix.
|
||||||
|
func EnsureSuffix(s, suffix string) string {
|
||||||
|
if strings.HasSuffix(s, suffix) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s + suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureSuffixF is the filter form of EnsureSuffix.
|
||||||
|
func EnsureSuffixF(suffix string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return EnsureSuffix(s, suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Humanize transforms s into a human friendly form.
|
||||||
|
func Humanize(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
s = Underscore(s)
|
||||||
|
var humanizeRe = regexp.MustCompile(`_id$`)
|
||||||
|
s = humanizeRe.ReplaceAllString(s, "")
|
||||||
|
s = strings.Replace(s, "_", " ", -1)
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
s = Capitalize(s)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iif is short for immediate if. If condition is true return truthy else falsey.
|
||||||
|
func Iif(condition bool, truthy string, falsey string) string {
|
||||||
|
if condition {
|
||||||
|
return truthy
|
||||||
|
}
|
||||||
|
return falsey
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOf finds the index of needle in s starting from start.
|
||||||
|
func IndexOf(s string, needle string, start int) int {
|
||||||
|
l := len(s)
|
||||||
|
if needle == "" {
|
||||||
|
if start < 0 {
|
||||||
|
return 0
|
||||||
|
} else if start < l {
|
||||||
|
return start
|
||||||
|
} else {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start < 0 || start > l-1 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
pos := strings.Index(s[start:], needle)
|
||||||
|
if pos == -1 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return start + pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlpha returns true if a string contains only letters from ASCII (a-z,A-Z). Other letters from other languages are not supported.
|
||||||
|
func IsAlpha(s string) bool {
|
||||||
|
return !isAlphaRe.MatchString(strings.ToLower(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlphaNumeric returns true if a string contains letters and digits.
|
||||||
|
func IsAlphaNumeric(s string) bool {
|
||||||
|
return !isAlphaNumericRe.MatchString(strings.ToLower(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLower returns true if s comprised of all lower case characters.
|
||||||
|
func IsLower(s string) bool {
|
||||||
|
return IsAlpha(s) && s == strings.ToLower(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNumeric returns true if a string contains only digits from 0-9. Other digits not in Latin (such as Arabic) are not currently supported.
|
||||||
|
func IsNumeric(s string) bool {
|
||||||
|
return !notDigitsRe.MatchString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpper returns true if s contains all upper case chracters.
|
||||||
|
func IsUpper(s string) bool {
|
||||||
|
return IsAlpha(s) && s == strings.ToUpper(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if the string is solely composed of whitespace.
|
||||||
|
func IsEmpty(s string) bool {
|
||||||
|
if s == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return whitespaceRe.MatchString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left returns the left substring of length n.
|
||||||
|
func Left(s string, n int) string {
|
||||||
|
if n < 0 {
|
||||||
|
return Right(s, -n)
|
||||||
|
}
|
||||||
|
return Substr(s, 0, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftF is the filter form of Left.
|
||||||
|
func LeftF(n int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return Left(s, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftOf returns the substring left of needle.
|
||||||
|
func LeftOf(s string, needle string) string {
|
||||||
|
return Between(s, "", needle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Letters returns an array of runes as strings so it can be indexed into.
|
||||||
|
func Letters(s string) []string {
|
||||||
|
result := []string{}
|
||||||
|
for _, r := range s {
|
||||||
|
result = append(result, string(r))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lines convert windows newlines to unix newlines then convert to an Array of lines.
|
||||||
|
func Lines(s string) []string {
|
||||||
|
s = strings.Replace(s, "\r\n", "\n", -1)
|
||||||
|
return strings.Split(s, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map maps an array's iitem through an iterator.
|
||||||
|
func Map(arr []string, iterator func(string) string) []string {
|
||||||
|
r := []string{}
|
||||||
|
for _, item := range arr {
|
||||||
|
r = append(r, iterator(item))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if patterns matches the string
|
||||||
|
func Match(s, pattern string) bool {
|
||||||
|
r := regexp.MustCompile(pattern)
|
||||||
|
return r.MatchString(s)
|
||||||
|
}
|
534
vendor/github.com/mgutz/str/funcsPZ.go
generated
vendored
Normal file
534
vendor/github.com/mgutz/str/funcsPZ.go
generated
vendored
Normal file
|
@ -0,0 +1,534 @@
|
||||||
|
package str
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
//"log"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pad pads string s on both sides with c until it has length of n.
|
||||||
|
func Pad(s, c string, n int) string {
|
||||||
|
L := len(s)
|
||||||
|
if L >= n {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
n -= L
|
||||||
|
|
||||||
|
left := strings.Repeat(c, int(math.Ceil(float64(n)/2)))
|
||||||
|
right := strings.Repeat(c, int(math.Floor(float64(n)/2)))
|
||||||
|
return left + s + right
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadF is the filter form of Pad.
|
||||||
|
func PadF(c string, n int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return Pad(s, c, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadLeft pads s on left side with c until it has length of n.
|
||||||
|
func PadLeft(s, c string, n int) string {
|
||||||
|
L := len(s)
|
||||||
|
if L > n {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return strings.Repeat(c, (n-L)) + s
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadLeftF is the filter form of PadLeft.
|
||||||
|
func PadLeftF(c string, n int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return PadLeft(s, c, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadRight pads s on right side with c until it has length of n.
|
||||||
|
func PadRight(s, c string, n int) string {
|
||||||
|
L := len(s)
|
||||||
|
if L > n {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s + strings.Repeat(c, n-L)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadRightF is the filter form of Padright
|
||||||
|
func PadRightF(c string, n int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return PadRight(s, c, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pipe pipes s through one or more string filters.
|
||||||
|
func Pipe(s string, funcs ...func(string) string) string {
|
||||||
|
for _, fn := range funcs {
|
||||||
|
s = fn(s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuoteItems quotes all items in array, mostly for debugging.
|
||||||
|
func QuoteItems(arr []string) []string {
|
||||||
|
return Map(arr, func(s string) string {
|
||||||
|
return strconv.Quote(s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceF is the filter form of strings.Replace.
|
||||||
|
func ReplaceF(old, new string, n int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return strings.Replace(s, old, new, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplacePattern replaces string with regexp string.
|
||||||
|
// ReplacePattern returns a copy of src, replacing matches of the Regexp with the replacement string repl. Inside repl, $ signs are interpreted as in Expand, so for instance $1 represents the text of the first submatch.
|
||||||
|
func ReplacePattern(s, pattern, repl string) string {
|
||||||
|
r := regexp.MustCompile(pattern)
|
||||||
|
return r.ReplaceAllString(s, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplacePatternF is the filter form of ReplaceRegexp.
|
||||||
|
func ReplacePatternF(pattern, repl string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return ReplacePattern(s, pattern, repl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse a string
|
||||||
|
func Reverse(s string) string {
|
||||||
|
cs := make([]rune, utf8.RuneCountInString(s))
|
||||||
|
i := len(cs)
|
||||||
|
for _, c := range s {
|
||||||
|
i--
|
||||||
|
cs[i] = c
|
||||||
|
}
|
||||||
|
return string(cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right returns the right substring of length n.
|
||||||
|
func Right(s string, n int) string {
|
||||||
|
if n < 0 {
|
||||||
|
return Left(s, -n)
|
||||||
|
}
|
||||||
|
return Substr(s, len(s)-n, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightF is the Filter version of Right.
|
||||||
|
func RightF(n int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return Right(s, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightOf returns the substring to the right of prefix.
|
||||||
|
func RightOf(s string, prefix string) string {
|
||||||
|
return Between(s, prefix, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTemplateDelimiters sets the delimiters for Template function. Defaults to "{{" and "}}"
|
||||||
|
func SetTemplateDelimiters(opening, closing string) {
|
||||||
|
templateOpen = opening
|
||||||
|
templateClose = closing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice slices a string. If end is negative then it is the from the end
|
||||||
|
// of the string.
|
||||||
|
func Slice(s string, start, end int) string {
|
||||||
|
if end > -1 {
|
||||||
|
return s[start:end]
|
||||||
|
}
|
||||||
|
L := len(s)
|
||||||
|
if L+end > 0 {
|
||||||
|
return s[start : L-end]
|
||||||
|
}
|
||||||
|
return s[start:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceF is the filter for Slice.
|
||||||
|
func SliceF(start, end int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return Slice(s, start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceContains determines whether val is an element in slice.
|
||||||
|
func SliceContains(slice []string, val string) bool {
|
||||||
|
if slice == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, it := range slice {
|
||||||
|
if it == val {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceIndexOf gets the indx of val in slice. Returns -1 if not found.
|
||||||
|
func SliceIndexOf(slice []string, val string) int {
|
||||||
|
if slice == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, it := range slice {
|
||||||
|
if it == val {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slugify converts s into a dasherized string suitable for URL segment.
|
||||||
|
func Slugify(s string) string {
|
||||||
|
sl := slugifyRe.ReplaceAllString(s, "")
|
||||||
|
sl = strings.ToLower(sl)
|
||||||
|
sl = Dasherize(sl)
|
||||||
|
return sl
|
||||||
|
}
|
||||||
|
|
||||||
|
// StripPunctuation strips puncation from string.
|
||||||
|
func StripPunctuation(s string) string {
|
||||||
|
s = stripPuncRe.ReplaceAllString(s, "")
|
||||||
|
s = nWhitespaceRe.ReplaceAllString(s, " ")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// StripTags strips all of the html tags or tags specified by the parameters
|
||||||
|
func StripTags(s string, tags ...string) string {
|
||||||
|
if len(tags) == 0 {
|
||||||
|
tags = append(tags, "")
|
||||||
|
}
|
||||||
|
for _, tag := range tags {
|
||||||
|
stripTagsRe := regexp.MustCompile(`(?i)<\/?` + tag + `[^<>]*>`)
|
||||||
|
s = stripTagsRe.ReplaceAllString(s, "")
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substr returns a substring of s starting at index of length n.
|
||||||
|
func Substr(s string, index int, n int) string {
|
||||||
|
L := len(s)
|
||||||
|
if index < 0 || index >= L || s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
end := index + n
|
||||||
|
if end >= L {
|
||||||
|
end = L
|
||||||
|
}
|
||||||
|
if end <= index {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s[index:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubstrF is the filter form of Substr.
|
||||||
|
func SubstrF(index, n int) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return Substr(s, index, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template is a string template which replaces template placeholders delimited
|
||||||
|
// by "{{" and "}}" with values from map. The global delimiters may be set with
|
||||||
|
// SetTemplateDelimiters.
|
||||||
|
func Template(s string, values map[string]interface{}) string {
|
||||||
|
return TemplateWithDelimiters(s, values, templateOpen, templateClose)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemplateDelimiters is the getter for the opening and closing delimiters for Template.
|
||||||
|
func TemplateDelimiters() (opening string, closing string) {
|
||||||
|
return templateOpen, templateClose
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemplateWithDelimiters is string template with user-defineable opening and closing delimiters.
|
||||||
|
func TemplateWithDelimiters(s string, values map[string]interface{}, opening, closing string) string {
|
||||||
|
escapeDelimiter := func(delim string) string {
|
||||||
|
result := templateRe.ReplaceAllString(delim, "\\$1")
|
||||||
|
return templateRe2.ReplaceAllString(result, "\\$")
|
||||||
|
}
|
||||||
|
|
||||||
|
openingDelim := escapeDelimiter(opening)
|
||||||
|
closingDelim := escapeDelimiter(closing)
|
||||||
|
r := regexp.MustCompile(openingDelim + `(.+?)` + closingDelim)
|
||||||
|
matches := r.FindAllStringSubmatch(s, -1)
|
||||||
|
for _, submatches := range matches {
|
||||||
|
match := submatches[0]
|
||||||
|
key := submatches[1]
|
||||||
|
//log.Printf("match %s key %s\n", match, key)
|
||||||
|
if values[key] != nil {
|
||||||
|
v := fmt.Sprintf("%v", values[key])
|
||||||
|
s = strings.Replace(s, match, v, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToArgv converts string s into an argv for exec.
|
||||||
|
func ToArgv(s string) []string {
|
||||||
|
const (
|
||||||
|
InArg = iota
|
||||||
|
InArgQuote
|
||||||
|
OutOfArg
|
||||||
|
)
|
||||||
|
currentState := OutOfArg
|
||||||
|
currentQuoteChar := "\x00" // to distinguish between ' and " quotations
|
||||||
|
// this allows to use "foo'bar"
|
||||||
|
currentArg := ""
|
||||||
|
argv := []string{}
|
||||||
|
|
||||||
|
isQuote := func(c string) bool {
|
||||||
|
return c == `"` || c == `'`
|
||||||
|
}
|
||||||
|
|
||||||
|
isEscape := func(c string) bool {
|
||||||
|
return c == `\`
|
||||||
|
}
|
||||||
|
|
||||||
|
isWhitespace := func(c string) bool {
|
||||||
|
return c == " " || c == "\t"
|
||||||
|
}
|
||||||
|
|
||||||
|
L := len(s)
|
||||||
|
for i := 0; i < L; i++ {
|
||||||
|
c := s[i : i+1]
|
||||||
|
|
||||||
|
//fmt.Printf("c %s state %v arg %s argv %v i %d\n", c, currentState, currentArg, args, i)
|
||||||
|
if isQuote(c) {
|
||||||
|
switch currentState {
|
||||||
|
case OutOfArg:
|
||||||
|
currentArg = ""
|
||||||
|
fallthrough
|
||||||
|
case InArg:
|
||||||
|
currentState = InArgQuote
|
||||||
|
currentQuoteChar = c
|
||||||
|
|
||||||
|
case InArgQuote:
|
||||||
|
if c == currentQuoteChar {
|
||||||
|
currentState = InArg
|
||||||
|
} else {
|
||||||
|
currentArg += c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if isWhitespace(c) {
|
||||||
|
switch currentState {
|
||||||
|
case InArg:
|
||||||
|
argv = append(argv, currentArg)
|
||||||
|
currentState = OutOfArg
|
||||||
|
case InArgQuote:
|
||||||
|
currentArg += c
|
||||||
|
case OutOfArg:
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if isEscape(c) {
|
||||||
|
switch currentState {
|
||||||
|
case OutOfArg:
|
||||||
|
currentArg = ""
|
||||||
|
currentState = InArg
|
||||||
|
fallthrough
|
||||||
|
case InArg:
|
||||||
|
fallthrough
|
||||||
|
case InArgQuote:
|
||||||
|
if i == L-1 {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// just add \ to end for windows
|
||||||
|
currentArg += c
|
||||||
|
} else {
|
||||||
|
panic("Escape character at end string")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
peek := s[i+1 : i+2]
|
||||||
|
if peek != `"` {
|
||||||
|
currentArg += c
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
c = s[i : i+1]
|
||||||
|
currentArg += c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch currentState {
|
||||||
|
case InArg, InArgQuote:
|
||||||
|
currentArg += c
|
||||||
|
|
||||||
|
case OutOfArg:
|
||||||
|
currentArg = ""
|
||||||
|
currentArg += c
|
||||||
|
currentState = InArg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentState == InArg {
|
||||||
|
argv = append(argv, currentArg)
|
||||||
|
} else if currentState == InArgQuote {
|
||||||
|
panic("Starting quote has no ending quote.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return argv
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBool fuzzily converts truthy values.
|
||||||
|
func ToBool(s string) bool {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
return s == "true" || s == "yes" || s == "on" || s == "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBoolOr parses s as a bool or returns defaultValue.
|
||||||
|
func ToBoolOr(s string, defaultValue bool) bool {
|
||||||
|
b, err := strconv.ParseBool(s)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToIntOr parses s as an int or returns defaultValue.
|
||||||
|
func ToIntOr(s string, defaultValue int) int {
|
||||||
|
n, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloat32Or parses as a float32 or returns defaultValue on error.
|
||||||
|
func ToFloat32Or(s string, defaultValue float32) float32 {
|
||||||
|
f, err := strconv.ParseFloat(s, 32)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return float32(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloat64Or parses s as a float64 or returns defaultValue.
|
||||||
|
func ToFloat64Or(s string, defaultValue float64) float64 {
|
||||||
|
f, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloatOr parses as a float64 or returns defaultValue.
|
||||||
|
var ToFloatOr = ToFloat64Or
|
||||||
|
|
||||||
|
// TODO This is not working yet. Go's regexp package does not have some
|
||||||
|
// of the niceities in JavaScript
|
||||||
|
//
|
||||||
|
// Truncate truncates the string, accounting for word placement and chars count
|
||||||
|
// adding a morestr (defaults to ellipsis)
|
||||||
|
// func Truncate(s, morestr string, n int) string {
|
||||||
|
// L := len(s)
|
||||||
|
// if L <= n {
|
||||||
|
// return s
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if morestr == "" {
|
||||||
|
// morestr = "..."
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// tmpl := func(c string) string {
|
||||||
|
// if strings.ToUpper(c) != strings.ToLower(c) {
|
||||||
|
// return "A"
|
||||||
|
// }
|
||||||
|
// return " "
|
||||||
|
// }
|
||||||
|
// template := s[0 : n+1]
|
||||||
|
// var truncateRe = regexp.MustCompile(`.(?=\W*\w*$)`)
|
||||||
|
// truncateRe.ReplaceAllStringFunc(template, tmpl) // 'Hello, world' -> 'HellAA AAAAA'
|
||||||
|
// var wwRe = regexp.MustCompile(`\w\w`)
|
||||||
|
// var whitespaceRe2 = regexp.MustCompile(`\s*\S+$`)
|
||||||
|
// if wwRe.MatchString(template[len(template)-2:]) {
|
||||||
|
// template = whitespaceRe2.ReplaceAllString(template, "")
|
||||||
|
// } else {
|
||||||
|
// template = strings.TrimRight(template, " \t\n")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if len(template+morestr) > L {
|
||||||
|
// return s
|
||||||
|
// }
|
||||||
|
// return s[0:len(template)] + morestr
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// truncate: function(length, pruneStr) { //from underscore.string, author: github.com/rwz
|
||||||
|
// var str = this.s;
|
||||||
|
//
|
||||||
|
// length = ~~length;
|
||||||
|
// pruneStr = pruneStr || '...';
|
||||||
|
//
|
||||||
|
// if (str.length <= length) return new this.constructor(str);
|
||||||
|
//
|
||||||
|
// var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; },
|
||||||
|
// template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA'
|
||||||
|
//
|
||||||
|
// if (template.slice(template.length-2).match(/\w\w/))
|
||||||
|
// template = template.replace(/\s*\S+$/, '');
|
||||||
|
// else
|
||||||
|
// template = new S(template.slice(0, template.length-1)).trimRight().s;
|
||||||
|
//
|
||||||
|
// return (template+pruneStr).length > str.length ? new S(str) : new S(str.slice(0, template.length)+pruneStr);
|
||||||
|
// },
|
||||||
|
|
||||||
|
// Underscore returns converted camel cased string into a string delimited by underscores.
|
||||||
|
func Underscore(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
u := strings.TrimSpace(s)
|
||||||
|
|
||||||
|
u = underscoreRe.ReplaceAllString(u, "${1}_$2")
|
||||||
|
u = dashSpaceRe.ReplaceAllString(u, "_")
|
||||||
|
u = strings.ToLower(u)
|
||||||
|
if IsUpper(s[0:1]) {
|
||||||
|
return "_" + u
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnescapeHTML is an alias for html.UnescapeString.
|
||||||
|
func UnescapeHTML(s string) string {
|
||||||
|
if Verbose {
|
||||||
|
fmt.Println("Use html.UnescapeString instead of UnescapeHTML")
|
||||||
|
}
|
||||||
|
return html.UnescapeString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapHTML wraps s within HTML tag having attributes attrs. Note,
|
||||||
|
// WrapHTML does not escape s value.
|
||||||
|
func WrapHTML(s string, tag string, attrs map[string]string) string {
|
||||||
|
escapeHTMLAttributeQuotes := func(v string) string {
|
||||||
|
v = strings.Replace(v, "<", "<", -1)
|
||||||
|
v = strings.Replace(v, "&", "&", -1)
|
||||||
|
v = strings.Replace(v, "\"", """, -1)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if tag == "" {
|
||||||
|
tag = "div"
|
||||||
|
}
|
||||||
|
el := "<" + tag
|
||||||
|
for name, val := range attrs {
|
||||||
|
el += " " + name + "=\"" + escapeHTMLAttributeQuotes(val) + "\""
|
||||||
|
}
|
||||||
|
el += ">" + s + "</" + tag + ">"
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapHTMLF is the filter form of WrapHTML.
|
||||||
|
func WrapHTMLF(tag string, attrs map[string]string) func(string) string {
|
||||||
|
return func(s string) string {
|
||||||
|
return WrapHTML(s, tag, attrs)
|
||||||
|
}
|
||||||
|
}
|
5
vendor/github.com/robertkrimen/otto/.gitignore
generated
vendored
Normal file
5
vendor/github.com/robertkrimen/otto/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/.test
|
||||||
|
/otto/otto
|
||||||
|
/otto/otto-*
|
||||||
|
/test/test-*.js
|
||||||
|
/test/tester
|
1
vendor/github.com/robertkrimen/otto/DESIGN.markdown
generated
vendored
Normal file
1
vendor/github.com/robertkrimen/otto/DESIGN.markdown
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.)
|
7
vendor/github.com/robertkrimen/otto/LICENSE
generated
vendored
Normal file
7
vendor/github.com/robertkrimen/otto/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Copyright (c) 2012 Robert Krimen
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
63
vendor/github.com/robertkrimen/otto/Makefile
generated
vendored
Normal file
63
vendor/github.com/robertkrimen/otto/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
.PHONY: test test-race test-release release release-check test-262
|
||||||
|
.PHONY: parser
|
||||||
|
.PHONY: otto assets underscore
|
||||||
|
|
||||||
|
TESTS := \
|
||||||
|
~
|
||||||
|
|
||||||
|
TEST := -v --run
|
||||||
|
TEST := -v
|
||||||
|
TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\)
|
||||||
|
TEST := .
|
||||||
|
|
||||||
|
test: parser inline.go
|
||||||
|
go test -i
|
||||||
|
go test $(TEST)
|
||||||
|
@echo PASS
|
||||||
|
|
||||||
|
parser:
|
||||||
|
$(MAKE) -C parser
|
||||||
|
|
||||||
|
inline.go: inline.pl
|
||||||
|
./$< > $@
|
||||||
|
|
||||||
|
#################
|
||||||
|
# release, test #
|
||||||
|
#################
|
||||||
|
|
||||||
|
release: test-race test-release
|
||||||
|
for package in . parser token ast file underscore registry; do (cd $$package && godocdown --signature > README.markdown); done
|
||||||
|
@echo \*\*\* make release-check
|
||||||
|
@echo PASS
|
||||||
|
|
||||||
|
release-check: .test
|
||||||
|
$(MAKE) -C test build test
|
||||||
|
$(MAKE) -C .test/test262 build test
|
||||||
|
@echo PASS
|
||||||
|
|
||||||
|
test-262: .test
|
||||||
|
$(MAKE) -C .test/test262 build test
|
||||||
|
@echo PASS
|
||||||
|
|
||||||
|
test-release:
|
||||||
|
go test -i
|
||||||
|
go test
|
||||||
|
|
||||||
|
test-race:
|
||||||
|
go test -race -i
|
||||||
|
go test -race
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# otto, assets, underscore, ... #
|
||||||
|
#################################
|
||||||
|
|
||||||
|
otto:
|
||||||
|
$(MAKE) -C otto
|
||||||
|
|
||||||
|
assets:
|
||||||
|
mkdir -p .assets
|
||||||
|
for file in underscore/test/*.js; do tr "\`" "_" < $$file > .assets/`basename $$file`; done
|
||||||
|
|
||||||
|
underscore:
|
||||||
|
$(MAKE) -C $@
|
||||||
|
|
871
vendor/github.com/robertkrimen/otto/README.markdown
generated
vendored
Normal file
871
vendor/github.com/robertkrimen/otto/README.markdown
generated
vendored
Normal file
|
@ -0,0 +1,871 @@
|
||||||
|
# otto
|
||||||
|
--
|
||||||
|
```go
|
||||||
|
import "github.com/robertkrimen/otto"
|
||||||
|
```
|
||||||
|
|
||||||
|
Package otto is a JavaScript parser and interpreter written natively in Go.
|
||||||
|
|
||||||
|
http://godoc.org/github.com/robertkrimen/otto
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Run something in the VM
|
||||||
|
|
||||||
|
```go
|
||||||
|
vm := otto.New()
|
||||||
|
vm.Run(`
|
||||||
|
abc = 2 + 2;
|
||||||
|
console.log("The value of abc is " + abc); // 4
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
Get a value out of the VM
|
||||||
|
|
||||||
|
```go
|
||||||
|
if value, err := vm.Get("abc"); err == nil {
|
||||||
|
if value_int, err := value.ToInteger(); err == nil {
|
||||||
|
fmt.Printf("", value_int, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Set a number
|
||||||
|
|
||||||
|
```go
|
||||||
|
vm.Set("def", 11)
|
||||||
|
vm.Run(`
|
||||||
|
console.log("The value of def is " + def);
|
||||||
|
// The value of def is 11
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
Set a string
|
||||||
|
|
||||||
|
```go
|
||||||
|
vm.Set("xyzzy", "Nothing happens.")
|
||||||
|
vm.Run(`
|
||||||
|
console.log(xyzzy.length); // 16
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
Get the value of an expression
|
||||||
|
|
||||||
|
```go
|
||||||
|
value, _ = vm.Run("xyzzy.length")
|
||||||
|
{
|
||||||
|
// value is an int64 with a value of 16
|
||||||
|
value, _ := value.ToInteger()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
An error happens
|
||||||
|
|
||||||
|
```go
|
||||||
|
value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length")
|
||||||
|
if err != nil {
|
||||||
|
// err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined
|
||||||
|
// If there is an error, then value.IsUndefined() is true
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Set a Go function
|
||||||
|
|
||||||
|
```go
|
||||||
|
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
|
||||||
|
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
|
||||||
|
return otto.Value{}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Set a Go function that returns something useful
|
||||||
|
|
||||||
|
```go
|
||||||
|
vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value {
|
||||||
|
right, _ := call.Argument(0).ToInteger()
|
||||||
|
result, _ := vm.ToValue(2 + right)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the functions in JavaScript
|
||||||
|
|
||||||
|
```go
|
||||||
|
result, _ = vm.Run(`
|
||||||
|
sayHello("Xyzzy"); // Hello, Xyzzy.
|
||||||
|
sayHello(); // Hello, undefined
|
||||||
|
|
||||||
|
result = twoPlus(2.0); // 4
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parser
|
||||||
|
|
||||||
|
A separate parser is available in the parser package if you're just interested
|
||||||
|
in building an AST.
|
||||||
|
|
||||||
|
http://godoc.org/github.com/robertkrimen/otto/parser
|
||||||
|
|
||||||
|
Parse and return an AST
|
||||||
|
|
||||||
|
```go
|
||||||
|
filename := "" // A filename is optional
|
||||||
|
src := `
|
||||||
|
// Sample xyzzy example
|
||||||
|
(function(){
|
||||||
|
if (3.14159 > 0) {
|
||||||
|
console.log("Hello, World.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xyzzy = NaN;
|
||||||
|
console.log("Nothing happens.");
|
||||||
|
return xyzzy;
|
||||||
|
})();
|
||||||
|
`
|
||||||
|
|
||||||
|
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
||||||
|
program, err := parser.ParseFile(nil, filename, src, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### otto
|
||||||
|
|
||||||
|
You can run (Go) JavaScript from the commandline with:
|
||||||
|
http://github.com/robertkrimen/otto/tree/master/otto
|
||||||
|
|
||||||
|
$ go get -v github.com/robertkrimen/otto/otto
|
||||||
|
|
||||||
|
Run JavaScript by entering some source on stdin or by giving otto a filename:
|
||||||
|
|
||||||
|
$ otto example.js
|
||||||
|
|
||||||
|
### underscore
|
||||||
|
|
||||||
|
Optionally include the JavaScript utility-belt library, underscore, with this
|
||||||
|
import:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
_ "github.com/robertkrimen/otto/underscore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now every otto runtime will come loaded with underscore
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information: http://github.com/robertkrimen/otto/tree/master/underscore
|
||||||
|
|
||||||
|
|
||||||
|
### Caveat Emptor
|
||||||
|
|
||||||
|
The following are some limitations with otto:
|
||||||
|
|
||||||
|
* "use strict" will parse, but does nothing.
|
||||||
|
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
|
||||||
|
* Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported.
|
||||||
|
|
||||||
|
|
||||||
|
### Regular Expression Incompatibility
|
||||||
|
|
||||||
|
Go translates JavaScript-style regular expressions into something that is
|
||||||
|
"regexp" compatible via `parser.TransformRegExp`. Unfortunately, RegExp requires
|
||||||
|
backtracking for some patterns, and backtracking is not supported by the
|
||||||
|
standard Go engine: https://code.google.com/p/re2/wiki/Syntax
|
||||||
|
|
||||||
|
Therefore, the following syntax is incompatible:
|
||||||
|
|
||||||
|
(?=) // Lookahead (positive), currently a parsing error
|
||||||
|
(?!) // Lookahead (backhead), currently a parsing error
|
||||||
|
\1 // Backreference (\1, \2, \3, ...), currently a parsing error
|
||||||
|
|
||||||
|
A brief discussion of these limitations: "Regexp (?!re)"
|
||||||
|
https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E
|
||||||
|
|
||||||
|
More information about re2: https://code.google.com/p/re2/
|
||||||
|
|
||||||
|
In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r
|
||||||
|
]. The JavaScript definition, on the other hand, also includes \v, Unicode
|
||||||
|
"Separator, Space", etc.
|
||||||
|
|
||||||
|
|
||||||
|
### Halting Problem
|
||||||
|
|
||||||
|
If you want to stop long running executions (like third-party code), you can use
|
||||||
|
the interrupt channel to do this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var halt = errors.New("Stahp")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
runUnsafe(`var abc = [];`)
|
||||||
|
runUnsafe(`
|
||||||
|
while (true) {
|
||||||
|
// Loop forever
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runUnsafe(unsafe string) {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
duration := time.Since(start)
|
||||||
|
if caught := recover(); caught != nil {
|
||||||
|
if caught == halt {
|
||||||
|
fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(caught) // Something else happened, repanic!
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
|
||||||
|
}()
|
||||||
|
|
||||||
|
vm := otto.New()
|
||||||
|
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(2 * time.Second) // Stop after two seconds
|
||||||
|
vm.Interrupt <- func() {
|
||||||
|
panic(halt)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
vm.Run(unsafe) // Here be dragons (risky code)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Where is setTimeout/setInterval?
|
||||||
|
|
||||||
|
These timing functions are not actually part of the ECMA-262 specification.
|
||||||
|
Typically, they belong to the `window` object (in the browser). It would not be
|
||||||
|
difficult to provide something like these via Go, but you probably want to wrap
|
||||||
|
otto in an event loop in that case.
|
||||||
|
|
||||||
|
For an example of how this could be done in Go with otto, see natto:
|
||||||
|
|
||||||
|
http://github.com/robertkrimen/natto
|
||||||
|
|
||||||
|
Here is some more discussion of the issue:
|
||||||
|
|
||||||
|
* http://book.mixu.net/node/ch2.html
|
||||||
|
|
||||||
|
* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29
|
||||||
|
|
||||||
|
* http://aaroncrane.co.uk/2009/02/perl_safe_signals/
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
var ErrVersion = errors.New("version mismatch")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### type Error
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Error struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
|
||||||
|
|
||||||
|
#### func (Error) Error
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (err Error) Error() string
|
||||||
|
```
|
||||||
|
Error returns a description of the error
|
||||||
|
|
||||||
|
TypeError: 'def' is not a function
|
||||||
|
|
||||||
|
#### func (Error) String
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (err Error) String() string
|
||||||
|
```
|
||||||
|
String returns a description of the error and a trace of where the error
|
||||||
|
occurred.
|
||||||
|
|
||||||
|
TypeError: 'def' is not a function
|
||||||
|
at xyz (<anonymous>:3:9)
|
||||||
|
at <anonymous>:7:1/
|
||||||
|
|
||||||
|
#### type FunctionCall
|
||||||
|
|
||||||
|
```go
|
||||||
|
type FunctionCall struct {
|
||||||
|
This Value
|
||||||
|
ArgumentList []Value
|
||||||
|
Otto *Otto
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
FunctionCall is an encapsulation of a JavaScript function call.
|
||||||
|
|
||||||
|
#### func (FunctionCall) Argument
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self FunctionCall) Argument(index int) Value
|
||||||
|
```
|
||||||
|
Argument will return the value of the argument at the given index.
|
||||||
|
|
||||||
|
If no such argument exists, undefined is returned.
|
||||||
|
|
||||||
|
#### type Object
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Object struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Object is the representation of a JavaScript object.
|
||||||
|
|
||||||
|
#### func (Object) Call
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Object) Call(name string, argumentList ...interface{}) (Value, error)
|
||||||
|
```
|
||||||
|
Call a method on the object.
|
||||||
|
|
||||||
|
It is essentially equivalent to:
|
||||||
|
|
||||||
|
var method, _ := object.Get(name)
|
||||||
|
method.Call(object, argumentList...)
|
||||||
|
|
||||||
|
An undefined value and an error will result if:
|
||||||
|
|
||||||
|
1. There is an error during conversion of the argument list
|
||||||
|
2. The property is not actually a function
|
||||||
|
3. An (uncaught) exception is thrown
|
||||||
|
|
||||||
|
#### func (Object) Class
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Object) Class() string
|
||||||
|
```
|
||||||
|
Class will return the class string of the object.
|
||||||
|
|
||||||
|
The return value will (generally) be one of:
|
||||||
|
|
||||||
|
Object
|
||||||
|
Function
|
||||||
|
Array
|
||||||
|
String
|
||||||
|
Number
|
||||||
|
Boolean
|
||||||
|
Date
|
||||||
|
RegExp
|
||||||
|
|
||||||
|
#### func (Object) Get
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Object) Get(name string) (Value, error)
|
||||||
|
```
|
||||||
|
Get the value of the property with the given name.
|
||||||
|
|
||||||
|
#### func (Object) Keys
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Object) Keys() []string
|
||||||
|
```
|
||||||
|
Get the keys for the object
|
||||||
|
|
||||||
|
Equivalent to calling Object.keys on the object
|
||||||
|
|
||||||
|
#### func (Object) Set
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Object) Set(name string, value interface{}) error
|
||||||
|
```
|
||||||
|
Set the property of the given name to the given value.
|
||||||
|
|
||||||
|
An error will result if the setting the property triggers an exception (i.e.
|
||||||
|
read-only), or there is an error during conversion of the given value.
|
||||||
|
|
||||||
|
#### func (Object) Value
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Object) Value() Value
|
||||||
|
```
|
||||||
|
Value will return self as a value.
|
||||||
|
|
||||||
|
#### type Otto
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Otto struct {
|
||||||
|
// Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example.
|
||||||
|
// See "Halting Problem" for more information.
|
||||||
|
Interrupt chan func()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Otto is the representation of the JavaScript runtime. Each instance of Otto has
|
||||||
|
a self-contained namespace.
|
||||||
|
|
||||||
|
#### func New
|
||||||
|
|
||||||
|
```go
|
||||||
|
func New() *Otto
|
||||||
|
```
|
||||||
|
New will allocate a new JavaScript runtime
|
||||||
|
|
||||||
|
#### func Run
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Run(src interface{}) (*Otto, Value, error)
|
||||||
|
```
|
||||||
|
Run will allocate a new JavaScript runtime, run the given source on the
|
||||||
|
allocated runtime, and return the runtime, resulting value, and error (if any).
|
||||||
|
|
||||||
|
src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
|
||||||
|
always be in UTF-8.
|
||||||
|
|
||||||
|
src may also be a Script.
|
||||||
|
|
||||||
|
src may also be a Program, but if the AST has been modified, then runtime
|
||||||
|
behavior is undefined.
|
||||||
|
|
||||||
|
#### func (Otto) Call
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error)
|
||||||
|
```
|
||||||
|
Call the given JavaScript with a given this and arguments.
|
||||||
|
|
||||||
|
If this is nil, then some special handling takes place to determine the proper
|
||||||
|
this value, falling back to a "standard" invocation if necessary (where this is
|
||||||
|
undefined).
|
||||||
|
|
||||||
|
If source begins with "new " (A lowercase new followed by a space), then Call
|
||||||
|
will invoke the function constructor rather than performing a function call. In
|
||||||
|
this case, the this argument has no effect.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// value is a String object
|
||||||
|
value, _ := vm.Call("Object", nil, "Hello, World.")
|
||||||
|
|
||||||
|
// Likewise...
|
||||||
|
value, _ := vm.Call("new Object", nil, "Hello, World.")
|
||||||
|
|
||||||
|
// This will perform a concat on the given array and return the result
|
||||||
|
// value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ]
|
||||||
|
value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*Otto) Compile
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *Otto) Compile(filename string, src interface{}) (*Script, error)
|
||||||
|
```
|
||||||
|
Compile will parse the given source and return a Script value or nil and an
|
||||||
|
error if there was a problem during compilation.
|
||||||
|
|
||||||
|
```go
|
||||||
|
script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`)
|
||||||
|
vm.Run(script)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*Otto) Copy
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (in *Otto) Copy() *Otto
|
||||||
|
```
|
||||||
|
Copy will create a copy/clone of the runtime.
|
||||||
|
|
||||||
|
Copy is useful for saving some time when creating many similar runtimes.
|
||||||
|
|
||||||
|
This method works by walking the original runtime and cloning each object,
|
||||||
|
scope, stash, etc. into a new runtime.
|
||||||
|
|
||||||
|
Be on the lookout for memory leaks or inadvertent sharing of resources.
|
||||||
|
|
||||||
|
#### func (Otto) Get
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Otto) Get(name string) (Value, error)
|
||||||
|
```
|
||||||
|
Get the value of the top-level binding of the given name.
|
||||||
|
|
||||||
|
If there is an error (like the binding does not exist), then the value will be
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
#### func (Otto) Object
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Otto) Object(source string) (*Object, error)
|
||||||
|
```
|
||||||
|
Object will run the given source and return the result as an object.
|
||||||
|
|
||||||
|
For example, accessing an existing object:
|
||||||
|
|
||||||
|
```go
|
||||||
|
object, _ := vm.Object(`Number`)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, creating a new object:
|
||||||
|
|
||||||
|
```go
|
||||||
|
object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, creating and assigning an object:
|
||||||
|
|
||||||
|
```go
|
||||||
|
object, _ := vm.Object(`xyzzy = {}`)
|
||||||
|
object.Set("volume", 11)
|
||||||
|
```
|
||||||
|
|
||||||
|
If there is an error (like the source does not result in an object), then nil
|
||||||
|
and an error is returned.
|
||||||
|
|
||||||
|
#### func (Otto) Run
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Otto) Run(src interface{}) (Value, error)
|
||||||
|
```
|
||||||
|
Run will run the given source (parsing it first if necessary), returning the
|
||||||
|
resulting value and error (if any)
|
||||||
|
|
||||||
|
src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
|
||||||
|
always be in UTF-8.
|
||||||
|
|
||||||
|
If the runtime is unable to parse source, then this function will return
|
||||||
|
undefined and the parse error (nothing will be evaluated in this case).
|
||||||
|
|
||||||
|
src may also be a Script.
|
||||||
|
|
||||||
|
src may also be a Program, but if the AST has been modified, then runtime
|
||||||
|
behavior is undefined.
|
||||||
|
|
||||||
|
#### func (Otto) Set
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Otto) Set(name string, value interface{}) error
|
||||||
|
```
|
||||||
|
Set the top-level binding of the given name to the given value.
|
||||||
|
|
||||||
|
Set will automatically apply ToValue to the given value in order to convert it
|
||||||
|
to a JavaScript value (type Value).
|
||||||
|
|
||||||
|
If there is an error (like the binding is read-only, or the ToValue conversion
|
||||||
|
fails), then an error is returned.
|
||||||
|
|
||||||
|
If the top-level binding does not exist, it will be created.
|
||||||
|
|
||||||
|
#### func (Otto) ToValue
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Otto) ToValue(value interface{}) (Value, error)
|
||||||
|
```
|
||||||
|
ToValue will convert an interface{} value to a value digestible by
|
||||||
|
otto/JavaScript.
|
||||||
|
|
||||||
|
#### type Script
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Script struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Script is a handle for some (reusable) JavaScript. Passing a Script value to a
|
||||||
|
run method will evaluate the JavaScript.
|
||||||
|
|
||||||
|
#### func (*Script) String
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *Script) String() string
|
||||||
|
```
|
||||||
|
|
||||||
|
#### type Value
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Value struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Value is the representation of a JavaScript value.
|
||||||
|
|
||||||
|
#### func FalseValue
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FalseValue() Value
|
||||||
|
```
|
||||||
|
FalseValue will return a value representing false.
|
||||||
|
|
||||||
|
It is equivalent to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
ToValue(false)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func NaNValue
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NaNValue() Value
|
||||||
|
```
|
||||||
|
NaNValue will return a value representing NaN.
|
||||||
|
|
||||||
|
It is equivalent to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
ToValue(math.NaN())
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func NullValue
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NullValue() Value
|
||||||
|
```
|
||||||
|
NullValue will return a Value representing null.
|
||||||
|
|
||||||
|
#### func ToValue
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToValue(value interface{}) (Value, error)
|
||||||
|
```
|
||||||
|
ToValue will convert an interface{} value to a value digestible by
|
||||||
|
otto/JavaScript
|
||||||
|
|
||||||
|
This function will not work for advanced types (struct, map, slice/array, etc.)
|
||||||
|
and you should use Otto.ToValue instead.
|
||||||
|
|
||||||
|
#### func TrueValue
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TrueValue() Value
|
||||||
|
```
|
||||||
|
TrueValue will return a value representing true.
|
||||||
|
|
||||||
|
It is equivalent to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
ToValue(true)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func UndefinedValue
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UndefinedValue() Value
|
||||||
|
```
|
||||||
|
UndefinedValue will return a Value representing undefined.
|
||||||
|
|
||||||
|
#### func (Value) Call
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) Call(this Value, argumentList ...interface{}) (Value, error)
|
||||||
|
```
|
||||||
|
Call the value as a function with the given this value and argument list and
|
||||||
|
return the result of invocation. It is essentially equivalent to:
|
||||||
|
|
||||||
|
value.apply(thisValue, argumentList)
|
||||||
|
|
||||||
|
An undefined value and an error will result if:
|
||||||
|
|
||||||
|
1. There is an error during conversion of the argument list
|
||||||
|
2. The value is not actually a function
|
||||||
|
3. An (uncaught) exception is thrown
|
||||||
|
|
||||||
|
#### func (Value) Class
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) Class() string
|
||||||
|
```
|
||||||
|
Class will return the class string of the value or the empty string if value is
|
||||||
|
not an object.
|
||||||
|
|
||||||
|
The return value will (generally) be one of:
|
||||||
|
|
||||||
|
Object
|
||||||
|
Function
|
||||||
|
Array
|
||||||
|
String
|
||||||
|
Number
|
||||||
|
Boolean
|
||||||
|
Date
|
||||||
|
RegExp
|
||||||
|
|
||||||
|
#### func (Value) Export
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Value) Export() (interface{}, error)
|
||||||
|
```
|
||||||
|
Export will attempt to convert the value to a Go representation and return it
|
||||||
|
via an interface{} kind.
|
||||||
|
|
||||||
|
Export returns an error, but it will always be nil. It is present for backwards
|
||||||
|
compatibility.
|
||||||
|
|
||||||
|
If a reasonable conversion is not possible, then the original value is returned.
|
||||||
|
|
||||||
|
undefined -> nil (FIXME?: Should be Value{})
|
||||||
|
null -> nil
|
||||||
|
boolean -> bool
|
||||||
|
number -> A number type (int, float32, uint64, ...)
|
||||||
|
string -> string
|
||||||
|
Array -> []interface{}
|
||||||
|
Object -> map[string]interface{}
|
||||||
|
|
||||||
|
#### func (Value) IsBoolean
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsBoolean() bool
|
||||||
|
```
|
||||||
|
IsBoolean will return true if value is a boolean (primitive).
|
||||||
|
|
||||||
|
#### func (Value) IsDefined
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsDefined() bool
|
||||||
|
```
|
||||||
|
IsDefined will return false if the value is undefined, and true otherwise.
|
||||||
|
|
||||||
|
#### func (Value) IsFunction
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsFunction() bool
|
||||||
|
```
|
||||||
|
IsFunction will return true if value is a function.
|
||||||
|
|
||||||
|
#### func (Value) IsNaN
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsNaN() bool
|
||||||
|
```
|
||||||
|
IsNaN will return true if value is NaN (or would convert to NaN).
|
||||||
|
|
||||||
|
#### func (Value) IsNull
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsNull() bool
|
||||||
|
```
|
||||||
|
IsNull will return true if the value is null, and false otherwise.
|
||||||
|
|
||||||
|
#### func (Value) IsNumber
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsNumber() bool
|
||||||
|
```
|
||||||
|
IsNumber will return true if value is a number (primitive).
|
||||||
|
|
||||||
|
#### func (Value) IsObject
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsObject() bool
|
||||||
|
```
|
||||||
|
IsObject will return true if value is an object.
|
||||||
|
|
||||||
|
#### func (Value) IsPrimitive
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsPrimitive() bool
|
||||||
|
```
|
||||||
|
IsPrimitive will return true if value is a primitive (any kind of primitive).
|
||||||
|
|
||||||
|
#### func (Value) IsString
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsString() bool
|
||||||
|
```
|
||||||
|
IsString will return true if value is a string (primitive).
|
||||||
|
|
||||||
|
#### func (Value) IsUndefined
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) IsUndefined() bool
|
||||||
|
```
|
||||||
|
IsUndefined will return true if the value is undefined, and false otherwise.
|
||||||
|
|
||||||
|
#### func (Value) Object
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) Object() *Object
|
||||||
|
```
|
||||||
|
Object will return the object of the value, or nil if value is not an object.
|
||||||
|
|
||||||
|
This method will not do any implicit conversion. For example, calling this
|
||||||
|
method on a string primitive value will not return a String object.
|
||||||
|
|
||||||
|
#### func (Value) String
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) String() string
|
||||||
|
```
|
||||||
|
String will return the value as a string.
|
||||||
|
|
||||||
|
This method will make return the empty string if there is an error.
|
||||||
|
|
||||||
|
#### func (Value) ToBoolean
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) ToBoolean() (bool, error)
|
||||||
|
```
|
||||||
|
ToBoolean will convert the value to a boolean (bool).
|
||||||
|
|
||||||
|
ToValue(0).ToBoolean() => false
|
||||||
|
ToValue("").ToBoolean() => false
|
||||||
|
ToValue(true).ToBoolean() => true
|
||||||
|
ToValue(1).ToBoolean() => true
|
||||||
|
ToValue("Nothing happens").ToBoolean() => true
|
||||||
|
|
||||||
|
If there is an error during the conversion process (like an uncaught exception),
|
||||||
|
then the result will be false and an error.
|
||||||
|
|
||||||
|
#### func (Value) ToFloat
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) ToFloat() (float64, error)
|
||||||
|
```
|
||||||
|
ToFloat will convert the value to a number (float64).
|
||||||
|
|
||||||
|
ToValue(0).ToFloat() => 0.
|
||||||
|
ToValue(1.1).ToFloat() => 1.1
|
||||||
|
ToValue("11").ToFloat() => 11.
|
||||||
|
|
||||||
|
If there is an error during the conversion process (like an uncaught exception),
|
||||||
|
then the result will be 0 and an error.
|
||||||
|
|
||||||
|
#### func (Value) ToInteger
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) ToInteger() (int64, error)
|
||||||
|
```
|
||||||
|
ToInteger will convert the value to a number (int64).
|
||||||
|
|
||||||
|
ToValue(0).ToInteger() => 0
|
||||||
|
ToValue(1.1).ToInteger() => 1
|
||||||
|
ToValue("11").ToInteger() => 11
|
||||||
|
|
||||||
|
If there is an error during the conversion process (like an uncaught exception),
|
||||||
|
then the result will be 0 and an error.
|
||||||
|
|
||||||
|
#### func (Value) ToString
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (value Value) ToString() (string, error)
|
||||||
|
```
|
||||||
|
ToString will convert the value to a string (string).
|
||||||
|
|
||||||
|
ToValue(0).ToString() => "0"
|
||||||
|
ToValue(false).ToString() => "false"
|
||||||
|
ToValue(1.1).ToString() => "1.1"
|
||||||
|
ToValue("11").ToString() => "11"
|
||||||
|
ToValue('Nothing happens.').ToString() => "Nothing happens."
|
||||||
|
|
||||||
|
If there is an error during the conversion process (like an uncaught exception),
|
||||||
|
then the result will be the empty string ("") and an error.
|
||||||
|
|
||||||
|
--
|
||||||
|
**godocdown** http://github.com/robertkrimen/godocdown
|
1068
vendor/github.com/robertkrimen/otto/ast/README.markdown
generated
vendored
Normal file
1068
vendor/github.com/robertkrimen/otto/ast/README.markdown
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
278
vendor/github.com/robertkrimen/otto/ast/comments.go
generated
vendored
Normal file
278
vendor/github.com/robertkrimen/otto/ast/comments.go
generated
vendored
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommentPosition determines where the comment is in a given context
|
||||||
|
type CommentPosition int
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ CommentPosition = iota
|
||||||
|
LEADING // Before the pertinent expression
|
||||||
|
TRAILING // After the pertinent expression
|
||||||
|
KEY // Before a key in an object
|
||||||
|
COLON // After a colon in a field declaration
|
||||||
|
FINAL // Final comments in a block, not belonging to a specific expression or the comment after a trailing , in an array or object literal
|
||||||
|
IF // After an if keyword
|
||||||
|
WHILE // After a while keyword
|
||||||
|
DO // After do keyword
|
||||||
|
FOR // After a for keyword
|
||||||
|
WITH // After a with keyword
|
||||||
|
TBD
|
||||||
|
)
|
||||||
|
|
||||||
|
// Comment contains the data of the comment
|
||||||
|
type Comment struct {
|
||||||
|
Begin file.Idx
|
||||||
|
Text string
|
||||||
|
Position CommentPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewComment creates a new comment
|
||||||
|
func NewComment(text string, idx file.Idx) *Comment {
|
||||||
|
comment := &Comment{
|
||||||
|
Begin: idx,
|
||||||
|
Text: text,
|
||||||
|
Position: TBD,
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a stringified version of the position
|
||||||
|
func (cp CommentPosition) String() string {
|
||||||
|
switch cp {
|
||||||
|
case LEADING:
|
||||||
|
return "Leading"
|
||||||
|
case TRAILING:
|
||||||
|
return "Trailing"
|
||||||
|
case KEY:
|
||||||
|
return "Key"
|
||||||
|
case COLON:
|
||||||
|
return "Colon"
|
||||||
|
case FINAL:
|
||||||
|
return "Final"
|
||||||
|
case IF:
|
||||||
|
return "If"
|
||||||
|
case WHILE:
|
||||||
|
return "While"
|
||||||
|
case DO:
|
||||||
|
return "Do"
|
||||||
|
case FOR:
|
||||||
|
return "For"
|
||||||
|
case WITH:
|
||||||
|
return "With"
|
||||||
|
default:
|
||||||
|
return "???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a stringified version of the comment
|
||||||
|
func (c Comment) String() string {
|
||||||
|
return fmt.Sprintf("Comment: %v", c.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comments defines the current view of comments from the parser
|
||||||
|
type Comments struct {
|
||||||
|
// CommentMap is a reference to the parser comment map
|
||||||
|
CommentMap CommentMap
|
||||||
|
// Comments lists the comments scanned, not linked to a node yet
|
||||||
|
Comments []*Comment
|
||||||
|
// future lists the comments after a line break during a sequence of comments
|
||||||
|
future []*Comment
|
||||||
|
// Current is node for which comments are linked to
|
||||||
|
Current Expression
|
||||||
|
|
||||||
|
// wasLineBreak determines if a line break occured while scanning for comments
|
||||||
|
wasLineBreak bool
|
||||||
|
// primary determines whether or not processing a primary expression
|
||||||
|
primary bool
|
||||||
|
// afterBlock determines whether or not being after a block statement
|
||||||
|
afterBlock bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewComments() *Comments {
|
||||||
|
comments := &Comments{
|
||||||
|
CommentMap: CommentMap{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return comments
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Comments) String() string {
|
||||||
|
return fmt.Sprintf("NODE: %v, Comments: %v, Future: %v(LINEBREAK:%v)", c.Current, len(c.Comments), len(c.future), c.wasLineBreak)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchAll returns all the currently scanned comments,
|
||||||
|
// including those from the next line
|
||||||
|
func (c *Comments) FetchAll() []*Comment {
|
||||||
|
defer func() {
|
||||||
|
c.Comments = nil
|
||||||
|
c.future = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
return append(c.Comments, c.future...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch returns all the currently scanned comments
|
||||||
|
func (c *Comments) Fetch() []*Comment {
|
||||||
|
defer func() {
|
||||||
|
c.Comments = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
return c.Comments
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetLineBreak marks the beginning of a new statement
|
||||||
|
func (c *Comments) ResetLineBreak() {
|
||||||
|
c.wasLineBreak = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkPrimary will mark the context as processing a primary expression
|
||||||
|
func (c *Comments) MarkPrimary() {
|
||||||
|
c.primary = true
|
||||||
|
c.wasLineBreak = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterBlock will mark the context as being after a block.
|
||||||
|
func (c *Comments) AfterBlock() {
|
||||||
|
c.afterBlock = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddComment adds a comment to the view.
|
||||||
|
// Depending on the context, comments are added normally or as post line break.
|
||||||
|
func (c *Comments) AddComment(comment *Comment) {
|
||||||
|
if c.primary {
|
||||||
|
if !c.wasLineBreak {
|
||||||
|
c.Comments = append(c.Comments, comment)
|
||||||
|
} else {
|
||||||
|
c.future = append(c.future, comment)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !c.wasLineBreak || (c.Current == nil && !c.afterBlock) {
|
||||||
|
c.Comments = append(c.Comments, comment)
|
||||||
|
} else {
|
||||||
|
c.future = append(c.future, comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkComments will mark the found comments as the given position.
|
||||||
|
func (c *Comments) MarkComments(position CommentPosition) {
|
||||||
|
for _, comment := range c.Comments {
|
||||||
|
if comment.Position == TBD {
|
||||||
|
comment.Position = position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, c := range c.future {
|
||||||
|
if c.Position == TBD {
|
||||||
|
c.Position = position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset the current node and apply the comments to the current expression.
|
||||||
|
// Resets context variables.
|
||||||
|
func (c *Comments) Unset() {
|
||||||
|
if c.Current != nil {
|
||||||
|
c.applyComments(c.Current, c.Current, TRAILING)
|
||||||
|
c.Current = nil
|
||||||
|
}
|
||||||
|
c.wasLineBreak = false
|
||||||
|
c.primary = false
|
||||||
|
c.afterBlock = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExpression sets the current expression.
|
||||||
|
// It is applied the found comments, unless the previous expression has not been unset.
|
||||||
|
// It is skipped if the node is already set or if it is a part of the previous node.
|
||||||
|
func (c *Comments) SetExpression(node Expression) {
|
||||||
|
// Skipping same node
|
||||||
|
if c.Current == node {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.Current != nil && c.Current.Idx1() == node.Idx1() {
|
||||||
|
c.Current = node
|
||||||
|
return
|
||||||
|
}
|
||||||
|
previous := c.Current
|
||||||
|
c.Current = node
|
||||||
|
|
||||||
|
// Apply the found comments and futures to the node and the previous.
|
||||||
|
c.applyComments(node, previous, TRAILING)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostProcessNode applies all found comments to the given node
|
||||||
|
func (c *Comments) PostProcessNode(node Node) {
|
||||||
|
c.applyComments(node, nil, TRAILING)
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyComments applies both the comments and the future comments to the given node and the previous one,
|
||||||
|
// based on the context.
|
||||||
|
func (c *Comments) applyComments(node, previous Node, position CommentPosition) {
|
||||||
|
if previous != nil {
|
||||||
|
c.CommentMap.AddComments(previous, c.Comments, position)
|
||||||
|
c.Comments = nil
|
||||||
|
} else {
|
||||||
|
c.CommentMap.AddComments(node, c.Comments, position)
|
||||||
|
c.Comments = nil
|
||||||
|
}
|
||||||
|
// Only apply the future comments to the node if the previous is set.
|
||||||
|
// This is for detecting end of line comments and which node comments on the following lines belongs to
|
||||||
|
if previous != nil {
|
||||||
|
c.CommentMap.AddComments(node, c.future, position)
|
||||||
|
c.future = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtLineBreak will mark a line break
|
||||||
|
func (c *Comments) AtLineBreak() {
|
||||||
|
c.wasLineBreak = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentMap is the data structure where all found comments are stored
|
||||||
|
type CommentMap map[Node][]*Comment
|
||||||
|
|
||||||
|
// AddComment adds a single comment to the map
|
||||||
|
func (cm CommentMap) AddComment(node Node, comment *Comment) {
|
||||||
|
list := cm[node]
|
||||||
|
list = append(list, comment)
|
||||||
|
|
||||||
|
cm[node] = list
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddComments adds a slice of comments, given a node and an updated position
|
||||||
|
func (cm CommentMap) AddComments(node Node, comments []*Comment, position CommentPosition) {
|
||||||
|
for _, comment := range comments {
|
||||||
|
if comment.Position == TBD {
|
||||||
|
comment.Position = position
|
||||||
|
}
|
||||||
|
cm.AddComment(node, comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the size of the map
|
||||||
|
func (cm CommentMap) Size() int {
|
||||||
|
size := 0
|
||||||
|
for _, comments := range cm {
|
||||||
|
size += len(comments)
|
||||||
|
}
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveComments moves comments with a given position from a node to another
|
||||||
|
func (cm CommentMap) MoveComments(from, to Node, position CommentPosition) {
|
||||||
|
for i, c := range cm[from] {
|
||||||
|
if c.Position == position {
|
||||||
|
cm.AddComment(to, c)
|
||||||
|
|
||||||
|
// Remove the comment from the "from" slice
|
||||||
|
cm[from][i] = cm[from][len(cm[from])-1]
|
||||||
|
cm[from][len(cm[from])-1] = nil
|
||||||
|
cm[from] = cm[from][:len(cm[from])-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
515
vendor/github.com/robertkrimen/otto/ast/node.go
generated
vendored
Normal file
515
vendor/github.com/robertkrimen/otto/ast/node.go
generated
vendored
Normal file
|
@ -0,0 +1,515 @@
|
||||||
|
/*
|
||||||
|
Package ast declares types representing a JavaScript AST.
|
||||||
|
|
||||||
|
Warning
|
||||||
|
|
||||||
|
The parser and AST interfaces are still works-in-progress (particularly where
|
||||||
|
node types are concerned) and may change in the future.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// All nodes implement the Node interface.
|
||||||
|
type Node interface {
|
||||||
|
Idx0() file.Idx // The index of the first character belonging to the node
|
||||||
|
Idx1() file.Idx // The index of the first character immediately after the node
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== //
|
||||||
|
// Expression //
|
||||||
|
// ========== //
|
||||||
|
|
||||||
|
type (
|
||||||
|
// All expression nodes implement the Expression interface.
|
||||||
|
Expression interface {
|
||||||
|
Node
|
||||||
|
_expressionNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayLiteral struct {
|
||||||
|
LeftBracket file.Idx
|
||||||
|
RightBracket file.Idx
|
||||||
|
Value []Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
AssignExpression struct {
|
||||||
|
Operator token.Token
|
||||||
|
Left Expression
|
||||||
|
Right Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
BadExpression struct {
|
||||||
|
From file.Idx
|
||||||
|
To file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryExpression struct {
|
||||||
|
Operator token.Token
|
||||||
|
Left Expression
|
||||||
|
Right Expression
|
||||||
|
Comparison bool
|
||||||
|
}
|
||||||
|
|
||||||
|
BooleanLiteral struct {
|
||||||
|
Idx file.Idx
|
||||||
|
Literal string
|
||||||
|
Value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
BracketExpression struct {
|
||||||
|
Left Expression
|
||||||
|
Member Expression
|
||||||
|
LeftBracket file.Idx
|
||||||
|
RightBracket file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
CallExpression struct {
|
||||||
|
Callee Expression
|
||||||
|
LeftParenthesis file.Idx
|
||||||
|
ArgumentList []Expression
|
||||||
|
RightParenthesis file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
ConditionalExpression struct {
|
||||||
|
Test Expression
|
||||||
|
Consequent Expression
|
||||||
|
Alternate Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
DotExpression struct {
|
||||||
|
Left Expression
|
||||||
|
Identifier *Identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
EmptyExpression struct {
|
||||||
|
Begin file.Idx
|
||||||
|
End file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionLiteral struct {
|
||||||
|
Function file.Idx
|
||||||
|
Name *Identifier
|
||||||
|
ParameterList *ParameterList
|
||||||
|
Body Statement
|
||||||
|
Source string
|
||||||
|
|
||||||
|
DeclarationList []Declaration
|
||||||
|
}
|
||||||
|
|
||||||
|
Identifier struct {
|
||||||
|
Name string
|
||||||
|
Idx file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
NewExpression struct {
|
||||||
|
New file.Idx
|
||||||
|
Callee Expression
|
||||||
|
LeftParenthesis file.Idx
|
||||||
|
ArgumentList []Expression
|
||||||
|
RightParenthesis file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
NullLiteral struct {
|
||||||
|
Idx file.Idx
|
||||||
|
Literal string
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberLiteral struct {
|
||||||
|
Idx file.Idx
|
||||||
|
Literal string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectLiteral struct {
|
||||||
|
LeftBrace file.Idx
|
||||||
|
RightBrace file.Idx
|
||||||
|
Value []Property
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterList struct {
|
||||||
|
Opening file.Idx
|
||||||
|
List []*Identifier
|
||||||
|
Closing file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
Property struct {
|
||||||
|
Key string
|
||||||
|
Kind string
|
||||||
|
Value Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
RegExpLiteral struct {
|
||||||
|
Idx file.Idx
|
||||||
|
Literal string
|
||||||
|
Pattern string
|
||||||
|
Flags string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
SequenceExpression struct {
|
||||||
|
Sequence []Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
StringLiteral struct {
|
||||||
|
Idx file.Idx
|
||||||
|
Literal string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
ThisExpression struct {
|
||||||
|
Idx file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
UnaryExpression struct {
|
||||||
|
Operator token.Token
|
||||||
|
Idx file.Idx // If a prefix operation
|
||||||
|
Operand Expression
|
||||||
|
Postfix bool
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableExpression struct {
|
||||||
|
Name string
|
||||||
|
Idx file.Idx
|
||||||
|
Initializer Expression
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// _expressionNode
|
||||||
|
|
||||||
|
func (*ArrayLiteral) _expressionNode() {}
|
||||||
|
func (*AssignExpression) _expressionNode() {}
|
||||||
|
func (*BadExpression) _expressionNode() {}
|
||||||
|
func (*BinaryExpression) _expressionNode() {}
|
||||||
|
func (*BooleanLiteral) _expressionNode() {}
|
||||||
|
func (*BracketExpression) _expressionNode() {}
|
||||||
|
func (*CallExpression) _expressionNode() {}
|
||||||
|
func (*ConditionalExpression) _expressionNode() {}
|
||||||
|
func (*DotExpression) _expressionNode() {}
|
||||||
|
func (*EmptyExpression) _expressionNode() {}
|
||||||
|
func (*FunctionLiteral) _expressionNode() {}
|
||||||
|
func (*Identifier) _expressionNode() {}
|
||||||
|
func (*NewExpression) _expressionNode() {}
|
||||||
|
func (*NullLiteral) _expressionNode() {}
|
||||||
|
func (*NumberLiteral) _expressionNode() {}
|
||||||
|
func (*ObjectLiteral) _expressionNode() {}
|
||||||
|
func (*RegExpLiteral) _expressionNode() {}
|
||||||
|
func (*SequenceExpression) _expressionNode() {}
|
||||||
|
func (*StringLiteral) _expressionNode() {}
|
||||||
|
func (*ThisExpression) _expressionNode() {}
|
||||||
|
func (*UnaryExpression) _expressionNode() {}
|
||||||
|
func (*VariableExpression) _expressionNode() {}
|
||||||
|
|
||||||
|
// ========= //
|
||||||
|
// Statement //
|
||||||
|
// ========= //
|
||||||
|
|
||||||
|
type (
|
||||||
|
// All statement nodes implement the Statement interface.
|
||||||
|
Statement interface {
|
||||||
|
Node
|
||||||
|
_statementNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
BadStatement struct {
|
||||||
|
From file.Idx
|
||||||
|
To file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockStatement struct {
|
||||||
|
LeftBrace file.Idx
|
||||||
|
List []Statement
|
||||||
|
RightBrace file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
BranchStatement struct {
|
||||||
|
Idx file.Idx
|
||||||
|
Token token.Token
|
||||||
|
Label *Identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
CaseStatement struct {
|
||||||
|
Case file.Idx
|
||||||
|
Test Expression
|
||||||
|
Consequent []Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
CatchStatement struct {
|
||||||
|
Catch file.Idx
|
||||||
|
Parameter *Identifier
|
||||||
|
Body Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
DebuggerStatement struct {
|
||||||
|
Debugger file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
DoWhileStatement struct {
|
||||||
|
Do file.Idx
|
||||||
|
Test Expression
|
||||||
|
Body Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
EmptyStatement struct {
|
||||||
|
Semicolon file.Idx
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionStatement struct {
|
||||||
|
Expression Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
ForInStatement struct {
|
||||||
|
For file.Idx
|
||||||
|
Into Expression
|
||||||
|
Source Expression
|
||||||
|
Body Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
ForStatement struct {
|
||||||
|
For file.Idx
|
||||||
|
Initializer Expression
|
||||||
|
Update Expression
|
||||||
|
Test Expression
|
||||||
|
Body Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionStatement struct {
|
||||||
|
Function *FunctionLiteral
|
||||||
|
}
|
||||||
|
|
||||||
|
IfStatement struct {
|
||||||
|
If file.Idx
|
||||||
|
Test Expression
|
||||||
|
Consequent Statement
|
||||||
|
Alternate Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelledStatement struct {
|
||||||
|
Label *Identifier
|
||||||
|
Colon file.Idx
|
||||||
|
Statement Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnStatement struct {
|
||||||
|
Return file.Idx
|
||||||
|
Argument Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchStatement struct {
|
||||||
|
Switch file.Idx
|
||||||
|
Discriminant Expression
|
||||||
|
Default int
|
||||||
|
Body []*CaseStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowStatement struct {
|
||||||
|
Throw file.Idx
|
||||||
|
Argument Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
TryStatement struct {
|
||||||
|
Try file.Idx
|
||||||
|
Body Statement
|
||||||
|
Catch *CatchStatement
|
||||||
|
Finally Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableStatement struct {
|
||||||
|
Var file.Idx
|
||||||
|
List []Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
WhileStatement struct {
|
||||||
|
While file.Idx
|
||||||
|
Test Expression
|
||||||
|
Body Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
WithStatement struct {
|
||||||
|
With file.Idx
|
||||||
|
Object Expression
|
||||||
|
Body Statement
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// _statementNode
|
||||||
|
|
||||||
|
func (*BadStatement) _statementNode() {}
|
||||||
|
func (*BlockStatement) _statementNode() {}
|
||||||
|
func (*BranchStatement) _statementNode() {}
|
||||||
|
func (*CaseStatement) _statementNode() {}
|
||||||
|
func (*CatchStatement) _statementNode() {}
|
||||||
|
func (*DebuggerStatement) _statementNode() {}
|
||||||
|
func (*DoWhileStatement) _statementNode() {}
|
||||||
|
func (*EmptyStatement) _statementNode() {}
|
||||||
|
func (*ExpressionStatement) _statementNode() {}
|
||||||
|
func (*ForInStatement) _statementNode() {}
|
||||||
|
func (*ForStatement) _statementNode() {}
|
||||||
|
func (*FunctionStatement) _statementNode() {}
|
||||||
|
func (*IfStatement) _statementNode() {}
|
||||||
|
func (*LabelledStatement) _statementNode() {}
|
||||||
|
func (*ReturnStatement) _statementNode() {}
|
||||||
|
func (*SwitchStatement) _statementNode() {}
|
||||||
|
func (*ThrowStatement) _statementNode() {}
|
||||||
|
func (*TryStatement) _statementNode() {}
|
||||||
|
func (*VariableStatement) _statementNode() {}
|
||||||
|
func (*WhileStatement) _statementNode() {}
|
||||||
|
func (*WithStatement) _statementNode() {}
|
||||||
|
|
||||||
|
// =========== //
|
||||||
|
// Declaration //
|
||||||
|
// =========== //
|
||||||
|
|
||||||
|
type (
|
||||||
|
// All declaration nodes implement the Declaration interface.
|
||||||
|
Declaration interface {
|
||||||
|
_declarationNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionDeclaration struct {
|
||||||
|
Function *FunctionLiteral
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableDeclaration struct {
|
||||||
|
Var file.Idx
|
||||||
|
List []*VariableExpression
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// _declarationNode
|
||||||
|
|
||||||
|
func (*FunctionDeclaration) _declarationNode() {}
|
||||||
|
func (*VariableDeclaration) _declarationNode() {}
|
||||||
|
|
||||||
|
// ==== //
|
||||||
|
// Node //
|
||||||
|
// ==== //
|
||||||
|
|
||||||
|
type Program struct {
|
||||||
|
Body []Statement
|
||||||
|
|
||||||
|
DeclarationList []Declaration
|
||||||
|
|
||||||
|
File *file.File
|
||||||
|
|
||||||
|
Comments CommentMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== //
|
||||||
|
// Idx0 //
|
||||||
|
// ==== //
|
||||||
|
|
||||||
|
func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket }
|
||||||
|
func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
||||||
|
func (self *BadExpression) Idx0() file.Idx { return self.From }
|
||||||
|
func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
||||||
|
func (self *BooleanLiteral) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
||||||
|
func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() }
|
||||||
|
func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() }
|
||||||
|
func (self *DotExpression) Idx0() file.Idx { return self.Left.Idx0() }
|
||||||
|
func (self *EmptyExpression) Idx0() file.Idx { return self.Begin }
|
||||||
|
func (self *FunctionLiteral) Idx0() file.Idx { return self.Function }
|
||||||
|
func (self *Identifier) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *NewExpression) Idx0() file.Idx { return self.New }
|
||||||
|
func (self *NullLiteral) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *NumberLiteral) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *ObjectLiteral) Idx0() file.Idx { return self.LeftBrace }
|
||||||
|
func (self *RegExpLiteral) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *SequenceExpression) Idx0() file.Idx { return self.Sequence[0].Idx0() }
|
||||||
|
func (self *StringLiteral) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *ThisExpression) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *UnaryExpression) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *VariableExpression) Idx0() file.Idx { return self.Idx }
|
||||||
|
|
||||||
|
func (self *BadStatement) Idx0() file.Idx { return self.From }
|
||||||
|
func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace }
|
||||||
|
func (self *BranchStatement) Idx0() file.Idx { return self.Idx }
|
||||||
|
func (self *CaseStatement) Idx0() file.Idx { return self.Case }
|
||||||
|
func (self *CatchStatement) Idx0() file.Idx { return self.Catch }
|
||||||
|
func (self *DebuggerStatement) Idx0() file.Idx { return self.Debugger }
|
||||||
|
func (self *DoWhileStatement) Idx0() file.Idx { return self.Do }
|
||||||
|
func (self *EmptyStatement) Idx0() file.Idx { return self.Semicolon }
|
||||||
|
func (self *ExpressionStatement) Idx0() file.Idx { return self.Expression.Idx0() }
|
||||||
|
func (self *ForInStatement) Idx0() file.Idx { return self.For }
|
||||||
|
func (self *ForStatement) Idx0() file.Idx { return self.For }
|
||||||
|
func (self *FunctionStatement) Idx0() file.Idx { return self.Function.Idx0() }
|
||||||
|
func (self *IfStatement) Idx0() file.Idx { return self.If }
|
||||||
|
func (self *LabelledStatement) Idx0() file.Idx { return self.Label.Idx0() }
|
||||||
|
func (self *Program) Idx0() file.Idx { return self.Body[0].Idx0() }
|
||||||
|
func (self *ReturnStatement) Idx0() file.Idx { return self.Return }
|
||||||
|
func (self *SwitchStatement) Idx0() file.Idx { return self.Switch }
|
||||||
|
func (self *ThrowStatement) Idx0() file.Idx { return self.Throw }
|
||||||
|
func (self *TryStatement) Idx0() file.Idx { return self.Try }
|
||||||
|
func (self *VariableStatement) Idx0() file.Idx { return self.Var }
|
||||||
|
func (self *WhileStatement) Idx0() file.Idx { return self.While }
|
||||||
|
func (self *WithStatement) Idx0() file.Idx { return self.With }
|
||||||
|
|
||||||
|
// ==== //
|
||||||
|
// Idx1 //
|
||||||
|
// ==== //
|
||||||
|
|
||||||
|
func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket }
|
||||||
|
func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() }
|
||||||
|
func (self *BadExpression) Idx1() file.Idx { return self.To }
|
||||||
|
func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() }
|
||||||
|
func (self *BooleanLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
||||||
|
func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + 1 }
|
||||||
|
func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
|
||||||
|
func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() }
|
||||||
|
func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1() }
|
||||||
|
func (self *EmptyExpression) Idx1() file.Idx { return self.End }
|
||||||
|
func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() }
|
||||||
|
func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) }
|
||||||
|
func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
|
||||||
|
func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null"
|
||||||
|
func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
||||||
|
func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace }
|
||||||
|
func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
||||||
|
func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() }
|
||||||
|
func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
|
||||||
|
func (self *ThisExpression) Idx1() file.Idx { return self.Idx + 4 }
|
||||||
|
func (self *UnaryExpression) Idx1() file.Idx {
|
||||||
|
if self.Postfix {
|
||||||
|
return self.Operand.Idx1() + 2 // ++ --
|
||||||
|
}
|
||||||
|
return self.Operand.Idx1()
|
||||||
|
}
|
||||||
|
func (self *VariableExpression) Idx1() file.Idx {
|
||||||
|
if self.Initializer == nil {
|
||||||
|
return file.Idx(int(self.Idx) + len(self.Name) + 1)
|
||||||
|
}
|
||||||
|
return self.Initializer.Idx1()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BadStatement) Idx1() file.Idx { return self.To }
|
||||||
|
func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 }
|
||||||
|
func (self *BranchStatement) Idx1() file.Idx { return self.Idx }
|
||||||
|
func (self *CaseStatement) Idx1() file.Idx { return self.Consequent[len(self.Consequent)-1].Idx1() }
|
||||||
|
func (self *CatchStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
||||||
|
func (self *DebuggerStatement) Idx1() file.Idx { return self.Debugger + 8 }
|
||||||
|
func (self *DoWhileStatement) Idx1() file.Idx { return self.Test.Idx1() }
|
||||||
|
func (self *EmptyStatement) Idx1() file.Idx { return self.Semicolon + 1 }
|
||||||
|
func (self *ExpressionStatement) Idx1() file.Idx { return self.Expression.Idx1() }
|
||||||
|
func (self *ForInStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
||||||
|
func (self *ForStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
||||||
|
func (self *FunctionStatement) Idx1() file.Idx { return self.Function.Idx1() }
|
||||||
|
func (self *IfStatement) Idx1() file.Idx {
|
||||||
|
if self.Alternate != nil {
|
||||||
|
return self.Alternate.Idx1()
|
||||||
|
}
|
||||||
|
return self.Consequent.Idx1()
|
||||||
|
}
|
||||||
|
func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 }
|
||||||
|
func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
|
||||||
|
func (self *ReturnStatement) Idx1() file.Idx { return self.Return }
|
||||||
|
func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
|
||||||
|
func (self *ThrowStatement) Idx1() file.Idx { return self.Throw }
|
||||||
|
func (self *TryStatement) Idx1() file.Idx { return self.Try }
|
||||||
|
func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }
|
||||||
|
func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
||||||
|
func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() }
|
217
vendor/github.com/robertkrimen/otto/ast/walk.go
generated
vendored
Normal file
217
vendor/github.com/robertkrimen/otto/ast/walk.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Visitor Enter method is invoked for each node encountered by Walk.
|
||||||
|
// If the result visitor w is not nil, Walk visits each of the children
|
||||||
|
// of node with the visitor v, followed by a call of the Exit method.
|
||||||
|
type Visitor interface {
|
||||||
|
Enter(n Node) (v Visitor)
|
||||||
|
Exit(n Node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk traverses an AST in depth-first order: It starts by calling
|
||||||
|
// v.Enter(node); node must not be nil. If the visitor v returned by
|
||||||
|
// v.Enter(node) is not nil, Walk is invoked recursively with visitor
|
||||||
|
// v for each of the non-nil children of node, followed by a call
|
||||||
|
// of v.Exit(node).
|
||||||
|
func Walk(v Visitor, n Node) {
|
||||||
|
if n == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v = v.Enter(n); v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer v.Exit(n)
|
||||||
|
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *ArrayLiteral:
|
||||||
|
if n != nil {
|
||||||
|
for _, ex := range n.Value {
|
||||||
|
Walk(v, ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *AssignExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Left)
|
||||||
|
Walk(v, n.Right)
|
||||||
|
}
|
||||||
|
case *BadExpression:
|
||||||
|
case *BinaryExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Left)
|
||||||
|
Walk(v, n.Right)
|
||||||
|
}
|
||||||
|
case *BlockStatement:
|
||||||
|
if n != nil {
|
||||||
|
for _, s := range n.List {
|
||||||
|
Walk(v, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *BooleanLiteral:
|
||||||
|
case *BracketExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Left)
|
||||||
|
Walk(v, n.Member)
|
||||||
|
}
|
||||||
|
case *BranchStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Label)
|
||||||
|
}
|
||||||
|
case *CallExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Callee)
|
||||||
|
for _, a := range n.ArgumentList {
|
||||||
|
Walk(v, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *CaseStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Test)
|
||||||
|
for _, c := range n.Consequent {
|
||||||
|
Walk(v, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *CatchStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Parameter)
|
||||||
|
Walk(v, n.Body)
|
||||||
|
}
|
||||||
|
case *ConditionalExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Test)
|
||||||
|
Walk(v, n.Consequent)
|
||||||
|
Walk(v, n.Alternate)
|
||||||
|
}
|
||||||
|
case *DebuggerStatement:
|
||||||
|
case *DoWhileStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Test)
|
||||||
|
Walk(v, n.Body)
|
||||||
|
}
|
||||||
|
case *DotExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Left)
|
||||||
|
}
|
||||||
|
case *EmptyExpression:
|
||||||
|
case *EmptyStatement:
|
||||||
|
case *ExpressionStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Expression)
|
||||||
|
}
|
||||||
|
case *ForInStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Into)
|
||||||
|
Walk(v, n.Source)
|
||||||
|
Walk(v, n.Body)
|
||||||
|
}
|
||||||
|
case *ForStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Initializer)
|
||||||
|
Walk(v, n.Update)
|
||||||
|
Walk(v, n.Test)
|
||||||
|
Walk(v, n.Body)
|
||||||
|
}
|
||||||
|
case *FunctionLiteral:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Name)
|
||||||
|
for _, p := range n.ParameterList.List {
|
||||||
|
Walk(v, p)
|
||||||
|
}
|
||||||
|
Walk(v, n.Body)
|
||||||
|
}
|
||||||
|
case *FunctionStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Function)
|
||||||
|
}
|
||||||
|
case *Identifier:
|
||||||
|
case *IfStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Test)
|
||||||
|
Walk(v, n.Consequent)
|
||||||
|
Walk(v, n.Alternate)
|
||||||
|
}
|
||||||
|
case *LabelledStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Statement)
|
||||||
|
}
|
||||||
|
case *NewExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Callee)
|
||||||
|
for _, a := range n.ArgumentList {
|
||||||
|
Walk(v, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *NullLiteral:
|
||||||
|
case *NumberLiteral:
|
||||||
|
case *ObjectLiteral:
|
||||||
|
if n != nil {
|
||||||
|
for _, p := range n.Value {
|
||||||
|
Walk(v, p.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *Program:
|
||||||
|
if n != nil {
|
||||||
|
for _, b := range n.Body {
|
||||||
|
Walk(v, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *RegExpLiteral:
|
||||||
|
case *ReturnStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Argument)
|
||||||
|
}
|
||||||
|
case *SequenceExpression:
|
||||||
|
if n != nil {
|
||||||
|
for _, e := range n.Sequence {
|
||||||
|
Walk(v, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *StringLiteral:
|
||||||
|
case *SwitchStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Discriminant)
|
||||||
|
for _, c := range n.Body {
|
||||||
|
Walk(v, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ThisExpression:
|
||||||
|
case *ThrowStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Argument)
|
||||||
|
}
|
||||||
|
case *TryStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Body)
|
||||||
|
Walk(v, n.Catch)
|
||||||
|
Walk(v, n.Finally)
|
||||||
|
}
|
||||||
|
case *UnaryExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Operand)
|
||||||
|
}
|
||||||
|
case *VariableExpression:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Initializer)
|
||||||
|
}
|
||||||
|
case *VariableStatement:
|
||||||
|
if n != nil {
|
||||||
|
for _, e := range n.List {
|
||||||
|
Walk(v, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *WhileStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Test)
|
||||||
|
Walk(v, n.Body)
|
||||||
|
}
|
||||||
|
case *WithStatement:
|
||||||
|
if n != nil {
|
||||||
|
Walk(v, n.Object)
|
||||||
|
Walk(v, n.Body)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Walk: unexpected node type %T", n))
|
||||||
|
}
|
||||||
|
}
|
354
vendor/github.com/robertkrimen/otto/builtin.go
generated
vendored
Normal file
354
vendor/github.com/robertkrimen/otto/builtin.go
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"math"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Global
|
||||||
|
func builtinGlobal_eval(call FunctionCall) Value {
|
||||||
|
src := call.Argument(0)
|
||||||
|
if !src.IsString() {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
runtime := call.runtime
|
||||||
|
program := runtime.cmpl_parseOrThrow(src.string(), nil)
|
||||||
|
if !call.eval {
|
||||||
|
// Not a direct call to eval, so we enter the global ExecutionContext
|
||||||
|
runtime.enterGlobalScope()
|
||||||
|
defer runtime.leaveScope()
|
||||||
|
}
|
||||||
|
returnValue := runtime.cmpl_evaluate_nodeProgram(program, true)
|
||||||
|
if returnValue.isEmpty() {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinGlobal_isNaN(call FunctionCall) Value {
|
||||||
|
value := call.Argument(0).float64()
|
||||||
|
return toValue_bool(math.IsNaN(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinGlobal_isFinite(call FunctionCall) Value {
|
||||||
|
value := call.Argument(0).float64()
|
||||||
|
return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// radix 3 => 2 (ASCII 50) +47
|
||||||
|
// radix 11 => A/a (ASCII 65/97) +54/+86
|
||||||
|
var parseInt_alphabetTable = func() []string {
|
||||||
|
table := []string{"", "", "01"}
|
||||||
|
for radix := 3; radix <= 36; radix += 1 {
|
||||||
|
alphabet := table[radix-1]
|
||||||
|
if radix <= 10 {
|
||||||
|
alphabet += string(radix + 47)
|
||||||
|
} else {
|
||||||
|
alphabet += string(radix+54) + string(radix+86)
|
||||||
|
}
|
||||||
|
table = append(table, alphabet)
|
||||||
|
}
|
||||||
|
return table
|
||||||
|
}()
|
||||||
|
|
||||||
|
func digitValue(chr rune) int {
|
||||||
|
switch {
|
||||||
|
case '0' <= chr && chr <= '9':
|
||||||
|
return int(chr - '0')
|
||||||
|
case 'a' <= chr && chr <= 'z':
|
||||||
|
return int(chr - 'a' + 10)
|
||||||
|
case 'A' <= chr && chr <= 'Z':
|
||||||
|
return int(chr - 'A' + 10)
|
||||||
|
}
|
||||||
|
return 36 // Larger than any legal digit value
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinGlobal_parseInt(call FunctionCall) Value {
|
||||||
|
input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace)
|
||||||
|
if len(input) == 0 {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
radix := int(toInt32(call.Argument(1)))
|
||||||
|
|
||||||
|
negative := false
|
||||||
|
switch input[0] {
|
||||||
|
case '+':
|
||||||
|
input = input[1:]
|
||||||
|
case '-':
|
||||||
|
negative = true
|
||||||
|
input = input[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
strip := true
|
||||||
|
if radix == 0 {
|
||||||
|
radix = 10
|
||||||
|
} else {
|
||||||
|
if radix < 2 || radix > 36 {
|
||||||
|
return NaNValue()
|
||||||
|
} else if radix != 16 {
|
||||||
|
strip = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(input) {
|
||||||
|
case 0:
|
||||||
|
return NaNValue()
|
||||||
|
case 1:
|
||||||
|
default:
|
||||||
|
if strip {
|
||||||
|
if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') {
|
||||||
|
input = input[2:]
|
||||||
|
radix = 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base := radix
|
||||||
|
index := 0
|
||||||
|
for ; index < len(input); index++ {
|
||||||
|
digit := digitValue(rune(input[index])) // If not ASCII, then an error anyway
|
||||||
|
if digit >= base {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input = input[0:index]
|
||||||
|
|
||||||
|
value, err := strconv.ParseInt(input, radix, 64)
|
||||||
|
if err != nil {
|
||||||
|
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
||||||
|
base := float64(base)
|
||||||
|
// Could just be a very large number (e.g. 0x8000000000000000)
|
||||||
|
var value float64
|
||||||
|
for _, chr := range input {
|
||||||
|
digit := float64(digitValue(chr))
|
||||||
|
if digit >= base {
|
||||||
|
goto error
|
||||||
|
}
|
||||||
|
value = value*base + digit
|
||||||
|
}
|
||||||
|
if negative {
|
||||||
|
value *= -1
|
||||||
|
}
|
||||||
|
return toValue_float64(value)
|
||||||
|
}
|
||||||
|
error:
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
if negative {
|
||||||
|
value *= -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_int64(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseFloat_matchBadSpecial = regexp.MustCompile(`[\+\-]?(?:[Ii]nf$|infinity)`)
|
||||||
|
var parseFloat_matchValid = regexp.MustCompile(`[0-9eE\+\-\.]|Infinity`)
|
||||||
|
|
||||||
|
func builtinGlobal_parseFloat(call FunctionCall) Value {
|
||||||
|
// Caveat emptor: This implementation does NOT match the specification
|
||||||
|
input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace)
|
||||||
|
|
||||||
|
if parseFloat_matchBadSpecial.MatchString(input) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
value, err := strconv.ParseFloat(input, 64)
|
||||||
|
if err != nil {
|
||||||
|
for end := len(input); end > 0; end-- {
|
||||||
|
input := input[0:end]
|
||||||
|
if !parseFloat_matchValid.MatchString(input) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
value, err = strconv.ParseFloat(input, 64)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_float64(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeURI/decodeURI
|
||||||
|
|
||||||
|
func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value {
|
||||||
|
value := call.Argument(0)
|
||||||
|
var input []uint16
|
||||||
|
switch vl := value.value.(type) {
|
||||||
|
case []uint16:
|
||||||
|
input = vl
|
||||||
|
default:
|
||||||
|
input = utf16.Encode([]rune(value.string()))
|
||||||
|
}
|
||||||
|
if len(input) == 0 {
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
output := []byte{}
|
||||||
|
length := len(input)
|
||||||
|
encode := make([]byte, 4)
|
||||||
|
for index := 0; index < length; {
|
||||||
|
value := input[index]
|
||||||
|
decode := utf16.Decode(input[index : index+1])
|
||||||
|
if value >= 0xDC00 && value <= 0xDFFF {
|
||||||
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
|
}
|
||||||
|
if value >= 0xD800 && value <= 0xDBFF {
|
||||||
|
index += 1
|
||||||
|
if index >= length {
|
||||||
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
|
}
|
||||||
|
// input = ..., value, value1, ...
|
||||||
|
value1 := input[index]
|
||||||
|
if value1 < 0xDC00 || value1 > 0xDFFF {
|
||||||
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
|
}
|
||||||
|
decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000}
|
||||||
|
}
|
||||||
|
index += 1
|
||||||
|
size := utf8.EncodeRune(encode, decode[0])
|
||||||
|
encode := encode[0:size]
|
||||||
|
output = append(output, encode...)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value := escape.ReplaceAllFunc(output, func(target []byte) []byte {
|
||||||
|
// Probably a better way of doing this
|
||||||
|
if target[0] == ' ' {
|
||||||
|
return []byte("%20")
|
||||||
|
}
|
||||||
|
return []byte(url.QueryEscape(string(target)))
|
||||||
|
})
|
||||||
|
return toValue_string(string(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodeURI_Regexp = regexp.MustCompile(`([^~!@#$&*()=:/,;?+'])`)
|
||||||
|
|
||||||
|
func builtinGlobal_encodeURI(call FunctionCall) Value {
|
||||||
|
return _builtinGlobal_encodeURI(call, encodeURI_Regexp)
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodeURIComponent_Regexp = regexp.MustCompile(`([^~!*()'])`)
|
||||||
|
|
||||||
|
func builtinGlobal_encodeURIComponent(call FunctionCall) Value {
|
||||||
|
return _builtinGlobal_encodeURI(call, encodeURIComponent_Regexp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3B/2F/3F/3A/40/26/3D/2B/24/2C/23
|
||||||
|
var decodeURI_guard = regexp.MustCompile(`(?i)(?:%)(3B|2F|3F|3A|40|26|3D|2B|24|2C|23)`)
|
||||||
|
|
||||||
|
func _decodeURI(input string, reserve bool) (string, bool) {
|
||||||
|
if reserve {
|
||||||
|
input = decodeURI_guard.ReplaceAllString(input, "%25$1")
|
||||||
|
}
|
||||||
|
input = strings.Replace(input, "+", "%2B", -1) // Ugly hack to make QueryUnescape work with our use case
|
||||||
|
output, err := url.QueryUnescape(input)
|
||||||
|
if err != nil || !utf8.ValidString(output) {
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
return output, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinGlobal_decodeURI(call FunctionCall) Value {
|
||||||
|
output, err := _decodeURI(call.Argument(0).string(), true)
|
||||||
|
if err {
|
||||||
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
|
}
|
||||||
|
return toValue_string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinGlobal_decodeURIComponent(call FunctionCall) Value {
|
||||||
|
output, err := _decodeURI(call.Argument(0).string(), false)
|
||||||
|
if err {
|
||||||
|
panic(call.runtime.panicURIError("URI malformed"))
|
||||||
|
}
|
||||||
|
return toValue_string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// escape/unescape
|
||||||
|
|
||||||
|
func builtin_shouldEscape(chr byte) bool {
|
||||||
|
if 'A' <= chr && chr <= 'Z' || 'a' <= chr && chr <= 'z' || '0' <= chr && chr <= '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !strings.ContainsRune("*_+-./", rune(chr))
|
||||||
|
}
|
||||||
|
|
||||||
|
const escapeBase16 = "0123456789ABCDEF"
|
||||||
|
|
||||||
|
func builtin_escape(input string) string {
|
||||||
|
output := make([]byte, 0, len(input))
|
||||||
|
length := len(input)
|
||||||
|
for index := 0; index < length; {
|
||||||
|
if builtin_shouldEscape(input[index]) {
|
||||||
|
chr, width := utf8.DecodeRuneInString(input[index:])
|
||||||
|
chr16 := utf16.Encode([]rune{chr})[0]
|
||||||
|
if 256 > chr16 {
|
||||||
|
output = append(output, '%',
|
||||||
|
escapeBase16[chr16>>4],
|
||||||
|
escapeBase16[chr16&15],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
output = append(output, '%', 'u',
|
||||||
|
escapeBase16[chr16>>12],
|
||||||
|
escapeBase16[(chr16>>8)&15],
|
||||||
|
escapeBase16[(chr16>>4)&15],
|
||||||
|
escapeBase16[chr16&15],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
index += width
|
||||||
|
|
||||||
|
} else {
|
||||||
|
output = append(output, input[index])
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtin_unescape(input string) string {
|
||||||
|
output := make([]rune, 0, len(input))
|
||||||
|
length := len(input)
|
||||||
|
for index := 0; index < length; {
|
||||||
|
if input[index] == '%' {
|
||||||
|
if index <= length-6 && input[index+1] == 'u' {
|
||||||
|
byte16, err := hex.DecodeString(input[index+2 : index+6])
|
||||||
|
if err == nil {
|
||||||
|
value := uint16(byte16[0])<<8 + uint16(byte16[1])
|
||||||
|
chr := utf16.Decode([]uint16{value})[0]
|
||||||
|
output = append(output, chr)
|
||||||
|
index += 6
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index <= length-3 {
|
||||||
|
byte8, err := hex.DecodeString(input[index+1 : index+3])
|
||||||
|
if err == nil {
|
||||||
|
value := uint16(byte8[0])
|
||||||
|
chr := utf16.Decode([]uint16{value})[0]
|
||||||
|
output = append(output, chr)
|
||||||
|
index += 3
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output = append(output, rune(input[index]))
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
return string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinGlobal_escape(call FunctionCall) Value {
|
||||||
|
return toValue_string(builtin_escape(call.Argument(0).string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinGlobal_unescape(call FunctionCall) Value {
|
||||||
|
return toValue_string(builtin_unescape(call.Argument(0).string()))
|
||||||
|
}
|
684
vendor/github.com/robertkrimen/otto/builtin_array.go
generated
vendored
Normal file
684
vendor/github.com/robertkrimen/otto/builtin_array.go
generated
vendored
Normal file
|
@ -0,0 +1,684 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Array
|
||||||
|
|
||||||
|
func builtinArray(call FunctionCall) Value {
|
||||||
|
return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewArray(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(builtinNewArrayNative(self.runtime, argumentList))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object {
|
||||||
|
if len(argumentList) == 1 {
|
||||||
|
firstArgument := argumentList[0]
|
||||||
|
if firstArgument.IsNumber() {
|
||||||
|
return runtime.newArray(arrayUint32(runtime, firstArgument))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runtime.newArrayOf(argumentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_toString(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
join := thisObject.get("join")
|
||||||
|
if join.isCallable() {
|
||||||
|
join := join._object()
|
||||||
|
return join.call(call.This, call.ArgumentList, false, nativeFrame)
|
||||||
|
}
|
||||||
|
return builtinObject_toString(call)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_toLocaleString(call FunctionCall) Value {
|
||||||
|
separator := ","
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
if length == 0 {
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
stringList := make([]string, 0, length)
|
||||||
|
for index := int64(0); index < length; index += 1 {
|
||||||
|
value := thisObject.get(arrayIndexToString(index))
|
||||||
|
stringValue := ""
|
||||||
|
switch value.kind {
|
||||||
|
case valueEmpty, valueUndefined, valueNull:
|
||||||
|
default:
|
||||||
|
object := call.runtime.toObject(value)
|
||||||
|
toLocaleString := object.get("toLocaleString")
|
||||||
|
if !toLocaleString.isCallable() {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string()
|
||||||
|
}
|
||||||
|
stringList = append(stringList, stringValue)
|
||||||
|
}
|
||||||
|
return toValue_string(strings.Join(stringList, separator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_concat(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
valueArray := []Value{}
|
||||||
|
source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...)
|
||||||
|
for _, item := range source {
|
||||||
|
switch item.kind {
|
||||||
|
case valueObject:
|
||||||
|
object := item._object()
|
||||||
|
if isArray(object) {
|
||||||
|
length := object.get("length").number().int64
|
||||||
|
for index := int64(0); index < length; index += 1 {
|
||||||
|
name := strconv.FormatInt(index, 10)
|
||||||
|
if object.hasProperty(name) {
|
||||||
|
valueArray = append(valueArray, object.get(name))
|
||||||
|
} else {
|
||||||
|
valueArray = append(valueArray, Value{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
valueArray = append(valueArray, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_shift(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
if 0 == length {
|
||||||
|
thisObject.put("length", toValue_int64(0), true)
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
first := thisObject.get("0")
|
||||||
|
for index := int64(1); index < length; index++ {
|
||||||
|
from := arrayIndexToString(index)
|
||||||
|
to := arrayIndexToString(index - 1)
|
||||||
|
if thisObject.hasProperty(from) {
|
||||||
|
thisObject.put(to, thisObject.get(from), true)
|
||||||
|
} else {
|
||||||
|
thisObject.delete(to, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thisObject.delete(arrayIndexToString(length-1), true)
|
||||||
|
thisObject.put("length", toValue_int64(length-1), true)
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_push(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
itemList := call.ArgumentList
|
||||||
|
index := int64(toUint32(thisObject.get("length")))
|
||||||
|
for len(itemList) > 0 {
|
||||||
|
thisObject.put(arrayIndexToString(index), itemList[0], true)
|
||||||
|
itemList = itemList[1:]
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
length := toValue_int64(index)
|
||||||
|
thisObject.put("length", length, true)
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_pop(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
if 0 == length {
|
||||||
|
thisObject.put("length", toValue_uint32(0), true)
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
last := thisObject.get(arrayIndexToString(length - 1))
|
||||||
|
thisObject.delete(arrayIndexToString(length-1), true)
|
||||||
|
thisObject.put("length", toValue_int64(length-1), true)
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_join(call FunctionCall) Value {
|
||||||
|
separator := ","
|
||||||
|
{
|
||||||
|
argument := call.Argument(0)
|
||||||
|
if argument.IsDefined() {
|
||||||
|
separator = argument.string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
if length == 0 {
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
stringList := make([]string, 0, length)
|
||||||
|
for index := int64(0); index < length; index += 1 {
|
||||||
|
value := thisObject.get(arrayIndexToString(index))
|
||||||
|
stringValue := ""
|
||||||
|
switch value.kind {
|
||||||
|
case valueEmpty, valueUndefined, valueNull:
|
||||||
|
default:
|
||||||
|
stringValue = value.string()
|
||||||
|
}
|
||||||
|
stringList = append(stringList, stringValue)
|
||||||
|
}
|
||||||
|
return toValue_string(strings.Join(stringList, separator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_splice(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
|
||||||
|
start := valueToRangeIndex(call.Argument(0), length, false)
|
||||||
|
deleteCount := length - start
|
||||||
|
if arg, ok := call.getArgument(1); ok {
|
||||||
|
deleteCount = valueToRangeIndex(arg, length-start, true)
|
||||||
|
}
|
||||||
|
valueArray := make([]Value, deleteCount)
|
||||||
|
|
||||||
|
for index := int64(0); index < deleteCount; index++ {
|
||||||
|
indexString := arrayIndexToString(int64(start + index))
|
||||||
|
if thisObject.hasProperty(indexString) {
|
||||||
|
valueArray[index] = thisObject.get(indexString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0, <1, 2, 3, 4>, 5, 6, 7
|
||||||
|
// a, b
|
||||||
|
// length 8 - delete 4 @ start 1
|
||||||
|
|
||||||
|
itemList := []Value{}
|
||||||
|
itemCount := int64(len(call.ArgumentList))
|
||||||
|
if itemCount > 2 {
|
||||||
|
itemCount -= 2 // Less the first two arguments
|
||||||
|
itemList = call.ArgumentList[2:]
|
||||||
|
} else {
|
||||||
|
itemCount = 0
|
||||||
|
}
|
||||||
|
if itemCount < deleteCount {
|
||||||
|
// The Object/Array is shrinking
|
||||||
|
stop := int64(length) - deleteCount
|
||||||
|
// The new length of the Object/Array before
|
||||||
|
// appending the itemList remainder
|
||||||
|
// Stopping at the lower bound of the insertion:
|
||||||
|
// Move an item from the after the deleted portion
|
||||||
|
// to a position after the inserted portion
|
||||||
|
for index := start; index < stop; index++ {
|
||||||
|
from := arrayIndexToString(index + deleteCount) // Position just after deletion
|
||||||
|
to := arrayIndexToString(index + itemCount) // Position just after splice (insertion)
|
||||||
|
if thisObject.hasProperty(from) {
|
||||||
|
thisObject.put(to, thisObject.get(from), true)
|
||||||
|
} else {
|
||||||
|
thisObject.delete(to, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delete off the end
|
||||||
|
// We don't bother to delete below <stop + itemCount> (if any) since those
|
||||||
|
// will be overwritten anyway
|
||||||
|
for index := int64(length); index > (stop + itemCount); index-- {
|
||||||
|
thisObject.delete(arrayIndexToString(index-1), true)
|
||||||
|
}
|
||||||
|
} else if itemCount > deleteCount {
|
||||||
|
// The Object/Array is growing
|
||||||
|
// The itemCount is greater than the deleteCount, so we do
|
||||||
|
// not have to worry about overwriting what we should be moving
|
||||||
|
// ---
|
||||||
|
// Starting from the upper bound of the deletion:
|
||||||
|
// Move an item from the after the deleted portion
|
||||||
|
// to a position after the inserted portion
|
||||||
|
for index := int64(length) - deleteCount; index > start; index-- {
|
||||||
|
from := arrayIndexToString(index + deleteCount - 1)
|
||||||
|
to := arrayIndexToString(index + itemCount - 1)
|
||||||
|
if thisObject.hasProperty(from) {
|
||||||
|
thisObject.put(to, thisObject.get(from), true)
|
||||||
|
} else {
|
||||||
|
thisObject.delete(to, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for index := int64(0); index < itemCount; index++ {
|
||||||
|
thisObject.put(arrayIndexToString(index+start), itemList[index], true)
|
||||||
|
}
|
||||||
|
thisObject.put("length", toValue_int64(int64(length)+itemCount-deleteCount), true)
|
||||||
|
|
||||||
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_slice(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
start, end := rangeStartEnd(call.ArgumentList, length, false)
|
||||||
|
|
||||||
|
if start >= end {
|
||||||
|
// Always an empty array
|
||||||
|
return toValue_object(call.runtime.newArray(0))
|
||||||
|
}
|
||||||
|
sliceLength := end - start
|
||||||
|
sliceValueArray := make([]Value, sliceLength)
|
||||||
|
|
||||||
|
for index := int64(0); index < sliceLength; index++ {
|
||||||
|
from := arrayIndexToString(index + start)
|
||||||
|
if thisObject.hasProperty(from) {
|
||||||
|
sliceValueArray[index] = thisObject.get(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_object(call.runtime.newArrayOf(sliceValueArray))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_unshift(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
itemList := call.ArgumentList
|
||||||
|
itemCount := int64(len(itemList))
|
||||||
|
|
||||||
|
for index := length; index > 0; index-- {
|
||||||
|
from := arrayIndexToString(index - 1)
|
||||||
|
to := arrayIndexToString(index + itemCount - 1)
|
||||||
|
if thisObject.hasProperty(from) {
|
||||||
|
thisObject.put(to, thisObject.get(from), true)
|
||||||
|
} else {
|
||||||
|
thisObject.delete(to, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for index := int64(0); index < itemCount; index++ {
|
||||||
|
thisObject.put(arrayIndexToString(index), itemList[index], true)
|
||||||
|
}
|
||||||
|
|
||||||
|
newLength := toValue_int64(length + itemCount)
|
||||||
|
thisObject.put("length", newLength, true)
|
||||||
|
return newLength
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_reverse(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
|
||||||
|
lower := struct {
|
||||||
|
name string
|
||||||
|
index int64
|
||||||
|
exists bool
|
||||||
|
}{}
|
||||||
|
upper := lower
|
||||||
|
|
||||||
|
lower.index = 0
|
||||||
|
middle := length / 2 // Division will floor
|
||||||
|
|
||||||
|
for lower.index != middle {
|
||||||
|
lower.name = arrayIndexToString(lower.index)
|
||||||
|
upper.index = length - lower.index - 1
|
||||||
|
upper.name = arrayIndexToString(upper.index)
|
||||||
|
|
||||||
|
lower.exists = thisObject.hasProperty(lower.name)
|
||||||
|
upper.exists = thisObject.hasProperty(upper.name)
|
||||||
|
|
||||||
|
if lower.exists && upper.exists {
|
||||||
|
lowerValue := thisObject.get(lower.name)
|
||||||
|
upperValue := thisObject.get(upper.name)
|
||||||
|
thisObject.put(lower.name, upperValue, true)
|
||||||
|
thisObject.put(upper.name, lowerValue, true)
|
||||||
|
} else if !lower.exists && upper.exists {
|
||||||
|
value := thisObject.get(upper.name)
|
||||||
|
thisObject.delete(upper.name, true)
|
||||||
|
thisObject.put(lower.name, value, true)
|
||||||
|
} else if lower.exists && !upper.exists {
|
||||||
|
value := thisObject.get(lower.name)
|
||||||
|
thisObject.delete(lower.name, true)
|
||||||
|
thisObject.put(upper.name, value, true)
|
||||||
|
} else {
|
||||||
|
// Nothing happens.
|
||||||
|
}
|
||||||
|
|
||||||
|
lower.index += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return call.This
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int {
|
||||||
|
j := struct {
|
||||||
|
name string
|
||||||
|
exists bool
|
||||||
|
defined bool
|
||||||
|
value string
|
||||||
|
}{}
|
||||||
|
k := j
|
||||||
|
j.name = arrayIndexToString(int64(index0))
|
||||||
|
j.exists = thisObject.hasProperty(j.name)
|
||||||
|
k.name = arrayIndexToString(int64(index1))
|
||||||
|
k.exists = thisObject.hasProperty(k.name)
|
||||||
|
|
||||||
|
if !j.exists && !k.exists {
|
||||||
|
return 0
|
||||||
|
} else if !j.exists {
|
||||||
|
return 1
|
||||||
|
} else if !k.exists {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
x := thisObject.get(j.name)
|
||||||
|
y := thisObject.get(k.name)
|
||||||
|
j.defined = x.IsDefined()
|
||||||
|
k.defined = y.IsDefined()
|
||||||
|
|
||||||
|
if !j.defined && !k.defined {
|
||||||
|
return 0
|
||||||
|
} else if !j.defined {
|
||||||
|
return 1
|
||||||
|
} else if !k.defined {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if compare == nil {
|
||||||
|
j.value = x.string()
|
||||||
|
k.value = y.string()
|
||||||
|
|
||||||
|
if j.value == k.value {
|
||||||
|
return 0
|
||||||
|
} else if j.value < k.value {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func arraySortSwap(thisObject *_object, index0, index1 uint) {
|
||||||
|
|
||||||
|
j := struct {
|
||||||
|
name string
|
||||||
|
exists bool
|
||||||
|
}{}
|
||||||
|
k := j
|
||||||
|
|
||||||
|
j.name = arrayIndexToString(int64(index0))
|
||||||
|
j.exists = thisObject.hasProperty(j.name)
|
||||||
|
k.name = arrayIndexToString(int64(index1))
|
||||||
|
k.exists = thisObject.hasProperty(k.name)
|
||||||
|
|
||||||
|
if j.exists && k.exists {
|
||||||
|
jValue := thisObject.get(j.name)
|
||||||
|
kValue := thisObject.get(k.name)
|
||||||
|
thisObject.put(j.name, kValue, true)
|
||||||
|
thisObject.put(k.name, jValue, true)
|
||||||
|
} else if !j.exists && k.exists {
|
||||||
|
value := thisObject.get(k.name)
|
||||||
|
thisObject.delete(k.name, true)
|
||||||
|
thisObject.put(j.name, value, true)
|
||||||
|
} else if j.exists && !k.exists {
|
||||||
|
value := thisObject.get(j.name)
|
||||||
|
thisObject.delete(j.name, true)
|
||||||
|
thisObject.put(k.name, value, true)
|
||||||
|
} else {
|
||||||
|
// Nothing happens.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func arraySortQuickPartition(thisObject *_object, left, right, pivot uint, compare *_object) (uint, uint) {
|
||||||
|
arraySortSwap(thisObject, pivot, right) // Right is now the pivot value
|
||||||
|
cursor := left
|
||||||
|
cursor2 := left
|
||||||
|
for index := left; index < right; index++ {
|
||||||
|
comparison := sortCompare(thisObject, index, right, compare) // Compare to the pivot value
|
||||||
|
if comparison < 0 {
|
||||||
|
arraySortSwap(thisObject, index, cursor)
|
||||||
|
if cursor < cursor2 {
|
||||||
|
arraySortSwap(thisObject, index, cursor2)
|
||||||
|
}
|
||||||
|
cursor += 1
|
||||||
|
cursor2 += 1
|
||||||
|
} else if comparison == 0 {
|
||||||
|
arraySortSwap(thisObject, index, cursor2)
|
||||||
|
cursor2 += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arraySortSwap(thisObject, cursor2, right)
|
||||||
|
return cursor, cursor2
|
||||||
|
}
|
||||||
|
|
||||||
|
func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) {
|
||||||
|
if left < right {
|
||||||
|
middle := left + (right-left)/2
|
||||||
|
pivot, pivot2 := arraySortQuickPartition(thisObject, left, right, middle, compare)
|
||||||
|
if pivot > 0 {
|
||||||
|
arraySortQuickSort(thisObject, left, pivot-1, compare)
|
||||||
|
}
|
||||||
|
arraySortQuickSort(thisObject, pivot2+1, right, compare)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_sort(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := uint(toUint32(thisObject.get("length")))
|
||||||
|
compareValue := call.Argument(0)
|
||||||
|
compare := compareValue._object()
|
||||||
|
if compareValue.IsUndefined() {
|
||||||
|
} else if !compareValue.isCallable() {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
if length > 1 {
|
||||||
|
arraySortQuickSort(thisObject, 0, length-1, compare)
|
||||||
|
}
|
||||||
|
return call.This
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_isArray(call FunctionCall) Value {
|
||||||
|
return toValue_bool(isArray(call.Argument(0)._object()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_indexOf(call FunctionCall) Value {
|
||||||
|
thisObject, matchValue := call.thisObject(), call.Argument(0)
|
||||||
|
if length := int64(toUint32(thisObject.get("length"))); length > 0 {
|
||||||
|
index := int64(0)
|
||||||
|
if len(call.ArgumentList) > 1 {
|
||||||
|
index = call.Argument(1).number().int64
|
||||||
|
}
|
||||||
|
if index < 0 {
|
||||||
|
if index += length; index < 0 {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
} else if index >= length {
|
||||||
|
index = -1
|
||||||
|
}
|
||||||
|
for ; index >= 0 && index < length; index++ {
|
||||||
|
name := arrayIndexToString(int64(index))
|
||||||
|
if !thisObject.hasProperty(name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value := thisObject.get(name)
|
||||||
|
if strictEqualityComparison(matchValue, value) {
|
||||||
|
return toValue_uint32(uint32(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_int(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_lastIndexOf(call FunctionCall) Value {
|
||||||
|
thisObject, matchValue := call.thisObject(), call.Argument(0)
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
index := length - 1
|
||||||
|
if len(call.ArgumentList) > 1 {
|
||||||
|
index = call.Argument(1).number().int64
|
||||||
|
}
|
||||||
|
if 0 > index {
|
||||||
|
index += length
|
||||||
|
}
|
||||||
|
if index > length {
|
||||||
|
index = length - 1
|
||||||
|
} else if 0 > index {
|
||||||
|
return toValue_int(-1)
|
||||||
|
}
|
||||||
|
for ; index >= 0; index-- {
|
||||||
|
name := arrayIndexToString(int64(index))
|
||||||
|
if !thisObject.hasProperty(name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value := thisObject.get(name)
|
||||||
|
if strictEqualityComparison(matchValue, value) {
|
||||||
|
return toValue_uint32(uint32(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_int(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_every(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := toValue_object(thisObject)
|
||||||
|
if iterator := call.Argument(0); iterator.isCallable() {
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
callThis := call.Argument(1)
|
||||||
|
for index := int64(0); index < length; index++ {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_some(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := toValue_object(thisObject)
|
||||||
|
if iterator := call.Argument(0); iterator.isCallable() {
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
callThis := call.Argument(1)
|
||||||
|
for index := int64(0); index < length; index++ {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_forEach(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := toValue_object(thisObject)
|
||||||
|
if iterator := call.Argument(0); iterator.isCallable() {
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
callThis := call.Argument(1)
|
||||||
|
for index := int64(0); index < length; index++ {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_map(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := toValue_object(thisObject)
|
||||||
|
if iterator := call.Argument(0); iterator.isCallable() {
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
callThis := call.Argument(1)
|
||||||
|
values := make([]Value, length)
|
||||||
|
for index := int64(0); index < length; index++ {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this)
|
||||||
|
} else {
|
||||||
|
values[index] = Value{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_object(call.runtime.newArrayOf(values))
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_filter(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := toValue_object(thisObject)
|
||||||
|
if iterator := call.Argument(0); iterator.isCallable() {
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
callThis := call.Argument(1)
|
||||||
|
values := make([]Value, 0)
|
||||||
|
for index := int64(0); index < length; index++ {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
value := thisObject.get(key)
|
||||||
|
if iterator.call(call.runtime, callThis, value, index, this).bool() {
|
||||||
|
values = append(values, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_object(call.runtime.newArrayOf(values))
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_reduce(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := toValue_object(thisObject)
|
||||||
|
if iterator := call.Argument(0); iterator.isCallable() {
|
||||||
|
initial := len(call.ArgumentList) > 1
|
||||||
|
start := call.Argument(1)
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
index := int64(0)
|
||||||
|
if length > 0 || initial {
|
||||||
|
var accumulator Value
|
||||||
|
if !initial {
|
||||||
|
for ; index < length; index++ {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
accumulator = thisObject.get(key)
|
||||||
|
index++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
accumulator = start
|
||||||
|
}
|
||||||
|
for ; index < length; index++ {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accumulator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinArray_reduceRight(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := toValue_object(thisObject)
|
||||||
|
if iterator := call.Argument(0); iterator.isCallable() {
|
||||||
|
initial := len(call.ArgumentList) > 1
|
||||||
|
start := call.Argument(1)
|
||||||
|
length := int64(toUint32(thisObject.get("length")))
|
||||||
|
if length > 0 || initial {
|
||||||
|
index := length - 1
|
||||||
|
var accumulator Value
|
||||||
|
if !initial {
|
||||||
|
for ; index >= 0; index-- {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
accumulator = thisObject.get(key)
|
||||||
|
index--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
accumulator = start
|
||||||
|
}
|
||||||
|
for ; index >= 0; index-- {
|
||||||
|
if key := arrayIndexToString(index); thisObject.hasProperty(key) {
|
||||||
|
accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accumulator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
28
vendor/github.com/robertkrimen/otto/builtin_boolean.go
generated
vendored
Normal file
28
vendor/github.com/robertkrimen/otto/builtin_boolean.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
// Boolean
|
||||||
|
|
||||||
|
func builtinBoolean(call FunctionCall) Value {
|
||||||
|
return toValue_bool(call.Argument(0).bool())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewBoolean(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinBoolean_toString(call FunctionCall) Value {
|
||||||
|
value := call.This
|
||||||
|
if !value.IsBoolean() {
|
||||||
|
// Will throw a TypeError if ThisObject is not a Boolean
|
||||||
|
value = call.thisClassObject("Boolean").primitiveValue()
|
||||||
|
}
|
||||||
|
return toValue_string(value.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinBoolean_valueOf(call FunctionCall) Value {
|
||||||
|
value := call.This
|
||||||
|
if !value.IsBoolean() {
|
||||||
|
value = call.thisClassObject("Boolean").primitiveValue()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
615
vendor/github.com/robertkrimen/otto/builtin_date.go
generated
vendored
Normal file
615
vendor/github.com/robertkrimen/otto/builtin_date.go
generated
vendored
Normal file
|
@ -0,0 +1,615 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
Time "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Date
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TODO Be like V8?
|
||||||
|
// builtinDate_goDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)"
|
||||||
|
builtinDate_goDateTimeLayout = Time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST"
|
||||||
|
builtinDate_goDateLayout = "Mon, 02 Jan 2006"
|
||||||
|
builtinDate_goTimeLayout = "15:04:05 MST"
|
||||||
|
)
|
||||||
|
|
||||||
|
func builtinDate(call FunctionCall) Value {
|
||||||
|
date := &_dateObject{}
|
||||||
|
date.Set(newDateTime([]Value{}, Time.Local))
|
||||||
|
return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewDate(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_toString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Local().Format(builtinDate_goDateTimeLayout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_toDateString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Local().Format(builtinDate_goDateLayout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_toTimeString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Local().Format(builtinDate_goTimeLayout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_toUTCString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_toISOString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Format("2006-01-02T15:04:05.000Z"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_toJSON(call FunctionCall) Value {
|
||||||
|
object := call.thisObject()
|
||||||
|
value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
|
||||||
|
{ // FIXME value.isFinite
|
||||||
|
value := value.float64()
|
||||||
|
if math.IsNaN(value) || math.IsInf(value, 0) {
|
||||||
|
return nullValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toISOString := object.get("toISOString")
|
||||||
|
if !toISOString.isCallable() {
|
||||||
|
// FIXME
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return toISOString.call(call.runtime, toValue_object(object), []Value{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_toGMTString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getTime(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
// We do this (convert away from a float) so the user
|
||||||
|
// does not get something back in exponential notation
|
||||||
|
return toValue_int64(int64(date.Epoch()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setTime(call FunctionCall) Value {
|
||||||
|
object := call.thisObject()
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
date.Set(call.Argument(0).float64())
|
||||||
|
object.value = date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) {
|
||||||
|
object := call.thisObject()
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return nil, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if argumentLimit > len(call.ArgumentList) {
|
||||||
|
argumentLimit = len(call.ArgumentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if argumentLimit == 0 {
|
||||||
|
object.value = invalidDateObject
|
||||||
|
return nil, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
valueList := make([]int, argumentLimit)
|
||||||
|
for index := 0; index < argumentLimit; index++ {
|
||||||
|
value := call.ArgumentList[index]
|
||||||
|
nm := value.number()
|
||||||
|
switch nm.kind {
|
||||||
|
case numberInteger, numberFloat:
|
||||||
|
default:
|
||||||
|
object.value = invalidDateObject
|
||||||
|
return nil, nil, nil, nil
|
||||||
|
}
|
||||||
|
valueList[index] = int(nm.int64)
|
||||||
|
}
|
||||||
|
baseTime := date.Time()
|
||||||
|
if timeLocal {
|
||||||
|
baseTime = baseTime.Local()
|
||||||
|
}
|
||||||
|
ecmaTime := ecmaTime(baseTime)
|
||||||
|
return object, &date, &ecmaTime, valueList
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_parse(call FunctionCall) Value {
|
||||||
|
date := call.Argument(0).string()
|
||||||
|
return toValue_float64(dateParse(date))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_UTC(call FunctionCall) Value {
|
||||||
|
return toValue_float64(newDateTime(call.ArgumentList, Time.UTC))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_now(call FunctionCall) Value {
|
||||||
|
call.ArgumentList = []Value(nil)
|
||||||
|
return builtinDate_UTC(call)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a placeholder
|
||||||
|
func builtinDate_toLocaleString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Local().Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a placeholder
|
||||||
|
func builtinDate_toLocaleDateString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Local().Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a placeholder
|
||||||
|
func builtinDate_toLocaleTimeString(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return toValue_string("Invalid Date")
|
||||||
|
}
|
||||||
|
return toValue_string(date.Time().Local().Format("15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_valueOf(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getYear(call FunctionCall) Value {
|
||||||
|
// Will throw a TypeError is ThisObject is nil or
|
||||||
|
// does not have Class of "Date"
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Local().Year() - 1900)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getFullYear(call FunctionCall) Value {
|
||||||
|
// Will throw a TypeError is ThisObject is nil or
|
||||||
|
// does not have Class of "Date"
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Local().Year())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCFullYear(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Year())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getMonth(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(dateFromGoMonth(date.Time().Local().Month()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCMonth(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(dateFromGoMonth(date.Time().Month()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getDate(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Local().Day())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCDate(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Day())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getDay(call FunctionCall) Value {
|
||||||
|
// Actually day of the week
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(dateFromGoDay(date.Time().Local().Weekday()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCDay(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(dateFromGoDay(date.Time().Weekday()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getHours(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Local().Hour())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCHours(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Hour())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getMinutes(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Local().Minute())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCMinutes(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Minute())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getSeconds(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Local().Second())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCSeconds(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Second())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getMilliseconds(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Local().Nanosecond() / (100 * 100 * 100))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_int(date.Time().Nanosecond() / (100 * 100 * 100))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_getTimezoneOffset(call FunctionCall) Value {
|
||||||
|
date := dateObjectOf(call.runtime, call.thisObject())
|
||||||
|
if date.isNaN {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
timeLocal := date.Time().Local()
|
||||||
|
// Is this kosher?
|
||||||
|
timeLocalAsUTC := Time.Date(
|
||||||
|
timeLocal.Year(),
|
||||||
|
timeLocal.Month(),
|
||||||
|
timeLocal.Day(),
|
||||||
|
timeLocal.Hour(),
|
||||||
|
timeLocal.Minute(),
|
||||||
|
timeLocal.Second(),
|
||||||
|
timeLocal.Nanosecond(),
|
||||||
|
Time.UTC,
|
||||||
|
)
|
||||||
|
return toValue_float64(date.Time().Sub(timeLocalAsUTC).Seconds() / 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setMilliseconds(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
ecmaTime.millisecond = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setUTCMilliseconds(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
ecmaTime.millisecond = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setSeconds(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 1 {
|
||||||
|
ecmaTime.millisecond = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.second = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setUTCSeconds(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 1 {
|
||||||
|
ecmaTime.millisecond = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.second = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setMinutes(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 2 {
|
||||||
|
ecmaTime.millisecond = value[2]
|
||||||
|
ecmaTime.second = value[1]
|
||||||
|
} else if len(value) > 1 {
|
||||||
|
ecmaTime.second = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.minute = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setUTCMinutes(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 2 {
|
||||||
|
ecmaTime.millisecond = value[2]
|
||||||
|
ecmaTime.second = value[1]
|
||||||
|
} else if len(value) > 1 {
|
||||||
|
ecmaTime.second = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.minute = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setHours(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 3 {
|
||||||
|
ecmaTime.millisecond = value[3]
|
||||||
|
ecmaTime.second = value[2]
|
||||||
|
ecmaTime.minute = value[1]
|
||||||
|
} else if len(value) > 2 {
|
||||||
|
ecmaTime.second = value[2]
|
||||||
|
ecmaTime.minute = value[1]
|
||||||
|
} else if len(value) > 1 {
|
||||||
|
ecmaTime.minute = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.hour = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setUTCHours(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, false)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 3 {
|
||||||
|
ecmaTime.millisecond = value[3]
|
||||||
|
ecmaTime.second = value[2]
|
||||||
|
ecmaTime.minute = value[1]
|
||||||
|
} else if len(value) > 2 {
|
||||||
|
ecmaTime.second = value[2]
|
||||||
|
ecmaTime.minute = value[1]
|
||||||
|
} else if len(value) > 1 {
|
||||||
|
ecmaTime.minute = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.hour = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setDate(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
ecmaTime.day = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setUTCDate(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
ecmaTime.day = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setMonth(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 1 {
|
||||||
|
ecmaTime.day = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.month = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setUTCMonth(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 1 {
|
||||||
|
ecmaTime.day = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.month = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setYear(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
year := value[0]
|
||||||
|
if 0 <= year && year <= 99 {
|
||||||
|
year += 1900
|
||||||
|
}
|
||||||
|
ecmaTime.year = year
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setFullYear(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 2 {
|
||||||
|
ecmaTime.day = value[2]
|
||||||
|
ecmaTime.month = value[1]
|
||||||
|
} else if len(value) > 1 {
|
||||||
|
ecmaTime.month = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.year = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinDate_setUTCFullYear(call FunctionCall) Value {
|
||||||
|
object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false)
|
||||||
|
if ecmaTime == nil {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 2 {
|
||||||
|
ecmaTime.day = value[2]
|
||||||
|
ecmaTime.month = value[1]
|
||||||
|
} else if len(value) > 1 {
|
||||||
|
ecmaTime.month = value[1]
|
||||||
|
}
|
||||||
|
ecmaTime.year = value[0]
|
||||||
|
|
||||||
|
date.SetTime(ecmaTime.goTime())
|
||||||
|
object.value = *date
|
||||||
|
return date.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// toUTCString
|
||||||
|
// toISOString
|
||||||
|
// toJSONString
|
||||||
|
// toJSON
|
126
vendor/github.com/robertkrimen/otto/builtin_error.go
generated
vendored
Normal file
126
vendor/github.com/robertkrimen/otto/builtin_error.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func builtinError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newError("Error", call.Argument(0), 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newError("Error", valueOfArrayIndex(argumentList, 0), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinError_toString(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
if thisObject == nil {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := "Error"
|
||||||
|
nameValue := thisObject.get("name")
|
||||||
|
if nameValue.IsDefined() {
|
||||||
|
name = nameValue.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
message := ""
|
||||||
|
messageValue := thisObject.get("message")
|
||||||
|
if messageValue.IsDefined() {
|
||||||
|
message = messageValue.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
return toValue_string(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(message) == 0 {
|
||||||
|
return toValue_string(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_string(fmt.Sprintf("%s: %s", name, message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newEvalError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("EvalError", message, 0)
|
||||||
|
self.prototype = runtime.global.EvalErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinEvalError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newEvalError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewEvalError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newTypeError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("TypeError", message, 0)
|
||||||
|
self.prototype = runtime.global.TypeErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinTypeError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newTypeError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewTypeError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newRangeError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("RangeError", message, 0)
|
||||||
|
self.prototype = runtime.global.RangeErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinRangeError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newRangeError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewRangeError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newURIError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("URIError", message, 0)
|
||||||
|
self.prototype = runtime.global.URIErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newReferenceError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("ReferenceError", message, 0)
|
||||||
|
self.prototype = runtime.global.ReferenceErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinReferenceError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newReferenceError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewReferenceError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newSyntaxError(message Value) *_object {
|
||||||
|
self := runtime.newErrorObject("SyntaxError", message, 0)
|
||||||
|
self.prototype = runtime.global.SyntaxErrorPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinSyntaxError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newSyntaxError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewSyntaxError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinURIError(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.runtime.newURIError(call.Argument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewURIError(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0)))
|
||||||
|
}
|
129
vendor/github.com/robertkrimen/otto/builtin_function.go
generated
vendored
Normal file
129
vendor/github.com/robertkrimen/otto/builtin_function.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function
|
||||||
|
|
||||||
|
func builtinFunction(call FunctionCall) Value {
|
||||||
|
return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewFunction(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(builtinNewFunctionNative(self.runtime, argumentList))
|
||||||
|
}
|
||||||
|
|
||||||
|
func argumentList2parameterList(argumentList []Value) []string {
|
||||||
|
parameterList := make([]string, 0, len(argumentList))
|
||||||
|
for _, value := range argumentList {
|
||||||
|
tmp := strings.FieldsFunc(value.string(), func(chr rune) bool {
|
||||||
|
return chr == ',' || unicode.IsSpace(chr)
|
||||||
|
})
|
||||||
|
parameterList = append(parameterList, tmp...)
|
||||||
|
}
|
||||||
|
return parameterList
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
|
||||||
|
|
||||||
|
func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object {
|
||||||
|
var parameterList, body string
|
||||||
|
count := len(argumentList)
|
||||||
|
if count > 0 {
|
||||||
|
tmp := make([]string, 0, count-1)
|
||||||
|
for _, value := range argumentList[0 : count-1] {
|
||||||
|
tmp = append(tmp, value.string())
|
||||||
|
}
|
||||||
|
parameterList = strings.Join(tmp, ",")
|
||||||
|
body = argumentList[count-1].string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
function, err := parser.ParseFunction(parameterList, body)
|
||||||
|
runtime.parseThrow(err) // Will panic/throw appropriately
|
||||||
|
cmpl := _compiler{}
|
||||||
|
cmpl_function := cmpl.parseExpression(function)
|
||||||
|
|
||||||
|
return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.globalStash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinFunction_toString(call FunctionCall) Value {
|
||||||
|
object := call.thisClassObject("Function") // Should throw a TypeError unless Function
|
||||||
|
switch fn := object.value.(type) {
|
||||||
|
case _nativeFunctionObject:
|
||||||
|
return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name))
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
return toValue_string(fn.node.source)
|
||||||
|
case _bindFunctionObject:
|
||||||
|
return toValue_string("function () { [native code] }")
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(call.runtime.panicTypeError("Function.toString()"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinFunction_apply(call FunctionCall) Value {
|
||||||
|
if !call.This.isCallable() {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
this := call.Argument(0)
|
||||||
|
if this.IsUndefined() {
|
||||||
|
// FIXME Not ECMA5
|
||||||
|
this = toValue_object(call.runtime.globalObject)
|
||||||
|
}
|
||||||
|
argumentList := call.Argument(1)
|
||||||
|
switch argumentList.kind {
|
||||||
|
case valueUndefined, valueNull:
|
||||||
|
return call.thisObject().call(this, nil, false, nativeFrame)
|
||||||
|
case valueObject:
|
||||||
|
default:
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayObject := argumentList._object()
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
length := int64(toUint32(arrayObject.get("length")))
|
||||||
|
valueArray := make([]Value, length)
|
||||||
|
for index := int64(0); index < length; index++ {
|
||||||
|
valueArray[index] = arrayObject.get(arrayIndexToString(index))
|
||||||
|
}
|
||||||
|
return thisObject.call(this, valueArray, false, nativeFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinFunction_call(call FunctionCall) Value {
|
||||||
|
if !call.This.isCallable() {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
this := call.Argument(0)
|
||||||
|
if this.IsUndefined() {
|
||||||
|
// FIXME Not ECMA5
|
||||||
|
this = toValue_object(call.runtime.globalObject)
|
||||||
|
}
|
||||||
|
if len(call.ArgumentList) >= 1 {
|
||||||
|
return thisObject.call(this, call.ArgumentList[1:], false, nativeFrame)
|
||||||
|
}
|
||||||
|
return thisObject.call(this, nil, false, nativeFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinFunction_bind(call FunctionCall) Value {
|
||||||
|
target := call.This
|
||||||
|
if !target.isCallable() {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
targetObject := target._object()
|
||||||
|
|
||||||
|
this := call.Argument(0)
|
||||||
|
argumentList := call.slice(1)
|
||||||
|
if this.IsUndefined() {
|
||||||
|
// FIXME Do this elsewhere?
|
||||||
|
this = toValue_object(call.runtime.globalObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList))
|
||||||
|
}
|
299
vendor/github.com/robertkrimen/otto/builtin_json.go
generated
vendored
Normal file
299
vendor/github.com/robertkrimen/otto/builtin_json.go
generated
vendored
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _builtinJSON_parseContext struct {
|
||||||
|
call FunctionCall
|
||||||
|
reviver Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinJSON_parse(call FunctionCall) Value {
|
||||||
|
ctx := _builtinJSON_parseContext{
|
||||||
|
call: call,
|
||||||
|
}
|
||||||
|
revive := false
|
||||||
|
if reviver := call.Argument(1); reviver.isCallable() {
|
||||||
|
revive = true
|
||||||
|
ctx.reviver = reviver
|
||||||
|
}
|
||||||
|
|
||||||
|
var root interface{}
|
||||||
|
err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
|
||||||
|
if err != nil {
|
||||||
|
panic(call.runtime.panicSyntaxError(err.Error()))
|
||||||
|
}
|
||||||
|
value, exists := builtinJSON_parseWalk(ctx, root)
|
||||||
|
if !exists {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
|
if revive {
|
||||||
|
root := ctx.call.runtime.newObject()
|
||||||
|
root.put("", value, false)
|
||||||
|
return builtinJSON_reviveWalk(ctx, root, "")
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value {
|
||||||
|
value := holder.get(name)
|
||||||
|
if object := value._object(); object != nil {
|
||||||
|
if isArray(object) {
|
||||||
|
length := int64(objectLength(object))
|
||||||
|
for index := int64(0); index < length; index += 1 {
|
||||||
|
name := arrayIndexToString(index)
|
||||||
|
value := builtinJSON_reviveWalk(ctx, object, name)
|
||||||
|
if value.IsUndefined() {
|
||||||
|
object.delete(name, false)
|
||||||
|
} else {
|
||||||
|
object.defineProperty(name, value, 0111, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
object.enumerate(false, func(name string) bool {
|
||||||
|
value := builtinJSON_reviveWalk(ctx, object, name)
|
||||||
|
if value.IsUndefined() {
|
||||||
|
object.delete(name, false)
|
||||||
|
} else {
|
||||||
|
object.defineProperty(name, value, 0111, false)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) {
|
||||||
|
switch value := rawValue.(type) {
|
||||||
|
case nil:
|
||||||
|
return nullValue, true
|
||||||
|
case bool:
|
||||||
|
return toValue_bool(value), true
|
||||||
|
case string:
|
||||||
|
return toValue_string(value), true
|
||||||
|
case float64:
|
||||||
|
return toValue_float64(value), true
|
||||||
|
case []interface{}:
|
||||||
|
arrayValue := make([]Value, len(value))
|
||||||
|
for index, rawValue := range value {
|
||||||
|
if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
|
||||||
|
arrayValue[index] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true
|
||||||
|
case map[string]interface{}:
|
||||||
|
object := ctx.call.runtime.newObject()
|
||||||
|
for name, rawValue := range value {
|
||||||
|
if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists {
|
||||||
|
object.put(name, value, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_object(object), true
|
||||||
|
}
|
||||||
|
return Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
type _builtinJSON_stringifyContext struct {
|
||||||
|
call FunctionCall
|
||||||
|
stack []*_object
|
||||||
|
propertyList []string
|
||||||
|
replacerFunction *Value
|
||||||
|
gap string
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinJSON_stringify(call FunctionCall) Value {
|
||||||
|
ctx := _builtinJSON_stringifyContext{
|
||||||
|
call: call,
|
||||||
|
stack: []*_object{nil},
|
||||||
|
}
|
||||||
|
replacer := call.Argument(1)._object()
|
||||||
|
if replacer != nil {
|
||||||
|
if isArray(replacer) {
|
||||||
|
length := objectLength(replacer)
|
||||||
|
seen := map[string]bool{}
|
||||||
|
propertyList := make([]string, length)
|
||||||
|
length = 0
|
||||||
|
for index, _ := range propertyList {
|
||||||
|
value := replacer.get(arrayIndexToString(int64(index)))
|
||||||
|
switch value.kind {
|
||||||
|
case valueObject:
|
||||||
|
switch value.value.(*_object).class {
|
||||||
|
case "String":
|
||||||
|
case "Number":
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case valueString:
|
||||||
|
case valueNumber:
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := value.string()
|
||||||
|
if seen[name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[name] = true
|
||||||
|
length += 1
|
||||||
|
propertyList[index] = name
|
||||||
|
}
|
||||||
|
ctx.propertyList = propertyList[0:length]
|
||||||
|
} else if replacer.class == "Function" {
|
||||||
|
value := toValue_object(replacer)
|
||||||
|
ctx.replacerFunction = &value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if spaceValue, exists := call.getArgument(2); exists {
|
||||||
|
if spaceValue.kind == valueObject {
|
||||||
|
switch spaceValue.value.(*_object).class {
|
||||||
|
case "String":
|
||||||
|
spaceValue = toValue_string(spaceValue.string())
|
||||||
|
case "Number":
|
||||||
|
spaceValue = spaceValue.numberValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch spaceValue.kind {
|
||||||
|
case valueString:
|
||||||
|
value := spaceValue.string()
|
||||||
|
if len(value) > 10 {
|
||||||
|
ctx.gap = value[0:10]
|
||||||
|
} else {
|
||||||
|
ctx.gap = value
|
||||||
|
}
|
||||||
|
case valueNumber:
|
||||||
|
value := spaceValue.number().int64
|
||||||
|
if value > 10 {
|
||||||
|
value = 10
|
||||||
|
} else if value < 0 {
|
||||||
|
value = 0
|
||||||
|
}
|
||||||
|
ctx.gap = strings.Repeat(" ", int(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
holder := call.runtime.newObject()
|
||||||
|
holder.put("", call.Argument(0), false)
|
||||||
|
value, exists := builtinJSON_stringifyWalk(ctx, "", holder)
|
||||||
|
if !exists {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
valueJSON, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
panic(call.runtime.panicTypeError(err.Error()))
|
||||||
|
}
|
||||||
|
if ctx.gap != "" {
|
||||||
|
valueJSON1 := bytes.Buffer{}
|
||||||
|
json.Indent(&valueJSON1, valueJSON, "", ctx.gap)
|
||||||
|
valueJSON = valueJSON1.Bytes()
|
||||||
|
}
|
||||||
|
return toValue_string(string(valueJSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) {
|
||||||
|
value := holder.get(key)
|
||||||
|
|
||||||
|
if value.IsObject() {
|
||||||
|
object := value._object()
|
||||||
|
if toJSON := object.get("toJSON"); toJSON.IsFunction() {
|
||||||
|
value = toJSON.call(ctx.call.runtime, value, key)
|
||||||
|
} else {
|
||||||
|
// If the object is a GoStruct or something that implements json.Marshaler
|
||||||
|
if object.objectClass.marshalJSON != nil {
|
||||||
|
marshaler := object.objectClass.marshalJSON(object)
|
||||||
|
if marshaler != nil {
|
||||||
|
return marshaler, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.replacerFunction != nil {
|
||||||
|
value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.kind == valueObject {
|
||||||
|
switch value.value.(*_object).class {
|
||||||
|
case "Boolean":
|
||||||
|
value = value._object().value.(Value)
|
||||||
|
case "String":
|
||||||
|
value = toValue_string(value.string())
|
||||||
|
case "Number":
|
||||||
|
value = value.numberValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value.kind {
|
||||||
|
case valueBoolean:
|
||||||
|
return value.bool(), true
|
||||||
|
case valueString:
|
||||||
|
return value.string(), true
|
||||||
|
case valueNumber:
|
||||||
|
integer := value.number()
|
||||||
|
switch integer.kind {
|
||||||
|
case numberInteger:
|
||||||
|
return integer.int64, true
|
||||||
|
case numberFloat:
|
||||||
|
return integer.float64, true
|
||||||
|
default:
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
case valueNull:
|
||||||
|
return nil, true
|
||||||
|
case valueObject:
|
||||||
|
holder := value._object()
|
||||||
|
if value := value._object(); nil != value {
|
||||||
|
for _, object := range ctx.stack {
|
||||||
|
if holder == object {
|
||||||
|
panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.stack = append(ctx.stack, value)
|
||||||
|
defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
|
||||||
|
}
|
||||||
|
if isArray(holder) {
|
||||||
|
var length uint32
|
||||||
|
switch value := holder.get("length").value.(type) {
|
||||||
|
case uint32:
|
||||||
|
length = value
|
||||||
|
case int:
|
||||||
|
if value >= 0 {
|
||||||
|
length = uint32(value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
|
||||||
|
}
|
||||||
|
array := make([]interface{}, length)
|
||||||
|
for index, _ := range array {
|
||||||
|
name := arrayIndexToString(int64(index))
|
||||||
|
value, _ := builtinJSON_stringifyWalk(ctx, name, holder)
|
||||||
|
array[index] = value
|
||||||
|
}
|
||||||
|
return array, true
|
||||||
|
} else if holder.class != "Function" {
|
||||||
|
object := map[string]interface{}{}
|
||||||
|
if ctx.propertyList != nil {
|
||||||
|
for _, name := range ctx.propertyList {
|
||||||
|
value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
|
||||||
|
if exists {
|
||||||
|
object[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Go maps are without order, so this doesn't conform to the ECMA ordering
|
||||||
|
// standard, but oh well...
|
||||||
|
holder.enumerate(false, func(name string) bool {
|
||||||
|
value, exists := builtinJSON_stringifyWalk(ctx, name, holder)
|
||||||
|
if exists {
|
||||||
|
object[name] = value
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return object, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
151
vendor/github.com/robertkrimen/otto/builtin_math.go
generated
vendored
Normal file
151
vendor/github.com/robertkrimen/otto/builtin_math.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Math
|
||||||
|
|
||||||
|
func builtinMath_abs(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Abs(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_acos(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Acos(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_asin(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Asin(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_atan(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Atan(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_atan2(call FunctionCall) Value {
|
||||||
|
y := call.Argument(0).float64()
|
||||||
|
if math.IsNaN(y) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
x := call.Argument(1).float64()
|
||||||
|
if math.IsNaN(x) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_float64(math.Atan2(y, x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_cos(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Cos(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_ceil(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Ceil(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_exp(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Exp(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_floor(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Floor(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_log(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Log(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_max(call FunctionCall) Value {
|
||||||
|
switch len(call.ArgumentList) {
|
||||||
|
case 0:
|
||||||
|
return negativeInfinityValue()
|
||||||
|
case 1:
|
||||||
|
return toValue_float64(call.ArgumentList[0].float64())
|
||||||
|
}
|
||||||
|
result := call.ArgumentList[0].float64()
|
||||||
|
if math.IsNaN(result) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
for _, value := range call.ArgumentList[1:] {
|
||||||
|
value := value.float64()
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
result = math.Max(result, value)
|
||||||
|
}
|
||||||
|
return toValue_float64(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_min(call FunctionCall) Value {
|
||||||
|
switch len(call.ArgumentList) {
|
||||||
|
case 0:
|
||||||
|
return positiveInfinityValue()
|
||||||
|
case 1:
|
||||||
|
return toValue_float64(call.ArgumentList[0].float64())
|
||||||
|
}
|
||||||
|
result := call.ArgumentList[0].float64()
|
||||||
|
if math.IsNaN(result) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
for _, value := range call.ArgumentList[1:] {
|
||||||
|
value := value.float64()
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
result = math.Min(result, value)
|
||||||
|
}
|
||||||
|
return toValue_float64(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_pow(call FunctionCall) Value {
|
||||||
|
// TODO Make sure this works according to the specification (15.8.2.13)
|
||||||
|
x := call.Argument(0).float64()
|
||||||
|
y := call.Argument(1).float64()
|
||||||
|
if math.Abs(x) == 1 && math.IsInf(y, 0) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_float64(math.Pow(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_random(call FunctionCall) Value {
|
||||||
|
var v float64
|
||||||
|
if call.runtime.random != nil {
|
||||||
|
v = call.runtime.random()
|
||||||
|
} else {
|
||||||
|
v = rand.Float64()
|
||||||
|
}
|
||||||
|
return toValue_float64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_round(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
value := math.Floor(number + 0.5)
|
||||||
|
if value == 0 {
|
||||||
|
value = math.Copysign(0, number)
|
||||||
|
}
|
||||||
|
return toValue_float64(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_sin(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Sin(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_sqrt(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Sqrt(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinMath_tan(call FunctionCall) Value {
|
||||||
|
number := call.Argument(0).float64()
|
||||||
|
return toValue_float64(math.Tan(number))
|
||||||
|
}
|
100
vendor/github.com/robertkrimen/otto/builtin_number.go
generated
vendored
Normal file
100
vendor/github.com/robertkrimen/otto/builtin_number.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Number
|
||||||
|
|
||||||
|
func numberValueFromNumberArgumentList(argumentList []Value) Value {
|
||||||
|
if len(argumentList) > 0 {
|
||||||
|
return argumentList[0].numberValue()
|
||||||
|
}
|
||||||
|
return toValue_int(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber(call FunctionCall) Value {
|
||||||
|
return numberValueFromNumberArgumentList(call.ArgumentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewNumber(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber_toString(call FunctionCall) Value {
|
||||||
|
// Will throw a TypeError if ThisObject is not a Number
|
||||||
|
value := call.thisClassObject("Number").primitiveValue()
|
||||||
|
radix := 10
|
||||||
|
radixArgument := call.Argument(0)
|
||||||
|
if radixArgument.IsDefined() {
|
||||||
|
integer := toIntegerFloat(radixArgument)
|
||||||
|
if integer < 2 || integer > 36 {
|
||||||
|
panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36"))
|
||||||
|
}
|
||||||
|
radix = int(integer)
|
||||||
|
}
|
||||||
|
if radix == 10 {
|
||||||
|
return toValue_string(value.string())
|
||||||
|
}
|
||||||
|
return toValue_string(numberToStringRadix(value, radix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber_valueOf(call FunctionCall) Value {
|
||||||
|
return call.thisClassObject("Number").primitiveValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber_toFixed(call FunctionCall) Value {
|
||||||
|
precision := toIntegerFloat(call.Argument(0))
|
||||||
|
if 20 < precision || 0 > precision {
|
||||||
|
panic(call.runtime.panicRangeError("toFixed() precision must be between 0 and 20"))
|
||||||
|
}
|
||||||
|
if call.This.IsNaN() {
|
||||||
|
return toValue_string("NaN")
|
||||||
|
}
|
||||||
|
value := call.This.float64()
|
||||||
|
if math.Abs(value) >= 1e21 {
|
||||||
|
return toValue_string(floatToString(value, 64))
|
||||||
|
}
|
||||||
|
return toValue_string(strconv.FormatFloat(call.This.float64(), 'f', int(precision), 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber_toExponential(call FunctionCall) Value {
|
||||||
|
if call.This.IsNaN() {
|
||||||
|
return toValue_string("NaN")
|
||||||
|
}
|
||||||
|
precision := float64(-1)
|
||||||
|
if value := call.Argument(0); value.IsDefined() {
|
||||||
|
precision = toIntegerFloat(value)
|
||||||
|
if 0 > precision {
|
||||||
|
panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_string(strconv.FormatFloat(call.This.float64(), 'e', int(precision), 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber_toPrecision(call FunctionCall) Value {
|
||||||
|
if call.This.IsNaN() {
|
||||||
|
return toValue_string("NaN")
|
||||||
|
}
|
||||||
|
value := call.Argument(0)
|
||||||
|
if value.IsUndefined() {
|
||||||
|
return toValue_string(call.This.string())
|
||||||
|
}
|
||||||
|
precision := toIntegerFloat(value)
|
||||||
|
if 1 > precision {
|
||||||
|
panic(call.runtime.panicRangeError("toPrecision() precision must be greater than 1"))
|
||||||
|
}
|
||||||
|
return toValue_string(strconv.FormatFloat(call.This.float64(), 'g', int(precision), 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber_isNaN(call FunctionCall) Value {
|
||||||
|
if len(call.ArgumentList) < 1 {
|
||||||
|
return toValue_bool(false)
|
||||||
|
}
|
||||||
|
return toValue_bool(call.Argument(0).IsNaN())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNumber_toLocaleString(call FunctionCall) Value {
|
||||||
|
return builtinNumber_toString(call)
|
||||||
|
}
|
289
vendor/github.com/robertkrimen/otto/builtin_object.go
generated
vendored
Normal file
289
vendor/github.com/robertkrimen/otto/builtin_object.go
generated
vendored
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Object
|
||||||
|
|
||||||
|
func builtinObject(call FunctionCall) Value {
|
||||||
|
value := call.Argument(0)
|
||||||
|
switch value.kind {
|
||||||
|
case valueUndefined, valueNull:
|
||||||
|
return toValue_object(call.runtime.newObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_object(call.runtime.toObject(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewObject(self *_object, argumentList []Value) Value {
|
||||||
|
value := valueOfArrayIndex(argumentList, 0)
|
||||||
|
switch value.kind {
|
||||||
|
case valueNull, valueUndefined:
|
||||||
|
case valueNumber, valueString, valueBoolean:
|
||||||
|
return toValue_object(self.runtime.toObject(value))
|
||||||
|
case valueObject:
|
||||||
|
return value
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return toValue_object(self.runtime.newObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_valueOf(call FunctionCall) Value {
|
||||||
|
return toValue_object(call.thisObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_hasOwnProperty(call FunctionCall) Value {
|
||||||
|
propertyName := call.Argument(0).string()
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
return toValue_bool(thisObject.hasOwnProperty(propertyName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_isPrototypeOf(call FunctionCall) Value {
|
||||||
|
value := call.Argument(0)
|
||||||
|
if !value.IsObject() {
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
prototype := call.toObject(value).prototype
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
for prototype != nil {
|
||||||
|
if thisObject == prototype {
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
prototype = prototype.prototype
|
||||||
|
}
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_propertyIsEnumerable(call FunctionCall) Value {
|
||||||
|
propertyName := call.Argument(0).string()
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
property := thisObject.getOwnProperty(propertyName)
|
||||||
|
if property != nil && property.enumerable() {
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_toString(call FunctionCall) Value {
|
||||||
|
result := ""
|
||||||
|
if call.This.IsUndefined() {
|
||||||
|
result = "[object Undefined]"
|
||||||
|
} else if call.This.IsNull() {
|
||||||
|
result = "[object Null]"
|
||||||
|
} else {
|
||||||
|
result = fmt.Sprintf("[object %s]", call.thisObject().class)
|
||||||
|
}
|
||||||
|
return toValue_string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_toLocaleString(call FunctionCall) Value {
|
||||||
|
toString := call.thisObject().get("toString")
|
||||||
|
if !toString.isCallable() {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return toString.call(call.runtime, call.This)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_getPrototypeOf(call FunctionCall) Value {
|
||||||
|
objectValue := call.Argument(0)
|
||||||
|
object := objectValue._object()
|
||||||
|
if object == nil {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
if object.prototype == nil {
|
||||||
|
return nullValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_object(object.prototype)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_getOwnPropertyDescriptor(call FunctionCall) Value {
|
||||||
|
objectValue := call.Argument(0)
|
||||||
|
object := objectValue._object()
|
||||||
|
if object == nil {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := call.Argument(1).string()
|
||||||
|
descriptor := object.getOwnProperty(name)
|
||||||
|
if descriptor == nil {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_defineProperty(call FunctionCall) Value {
|
||||||
|
objectValue := call.Argument(0)
|
||||||
|
object := objectValue._object()
|
||||||
|
if object == nil {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
name := call.Argument(1).string()
|
||||||
|
descriptor := toPropertyDescriptor(call.runtime, call.Argument(2))
|
||||||
|
object.defineOwnProperty(name, descriptor, true)
|
||||||
|
return objectValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_defineProperties(call FunctionCall) Value {
|
||||||
|
objectValue := call.Argument(0)
|
||||||
|
object := objectValue._object()
|
||||||
|
if object == nil {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
properties := call.runtime.toObject(call.Argument(1))
|
||||||
|
properties.enumerate(false, func(name string) bool {
|
||||||
|
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
|
||||||
|
object.defineOwnProperty(name, descriptor, true)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return objectValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_create(call FunctionCall) Value {
|
||||||
|
prototypeValue := call.Argument(0)
|
||||||
|
if !prototypeValue.IsNull() && !prototypeValue.IsObject() {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
object := call.runtime.newObject()
|
||||||
|
object.prototype = prototypeValue._object()
|
||||||
|
|
||||||
|
propertiesValue := call.Argument(1)
|
||||||
|
if propertiesValue.IsDefined() {
|
||||||
|
properties := call.runtime.toObject(propertiesValue)
|
||||||
|
properties.enumerate(false, func(name string) bool {
|
||||||
|
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
|
||||||
|
object.defineOwnProperty(name, descriptor, true)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_object(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_isExtensible(call FunctionCall) Value {
|
||||||
|
object := call.Argument(0)
|
||||||
|
if object := object._object(); object != nil {
|
||||||
|
return toValue_bool(object.extensible)
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_preventExtensions(call FunctionCall) Value {
|
||||||
|
object := call.Argument(0)
|
||||||
|
if object := object._object(); object != nil {
|
||||||
|
object.extensible = false
|
||||||
|
} else {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_isSealed(call FunctionCall) Value {
|
||||||
|
object := call.Argument(0)
|
||||||
|
if object := object._object(); object != nil {
|
||||||
|
if object.extensible {
|
||||||
|
return toValue_bool(false)
|
||||||
|
}
|
||||||
|
result := true
|
||||||
|
object.enumerate(true, func(name string) bool {
|
||||||
|
property := object.getProperty(name)
|
||||||
|
if property.configurable() {
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return toValue_bool(result)
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_seal(call FunctionCall) Value {
|
||||||
|
object := call.Argument(0)
|
||||||
|
if object := object._object(); object != nil {
|
||||||
|
object.enumerate(true, func(name string) bool {
|
||||||
|
if property := object.getOwnProperty(name); nil != property && property.configurable() {
|
||||||
|
property.configureOff()
|
||||||
|
object.defineOwnProperty(name, *property, true)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
object.extensible = false
|
||||||
|
} else {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_isFrozen(call FunctionCall) Value {
|
||||||
|
object := call.Argument(0)
|
||||||
|
if object := object._object(); object != nil {
|
||||||
|
if object.extensible {
|
||||||
|
return toValue_bool(false)
|
||||||
|
}
|
||||||
|
result := true
|
||||||
|
object.enumerate(true, func(name string) bool {
|
||||||
|
property := object.getProperty(name)
|
||||||
|
if property.configurable() || property.writable() {
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return toValue_bool(result)
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_freeze(call FunctionCall) Value {
|
||||||
|
object := call.Argument(0)
|
||||||
|
if object := object._object(); object != nil {
|
||||||
|
object.enumerate(true, func(name string) bool {
|
||||||
|
if property, update := object.getOwnProperty(name), false; nil != property {
|
||||||
|
if property.isDataDescriptor() && property.writable() {
|
||||||
|
property.writeOff()
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
if property.configurable() {
|
||||||
|
property.configureOff()
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
if update {
|
||||||
|
object.defineOwnProperty(name, *property, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
object.extensible = false
|
||||||
|
} else {
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_keys(call FunctionCall) Value {
|
||||||
|
if object, keys := call.Argument(0)._object(), []Value(nil); nil != object {
|
||||||
|
object.enumerate(false, func(name string) bool {
|
||||||
|
keys = append(keys, toValue_string(name))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return toValue_object(call.runtime.newArrayOf(keys))
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinObject_getOwnPropertyNames(call FunctionCall) Value {
|
||||||
|
if object, propertyNames := call.Argument(0)._object(), []Value(nil); nil != object {
|
||||||
|
object.enumerate(true, func(name string) bool {
|
||||||
|
if object.hasOwnProperty(name) {
|
||||||
|
propertyNames = append(propertyNames, toValue_string(name))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return toValue_object(call.runtime.newArrayOf(propertyNames))
|
||||||
|
}
|
||||||
|
panic(call.runtime.panicTypeError())
|
||||||
|
}
|
65
vendor/github.com/robertkrimen/otto/builtin_regexp.go
generated
vendored
Normal file
65
vendor/github.com/robertkrimen/otto/builtin_regexp.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegExp
|
||||||
|
|
||||||
|
func builtinRegExp(call FunctionCall) Value {
|
||||||
|
pattern := call.Argument(0)
|
||||||
|
flags := call.Argument(1)
|
||||||
|
if object := pattern._object(); object != nil {
|
||||||
|
if object.class == "RegExp" && flags.IsUndefined() {
|
||||||
|
return pattern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_object(call.runtime.newRegExp(pattern, flags))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewRegExp(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newRegExp(
|
||||||
|
valueOfArrayIndex(argumentList, 0),
|
||||||
|
valueOfArrayIndex(argumentList, 1),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinRegExp_toString(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
source := thisObject.get("source").string()
|
||||||
|
flags := []byte{}
|
||||||
|
if thisObject.get("global").bool() {
|
||||||
|
flags = append(flags, 'g')
|
||||||
|
}
|
||||||
|
if thisObject.get("ignoreCase").bool() {
|
||||||
|
flags = append(flags, 'i')
|
||||||
|
}
|
||||||
|
if thisObject.get("multiline").bool() {
|
||||||
|
flags = append(flags, 'm')
|
||||||
|
}
|
||||||
|
return toValue_string(fmt.Sprintf("/%s/%s", source, flags))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinRegExp_exec(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
target := call.Argument(0).string()
|
||||||
|
match, result := execRegExp(thisObject, target)
|
||||||
|
if !match {
|
||||||
|
return nullValue
|
||||||
|
}
|
||||||
|
return toValue_object(execResultToArray(call.runtime, target, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinRegExp_test(call FunctionCall) Value {
|
||||||
|
thisObject := call.thisObject()
|
||||||
|
target := call.Argument(0).string()
|
||||||
|
match, _ := execRegExp(thisObject, target)
|
||||||
|
return toValue_bool(match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinRegExp_compile(call FunctionCall) Value {
|
||||||
|
// This (useless) function is deprecated, but is here to provide some
|
||||||
|
// semblance of compatibility.
|
||||||
|
// Caveat emptor: it may not be around for long.
|
||||||
|
return Value{}
|
||||||
|
}
|
500
vendor/github.com/robertkrimen/otto/builtin_string.go
generated
vendored
Normal file
500
vendor/github.com/robertkrimen/otto/builtin_string.go
generated
vendored
Normal file
|
@ -0,0 +1,500 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String
|
||||||
|
|
||||||
|
func stringValueFromStringArgumentList(argumentList []Value) Value {
|
||||||
|
if len(argumentList) > 0 {
|
||||||
|
return toValue_string(argumentList[0].string())
|
||||||
|
}
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString(call FunctionCall) Value {
|
||||||
|
return stringValueFromStringArgumentList(call.ArgumentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinNewString(self *_object, argumentList []Value) Value {
|
||||||
|
return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_toString(call FunctionCall) Value {
|
||||||
|
return call.thisClassObject("String").primitiveValue()
|
||||||
|
}
|
||||||
|
func builtinString_valueOf(call FunctionCall) Value {
|
||||||
|
return call.thisClassObject("String").primitiveValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_fromCharCode(call FunctionCall) Value {
|
||||||
|
chrList := make([]uint16, len(call.ArgumentList))
|
||||||
|
for index, value := range call.ArgumentList {
|
||||||
|
chrList[index] = toUint16(value)
|
||||||
|
}
|
||||||
|
return toValue_string16(chrList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_charAt(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
idx := int(call.Argument(0).number().int64)
|
||||||
|
chr := stringAt(call.This._object().stringValue(), idx)
|
||||||
|
if chr == utf8.RuneError {
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
return toValue_string(string(chr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_charCodeAt(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
idx := int(call.Argument(0).number().int64)
|
||||||
|
chr := stringAt(call.This._object().stringValue(), idx)
|
||||||
|
if chr == utf8.RuneError {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
return toValue_uint16(uint16(chr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_concat(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
var value bytes.Buffer
|
||||||
|
value.WriteString(call.This.string())
|
||||||
|
for _, item := range call.ArgumentList {
|
||||||
|
value.WriteString(item.string())
|
||||||
|
}
|
||||||
|
return toValue_string(value.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_indexOf(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
value := call.This.string()
|
||||||
|
target := call.Argument(0).string()
|
||||||
|
if 2 > len(call.ArgumentList) {
|
||||||
|
return toValue_int(strings.Index(value, target))
|
||||||
|
}
|
||||||
|
start := toIntegerFloat(call.Argument(1))
|
||||||
|
if 0 > start {
|
||||||
|
start = 0
|
||||||
|
} else if start >= float64(len(value)) {
|
||||||
|
if target == "" {
|
||||||
|
return toValue_int(len(value))
|
||||||
|
}
|
||||||
|
return toValue_int(-1)
|
||||||
|
}
|
||||||
|
index := strings.Index(value[int(start):], target)
|
||||||
|
if index >= 0 {
|
||||||
|
index += int(start)
|
||||||
|
}
|
||||||
|
return toValue_int(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_lastIndexOf(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
value := call.This.string()
|
||||||
|
target := call.Argument(0).string()
|
||||||
|
if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() {
|
||||||
|
return toValue_int(strings.LastIndex(value, target))
|
||||||
|
}
|
||||||
|
length := len(value)
|
||||||
|
if length == 0 {
|
||||||
|
return toValue_int(strings.LastIndex(value, target))
|
||||||
|
}
|
||||||
|
start := call.ArgumentList[1].number()
|
||||||
|
if start.kind == numberInfinity { // FIXME
|
||||||
|
// startNumber is infinity, so start is the end of string (start = length)
|
||||||
|
return toValue_int(strings.LastIndex(value, target))
|
||||||
|
}
|
||||||
|
if 0 > start.int64 {
|
||||||
|
start.int64 = 0
|
||||||
|
}
|
||||||
|
end := int(start.int64) + len(target)
|
||||||
|
if end > length {
|
||||||
|
end = length
|
||||||
|
}
|
||||||
|
return toValue_int(strings.LastIndex(value[:end], target))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_match(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
target := call.This.string()
|
||||||
|
matcherValue := call.Argument(0)
|
||||||
|
matcher := matcherValue._object()
|
||||||
|
if !matcherValue.IsObject() || matcher.class != "RegExp" {
|
||||||
|
matcher = call.runtime.newRegExp(matcherValue, Value{})
|
||||||
|
}
|
||||||
|
global := matcher.get("global").bool()
|
||||||
|
if !global {
|
||||||
|
match, result := execRegExp(matcher, target)
|
||||||
|
if !match {
|
||||||
|
return nullValue
|
||||||
|
}
|
||||||
|
return toValue_object(execResultToArray(call.runtime, target, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
result := matcher.regExpValue().regularExpression.FindAllStringIndex(target, -1)
|
||||||
|
matchCount := len(result)
|
||||||
|
if result == nil {
|
||||||
|
matcher.put("lastIndex", toValue_int(0), true)
|
||||||
|
return Value{} // !match
|
||||||
|
}
|
||||||
|
matchCount = len(result)
|
||||||
|
valueArray := make([]Value, matchCount)
|
||||||
|
for index := 0; index < matchCount; index++ {
|
||||||
|
valueArray[index] = toValue_string(target[result[index][0]:result[index][1]])
|
||||||
|
}
|
||||||
|
matcher.put("lastIndex", toValue_int(result[matchCount-1][1]), true)
|
||||||
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var builtinString_replace_Regexp = regexp.MustCompile("\\$(?:[\\$\\&\\'\\`1-9]|0[1-9]|[1-9][0-9])")
|
||||||
|
|
||||||
|
func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int, target []byte, replaceValue []byte) (output []byte) {
|
||||||
|
matchCount := len(match) / 2
|
||||||
|
output = input
|
||||||
|
if match[0] != lastIndex {
|
||||||
|
output = append(output, target[lastIndex:match[0]]...)
|
||||||
|
}
|
||||||
|
replacement := builtinString_replace_Regexp.ReplaceAllFunc(replaceValue, func(part []byte) []byte {
|
||||||
|
// TODO Check if match[0] or match[1] can be -1 in this scenario
|
||||||
|
switch part[1] {
|
||||||
|
case '$':
|
||||||
|
return []byte{'$'}
|
||||||
|
case '&':
|
||||||
|
return target[match[0]:match[1]]
|
||||||
|
case '`':
|
||||||
|
return target[:match[0]]
|
||||||
|
case '\'':
|
||||||
|
return target[match[1]:len(target)]
|
||||||
|
}
|
||||||
|
matchNumberParse, error := strconv.ParseInt(string(part[1:]), 10, 64)
|
||||||
|
matchNumber := int(matchNumberParse)
|
||||||
|
if error != nil || matchNumber >= matchCount {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
offset := 2 * matchNumber
|
||||||
|
if match[offset] != -1 {
|
||||||
|
return target[match[offset]:match[offset+1]]
|
||||||
|
}
|
||||||
|
return []byte{} // The empty string
|
||||||
|
})
|
||||||
|
output = append(output, replacement...)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_replace(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
target := []byte(call.This.string())
|
||||||
|
searchValue := call.Argument(0)
|
||||||
|
searchObject := searchValue._object()
|
||||||
|
|
||||||
|
// TODO If a capture is -1?
|
||||||
|
var search *regexp.Regexp
|
||||||
|
global := false
|
||||||
|
find := 1
|
||||||
|
if searchValue.IsObject() && searchObject.class == "RegExp" {
|
||||||
|
regExp := searchObject.regExpValue()
|
||||||
|
search = regExp.regularExpression
|
||||||
|
if regExp.global {
|
||||||
|
find = -1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
found := search.FindAllSubmatchIndex(target, find)
|
||||||
|
if found == nil {
|
||||||
|
return toValue_string(string(target)) // !match
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
lastIndex := 0
|
||||||
|
result := []byte{}
|
||||||
|
|
||||||
|
replaceValue := call.Argument(1)
|
||||||
|
if replaceValue.isCallable() {
|
||||||
|
target := string(target)
|
||||||
|
replace := replaceValue._object()
|
||||||
|
for _, match := range found {
|
||||||
|
if match[0] != lastIndex {
|
||||||
|
result = append(result, target[lastIndex:match[0]]...)
|
||||||
|
}
|
||||||
|
matchCount := len(match) / 2
|
||||||
|
argumentList := make([]Value, matchCount+2)
|
||||||
|
for index := 0; index < matchCount; index++ {
|
||||||
|
offset := 2 * index
|
||||||
|
if match[offset] != -1 {
|
||||||
|
argumentList[index] = toValue_string(target[match[offset]:match[offset+1]])
|
||||||
|
} else {
|
||||||
|
argumentList[index] = Value{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argumentList[matchCount+0] = toValue_int(match[0])
|
||||||
|
argumentList[matchCount+1] = toValue_string(target)
|
||||||
|
replacement := replace.call(Value{}, argumentList, false, nativeFrame).string()
|
||||||
|
result = append(result, []byte(replacement)...)
|
||||||
|
lastIndex = match[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
replace := []byte(replaceValue.string())
|
||||||
|
for _, match := range found {
|
||||||
|
result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace)
|
||||||
|
lastIndex = match[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastIndex != len(target) {
|
||||||
|
result = append(result, target[lastIndex:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if global && searchObject != nil {
|
||||||
|
searchObject.put("lastIndex", toValue_int(lastIndex), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_string(string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_search(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
target := call.This.string()
|
||||||
|
searchValue := call.Argument(0)
|
||||||
|
search := searchValue._object()
|
||||||
|
if !searchValue.IsObject() || search.class != "RegExp" {
|
||||||
|
search = call.runtime.newRegExp(searchValue, Value{})
|
||||||
|
}
|
||||||
|
result := search.regExpValue().regularExpression.FindStringIndex(target)
|
||||||
|
if result == nil {
|
||||||
|
return toValue_int(-1)
|
||||||
|
}
|
||||||
|
return toValue_int(result[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringSplitMatch(target string, targetLength int64, index uint, search string, searchLength int64) (bool, uint) {
|
||||||
|
if int64(index)+searchLength > searchLength {
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
found := strings.Index(target[index:], search)
|
||||||
|
if 0 > found {
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
return true, uint(found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_split(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
target := call.This.string()
|
||||||
|
|
||||||
|
separatorValue := call.Argument(0)
|
||||||
|
limitValue := call.Argument(1)
|
||||||
|
limit := -1
|
||||||
|
if limitValue.IsDefined() {
|
||||||
|
limit = int(toUint32(limitValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit == 0 {
|
||||||
|
return toValue_object(call.runtime.newArray(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
if separatorValue.IsUndefined() {
|
||||||
|
return toValue_object(call.runtime.newArrayOf([]Value{toValue_string(target)}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if separatorValue.isRegExp() {
|
||||||
|
targetLength := len(target)
|
||||||
|
search := separatorValue._object().regExpValue().regularExpression
|
||||||
|
valueArray := []Value{}
|
||||||
|
result := search.FindAllStringSubmatchIndex(target, -1)
|
||||||
|
lastIndex := 0
|
||||||
|
found := 0
|
||||||
|
|
||||||
|
for _, match := range result {
|
||||||
|
if match[0] == match[1] {
|
||||||
|
// FIXME Ugh, this is a hack
|
||||||
|
if match[0] == 0 || match[0] == targetLength {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastIndex != match[0] {
|
||||||
|
valueArray = append(valueArray, toValue_string(target[lastIndex:match[0]]))
|
||||||
|
found++
|
||||||
|
} else if lastIndex == match[0] {
|
||||||
|
if lastIndex != -1 {
|
||||||
|
valueArray = append(valueArray, toValue_string(""))
|
||||||
|
found++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex = match[1]
|
||||||
|
if found == limit {
|
||||||
|
goto RETURN
|
||||||
|
}
|
||||||
|
|
||||||
|
captureCount := len(match) / 2
|
||||||
|
for index := 1; index < captureCount; index++ {
|
||||||
|
offset := index * 2
|
||||||
|
value := Value{}
|
||||||
|
if match[offset] != -1 {
|
||||||
|
value = toValue_string(target[match[offset]:match[offset+1]])
|
||||||
|
}
|
||||||
|
valueArray = append(valueArray, value)
|
||||||
|
found++
|
||||||
|
if found == limit {
|
||||||
|
goto RETURN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found != limit {
|
||||||
|
if lastIndex != targetLength {
|
||||||
|
valueArray = append(valueArray, toValue_string(target[lastIndex:targetLength]))
|
||||||
|
} else {
|
||||||
|
valueArray = append(valueArray, toValue_string(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN:
|
||||||
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
separator := separatorValue.string()
|
||||||
|
|
||||||
|
splitLimit := limit
|
||||||
|
excess := false
|
||||||
|
if limit > 0 {
|
||||||
|
splitLimit = limit + 1
|
||||||
|
excess = true
|
||||||
|
}
|
||||||
|
|
||||||
|
split := strings.SplitN(target, separator, splitLimit)
|
||||||
|
|
||||||
|
if excess && len(split) > limit {
|
||||||
|
split = split[:limit]
|
||||||
|
}
|
||||||
|
|
||||||
|
valueArray := make([]Value, len(split))
|
||||||
|
for index, value := range split {
|
||||||
|
valueArray[index] = toValue_string(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_object(call.runtime.newArrayOf(valueArray))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_slice(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
target := call.This.string()
|
||||||
|
|
||||||
|
length := int64(len(target))
|
||||||
|
start, end := rangeStartEnd(call.ArgumentList, length, false)
|
||||||
|
if end-start <= 0 {
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
return toValue_string(target[start:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_substring(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
target := call.This.string()
|
||||||
|
|
||||||
|
length := int64(len(target))
|
||||||
|
start, end := rangeStartEnd(call.ArgumentList, length, true)
|
||||||
|
if start > end {
|
||||||
|
start, end = end, start
|
||||||
|
}
|
||||||
|
return toValue_string(target[start:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_substr(call FunctionCall) Value {
|
||||||
|
target := call.This.string()
|
||||||
|
|
||||||
|
size := int64(len(target))
|
||||||
|
start, length := rangeStartLength(call.ArgumentList, size)
|
||||||
|
|
||||||
|
if start >= size {
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length <= 0 {
|
||||||
|
return toValue_string("")
|
||||||
|
}
|
||||||
|
|
||||||
|
if start+length >= size {
|
||||||
|
// Cap length to be to the end of the string
|
||||||
|
// start = 3, length = 5, size = 4 [0, 1, 2, 3]
|
||||||
|
// 4 - 3 = 1
|
||||||
|
// target[3:4]
|
||||||
|
length = size - start
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_string(target[start : start+length])
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_toLowerCase(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
return toValue_string(strings.ToLower(call.This.string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_toUpperCase(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
return toValue_string(strings.ToUpper(call.This.string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters
|
||||||
|
const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
||||||
|
|
||||||
|
func builtinString_trim(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
return toValue(strings.Trim(call.This.string(),
|
||||||
|
builtinString_trim_whitespace))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mozilla extension, not ECMAScript 5
|
||||||
|
func builtinString_trimLeft(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
return toValue(strings.TrimLeft(call.This.string(),
|
||||||
|
builtinString_trim_whitespace))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mozilla extension, not ECMAScript 5
|
||||||
|
func builtinString_trimRight(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
return toValue(strings.TrimRight(call.This.string(),
|
||||||
|
builtinString_trim_whitespace))
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_localeCompare(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.runtime, call.This)
|
||||||
|
this := call.This.string()
|
||||||
|
that := call.Argument(0).string()
|
||||||
|
if this < that {
|
||||||
|
return toValue_int(-1)
|
||||||
|
} else if this == that {
|
||||||
|
return toValue_int(0)
|
||||||
|
}
|
||||||
|
return toValue_int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
An alternate version of String.trim
|
||||||
|
func builtinString_trim(call FunctionCall) Value {
|
||||||
|
checkObjectCoercible(call.This)
|
||||||
|
return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func builtinString_toLocaleLowerCase(call FunctionCall) Value {
|
||||||
|
return builtinString_toLowerCase(call)
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinString_toLocaleUpperCase(call FunctionCall) Value {
|
||||||
|
return builtinString_toUpperCase(call)
|
||||||
|
}
|
173
vendor/github.com/robertkrimen/otto/clone.go
generated
vendored
Normal file
173
vendor/github.com/robertkrimen/otto/clone.go
generated
vendored
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _clone struct {
|
||||||
|
runtime *_runtime
|
||||||
|
_object map[*_object]*_object
|
||||||
|
_objectStash map[*_objectStash]*_objectStash
|
||||||
|
_dclStash map[*_dclStash]*_dclStash
|
||||||
|
_fnStash map[*_fnStash]*_fnStash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_runtime) clone() *_runtime {
|
||||||
|
|
||||||
|
in.lck.Lock()
|
||||||
|
defer in.lck.Unlock()
|
||||||
|
|
||||||
|
out := &_runtime{
|
||||||
|
debugger: in.debugger,
|
||||||
|
random: in.random,
|
||||||
|
stackLimit: in.stackLimit,
|
||||||
|
traceLimit: in.traceLimit,
|
||||||
|
}
|
||||||
|
|
||||||
|
clone := _clone{
|
||||||
|
runtime: out,
|
||||||
|
_object: make(map[*_object]*_object),
|
||||||
|
_objectStash: make(map[*_objectStash]*_objectStash),
|
||||||
|
_dclStash: make(map[*_dclStash]*_dclStash),
|
||||||
|
_fnStash: make(map[*_fnStash]*_fnStash),
|
||||||
|
}
|
||||||
|
|
||||||
|
globalObject := clone.object(in.globalObject)
|
||||||
|
out.globalStash = out.newObjectStash(globalObject, nil)
|
||||||
|
out.globalObject = globalObject
|
||||||
|
out.global = _global{
|
||||||
|
clone.object(in.global.Object),
|
||||||
|
clone.object(in.global.Function),
|
||||||
|
clone.object(in.global.Array),
|
||||||
|
clone.object(in.global.String),
|
||||||
|
clone.object(in.global.Boolean),
|
||||||
|
clone.object(in.global.Number),
|
||||||
|
clone.object(in.global.Math),
|
||||||
|
clone.object(in.global.Date),
|
||||||
|
clone.object(in.global.RegExp),
|
||||||
|
clone.object(in.global.Error),
|
||||||
|
clone.object(in.global.EvalError),
|
||||||
|
clone.object(in.global.TypeError),
|
||||||
|
clone.object(in.global.RangeError),
|
||||||
|
clone.object(in.global.ReferenceError),
|
||||||
|
clone.object(in.global.SyntaxError),
|
||||||
|
clone.object(in.global.URIError),
|
||||||
|
clone.object(in.global.JSON),
|
||||||
|
|
||||||
|
clone.object(in.global.ObjectPrototype),
|
||||||
|
clone.object(in.global.FunctionPrototype),
|
||||||
|
clone.object(in.global.ArrayPrototype),
|
||||||
|
clone.object(in.global.StringPrototype),
|
||||||
|
clone.object(in.global.BooleanPrototype),
|
||||||
|
clone.object(in.global.NumberPrototype),
|
||||||
|
clone.object(in.global.DatePrototype),
|
||||||
|
clone.object(in.global.RegExpPrototype),
|
||||||
|
clone.object(in.global.ErrorPrototype),
|
||||||
|
clone.object(in.global.EvalErrorPrototype),
|
||||||
|
clone.object(in.global.TypeErrorPrototype),
|
||||||
|
clone.object(in.global.RangeErrorPrototype),
|
||||||
|
clone.object(in.global.ReferenceErrorPrototype),
|
||||||
|
clone.object(in.global.SyntaxErrorPrototype),
|
||||||
|
clone.object(in.global.URIErrorPrototype),
|
||||||
|
}
|
||||||
|
|
||||||
|
out.eval = out.globalObject.property["eval"].value.(Value).value.(*_object)
|
||||||
|
out.globalObject.prototype = out.global.ObjectPrototype
|
||||||
|
|
||||||
|
// Not sure if this is necessary, but give some help to the GC
|
||||||
|
clone.runtime = nil
|
||||||
|
clone._object = nil
|
||||||
|
clone._objectStash = nil
|
||||||
|
clone._dclStash = nil
|
||||||
|
clone._fnStash = nil
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) object(in *_object) *_object {
|
||||||
|
if out, exists := clone._object[in]; exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
out := &_object{}
|
||||||
|
clone._object[in] = out
|
||||||
|
return in.objectClass.clone(in, out, clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) dclStash(in *_dclStash) (*_dclStash, bool) {
|
||||||
|
if out, exists := clone._dclStash[in]; exists {
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
out := &_dclStash{}
|
||||||
|
clone._dclStash[in] = out
|
||||||
|
return out, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) objectStash(in *_objectStash) (*_objectStash, bool) {
|
||||||
|
if out, exists := clone._objectStash[in]; exists {
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
out := &_objectStash{}
|
||||||
|
clone._objectStash[in] = out
|
||||||
|
return out, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) fnStash(in *_fnStash) (*_fnStash, bool) {
|
||||||
|
if out, exists := clone._fnStash[in]; exists {
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
out := &_fnStash{}
|
||||||
|
clone._fnStash[in] = out
|
||||||
|
return out, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) value(in Value) Value {
|
||||||
|
out := in
|
||||||
|
switch value := in.value.(type) {
|
||||||
|
case *_object:
|
||||||
|
out.value = clone.object(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) valueArray(in []Value) []Value {
|
||||||
|
out := make([]Value, len(in))
|
||||||
|
for index, value := range in {
|
||||||
|
out[index] = clone.value(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) stash(in _stash) _stash {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return in.clone(clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) property(in _property) _property {
|
||||||
|
out := in
|
||||||
|
|
||||||
|
switch value := in.value.(type) {
|
||||||
|
case Value:
|
||||||
|
out.value = clone.value(value)
|
||||||
|
case _propertyGetSet:
|
||||||
|
p := _propertyGetSet{}
|
||||||
|
if value[0] != nil {
|
||||||
|
p[0] = clone.object(value[0])
|
||||||
|
}
|
||||||
|
if value[1] != nil {
|
||||||
|
p[1] = clone.object(value[1])
|
||||||
|
}
|
||||||
|
out.value = p
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("in.value.(Value) != true; in.value is %T", in.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clone *_clone) dclProperty(in _dclProperty) _dclProperty {
|
||||||
|
out := in
|
||||||
|
out.value = clone.value(in.value)
|
||||||
|
return out
|
||||||
|
}
|
24
vendor/github.com/robertkrimen/otto/cmpl.go
generated
vendored
Normal file
24
vendor/github.com/robertkrimen/otto/cmpl.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _file struct {
|
||||||
|
name string
|
||||||
|
src string
|
||||||
|
base int // This will always be 1 or greater
|
||||||
|
}
|
||||||
|
|
||||||
|
type _compiler struct {
|
||||||
|
file *file.File
|
||||||
|
program *ast.Program
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmpl *_compiler) parse() *_nodeProgram {
|
||||||
|
if cmpl.program != nil {
|
||||||
|
cmpl.file = cmpl.program.File
|
||||||
|
}
|
||||||
|
return cmpl._parse(cmpl.program)
|
||||||
|
}
|
96
vendor/github.com/robertkrimen/otto/cmpl_evaluate.go
generated
vendored
Normal file
96
vendor/github.com/robertkrimen/otto/cmpl_evaluate.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram, eval bool) Value {
|
||||||
|
if !eval {
|
||||||
|
self.enterGlobalScope()
|
||||||
|
defer func() {
|
||||||
|
self.leaveScope()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
self.cmpl_functionDeclaration(node.functionList)
|
||||||
|
self.cmpl_variableDeclaration(node.varList)
|
||||||
|
self.scope.frame.file = node.file
|
||||||
|
return self.cmpl_evaluate_nodeStatementList(node.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_call_nodeFunction(function *_object, stash *_fnStash, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value {
|
||||||
|
|
||||||
|
indexOfParameterName := make([]string, len(argumentList))
|
||||||
|
// function(abc, def, ghi)
|
||||||
|
// indexOfParameterName[0] = "abc"
|
||||||
|
// indexOfParameterName[1] = "def"
|
||||||
|
// indexOfParameterName[2] = "ghi"
|
||||||
|
// ...
|
||||||
|
|
||||||
|
argumentsFound := false
|
||||||
|
for index, name := range node.parameterList {
|
||||||
|
if name == "arguments" {
|
||||||
|
argumentsFound = true
|
||||||
|
}
|
||||||
|
value := Value{}
|
||||||
|
if index < len(argumentList) {
|
||||||
|
value = argumentList[index]
|
||||||
|
indexOfParameterName[index] = name
|
||||||
|
}
|
||||||
|
// strict = false
|
||||||
|
self.scope.lexical.setValue(name, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !argumentsFound {
|
||||||
|
arguments := self.newArgumentsObject(indexOfParameterName, stash, len(argumentList))
|
||||||
|
arguments.defineProperty("callee", toValue_object(function), 0101, false)
|
||||||
|
stash.arguments = arguments
|
||||||
|
// strict = false
|
||||||
|
self.scope.lexical.setValue("arguments", toValue_object(arguments), false)
|
||||||
|
for index, _ := range argumentList {
|
||||||
|
if index < len(node.parameterList) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
indexAsString := strconv.FormatInt(int64(index), 10)
|
||||||
|
arguments.defineProperty(indexAsString, argumentList[index], 0111, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cmpl_functionDeclaration(node.functionList)
|
||||||
|
self.cmpl_variableDeclaration(node.varList)
|
||||||
|
|
||||||
|
result := self.cmpl_evaluate_nodeStatement(node.body)
|
||||||
|
if result.kind == valueResult {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) {
|
||||||
|
executionContext := self.scope
|
||||||
|
eval := executionContext.eval
|
||||||
|
stash := executionContext.variable
|
||||||
|
|
||||||
|
for _, function := range list {
|
||||||
|
name := function.name
|
||||||
|
value := self.cmpl_evaluate_nodeExpression(function)
|
||||||
|
if !stash.hasBinding(name) {
|
||||||
|
stash.createBinding(name, eval == true, value)
|
||||||
|
} else {
|
||||||
|
// TODO 10.5.5.e
|
||||||
|
stash.setBinding(name, value, false) // TODO strict
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_variableDeclaration(list []string) {
|
||||||
|
executionContext := self.scope
|
||||||
|
eval := executionContext.eval
|
||||||
|
stash := executionContext.variable
|
||||||
|
|
||||||
|
for _, name := range list {
|
||||||
|
if !stash.hasBinding(name) {
|
||||||
|
stash.createBinding(name, eval == true, Value{}) // TODO strict?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
460
vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go
generated
vendored
Normal file
460
vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go
generated
vendored
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value {
|
||||||
|
// Allow interpreter interruption
|
||||||
|
// If the Interrupt channel is nil, then
|
||||||
|
// we avoid runtime.Gosched() overhead (if any)
|
||||||
|
// FIXME: Test this
|
||||||
|
if self.otto.Interrupt != nil {
|
||||||
|
runtime.Gosched()
|
||||||
|
select {
|
||||||
|
case value := <-self.otto.Interrupt:
|
||||||
|
value()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node := node.(type) {
|
||||||
|
|
||||||
|
case *_nodeArrayLiteral:
|
||||||
|
return self.cmpl_evaluate_nodeArrayLiteral(node)
|
||||||
|
|
||||||
|
case *_nodeAssignExpression:
|
||||||
|
return self.cmpl_evaluate_nodeAssignExpression(node)
|
||||||
|
|
||||||
|
case *_nodeBinaryExpression:
|
||||||
|
if node.comparison {
|
||||||
|
return self.cmpl_evaluate_nodeBinaryExpression_comparison(node)
|
||||||
|
} else {
|
||||||
|
return self.cmpl_evaluate_nodeBinaryExpression(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *_nodeBracketExpression:
|
||||||
|
return self.cmpl_evaluate_nodeBracketExpression(node)
|
||||||
|
|
||||||
|
case *_nodeCallExpression:
|
||||||
|
return self.cmpl_evaluate_nodeCallExpression(node, nil)
|
||||||
|
|
||||||
|
case *_nodeConditionalExpression:
|
||||||
|
return self.cmpl_evaluate_nodeConditionalExpression(node)
|
||||||
|
|
||||||
|
case *_nodeDotExpression:
|
||||||
|
return self.cmpl_evaluate_nodeDotExpression(node)
|
||||||
|
|
||||||
|
case *_nodeFunctionLiteral:
|
||||||
|
var local = self.scope.lexical
|
||||||
|
if node.name != "" {
|
||||||
|
local = self.newDeclarationStash(local)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := toValue_object(self.newNodeFunction(node, local))
|
||||||
|
if node.name != "" {
|
||||||
|
local.createBinding(node.name, false, value)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
|
case *_nodeIdentifier:
|
||||||
|
name := node.name
|
||||||
|
// TODO Should be true or false (strictness) depending on context
|
||||||
|
// getIdentifierReference should not return nil, but we check anyway and panic
|
||||||
|
// so as not to propagate the nil into something else
|
||||||
|
reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx))
|
||||||
|
if reference == nil {
|
||||||
|
// Should never get here!
|
||||||
|
panic(hereBeDragons("referenceError == nil: " + name))
|
||||||
|
}
|
||||||
|
return toValue(reference)
|
||||||
|
|
||||||
|
case *_nodeLiteral:
|
||||||
|
return node.value
|
||||||
|
|
||||||
|
case *_nodeNewExpression:
|
||||||
|
return self.cmpl_evaluate_nodeNewExpression(node)
|
||||||
|
|
||||||
|
case *_nodeObjectLiteral:
|
||||||
|
return self.cmpl_evaluate_nodeObjectLiteral(node)
|
||||||
|
|
||||||
|
case *_nodeRegExpLiteral:
|
||||||
|
return toValue_object(self._newRegExp(node.pattern, node.flags))
|
||||||
|
|
||||||
|
case *_nodeSequenceExpression:
|
||||||
|
return self.cmpl_evaluate_nodeSequenceExpression(node)
|
||||||
|
|
||||||
|
case *_nodeThisExpression:
|
||||||
|
return toValue_object(self.scope.this)
|
||||||
|
|
||||||
|
case *_nodeUnaryExpression:
|
||||||
|
return self.cmpl_evaluate_nodeUnaryExpression(node)
|
||||||
|
|
||||||
|
case *_nodeVariableExpression:
|
||||||
|
return self.cmpl_evaluate_nodeVariableExpression(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Errorf("Here be dragons: evaluate_nodeExpression(%T)", node))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Value {
|
||||||
|
|
||||||
|
valueArray := []Value{}
|
||||||
|
|
||||||
|
for _, node := range node.value {
|
||||||
|
if node == nil {
|
||||||
|
valueArray = append(valueArray, emptyValue)
|
||||||
|
} else {
|
||||||
|
valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := self.newArrayOf(valueArray)
|
||||||
|
|
||||||
|
return toValue_object(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpression) Value {
|
||||||
|
|
||||||
|
left := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
|
right := self.cmpl_evaluate_nodeExpression(node.right)
|
||||||
|
rightValue := right.resolve()
|
||||||
|
|
||||||
|
result := rightValue
|
||||||
|
if node.operator != token.ASSIGN {
|
||||||
|
result = self.calculateBinaryExpression(node.operator, left, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.putValue(left.reference(), result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value {
|
||||||
|
|
||||||
|
left := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
|
leftValue := left.resolve()
|
||||||
|
|
||||||
|
switch node.operator {
|
||||||
|
// Logical
|
||||||
|
case token.LOGICAL_AND:
|
||||||
|
if !leftValue.bool() {
|
||||||
|
return leftValue
|
||||||
|
}
|
||||||
|
right := self.cmpl_evaluate_nodeExpression(node.right)
|
||||||
|
return right.resolve()
|
||||||
|
case token.LOGICAL_OR:
|
||||||
|
if leftValue.bool() {
|
||||||
|
return leftValue
|
||||||
|
}
|
||||||
|
right := self.cmpl_evaluate_nodeExpression(node.right)
|
||||||
|
return right.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value {
|
||||||
|
|
||||||
|
left := self.cmpl_evaluate_nodeExpression(node.left).resolve()
|
||||||
|
right := self.cmpl_evaluate_nodeExpression(node.right).resolve()
|
||||||
|
|
||||||
|
return toValue_bool(self.calculateComparison(node.operator, left, right))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value {
|
||||||
|
target := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
|
targetValue := target.resolve()
|
||||||
|
member := self.cmpl_evaluate_nodeExpression(node.member)
|
||||||
|
memberValue := member.resolve()
|
||||||
|
|
||||||
|
// TODO Pass in base value as-is, and defer toObject till later?
|
||||||
|
object, err := self.objectCoerce(targetValue)
|
||||||
|
if err != nil {
|
||||||
|
panic(self.panicTypeError("Cannot access member '%s' of %s", memberValue.string(), err.Error(), _at(node.idx)))
|
||||||
|
}
|
||||||
|
return toValue(newPropertyReference(self, object, memberValue.string(), false, _at(node.idx)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value {
|
||||||
|
rt := self
|
||||||
|
this := Value{}
|
||||||
|
callee := self.cmpl_evaluate_nodeExpression(node.callee)
|
||||||
|
|
||||||
|
argumentList := []Value{}
|
||||||
|
if withArgumentList != nil {
|
||||||
|
argumentList = self.toValueArray(withArgumentList...)
|
||||||
|
} else {
|
||||||
|
for _, argumentNode := range node.argumentList {
|
||||||
|
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rf := callee.reference()
|
||||||
|
vl := callee.resolve()
|
||||||
|
|
||||||
|
eval := false // Whether this call is a (candidate for) direct call to eval
|
||||||
|
name := ""
|
||||||
|
if rf != nil {
|
||||||
|
switch rf := rf.(type) {
|
||||||
|
case *_propertyReference:
|
||||||
|
name = rf.name
|
||||||
|
object := rf.base
|
||||||
|
this = toValue_object(object)
|
||||||
|
eval = rf.name == "eval" // Possible direct eval
|
||||||
|
case *_stashReference:
|
||||||
|
// TODO ImplicitThisValue
|
||||||
|
name = rf.name
|
||||||
|
eval = rf.name == "eval" // Possible direct eval
|
||||||
|
default:
|
||||||
|
// FIXME?
|
||||||
|
panic(rt.panicTypeError("Here be dragons"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
at := _at(-1)
|
||||||
|
switch callee := node.callee.(type) {
|
||||||
|
case *_nodeIdentifier:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeDotExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeBracketExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
frame := _frame{
|
||||||
|
callee: name,
|
||||||
|
file: self.scope.frame.file,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !vl.IsFunction() {
|
||||||
|
if name == "" {
|
||||||
|
// FIXME Maybe typeof?
|
||||||
|
panic(rt.panicTypeError("%v is not a function", vl, at))
|
||||||
|
}
|
||||||
|
panic(rt.panicTypeError("'%s' is not a function", name, at))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scope.frame.offset = int(at)
|
||||||
|
|
||||||
|
return vl._object().call(this, argumentList, eval, frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value {
|
||||||
|
test := self.cmpl_evaluate_nodeExpression(node.test)
|
||||||
|
testValue := test.resolve()
|
||||||
|
if testValue.bool() {
|
||||||
|
return self.cmpl_evaluate_nodeExpression(node.consequent)
|
||||||
|
}
|
||||||
|
return self.cmpl_evaluate_nodeExpression(node.alternate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value {
|
||||||
|
target := self.cmpl_evaluate_nodeExpression(node.left)
|
||||||
|
targetValue := target.resolve()
|
||||||
|
// TODO Pass in base value as-is, and defer toObject till later?
|
||||||
|
object, err := self.objectCoerce(targetValue)
|
||||||
|
if err != nil {
|
||||||
|
panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error(), _at(node.idx)))
|
||||||
|
}
|
||||||
|
return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value {
|
||||||
|
rt := self
|
||||||
|
callee := self.cmpl_evaluate_nodeExpression(node.callee)
|
||||||
|
|
||||||
|
argumentList := []Value{}
|
||||||
|
for _, argumentNode := range node.argumentList {
|
||||||
|
argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve())
|
||||||
|
}
|
||||||
|
|
||||||
|
rf := callee.reference()
|
||||||
|
vl := callee.resolve()
|
||||||
|
|
||||||
|
name := ""
|
||||||
|
if rf != nil {
|
||||||
|
switch rf := rf.(type) {
|
||||||
|
case *_propertyReference:
|
||||||
|
name = rf.name
|
||||||
|
case *_stashReference:
|
||||||
|
name = rf.name
|
||||||
|
default:
|
||||||
|
panic(rt.panicTypeError("Here be dragons"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
at := _at(-1)
|
||||||
|
switch callee := node.callee.(type) {
|
||||||
|
case *_nodeIdentifier:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeDotExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
case *_nodeBracketExpression:
|
||||||
|
at = _at(callee.idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !vl.IsFunction() {
|
||||||
|
if name == "" {
|
||||||
|
// FIXME Maybe typeof?
|
||||||
|
panic(rt.panicTypeError("%v is not a function", vl, at))
|
||||||
|
}
|
||||||
|
panic(rt.panicTypeError("'%s' is not a function", name, at))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scope.frame.offset = int(at)
|
||||||
|
|
||||||
|
return vl._object().construct(argumentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value {
|
||||||
|
|
||||||
|
result := self.newObject()
|
||||||
|
|
||||||
|
for _, property := range node.value {
|
||||||
|
switch property.kind {
|
||||||
|
case "value":
|
||||||
|
result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false)
|
||||||
|
case "get":
|
||||||
|
getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
|
||||||
|
descriptor := _property{}
|
||||||
|
descriptor.mode = 0211
|
||||||
|
descriptor.value = _propertyGetSet{getter, nil}
|
||||||
|
result.defineOwnProperty(property.key, descriptor, false)
|
||||||
|
case "set":
|
||||||
|
setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical)
|
||||||
|
descriptor := _property{}
|
||||||
|
descriptor.mode = 0211
|
||||||
|
descriptor.value = _propertyGetSet{nil, setter}
|
||||||
|
result.defineOwnProperty(property.key, descriptor, false)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("Here be dragons: evaluate_nodeObjectLiteral: invalid property.Kind: %v", property.kind))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue_object(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceExpression) Value {
|
||||||
|
var result Value
|
||||||
|
for _, node := range node.sequence {
|
||||||
|
result = self.cmpl_evaluate_nodeExpression(node)
|
||||||
|
result = result.resolve()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpression) Value {
|
||||||
|
|
||||||
|
target := self.cmpl_evaluate_nodeExpression(node.operand)
|
||||||
|
switch node.operator {
|
||||||
|
case token.TYPEOF, token.DELETE:
|
||||||
|
if target.kind == valueReference && target.reference().invalid() {
|
||||||
|
if node.operator == token.TYPEOF {
|
||||||
|
return toValue_string("undefined")
|
||||||
|
}
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node.operator {
|
||||||
|
case token.NOT:
|
||||||
|
targetValue := target.resolve()
|
||||||
|
if targetValue.bool() {
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
return trueValue
|
||||||
|
case token.BITWISE_NOT:
|
||||||
|
targetValue := target.resolve()
|
||||||
|
integerValue := toInt32(targetValue)
|
||||||
|
return toValue_int32(^integerValue)
|
||||||
|
case token.PLUS:
|
||||||
|
targetValue := target.resolve()
|
||||||
|
return toValue_float64(targetValue.float64())
|
||||||
|
case token.MINUS:
|
||||||
|
targetValue := target.resolve()
|
||||||
|
value := targetValue.float64()
|
||||||
|
// TODO Test this
|
||||||
|
sign := float64(-1)
|
||||||
|
if math.Signbit(value) {
|
||||||
|
sign = 1
|
||||||
|
}
|
||||||
|
return toValue_float64(math.Copysign(value, sign))
|
||||||
|
case token.INCREMENT:
|
||||||
|
targetValue := target.resolve()
|
||||||
|
if node.postfix {
|
||||||
|
// Postfix++
|
||||||
|
oldValue := targetValue.float64()
|
||||||
|
newValue := toValue_float64(+1 + oldValue)
|
||||||
|
self.putValue(target.reference(), newValue)
|
||||||
|
return toValue_float64(oldValue)
|
||||||
|
} else {
|
||||||
|
// ++Prefix
|
||||||
|
newValue := toValue_float64(+1 + targetValue.float64())
|
||||||
|
self.putValue(target.reference(), newValue)
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
|
case token.DECREMENT:
|
||||||
|
targetValue := target.resolve()
|
||||||
|
if node.postfix {
|
||||||
|
// Postfix--
|
||||||
|
oldValue := targetValue.float64()
|
||||||
|
newValue := toValue_float64(-1 + oldValue)
|
||||||
|
self.putValue(target.reference(), newValue)
|
||||||
|
return toValue_float64(oldValue)
|
||||||
|
} else {
|
||||||
|
// --Prefix
|
||||||
|
newValue := toValue_float64(-1 + targetValue.float64())
|
||||||
|
self.putValue(target.reference(), newValue)
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
|
case token.VOID:
|
||||||
|
target.resolve() // FIXME Side effect?
|
||||||
|
return Value{}
|
||||||
|
case token.DELETE:
|
||||||
|
reference := target.reference()
|
||||||
|
if reference == nil {
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
return toValue_bool(target.reference().delete())
|
||||||
|
case token.TYPEOF:
|
||||||
|
targetValue := target.resolve()
|
||||||
|
switch targetValue.kind {
|
||||||
|
case valueUndefined:
|
||||||
|
return toValue_string("undefined")
|
||||||
|
case valueNull:
|
||||||
|
return toValue_string("object")
|
||||||
|
case valueBoolean:
|
||||||
|
return toValue_string("boolean")
|
||||||
|
case valueNumber:
|
||||||
|
return toValue_string("number")
|
||||||
|
case valueString:
|
||||||
|
return toValue_string("string")
|
||||||
|
case valueObject:
|
||||||
|
if targetValue._object().isCall() {
|
||||||
|
return toValue_string("function")
|
||||||
|
}
|
||||||
|
return toValue_string("object")
|
||||||
|
default:
|
||||||
|
// FIXME ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(hereBeDragons())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value {
|
||||||
|
if node.initializer != nil {
|
||||||
|
// FIXME If reference is nil
|
||||||
|
left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx))
|
||||||
|
right := self.cmpl_evaluate_nodeExpression(node.initializer)
|
||||||
|
rightValue := right.resolve()
|
||||||
|
|
||||||
|
self.putValue(left, rightValue)
|
||||||
|
}
|
||||||
|
return toValue_string(node.name)
|
||||||
|
}
|
424
vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go
generated
vendored
Normal file
424
vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go
generated
vendored
Normal file
|
@ -0,0 +1,424 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value {
|
||||||
|
// Allow interpreter interruption
|
||||||
|
// If the Interrupt channel is nil, then
|
||||||
|
// we avoid runtime.Gosched() overhead (if any)
|
||||||
|
// FIXME: Test this
|
||||||
|
if self.otto.Interrupt != nil {
|
||||||
|
runtime.Gosched()
|
||||||
|
select {
|
||||||
|
case value := <-self.otto.Interrupt:
|
||||||
|
value()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node := node.(type) {
|
||||||
|
|
||||||
|
case *_nodeBlockStatement:
|
||||||
|
labels := self.labels
|
||||||
|
self.labels = nil
|
||||||
|
|
||||||
|
value := self.cmpl_evaluate_nodeStatementList(node.list)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
switch value.evaluateBreak(labels) {
|
||||||
|
case resultBreak:
|
||||||
|
return emptyValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
|
case *_nodeBranchStatement:
|
||||||
|
target := node.label
|
||||||
|
switch node.branch { // FIXME Maybe node.kind? node.operator?
|
||||||
|
case token.BREAK:
|
||||||
|
return toValue(newBreakResult(target))
|
||||||
|
case token.CONTINUE:
|
||||||
|
return toValue(newContinueResult(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
case *_nodeDebuggerStatement:
|
||||||
|
if self.debugger != nil {
|
||||||
|
self.debugger(self.otto)
|
||||||
|
}
|
||||||
|
return emptyValue // Nothing happens.
|
||||||
|
|
||||||
|
case *_nodeDoWhileStatement:
|
||||||
|
return self.cmpl_evaluate_nodeDoWhileStatement(node)
|
||||||
|
|
||||||
|
case *_nodeEmptyStatement:
|
||||||
|
return emptyValue
|
||||||
|
|
||||||
|
case *_nodeExpressionStatement:
|
||||||
|
return self.cmpl_evaluate_nodeExpression(node.expression)
|
||||||
|
|
||||||
|
case *_nodeForInStatement:
|
||||||
|
return self.cmpl_evaluate_nodeForInStatement(node)
|
||||||
|
|
||||||
|
case *_nodeForStatement:
|
||||||
|
return self.cmpl_evaluate_nodeForStatement(node)
|
||||||
|
|
||||||
|
case *_nodeIfStatement:
|
||||||
|
return self.cmpl_evaluate_nodeIfStatement(node)
|
||||||
|
|
||||||
|
case *_nodeLabelledStatement:
|
||||||
|
self.labels = append(self.labels, node.label)
|
||||||
|
defer func() {
|
||||||
|
if len(self.labels) > 0 {
|
||||||
|
self.labels = self.labels[:len(self.labels)-1] // Pop the label
|
||||||
|
} else {
|
||||||
|
self.labels = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return self.cmpl_evaluate_nodeStatement(node.statement)
|
||||||
|
|
||||||
|
case *_nodeReturnStatement:
|
||||||
|
if node.argument != nil {
|
||||||
|
return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve()))
|
||||||
|
}
|
||||||
|
return toValue(newReturnResult(Value{}))
|
||||||
|
|
||||||
|
case *_nodeSwitchStatement:
|
||||||
|
return self.cmpl_evaluate_nodeSwitchStatement(node)
|
||||||
|
|
||||||
|
case *_nodeThrowStatement:
|
||||||
|
value := self.cmpl_evaluate_nodeExpression(node.argument).resolve()
|
||||||
|
panic(newException(value))
|
||||||
|
|
||||||
|
case *_nodeTryStatement:
|
||||||
|
return self.cmpl_evaluate_nodeTryStatement(node)
|
||||||
|
|
||||||
|
case *_nodeVariableStatement:
|
||||||
|
// Variables are already defined, this is initialization only
|
||||||
|
for _, variable := range node.list {
|
||||||
|
self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression))
|
||||||
|
}
|
||||||
|
return emptyValue
|
||||||
|
|
||||||
|
case *_nodeWhileStatement:
|
||||||
|
return self.cmpl_evaluate_nodeWhileStatement(node)
|
||||||
|
|
||||||
|
case *_nodeWithStatement:
|
||||||
|
return self.cmpl_evaluate_nodeWithStatement(node)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Errorf("Here be dragons: evaluate_nodeStatement(%T)", node))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Value {
|
||||||
|
var result Value
|
||||||
|
for _, node := range list {
|
||||||
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
return value
|
||||||
|
case valueEmpty:
|
||||||
|
default:
|
||||||
|
// We have getValue here to (for example) trigger a
|
||||||
|
// ReferenceError (of the not defined variety)
|
||||||
|
// Not sure if this is the best way to error out early
|
||||||
|
// for such errors or if there is a better way
|
||||||
|
// TODO Do we still need this?
|
||||||
|
result = value.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileStatement) Value {
|
||||||
|
|
||||||
|
labels := append(self.labels, "")
|
||||||
|
self.labels = nil
|
||||||
|
|
||||||
|
test := node.test
|
||||||
|
|
||||||
|
result := emptyValue
|
||||||
|
resultBreak:
|
||||||
|
for {
|
||||||
|
for _, node := range node.body {
|
||||||
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
switch value.evaluateBreakContinue(labels) {
|
||||||
|
case resultReturn:
|
||||||
|
return value
|
||||||
|
case resultBreak:
|
||||||
|
break resultBreak
|
||||||
|
case resultContinue:
|
||||||
|
goto resultContinue
|
||||||
|
}
|
||||||
|
case valueEmpty:
|
||||||
|
default:
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultContinue:
|
||||||
|
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
|
||||||
|
// Stahp: do ... while (false)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement) Value {
|
||||||
|
|
||||||
|
labels := append(self.labels, "")
|
||||||
|
self.labels = nil
|
||||||
|
|
||||||
|
source := self.cmpl_evaluate_nodeExpression(node.source)
|
||||||
|
sourceValue := source.resolve()
|
||||||
|
|
||||||
|
switch sourceValue.kind {
|
||||||
|
case valueUndefined, valueNull:
|
||||||
|
return emptyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceObject := self.toObject(sourceValue)
|
||||||
|
|
||||||
|
into := node.into
|
||||||
|
body := node.body
|
||||||
|
|
||||||
|
result := emptyValue
|
||||||
|
object := sourceObject
|
||||||
|
for object != nil {
|
||||||
|
enumerateValue := emptyValue
|
||||||
|
object.enumerate(false, func(name string) bool {
|
||||||
|
into := self.cmpl_evaluate_nodeExpression(into)
|
||||||
|
// In the case of: for (var abc in def) ...
|
||||||
|
if into.reference() == nil {
|
||||||
|
identifier := into.string()
|
||||||
|
// TODO Should be true or false (strictness) depending on context
|
||||||
|
into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1))
|
||||||
|
}
|
||||||
|
self.putValue(into.reference(), toValue_string(name))
|
||||||
|
for _, node := range body {
|
||||||
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
switch value.evaluateBreakContinue(labels) {
|
||||||
|
case resultReturn:
|
||||||
|
enumerateValue = value
|
||||||
|
return false
|
||||||
|
case resultBreak:
|
||||||
|
object = nil
|
||||||
|
return false
|
||||||
|
case resultContinue:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case valueEmpty:
|
||||||
|
default:
|
||||||
|
enumerateValue = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if object == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
object = object.prototype
|
||||||
|
if !enumerateValue.isEmpty() {
|
||||||
|
result = enumerateValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Value {
|
||||||
|
|
||||||
|
labels := append(self.labels, "")
|
||||||
|
self.labels = nil
|
||||||
|
|
||||||
|
initializer := node.initializer
|
||||||
|
test := node.test
|
||||||
|
update := node.update
|
||||||
|
body := node.body
|
||||||
|
|
||||||
|
if initializer != nil {
|
||||||
|
initialResult := self.cmpl_evaluate_nodeExpression(initializer)
|
||||||
|
initialResult.resolve() // Side-effect trigger
|
||||||
|
}
|
||||||
|
|
||||||
|
result := emptyValue
|
||||||
|
resultBreak:
|
||||||
|
for {
|
||||||
|
if test != nil {
|
||||||
|
testResult := self.cmpl_evaluate_nodeExpression(test)
|
||||||
|
testResultValue := testResult.resolve()
|
||||||
|
if testResultValue.bool() == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, node := range body {
|
||||||
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
switch value.evaluateBreakContinue(labels) {
|
||||||
|
case resultReturn:
|
||||||
|
return value
|
||||||
|
case resultBreak:
|
||||||
|
break resultBreak
|
||||||
|
case resultContinue:
|
||||||
|
goto resultContinue
|
||||||
|
}
|
||||||
|
case valueEmpty:
|
||||||
|
default:
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultContinue:
|
||||||
|
if update != nil {
|
||||||
|
updateResult := self.cmpl_evaluate_nodeExpression(update)
|
||||||
|
updateResult.resolve() // Side-effect trigger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value {
|
||||||
|
test := self.cmpl_evaluate_nodeExpression(node.test)
|
||||||
|
testValue := test.resolve()
|
||||||
|
if testValue.bool() {
|
||||||
|
return self.cmpl_evaluate_nodeStatement(node.consequent)
|
||||||
|
} else if node.alternate != nil {
|
||||||
|
return self.cmpl_evaluate_nodeStatement(node.alternate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value {
|
||||||
|
|
||||||
|
labels := append(self.labels, "")
|
||||||
|
self.labels = nil
|
||||||
|
|
||||||
|
discriminantResult := self.cmpl_evaluate_nodeExpression(node.discriminant)
|
||||||
|
target := node.default_
|
||||||
|
|
||||||
|
for index, clause := range node.body {
|
||||||
|
test := clause.test
|
||||||
|
if test != nil {
|
||||||
|
if self.calculateComparison(token.STRICT_EQUAL, discriminantResult, self.cmpl_evaluate_nodeExpression(test)) {
|
||||||
|
target = index
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := emptyValue
|
||||||
|
if target != -1 {
|
||||||
|
for _, clause := range node.body[target:] {
|
||||||
|
for _, statement := range clause.consequent {
|
||||||
|
value := self.cmpl_evaluate_nodeStatement(statement)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
switch value.evaluateBreak(labels) {
|
||||||
|
case resultReturn:
|
||||||
|
return value
|
||||||
|
case resultBreak:
|
||||||
|
return emptyValue
|
||||||
|
}
|
||||||
|
case valueEmpty:
|
||||||
|
default:
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Value {
|
||||||
|
tryCatchValue, exception := self.tryCatchEvaluate(func() Value {
|
||||||
|
return self.cmpl_evaluate_nodeStatement(node.body)
|
||||||
|
})
|
||||||
|
|
||||||
|
if exception && node.catch != nil {
|
||||||
|
outer := self.scope.lexical
|
||||||
|
self.scope.lexical = self.newDeclarationStash(outer)
|
||||||
|
defer func() {
|
||||||
|
self.scope.lexical = outer
|
||||||
|
}()
|
||||||
|
// TODO If necessary, convert TypeError<runtime> => TypeError
|
||||||
|
// That, is, such errors can be thrown despite not being JavaScript "native"
|
||||||
|
// strict = false
|
||||||
|
self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false)
|
||||||
|
|
||||||
|
// FIXME node.CatchParameter
|
||||||
|
// FIXME node.Catch
|
||||||
|
tryCatchValue, exception = self.tryCatchEvaluate(func() Value {
|
||||||
|
return self.cmpl_evaluate_nodeStatement(node.catch.body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.finally != nil {
|
||||||
|
finallyValue := self.cmpl_evaluate_nodeStatement(node.finally)
|
||||||
|
if finallyValue.kind == valueResult {
|
||||||
|
return finallyValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if exception {
|
||||||
|
panic(newException(tryCatchValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tryCatchValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement) Value {
|
||||||
|
|
||||||
|
test := node.test
|
||||||
|
body := node.body
|
||||||
|
labels := append(self.labels, "")
|
||||||
|
self.labels = nil
|
||||||
|
|
||||||
|
result := emptyValue
|
||||||
|
resultBreakContinue:
|
||||||
|
for {
|
||||||
|
if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() {
|
||||||
|
// Stahp: while (false) ...
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, node := range body {
|
||||||
|
value := self.cmpl_evaluate_nodeStatement(node)
|
||||||
|
switch value.kind {
|
||||||
|
case valueResult:
|
||||||
|
switch value.evaluateBreakContinue(labels) {
|
||||||
|
case resultReturn:
|
||||||
|
return value
|
||||||
|
case resultBreak:
|
||||||
|
break resultBreakContinue
|
||||||
|
case resultContinue:
|
||||||
|
continue resultBreakContinue
|
||||||
|
}
|
||||||
|
case valueEmpty:
|
||||||
|
default:
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value {
|
||||||
|
object := self.cmpl_evaluate_nodeExpression(node.object)
|
||||||
|
outer := self.scope.lexical
|
||||||
|
lexical := self.newObjectStash(self.toObject(object.resolve()), outer)
|
||||||
|
self.scope.lexical = lexical
|
||||||
|
defer func() {
|
||||||
|
self.scope.lexical = outer
|
||||||
|
}()
|
||||||
|
|
||||||
|
return self.cmpl_evaluate_nodeStatement(node.body)
|
||||||
|
}
|
656
vendor/github.com/robertkrimen/otto/cmpl_parse.go
generated
vendored
Normal file
656
vendor/github.com/robertkrimen/otto/cmpl_parse.go
generated
vendored
Normal file
|
@ -0,0 +1,656 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
var trueLiteral = &_nodeLiteral{value: toValue_bool(true)}
|
||||||
|
var falseLiteral = &_nodeLiteral{value: toValue_bool(false)}
|
||||||
|
var nullLiteral = &_nodeLiteral{value: nullValue}
|
||||||
|
var emptyStatement = &_nodeEmptyStatement{}
|
||||||
|
|
||||||
|
func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch in := in.(type) {
|
||||||
|
|
||||||
|
case *ast.ArrayLiteral:
|
||||||
|
out := &_nodeArrayLiteral{
|
||||||
|
value: make([]_nodeExpression, len(in.Value)),
|
||||||
|
}
|
||||||
|
for i, value := range in.Value {
|
||||||
|
out.value[i] = cmpl.parseExpression(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.AssignExpression:
|
||||||
|
return &_nodeAssignExpression{
|
||||||
|
operator: in.Operator,
|
||||||
|
left: cmpl.parseExpression(in.Left),
|
||||||
|
right: cmpl.parseExpression(in.Right),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BinaryExpression:
|
||||||
|
return &_nodeBinaryExpression{
|
||||||
|
operator: in.Operator,
|
||||||
|
left: cmpl.parseExpression(in.Left),
|
||||||
|
right: cmpl.parseExpression(in.Right),
|
||||||
|
comparison: in.Comparison,
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BooleanLiteral:
|
||||||
|
if in.Value {
|
||||||
|
return trueLiteral
|
||||||
|
}
|
||||||
|
return falseLiteral
|
||||||
|
|
||||||
|
case *ast.BracketExpression:
|
||||||
|
return &_nodeBracketExpression{
|
||||||
|
idx: in.Left.Idx0(),
|
||||||
|
left: cmpl.parseExpression(in.Left),
|
||||||
|
member: cmpl.parseExpression(in.Member),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CallExpression:
|
||||||
|
out := &_nodeCallExpression{
|
||||||
|
callee: cmpl.parseExpression(in.Callee),
|
||||||
|
argumentList: make([]_nodeExpression, len(in.ArgumentList)),
|
||||||
|
}
|
||||||
|
for i, value := range in.ArgumentList {
|
||||||
|
out.argumentList[i] = cmpl.parseExpression(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.ConditionalExpression:
|
||||||
|
return &_nodeConditionalExpression{
|
||||||
|
test: cmpl.parseExpression(in.Test),
|
||||||
|
consequent: cmpl.parseExpression(in.Consequent),
|
||||||
|
alternate: cmpl.parseExpression(in.Alternate),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.DotExpression:
|
||||||
|
return &_nodeDotExpression{
|
||||||
|
idx: in.Left.Idx0(),
|
||||||
|
left: cmpl.parseExpression(in.Left),
|
||||||
|
identifier: in.Identifier.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.EmptyExpression:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.FunctionLiteral:
|
||||||
|
name := ""
|
||||||
|
if in.Name != nil {
|
||||||
|
name = in.Name.Name
|
||||||
|
}
|
||||||
|
out := &_nodeFunctionLiteral{
|
||||||
|
name: name,
|
||||||
|
body: cmpl.parseStatement(in.Body),
|
||||||
|
source: in.Source,
|
||||||
|
file: cmpl.file,
|
||||||
|
}
|
||||||
|
if in.ParameterList != nil {
|
||||||
|
list := in.ParameterList.List
|
||||||
|
out.parameterList = make([]string, len(list))
|
||||||
|
for i, value := range list {
|
||||||
|
out.parameterList[i] = value.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, value := range in.DeclarationList {
|
||||||
|
switch value := value.(type) {
|
||||||
|
case *ast.FunctionDeclaration:
|
||||||
|
out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral))
|
||||||
|
case *ast.VariableDeclaration:
|
||||||
|
for _, value := range value.List {
|
||||||
|
out.varList = append(out.varList, value.Name)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.Identifier:
|
||||||
|
return &_nodeIdentifier{
|
||||||
|
idx: in.Idx,
|
||||||
|
name: in.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.NewExpression:
|
||||||
|
out := &_nodeNewExpression{
|
||||||
|
callee: cmpl.parseExpression(in.Callee),
|
||||||
|
argumentList: make([]_nodeExpression, len(in.ArgumentList)),
|
||||||
|
}
|
||||||
|
for i, value := range in.ArgumentList {
|
||||||
|
out.argumentList[i] = cmpl.parseExpression(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.NullLiteral:
|
||||||
|
return nullLiteral
|
||||||
|
|
||||||
|
case *ast.NumberLiteral:
|
||||||
|
return &_nodeLiteral{
|
||||||
|
value: toValue(in.Value),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ObjectLiteral:
|
||||||
|
out := &_nodeObjectLiteral{
|
||||||
|
value: make([]_nodeProperty, len(in.Value)),
|
||||||
|
}
|
||||||
|
for i, value := range in.Value {
|
||||||
|
out.value[i] = _nodeProperty{
|
||||||
|
key: value.Key,
|
||||||
|
kind: value.Kind,
|
||||||
|
value: cmpl.parseExpression(value.Value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.RegExpLiteral:
|
||||||
|
return &_nodeRegExpLiteral{
|
||||||
|
flags: in.Flags,
|
||||||
|
pattern: in.Pattern,
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SequenceExpression:
|
||||||
|
out := &_nodeSequenceExpression{
|
||||||
|
sequence: make([]_nodeExpression, len(in.Sequence)),
|
||||||
|
}
|
||||||
|
for i, value := range in.Sequence {
|
||||||
|
out.sequence[i] = cmpl.parseExpression(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.StringLiteral:
|
||||||
|
return &_nodeLiteral{
|
||||||
|
value: toValue_string(in.Value),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ThisExpression:
|
||||||
|
return &_nodeThisExpression{}
|
||||||
|
|
||||||
|
case *ast.UnaryExpression:
|
||||||
|
return &_nodeUnaryExpression{
|
||||||
|
operator: in.Operator,
|
||||||
|
operand: cmpl.parseExpression(in.Operand),
|
||||||
|
postfix: in.Postfix,
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.VariableExpression:
|
||||||
|
return &_nodeVariableExpression{
|
||||||
|
idx: in.Idx0(),
|
||||||
|
name: in.Name,
|
||||||
|
initializer: cmpl.parseExpression(in.Initializer),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Errorf("Here be dragons: cmpl.parseExpression(%T)", in))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmpl *_compiler) parseStatement(in ast.Statement) _nodeStatement {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch in := in.(type) {
|
||||||
|
|
||||||
|
case *ast.BlockStatement:
|
||||||
|
out := &_nodeBlockStatement{
|
||||||
|
list: make([]_nodeStatement, len(in.List)),
|
||||||
|
}
|
||||||
|
for i, value := range in.List {
|
||||||
|
out.list[i] = cmpl.parseStatement(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.BranchStatement:
|
||||||
|
out := &_nodeBranchStatement{
|
||||||
|
branch: in.Token,
|
||||||
|
}
|
||||||
|
if in.Label != nil {
|
||||||
|
out.label = in.Label.Name
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.DebuggerStatement:
|
||||||
|
return &_nodeDebuggerStatement{}
|
||||||
|
|
||||||
|
case *ast.DoWhileStatement:
|
||||||
|
out := &_nodeDoWhileStatement{
|
||||||
|
test: cmpl.parseExpression(in.Test),
|
||||||
|
}
|
||||||
|
body := cmpl.parseStatement(in.Body)
|
||||||
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
|
out.body = block.list
|
||||||
|
} else {
|
||||||
|
out.body = append(out.body, body)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.EmptyStatement:
|
||||||
|
return emptyStatement
|
||||||
|
|
||||||
|
case *ast.ExpressionStatement:
|
||||||
|
return &_nodeExpressionStatement{
|
||||||
|
expression: cmpl.parseExpression(in.Expression),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ForInStatement:
|
||||||
|
out := &_nodeForInStatement{
|
||||||
|
into: cmpl.parseExpression(in.Into),
|
||||||
|
source: cmpl.parseExpression(in.Source),
|
||||||
|
}
|
||||||
|
body := cmpl.parseStatement(in.Body)
|
||||||
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
|
out.body = block.list
|
||||||
|
} else {
|
||||||
|
out.body = append(out.body, body)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.ForStatement:
|
||||||
|
out := &_nodeForStatement{
|
||||||
|
initializer: cmpl.parseExpression(in.Initializer),
|
||||||
|
update: cmpl.parseExpression(in.Update),
|
||||||
|
test: cmpl.parseExpression(in.Test),
|
||||||
|
}
|
||||||
|
body := cmpl.parseStatement(in.Body)
|
||||||
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
|
out.body = block.list
|
||||||
|
} else {
|
||||||
|
out.body = append(out.body, body)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.FunctionStatement:
|
||||||
|
return emptyStatement
|
||||||
|
|
||||||
|
case *ast.IfStatement:
|
||||||
|
return &_nodeIfStatement{
|
||||||
|
test: cmpl.parseExpression(in.Test),
|
||||||
|
consequent: cmpl.parseStatement(in.Consequent),
|
||||||
|
alternate: cmpl.parseStatement(in.Alternate),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.LabelledStatement:
|
||||||
|
return &_nodeLabelledStatement{
|
||||||
|
label: in.Label.Name,
|
||||||
|
statement: cmpl.parseStatement(in.Statement),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ReturnStatement:
|
||||||
|
return &_nodeReturnStatement{
|
||||||
|
argument: cmpl.parseExpression(in.Argument),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SwitchStatement:
|
||||||
|
out := &_nodeSwitchStatement{
|
||||||
|
discriminant: cmpl.parseExpression(in.Discriminant),
|
||||||
|
default_: in.Default,
|
||||||
|
body: make([]*_nodeCaseStatement, len(in.Body)),
|
||||||
|
}
|
||||||
|
for i, clause := range in.Body {
|
||||||
|
out.body[i] = &_nodeCaseStatement{
|
||||||
|
test: cmpl.parseExpression(clause.Test),
|
||||||
|
consequent: make([]_nodeStatement, len(clause.Consequent)),
|
||||||
|
}
|
||||||
|
for j, value := range clause.Consequent {
|
||||||
|
out.body[i].consequent[j] = cmpl.parseStatement(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.ThrowStatement:
|
||||||
|
return &_nodeThrowStatement{
|
||||||
|
argument: cmpl.parseExpression(in.Argument),
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.TryStatement:
|
||||||
|
out := &_nodeTryStatement{
|
||||||
|
body: cmpl.parseStatement(in.Body),
|
||||||
|
finally: cmpl.parseStatement(in.Finally),
|
||||||
|
}
|
||||||
|
if in.Catch != nil {
|
||||||
|
out.catch = &_nodeCatchStatement{
|
||||||
|
parameter: in.Catch.Parameter.Name,
|
||||||
|
body: cmpl.parseStatement(in.Catch.Body),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.VariableStatement:
|
||||||
|
out := &_nodeVariableStatement{
|
||||||
|
list: make([]_nodeExpression, len(in.List)),
|
||||||
|
}
|
||||||
|
for i, value := range in.List {
|
||||||
|
out.list[i] = cmpl.parseExpression(value)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.WhileStatement:
|
||||||
|
out := &_nodeWhileStatement{
|
||||||
|
test: cmpl.parseExpression(in.Test),
|
||||||
|
}
|
||||||
|
body := cmpl.parseStatement(in.Body)
|
||||||
|
if block, ok := body.(*_nodeBlockStatement); ok {
|
||||||
|
out.body = block.list
|
||||||
|
} else {
|
||||||
|
out.body = append(out.body, body)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
case *ast.WithStatement:
|
||||||
|
return &_nodeWithStatement{
|
||||||
|
object: cmpl.parseExpression(in.Object),
|
||||||
|
body: cmpl.parseStatement(in.Body),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Errorf("Here be dragons: cmpl.parseStatement(%T)", in))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmpl_parse(in *ast.Program) *_nodeProgram {
|
||||||
|
cmpl := _compiler{
|
||||||
|
program: in,
|
||||||
|
}
|
||||||
|
return cmpl.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmpl *_compiler) _parse(in *ast.Program) *_nodeProgram {
|
||||||
|
out := &_nodeProgram{
|
||||||
|
body: make([]_nodeStatement, len(in.Body)),
|
||||||
|
file: in.File,
|
||||||
|
}
|
||||||
|
for i, value := range in.Body {
|
||||||
|
out.body[i] = cmpl.parseStatement(value)
|
||||||
|
}
|
||||||
|
for _, value := range in.DeclarationList {
|
||||||
|
switch value := value.(type) {
|
||||||
|
case *ast.FunctionDeclaration:
|
||||||
|
out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral))
|
||||||
|
case *ast.VariableDeclaration:
|
||||||
|
for _, value := range value.List {
|
||||||
|
out.varList = append(out.varList, value.Name)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("Here be dragons: cmpl.parseProgram.DeclarationList(%T)", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
type _nodeProgram struct {
|
||||||
|
body []_nodeStatement
|
||||||
|
|
||||||
|
varList []string
|
||||||
|
functionList []*_nodeFunctionLiteral
|
||||||
|
|
||||||
|
variableList []_nodeDeclaration
|
||||||
|
|
||||||
|
file *file.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type _nodeDeclaration struct {
|
||||||
|
name string
|
||||||
|
definition _node
|
||||||
|
}
|
||||||
|
|
||||||
|
type _node interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
_nodeExpression interface {
|
||||||
|
_node
|
||||||
|
_expressionNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeArrayLiteral struct {
|
||||||
|
value []_nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeAssignExpression struct {
|
||||||
|
operator token.Token
|
||||||
|
left _nodeExpression
|
||||||
|
right _nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeBinaryExpression struct {
|
||||||
|
operator token.Token
|
||||||
|
left _nodeExpression
|
||||||
|
right _nodeExpression
|
||||||
|
comparison bool
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeBracketExpression struct {
|
||||||
|
idx file.Idx
|
||||||
|
left _nodeExpression
|
||||||
|
member _nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeCallExpression struct {
|
||||||
|
callee _nodeExpression
|
||||||
|
argumentList []_nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeConditionalExpression struct {
|
||||||
|
test _nodeExpression
|
||||||
|
consequent _nodeExpression
|
||||||
|
alternate _nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeDotExpression struct {
|
||||||
|
idx file.Idx
|
||||||
|
left _nodeExpression
|
||||||
|
identifier string
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeFunctionLiteral struct {
|
||||||
|
name string
|
||||||
|
body _nodeStatement
|
||||||
|
source string
|
||||||
|
parameterList []string
|
||||||
|
varList []string
|
||||||
|
functionList []*_nodeFunctionLiteral
|
||||||
|
file *file.File
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeIdentifier struct {
|
||||||
|
idx file.Idx
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeLiteral struct {
|
||||||
|
value Value
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeNewExpression struct {
|
||||||
|
callee _nodeExpression
|
||||||
|
argumentList []_nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeObjectLiteral struct {
|
||||||
|
value []_nodeProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeProperty struct {
|
||||||
|
key string
|
||||||
|
kind string
|
||||||
|
value _nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeRegExpLiteral struct {
|
||||||
|
flags string
|
||||||
|
pattern string // Value?
|
||||||
|
regexp *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeSequenceExpression struct {
|
||||||
|
sequence []_nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeThisExpression struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeUnaryExpression struct {
|
||||||
|
operator token.Token
|
||||||
|
operand _nodeExpression
|
||||||
|
postfix bool
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeVariableExpression struct {
|
||||||
|
idx file.Idx
|
||||||
|
name string
|
||||||
|
initializer _nodeExpression
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
_nodeStatement interface {
|
||||||
|
_node
|
||||||
|
_statementNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeBlockStatement struct {
|
||||||
|
list []_nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeBranchStatement struct {
|
||||||
|
branch token.Token
|
||||||
|
label string
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeCaseStatement struct {
|
||||||
|
test _nodeExpression
|
||||||
|
consequent []_nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeCatchStatement struct {
|
||||||
|
parameter string
|
||||||
|
body _nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeDebuggerStatement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeDoWhileStatement struct {
|
||||||
|
test _nodeExpression
|
||||||
|
body []_nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeEmptyStatement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeExpressionStatement struct {
|
||||||
|
expression _nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeForInStatement struct {
|
||||||
|
into _nodeExpression
|
||||||
|
source _nodeExpression
|
||||||
|
body []_nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeForStatement struct {
|
||||||
|
initializer _nodeExpression
|
||||||
|
update _nodeExpression
|
||||||
|
test _nodeExpression
|
||||||
|
body []_nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeIfStatement struct {
|
||||||
|
test _nodeExpression
|
||||||
|
consequent _nodeStatement
|
||||||
|
alternate _nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeLabelledStatement struct {
|
||||||
|
label string
|
||||||
|
statement _nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeReturnStatement struct {
|
||||||
|
argument _nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeSwitchStatement struct {
|
||||||
|
discriminant _nodeExpression
|
||||||
|
default_ int
|
||||||
|
body []*_nodeCaseStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeThrowStatement struct {
|
||||||
|
argument _nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeTryStatement struct {
|
||||||
|
body _nodeStatement
|
||||||
|
catch *_nodeCatchStatement
|
||||||
|
finally _nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeVariableStatement struct {
|
||||||
|
list []_nodeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeWhileStatement struct {
|
||||||
|
test _nodeExpression
|
||||||
|
body []_nodeStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodeWithStatement struct {
|
||||||
|
object _nodeExpression
|
||||||
|
body _nodeStatement
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// _expressionNode
|
||||||
|
|
||||||
|
func (*_nodeArrayLiteral) _expressionNode() {}
|
||||||
|
func (*_nodeAssignExpression) _expressionNode() {}
|
||||||
|
func (*_nodeBinaryExpression) _expressionNode() {}
|
||||||
|
func (*_nodeBracketExpression) _expressionNode() {}
|
||||||
|
func (*_nodeCallExpression) _expressionNode() {}
|
||||||
|
func (*_nodeConditionalExpression) _expressionNode() {}
|
||||||
|
func (*_nodeDotExpression) _expressionNode() {}
|
||||||
|
func (*_nodeFunctionLiteral) _expressionNode() {}
|
||||||
|
func (*_nodeIdentifier) _expressionNode() {}
|
||||||
|
func (*_nodeLiteral) _expressionNode() {}
|
||||||
|
func (*_nodeNewExpression) _expressionNode() {}
|
||||||
|
func (*_nodeObjectLiteral) _expressionNode() {}
|
||||||
|
func (*_nodeRegExpLiteral) _expressionNode() {}
|
||||||
|
func (*_nodeSequenceExpression) _expressionNode() {}
|
||||||
|
func (*_nodeThisExpression) _expressionNode() {}
|
||||||
|
func (*_nodeUnaryExpression) _expressionNode() {}
|
||||||
|
func (*_nodeVariableExpression) _expressionNode() {}
|
||||||
|
|
||||||
|
// _statementNode
|
||||||
|
|
||||||
|
func (*_nodeBlockStatement) _statementNode() {}
|
||||||
|
func (*_nodeBranchStatement) _statementNode() {}
|
||||||
|
func (*_nodeCaseStatement) _statementNode() {}
|
||||||
|
func (*_nodeCatchStatement) _statementNode() {}
|
||||||
|
func (*_nodeDebuggerStatement) _statementNode() {}
|
||||||
|
func (*_nodeDoWhileStatement) _statementNode() {}
|
||||||
|
func (*_nodeEmptyStatement) _statementNode() {}
|
||||||
|
func (*_nodeExpressionStatement) _statementNode() {}
|
||||||
|
func (*_nodeForInStatement) _statementNode() {}
|
||||||
|
func (*_nodeForStatement) _statementNode() {}
|
||||||
|
func (*_nodeIfStatement) _statementNode() {}
|
||||||
|
func (*_nodeLabelledStatement) _statementNode() {}
|
||||||
|
func (*_nodeReturnStatement) _statementNode() {}
|
||||||
|
func (*_nodeSwitchStatement) _statementNode() {}
|
||||||
|
func (*_nodeThrowStatement) _statementNode() {}
|
||||||
|
func (*_nodeTryStatement) _statementNode() {}
|
||||||
|
func (*_nodeVariableStatement) _statementNode() {}
|
||||||
|
func (*_nodeWhileStatement) _statementNode() {}
|
||||||
|
func (*_nodeWithStatement) _statementNode() {}
|
51
vendor/github.com/robertkrimen/otto/console.go
generated
vendored
Normal file
51
vendor/github.com/robertkrimen/otto/console.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func formatForConsole(argumentList []Value) string {
|
||||||
|
output := []string{}
|
||||||
|
for _, argument := range argumentList {
|
||||||
|
output = append(output, fmt.Sprintf("%v", argument))
|
||||||
|
}
|
||||||
|
return strings.Join(output, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinConsole_log(call FunctionCall) Value {
|
||||||
|
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinConsole_error(call FunctionCall) Value {
|
||||||
|
fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList))
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing happens.
|
||||||
|
func builtinConsole_dir(call FunctionCall) Value {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinConsole_time(call FunctionCall) Value {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinConsole_timeEnd(call FunctionCall) Value {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinConsole_trace(call FunctionCall) Value {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinConsole_assert(call FunctionCall) Value {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newConsole() *_object {
|
||||||
|
|
||||||
|
return newConsoleObject(runtime)
|
||||||
|
}
|
9
vendor/github.com/robertkrimen/otto/dbg.go
generated
vendored
Normal file
9
vendor/github.com/robertkrimen/otto/dbg.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg
|
||||||
|
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
Dbg "github.com/robertkrimen/otto/dbg"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dbg, dbgf = Dbg.New()
|
387
vendor/github.com/robertkrimen/otto/dbg/dbg.go
generated
vendored
Normal file
387
vendor/github.com/robertkrimen/otto/dbg/dbg.go
generated
vendored
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package dbg is a println/printf/log-debugging utility library.
|
||||||
|
|
||||||
|
import (
|
||||||
|
Dbg "github.com/robertkrimen/dbg"
|
||||||
|
)
|
||||||
|
|
||||||
|
dbg, dbgf := Dbg.New()
|
||||||
|
|
||||||
|
dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi)
|
||||||
|
# "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793"
|
||||||
|
|
||||||
|
dbgf("With a %s formatting %.2f", "little", math.Pi)
|
||||||
|
# "2013/01/28 16:51:55 With a little formatting (3.14)"
|
||||||
|
|
||||||
|
dbgf("%/fatal//A fatal debug statement: should not be here")
|
||||||
|
# "A fatal debug statement: should not be here"
|
||||||
|
# ...and then, os.Exit(1)
|
||||||
|
|
||||||
|
dbgf("%/panic//Can also panic %s", "this")
|
||||||
|
# "Can also panic this"
|
||||||
|
# ...as a panic, equivalent to: panic("Can also panic this")
|
||||||
|
|
||||||
|
dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()")
|
||||||
|
# "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()"
|
||||||
|
|
||||||
|
dbgf("%d %d", 1, 2, 3, 4, 5)
|
||||||
|
# "2013/01/28 17:16:32 Another example: 1 2 3 4 5"
|
||||||
|
|
||||||
|
dbgf("%@: Include the function name for a little context (via %s)", "%@")
|
||||||
|
# "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)"
|
||||||
|
|
||||||
|
By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output.
|
||||||
|
However, you can also provide your own output destination by invoking dbg.New with
|
||||||
|
a customization function:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
Dbg "github.com/robertkrimen/dbg"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
# dbg to os.Stderr
|
||||||
|
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
|
||||||
|
dbgr.SetOutput(os.Stderr)
|
||||||
|
})
|
||||||
|
|
||||||
|
# A slightly contrived example:
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
dbg, dbgf := New(func(dbgr *Dbgr) {
|
||||||
|
dbgr.SetOutput(&buffer)
|
||||||
|
})
|
||||||
|
|
||||||
|
*/
|
||||||
|
package dbg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _frmt struct {
|
||||||
|
ctl string
|
||||||
|
format string
|
||||||
|
operandCount int
|
||||||
|
panic bool
|
||||||
|
fatal bool
|
||||||
|
check bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctlTest = regexp.MustCompile(`^\s*%/`)
|
||||||
|
ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func operandCount(format string) int {
|
||||||
|
count := 0
|
||||||
|
end := len(format)
|
||||||
|
for at := 0; at < end; {
|
||||||
|
for at < end && format[at] != '%' {
|
||||||
|
at++
|
||||||
|
}
|
||||||
|
at++
|
||||||
|
if at < end {
|
||||||
|
if format[at] != '%' && format[at] != '@' {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
at++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFormat(format string) (frmt _frmt) {
|
||||||
|
if ctlTest.MatchString(format) {
|
||||||
|
format = strings.TrimLeftFunc(format, unicode.IsSpace)
|
||||||
|
index := strings.Index(format, "//")
|
||||||
|
if index != -1 {
|
||||||
|
frmt.ctl = format[0:index]
|
||||||
|
format = format[index+2:] // Skip the second slash via +2 (instead of +1)
|
||||||
|
} else {
|
||||||
|
frmt.ctl = format
|
||||||
|
format = ""
|
||||||
|
}
|
||||||
|
for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) {
|
||||||
|
for _, value := range tmp[1:] {
|
||||||
|
switch value {
|
||||||
|
case "panic":
|
||||||
|
frmt.panic = true
|
||||||
|
case "fatal":
|
||||||
|
frmt.fatal = true
|
||||||
|
case "check":
|
||||||
|
frmt.check = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frmt.format = format
|
||||||
|
frmt.operandCount = operandCount(format)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dbgr struct {
|
||||||
|
emit _emit
|
||||||
|
}
|
||||||
|
|
||||||
|
type DbgFunction func(values ...interface{})
|
||||||
|
|
||||||
|
func NewDbgr() *Dbgr {
|
||||||
|
self := &Dbgr{}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
New will create and return a pair of debugging functions. You can customize where
|
||||||
|
they output to by passing in an (optional) customization function:
|
||||||
|
|
||||||
|
import (
|
||||||
|
Dbg "github.com/robertkrimen/dbg"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
# dbg to os.Stderr
|
||||||
|
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) {
|
||||||
|
dbgr.SetOutput(os.Stderr)
|
||||||
|
})
|
||||||
|
|
||||||
|
*/
|
||||||
|
func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) {
|
||||||
|
dbgr := NewDbgr()
|
||||||
|
if len(options) > 0 {
|
||||||
|
if fn, ok := options[0].(func(*Dbgr)); ok {
|
||||||
|
fn(dbgr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbgr.DbgDbgf()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Dbgr) Dbg(values ...interface{}) {
|
||||||
|
self.getEmit().emit(_frmt{}, "", values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Dbgr) Dbgf(values ...interface{}) {
|
||||||
|
self.dbgf(values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) {
|
||||||
|
dbg = func(vl ...interface{}) {
|
||||||
|
self.Dbg(vl...)
|
||||||
|
}
|
||||||
|
dbgf = func(vl ...interface{}) {
|
||||||
|
self.dbgf(vl...)
|
||||||
|
}
|
||||||
|
return dbg, dbgf // Redundant, but...
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Dbgr) dbgf(values ...interface{}) {
|
||||||
|
|
||||||
|
var frmt _frmt
|
||||||
|
if len(values) > 0 {
|
||||||
|
tmp := fmt.Sprint(values[0])
|
||||||
|
frmt = parseFormat(tmp)
|
||||||
|
values = values[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_f := bytes.Buffer{}
|
||||||
|
format := frmt.format
|
||||||
|
end := len(format)
|
||||||
|
for at := 0; at < end; {
|
||||||
|
last := at
|
||||||
|
for at < end && format[at] != '%' {
|
||||||
|
at++
|
||||||
|
}
|
||||||
|
if at > last {
|
||||||
|
buffer_f.WriteString(format[last:at])
|
||||||
|
}
|
||||||
|
if at >= end {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// format[at] == '%'
|
||||||
|
at++
|
||||||
|
// format[at] == ?
|
||||||
|
if format[at] == '@' {
|
||||||
|
depth := 2
|
||||||
|
pc, _, _, _ := runtime.Caller(depth)
|
||||||
|
name := runtime.FuncForPC(pc).Name()
|
||||||
|
buffer_f.WriteString(name)
|
||||||
|
} else {
|
||||||
|
buffer_f.WriteString(format[at-1 : at+1])
|
||||||
|
}
|
||||||
|
at++
|
||||||
|
}
|
||||||
|
|
||||||
|
//values_f := append([]interface{}{}, values[0:frmt.operandCount]...)
|
||||||
|
values_f := values[0:frmt.operandCount]
|
||||||
|
values_dbg := values[frmt.operandCount:]
|
||||||
|
if len(values_dbg) > 0 {
|
||||||
|
// Adjust frmt.format:
|
||||||
|
// (%v instead of %s because: frmt.check)
|
||||||
|
{
|
||||||
|
tmp := format
|
||||||
|
if len(tmp) > 0 {
|
||||||
|
if unicode.IsSpace(rune(tmp[len(tmp)-1])) {
|
||||||
|
buffer_f.WriteString("%v")
|
||||||
|
} else {
|
||||||
|
buffer_f.WriteString(" %v")
|
||||||
|
}
|
||||||
|
} else if frmt.check {
|
||||||
|
// Performing a check, so no output
|
||||||
|
} else {
|
||||||
|
buffer_f.WriteString("%v")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust values_f:
|
||||||
|
if !frmt.check {
|
||||||
|
tmp := []string{}
|
||||||
|
for _, value := range values_dbg {
|
||||||
|
tmp = append(tmp, fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
// First, make a copy of values_f, so we avoid overwriting values_dbg when appending
|
||||||
|
values_f = append([]interface{}{}, values_f...)
|
||||||
|
values_f = append(values_f, strings.Join(tmp, " "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format = buffer_f.String()
|
||||||
|
if frmt.check {
|
||||||
|
// We do not actually emit to the log, but panic if
|
||||||
|
// a non-nil value is detected (e.g. a non-nil error)
|
||||||
|
for _, value := range values_dbg {
|
||||||
|
if value != nil {
|
||||||
|
if format == "" {
|
||||||
|
panic(value)
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf(format, append(values_f, value)...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.getEmit().emit(frmt, format, values_f...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Idiot-proof &Dbgr{}, etc.
|
||||||
|
func (self *Dbgr) getEmit() _emit {
|
||||||
|
if self.emit == nil {
|
||||||
|
self.emit = standardEmit()
|
||||||
|
}
|
||||||
|
return self.emit
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput will accept the following as a destination for output:
|
||||||
|
//
|
||||||
|
// *log.Logger Print*/Panic*/Fatal* of the logger
|
||||||
|
// io.Writer -
|
||||||
|
// nil Reset to the default output (os.Stderr)
|
||||||
|
// "log" Print*/Panic*/Fatal* via the "log" package
|
||||||
|
//
|
||||||
|
func (self *Dbgr) SetOutput(output interface{}) {
|
||||||
|
if output == nil {
|
||||||
|
self.emit = standardEmit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch output := output.(type) {
|
||||||
|
case *log.Logger:
|
||||||
|
self.emit = _emitLogger{
|
||||||
|
logger: output,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case io.Writer:
|
||||||
|
self.emit = _emitWriter{
|
||||||
|
writer: output,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case string:
|
||||||
|
if output == "log" {
|
||||||
|
self.emit = _emitLog{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======== //
|
||||||
|
// = emit = //
|
||||||
|
// ======== //
|
||||||
|
|
||||||
|
func standardEmit() _emit {
|
||||||
|
return _emitWriter{
|
||||||
|
writer: os.Stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ln(tmp string) string {
|
||||||
|
length := len(tmp)
|
||||||
|
if length > 0 && tmp[length-1] != '\n' {
|
||||||
|
return tmp + "\n"
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
type _emit interface {
|
||||||
|
emit(_frmt, string, ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type _emitWriter struct {
|
||||||
|
writer io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) {
|
||||||
|
if format == "" {
|
||||||
|
fmt.Fprintln(self.writer, values...)
|
||||||
|
} else {
|
||||||
|
if frmt.panic {
|
||||||
|
panic(fmt.Sprintf(format, values...))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(self.writer, ln(format), values...)
|
||||||
|
if frmt.fatal {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _emitLogger struct {
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) {
|
||||||
|
if format == "" {
|
||||||
|
self.logger.Println(values...)
|
||||||
|
} else {
|
||||||
|
if frmt.panic {
|
||||||
|
self.logger.Panicf(format, values...)
|
||||||
|
} else if frmt.fatal {
|
||||||
|
self.logger.Fatalf(format, values...)
|
||||||
|
} else {
|
||||||
|
self.logger.Printf(format, values...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _emitLog struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) {
|
||||||
|
if format == "" {
|
||||||
|
log.Println(values...)
|
||||||
|
} else {
|
||||||
|
if frmt.panic {
|
||||||
|
log.Panicf(format, values...)
|
||||||
|
} else if frmt.fatal {
|
||||||
|
log.Fatalf(format, values...)
|
||||||
|
} else {
|
||||||
|
log.Printf(format, values...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
253
vendor/github.com/robertkrimen/otto/error.go
generated
vendored
Normal file
253
vendor/github.com/robertkrimen/otto/error.go
generated
vendored
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _exception struct {
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newException(value interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_exception) eject() interface{} {
|
||||||
|
value := self.value
|
||||||
|
self.value = nil // Prevent Go from holding on to the value, whatever it is
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
type _error struct {
|
||||||
|
name string
|
||||||
|
message string
|
||||||
|
trace []_frame
|
||||||
|
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err _error) format() string {
|
||||||
|
if len(err.name) == 0 {
|
||||||
|
return err.message
|
||||||
|
}
|
||||||
|
if len(err.message) == 0 {
|
||||||
|
return err.name
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s: %s", err.name, err.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err _error) formatWithStack() string {
|
||||||
|
str := err.format() + "\n"
|
||||||
|
for _, frame := range err.trace {
|
||||||
|
str += " at " + frame.location() + "\n"
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
type _frame struct {
|
||||||
|
native bool
|
||||||
|
nativeFile string
|
||||||
|
nativeLine int
|
||||||
|
file *file.File
|
||||||
|
offset int
|
||||||
|
callee string
|
||||||
|
fn interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
nativeFrame = _frame{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type _at int
|
||||||
|
|
||||||
|
func (fr _frame) location() string {
|
||||||
|
str := "<unknown>"
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case fr.native:
|
||||||
|
str = "<native code>"
|
||||||
|
if fr.nativeFile != "" && fr.nativeLine != 0 {
|
||||||
|
str = fmt.Sprintf("%s:%d", fr.nativeFile, fr.nativeLine)
|
||||||
|
}
|
||||||
|
case fr.file != nil:
|
||||||
|
if p := fr.file.Position(file.Idx(fr.offset)); p != nil {
|
||||||
|
path, line, column := p.Filename, p.Line, p.Column
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
path = "<anonymous>"
|
||||||
|
}
|
||||||
|
|
||||||
|
str = fmt.Sprintf("%s:%d:%d", path, line, column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fr.callee != "" {
|
||||||
|
str = fmt.Sprintf("%s (%s)", fr.callee, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
|
||||||
|
type Error struct {
|
||||||
|
_error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a description of the error
|
||||||
|
//
|
||||||
|
// TypeError: 'def' is not a function
|
||||||
|
//
|
||||||
|
func (err Error) Error() string {
|
||||||
|
return err.format()
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a description of the error and a trace of where the
|
||||||
|
// error occurred.
|
||||||
|
//
|
||||||
|
// TypeError: 'def' is not a function
|
||||||
|
// at xyz (<anonymous>:3:9)
|
||||||
|
// at <anonymous>:7:1/
|
||||||
|
//
|
||||||
|
func (err Error) String() string {
|
||||||
|
return err.formatWithStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err _error) describe(format string, in ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, in...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _error) messageValue() Value {
|
||||||
|
if self.message == "" {
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
return toValue_string(self.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) typeErrorResult(throw bool) bool {
|
||||||
|
if throw {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newError(rt *_runtime, name string, stackFramesToPop int, in ...interface{}) _error {
|
||||||
|
err := _error{
|
||||||
|
name: name,
|
||||||
|
offset: -1,
|
||||||
|
}
|
||||||
|
description := ""
|
||||||
|
length := len(in)
|
||||||
|
|
||||||
|
if rt != nil && rt.scope != nil {
|
||||||
|
scope := rt.scope
|
||||||
|
|
||||||
|
for i := 0; i < stackFramesToPop; i++ {
|
||||||
|
if scope.outer != nil {
|
||||||
|
scope = scope.outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frame := scope.frame
|
||||||
|
|
||||||
|
if length > 0 {
|
||||||
|
if at, ok := in[length-1].(_at); ok {
|
||||||
|
in = in[0 : length-1]
|
||||||
|
if scope != nil {
|
||||||
|
frame.offset = int(at)
|
||||||
|
}
|
||||||
|
length--
|
||||||
|
}
|
||||||
|
if length > 0 {
|
||||||
|
description, in = in[0].(string), in[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := rt.traceLimit
|
||||||
|
|
||||||
|
err.trace = append(err.trace, frame)
|
||||||
|
if scope != nil {
|
||||||
|
for scope = scope.outer; scope != nil; scope = scope.outer {
|
||||||
|
if limit--; limit == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if scope.frame.offset >= 0 {
|
||||||
|
err.trace = append(err.trace, scope.frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if length > 0 {
|
||||||
|
description, in = in[0].(string), in[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err.message = err.describe(description, in...)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "TypeError", 0, argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "ReferenceError", 0, argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "URIError", 0, argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "SyntaxError", 0, argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception {
|
||||||
|
return &_exception{
|
||||||
|
value: newError(rt, "RangeError", 0, argumentList...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func catchPanic(function func()) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if caught := recover(); caught != nil {
|
||||||
|
if exception, ok := caught.(*_exception); ok {
|
||||||
|
caught = exception.eject()
|
||||||
|
}
|
||||||
|
switch caught := caught.(type) {
|
||||||
|
case *Error:
|
||||||
|
err = caught
|
||||||
|
return
|
||||||
|
case _error:
|
||||||
|
err = &Error{caught}
|
||||||
|
return
|
||||||
|
case Value:
|
||||||
|
if vl := caught._object(); vl != nil {
|
||||||
|
switch vl := vl.value.(type) {
|
||||||
|
case _error:
|
||||||
|
err = &Error{vl}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = errors.New(caught.string())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(caught)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
function()
|
||||||
|
return nil
|
||||||
|
}
|
318
vendor/github.com/robertkrimen/otto/evaluate.go
generated
vendored
Normal file
318
vendor/github.com/robertkrimen/otto/evaluate.go
generated
vendored
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *_runtime) evaluateMultiply(left float64, right float64) Value {
|
||||||
|
// TODO 11.5.1
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) evaluateDivide(left float64, right float64) Value {
|
||||||
|
if math.IsNaN(left) || math.IsNaN(right) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
if math.IsInf(left, 0) && math.IsInf(right, 0) {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
if left == 0 && right == 0 {
|
||||||
|
return NaNValue()
|
||||||
|
}
|
||||||
|
if math.IsInf(left, 0) {
|
||||||
|
if math.Signbit(left) == math.Signbit(right) {
|
||||||
|
return positiveInfinityValue()
|
||||||
|
} else {
|
||||||
|
return negativeInfinityValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if math.IsInf(right, 0) {
|
||||||
|
if math.Signbit(left) == math.Signbit(right) {
|
||||||
|
return positiveZeroValue()
|
||||||
|
} else {
|
||||||
|
return negativeZeroValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if right == 0 {
|
||||||
|
if math.Signbit(left) == math.Signbit(right) {
|
||||||
|
return positiveInfinityValue()
|
||||||
|
} else {
|
||||||
|
return negativeInfinityValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toValue_float64(left / right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) evaluateModulo(left float64, right float64) Value {
|
||||||
|
// TODO 11.5.3
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value {
|
||||||
|
|
||||||
|
leftValue := left.resolve()
|
||||||
|
|
||||||
|
switch operator {
|
||||||
|
|
||||||
|
// Additive
|
||||||
|
case token.PLUS:
|
||||||
|
leftValue = toPrimitive(leftValue)
|
||||||
|
rightValue := right.resolve()
|
||||||
|
rightValue = toPrimitive(rightValue)
|
||||||
|
|
||||||
|
if leftValue.IsString() || rightValue.IsString() {
|
||||||
|
return toValue_string(strings.Join([]string{leftValue.string(), rightValue.string()}, ""))
|
||||||
|
} else {
|
||||||
|
return toValue_float64(leftValue.float64() + rightValue.float64())
|
||||||
|
}
|
||||||
|
case token.MINUS:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_float64(leftValue.float64() - rightValue.float64())
|
||||||
|
|
||||||
|
// Multiplicative
|
||||||
|
case token.MULTIPLY:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_float64(leftValue.float64() * rightValue.float64())
|
||||||
|
case token.SLASH:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return self.evaluateDivide(leftValue.float64(), rightValue.float64())
|
||||||
|
case token.REMAINDER:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_float64(math.Mod(leftValue.float64(), rightValue.float64()))
|
||||||
|
|
||||||
|
// Logical
|
||||||
|
case token.LOGICAL_AND:
|
||||||
|
left := leftValue.bool()
|
||||||
|
if !left {
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
return toValue_bool(right.resolve().bool())
|
||||||
|
case token.LOGICAL_OR:
|
||||||
|
left := leftValue.bool()
|
||||||
|
if left {
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
return toValue_bool(right.resolve().bool())
|
||||||
|
|
||||||
|
// Bitwise
|
||||||
|
case token.AND:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_int32(toInt32(leftValue) & toInt32(rightValue))
|
||||||
|
case token.OR:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_int32(toInt32(leftValue) | toInt32(rightValue))
|
||||||
|
case token.EXCLUSIVE_OR:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_int32(toInt32(leftValue) ^ toInt32(rightValue))
|
||||||
|
|
||||||
|
// Shift
|
||||||
|
// (Masking of 0x1f is to restrict the shift to a maximum of 31 places)
|
||||||
|
case token.SHIFT_LEFT:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f))
|
||||||
|
case token.SHIFT_RIGHT:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f))
|
||||||
|
case token.UNSIGNED_SHIFT_RIGHT:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
// Shifting an unsigned integer is a logical shift
|
||||||
|
return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f))
|
||||||
|
|
||||||
|
case token.INSTANCEOF:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
if !rightValue.IsObject() {
|
||||||
|
panic(self.panicTypeError("Expecting a function in instanceof check, but got: %v", rightValue))
|
||||||
|
}
|
||||||
|
return toValue_bool(rightValue._object().hasInstance(leftValue))
|
||||||
|
|
||||||
|
case token.IN:
|
||||||
|
rightValue := right.resolve()
|
||||||
|
if !rightValue.IsObject() {
|
||||||
|
panic(self.panicTypeError())
|
||||||
|
}
|
||||||
|
return toValue_bool(rightValue._object().hasProperty(leftValue.string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(hereBeDragons(operator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func valueKindDispatchKey(left _valueKind, right _valueKind) int {
|
||||||
|
return (int(left) << 2) + int(right)
|
||||||
|
}
|
||||||
|
|
||||||
|
var equalDispatch map[int](func(Value, Value) bool) = makeEqualDispatch()
|
||||||
|
|
||||||
|
func makeEqualDispatch() map[int](func(Value, Value) bool) {
|
||||||
|
key := valueKindDispatchKey
|
||||||
|
return map[int](func(Value, Value) bool){
|
||||||
|
|
||||||
|
key(valueNumber, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
|
key(valueString, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
|
key(valueObject, valueNumber): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
|
key(valueObject, valueString): func(x Value, y Value) bool { return x.float64() == y.float64() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _lessThanResult int
|
||||||
|
|
||||||
|
const (
|
||||||
|
lessThanFalse _lessThanResult = iota
|
||||||
|
lessThanTrue
|
||||||
|
lessThanUndefined
|
||||||
|
)
|
||||||
|
|
||||||
|
func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult {
|
||||||
|
|
||||||
|
x := Value{}
|
||||||
|
y := x
|
||||||
|
|
||||||
|
if leftFirst {
|
||||||
|
x = toNumberPrimitive(left)
|
||||||
|
y = toNumberPrimitive(right)
|
||||||
|
} else {
|
||||||
|
y = toNumberPrimitive(right)
|
||||||
|
x = toNumberPrimitive(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := false
|
||||||
|
if x.kind != valueString || y.kind != valueString {
|
||||||
|
x, y := x.float64(), y.float64()
|
||||||
|
if math.IsNaN(x) || math.IsNaN(y) {
|
||||||
|
return lessThanUndefined
|
||||||
|
}
|
||||||
|
result = x < y
|
||||||
|
} else {
|
||||||
|
x, y := x.string(), y.string()
|
||||||
|
result = x < y
|
||||||
|
}
|
||||||
|
|
||||||
|
if result {
|
||||||
|
return lessThanTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
return lessThanFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME Probably a map is not the most efficient way to do this
|
||||||
|
var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){
|
||||||
|
// <
|
||||||
|
map[_lessThanResult]bool{
|
||||||
|
lessThanFalse: false,
|
||||||
|
lessThanTrue: true,
|
||||||
|
lessThanUndefined: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// >
|
||||||
|
map[_lessThanResult]bool{
|
||||||
|
lessThanFalse: false,
|
||||||
|
lessThanTrue: true,
|
||||||
|
lessThanUndefined: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// <=
|
||||||
|
map[_lessThanResult]bool{
|
||||||
|
lessThanFalse: true,
|
||||||
|
lessThanTrue: false,
|
||||||
|
lessThanUndefined: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// >=
|
||||||
|
map[_lessThanResult]bool{
|
||||||
|
lessThanFalse: true,
|
||||||
|
lessThanTrue: false,
|
||||||
|
lessThanUndefined: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) calculateComparison(comparator token.Token, left Value, right Value) bool {
|
||||||
|
|
||||||
|
// FIXME Use strictEqualityComparison?
|
||||||
|
// TODO This might be redundant now (with regards to evaluateComparison)
|
||||||
|
x := left.resolve()
|
||||||
|
y := right.resolve()
|
||||||
|
|
||||||
|
kindEqualKind := false
|
||||||
|
result := true
|
||||||
|
negate := false
|
||||||
|
|
||||||
|
switch comparator {
|
||||||
|
case token.LESS:
|
||||||
|
result = lessThanTable[0][calculateLessThan(x, y, true)]
|
||||||
|
case token.GREATER:
|
||||||
|
result = lessThanTable[1][calculateLessThan(y, x, false)]
|
||||||
|
case token.LESS_OR_EQUAL:
|
||||||
|
result = lessThanTable[2][calculateLessThan(y, x, false)]
|
||||||
|
case token.GREATER_OR_EQUAL:
|
||||||
|
result = lessThanTable[3][calculateLessThan(x, y, true)]
|
||||||
|
case token.STRICT_NOT_EQUAL:
|
||||||
|
negate = true
|
||||||
|
fallthrough
|
||||||
|
case token.STRICT_EQUAL:
|
||||||
|
if x.kind != y.kind {
|
||||||
|
result = false
|
||||||
|
} else {
|
||||||
|
kindEqualKind = true
|
||||||
|
}
|
||||||
|
case token.NOT_EQUAL:
|
||||||
|
negate = true
|
||||||
|
fallthrough
|
||||||
|
case token.EQUAL:
|
||||||
|
if x.kind == y.kind {
|
||||||
|
kindEqualKind = true
|
||||||
|
} else if x.kind <= valueNull && y.kind <= valueNull {
|
||||||
|
result = true
|
||||||
|
} else if x.kind <= valueNull || y.kind <= valueNull {
|
||||||
|
result = false
|
||||||
|
} else if x.kind <= valueString && y.kind <= valueString {
|
||||||
|
result = x.float64() == y.float64()
|
||||||
|
} else if x.kind == valueBoolean {
|
||||||
|
result = self.calculateComparison(token.EQUAL, toValue_float64(x.float64()), y)
|
||||||
|
} else if y.kind == valueBoolean {
|
||||||
|
result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.float64()))
|
||||||
|
} else if x.kind == valueObject {
|
||||||
|
result = self.calculateComparison(token.EQUAL, toPrimitive(x), y)
|
||||||
|
} else if y.kind == valueObject {
|
||||||
|
result = self.calculateComparison(token.EQUAL, x, toPrimitive(y))
|
||||||
|
} else {
|
||||||
|
panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("Unknown comparator %s", comparator.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if kindEqualKind {
|
||||||
|
switch x.kind {
|
||||||
|
case valueUndefined, valueNull:
|
||||||
|
result = true
|
||||||
|
case valueNumber:
|
||||||
|
x := x.float64()
|
||||||
|
y := y.float64()
|
||||||
|
if math.IsNaN(x) || math.IsNaN(y) {
|
||||||
|
result = false
|
||||||
|
} else {
|
||||||
|
result = x == y
|
||||||
|
}
|
||||||
|
case valueString:
|
||||||
|
result = x.string() == y.string()
|
||||||
|
case valueBoolean:
|
||||||
|
result = x.bool() == y.bool()
|
||||||
|
case valueObject:
|
||||||
|
result = x._object() == y._object()
|
||||||
|
default:
|
||||||
|
goto ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if negate {
|
||||||
|
result = !result
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
ERROR:
|
||||||
|
panic(hereBeDragons("%v (%v) %s %v (%v)", x, x.kind, comparator, y, y.kind))
|
||||||
|
}
|
110
vendor/github.com/robertkrimen/otto/file/README.markdown
generated
vendored
Normal file
110
vendor/github.com/robertkrimen/otto/file/README.markdown
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
# file
|
||||||
|
--
|
||||||
|
import "github.com/robertkrimen/otto/file"
|
||||||
|
|
||||||
|
Package file encapsulates the file abstractions used by the ast & parser.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
#### type File
|
||||||
|
|
||||||
|
```go
|
||||||
|
type File struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### func NewFile
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewFile(filename, src string, base int) *File
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*File) Base
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (fl *File) Base() int
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*File) Name
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (fl *File) Name() string
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*File) Source
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (fl *File) Source() string
|
||||||
|
```
|
||||||
|
|
||||||
|
#### type FileSet
|
||||||
|
|
||||||
|
```go
|
||||||
|
type FileSet struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A FileSet represents a set of source files.
|
||||||
|
|
||||||
|
#### func (*FileSet) AddFile
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *FileSet) AddFile(filename, src string) int
|
||||||
|
```
|
||||||
|
AddFile adds a new file with the given filename and src.
|
||||||
|
|
||||||
|
This an internal method, but exported for cross-package use.
|
||||||
|
|
||||||
|
#### func (*FileSet) File
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *FileSet) File(idx Idx) *File
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*FileSet) Position
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *FileSet) Position(idx Idx) *Position
|
||||||
|
```
|
||||||
|
Position converts an Idx in the FileSet into a Position.
|
||||||
|
|
||||||
|
#### type Idx
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Idx int
|
||||||
|
```
|
||||||
|
|
||||||
|
Idx is a compact encoding of a source position within a file set. It can be
|
||||||
|
converted into a Position for a more convenient, but much larger,
|
||||||
|
representation.
|
||||||
|
|
||||||
|
#### type Position
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Position struct {
|
||||||
|
Filename string // The filename where the error occurred, if any
|
||||||
|
Offset int // The src offset
|
||||||
|
Line int // The line number, starting at 1
|
||||||
|
Column int // The column number, starting at 1 (The character count)
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Position describes an arbitrary source position including the filename, line,
|
||||||
|
and column location.
|
||||||
|
|
||||||
|
#### func (*Position) String
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *Position) String() string
|
||||||
|
```
|
||||||
|
String returns a string in one of several forms:
|
||||||
|
|
||||||
|
file:line:column A valid position with filename
|
||||||
|
line:column A valid position without filename
|
||||||
|
file An invalid position with filename
|
||||||
|
- An invalid position without filename
|
||||||
|
|
||||||
|
--
|
||||||
|
**godocdown** http://github.com/robertkrimen/godocdown
|
164
vendor/github.com/robertkrimen/otto/file/file.go
generated
vendored
Normal file
164
vendor/github.com/robertkrimen/otto/file/file.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// Package file encapsulates the file abstractions used by the ast & parser.
|
||||||
|
//
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/sourcemap.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Idx is a compact encoding of a source position within a file set.
|
||||||
|
// It can be converted into a Position for a more convenient, but much
|
||||||
|
// larger, representation.
|
||||||
|
type Idx int
|
||||||
|
|
||||||
|
// Position describes an arbitrary source position
|
||||||
|
// including the filename, line, and column location.
|
||||||
|
type Position struct {
|
||||||
|
Filename string // The filename where the error occurred, if any
|
||||||
|
Offset int // The src offset
|
||||||
|
Line int // The line number, starting at 1
|
||||||
|
Column int // The column number, starting at 1 (The character count)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Position is valid if the line number is > 0.
|
||||||
|
|
||||||
|
func (self *Position) isValid() bool {
|
||||||
|
return self.Line > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string in one of several forms:
|
||||||
|
//
|
||||||
|
// file:line:column A valid position with filename
|
||||||
|
// line:column A valid position without filename
|
||||||
|
// file An invalid position with filename
|
||||||
|
// - An invalid position without filename
|
||||||
|
//
|
||||||
|
func (self *Position) String() string {
|
||||||
|
str := self.Filename
|
||||||
|
if self.isValid() {
|
||||||
|
if str != "" {
|
||||||
|
str += ":"
|
||||||
|
}
|
||||||
|
str += fmt.Sprintf("%d:%d", self.Line, self.Column)
|
||||||
|
}
|
||||||
|
if str == "" {
|
||||||
|
str = "-"
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileSet
|
||||||
|
|
||||||
|
// A FileSet represents a set of source files.
|
||||||
|
type FileSet struct {
|
||||||
|
files []*File
|
||||||
|
last *File
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFile adds a new file with the given filename and src.
|
||||||
|
//
|
||||||
|
// This an internal method, but exported for cross-package use.
|
||||||
|
func (self *FileSet) AddFile(filename, src string) int {
|
||||||
|
base := self.nextBase()
|
||||||
|
file := &File{
|
||||||
|
name: filename,
|
||||||
|
src: src,
|
||||||
|
base: base,
|
||||||
|
}
|
||||||
|
self.files = append(self.files, file)
|
||||||
|
self.last = file
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FileSet) nextBase() int {
|
||||||
|
if self.last == nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return self.last.base + len(self.last.src) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FileSet) File(idx Idx) *File {
|
||||||
|
for _, file := range self.files {
|
||||||
|
if idx <= Idx(file.base+len(file.src)) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position converts an Idx in the FileSet into a Position.
|
||||||
|
func (self *FileSet) Position(idx Idx) *Position {
|
||||||
|
for _, file := range self.files {
|
||||||
|
if idx <= Idx(file.base+len(file.src)) {
|
||||||
|
return file.Position(idx - Idx(file.base))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
name string
|
||||||
|
src string
|
||||||
|
base int // This will always be 1 or greater
|
||||||
|
sm *sourcemap.Consumer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFile(filename, src string, base int) *File {
|
||||||
|
return &File{
|
||||||
|
name: filename,
|
||||||
|
src: src,
|
||||||
|
base: base,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *File) WithSourceMap(sm *sourcemap.Consumer) *File {
|
||||||
|
fl.sm = sm
|
||||||
|
return fl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *File) Name() string {
|
||||||
|
return fl.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *File) Source() string {
|
||||||
|
return fl.src
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *File) Base() int {
|
||||||
|
return fl.base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *File) Position(idx Idx) *Position {
|
||||||
|
position := &Position{}
|
||||||
|
|
||||||
|
offset := int(idx) - fl.base
|
||||||
|
|
||||||
|
if offset >= len(fl.src) || offset < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
src := fl.src[:offset]
|
||||||
|
|
||||||
|
position.Filename = fl.name
|
||||||
|
position.Offset = offset
|
||||||
|
position.Line = strings.Count(src, "\n") + 1
|
||||||
|
|
||||||
|
if index := strings.LastIndex(src, "\n"); index >= 0 {
|
||||||
|
position.Column = offset - index
|
||||||
|
} else {
|
||||||
|
position.Column = len(src) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if fl.sm != nil {
|
||||||
|
if f, _, l, c, ok := fl.sm.Source(position.Line, position.Column); ok {
|
||||||
|
position.Filename, position.Line, position.Column = f, l, c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return position
|
||||||
|
}
|
221
vendor/github.com/robertkrimen/otto/global.go
generated
vendored
Normal file
221
vendor/github.com/robertkrimen/otto/global.go
generated
vendored
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
prototypeValueObject = interface{}(nil)
|
||||||
|
prototypeValueFunction = _nativeFunctionObject{
|
||||||
|
call: func(_ FunctionCall) Value {
|
||||||
|
return Value{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
prototypeValueString = _stringASCII("")
|
||||||
|
// TODO Make this just false?
|
||||||
|
prototypeValueBoolean = Value{
|
||||||
|
kind: valueBoolean,
|
||||||
|
value: false,
|
||||||
|
}
|
||||||
|
prototypeValueNumber = Value{
|
||||||
|
kind: valueNumber,
|
||||||
|
value: 0,
|
||||||
|
}
|
||||||
|
prototypeValueDate = _dateObject{
|
||||||
|
epoch: 0,
|
||||||
|
isNaN: false,
|
||||||
|
time: time.Unix(0, 0).UTC(),
|
||||||
|
value: Value{
|
||||||
|
kind: valueNumber,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
prototypeValueRegExp = _regExpObject{
|
||||||
|
regularExpression: nil,
|
||||||
|
global: false,
|
||||||
|
ignoreCase: false,
|
||||||
|
multiline: false,
|
||||||
|
source: "",
|
||||||
|
flags: "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newContext() *_runtime {
|
||||||
|
|
||||||
|
self := &_runtime{}
|
||||||
|
|
||||||
|
self.globalStash = self.newObjectStash(nil, nil)
|
||||||
|
self.globalObject = self.globalStash.object
|
||||||
|
|
||||||
|
_newContext(self)
|
||||||
|
|
||||||
|
self.eval = self.globalObject.property["eval"].value.(Value).value.(*_object)
|
||||||
|
self.globalObject.prototype = self.global.ObjectPrototype
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newBaseObject() *_object {
|
||||||
|
self := newObject(runtime, "")
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newClassObject(class string) *_object {
|
||||||
|
return newObject(runtime, class)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newPrimitiveObject(class string, value Value) *_object {
|
||||||
|
self := runtime.newClassObject(class)
|
||||||
|
self.value = value
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) primitiveValue() Value {
|
||||||
|
switch value := self.value.(type) {
|
||||||
|
case Value:
|
||||||
|
return value
|
||||||
|
case _stringObject:
|
||||||
|
return toValue_string(value.String())
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) hasPrimitive() bool {
|
||||||
|
switch self.value.(type) {
|
||||||
|
case Value, _stringObject:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newObject() *_object {
|
||||||
|
self := runtime.newClassObject("Object")
|
||||||
|
self.prototype = runtime.global.ObjectPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newArray(length uint32) *_object {
|
||||||
|
self := runtime.newArrayObject(length)
|
||||||
|
self.prototype = runtime.global.ArrayPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newArrayOf(valueArray []Value) *_object {
|
||||||
|
self := runtime.newArray(uint32(len(valueArray)))
|
||||||
|
for index, value := range valueArray {
|
||||||
|
if value.isEmpty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
self.defineProperty(strconv.FormatInt(int64(index), 10), value, 0111, false)
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newString(value Value) *_object {
|
||||||
|
self := runtime.newStringObject(value)
|
||||||
|
self.prototype = runtime.global.StringPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newBoolean(value Value) *_object {
|
||||||
|
self := runtime.newBooleanObject(value)
|
||||||
|
self.prototype = runtime.global.BooleanPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newNumber(value Value) *_object {
|
||||||
|
self := runtime.newNumberObject(value)
|
||||||
|
self.prototype = runtime.global.NumberPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newRegExp(patternValue Value, flagsValue Value) *_object {
|
||||||
|
|
||||||
|
pattern := ""
|
||||||
|
flags := ""
|
||||||
|
if object := patternValue._object(); object != nil && object.class == "RegExp" {
|
||||||
|
if flagsValue.IsDefined() {
|
||||||
|
panic(runtime.panicTypeError("Cannot supply flags when constructing one RegExp from another"))
|
||||||
|
}
|
||||||
|
regExp := object.regExpValue()
|
||||||
|
pattern = regExp.source
|
||||||
|
flags = regExp.flags
|
||||||
|
} else {
|
||||||
|
if patternValue.IsDefined() {
|
||||||
|
pattern = patternValue.string()
|
||||||
|
}
|
||||||
|
if flagsValue.IsDefined() {
|
||||||
|
flags = flagsValue.string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtime._newRegExp(pattern, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) _newRegExp(pattern string, flags string) *_object {
|
||||||
|
self := runtime.newRegExpObject(pattern, flags)
|
||||||
|
self.prototype = runtime.global.RegExpPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Should (probably) be one argument, right? This is redundant
|
||||||
|
func (runtime *_runtime) newDate(epoch float64) *_object {
|
||||||
|
self := runtime.newDateObject(epoch)
|
||||||
|
self.prototype = runtime.global.DatePrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newError(name string, message Value, stackFramesToPop int) *_object {
|
||||||
|
var self *_object
|
||||||
|
switch name {
|
||||||
|
case "EvalError":
|
||||||
|
return runtime.newEvalError(message)
|
||||||
|
case "TypeError":
|
||||||
|
return runtime.newTypeError(message)
|
||||||
|
case "RangeError":
|
||||||
|
return runtime.newRangeError(message)
|
||||||
|
case "ReferenceError":
|
||||||
|
return runtime.newReferenceError(message)
|
||||||
|
case "SyntaxError":
|
||||||
|
return runtime.newSyntaxError(message)
|
||||||
|
case "URIError":
|
||||||
|
return runtime.newURIError(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
self = runtime.newErrorObject(name, message, stackFramesToPop)
|
||||||
|
self.prototype = runtime.global.ErrorPrototype
|
||||||
|
if name != "" {
|
||||||
|
self.defineProperty("name", toValue_string(name), 0111, false)
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newNativeFunction(name, file string, line int, _nativeFunction _nativeFunction) *_object {
|
||||||
|
self := runtime.newNativeFunctionObject(name, file, line, _nativeFunction, 0)
|
||||||
|
self.prototype = runtime.global.FunctionPrototype
|
||||||
|
prototype := runtime.newObject()
|
||||||
|
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
||||||
|
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newNodeFunction(node *_nodeFunctionLiteral, scopeEnvironment _stash) *_object {
|
||||||
|
// TODO Implement 13.2 fully
|
||||||
|
self := runtime.newNodeFunctionObject(node, scopeEnvironment)
|
||||||
|
self.prototype = runtime.global.FunctionPrototype
|
||||||
|
prototype := runtime.newObject()
|
||||||
|
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
||||||
|
prototype.defineProperty("constructor", toValue_object(self), 0101, false)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME Only in one place...
|
||||||
|
func (runtime *_runtime) newBoundFunction(target *_object, this Value, argumentList []Value) *_object {
|
||||||
|
self := runtime.newBoundFunctionObject(target, this, argumentList)
|
||||||
|
self.prototype = runtime.global.FunctionPrototype
|
||||||
|
prototype := runtime.newObject()
|
||||||
|
self.defineProperty("prototype", toValue_object(prototype), 0100, false)
|
||||||
|
prototype.defineProperty("constructor", toValue_object(self), 0100, false)
|
||||||
|
return self
|
||||||
|
}
|
6680
vendor/github.com/robertkrimen/otto/inline.go
generated
vendored
Normal file
6680
vendor/github.com/robertkrimen/otto/inline.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1087
vendor/github.com/robertkrimen/otto/inline.pl
generated
vendored
Normal file
1087
vendor/github.com/robertkrimen/otto/inline.pl
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
156
vendor/github.com/robertkrimen/otto/object.go
generated
vendored
Normal file
156
vendor/github.com/robertkrimen/otto/object.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
type _object struct {
|
||||||
|
runtime *_runtime
|
||||||
|
|
||||||
|
class string
|
||||||
|
objectClass *_objectClass
|
||||||
|
value interface{}
|
||||||
|
|
||||||
|
prototype *_object
|
||||||
|
extensible bool
|
||||||
|
|
||||||
|
property map[string]_property
|
||||||
|
propertyOrder []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newObject(runtime *_runtime, class string) *_object {
|
||||||
|
self := &_object{
|
||||||
|
runtime: runtime,
|
||||||
|
class: class,
|
||||||
|
objectClass: _classObject,
|
||||||
|
property: make(map[string]_property),
|
||||||
|
extensible: true,
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12
|
||||||
|
|
||||||
|
// 8.12.1
|
||||||
|
func (self *_object) getOwnProperty(name string) *_property {
|
||||||
|
return self.objectClass.getOwnProperty(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.2
|
||||||
|
func (self *_object) getProperty(name string) *_property {
|
||||||
|
return self.objectClass.getProperty(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.3
|
||||||
|
func (self *_object) get(name string) Value {
|
||||||
|
return self.objectClass.get(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.4
|
||||||
|
func (self *_object) canPut(name string) bool {
|
||||||
|
return self.objectClass.canPut(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.5
|
||||||
|
func (self *_object) put(name string, value Value, throw bool) {
|
||||||
|
self.objectClass.put(self, name, value, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.6
|
||||||
|
func (self *_object) hasProperty(name string) bool {
|
||||||
|
return self.objectClass.hasProperty(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) hasOwnProperty(name string) bool {
|
||||||
|
return self.objectClass.hasOwnProperty(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type _defaultValueHint int
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultValueNoHint _defaultValueHint = iota
|
||||||
|
defaultValueHintString
|
||||||
|
defaultValueHintNumber
|
||||||
|
)
|
||||||
|
|
||||||
|
// 8.12.8
|
||||||
|
func (self *_object) DefaultValue(hint _defaultValueHint) Value {
|
||||||
|
if hint == defaultValueNoHint {
|
||||||
|
if self.class == "Date" {
|
||||||
|
// Date exception
|
||||||
|
hint = defaultValueHintString
|
||||||
|
} else {
|
||||||
|
hint = defaultValueHintNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodSequence := []string{"valueOf", "toString"}
|
||||||
|
if hint == defaultValueHintString {
|
||||||
|
methodSequence = []string{"toString", "valueOf"}
|
||||||
|
}
|
||||||
|
for _, methodName := range methodSequence {
|
||||||
|
method := self.get(methodName)
|
||||||
|
// FIXME This is redundant...
|
||||||
|
if method.isCallable() {
|
||||||
|
result := method._object().call(toValue_object(self), nil, false, nativeFrame)
|
||||||
|
if result.IsPrimitive() {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) String() string {
|
||||||
|
return self.DefaultValue(defaultValueHintString).string()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) defineProperty(name string, value Value, mode _propertyMode, throw bool) bool {
|
||||||
|
return self.defineOwnProperty(name, _property{value, mode}, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.9
|
||||||
|
func (self *_object) defineOwnProperty(name string, descriptor _property, throw bool) bool {
|
||||||
|
return self.objectClass.defineOwnProperty(self, name, descriptor, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) delete(name string, throw bool) bool {
|
||||||
|
return self.objectClass.delete(self, name, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) enumerate(all bool, each func(string) bool) {
|
||||||
|
self.objectClass.enumerate(self, all, each)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) _exists(name string) bool {
|
||||||
|
_, exists := self.property[name]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) _read(name string) (_property, bool) {
|
||||||
|
property, exists := self.property[name]
|
||||||
|
return property, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) _write(name string, value interface{}, mode _propertyMode) {
|
||||||
|
if value == nil {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
|
_, exists := self.property[name]
|
||||||
|
self.property[name] = _property{value, mode}
|
||||||
|
if !exists {
|
||||||
|
self.propertyOrder = append(self.propertyOrder, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) _delete(name string) {
|
||||||
|
_, exists := self.property[name]
|
||||||
|
delete(self.property, name)
|
||||||
|
if exists {
|
||||||
|
for index, property := range self.propertyOrder {
|
||||||
|
if name == property {
|
||||||
|
if index == len(self.propertyOrder)-1 {
|
||||||
|
self.propertyOrder = self.propertyOrder[:index]
|
||||||
|
} else {
|
||||||
|
self.propertyOrder = append(self.propertyOrder[:index], self.propertyOrder[index+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
493
vendor/github.com/robertkrimen/otto/object_class.go
generated
vendored
Normal file
493
vendor/github.com/robertkrimen/otto/object_class.go
generated
vendored
Normal file
|
@ -0,0 +1,493 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _objectClass struct {
|
||||||
|
getOwnProperty func(*_object, string) *_property
|
||||||
|
getProperty func(*_object, string) *_property
|
||||||
|
get func(*_object, string) Value
|
||||||
|
canPut func(*_object, string) bool
|
||||||
|
put func(*_object, string, Value, bool)
|
||||||
|
hasProperty func(*_object, string) bool
|
||||||
|
hasOwnProperty func(*_object, string) bool
|
||||||
|
defineOwnProperty func(*_object, string, _property, bool) bool
|
||||||
|
delete func(*_object, string, bool) bool
|
||||||
|
enumerate func(*_object, bool, func(string) bool)
|
||||||
|
clone func(*_object, *_object, *_clone) *_object
|
||||||
|
marshalJSON func(*_object) json.Marshaler
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectEnumerate(self *_object, all bool, each func(string) bool) {
|
||||||
|
for _, name := range self.propertyOrder {
|
||||||
|
if all || self.property[name].enumerable() {
|
||||||
|
if !each(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_classObject,
|
||||||
|
_classArray,
|
||||||
|
_classString,
|
||||||
|
_classArguments,
|
||||||
|
_classGoStruct,
|
||||||
|
_classGoMap,
|
||||||
|
_classGoArray,
|
||||||
|
_classGoSlice,
|
||||||
|
_ *_objectClass
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_classObject = &_objectClass{
|
||||||
|
objectGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
objectGet,
|
||||||
|
objectCanPut,
|
||||||
|
objectPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
objectDefineOwnProperty,
|
||||||
|
objectDelete,
|
||||||
|
objectEnumerate,
|
||||||
|
objectClone,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_classArray = &_objectClass{
|
||||||
|
objectGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
objectGet,
|
||||||
|
objectCanPut,
|
||||||
|
objectPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
arrayDefineOwnProperty,
|
||||||
|
objectDelete,
|
||||||
|
objectEnumerate,
|
||||||
|
objectClone,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_classString = &_objectClass{
|
||||||
|
stringGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
objectGet,
|
||||||
|
objectCanPut,
|
||||||
|
objectPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
objectDefineOwnProperty,
|
||||||
|
objectDelete,
|
||||||
|
stringEnumerate,
|
||||||
|
objectClone,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_classArguments = &_objectClass{
|
||||||
|
argumentsGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
argumentsGet,
|
||||||
|
objectCanPut,
|
||||||
|
objectPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
argumentsDefineOwnProperty,
|
||||||
|
argumentsDelete,
|
||||||
|
objectEnumerate,
|
||||||
|
objectClone,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_classGoStruct = &_objectClass{
|
||||||
|
goStructGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
objectGet,
|
||||||
|
goStructCanPut,
|
||||||
|
goStructPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
objectDefineOwnProperty,
|
||||||
|
objectDelete,
|
||||||
|
goStructEnumerate,
|
||||||
|
objectClone,
|
||||||
|
goStructMarshalJSON,
|
||||||
|
}
|
||||||
|
|
||||||
|
_classGoMap = &_objectClass{
|
||||||
|
goMapGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
objectGet,
|
||||||
|
objectCanPut,
|
||||||
|
objectPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
goMapDefineOwnProperty,
|
||||||
|
goMapDelete,
|
||||||
|
goMapEnumerate,
|
||||||
|
objectClone,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_classGoArray = &_objectClass{
|
||||||
|
goArrayGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
objectGet,
|
||||||
|
objectCanPut,
|
||||||
|
objectPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
goArrayDefineOwnProperty,
|
||||||
|
goArrayDelete,
|
||||||
|
goArrayEnumerate,
|
||||||
|
objectClone,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_classGoSlice = &_objectClass{
|
||||||
|
goSliceGetOwnProperty,
|
||||||
|
objectGetProperty,
|
||||||
|
objectGet,
|
||||||
|
objectCanPut,
|
||||||
|
objectPut,
|
||||||
|
objectHasProperty,
|
||||||
|
objectHasOwnProperty,
|
||||||
|
goSliceDefineOwnProperty,
|
||||||
|
goSliceDelete,
|
||||||
|
goSliceEnumerate,
|
||||||
|
objectClone,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allons-y
|
||||||
|
|
||||||
|
// 8.12.1
|
||||||
|
func objectGetOwnProperty(self *_object, name string) *_property {
|
||||||
|
// Return a _copy_ of the property
|
||||||
|
property, exists := self._read(name)
|
||||||
|
if !exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &property
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.2
|
||||||
|
func objectGetProperty(self *_object, name string) *_property {
|
||||||
|
property := self.getOwnProperty(name)
|
||||||
|
if property != nil {
|
||||||
|
return property
|
||||||
|
}
|
||||||
|
if self.prototype != nil {
|
||||||
|
return self.prototype.getProperty(name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.3
|
||||||
|
func objectGet(self *_object, name string) Value {
|
||||||
|
property := self.getProperty(name)
|
||||||
|
if property != nil {
|
||||||
|
return property.get(self)
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.4
|
||||||
|
func objectCanPut(self *_object, name string) bool {
|
||||||
|
canPut, _, _ := _objectCanPut(self, name)
|
||||||
|
return canPut
|
||||||
|
}
|
||||||
|
|
||||||
|
func _objectCanPut(self *_object, name string) (canPut bool, property *_property, setter *_object) {
|
||||||
|
property = self.getOwnProperty(name)
|
||||||
|
if property != nil {
|
||||||
|
switch propertyValue := property.value.(type) {
|
||||||
|
case Value:
|
||||||
|
canPut = property.writable()
|
||||||
|
return
|
||||||
|
case _propertyGetSet:
|
||||||
|
setter = propertyValue[1]
|
||||||
|
canPut = setter != nil
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.prototype == nil {
|
||||||
|
return self.extensible, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
property = self.prototype.getProperty(name)
|
||||||
|
if property == nil {
|
||||||
|
return self.extensible, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch propertyValue := property.value.(type) {
|
||||||
|
case Value:
|
||||||
|
if !self.extensible {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
return property.writable(), nil, nil
|
||||||
|
case _propertyGetSet:
|
||||||
|
setter = propertyValue[1]
|
||||||
|
canPut = setter != nil
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.5
|
||||||
|
func objectPut(self *_object, name string, value Value, throw bool) {
|
||||||
|
|
||||||
|
if true {
|
||||||
|
// Shortcut...
|
||||||
|
//
|
||||||
|
// So, right now, every class is using objectCanPut and every class
|
||||||
|
// is using objectPut.
|
||||||
|
//
|
||||||
|
// If that were to no longer be the case, we would have to have
|
||||||
|
// something to detect that here, so that we do not use an
|
||||||
|
// incompatible canPut routine
|
||||||
|
canPut, property, setter := _objectCanPut(self, name)
|
||||||
|
if !canPut {
|
||||||
|
self.runtime.typeErrorResult(throw)
|
||||||
|
} else if setter != nil {
|
||||||
|
setter.call(toValue(self), []Value{value}, false, nativeFrame)
|
||||||
|
} else if property != nil {
|
||||||
|
property.value = value
|
||||||
|
self.defineOwnProperty(name, *property, throw)
|
||||||
|
} else {
|
||||||
|
self.defineProperty(name, value, 0111, throw)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The long way...
|
||||||
|
//
|
||||||
|
// Right now, code should never get here, see above
|
||||||
|
if !self.canPut(name) {
|
||||||
|
self.runtime.typeErrorResult(throw)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
property := self.getOwnProperty(name)
|
||||||
|
if property == nil {
|
||||||
|
property = self.getProperty(name)
|
||||||
|
if property != nil {
|
||||||
|
if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor {
|
||||||
|
getSet[1].call(toValue(self), []Value{value}, false, nativeFrame)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.defineProperty(name, value, 0111, throw)
|
||||||
|
} else {
|
||||||
|
switch propertyValue := property.value.(type) {
|
||||||
|
case Value:
|
||||||
|
property.value = value
|
||||||
|
self.defineOwnProperty(name, *property, throw)
|
||||||
|
case _propertyGetSet:
|
||||||
|
if propertyValue[1] != nil {
|
||||||
|
propertyValue[1].call(toValue(self), []Value{value}, false, nativeFrame)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if throw {
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.6
|
||||||
|
func objectHasProperty(self *_object, name string) bool {
|
||||||
|
return self.getProperty(name) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectHasOwnProperty(self *_object, name string) bool {
|
||||||
|
return self.getOwnProperty(name) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.12.9
|
||||||
|
func objectDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
|
||||||
|
property, exists := self._read(name)
|
||||||
|
{
|
||||||
|
if !exists {
|
||||||
|
if !self.extensible {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor {
|
||||||
|
if newGetSet[0] == &_nilGetSetObject {
|
||||||
|
newGetSet[0] = nil
|
||||||
|
}
|
||||||
|
if newGetSet[1] == &_nilGetSetObject {
|
||||||
|
newGetSet[1] = nil
|
||||||
|
}
|
||||||
|
descriptor.value = newGetSet
|
||||||
|
}
|
||||||
|
self._write(name, descriptor.value, descriptor.mode)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if descriptor.isEmpty() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Per 8.12.9.6 - We should shortcut here (returning true) if
|
||||||
|
// the current and new (define) properties are the same
|
||||||
|
|
||||||
|
configurable := property.configurable()
|
||||||
|
if !configurable {
|
||||||
|
if descriptor.configurable() {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
// Test that, if enumerable is set on the property descriptor, then it should
|
||||||
|
// be the same as the existing property
|
||||||
|
if descriptor.enumerateSet() && descriptor.enumerable() != property.enumerable() {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value, isDataDescriptor := property.value.(Value)
|
||||||
|
getSet, _ := property.value.(_propertyGetSet)
|
||||||
|
if descriptor.isGenericDescriptor() {
|
||||||
|
// GenericDescriptor
|
||||||
|
} else if isDataDescriptor != descriptor.isDataDescriptor() {
|
||||||
|
// DataDescriptor <=> AccessorDescriptor
|
||||||
|
if !configurable {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
} else if isDataDescriptor && descriptor.isDataDescriptor() {
|
||||||
|
// DataDescriptor <=> DataDescriptor
|
||||||
|
if !configurable {
|
||||||
|
if !property.writable() && descriptor.writable() {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
if !property.writable() {
|
||||||
|
if descriptor.value != nil && !sameValue(value, descriptor.value.(Value)) {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// AccessorDescriptor <=> AccessorDescriptor
|
||||||
|
newGetSet, _ := descriptor.value.(_propertyGetSet)
|
||||||
|
presentGet, presentSet := true, true
|
||||||
|
if newGetSet[0] == &_nilGetSetObject {
|
||||||
|
// Present, but nil
|
||||||
|
newGetSet[0] = nil
|
||||||
|
} else if newGetSet[0] == nil {
|
||||||
|
// Missing, not even nil
|
||||||
|
newGetSet[0] = getSet[0]
|
||||||
|
presentGet = false
|
||||||
|
}
|
||||||
|
if newGetSet[1] == &_nilGetSetObject {
|
||||||
|
// Present, but nil
|
||||||
|
newGetSet[1] = nil
|
||||||
|
} else if newGetSet[1] == nil {
|
||||||
|
// Missing, not even nil
|
||||||
|
newGetSet[1] = getSet[1]
|
||||||
|
presentSet = false
|
||||||
|
}
|
||||||
|
if !configurable {
|
||||||
|
if (presentGet && (getSet[0] != newGetSet[0])) || (presentSet && (getSet[1] != newGetSet[1])) {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
descriptor.value = newGetSet
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// This section will preserve attributes of
|
||||||
|
// the original property, if necessary
|
||||||
|
value1 := descriptor.value
|
||||||
|
if value1 == nil {
|
||||||
|
value1 = property.value
|
||||||
|
} else if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor {
|
||||||
|
if newGetSet[0] == &_nilGetSetObject {
|
||||||
|
newGetSet[0] = nil
|
||||||
|
}
|
||||||
|
if newGetSet[1] == &_nilGetSetObject {
|
||||||
|
newGetSet[1] = nil
|
||||||
|
}
|
||||||
|
value1 = newGetSet
|
||||||
|
}
|
||||||
|
mode1 := descriptor.mode
|
||||||
|
if mode1&0222 != 0 {
|
||||||
|
// TODO Factor this out into somewhere testable
|
||||||
|
// (Maybe put into switch ...)
|
||||||
|
mode0 := property.mode
|
||||||
|
if mode1&0200 != 0 {
|
||||||
|
if descriptor.isDataDescriptor() {
|
||||||
|
mode1 &= ^0200 // Turn off "writable" missing
|
||||||
|
mode1 |= (mode0 & 0100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mode1&020 != 0 {
|
||||||
|
mode1 |= (mode0 & 010)
|
||||||
|
}
|
||||||
|
if mode1&02 != 0 {
|
||||||
|
mode1 |= (mode0 & 01)
|
||||||
|
}
|
||||||
|
mode1 &= 0311 // 0311 to preserve the non-setting on "writable"
|
||||||
|
}
|
||||||
|
self._write(name, value1, mode1)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
Reject:
|
||||||
|
if throw {
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectDelete(self *_object, name string, throw bool) bool {
|
||||||
|
property_ := self.getOwnProperty(name)
|
||||||
|
if property_ == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if property_.configurable() {
|
||||||
|
self._delete(name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectClone(in *_object, out *_object, clone *_clone) *_object {
|
||||||
|
*out = *in
|
||||||
|
|
||||||
|
out.runtime = clone.runtime
|
||||||
|
if out.prototype != nil {
|
||||||
|
out.prototype = clone.object(in.prototype)
|
||||||
|
}
|
||||||
|
out.property = make(map[string]_property, len(in.property))
|
||||||
|
out.propertyOrder = make([]string, len(in.propertyOrder))
|
||||||
|
copy(out.propertyOrder, in.propertyOrder)
|
||||||
|
for index, property := range in.property {
|
||||||
|
out.property[index] = clone.property(property)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value := in.value.(type) {
|
||||||
|
case _nativeFunctionObject:
|
||||||
|
out.value = value
|
||||||
|
case _bindFunctionObject:
|
||||||
|
out.value = _bindFunctionObject{
|
||||||
|
target: clone.object(value.target),
|
||||||
|
this: clone.value(value.this),
|
||||||
|
argumentList: clone.valueArray(value.argumentList),
|
||||||
|
}
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
out.value = _nodeFunctionObject{
|
||||||
|
node: value.node,
|
||||||
|
stash: clone.stash(value.stash),
|
||||||
|
}
|
||||||
|
case _argumentsObject:
|
||||||
|
out.value = value.clone(clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
770
vendor/github.com/robertkrimen/otto/otto.go
generated
vendored
Normal file
770
vendor/github.com/robertkrimen/otto/otto.go
generated
vendored
Normal file
|
@ -0,0 +1,770 @@
|
||||||
|
/*
|
||||||
|
Package otto is a JavaScript parser and interpreter written natively in Go.
|
||||||
|
|
||||||
|
http://godoc.org/github.com/robertkrimen/otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
)
|
||||||
|
|
||||||
|
Run something in the VM
|
||||||
|
|
||||||
|
vm := otto.New()
|
||||||
|
vm.Run(`
|
||||||
|
abc = 2 + 2;
|
||||||
|
console.log("The value of abc is " + abc); // 4
|
||||||
|
`)
|
||||||
|
|
||||||
|
Get a value out of the VM
|
||||||
|
|
||||||
|
value, err := vm.Get("abc")
|
||||||
|
value, _ := value.ToInteger()
|
||||||
|
}
|
||||||
|
|
||||||
|
Set a number
|
||||||
|
|
||||||
|
vm.Set("def", 11)
|
||||||
|
vm.Run(`
|
||||||
|
console.log("The value of def is " + def);
|
||||||
|
// The value of def is 11
|
||||||
|
`)
|
||||||
|
|
||||||
|
Set a string
|
||||||
|
|
||||||
|
vm.Set("xyzzy", "Nothing happens.")
|
||||||
|
vm.Run(`
|
||||||
|
console.log(xyzzy.length); // 16
|
||||||
|
`)
|
||||||
|
|
||||||
|
Get the value of an expression
|
||||||
|
|
||||||
|
value, _ = vm.Run("xyzzy.length")
|
||||||
|
{
|
||||||
|
// value is an int64 with a value of 16
|
||||||
|
value, _ := value.ToInteger()
|
||||||
|
}
|
||||||
|
|
||||||
|
An error happens
|
||||||
|
|
||||||
|
value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length")
|
||||||
|
if err != nil {
|
||||||
|
// err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined
|
||||||
|
// If there is an error, then value.IsUndefined() is true
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Set a Go function
|
||||||
|
|
||||||
|
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
|
||||||
|
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
|
||||||
|
return otto.Value{}
|
||||||
|
})
|
||||||
|
|
||||||
|
Set a Go function that returns something useful
|
||||||
|
|
||||||
|
vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value {
|
||||||
|
right, _ := call.Argument(0).ToInteger()
|
||||||
|
result, _ := vm.ToValue(2 + right)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
Use the functions in JavaScript
|
||||||
|
|
||||||
|
result, _ = vm.Run(`
|
||||||
|
sayHello("Xyzzy"); // Hello, Xyzzy.
|
||||||
|
sayHello(); // Hello, undefined
|
||||||
|
|
||||||
|
result = twoPlus(2.0); // 4
|
||||||
|
`)
|
||||||
|
|
||||||
|
Parser
|
||||||
|
|
||||||
|
A separate parser is available in the parser package if you're just interested in building an AST.
|
||||||
|
|
||||||
|
http://godoc.org/github.com/robertkrimen/otto/parser
|
||||||
|
|
||||||
|
Parse and return an AST
|
||||||
|
|
||||||
|
filename := "" // A filename is optional
|
||||||
|
src := `
|
||||||
|
// Sample xyzzy example
|
||||||
|
(function(){
|
||||||
|
if (3.14159 > 0) {
|
||||||
|
console.log("Hello, World.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xyzzy = NaN;
|
||||||
|
console.log("Nothing happens.");
|
||||||
|
return xyzzy;
|
||||||
|
})();
|
||||||
|
`
|
||||||
|
|
||||||
|
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
||||||
|
program, err := parser.ParseFile(nil, filename, src, 0)
|
||||||
|
|
||||||
|
otto
|
||||||
|
|
||||||
|
You can run (Go) JavaScript from the commandline with: http://github.com/robertkrimen/otto/tree/master/otto
|
||||||
|
|
||||||
|
$ go get -v github.com/robertkrimen/otto/otto
|
||||||
|
|
||||||
|
Run JavaScript by entering some source on stdin or by giving otto a filename:
|
||||||
|
|
||||||
|
$ otto example.js
|
||||||
|
|
||||||
|
underscore
|
||||||
|
|
||||||
|
Optionally include the JavaScript utility-belt library, underscore, with this import:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
_ "github.com/robertkrimen/otto/underscore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now every otto runtime will come loaded with underscore
|
||||||
|
|
||||||
|
For more information: http://github.com/robertkrimen/otto/tree/master/underscore
|
||||||
|
|
||||||
|
Caveat Emptor
|
||||||
|
|
||||||
|
The following are some limitations with otto:
|
||||||
|
|
||||||
|
* "use strict" will parse, but does nothing.
|
||||||
|
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
|
||||||
|
* Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported.
|
||||||
|
|
||||||
|
Regular Expression Incompatibility
|
||||||
|
|
||||||
|
Go translates JavaScript-style regular expressions into something that is "regexp" compatible via `parser.TransformRegExp`.
|
||||||
|
Unfortunately, RegExp requires backtracking for some patterns, and backtracking is not supported by the standard Go engine: https://code.google.com/p/re2/wiki/Syntax
|
||||||
|
|
||||||
|
Therefore, the following syntax is incompatible:
|
||||||
|
|
||||||
|
(?=) // Lookahead (positive), currently a parsing error
|
||||||
|
(?!) // Lookahead (backhead), currently a parsing error
|
||||||
|
\1 // Backreference (\1, \2, \3, ...), currently a parsing error
|
||||||
|
|
||||||
|
A brief discussion of these limitations: "Regexp (?!re)" https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E
|
||||||
|
|
||||||
|
More information about re2: https://code.google.com/p/re2/
|
||||||
|
|
||||||
|
In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ].
|
||||||
|
The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc.
|
||||||
|
|
||||||
|
Halting Problem
|
||||||
|
|
||||||
|
If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var halt = errors.New("Stahp")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
runUnsafe(`var abc = [];`)
|
||||||
|
runUnsafe(`
|
||||||
|
while (true) {
|
||||||
|
// Loop forever
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runUnsafe(unsafe string) {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
duration := time.Since(start)
|
||||||
|
if caught := recover(); caught != nil {
|
||||||
|
if caught == halt {
|
||||||
|
fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(caught) // Something else happened, repanic!
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
|
||||||
|
}()
|
||||||
|
|
||||||
|
vm := otto.New()
|
||||||
|
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(2 * time.Second) // Stop after two seconds
|
||||||
|
vm.Interrupt <- func() {
|
||||||
|
panic(halt)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
vm.Run(unsafe) // Here be dragons (risky code)
|
||||||
|
}
|
||||||
|
|
||||||
|
Where is setTimeout/setInterval?
|
||||||
|
|
||||||
|
These timing functions are not actually part of the ECMA-262 specification. Typically, they belong to the `windows` object (in the browser).
|
||||||
|
It would not be difficult to provide something like these via Go, but you probably want to wrap otto in an event loop in that case.
|
||||||
|
|
||||||
|
For an example of how this could be done in Go with otto, see natto:
|
||||||
|
|
||||||
|
http://github.com/robertkrimen/natto
|
||||||
|
|
||||||
|
Here is some more discussion of the issue:
|
||||||
|
|
||||||
|
* http://book.mixu.net/node/ch2.html
|
||||||
|
|
||||||
|
* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29
|
||||||
|
|
||||||
|
* http://aaroncrane.co.uk/2009/02/perl_safe_signals/
|
||||||
|
|
||||||
|
*/
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
"github.com/robertkrimen/otto/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace.
|
||||||
|
type Otto struct {
|
||||||
|
// Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example.
|
||||||
|
// See "Halting Problem" for more information.
|
||||||
|
Interrupt chan func()
|
||||||
|
runtime *_runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
// New will allocate a new JavaScript runtime
|
||||||
|
func New() *Otto {
|
||||||
|
self := &Otto{
|
||||||
|
runtime: newContext(),
|
||||||
|
}
|
||||||
|
self.runtime.otto = self
|
||||||
|
self.runtime.traceLimit = 10
|
||||||
|
self.Set("console", self.runtime.newConsole())
|
||||||
|
|
||||||
|
registry.Apply(func(entry registry.Entry) {
|
||||||
|
self.Run(entry.Source())
|
||||||
|
})
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (otto *Otto) clone() *Otto {
|
||||||
|
self := &Otto{
|
||||||
|
runtime: otto.runtime.clone(),
|
||||||
|
}
|
||||||
|
self.runtime.otto = self
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run will allocate a new JavaScript runtime, run the given source
|
||||||
|
// on the allocated runtime, and return the runtime, resulting value, and
|
||||||
|
// error (if any).
|
||||||
|
//
|
||||||
|
// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
|
||||||
|
//
|
||||||
|
// src may also be a Script.
|
||||||
|
//
|
||||||
|
// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined.
|
||||||
|
//
|
||||||
|
func Run(src interface{}) (*Otto, Value, error) {
|
||||||
|
otto := New()
|
||||||
|
value, err := otto.Run(src) // This already does safety checking
|
||||||
|
return otto, value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any)
|
||||||
|
//
|
||||||
|
// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
|
||||||
|
//
|
||||||
|
// If the runtime is unable to parse source, then this function will return undefined and the parse error (nothing
|
||||||
|
// will be evaluated in this case).
|
||||||
|
//
|
||||||
|
// src may also be a Script.
|
||||||
|
//
|
||||||
|
// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined.
|
||||||
|
//
|
||||||
|
func (self Otto) Run(src interface{}) (Value, error) {
|
||||||
|
value, err := self.runtime.cmpl_run(src, nil)
|
||||||
|
if !value.safe() {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eval will do the same thing as Run, except without leaving the current scope.
|
||||||
|
//
|
||||||
|
// By staying in the same scope, the code evaluated has access to everything
|
||||||
|
// already defined in the current stack frame. This is most useful in, for
|
||||||
|
// example, a debugger call.
|
||||||
|
func (self Otto) Eval(src interface{}) (Value, error) {
|
||||||
|
if self.runtime.scope == nil {
|
||||||
|
self.runtime.enterGlobalScope()
|
||||||
|
defer self.runtime.leaveScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := self.runtime.cmpl_eval(src, nil)
|
||||||
|
if !value.safe() {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the value of the top-level binding of the given name.
|
||||||
|
//
|
||||||
|
// If there is an error (like the binding does not exist), then the value
|
||||||
|
// will be undefined.
|
||||||
|
func (self Otto) Get(name string) (Value, error) {
|
||||||
|
value := Value{}
|
||||||
|
err := catchPanic(func() {
|
||||||
|
value = self.getValue(name)
|
||||||
|
})
|
||||||
|
if !value.safe() {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Otto) getValue(name string) Value {
|
||||||
|
return self.runtime.globalStash.getBinding(name, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the top-level binding of the given name to the given value.
|
||||||
|
//
|
||||||
|
// Set will automatically apply ToValue to the given value in order
|
||||||
|
// to convert it to a JavaScript value (type Value).
|
||||||
|
//
|
||||||
|
// If there is an error (like the binding is read-only, or the ToValue conversion
|
||||||
|
// fails), then an error is returned.
|
||||||
|
//
|
||||||
|
// If the top-level binding does not exist, it will be created.
|
||||||
|
func (self Otto) Set(name string, value interface{}) error {
|
||||||
|
{
|
||||||
|
value, err := self.ToValue(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = catchPanic(func() {
|
||||||
|
self.setValue(name, value)
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Otto) setValue(name string, value Value) {
|
||||||
|
self.runtime.globalStash.setValue(name, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) {
|
||||||
|
self.runtime.debugger = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Otto) SetRandomSource(fn func() float64) {
|
||||||
|
self.runtime.random = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStackDepthLimit sets an upper limit to the depth of the JavaScript
|
||||||
|
// stack. In simpler terms, this limits the number of "nested" function calls
|
||||||
|
// you can make in a particular interpreter instance.
|
||||||
|
//
|
||||||
|
// Note that this doesn't take into account the Go stack depth. If your
|
||||||
|
// JavaScript makes a call to a Go function, otto won't keep track of what
|
||||||
|
// happens outside the interpreter. So if your Go function is infinitely
|
||||||
|
// recursive, you're still in trouble.
|
||||||
|
func (self Otto) SetStackDepthLimit(limit int) {
|
||||||
|
self.runtime.stackLimit = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStackTraceLimit sets an upper limit to the number of stack frames that
|
||||||
|
// otto will use when formatting an error's stack trace. By default, the limit
|
||||||
|
// is 10. This is consistent with V8 and SpiderMonkey.
|
||||||
|
//
|
||||||
|
// TODO: expose via `Error.stackTraceLimit`
|
||||||
|
func (self Otto) SetStackTraceLimit(limit int) {
|
||||||
|
self.runtime.traceLimit = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCustomError creates a new Error object with the given name and message,
|
||||||
|
// returning it as a Value.
|
||||||
|
func (self Otto) MakeCustomError(name, message string) Value {
|
||||||
|
return self.runtime.toValue(self.runtime.newError(name, self.runtime.toValue(message), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRangeError creates a new RangeError object with the given message,
|
||||||
|
// returning it as a Value.
|
||||||
|
func (self Otto) MakeRangeError(message string) Value {
|
||||||
|
return self.runtime.toValue(self.runtime.newRangeError(self.runtime.toValue(message)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSyntaxError creates a new SyntaxError object with the given message,
|
||||||
|
// returning it as a Value.
|
||||||
|
func (self Otto) MakeSyntaxError(message string) Value {
|
||||||
|
return self.runtime.toValue(self.runtime.newSyntaxError(self.runtime.toValue(message)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeTypeError creates a new TypeError object with the given message,
|
||||||
|
// returning it as a Value.
|
||||||
|
func (self Otto) MakeTypeError(message string) Value {
|
||||||
|
return self.runtime.toValue(self.runtime.newTypeError(self.runtime.toValue(message)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is a structure that contains information about the current execution
|
||||||
|
// context.
|
||||||
|
type Context struct {
|
||||||
|
Filename string
|
||||||
|
Line int
|
||||||
|
Column int
|
||||||
|
Callee string
|
||||||
|
Symbols map[string]Value
|
||||||
|
This Value
|
||||||
|
Stacktrace []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context returns the current execution context of the vm, traversing up to
|
||||||
|
// ten stack frames, and skipping any innermost native function stack frames.
|
||||||
|
func (self Otto) Context() Context {
|
||||||
|
return self.ContextSkip(10, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextLimit returns the current execution context of the vm, with a
|
||||||
|
// specific limit on the number of stack frames to traverse, skipping any
|
||||||
|
// innermost native function stack frames.
|
||||||
|
func (self Otto) ContextLimit(limit int) Context {
|
||||||
|
return self.ContextSkip(limit, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextSkip returns the current execution context of the vm, with a
|
||||||
|
// specific limit on the number of stack frames to traverse, optionally
|
||||||
|
// skipping any innermost native function stack frames.
|
||||||
|
func (self Otto) ContextSkip(limit int, skipNative bool) (ctx Context) {
|
||||||
|
// Ensure we are operating in a scope
|
||||||
|
if self.runtime.scope == nil {
|
||||||
|
self.runtime.enterGlobalScope()
|
||||||
|
defer self.runtime.leaveScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := self.runtime.scope
|
||||||
|
frame := scope.frame
|
||||||
|
|
||||||
|
for skipNative && frame.native && scope.outer != nil {
|
||||||
|
scope = scope.outer
|
||||||
|
frame = scope.frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get location information
|
||||||
|
ctx.Filename = "<unknown>"
|
||||||
|
ctx.Callee = frame.callee
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case frame.native:
|
||||||
|
ctx.Filename = frame.nativeFile
|
||||||
|
ctx.Line = frame.nativeLine
|
||||||
|
ctx.Column = 0
|
||||||
|
case frame.file != nil:
|
||||||
|
ctx.Filename = "<anonymous>"
|
||||||
|
|
||||||
|
if p := frame.file.Position(file.Idx(frame.offset)); p != nil {
|
||||||
|
ctx.Line = p.Line
|
||||||
|
ctx.Column = p.Column
|
||||||
|
|
||||||
|
if p.Filename != "" {
|
||||||
|
ctx.Filename = p.Filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current scope this Value
|
||||||
|
ctx.This = toValue_object(scope.this)
|
||||||
|
|
||||||
|
// Build stacktrace (up to 10 levels deep)
|
||||||
|
ctx.Symbols = make(map[string]Value)
|
||||||
|
ctx.Stacktrace = append(ctx.Stacktrace, frame.location())
|
||||||
|
for limit != 0 {
|
||||||
|
// Get variables
|
||||||
|
stash := scope.lexical
|
||||||
|
for {
|
||||||
|
for _, name := range getStashProperties(stash) {
|
||||||
|
if _, ok := ctx.Symbols[name]; !ok {
|
||||||
|
ctx.Symbols[name] = stash.getBinding(name, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stash = stash.outer()
|
||||||
|
if stash == nil || stash.outer() == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = scope.outer
|
||||||
|
if scope == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if scope.frame.offset >= 0 {
|
||||||
|
ctx.Stacktrace = append(ctx.Stacktrace, scope.frame.location())
|
||||||
|
}
|
||||||
|
limit--
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the given JavaScript with a given this and arguments.
|
||||||
|
//
|
||||||
|
// If this is nil, then some special handling takes place to determine the proper
|
||||||
|
// this value, falling back to a "standard" invocation if necessary (where this is
|
||||||
|
// undefined).
|
||||||
|
//
|
||||||
|
// If source begins with "new " (A lowercase new followed by a space), then
|
||||||
|
// Call will invoke the function constructor rather than performing a function call.
|
||||||
|
// In this case, the this argument has no effect.
|
||||||
|
//
|
||||||
|
// // value is a String object
|
||||||
|
// value, _ := vm.Call("Object", nil, "Hello, World.")
|
||||||
|
//
|
||||||
|
// // Likewise...
|
||||||
|
// value, _ := vm.Call("new Object", nil, "Hello, World.")
|
||||||
|
//
|
||||||
|
// // This will perform a concat on the given array and return the result
|
||||||
|
// // value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ]
|
||||||
|
// value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc")
|
||||||
|
//
|
||||||
|
func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) {
|
||||||
|
|
||||||
|
thisValue := Value{}
|
||||||
|
|
||||||
|
construct := false
|
||||||
|
if strings.HasPrefix(source, "new ") {
|
||||||
|
source = source[4:]
|
||||||
|
construct = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME enterGlobalScope
|
||||||
|
self.runtime.enterGlobalScope()
|
||||||
|
defer func() {
|
||||||
|
self.runtime.leaveScope()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if !construct && this == nil {
|
||||||
|
program, err := self.runtime.cmpl_parse("", source+"()", nil)
|
||||||
|
if err == nil {
|
||||||
|
if node, ok := program.body[0].(*_nodeExpressionStatement); ok {
|
||||||
|
if node, ok := node.expression.(*_nodeCallExpression); ok {
|
||||||
|
var value Value
|
||||||
|
err := catchPanic(func() {
|
||||||
|
value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value, err := self.ToValue(this)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
thisValue = value
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
this := thisValue
|
||||||
|
|
||||||
|
fn, err := self.Run(source)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if construct {
|
||||||
|
result, err := fn.constructSafe(self.runtime, this, argumentList...)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := fn.Call(this, argumentList...)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object will run the given source and return the result as an object.
|
||||||
|
//
|
||||||
|
// For example, accessing an existing object:
|
||||||
|
//
|
||||||
|
// object, _ := vm.Object(`Number`)
|
||||||
|
//
|
||||||
|
// Or, creating a new object:
|
||||||
|
//
|
||||||
|
// object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`)
|
||||||
|
//
|
||||||
|
// Or, creating and assigning an object:
|
||||||
|
//
|
||||||
|
// object, _ := vm.Object(`xyzzy = {}`)
|
||||||
|
// object.Set("volume", 11)
|
||||||
|
//
|
||||||
|
// If there is an error (like the source does not result in an object), then
|
||||||
|
// nil and an error is returned.
|
||||||
|
func (self Otto) Object(source string) (*Object, error) {
|
||||||
|
value, err := self.runtime.cmpl_run(source, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if value.IsObject() {
|
||||||
|
return value.Object(), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("value is not an object")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToValue will convert an interface{} value to a value digestible by otto/JavaScript.
|
||||||
|
func (self Otto) ToValue(value interface{}) (Value, error) {
|
||||||
|
return self.runtime.safeToValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy will create a copy/clone of the runtime.
|
||||||
|
//
|
||||||
|
// Copy is useful for saving some time when creating many similar runtimes.
|
||||||
|
//
|
||||||
|
// This method works by walking the original runtime and cloning each object, scope, stash,
|
||||||
|
// etc. into a new runtime.
|
||||||
|
//
|
||||||
|
// Be on the lookout for memory leaks or inadvertent sharing of resources.
|
||||||
|
func (in *Otto) Copy() *Otto {
|
||||||
|
out := &Otto{
|
||||||
|
runtime: in.runtime.clone(),
|
||||||
|
}
|
||||||
|
out.runtime.otto = out
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object{}
|
||||||
|
|
||||||
|
// Object is the representation of a JavaScript object.
|
||||||
|
type Object struct {
|
||||||
|
object *_object
|
||||||
|
value Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func _newObject(object *_object, value Value) *Object {
|
||||||
|
// value MUST contain object!
|
||||||
|
return &Object{
|
||||||
|
object: object,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call a method on the object.
|
||||||
|
//
|
||||||
|
// It is essentially equivalent to:
|
||||||
|
//
|
||||||
|
// var method, _ := object.Get(name)
|
||||||
|
// method.Call(object, argumentList...)
|
||||||
|
//
|
||||||
|
// An undefined value and an error will result if:
|
||||||
|
//
|
||||||
|
// 1. There is an error during conversion of the argument list
|
||||||
|
// 2. The property is not actually a function
|
||||||
|
// 3. An (uncaught) exception is thrown
|
||||||
|
//
|
||||||
|
func (self Object) Call(name string, argumentList ...interface{}) (Value, error) {
|
||||||
|
// TODO: Insert an example using JavaScript below...
|
||||||
|
// e.g., Object("JSON").Call("stringify", ...)
|
||||||
|
|
||||||
|
function, err := self.Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
return function.Call(self.Value(), argumentList...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value will return self as a value.
|
||||||
|
func (self Object) Value() Value {
|
||||||
|
return self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the value of the property with the given name.
|
||||||
|
func (self Object) Get(name string) (Value, error) {
|
||||||
|
value := Value{}
|
||||||
|
err := catchPanic(func() {
|
||||||
|
value = self.object.get(name)
|
||||||
|
})
|
||||||
|
if !value.safe() {
|
||||||
|
value = Value{}
|
||||||
|
}
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the property of the given name to the given value.
|
||||||
|
//
|
||||||
|
// An error will result if the setting the property triggers an exception (i.e. read-only),
|
||||||
|
// or there is an error during conversion of the given value.
|
||||||
|
func (self Object) Set(name string, value interface{}) error {
|
||||||
|
{
|
||||||
|
value, err := self.object.runtime.safeToValue(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = catchPanic(func() {
|
||||||
|
self.object.put(name, value, true)
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys gets the keys for the given object.
|
||||||
|
//
|
||||||
|
// Equivalent to calling Object.keys on the object.
|
||||||
|
func (self Object) Keys() []string {
|
||||||
|
var keys []string
|
||||||
|
self.object.enumerate(false, func(name string) bool {
|
||||||
|
keys = append(keys, name)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysByParent gets the keys (and those of the parents) for the given object,
|
||||||
|
// in order of "closest" to "furthest".
|
||||||
|
func (self Object) KeysByParent() [][]string {
|
||||||
|
var a [][]string
|
||||||
|
|
||||||
|
for o := self.object; o != nil; o = o.prototype {
|
||||||
|
var l []string
|
||||||
|
|
||||||
|
o.enumerate(false, func(name string) bool {
|
||||||
|
l = append(l, name)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
a = append(a, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class will return the class string of the object.
|
||||||
|
//
|
||||||
|
// The return value will (generally) be one of:
|
||||||
|
//
|
||||||
|
// Object
|
||||||
|
// Function
|
||||||
|
// Array
|
||||||
|
// String
|
||||||
|
// Number
|
||||||
|
// Boolean
|
||||||
|
// Date
|
||||||
|
// RegExp
|
||||||
|
//
|
||||||
|
func (self Object) Class() string {
|
||||||
|
return self.object.class
|
||||||
|
}
|
178
vendor/github.com/robertkrimen/otto/otto_.go
generated
vendored
Normal file
178
vendor/github.com/robertkrimen/otto/otto_.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
runtime_ "runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var isIdentifier_Regexp *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z\$][a-zA-Z0-9\$]*$`)
|
||||||
|
|
||||||
|
func isIdentifier(string_ string) bool {
|
||||||
|
return isIdentifier_Regexp.MatchString(string_)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) toValueArray(arguments ...interface{}) []Value {
|
||||||
|
length := len(arguments)
|
||||||
|
if length == 1 {
|
||||||
|
if valueArray, ok := arguments[0].([]Value); ok {
|
||||||
|
return valueArray
|
||||||
|
}
|
||||||
|
return []Value{self.toValue(arguments[0])}
|
||||||
|
}
|
||||||
|
|
||||||
|
valueArray := make([]Value, length)
|
||||||
|
for index, value := range arguments {
|
||||||
|
valueArray[index] = self.toValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringToArrayIndex(name string) int64 {
|
||||||
|
index, err := strconv.ParseInt(name, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if index < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if index >= maxUint32 {
|
||||||
|
// The value 2^32 (or above) is not a valid index because
|
||||||
|
// you cannot store a uint32 length for an index of uint32
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUint32(value int64) bool {
|
||||||
|
return value >= 0 && value <= maxUint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func arrayIndexToString(index int64) string {
|
||||||
|
return strconv.FormatInt(index, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func valueOfArrayIndex(array []Value, index int) Value {
|
||||||
|
value, _ := getValueOfArrayIndex(array, index)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func getValueOfArrayIndex(array []Value, index int) (Value, bool) {
|
||||||
|
if index >= 0 && index < len(array) {
|
||||||
|
value := array[index]
|
||||||
|
if !value.isEmpty() {
|
||||||
|
return value, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// A range index can be anything from 0 up to length. It is NOT safe to use as an index
|
||||||
|
// to an array, but is useful for slicing and in some ECMA algorithms.
|
||||||
|
func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int64 {
|
||||||
|
index := indexValue.number().int64
|
||||||
|
if negativeIsZero {
|
||||||
|
if index < 0 {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
// minimum(index, length)
|
||||||
|
if index >= length {
|
||||||
|
index = length
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < 0 {
|
||||||
|
index += length
|
||||||
|
if index < 0 {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if index > length {
|
||||||
|
index = length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end int64) {
|
||||||
|
start = valueToRangeIndex(valueOfArrayIndex(array, 0), size, negativeIsZero)
|
||||||
|
if len(array) == 1 {
|
||||||
|
// If there is only the start argument, then end = size
|
||||||
|
end = size
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assuming the argument is undefined...
|
||||||
|
end = size
|
||||||
|
endValue := valueOfArrayIndex(array, 1)
|
||||||
|
if !endValue.IsUndefined() {
|
||||||
|
// Which it is not, so get the value as an array index
|
||||||
|
end = valueToRangeIndex(endValue, size, negativeIsZero)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func rangeStartLength(source []Value, size int64) (start, length int64) {
|
||||||
|
start = valueToRangeIndex(valueOfArrayIndex(source, 0), size, false)
|
||||||
|
|
||||||
|
// Assume the second argument is missing or undefined
|
||||||
|
length = int64(size)
|
||||||
|
if len(source) == 1 {
|
||||||
|
// If there is only the start argument, then length = size
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lengthValue := valueOfArrayIndex(source, 1)
|
||||||
|
if !lengthValue.IsUndefined() {
|
||||||
|
// Which it is not, so get the value as an array index
|
||||||
|
length = lengthValue.number().int64
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolFields(input string) (result map[string]bool) {
|
||||||
|
result = map[string]bool{}
|
||||||
|
for _, word := range strings.Fields(input) {
|
||||||
|
result[word] = true
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func hereBeDragons(arguments ...interface{}) string {
|
||||||
|
pc, _, _, _ := runtime_.Caller(1)
|
||||||
|
name := runtime_.FuncForPC(pc).Name()
|
||||||
|
message := fmt.Sprintf("Here be dragons -- %s", name)
|
||||||
|
if len(arguments) > 0 {
|
||||||
|
message += ": "
|
||||||
|
argument0 := fmt.Sprintf("%s", arguments[0])
|
||||||
|
if len(arguments) == 1 {
|
||||||
|
message += argument0
|
||||||
|
} else {
|
||||||
|
message += fmt.Sprintf(argument0, arguments[1:]...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message += "."
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func throwHereBeDragons(arguments ...interface{}) {
|
||||||
|
panic(hereBeDragons(arguments...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func eachPair(list []interface{}, fn func(_0, _1 interface{})) {
|
||||||
|
for len(list) > 0 {
|
||||||
|
var _0, _1 interface{}
|
||||||
|
_0 = list[0]
|
||||||
|
list = list[1:] // Pop off first
|
||||||
|
if len(list) > 0 {
|
||||||
|
_1 = list[0]
|
||||||
|
list = list[1:] // Pop off second
|
||||||
|
}
|
||||||
|
fn(_0, _1)
|
||||||
|
}
|
||||||
|
}
|
4
vendor/github.com/robertkrimen/otto/parser/Makefile
generated
vendored
Normal file
4
vendor/github.com/robertkrimen/otto/parser/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.PHONY: test
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test
|
190
vendor/github.com/robertkrimen/otto/parser/README.markdown
generated
vendored
Normal file
190
vendor/github.com/robertkrimen/otto/parser/README.markdown
generated
vendored
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
# parser
|
||||||
|
--
|
||||||
|
import "github.com/robertkrimen/otto/parser"
|
||||||
|
|
||||||
|
Package parser implements a parser for JavaScript.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
Parse and return an AST
|
||||||
|
|
||||||
|
filename := "" // A filename is optional
|
||||||
|
src := `
|
||||||
|
// Sample xyzzy example
|
||||||
|
(function(){
|
||||||
|
if (3.14159 > 0) {
|
||||||
|
console.log("Hello, World.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xyzzy = NaN;
|
||||||
|
console.log("Nothing happens.");
|
||||||
|
return xyzzy;
|
||||||
|
})();
|
||||||
|
`
|
||||||
|
|
||||||
|
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
||||||
|
program, err := parser.ParseFile(nil, filename, src, 0)
|
||||||
|
|
||||||
|
|
||||||
|
### Warning
|
||||||
|
|
||||||
|
The parser and AST interfaces are still works-in-progress (particularly where
|
||||||
|
node types are concerned) and may change in the future.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
#### func ParseFile
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error)
|
||||||
|
```
|
||||||
|
ParseFile parses the source code of a single JavaScript/ECMAScript source file
|
||||||
|
and returns the corresponding ast.Program node.
|
||||||
|
|
||||||
|
If fileSet == nil, ParseFile parses source without a FileSet. If fileSet != nil,
|
||||||
|
ParseFile first adds filename and src to fileSet.
|
||||||
|
|
||||||
|
The filename argument is optional and is used for labelling errors, etc.
|
||||||
|
|
||||||
|
src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
|
||||||
|
always be in UTF-8.
|
||||||
|
|
||||||
|
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
||||||
|
program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
|
||||||
|
|
||||||
|
#### func ParseFunction
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error)
|
||||||
|
```
|
||||||
|
ParseFunction parses a given parameter list and body as a function and returns
|
||||||
|
the corresponding ast.FunctionLiteral node.
|
||||||
|
|
||||||
|
The parameter list, if any, should be a comma-separated list of identifiers.
|
||||||
|
|
||||||
|
#### func ReadSource
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ReadSource(filename string, src interface{}) ([]byte, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func TransformRegExp
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TransformRegExp(pattern string) (string, error)
|
||||||
|
```
|
||||||
|
TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
|
||||||
|
|
||||||
|
re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
|
||||||
|
backreference (\1, \2, ...) will cause an error.
|
||||||
|
|
||||||
|
re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript
|
||||||
|
definition, on the other hand, also includes \v, Unicode "Separator, Space",
|
||||||
|
etc.
|
||||||
|
|
||||||
|
If the pattern is invalid (not valid even in JavaScript), then this function
|
||||||
|
returns the empty string and an error.
|
||||||
|
|
||||||
|
If the pattern is valid, but incompatible (contains a lookahead or
|
||||||
|
backreference), then this function returns the transformation (a non-empty
|
||||||
|
string) AND an error.
|
||||||
|
|
||||||
|
#### type Error
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Error struct {
|
||||||
|
Position file.Position
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
An Error represents a parsing error. It includes the position where the error
|
||||||
|
occurred and a message/description.
|
||||||
|
|
||||||
|
#### func (Error) Error
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Error) Error() string
|
||||||
|
```
|
||||||
|
|
||||||
|
#### type ErrorList
|
||||||
|
|
||||||
|
```go
|
||||||
|
type ErrorList []*Error
|
||||||
|
```
|
||||||
|
|
||||||
|
ErrorList is a list of *Errors.
|
||||||
|
|
||||||
|
#### func (*ErrorList) Add
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *ErrorList) Add(position file.Position, msg string)
|
||||||
|
```
|
||||||
|
Add adds an Error with given position and message to an ErrorList.
|
||||||
|
|
||||||
|
#### func (ErrorList) Err
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self ErrorList) Err() error
|
||||||
|
```
|
||||||
|
Err returns an error equivalent to this ErrorList. If the list is empty, Err
|
||||||
|
returns nil.
|
||||||
|
|
||||||
|
#### func (ErrorList) Error
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self ErrorList) Error() string
|
||||||
|
```
|
||||||
|
Error implements the Error interface.
|
||||||
|
|
||||||
|
#### func (ErrorList) Len
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self ErrorList) Len() int
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (ErrorList) Less
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self ErrorList) Less(i, j int) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*ErrorList) Reset
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *ErrorList) Reset()
|
||||||
|
```
|
||||||
|
Reset resets an ErrorList to no errors.
|
||||||
|
|
||||||
|
#### func (ErrorList) Sort
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self ErrorList) Sort()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (ErrorList) Swap
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self ErrorList) Swap(i, j int)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### type Mode
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Mode uint
|
||||||
|
```
|
||||||
|
|
||||||
|
A Mode value is a set of flags (or 0). They control optional parser
|
||||||
|
functionality.
|
||||||
|
|
||||||
|
```go
|
||||||
|
const (
|
||||||
|
IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
--
|
||||||
|
**godocdown** http://github.com/robertkrimen/godocdown
|
9
vendor/github.com/robertkrimen/otto/parser/dbg.go
generated
vendored
Normal file
9
vendor/github.com/robertkrimen/otto/parser/dbg.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg
|
||||||
|
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
Dbg "github.com/robertkrimen/otto/dbg"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dbg, dbgf = Dbg.New()
|
175
vendor/github.com/robertkrimen/otto/parser/error.go
generated
vendored
Normal file
175
vendor/github.com/robertkrimen/otto/parser/error.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
err_UnexpectedToken = "Unexpected token %v"
|
||||||
|
err_UnexpectedEndOfInput = "Unexpected end of input"
|
||||||
|
err_UnexpectedEscape = "Unexpected escape"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnexpectedNumber: 'Unexpected number',
|
||||||
|
// UnexpectedString: 'Unexpected string',
|
||||||
|
// UnexpectedIdentifier: 'Unexpected identifier',
|
||||||
|
// UnexpectedReserved: 'Unexpected reserved word',
|
||||||
|
// NewlineAfterThrow: 'Illegal newline after throw',
|
||||||
|
// InvalidRegExp: 'Invalid regular expression',
|
||||||
|
// UnterminatedRegExp: 'Invalid regular expression: missing /',
|
||||||
|
// InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
|
||||||
|
// InvalidLHSInForIn: 'Invalid left-hand side in for-in',
|
||||||
|
// MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
|
||||||
|
// NoCatchOrFinally: 'Missing catch or finally after try',
|
||||||
|
// UnknownLabel: 'Undefined label \'%0\'',
|
||||||
|
// Redeclaration: '%0 \'%1\' has already been declared',
|
||||||
|
// IllegalContinue: 'Illegal continue statement',
|
||||||
|
// IllegalBreak: 'Illegal break statement',
|
||||||
|
// IllegalReturn: 'Illegal return statement',
|
||||||
|
// StrictModeWith: 'Strict mode code may not include a with statement',
|
||||||
|
// StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
|
||||||
|
// StrictVarName: 'Variable name may not be eval or arguments in strict mode',
|
||||||
|
// StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
|
||||||
|
// StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
|
||||||
|
// StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
|
||||||
|
// StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
|
||||||
|
// StrictDelete: 'Delete of an unqualified identifier in strict mode.',
|
||||||
|
// StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
|
||||||
|
// AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
|
||||||
|
// AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
|
||||||
|
// StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
|
||||||
|
// StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
|
||||||
|
// StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
|
||||||
|
// StrictReservedWord: 'Use of future reserved word in strict mode'
|
||||||
|
|
||||||
|
// A SyntaxError is a description of an ECMAScript syntax error.
|
||||||
|
|
||||||
|
// An Error represents a parsing error. It includes the position where the error occurred and a message/description.
|
||||||
|
type Error struct {
|
||||||
|
Position file.Position
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME Should this be "SyntaxError"?
|
||||||
|
|
||||||
|
func (self Error) Error() string {
|
||||||
|
filename := self.Position.Filename
|
||||||
|
if filename == "" {
|
||||||
|
filename = "(anonymous)"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s: Line %d:%d %s",
|
||||||
|
filename,
|
||||||
|
self.Position.Line,
|
||||||
|
self.Position.Column,
|
||||||
|
self.Message,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error {
|
||||||
|
idx := file.Idx(0)
|
||||||
|
switch place := place.(type) {
|
||||||
|
case int:
|
||||||
|
idx = self.idxOf(place)
|
||||||
|
case file.Idx:
|
||||||
|
if place == 0 {
|
||||||
|
idx = self.idxOf(self.chrOffset)
|
||||||
|
} else {
|
||||||
|
idx = place
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("error(%T, ...)", place))
|
||||||
|
}
|
||||||
|
|
||||||
|
position := self.position(idx)
|
||||||
|
msg = fmt.Sprintf(msg, msgValues...)
|
||||||
|
self.errors.Add(position, msg)
|
||||||
|
return self.errors[len(self.errors)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error {
|
||||||
|
if chr == -1 {
|
||||||
|
return self.error(idx, err_UnexpectedEndOfInput)
|
||||||
|
}
|
||||||
|
return self.error(idx, err_UnexpectedToken, token.ILLEGAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) errorUnexpectedToken(tkn token.Token) error {
|
||||||
|
switch tkn {
|
||||||
|
case token.EOF:
|
||||||
|
return self.error(file.Idx(0), err_UnexpectedEndOfInput)
|
||||||
|
}
|
||||||
|
value := tkn.String()
|
||||||
|
switch tkn {
|
||||||
|
case token.BOOLEAN, token.NULL:
|
||||||
|
value = self.literal
|
||||||
|
case token.IDENTIFIER:
|
||||||
|
return self.error(self.idx, "Unexpected identifier")
|
||||||
|
case token.KEYWORD:
|
||||||
|
// TODO Might be a future reserved word
|
||||||
|
return self.error(self.idx, "Unexpected reserved word")
|
||||||
|
case token.NUMBER:
|
||||||
|
return self.error(self.idx, "Unexpected number")
|
||||||
|
case token.STRING:
|
||||||
|
return self.error(self.idx, "Unexpected string")
|
||||||
|
}
|
||||||
|
return self.error(self.idx, err_UnexpectedToken, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorList is a list of *Errors.
|
||||||
|
//
|
||||||
|
type ErrorList []*Error
|
||||||
|
|
||||||
|
// Add adds an Error with given position and message to an ErrorList.
|
||||||
|
func (self *ErrorList) Add(position file.Position, msg string) {
|
||||||
|
*self = append(*self, &Error{position, msg})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets an ErrorList to no errors.
|
||||||
|
func (self *ErrorList) Reset() { *self = (*self)[0:0] }
|
||||||
|
|
||||||
|
func (self ErrorList) Len() int { return len(self) }
|
||||||
|
func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] }
|
||||||
|
func (self ErrorList) Less(i, j int) bool {
|
||||||
|
x := &self[i].Position
|
||||||
|
y := &self[j].Position
|
||||||
|
if x.Filename < y.Filename {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if x.Filename == y.Filename {
|
||||||
|
if x.Line < y.Line {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if x.Line == y.Line {
|
||||||
|
return x.Column < y.Column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self ErrorList) Sort() {
|
||||||
|
sort.Sort(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the Error interface.
|
||||||
|
func (self ErrorList) Error() string {
|
||||||
|
switch len(self) {
|
||||||
|
case 0:
|
||||||
|
return "no errors"
|
||||||
|
case 1:
|
||||||
|
return self[0].Error()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns an error equivalent to this ErrorList.
|
||||||
|
// If the list is empty, Err returns nil.
|
||||||
|
func (self ErrorList) Err() error {
|
||||||
|
if len(self) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
1005
vendor/github.com/robertkrimen/otto/parser/expression.go
generated
vendored
Normal file
1005
vendor/github.com/robertkrimen/otto/parser/expression.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
866
vendor/github.com/robertkrimen/otto/parser/lexer.go
generated
vendored
Normal file
866
vendor/github.com/robertkrimen/otto/parser/lexer.go
generated
vendored
Normal file
|
@ -0,0 +1,866 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _chr struct {
|
||||||
|
value rune
|
||||||
|
width int
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
|
||||||
|
|
||||||
|
func isDecimalDigit(chr rune) bool {
|
||||||
|
return '0' <= chr && chr <= '9'
|
||||||
|
}
|
||||||
|
|
||||||
|
func digitValue(chr rune) int {
|
||||||
|
switch {
|
||||||
|
case '0' <= chr && chr <= '9':
|
||||||
|
return int(chr - '0')
|
||||||
|
case 'a' <= chr && chr <= 'f':
|
||||||
|
return int(chr - 'a' + 10)
|
||||||
|
case 'A' <= chr && chr <= 'F':
|
||||||
|
return int(chr - 'A' + 10)
|
||||||
|
}
|
||||||
|
return 16 // Larger than any legal digit value
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDigit(chr rune, base int) bool {
|
||||||
|
return digitValue(chr) < base
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIdentifierStart(chr rune) bool {
|
||||||
|
return chr == '$' || chr == '_' || chr == '\\' ||
|
||||||
|
'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
|
||||||
|
chr >= utf8.RuneSelf && unicode.IsLetter(chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIdentifierPart(chr rune) bool {
|
||||||
|
return chr == '$' || chr == '_' || chr == '\\' ||
|
||||||
|
'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
|
||||||
|
'0' <= chr && chr <= '9' ||
|
||||||
|
chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) scanIdentifier() (string, error) {
|
||||||
|
offset := self.chrOffset
|
||||||
|
parse := false
|
||||||
|
for isIdentifierPart(self.chr) {
|
||||||
|
if self.chr == '\\' {
|
||||||
|
distance := self.chrOffset - offset
|
||||||
|
self.read()
|
||||||
|
if self.chr != 'u' {
|
||||||
|
return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
|
||||||
|
}
|
||||||
|
parse = true
|
||||||
|
var value rune
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
self.read()
|
||||||
|
decimal, ok := hex2decimal(byte(self.chr))
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
|
||||||
|
}
|
||||||
|
value = value<<4 | decimal
|
||||||
|
}
|
||||||
|
if value == '\\' {
|
||||||
|
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
|
||||||
|
} else if distance == 0 {
|
||||||
|
if !isIdentifierStart(value) {
|
||||||
|
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
|
||||||
|
}
|
||||||
|
} else if distance > 0 {
|
||||||
|
if !isIdentifierPart(value) {
|
||||||
|
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
literal := string(self.str[offset:self.chrOffset])
|
||||||
|
if parse {
|
||||||
|
return parseStringLiteral(literal)
|
||||||
|
}
|
||||||
|
return literal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.2
|
||||||
|
func isLineWhiteSpace(chr rune) bool {
|
||||||
|
switch chr {
|
||||||
|
case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff':
|
||||||
|
return true
|
||||||
|
case '\u000a', '\u000d', '\u2028', '\u2029':
|
||||||
|
return false
|
||||||
|
case '\u0085':
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return unicode.IsSpace(chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.3
|
||||||
|
func isLineTerminator(chr rune) bool {
|
||||||
|
switch chr {
|
||||||
|
case '\u000a', '\u000d', '\u2028', '\u2029':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) {
|
||||||
|
|
||||||
|
self.implicitSemicolon = false
|
||||||
|
|
||||||
|
for {
|
||||||
|
self.skipWhiteSpace()
|
||||||
|
|
||||||
|
idx = self.idxOf(self.chrOffset)
|
||||||
|
insertSemicolon := false
|
||||||
|
|
||||||
|
switch chr := self.chr; {
|
||||||
|
case isIdentifierStart(chr):
|
||||||
|
var err error
|
||||||
|
literal, err = self.scanIdentifier()
|
||||||
|
if err != nil {
|
||||||
|
tkn = token.ILLEGAL
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(literal) > 1 {
|
||||||
|
// Keywords are longer than 1 character, avoid lookup otherwise
|
||||||
|
var strict bool
|
||||||
|
tkn, strict = token.IsKeyword(literal)
|
||||||
|
|
||||||
|
switch tkn {
|
||||||
|
|
||||||
|
case 0: // Not a keyword
|
||||||
|
if literal == "true" || literal == "false" {
|
||||||
|
self.insertSemicolon = true
|
||||||
|
tkn = token.BOOLEAN
|
||||||
|
return
|
||||||
|
} else if literal == "null" {
|
||||||
|
self.insertSemicolon = true
|
||||||
|
tkn = token.NULL
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.KEYWORD:
|
||||||
|
tkn = token.KEYWORD
|
||||||
|
if strict {
|
||||||
|
// TODO If strict and in strict mode, then this is not a break
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case
|
||||||
|
token.THIS,
|
||||||
|
token.BREAK,
|
||||||
|
token.THROW, // A newline after a throw is not allowed, but we need to detect it
|
||||||
|
token.RETURN,
|
||||||
|
token.CONTINUE,
|
||||||
|
token.DEBUGGER:
|
||||||
|
self.insertSemicolon = true
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.insertSemicolon = true
|
||||||
|
tkn = token.IDENTIFIER
|
||||||
|
return
|
||||||
|
case '0' <= chr && chr <= '9':
|
||||||
|
self.insertSemicolon = true
|
||||||
|
tkn, literal = self.scanNumericLiteral(false)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
self.read()
|
||||||
|
switch chr {
|
||||||
|
case -1:
|
||||||
|
if self.insertSemicolon {
|
||||||
|
self.insertSemicolon = false
|
||||||
|
self.implicitSemicolon = true
|
||||||
|
}
|
||||||
|
tkn = token.EOF
|
||||||
|
case '\r', '\n', '\u2028', '\u2029':
|
||||||
|
self.insertSemicolon = false
|
||||||
|
self.implicitSemicolon = true
|
||||||
|
self.comments.AtLineBreak()
|
||||||
|
continue
|
||||||
|
case ':':
|
||||||
|
tkn = token.COLON
|
||||||
|
case '.':
|
||||||
|
if digitValue(self.chr) < 10 {
|
||||||
|
insertSemicolon = true
|
||||||
|
tkn, literal = self.scanNumericLiteral(true)
|
||||||
|
} else {
|
||||||
|
tkn = token.PERIOD
|
||||||
|
}
|
||||||
|
case ',':
|
||||||
|
tkn = token.COMMA
|
||||||
|
case ';':
|
||||||
|
tkn = token.SEMICOLON
|
||||||
|
case '(':
|
||||||
|
tkn = token.LEFT_PARENTHESIS
|
||||||
|
case ')':
|
||||||
|
tkn = token.RIGHT_PARENTHESIS
|
||||||
|
insertSemicolon = true
|
||||||
|
case '[':
|
||||||
|
tkn = token.LEFT_BRACKET
|
||||||
|
case ']':
|
||||||
|
tkn = token.RIGHT_BRACKET
|
||||||
|
insertSemicolon = true
|
||||||
|
case '{':
|
||||||
|
tkn = token.LEFT_BRACE
|
||||||
|
case '}':
|
||||||
|
tkn = token.RIGHT_BRACE
|
||||||
|
insertSemicolon = true
|
||||||
|
case '+':
|
||||||
|
tkn = self.switch3(token.PLUS, token.ADD_ASSIGN, '+', token.INCREMENT)
|
||||||
|
if tkn == token.INCREMENT {
|
||||||
|
insertSemicolon = true
|
||||||
|
}
|
||||||
|
case '-':
|
||||||
|
tkn = self.switch3(token.MINUS, token.SUBTRACT_ASSIGN, '-', token.DECREMENT)
|
||||||
|
if tkn == token.DECREMENT {
|
||||||
|
insertSemicolon = true
|
||||||
|
}
|
||||||
|
case '*':
|
||||||
|
tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN)
|
||||||
|
case '/':
|
||||||
|
if self.chr == '/' {
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
literal := string(self.readSingleLineComment())
|
||||||
|
self.comments.AddComment(ast.NewComment(literal, self.idx))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
self.skipSingleLineComment()
|
||||||
|
continue
|
||||||
|
} else if self.chr == '*' {
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
literal = string(self.readMultiLineComment())
|
||||||
|
self.comments.AddComment(ast.NewComment(literal, self.idx))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
self.skipMultiLineComment()
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// Could be division, could be RegExp literal
|
||||||
|
tkn = self.switch2(token.SLASH, token.QUOTIENT_ASSIGN)
|
||||||
|
insertSemicolon = true
|
||||||
|
}
|
||||||
|
case '%':
|
||||||
|
tkn = self.switch2(token.REMAINDER, token.REMAINDER_ASSIGN)
|
||||||
|
case '^':
|
||||||
|
tkn = self.switch2(token.EXCLUSIVE_OR, token.EXCLUSIVE_OR_ASSIGN)
|
||||||
|
case '<':
|
||||||
|
tkn = self.switch4(token.LESS, token.LESS_OR_EQUAL, '<', token.SHIFT_LEFT, token.SHIFT_LEFT_ASSIGN)
|
||||||
|
case '>':
|
||||||
|
tkn = self.switch6(token.GREATER, token.GREATER_OR_EQUAL, '>', token.SHIFT_RIGHT, token.SHIFT_RIGHT_ASSIGN, '>', token.UNSIGNED_SHIFT_RIGHT, token.UNSIGNED_SHIFT_RIGHT_ASSIGN)
|
||||||
|
case '=':
|
||||||
|
tkn = self.switch2(token.ASSIGN, token.EQUAL)
|
||||||
|
if tkn == token.EQUAL && self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
tkn = token.STRICT_EQUAL
|
||||||
|
}
|
||||||
|
case '!':
|
||||||
|
tkn = self.switch2(token.NOT, token.NOT_EQUAL)
|
||||||
|
if tkn == token.NOT_EQUAL && self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
tkn = token.STRICT_NOT_EQUAL
|
||||||
|
}
|
||||||
|
case '&':
|
||||||
|
if self.chr == '^' {
|
||||||
|
self.read()
|
||||||
|
tkn = self.switch2(token.AND_NOT, token.AND_NOT_ASSIGN)
|
||||||
|
} else {
|
||||||
|
tkn = self.switch3(token.AND, token.AND_ASSIGN, '&', token.LOGICAL_AND)
|
||||||
|
}
|
||||||
|
case '|':
|
||||||
|
tkn = self.switch3(token.OR, token.OR_ASSIGN, '|', token.LOGICAL_OR)
|
||||||
|
case '~':
|
||||||
|
tkn = token.BITWISE_NOT
|
||||||
|
case '?':
|
||||||
|
tkn = token.QUESTION_MARK
|
||||||
|
case '"', '\'':
|
||||||
|
insertSemicolon = true
|
||||||
|
tkn = token.STRING
|
||||||
|
var err error
|
||||||
|
literal, err = self.scanString(self.chrOffset - 1)
|
||||||
|
if err != nil {
|
||||||
|
tkn = token.ILLEGAL
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
self.errorUnexpected(idx, chr)
|
||||||
|
tkn = token.ILLEGAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.insertSemicolon = insertSemicolon
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) switch2(tkn0, tkn1 token.Token) token.Token {
|
||||||
|
if self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
return tkn1
|
||||||
|
}
|
||||||
|
return tkn0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) switch3(tkn0, tkn1 token.Token, chr2 rune, tkn2 token.Token) token.Token {
|
||||||
|
if self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
return tkn1
|
||||||
|
}
|
||||||
|
if self.chr == chr2 {
|
||||||
|
self.read()
|
||||||
|
return tkn2
|
||||||
|
}
|
||||||
|
return tkn0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) switch4(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token) token.Token {
|
||||||
|
if self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
return tkn1
|
||||||
|
}
|
||||||
|
if self.chr == chr2 {
|
||||||
|
self.read()
|
||||||
|
if self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
return tkn3
|
||||||
|
}
|
||||||
|
return tkn2
|
||||||
|
}
|
||||||
|
return tkn0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token, chr3 rune, tkn4, tkn5 token.Token) token.Token {
|
||||||
|
if self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
return tkn1
|
||||||
|
}
|
||||||
|
if self.chr == chr2 {
|
||||||
|
self.read()
|
||||||
|
if self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
return tkn3
|
||||||
|
}
|
||||||
|
if self.chr == chr3 {
|
||||||
|
self.read()
|
||||||
|
if self.chr == '=' {
|
||||||
|
self.read()
|
||||||
|
return tkn5
|
||||||
|
}
|
||||||
|
return tkn4
|
||||||
|
}
|
||||||
|
return tkn2
|
||||||
|
}
|
||||||
|
return tkn0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) chrAt(index int) _chr {
|
||||||
|
value, width := utf8.DecodeRuneInString(self.str[index:])
|
||||||
|
return _chr{
|
||||||
|
value: value,
|
||||||
|
width: width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) _peek() rune {
|
||||||
|
if self.offset+1 < self.length {
|
||||||
|
return rune(self.str[self.offset+1])
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) read() {
|
||||||
|
if self.offset < self.length {
|
||||||
|
self.chrOffset = self.offset
|
||||||
|
chr, width := rune(self.str[self.offset]), 1
|
||||||
|
if chr >= utf8.RuneSelf { // !ASCII
|
||||||
|
chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
|
||||||
|
if chr == utf8.RuneError && width == 1 {
|
||||||
|
self.error(self.chrOffset, "Invalid UTF-8 character")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.offset += width
|
||||||
|
self.chr = chr
|
||||||
|
} else {
|
||||||
|
self.chrOffset = self.length
|
||||||
|
self.chr = -1 // EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is here since the functions are so similar
|
||||||
|
func (self *_RegExp_parser) read() {
|
||||||
|
if self.offset < self.length {
|
||||||
|
self.chrOffset = self.offset
|
||||||
|
chr, width := rune(self.str[self.offset]), 1
|
||||||
|
if chr >= utf8.RuneSelf { // !ASCII
|
||||||
|
chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
|
||||||
|
if chr == utf8.RuneError && width == 1 {
|
||||||
|
self.error(self.chrOffset, "Invalid UTF-8 character")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.offset += width
|
||||||
|
self.chr = chr
|
||||||
|
} else {
|
||||||
|
self.chrOffset = self.length
|
||||||
|
self.chr = -1 // EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) readSingleLineComment() (result []rune) {
|
||||||
|
for self.chr != -1 {
|
||||||
|
self.read()
|
||||||
|
if isLineTerminator(self.chr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result = append(result, self.chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rid of the trailing -1
|
||||||
|
result = result[:len(result)-1]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) readMultiLineComment() (result []rune) {
|
||||||
|
self.read()
|
||||||
|
for self.chr >= 0 {
|
||||||
|
chr := self.chr
|
||||||
|
self.read()
|
||||||
|
if chr == '*' && self.chr == '/' {
|
||||||
|
self.read()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.errorUnexpected(0, self.chr)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) skipSingleLineComment() {
|
||||||
|
for self.chr != -1 {
|
||||||
|
self.read()
|
||||||
|
if isLineTerminator(self.chr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) skipMultiLineComment() {
|
||||||
|
self.read()
|
||||||
|
for self.chr >= 0 {
|
||||||
|
chr := self.chr
|
||||||
|
self.read()
|
||||||
|
if chr == '*' && self.chr == '/' {
|
||||||
|
self.read()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.errorUnexpected(0, self.chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) skipWhiteSpace() {
|
||||||
|
for {
|
||||||
|
switch self.chr {
|
||||||
|
case ' ', '\t', '\f', '\v', '\u00a0', '\ufeff':
|
||||||
|
self.read()
|
||||||
|
continue
|
||||||
|
case '\r':
|
||||||
|
if self._peek() == '\n' {
|
||||||
|
self.comments.AtLineBreak()
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case '\u2028', '\u2029', '\n':
|
||||||
|
if self.insertSemicolon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.comments.AtLineBreak()
|
||||||
|
self.read()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if self.chr >= utf8.RuneSelf {
|
||||||
|
if unicode.IsSpace(self.chr) {
|
||||||
|
self.read()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) skipLineWhiteSpace() {
|
||||||
|
for isLineWhiteSpace(self.chr) {
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) scanMantissa(base int) {
|
||||||
|
for digitValue(self.chr) < base {
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) scanEscape(quote rune) {
|
||||||
|
|
||||||
|
var length, base uint32
|
||||||
|
switch self.chr {
|
||||||
|
//case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
// Octal:
|
||||||
|
// length, base, limit = 3, 8, 255
|
||||||
|
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0':
|
||||||
|
self.read()
|
||||||
|
return
|
||||||
|
case '\r', '\n', '\u2028', '\u2029':
|
||||||
|
self.scanNewline()
|
||||||
|
return
|
||||||
|
case 'x':
|
||||||
|
self.read()
|
||||||
|
length, base = 2, 16
|
||||||
|
case 'u':
|
||||||
|
self.read()
|
||||||
|
length, base = 4, 16
|
||||||
|
default:
|
||||||
|
self.read() // Always make progress
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var value uint32
|
||||||
|
for ; length > 0 && self.chr != quote && self.chr >= 0; length-- {
|
||||||
|
digit := uint32(digitValue(self.chr))
|
||||||
|
if digit >= base {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
value = value*base + digit
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) scanString(offset int) (string, error) {
|
||||||
|
// " ' /
|
||||||
|
quote := rune(self.str[offset])
|
||||||
|
|
||||||
|
for self.chr != quote {
|
||||||
|
chr := self.chr
|
||||||
|
if chr == '\n' || chr == '\r' || chr == '\u2028' || chr == '\u2029' || chr < 0 {
|
||||||
|
goto newline
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
if chr == '\\' {
|
||||||
|
if quote == '/' {
|
||||||
|
if self.chr == '\n' || self.chr == '\r' || self.chr == '\u2028' || self.chr == '\u2029' || self.chr < 0 {
|
||||||
|
goto newline
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
} else {
|
||||||
|
self.scanEscape(quote)
|
||||||
|
}
|
||||||
|
} else if chr == '[' && quote == '/' {
|
||||||
|
// Allow a slash (/) in a bracket character class ([...])
|
||||||
|
// TODO Fix this, this is hacky...
|
||||||
|
quote = -1
|
||||||
|
} else if chr == ']' && quote == -1 {
|
||||||
|
quote = '/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// " ' /
|
||||||
|
self.read()
|
||||||
|
|
||||||
|
return string(self.str[offset:self.chrOffset]), nil
|
||||||
|
|
||||||
|
newline:
|
||||||
|
self.scanNewline()
|
||||||
|
err := "String not terminated"
|
||||||
|
if quote == '/' {
|
||||||
|
err = "Invalid regular expression: missing /"
|
||||||
|
self.error(self.idxOf(offset), err)
|
||||||
|
}
|
||||||
|
return "", errors.New(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) scanNewline() {
|
||||||
|
if self.chr == '\r' {
|
||||||
|
self.read()
|
||||||
|
if self.chr != '\n' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
func hex2decimal(chr byte) (value rune, ok bool) {
|
||||||
|
{
|
||||||
|
chr := rune(chr)
|
||||||
|
switch {
|
||||||
|
case '0' <= chr && chr <= '9':
|
||||||
|
return chr - '0', true
|
||||||
|
case 'a' <= chr && chr <= 'f':
|
||||||
|
return chr - 'a' + 10, true
|
||||||
|
case 'A' <= chr && chr <= 'F':
|
||||||
|
return chr - 'A' + 10, true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNumberLiteral(literal string) (value interface{}, err error) {
|
||||||
|
// TODO Is Uint okay? What about -MAX_UINT
|
||||||
|
value, err = strconv.ParseInt(literal, 0, 64)
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parseIntErr := err // Save this first error, just in case
|
||||||
|
|
||||||
|
value, err = strconv.ParseFloat(literal, 64)
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
} else if err.(*strconv.NumError).Err == strconv.ErrRange {
|
||||||
|
// Infinity, etc.
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = parseIntErr
|
||||||
|
|
||||||
|
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
||||||
|
if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') {
|
||||||
|
// Could just be a very large number (e.g. 0x8000000000000000)
|
||||||
|
var value float64
|
||||||
|
literal = literal[2:]
|
||||||
|
for _, chr := range literal {
|
||||||
|
digit := digitValue(chr)
|
||||||
|
if digit >= 16 {
|
||||||
|
goto error
|
||||||
|
}
|
||||||
|
value = value*16 + float64(digit)
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
return nil, errors.New("Illegal numeric literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStringLiteral(literal string) (string, error) {
|
||||||
|
// Best case scenario...
|
||||||
|
if literal == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slightly less-best case scenario...
|
||||||
|
if !strings.ContainsRune(literal, '\\') {
|
||||||
|
return literal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
str := literal
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 0, 3*len(literal)/2))
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
switch chr := str[0]; {
|
||||||
|
// We do not explicitly handle the case of the quote
|
||||||
|
// value, which can be: " ' /
|
||||||
|
// This assumes we're already passed a partially well-formed literal
|
||||||
|
case chr >= utf8.RuneSelf:
|
||||||
|
chr, size := utf8.DecodeRuneInString(str)
|
||||||
|
buffer.WriteRune(chr)
|
||||||
|
str = str[size:]
|
||||||
|
continue
|
||||||
|
case chr != '\\':
|
||||||
|
buffer.WriteByte(chr)
|
||||||
|
str = str[1:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) <= 1 {
|
||||||
|
panic("len(str) <= 1")
|
||||||
|
}
|
||||||
|
chr := str[1]
|
||||||
|
var value rune
|
||||||
|
if chr >= utf8.RuneSelf {
|
||||||
|
str = str[1:]
|
||||||
|
var size int
|
||||||
|
value, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:] // \ + <character>
|
||||||
|
} else {
|
||||||
|
str = str[2:] // \<character>
|
||||||
|
switch chr {
|
||||||
|
case 'b':
|
||||||
|
value = '\b'
|
||||||
|
case 'f':
|
||||||
|
value = '\f'
|
||||||
|
case 'n':
|
||||||
|
value = '\n'
|
||||||
|
case 'r':
|
||||||
|
value = '\r'
|
||||||
|
case 't':
|
||||||
|
value = '\t'
|
||||||
|
case 'v':
|
||||||
|
value = '\v'
|
||||||
|
case 'x', 'u':
|
||||||
|
size := 0
|
||||||
|
switch chr {
|
||||||
|
case 'x':
|
||||||
|
size = 2
|
||||||
|
case 'u':
|
||||||
|
size = 4
|
||||||
|
}
|
||||||
|
if len(str) < size {
|
||||||
|
return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size)
|
||||||
|
}
|
||||||
|
for j := 0; j < size; j++ {
|
||||||
|
decimal, ok := hex2decimal(str[j])
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size])
|
||||||
|
}
|
||||||
|
value = value<<4 | decimal
|
||||||
|
}
|
||||||
|
str = str[size:]
|
||||||
|
if chr == 'x' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if value > utf8.MaxRune {
|
||||||
|
panic("value > utf8.MaxRune")
|
||||||
|
}
|
||||||
|
case '0':
|
||||||
|
if len(str) == 0 || '0' > str[0] || str[0] > '7' {
|
||||||
|
value = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
// TODO strict
|
||||||
|
value = rune(chr) - '0'
|
||||||
|
j := 0
|
||||||
|
for ; j < 2; j++ {
|
||||||
|
if len(str) < j+1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
chr := str[j]
|
||||||
|
if '0' > chr || chr > '7' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
decimal := rune(str[j]) - '0'
|
||||||
|
value = (value << 3) | decimal
|
||||||
|
}
|
||||||
|
str = str[j:]
|
||||||
|
case '\\':
|
||||||
|
value = '\\'
|
||||||
|
case '\'', '"':
|
||||||
|
value = rune(chr)
|
||||||
|
case '\r':
|
||||||
|
if len(str) > 0 {
|
||||||
|
if str[0] == '\n' {
|
||||||
|
str = str[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case '\n':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
value = rune(chr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteRune(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) {
|
||||||
|
|
||||||
|
offset := self.chrOffset
|
||||||
|
tkn := token.NUMBER
|
||||||
|
|
||||||
|
if decimalPoint {
|
||||||
|
offset--
|
||||||
|
self.scanMantissa(10)
|
||||||
|
goto exponent
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.chr == '0' {
|
||||||
|
offset := self.chrOffset
|
||||||
|
self.read()
|
||||||
|
if self.chr == 'x' || self.chr == 'X' {
|
||||||
|
// Hexadecimal
|
||||||
|
self.read()
|
||||||
|
if isDigit(self.chr, 16) {
|
||||||
|
self.read()
|
||||||
|
} else {
|
||||||
|
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
||||||
|
}
|
||||||
|
self.scanMantissa(16)
|
||||||
|
|
||||||
|
if self.chrOffset-offset <= 2 {
|
||||||
|
// Only "0x" or "0X"
|
||||||
|
self.error(0, "Illegal hexadecimal number")
|
||||||
|
}
|
||||||
|
|
||||||
|
goto hexadecimal
|
||||||
|
} else if self.chr == '.' {
|
||||||
|
// Float
|
||||||
|
goto float
|
||||||
|
} else {
|
||||||
|
// Octal, Float
|
||||||
|
if self.chr == 'e' || self.chr == 'E' {
|
||||||
|
goto exponent
|
||||||
|
}
|
||||||
|
self.scanMantissa(8)
|
||||||
|
if self.chr == '8' || self.chr == '9' {
|
||||||
|
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
||||||
|
}
|
||||||
|
goto octal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scanMantissa(10)
|
||||||
|
|
||||||
|
float:
|
||||||
|
if self.chr == '.' {
|
||||||
|
self.read()
|
||||||
|
self.scanMantissa(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
exponent:
|
||||||
|
if self.chr == 'e' || self.chr == 'E' {
|
||||||
|
self.read()
|
||||||
|
if self.chr == '-' || self.chr == '+' {
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
if isDecimalDigit(self.chr) {
|
||||||
|
self.read()
|
||||||
|
self.scanMantissa(10)
|
||||||
|
} else {
|
||||||
|
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hexadecimal:
|
||||||
|
octal:
|
||||||
|
if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) {
|
||||||
|
return token.ILLEGAL, self.str[offset:self.chrOffset]
|
||||||
|
}
|
||||||
|
|
||||||
|
return tkn, self.str[offset:self.chrOffset]
|
||||||
|
}
|
344
vendor/github.com/robertkrimen/otto/parser/parser.go
generated
vendored
Normal file
344
vendor/github.com/robertkrimen/otto/parser/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
/*
|
||||||
|
Package parser implements a parser for JavaScript.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
Parse and return an AST
|
||||||
|
|
||||||
|
filename := "" // A filename is optional
|
||||||
|
src := `
|
||||||
|
// Sample xyzzy example
|
||||||
|
(function(){
|
||||||
|
if (3.14159 > 0) {
|
||||||
|
console.log("Hello, World.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xyzzy = NaN;
|
||||||
|
console.log("Nothing happens.");
|
||||||
|
return xyzzy;
|
||||||
|
})();
|
||||||
|
`
|
||||||
|
|
||||||
|
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
||||||
|
program, err := parser.ParseFile(nil, filename, src, 0)
|
||||||
|
|
||||||
|
Warning
|
||||||
|
|
||||||
|
The parser and AST interfaces are still works-in-progress (particularly where
|
||||||
|
node types are concerned) and may change in the future.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/file"
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
"gopkg.in/sourcemap.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Mode value is a set of flags (or 0). They control optional parser functionality.
|
||||||
|
type Mode uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
|
||||||
|
StoreComments // Store the comments from source to the comments map
|
||||||
|
)
|
||||||
|
|
||||||
|
type _parser struct {
|
||||||
|
str string
|
||||||
|
length int
|
||||||
|
base int
|
||||||
|
|
||||||
|
chr rune // The current character
|
||||||
|
chrOffset int // The offset of current character
|
||||||
|
offset int // The offset after current character (may be greater than 1)
|
||||||
|
|
||||||
|
idx file.Idx // The index of token
|
||||||
|
token token.Token // The token
|
||||||
|
literal string // The literal of the token, if any
|
||||||
|
|
||||||
|
scope *_scope
|
||||||
|
insertSemicolon bool // If we see a newline, then insert an implicit semicolon
|
||||||
|
implicitSemicolon bool // An implicit semicolon exists
|
||||||
|
|
||||||
|
errors ErrorList
|
||||||
|
|
||||||
|
recover struct {
|
||||||
|
// Scratch when trying to seek to the next statement, etc.
|
||||||
|
idx file.Idx
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
mode Mode
|
||||||
|
|
||||||
|
file *file.File
|
||||||
|
|
||||||
|
comments *ast.Comments
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parser interface {
|
||||||
|
Scan() (tkn token.Token, literal string, idx file.Idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _newParser(filename, src string, base int, sm *sourcemap.Consumer) *_parser {
|
||||||
|
return &_parser{
|
||||||
|
chr: ' ', // This is set so we can start scanning by skipping whitespace
|
||||||
|
str: src,
|
||||||
|
length: len(src),
|
||||||
|
base: base,
|
||||||
|
file: file.NewFile(filename, src, base).WithSourceMap(sm),
|
||||||
|
comments: ast.NewComments(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a new Parser.
|
||||||
|
func NewParser(filename, src string) Parser {
|
||||||
|
return _newParser(filename, src, 1, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadSource(filename string, src interface{}) ([]byte, error) {
|
||||||
|
if src != nil {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case string:
|
||||||
|
return []byte(src), nil
|
||||||
|
case []byte:
|
||||||
|
return src, nil
|
||||||
|
case *bytes.Buffer:
|
||||||
|
if src != nil {
|
||||||
|
return src.Bytes(), nil
|
||||||
|
}
|
||||||
|
case io.Reader:
|
||||||
|
var bfr bytes.Buffer
|
||||||
|
if _, err := io.Copy(&bfr, src); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bfr.Bytes(), nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("invalid source")
|
||||||
|
}
|
||||||
|
return ioutil.ReadFile(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadSourceMap(filename string, src interface{}) (*sourcemap.Consumer, error) {
|
||||||
|
if src == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch src := src.(type) {
|
||||||
|
case string:
|
||||||
|
return sourcemap.Parse(filename, []byte(src))
|
||||||
|
case []byte:
|
||||||
|
return sourcemap.Parse(filename, src)
|
||||||
|
case *bytes.Buffer:
|
||||||
|
if src != nil {
|
||||||
|
return sourcemap.Parse(filename, src.Bytes())
|
||||||
|
}
|
||||||
|
case io.Reader:
|
||||||
|
var bfr bytes.Buffer
|
||||||
|
if _, err := io.Copy(&bfr, src); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sourcemap.Parse(filename, bfr.Bytes())
|
||||||
|
case *sourcemap.Consumer:
|
||||||
|
return src, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("invalid sourcemap type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseFileWithSourceMap(fileSet *file.FileSet, filename string, javascriptSource, sourcemapSource interface{}, mode Mode) (*ast.Program, error) {
|
||||||
|
src, err := ReadSource(filename, javascriptSource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourcemapSource == nil {
|
||||||
|
lines := bytes.Split(src, []byte("\n"))
|
||||||
|
lastLine := lines[len(lines)-1]
|
||||||
|
if bytes.HasPrefix(lastLine, []byte("//# sourceMappingURL=data:application/json")) {
|
||||||
|
bits := bytes.SplitN(lastLine, []byte(","), 2)
|
||||||
|
if len(bits) == 2 {
|
||||||
|
if d, err := base64.StdEncoding.DecodeString(string(bits[1])); err == nil {
|
||||||
|
sourcemapSource = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sm, err := ReadSourceMap(filename, sourcemapSource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
base := 1
|
||||||
|
if fileSet != nil {
|
||||||
|
base = fileSet.AddFile(filename, string(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := _newParser(filename, string(src), base, sm)
|
||||||
|
parser.mode = mode
|
||||||
|
program, err := parser.parse()
|
||||||
|
program.Comments = parser.comments.CommentMap
|
||||||
|
|
||||||
|
return program, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns
|
||||||
|
// the corresponding ast.Program node.
|
||||||
|
//
|
||||||
|
// If fileSet == nil, ParseFile parses source without a FileSet.
|
||||||
|
// If fileSet != nil, ParseFile first adds filename and src to fileSet.
|
||||||
|
//
|
||||||
|
// The filename argument is optional and is used for labelling errors, etc.
|
||||||
|
//
|
||||||
|
// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
|
||||||
|
//
|
||||||
|
// // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
|
||||||
|
// program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
|
||||||
|
//
|
||||||
|
func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) {
|
||||||
|
return ParseFileWithSourceMap(fileSet, filename, src, nil, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFunction parses a given parameter list and body as a function and returns the
|
||||||
|
// corresponding ast.FunctionLiteral node.
|
||||||
|
//
|
||||||
|
// The parameter list, if any, should be a comma-separated list of identifiers.
|
||||||
|
//
|
||||||
|
func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) {
|
||||||
|
|
||||||
|
src := "(function(" + parameterList + ") {\n" + body + "\n})"
|
||||||
|
|
||||||
|
parser := _newParser("", src, 1, nil)
|
||||||
|
program, err := parser.parse()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan reads a single token from the source at the current offset, increments the offset and
|
||||||
|
// returns the token.Token token, a string literal representing the value of the token (if applicable)
|
||||||
|
// and it's current file.Idx index.
|
||||||
|
func (self *_parser) Scan() (tkn token.Token, literal string, idx file.Idx) {
|
||||||
|
return self.scan()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) slice(idx0, idx1 file.Idx) string {
|
||||||
|
from := int(idx0) - self.base
|
||||||
|
to := int(idx1) - self.base
|
||||||
|
if from >= 0 && to <= len(self.str) {
|
||||||
|
return self.str[from:to]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parse() (*ast.Program, error) {
|
||||||
|
self.next()
|
||||||
|
program := self.parseProgram()
|
||||||
|
if false {
|
||||||
|
self.errors.Sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(program, self.comments.FetchAll(), ast.TRAILING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return program, self.errors.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) next() {
|
||||||
|
self.token, self.literal, self.idx = self.scan()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) optionalSemicolon() {
|
||||||
|
if self.token == token.SEMICOLON {
|
||||||
|
self.next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.implicitSemicolon {
|
||||||
|
self.implicitSemicolon = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.token != token.EOF && self.token != token.RIGHT_BRACE {
|
||||||
|
self.expect(token.SEMICOLON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) semicolon() {
|
||||||
|
if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE {
|
||||||
|
if self.implicitSemicolon {
|
||||||
|
self.implicitSemicolon = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.SEMICOLON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) idxOf(offset int) file.Idx {
|
||||||
|
return file.Idx(self.base + offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) expect(value token.Token) file.Idx {
|
||||||
|
idx := self.idx
|
||||||
|
if self.token != value {
|
||||||
|
self.errorUnexpectedToken(self.token)
|
||||||
|
}
|
||||||
|
self.next()
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func lineCount(str string) (int, int) {
|
||||||
|
line, last := 0, -1
|
||||||
|
pair := false
|
||||||
|
for index, chr := range str {
|
||||||
|
switch chr {
|
||||||
|
case '\r':
|
||||||
|
line += 1
|
||||||
|
last = index
|
||||||
|
pair = true
|
||||||
|
continue
|
||||||
|
case '\n':
|
||||||
|
if !pair {
|
||||||
|
line += 1
|
||||||
|
}
|
||||||
|
last = index
|
||||||
|
case '\u2028', '\u2029':
|
||||||
|
line += 1
|
||||||
|
last = index + 2
|
||||||
|
}
|
||||||
|
pair = false
|
||||||
|
}
|
||||||
|
return line, last
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) position(idx file.Idx) file.Position {
|
||||||
|
position := file.Position{}
|
||||||
|
offset := int(idx) - self.base
|
||||||
|
str := self.str[:offset]
|
||||||
|
position.Filename = self.file.Name()
|
||||||
|
line, last := lineCount(str)
|
||||||
|
position.Line = 1 + line
|
||||||
|
if last >= 0 {
|
||||||
|
position.Column = offset - last
|
||||||
|
} else {
|
||||||
|
position.Column = 1 + len(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return position
|
||||||
|
}
|
358
vendor/github.com/robertkrimen/otto/parser/regexp.go
generated
vendored
Normal file
358
vendor/github.com/robertkrimen/otto/parser/regexp.go
generated
vendored
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _RegExp_parser struct {
|
||||||
|
str string
|
||||||
|
length int
|
||||||
|
|
||||||
|
chr rune // The current character
|
||||||
|
chrOffset int // The offset of current character
|
||||||
|
offset int // The offset after current character (may be greater than 1)
|
||||||
|
|
||||||
|
errors []error
|
||||||
|
invalid bool // The input is an invalid JavaScript RegExp
|
||||||
|
|
||||||
|
goRegexp *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
|
||||||
|
//
|
||||||
|
// re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
|
||||||
|
// backreference (\1, \2, ...) will cause an error.
|
||||||
|
//
|
||||||
|
// re2 (Go) has a different definition for \s: [\t\n\f\r ].
|
||||||
|
// The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc.
|
||||||
|
//
|
||||||
|
// If the pattern is invalid (not valid even in JavaScript), then this function
|
||||||
|
// returns the empty string and an error.
|
||||||
|
//
|
||||||
|
// If the pattern is valid, but incompatible (contains a lookahead or backreference),
|
||||||
|
// then this function returns the transformation (a non-empty string) AND an error.
|
||||||
|
func TransformRegExp(pattern string) (string, error) {
|
||||||
|
|
||||||
|
if pattern == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO If without \, if without (?=, (?!, then another shortcut
|
||||||
|
|
||||||
|
parser := _RegExp_parser{
|
||||||
|
str: pattern,
|
||||||
|
length: len(pattern),
|
||||||
|
goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)),
|
||||||
|
}
|
||||||
|
parser.read() // Pull in the first character
|
||||||
|
parser.scan()
|
||||||
|
var err error
|
||||||
|
if len(parser.errors) > 0 {
|
||||||
|
err = parser.errors[0]
|
||||||
|
}
|
||||||
|
if parser.invalid {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Might not be re2 compatible, but is still a valid JavaScript RegExp
|
||||||
|
return parser.goRegexp.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_RegExp_parser) scan() {
|
||||||
|
for self.chr != -1 {
|
||||||
|
switch self.chr {
|
||||||
|
case '\\':
|
||||||
|
self.read()
|
||||||
|
self.scanEscape(false)
|
||||||
|
case '(':
|
||||||
|
self.pass()
|
||||||
|
self.scanGroup()
|
||||||
|
case '[':
|
||||||
|
self.pass()
|
||||||
|
self.scanBracket()
|
||||||
|
case ')':
|
||||||
|
self.error(-1, "Unmatched ')'")
|
||||||
|
self.invalid = true
|
||||||
|
self.pass()
|
||||||
|
default:
|
||||||
|
self.pass()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (...)
|
||||||
|
func (self *_RegExp_parser) scanGroup() {
|
||||||
|
str := self.str[self.chrOffset:]
|
||||||
|
if len(str) > 1 { // A possibility of (?= or (?!
|
||||||
|
if str[0] == '?' {
|
||||||
|
if str[1] == '=' || str[1] == '!' {
|
||||||
|
self.error(-1, "re2: Invalid (%s) <lookahead>", self.str[self.chrOffset:self.chrOffset+2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for self.chr != -1 && self.chr != ')' {
|
||||||
|
switch self.chr {
|
||||||
|
case '\\':
|
||||||
|
self.read()
|
||||||
|
self.scanEscape(false)
|
||||||
|
case '(':
|
||||||
|
self.pass()
|
||||||
|
self.scanGroup()
|
||||||
|
case '[':
|
||||||
|
self.pass()
|
||||||
|
self.scanBracket()
|
||||||
|
default:
|
||||||
|
self.pass()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.chr != ')' {
|
||||||
|
self.error(-1, "Unterminated group")
|
||||||
|
self.invalid = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.pass()
|
||||||
|
}
|
||||||
|
|
||||||
|
// [...]
|
||||||
|
func (self *_RegExp_parser) scanBracket() {
|
||||||
|
for self.chr != -1 {
|
||||||
|
if self.chr == ']' {
|
||||||
|
break
|
||||||
|
} else if self.chr == '\\' {
|
||||||
|
self.read()
|
||||||
|
self.scanEscape(true)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
self.pass()
|
||||||
|
}
|
||||||
|
if self.chr != ']' {
|
||||||
|
self.error(-1, "Unterminated character class")
|
||||||
|
self.invalid = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.pass()
|
||||||
|
}
|
||||||
|
|
||||||
|
// \...
|
||||||
|
func (self *_RegExp_parser) scanEscape(inClass bool) {
|
||||||
|
offset := self.chrOffset
|
||||||
|
|
||||||
|
var length, base uint32
|
||||||
|
switch self.chr {
|
||||||
|
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
var value int64
|
||||||
|
size := 0
|
||||||
|
for {
|
||||||
|
digit := int64(digitValue(self.chr))
|
||||||
|
if digit >= 8 {
|
||||||
|
// Not a valid digit
|
||||||
|
break
|
||||||
|
}
|
||||||
|
value = value*8 + digit
|
||||||
|
self.read()
|
||||||
|
size += 1
|
||||||
|
}
|
||||||
|
if size == 1 { // The number of characters read
|
||||||
|
_, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'})
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
if value != 0 {
|
||||||
|
// An invalid backreference
|
||||||
|
self.error(-1, "re2: Invalid \\%d <backreference>", value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmp := []byte{'\\', 'x', '0', 0}
|
||||||
|
if value >= 16 {
|
||||||
|
tmp = tmp[0:2]
|
||||||
|
} else {
|
||||||
|
tmp = tmp[0:3]
|
||||||
|
}
|
||||||
|
tmp = strconv.AppendInt(tmp, value, 16)
|
||||||
|
_, err := self.goRegexp.Write(tmp)
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case '8', '9':
|
||||||
|
size := 0
|
||||||
|
for {
|
||||||
|
digit := digitValue(self.chr)
|
||||||
|
if digit >= 10 {
|
||||||
|
// Not a valid digit
|
||||||
|
break
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
size += 1
|
||||||
|
}
|
||||||
|
err := self.goRegexp.WriteByte('\\')
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
_, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset])
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
self.error(-1, "re2: Invalid \\%s <backreference>", self.str[offset:self.chrOffset])
|
||||||
|
return
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
self.read()
|
||||||
|
length, base = 2, 16
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
self.read()
|
||||||
|
length, base = 4, 16
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
if inClass {
|
||||||
|
_, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'})
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case 'B':
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case 'd', 'D', 's', 'S', 'w', 'W':
|
||||||
|
// This is slightly broken, because ECMAScript
|
||||||
|
// includes \v in \s, \S, while re2 does not
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case 'f', 'n', 'r', 't', 'v':
|
||||||
|
err := self.goRegexp.WriteByte('\\')
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
self.pass()
|
||||||
|
return
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
self.read()
|
||||||
|
var value int64
|
||||||
|
if 'a' <= self.chr && self.chr <= 'z' {
|
||||||
|
value = int64(self.chr) - 'a' + 1
|
||||||
|
} else if 'A' <= self.chr && self.chr <= 'Z' {
|
||||||
|
value = int64(self.chr) - 'A' + 1
|
||||||
|
} else {
|
||||||
|
err := self.goRegexp.WriteByte('c')
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmp := []byte{'\\', 'x', '0', 0}
|
||||||
|
if value >= 16 {
|
||||||
|
tmp = tmp[0:2]
|
||||||
|
} else {
|
||||||
|
tmp = tmp[0:3]
|
||||||
|
}
|
||||||
|
tmp = strconv.AppendInt(tmp, value, 16)
|
||||||
|
_, err := self.goRegexp.Write(tmp)
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
// $ is an identifier character, so we have to have
|
||||||
|
// a special case for it here
|
||||||
|
if self.chr == '$' || !isIdentifierPart(self.chr) {
|
||||||
|
// A non-identifier character needs escaping
|
||||||
|
err := self.goRegexp.WriteByte('\\')
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unescape the character for re2
|
||||||
|
}
|
||||||
|
self.pass()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we're a \u.... or \x...
|
||||||
|
valueOffset := self.chrOffset
|
||||||
|
|
||||||
|
var value uint32
|
||||||
|
{
|
||||||
|
length := length
|
||||||
|
for ; length > 0; length-- {
|
||||||
|
digit := uint32(digitValue(self.chr))
|
||||||
|
if digit >= base {
|
||||||
|
// Not a valid digit
|
||||||
|
goto skip
|
||||||
|
}
|
||||||
|
value = value*base + digit
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 4 {
|
||||||
|
_, err := self.goRegexp.Write([]byte{
|
||||||
|
'\\',
|
||||||
|
'x',
|
||||||
|
'{',
|
||||||
|
self.str[valueOffset+0],
|
||||||
|
self.str[valueOffset+1],
|
||||||
|
self.str[valueOffset+2],
|
||||||
|
self.str[valueOffset+3],
|
||||||
|
'}',
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
} else if length == 2 {
|
||||||
|
_, err := self.goRegexp.Write([]byte{
|
||||||
|
'\\',
|
||||||
|
'x',
|
||||||
|
self.str[valueOffset+0],
|
||||||
|
self.str[valueOffset+1],
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Should never, ever get here...
|
||||||
|
self.error(-1, "re2: Illegal branch in scanEscape")
|
||||||
|
goto skip
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
skip:
|
||||||
|
_, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset])
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_RegExp_parser) pass() {
|
||||||
|
if self.chr != -1 {
|
||||||
|
_, err := self.goRegexp.WriteRune(self.chr)
|
||||||
|
if err != nil {
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Better error reporting, use the offset, etc.
|
||||||
|
func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error {
|
||||||
|
err := fmt.Errorf(msg, msgValues...)
|
||||||
|
self.errors = append(self.errors, err)
|
||||||
|
return err
|
||||||
|
}
|
44
vendor/github.com/robertkrimen/otto/parser/scope.go
generated
vendored
Normal file
44
vendor/github.com/robertkrimen/otto/parser/scope.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _scope struct {
|
||||||
|
outer *_scope
|
||||||
|
allowIn bool
|
||||||
|
inIteration bool
|
||||||
|
inSwitch bool
|
||||||
|
inFunction bool
|
||||||
|
declarationList []ast.Declaration
|
||||||
|
|
||||||
|
labels []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) openScope() {
|
||||||
|
self.scope = &_scope{
|
||||||
|
outer: self.scope,
|
||||||
|
allowIn: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) closeScope() {
|
||||||
|
self.scope = self.scope.outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_scope) declare(declaration ast.Declaration) {
|
||||||
|
self.declarationList = append(self.declarationList, declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_scope) hasLabel(name string) bool {
|
||||||
|
for _, label := range self.labels {
|
||||||
|
if label == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.outer != nil && !self.inFunction {
|
||||||
|
// Crossing a function boundary to look for a label is verboten
|
||||||
|
return self.outer.hasLabel(name)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
940
vendor/github.com/robertkrimen/otto/parser/statement.go
generated
vendored
Normal file
940
vendor/github.com/robertkrimen/otto/parser/statement.go
generated
vendored
Normal file
|
@ -0,0 +1,940 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *_parser) parseBlockStatement() *ast.BlockStatement {
|
||||||
|
node := &ast.BlockStatement{}
|
||||||
|
|
||||||
|
// Find comments before the leading brace
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.LEADING)
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
|
||||||
|
node.LeftBrace = self.expect(token.LEFT_BRACE)
|
||||||
|
node.List = self.parseStatementList()
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.FINAL)
|
||||||
|
self.comments.AfterBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
node.RightBrace = self.expect(token.RIGHT_BRACE)
|
||||||
|
|
||||||
|
// Find comments after the trailing brace
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.ResetLineBreak()
|
||||||
|
self.comments.CommentMap.AddComments(node, self.comments.Fetch(), ast.TRAILING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseEmptyStatement() ast.Statement {
|
||||||
|
idx := self.expect(token.SEMICOLON)
|
||||||
|
return &ast.EmptyStatement{Semicolon: idx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseStatementList() (list []ast.Statement) {
|
||||||
|
for self.token != token.RIGHT_BRACE && self.token != token.EOF {
|
||||||
|
statement := self.parseStatement()
|
||||||
|
list = append(list, statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseStatement() ast.Statement {
|
||||||
|
|
||||||
|
if self.token == token.EOF {
|
||||||
|
self.errorUnexpectedToken(self.token)
|
||||||
|
return &ast.BadStatement{From: self.idx, To: self.idx + 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.ResetLineBreak()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self.token {
|
||||||
|
case token.SEMICOLON:
|
||||||
|
return self.parseEmptyStatement()
|
||||||
|
case token.LEFT_BRACE:
|
||||||
|
return self.parseBlockStatement()
|
||||||
|
case token.IF:
|
||||||
|
return self.parseIfStatement()
|
||||||
|
case token.DO:
|
||||||
|
statement := self.parseDoWhileStatement()
|
||||||
|
self.comments.PostProcessNode(statement)
|
||||||
|
return statement
|
||||||
|
case token.WHILE:
|
||||||
|
return self.parseWhileStatement()
|
||||||
|
case token.FOR:
|
||||||
|
return self.parseForOrForInStatement()
|
||||||
|
case token.BREAK:
|
||||||
|
return self.parseBreakStatement()
|
||||||
|
case token.CONTINUE:
|
||||||
|
return self.parseContinueStatement()
|
||||||
|
case token.DEBUGGER:
|
||||||
|
return self.parseDebuggerStatement()
|
||||||
|
case token.WITH:
|
||||||
|
return self.parseWithStatement()
|
||||||
|
case token.VAR:
|
||||||
|
return self.parseVariableStatement()
|
||||||
|
case token.FUNCTION:
|
||||||
|
return self.parseFunctionStatement()
|
||||||
|
case token.SWITCH:
|
||||||
|
return self.parseSwitchStatement()
|
||||||
|
case token.RETURN:
|
||||||
|
return self.parseReturnStatement()
|
||||||
|
case token.THROW:
|
||||||
|
return self.parseThrowStatement()
|
||||||
|
case token.TRY:
|
||||||
|
return self.parseTryStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
expression := self.parseExpression()
|
||||||
|
|
||||||
|
if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON {
|
||||||
|
// LabelledStatement
|
||||||
|
colon := self.idx
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.next() // :
|
||||||
|
|
||||||
|
label := identifier.Name
|
||||||
|
for _, value := range self.scope.labels {
|
||||||
|
if label == value {
|
||||||
|
self.error(identifier.Idx0(), "Label '%s' already exists", label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var labelComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
labelComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.scope.labels = append(self.scope.labels, label) // Push the label
|
||||||
|
statement := self.parseStatement()
|
||||||
|
self.scope.labels = self.scope.labels[:len(self.scope.labels)-1] // Pop the label
|
||||||
|
exp := &ast.LabelledStatement{
|
||||||
|
Label: identifier,
|
||||||
|
Colon: colon,
|
||||||
|
Statement: statement,
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(exp, labelComments, ast.LEADING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return exp
|
||||||
|
}
|
||||||
|
|
||||||
|
self.optionalSemicolon()
|
||||||
|
|
||||||
|
statement := &ast.ExpressionStatement{
|
||||||
|
Expression: expression,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(statement, comments, ast.LEADING)
|
||||||
|
}
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseTryStatement() ast.Statement {
|
||||||
|
var tryComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
tryComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
node := &ast.TryStatement{
|
||||||
|
Try: self.expect(token.TRY),
|
||||||
|
Body: self.parseBlockStatement(),
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, tryComments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(node.Body, self.comments.FetchAll(), ast.TRAILING)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.token == token.CATCH {
|
||||||
|
catch := self.idx
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.next()
|
||||||
|
self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
if self.token != token.IDENTIFIER {
|
||||||
|
self.expect(token.IDENTIFIER)
|
||||||
|
self.nextStatement()
|
||||||
|
return &ast.BadStatement{From: catch, To: self.idx}
|
||||||
|
} else {
|
||||||
|
identifier := self.parseIdentifier()
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
node.Catch = &ast.CatchStatement{
|
||||||
|
Catch: catch,
|
||||||
|
Parameter: identifier,
|
||||||
|
Body: self.parseBlockStatement(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node.Catch.Body, self.comments.FetchAll(), ast.TRAILING)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.token == token.FINALLY {
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.next()
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
tryComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Finally = self.parseBlockStatement()
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node.Finally, tryComments, ast.LEADING)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Catch == nil && node.Finally == nil {
|
||||||
|
self.error(node.Try, "Missing catch or finally after try")
|
||||||
|
return &ast.BadStatement{From: node.Try, To: node.Body.Idx1()}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseFunctionParameterList() *ast.ParameterList {
|
||||||
|
opening := self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
var list []*ast.Identifier
|
||||||
|
for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF {
|
||||||
|
if self.token != token.IDENTIFIER {
|
||||||
|
self.expect(token.IDENTIFIER)
|
||||||
|
} else {
|
||||||
|
identifier := self.parseIdentifier()
|
||||||
|
list = append(list, identifier)
|
||||||
|
}
|
||||||
|
if self.token != token.RIGHT_PARENTHESIS {
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.expect(token.COMMA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closing := self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
|
||||||
|
return &ast.ParameterList{
|
||||||
|
Opening: opening,
|
||||||
|
List: list,
|
||||||
|
Closing: closing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseParameterList() (list []string) {
|
||||||
|
for self.token != token.EOF {
|
||||||
|
if self.token != token.IDENTIFIER {
|
||||||
|
self.expect(token.IDENTIFIER)
|
||||||
|
}
|
||||||
|
list = append(list, self.literal)
|
||||||
|
self.next()
|
||||||
|
if self.token != token.EOF {
|
||||||
|
self.expect(token.COMMA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseFunctionStatement() *ast.FunctionStatement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
function := &ast.FunctionStatement{
|
||||||
|
Function: self.parseFunction(true),
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(function, comments, ast.LEADING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return function
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral {
|
||||||
|
|
||||||
|
node := &ast.FunctionLiteral{
|
||||||
|
Function: self.expect(token.FUNCTION),
|
||||||
|
}
|
||||||
|
|
||||||
|
var name *ast.Identifier
|
||||||
|
if self.token == token.IDENTIFIER {
|
||||||
|
name = self.parseIdentifier()
|
||||||
|
if declaration {
|
||||||
|
self.scope.declare(&ast.FunctionDeclaration{
|
||||||
|
Function: node,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if declaration {
|
||||||
|
// Use expect error handling
|
||||||
|
self.expect(token.IDENTIFIER)
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
node.Name = name
|
||||||
|
node.ParameterList = self.parseFunctionParameterList()
|
||||||
|
self.parseFunctionBlock(node)
|
||||||
|
node.Source = self.slice(node.Idx0(), node.Idx1())
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) {
|
||||||
|
{
|
||||||
|
self.openScope()
|
||||||
|
inFunction := self.scope.inFunction
|
||||||
|
self.scope.inFunction = true
|
||||||
|
defer func() {
|
||||||
|
self.scope.inFunction = inFunction
|
||||||
|
self.closeScope()
|
||||||
|
}()
|
||||||
|
node.Body = self.parseBlockStatement()
|
||||||
|
node.DeclarationList = self.scope.declarationList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseDebuggerStatement() ast.Statement {
|
||||||
|
idx := self.expect(token.DEBUGGER)
|
||||||
|
|
||||||
|
node := &ast.DebuggerStatement{
|
||||||
|
Debugger: idx,
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.TRAILING)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.semicolon()
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseReturnStatement() ast.Statement {
|
||||||
|
idx := self.expect(token.RETURN)
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.scope.inFunction {
|
||||||
|
self.error(idx, "Illegal return statement")
|
||||||
|
self.nextStatement()
|
||||||
|
return &ast.BadStatement{From: idx, To: self.idx}
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &ast.ReturnStatement{
|
||||||
|
Return: idx,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF {
|
||||||
|
node.Argument = self.parseExpression()
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.semicolon()
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseThrowStatement() ast.Statement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
idx := self.expect(token.THROW)
|
||||||
|
|
||||||
|
if self.implicitSemicolon {
|
||||||
|
if self.chr == -1 { // Hackish
|
||||||
|
self.error(idx, "Unexpected end of input")
|
||||||
|
} else {
|
||||||
|
self.error(idx, "Illegal newline after throw")
|
||||||
|
}
|
||||||
|
self.nextStatement()
|
||||||
|
return &ast.BadStatement{From: idx, To: self.idx}
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &ast.ThrowStatement{
|
||||||
|
Throw: self.idx,
|
||||||
|
Argument: self.parseExpression(),
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.semicolon()
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseSwitchStatement() ast.Statement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.expect(token.SWITCH)
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = append(comments, self.comments.FetchAll()...)
|
||||||
|
}
|
||||||
|
self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
node := &ast.SwitchStatement{
|
||||||
|
Discriminant: self.parseExpression(),
|
||||||
|
Default: -1,
|
||||||
|
}
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = append(comments, self.comments.FetchAll()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.LEFT_BRACE)
|
||||||
|
|
||||||
|
inSwitch := self.scope.inSwitch
|
||||||
|
self.scope.inSwitch = true
|
||||||
|
defer func() {
|
||||||
|
self.scope.inSwitch = inSwitch
|
||||||
|
}()
|
||||||
|
|
||||||
|
for index := 0; self.token != token.EOF; index++ {
|
||||||
|
if self.token == token.RIGHT_BRACE {
|
||||||
|
self.next()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
clause := self.parseCaseStatement()
|
||||||
|
if clause.Test == nil {
|
||||||
|
if node.Default != -1 {
|
||||||
|
self.error(clause.Case, "Already saw a default in switch")
|
||||||
|
}
|
||||||
|
node.Default = index
|
||||||
|
}
|
||||||
|
node.Body = append(node.Body, clause)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseWithStatement() ast.Statement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.expect(token.WITH)
|
||||||
|
var withComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
withComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
|
||||||
|
node := &ast.WithStatement{
|
||||||
|
Object: self.parseExpression(),
|
||||||
|
}
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
//comments = append(comments, self.comments.FetchAll()...)
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(node, withComments, ast.WITH)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Body = self.parseStatement()
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseCaseStatement() *ast.CaseStatement {
|
||||||
|
node := &ast.CaseStatement{
|
||||||
|
Case: self.idx,
|
||||||
|
}
|
||||||
|
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.token == token.DEFAULT {
|
||||||
|
self.next()
|
||||||
|
} else {
|
||||||
|
self.expect(token.CASE)
|
||||||
|
node.Test = self.parseExpression()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.expect(token.COLON)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if self.token == token.EOF ||
|
||||||
|
self.token == token.RIGHT_BRACE ||
|
||||||
|
self.token == token.CASE ||
|
||||||
|
self.token == token.DEFAULT {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
consequent := self.parseStatement()
|
||||||
|
node.Consequent = append(node.Consequent, consequent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link the comments to the case statement
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseIterationStatement() ast.Statement {
|
||||||
|
inIteration := self.scope.inIteration
|
||||||
|
self.scope.inIteration = true
|
||||||
|
defer func() {
|
||||||
|
self.scope.inIteration = inIteration
|
||||||
|
}()
|
||||||
|
return self.parseStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement {
|
||||||
|
|
||||||
|
// Already have consumed "<into> in"
|
||||||
|
|
||||||
|
source := self.parseExpression()
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
body := self.parseIterationStatement()
|
||||||
|
|
||||||
|
forin := &ast.ForInStatement{
|
||||||
|
Into: into,
|
||||||
|
Source: source,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
|
||||||
|
return forin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement {
|
||||||
|
|
||||||
|
// Already have consumed "<initializer> ;"
|
||||||
|
|
||||||
|
var test, update ast.Expression
|
||||||
|
|
||||||
|
if self.token != token.SEMICOLON {
|
||||||
|
test = self.parseExpression()
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.expect(token.SEMICOLON)
|
||||||
|
|
||||||
|
if self.token != token.RIGHT_PARENTHESIS {
|
||||||
|
update = self.parseExpression()
|
||||||
|
}
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
body := self.parseIterationStatement()
|
||||||
|
|
||||||
|
forstatement := &ast.ForStatement{
|
||||||
|
Initializer: initializer,
|
||||||
|
Test: test,
|
||||||
|
Update: update,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
|
||||||
|
return forstatement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseForOrForInStatement() ast.Statement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
idx := self.expect(token.FOR)
|
||||||
|
var forComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
forComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
|
||||||
|
var left []ast.Expression
|
||||||
|
|
||||||
|
forIn := false
|
||||||
|
if self.token != token.SEMICOLON {
|
||||||
|
|
||||||
|
allowIn := self.scope.allowIn
|
||||||
|
self.scope.allowIn = false
|
||||||
|
if self.token == token.VAR {
|
||||||
|
var_ := self.idx
|
||||||
|
var varComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
varComments = self.comments.FetchAll()
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.next()
|
||||||
|
list := self.parseVariableDeclarationList(var_)
|
||||||
|
if len(list) == 1 && self.token == token.IN {
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.next() // in
|
||||||
|
forIn = true
|
||||||
|
left = []ast.Expression{list[0]} // There is only one declaration
|
||||||
|
} else {
|
||||||
|
left = list
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(left[0], varComments, ast.LEADING)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
left = append(left, self.parseExpression())
|
||||||
|
if self.token == token.IN {
|
||||||
|
self.next()
|
||||||
|
forIn = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.scope.allowIn = allowIn
|
||||||
|
}
|
||||||
|
|
||||||
|
if forIn {
|
||||||
|
switch left[0].(type) {
|
||||||
|
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.VariableExpression:
|
||||||
|
// These are all acceptable
|
||||||
|
default:
|
||||||
|
self.error(idx, "Invalid left-hand side in for-in")
|
||||||
|
self.nextStatement()
|
||||||
|
return &ast.BadStatement{From: idx, To: self.idx}
|
||||||
|
}
|
||||||
|
|
||||||
|
forin := self.parseForIn(left[0])
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(forin, comments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(forin, forComments, ast.FOR)
|
||||||
|
}
|
||||||
|
return forin
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.expect(token.SEMICOLON)
|
||||||
|
initializer := &ast.SequenceExpression{Sequence: left}
|
||||||
|
forstatement := self.parseFor(initializer)
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(forstatement, comments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(forstatement, forComments, ast.FOR)
|
||||||
|
}
|
||||||
|
return forstatement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseVariableStatement() *ast.VariableStatement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
idx := self.expect(token.VAR)
|
||||||
|
|
||||||
|
list := self.parseVariableDeclarationList(idx)
|
||||||
|
|
||||||
|
statement := &ast.VariableStatement{
|
||||||
|
Var: idx,
|
||||||
|
List: list,
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(statement, comments, ast.LEADING)
|
||||||
|
self.comments.Unset()
|
||||||
|
}
|
||||||
|
self.semicolon()
|
||||||
|
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseDoWhileStatement() ast.Statement {
|
||||||
|
inIteration := self.scope.inIteration
|
||||||
|
self.scope.inIteration = true
|
||||||
|
defer func() {
|
||||||
|
self.scope.inIteration = inIteration
|
||||||
|
}()
|
||||||
|
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.expect(token.DO)
|
||||||
|
var doComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
doComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &ast.DoWhileStatement{}
|
||||||
|
if self.token == token.LEFT_BRACE {
|
||||||
|
node.Body = self.parseBlockStatement()
|
||||||
|
} else {
|
||||||
|
node.Body = self.parseStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.WHILE)
|
||||||
|
var whileComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
whileComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
node.Test = self.parseExpression()
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(node, doComments, ast.DO)
|
||||||
|
self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseWhileStatement() ast.Statement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.expect(token.WHILE)
|
||||||
|
|
||||||
|
var whileComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
whileComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
node := &ast.WhileStatement{
|
||||||
|
Test: self.parseExpression(),
|
||||||
|
}
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
node.Body = self.parseIterationStatement()
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseIfStatement() ast.Statement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
self.expect(token.IF)
|
||||||
|
var ifComments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
ifComments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.LEFT_PARENTHESIS)
|
||||||
|
node := &ast.IfStatement{
|
||||||
|
If: self.idx,
|
||||||
|
Test: self.parseExpression(),
|
||||||
|
}
|
||||||
|
self.expect(token.RIGHT_PARENTHESIS)
|
||||||
|
if self.token == token.LEFT_BRACE {
|
||||||
|
node.Consequent = self.parseBlockStatement()
|
||||||
|
} else {
|
||||||
|
node.Consequent = self.parseStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.token == token.ELSE {
|
||||||
|
self.next()
|
||||||
|
node.Alternate = self.parseStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(node, ifComments, ast.IF)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseSourceElement() ast.Statement {
|
||||||
|
statement := self.parseStatement()
|
||||||
|
//self.comments.Unset()
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseSourceElements() []ast.Statement {
|
||||||
|
body := []ast.Statement(nil)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if self.token != token.STRING {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
body = append(body, self.parseSourceElement())
|
||||||
|
}
|
||||||
|
|
||||||
|
for self.token != token.EOF {
|
||||||
|
body = append(body, self.parseSourceElement())
|
||||||
|
}
|
||||||
|
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseProgram() *ast.Program {
|
||||||
|
self.openScope()
|
||||||
|
defer self.closeScope()
|
||||||
|
return &ast.Program{
|
||||||
|
Body: self.parseSourceElements(),
|
||||||
|
DeclarationList: self.scope.declarationList,
|
||||||
|
File: self.file,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseBreakStatement() ast.Statement {
|
||||||
|
var comments []*ast.Comment
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
comments = self.comments.FetchAll()
|
||||||
|
}
|
||||||
|
idx := self.expect(token.BREAK)
|
||||||
|
semicolon := self.implicitSemicolon
|
||||||
|
if self.token == token.SEMICOLON {
|
||||||
|
semicolon = true
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if semicolon || self.token == token.RIGHT_BRACE {
|
||||||
|
self.implicitSemicolon = false
|
||||||
|
if !self.scope.inIteration && !self.scope.inSwitch {
|
||||||
|
goto illegal
|
||||||
|
}
|
||||||
|
breakStatement := &ast.BranchStatement{
|
||||||
|
Idx: idx,
|
||||||
|
Token: token.BREAK,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING)
|
||||||
|
self.comments.CommentMap.AddComments(breakStatement, self.comments.FetchAll(), ast.TRAILING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return breakStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.token == token.IDENTIFIER {
|
||||||
|
identifier := self.parseIdentifier()
|
||||||
|
if !self.scope.hasLabel(identifier.Name) {
|
||||||
|
self.error(idx, "Undefined label '%s'", identifier.Name)
|
||||||
|
return &ast.BadStatement{From: idx, To: identifier.Idx1()}
|
||||||
|
}
|
||||||
|
self.semicolon()
|
||||||
|
breakStatement := &ast.BranchStatement{
|
||||||
|
Idx: idx,
|
||||||
|
Token: token.BREAK,
|
||||||
|
Label: identifier,
|
||||||
|
}
|
||||||
|
if self.mode&StoreComments != 0 {
|
||||||
|
self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING)
|
||||||
|
}
|
||||||
|
|
||||||
|
return breakStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.IDENTIFIER)
|
||||||
|
|
||||||
|
illegal:
|
||||||
|
self.error(idx, "Illegal break statement")
|
||||||
|
self.nextStatement()
|
||||||
|
return &ast.BadStatement{From: idx, To: self.idx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_parser) parseContinueStatement() ast.Statement {
|
||||||
|
idx := self.expect(token.CONTINUE)
|
||||||
|
semicolon := self.implicitSemicolon
|
||||||
|
if self.token == token.SEMICOLON {
|
||||||
|
semicolon = true
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if semicolon || self.token == token.RIGHT_BRACE {
|
||||||
|
self.implicitSemicolon = false
|
||||||
|
if !self.scope.inIteration {
|
||||||
|
goto illegal
|
||||||
|
}
|
||||||
|
return &ast.BranchStatement{
|
||||||
|
Idx: idx,
|
||||||
|
Token: token.CONTINUE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.token == token.IDENTIFIER {
|
||||||
|
identifier := self.parseIdentifier()
|
||||||
|
if !self.scope.hasLabel(identifier.Name) {
|
||||||
|
self.error(idx, "Undefined label '%s'", identifier.Name)
|
||||||
|
return &ast.BadStatement{From: idx, To: identifier.Idx1()}
|
||||||
|
}
|
||||||
|
if !self.scope.inIteration {
|
||||||
|
goto illegal
|
||||||
|
}
|
||||||
|
self.semicolon()
|
||||||
|
return &ast.BranchStatement{
|
||||||
|
Idx: idx,
|
||||||
|
Token: token.CONTINUE,
|
||||||
|
Label: identifier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect(token.IDENTIFIER)
|
||||||
|
|
||||||
|
illegal:
|
||||||
|
self.error(idx, "Illegal continue statement")
|
||||||
|
self.nextStatement()
|
||||||
|
return &ast.BadStatement{From: idx, To: self.idx}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the next statement after an error (recover)
|
||||||
|
func (self *_parser) nextStatement() {
|
||||||
|
for {
|
||||||
|
switch self.token {
|
||||||
|
case token.BREAK, token.CONTINUE,
|
||||||
|
token.FOR, token.IF, token.RETURN, token.SWITCH,
|
||||||
|
token.VAR, token.DO, token.TRY, token.WITH,
|
||||||
|
token.WHILE, token.THROW, token.CATCH, token.FINALLY:
|
||||||
|
// Return only if parser made some progress since last
|
||||||
|
// sync or if it has not reached 10 next calls without
|
||||||
|
// progress. Otherwise consume at least one token to
|
||||||
|
// avoid an endless parser loop
|
||||||
|
if self.idx == self.recover.idx && self.recover.count < 10 {
|
||||||
|
self.recover.count++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.idx > self.recover.idx {
|
||||||
|
self.recover.idx = self.idx
|
||||||
|
self.recover.count = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Reaching here indicates a parser bug, likely an
|
||||||
|
// incorrect token list in this function, but it only
|
||||||
|
// leads to skipping of possibly correct code if a
|
||||||
|
// previous error is present, and thus is preferred
|
||||||
|
// over a non-terminating parse.
|
||||||
|
case token.EOF:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
|
}
|
220
vendor/github.com/robertkrimen/otto/property.go
generated
vendored
Normal file
220
vendor/github.com/robertkrimen/otto/property.go
generated
vendored
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
// property
|
||||||
|
|
||||||
|
type _propertyMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
modeWriteMask _propertyMode = 0700
|
||||||
|
modeEnumerateMask = 0070
|
||||||
|
modeConfigureMask = 0007
|
||||||
|
modeOnMask = 0111
|
||||||
|
modeOffMask = 0000
|
||||||
|
modeSetMask = 0222 // If value is 2, then mode is neither "On" nor "Off"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _propertyGetSet [2]*_object
|
||||||
|
|
||||||
|
var _nilGetSetObject _object = _object{}
|
||||||
|
|
||||||
|
type _property struct {
|
||||||
|
value interface{}
|
||||||
|
mode _propertyMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) writable() bool {
|
||||||
|
return self.mode&modeWriteMask == modeWriteMask&modeOnMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_property) writeOn() {
|
||||||
|
self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeOnMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_property) writeOff() {
|
||||||
|
self.mode &= ^modeWriteMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_property) writeClear() {
|
||||||
|
self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeSetMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) writeSet() bool {
|
||||||
|
return 0 == self.mode&modeWriteMask&modeSetMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) enumerable() bool {
|
||||||
|
return self.mode&modeEnumerateMask == modeEnumerateMask&modeOnMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_property) enumerateOn() {
|
||||||
|
self.mode = (self.mode & ^modeEnumerateMask) | (modeEnumerateMask & modeOnMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_property) enumerateOff() {
|
||||||
|
self.mode &= ^modeEnumerateMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) enumerateSet() bool {
|
||||||
|
return 0 == self.mode&modeEnumerateMask&modeSetMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) configurable() bool {
|
||||||
|
return self.mode&modeConfigureMask == modeConfigureMask&modeOnMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_property) configureOn() {
|
||||||
|
self.mode = (self.mode & ^modeConfigureMask) | (modeConfigureMask & modeOnMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_property) configureOff() {
|
||||||
|
self.mode &= ^modeConfigureMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) configureSet() bool {
|
||||||
|
return 0 == self.mode&modeConfigureMask&modeSetMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) copy() *_property {
|
||||||
|
property := self
|
||||||
|
return &property
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) get(this *_object) Value {
|
||||||
|
switch value := self.value.(type) {
|
||||||
|
case Value:
|
||||||
|
return value
|
||||||
|
case _propertyGetSet:
|
||||||
|
if value[0] != nil {
|
||||||
|
return value[0].call(toValue(this), nil, false, nativeFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) isAccessorDescriptor() bool {
|
||||||
|
setGet, test := self.value.(_propertyGetSet)
|
||||||
|
return test && (setGet[0] != nil || setGet[1] != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) isDataDescriptor() bool {
|
||||||
|
if self.writeSet() { // Either "On" or "Off"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
value, valid := self.value.(Value)
|
||||||
|
return valid && !value.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) isGenericDescriptor() bool {
|
||||||
|
return !(self.isDataDescriptor() || self.isAccessorDescriptor())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _property) isEmpty() bool {
|
||||||
|
return self.mode == 0222 && self.isGenericDescriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// _enumerableValue, _enumerableTrue, _enumerableFalse?
|
||||||
|
// .enumerableValue() .enumerableExists()
|
||||||
|
|
||||||
|
func toPropertyDescriptor(rt *_runtime, value Value) (descriptor _property) {
|
||||||
|
objectDescriptor := value._object()
|
||||||
|
if objectDescriptor == nil {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
descriptor.mode = modeSetMask // Initially nothing is set
|
||||||
|
if objectDescriptor.hasProperty("enumerable") {
|
||||||
|
if objectDescriptor.get("enumerable").bool() {
|
||||||
|
descriptor.enumerateOn()
|
||||||
|
} else {
|
||||||
|
descriptor.enumerateOff()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if objectDescriptor.hasProperty("configurable") {
|
||||||
|
if objectDescriptor.get("configurable").bool() {
|
||||||
|
descriptor.configureOn()
|
||||||
|
} else {
|
||||||
|
descriptor.configureOff()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if objectDescriptor.hasProperty("writable") {
|
||||||
|
if objectDescriptor.get("writable").bool() {
|
||||||
|
descriptor.writeOn()
|
||||||
|
} else {
|
||||||
|
descriptor.writeOff()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var getter, setter *_object
|
||||||
|
getterSetter := false
|
||||||
|
|
||||||
|
if objectDescriptor.hasProperty("get") {
|
||||||
|
value := objectDescriptor.get("get")
|
||||||
|
if value.IsDefined() {
|
||||||
|
if !value.isCallable() {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
getter = value._object()
|
||||||
|
getterSetter = true
|
||||||
|
} else {
|
||||||
|
getter = &_nilGetSetObject
|
||||||
|
getterSetter = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if objectDescriptor.hasProperty("set") {
|
||||||
|
value := objectDescriptor.get("set")
|
||||||
|
if value.IsDefined() {
|
||||||
|
if !value.isCallable() {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
setter = value._object()
|
||||||
|
getterSetter = true
|
||||||
|
} else {
|
||||||
|
setter = &_nilGetSetObject
|
||||||
|
getterSetter = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if getterSetter {
|
||||||
|
if descriptor.writeSet() {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
descriptor.value = _propertyGetSet{getter, setter}
|
||||||
|
}
|
||||||
|
|
||||||
|
if objectDescriptor.hasProperty("value") {
|
||||||
|
if getterSetter {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
descriptor.value = objectDescriptor.get("value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) fromPropertyDescriptor(descriptor _property) *_object {
|
||||||
|
object := self.newObject()
|
||||||
|
if descriptor.isDataDescriptor() {
|
||||||
|
object.defineProperty("value", descriptor.value.(Value), 0111, false)
|
||||||
|
object.defineProperty("writable", toValue_bool(descriptor.writable()), 0111, false)
|
||||||
|
} else if descriptor.isAccessorDescriptor() {
|
||||||
|
getSet := descriptor.value.(_propertyGetSet)
|
||||||
|
get := Value{}
|
||||||
|
if getSet[0] != nil {
|
||||||
|
get = toValue_object(getSet[0])
|
||||||
|
}
|
||||||
|
set := Value{}
|
||||||
|
if getSet[1] != nil {
|
||||||
|
set = toValue_object(getSet[1])
|
||||||
|
}
|
||||||
|
object.defineProperty("get", get, 0111, false)
|
||||||
|
object.defineProperty("set", set, 0111, false)
|
||||||
|
}
|
||||||
|
object.defineProperty("enumerable", toValue_bool(descriptor.enumerable()), 0111, false)
|
||||||
|
object.defineProperty("configurable", toValue_bool(descriptor.configurable()), 0111, false)
|
||||||
|
return object
|
||||||
|
}
|
51
vendor/github.com/robertkrimen/otto/registry/README.markdown
generated
vendored
Normal file
51
vendor/github.com/robertkrimen/otto/registry/README.markdown
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# registry
|
||||||
|
--
|
||||||
|
import "github.com/robertkrimen/otto/registry"
|
||||||
|
|
||||||
|
Package registry is an expirmental package to facillitate altering the otto
|
||||||
|
runtime via import.
|
||||||
|
|
||||||
|
This interface can change at any time.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
#### func Apply
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Apply(callback func(Entry))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### type Entry
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Entry struct {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### func Register
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Register(source func() string) *Entry
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*Entry) Disable
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *Entry) Disable()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (*Entry) Enable
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self *Entry) Enable()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### func (Entry) Source
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (self Entry) Source() string
|
||||||
|
```
|
||||||
|
|
||||||
|
--
|
||||||
|
**godocdown** http://github.com/robertkrimen/godocdown
|
47
vendor/github.com/robertkrimen/otto/registry/registry.go
generated
vendored
Normal file
47
vendor/github.com/robertkrimen/otto/registry/registry.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Package registry is an expirmental package to facillitate altering the otto runtime via import.
|
||||||
|
|
||||||
|
This interface can change at any time.
|
||||||
|
*/
|
||||||
|
package registry
|
||||||
|
|
||||||
|
var registry []*Entry = make([]*Entry, 0)
|
||||||
|
|
||||||
|
type Entry struct {
|
||||||
|
active bool
|
||||||
|
source func() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEntry(source func() string) *Entry {
|
||||||
|
return &Entry{
|
||||||
|
active: true,
|
||||||
|
source: source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Entry) Enable() {
|
||||||
|
self.active = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Entry) Disable() {
|
||||||
|
self.active = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Entry) Source() string {
|
||||||
|
return self.source()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Apply(callback func(Entry)) {
|
||||||
|
for _, entry := range registry {
|
||||||
|
if !entry.active {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
callback(*entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Register(source func() string) *Entry {
|
||||||
|
entry := newEntry(source)
|
||||||
|
registry = append(registry, entry)
|
||||||
|
return entry
|
||||||
|
}
|
30
vendor/github.com/robertkrimen/otto/result.go
generated
vendored
Normal file
30
vendor/github.com/robertkrimen/otto/result.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import ()
|
||||||
|
|
||||||
|
type _resultKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
resultNormal _resultKind = iota
|
||||||
|
resultReturn
|
||||||
|
resultBreak
|
||||||
|
resultContinue
|
||||||
|
)
|
||||||
|
|
||||||
|
type _result struct {
|
||||||
|
kind _resultKind
|
||||||
|
value Value
|
||||||
|
target string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReturnResult(value Value) _result {
|
||||||
|
return _result{resultReturn, value, ""}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContinueResult(target string) _result {
|
||||||
|
return _result{resultContinue, emptyValue, target}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBreakResult(target string) _result {
|
||||||
|
return _result{resultBreak, emptyValue, target}
|
||||||
|
}
|
825
vendor/github.com/robertkrimen/otto/runtime.go
generated
vendored
Normal file
825
vendor/github.com/robertkrimen/otto/runtime.go
generated
vendored
Normal file
|
@ -0,0 +1,825 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
"github.com/robertkrimen/otto/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _global struct {
|
||||||
|
Object *_object // Object( ... ), new Object( ... ) - 1 (length)
|
||||||
|
Function *_object // Function( ... ), new Function( ... ) - 1
|
||||||
|
Array *_object // Array( ... ), new Array( ... ) - 1
|
||||||
|
String *_object // String( ... ), new String( ... ) - 1
|
||||||
|
Boolean *_object // Boolean( ... ), new Boolean( ... ) - 1
|
||||||
|
Number *_object // Number( ... ), new Number( ... ) - 1
|
||||||
|
Math *_object
|
||||||
|
Date *_object // Date( ... ), new Date( ... ) - 7
|
||||||
|
RegExp *_object // RegExp( ... ), new RegExp( ... ) - 2
|
||||||
|
Error *_object // Error( ... ), new Error( ... ) - 1
|
||||||
|
EvalError *_object
|
||||||
|
TypeError *_object
|
||||||
|
RangeError *_object
|
||||||
|
ReferenceError *_object
|
||||||
|
SyntaxError *_object
|
||||||
|
URIError *_object
|
||||||
|
JSON *_object
|
||||||
|
|
||||||
|
ObjectPrototype *_object // Object.prototype
|
||||||
|
FunctionPrototype *_object // Function.prototype
|
||||||
|
ArrayPrototype *_object // Array.prototype
|
||||||
|
StringPrototype *_object // String.prototype
|
||||||
|
BooleanPrototype *_object // Boolean.prototype
|
||||||
|
NumberPrototype *_object // Number.prototype
|
||||||
|
DatePrototype *_object // Date.prototype
|
||||||
|
RegExpPrototype *_object // RegExp.prototype
|
||||||
|
ErrorPrototype *_object // Error.prototype
|
||||||
|
EvalErrorPrototype *_object
|
||||||
|
TypeErrorPrototype *_object
|
||||||
|
RangeErrorPrototype *_object
|
||||||
|
ReferenceErrorPrototype *_object
|
||||||
|
SyntaxErrorPrototype *_object
|
||||||
|
URIErrorPrototype *_object
|
||||||
|
}
|
||||||
|
|
||||||
|
type _runtime struct {
|
||||||
|
global _global
|
||||||
|
globalObject *_object
|
||||||
|
globalStash *_objectStash
|
||||||
|
scope *_scope
|
||||||
|
otto *Otto
|
||||||
|
eval *_object // The builtin eval, for determine indirect versus direct invocation
|
||||||
|
debugger func(*Otto)
|
||||||
|
random func() float64
|
||||||
|
stackLimit int
|
||||||
|
traceLimit int
|
||||||
|
|
||||||
|
labels []string // FIXME
|
||||||
|
lck sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) enterScope(scope *_scope) {
|
||||||
|
scope.outer = self.scope
|
||||||
|
if self.scope != nil {
|
||||||
|
if self.stackLimit != 0 && self.scope.depth+1 >= self.stackLimit {
|
||||||
|
panic(self.panicRangeError("Maximum call stack size exceeded"))
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.depth = self.scope.depth + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scope = scope
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) leaveScope() {
|
||||||
|
self.scope = self.scope.outer
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME This is used in two places (cloning)
|
||||||
|
func (self *_runtime) enterGlobalScope() {
|
||||||
|
self.enterScope(newScope(self.globalStash, self.globalStash, self.globalObject))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) enterFunctionScope(outer _stash, this Value) *_fnStash {
|
||||||
|
if outer == nil {
|
||||||
|
outer = self.globalStash
|
||||||
|
}
|
||||||
|
stash := self.newFunctionStash(outer)
|
||||||
|
var thisObject *_object
|
||||||
|
switch this.kind {
|
||||||
|
case valueUndefined, valueNull:
|
||||||
|
thisObject = self.globalObject
|
||||||
|
default:
|
||||||
|
thisObject = self.toObject(this)
|
||||||
|
}
|
||||||
|
self.enterScope(newScope(stash, stash, thisObject))
|
||||||
|
return stash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) putValue(reference _reference, value Value) {
|
||||||
|
name := reference.putValue(value)
|
||||||
|
if name != "" {
|
||||||
|
// Why? -- If reference.base == nil
|
||||||
|
// strict = false
|
||||||
|
self.globalObject.defineProperty(name, value, 0111, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exception bool) {
|
||||||
|
// resultValue = The value of the block (e.g. the last statement)
|
||||||
|
// throw = Something was thrown
|
||||||
|
// throwValue = The value of what was thrown
|
||||||
|
// other = Something that changes flow (return, break, continue) that is not a throw
|
||||||
|
// Otherwise, some sort of unknown panic happened, we'll just propagate it
|
||||||
|
defer func() {
|
||||||
|
if caught := recover(); caught != nil {
|
||||||
|
if exception, ok := caught.(*_exception); ok {
|
||||||
|
caught = exception.eject()
|
||||||
|
}
|
||||||
|
switch caught := caught.(type) {
|
||||||
|
case _error:
|
||||||
|
exception = true
|
||||||
|
tryValue = toValue_object(self.newError(caught.name, caught.messageValue(), 0))
|
||||||
|
case Value:
|
||||||
|
exception = true
|
||||||
|
tryValue = caught
|
||||||
|
default:
|
||||||
|
panic(caught)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tryValue = inner()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// toObject
|
||||||
|
|
||||||
|
func (self *_runtime) toObject(value Value) *_object {
|
||||||
|
switch value.kind {
|
||||||
|
case valueEmpty, valueUndefined, valueNull:
|
||||||
|
panic(self.panicTypeError())
|
||||||
|
case valueBoolean:
|
||||||
|
return self.newBoolean(value)
|
||||||
|
case valueString:
|
||||||
|
return self.newString(value)
|
||||||
|
case valueNumber:
|
||||||
|
return self.newNumber(value)
|
||||||
|
case valueObject:
|
||||||
|
return value._object()
|
||||||
|
}
|
||||||
|
panic(self.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) objectCoerce(value Value) (*_object, error) {
|
||||||
|
switch value.kind {
|
||||||
|
case valueUndefined:
|
||||||
|
return nil, errors.New("undefined")
|
||||||
|
case valueNull:
|
||||||
|
return nil, errors.New("null")
|
||||||
|
case valueBoolean:
|
||||||
|
return self.newBoolean(value), nil
|
||||||
|
case valueString:
|
||||||
|
return self.newString(value), nil
|
||||||
|
case valueNumber:
|
||||||
|
return self.newNumber(value), nil
|
||||||
|
case valueObject:
|
||||||
|
return value._object(), nil
|
||||||
|
}
|
||||||
|
panic(self.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkObjectCoercible(rt *_runtime, value Value) {
|
||||||
|
isObject, mustCoerce := testObjectCoercible(value)
|
||||||
|
if !isObject && !mustCoerce {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testObjectCoercible
|
||||||
|
|
||||||
|
func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
|
||||||
|
switch value.kind {
|
||||||
|
case valueReference, valueEmpty, valueNull, valueUndefined:
|
||||||
|
return false, false
|
||||||
|
case valueNumber, valueString, valueBoolean:
|
||||||
|
return false, true
|
||||||
|
case valueObject:
|
||||||
|
return true, false
|
||||||
|
default:
|
||||||
|
panic("this should never happen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) safeToValue(value interface{}) (Value, error) {
|
||||||
|
result := Value{}
|
||||||
|
err := catchPanic(func() {
|
||||||
|
result = self.toValue(value)
|
||||||
|
})
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertNumeric converts numeric parameter val from js to that of type t if it is safe to do so, otherwise it panics.
|
||||||
|
// This allows literals (int64), bitwise values (int32) and the general form (float64) of javascript numerics to be passed as parameters to go functions easily.
|
||||||
|
func (self *_runtime) convertNumeric(v Value, t reflect.Type) reflect.Value {
|
||||||
|
val := reflect.ValueOf(v.export())
|
||||||
|
|
||||||
|
if val.Kind() == t.Kind() {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Interface {
|
||||||
|
val = reflect.ValueOf(val.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
f64 := val.Float()
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Float64:
|
||||||
|
return reflect.ValueOf(f64)
|
||||||
|
case reflect.Float32:
|
||||||
|
if reflect.Zero(t).OverflowFloat(f64) {
|
||||||
|
panic(self.panicRangeError("converting float64 to float32 would overflow"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.Convert(t)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
i64 := int64(f64)
|
||||||
|
if float64(i64) != f64 {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would cause loss of precision", val.Type(), t)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The float represents an integer
|
||||||
|
val = reflect.ValueOf(i64)
|
||||||
|
default:
|
||||||
|
panic(self.panicTypeError(fmt.Sprintf("cannot convert %v to %v", val.Type(), t)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
i64 := val.Int()
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if reflect.Zero(t).OverflowInt(i64) {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
|
}
|
||||||
|
return val.Convert(t)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
if i64 < 0 {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would underflow", val.Type(), t)))
|
||||||
|
}
|
||||||
|
if reflect.Zero(t).OverflowUint(uint64(i64)) {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
|
}
|
||||||
|
return val.Convert(t)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return val.Convert(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
u64 := val.Uint()
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if u64 > math.MaxInt64 || reflect.Zero(t).OverflowInt(int64(u64)) {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
|
}
|
||||||
|
return val.Convert(t)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
if reflect.Zero(t).OverflowUint(u64) {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
|
}
|
||||||
|
return val.Convert(t)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return val.Convert(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(self.panicTypeError(fmt.Sprintf("unsupported type %v -> %v for numeric conversion", val.Type(), t)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldIndexByName(t reflect.Type, name string) []int {
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
|
||||||
|
if !validGoStructName(f.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Anonymous {
|
||||||
|
if a := fieldIndexByName(f.Type, name); a != nil {
|
||||||
|
return append([]int{i}, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a := strings.SplitN(f.Tag.Get("json"), ",", 2); a[0] != "" {
|
||||||
|
if a[0] == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[0] == name {
|
||||||
|
return []int{i}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Name == name {
|
||||||
|
return []int{i}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeOfValue = reflect.TypeOf(Value{})
|
||||||
|
var typeOfJSONRawMessage = reflect.TypeOf(json.RawMessage{})
|
||||||
|
|
||||||
|
// convertCallParameter converts request val to type t if possible.
|
||||||
|
// If the conversion fails due to overflow or type miss-match then it panics.
|
||||||
|
// If no conversion is known then the original value is returned.
|
||||||
|
func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Value {
|
||||||
|
if t == typeOfValue {
|
||||||
|
return reflect.ValueOf(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t == typeOfJSONRawMessage {
|
||||||
|
if d, err := json.Marshal(v.export()); err == nil {
|
||||||
|
return reflect.ValueOf(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.kind == valueObject {
|
||||||
|
if gso, ok := v._object().value.(*_goStructObject); ok {
|
||||||
|
if gso.value.Type().AssignableTo(t) {
|
||||||
|
// please see TestDynamicFunctionReturningInterface for why this exists
|
||||||
|
if t.Kind() == reflect.Interface && gso.value.Type().ConvertibleTo(t) {
|
||||||
|
return gso.value.Convert(t)
|
||||||
|
} else {
|
||||||
|
return gso.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gao, ok := v._object().value.(*_goArrayObject); ok {
|
||||||
|
if gao.value.Type().AssignableTo(t) {
|
||||||
|
// please see TestDynamicFunctionReturningInterface for why this exists
|
||||||
|
if t.Kind() == reflect.Interface && gao.value.Type().ConvertibleTo(t) {
|
||||||
|
return gao.value.Convert(t)
|
||||||
|
} else {
|
||||||
|
return gao.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind() == reflect.Interface {
|
||||||
|
e := v.export()
|
||||||
|
if e == nil {
|
||||||
|
return reflect.Zero(t)
|
||||||
|
}
|
||||||
|
iv := reflect.ValueOf(e)
|
||||||
|
if iv.Type().AssignableTo(t) {
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := t.Kind()
|
||||||
|
|
||||||
|
if tk == reflect.Ptr {
|
||||||
|
switch v.kind {
|
||||||
|
case valueEmpty, valueNull, valueUndefined:
|
||||||
|
return reflect.Zero(t)
|
||||||
|
default:
|
||||||
|
var vv reflect.Value
|
||||||
|
if err := catchPanic(func() { vv = self.convertCallParameter(v, t.Elem()) }); err == nil {
|
||||||
|
if vv.CanAddr() {
|
||||||
|
return vv.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pv := reflect.New(vv.Type())
|
||||||
|
pv.Elem().Set(vv)
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tk {
|
||||||
|
case reflect.Bool:
|
||||||
|
return reflect.ValueOf(v.bool())
|
||||||
|
case reflect.String:
|
||||||
|
switch v.kind {
|
||||||
|
case valueString:
|
||||||
|
return reflect.ValueOf(v.value)
|
||||||
|
case valueNumber:
|
||||||
|
return reflect.ValueOf(fmt.Sprintf("%v", v.value))
|
||||||
|
}
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||||
|
switch v.kind {
|
||||||
|
case valueNumber:
|
||||||
|
return self.convertNumeric(v, t)
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if o := v._object(); o != nil {
|
||||||
|
if lv := o.get("length"); lv.IsNumber() {
|
||||||
|
l := lv.number().int64
|
||||||
|
|
||||||
|
s := reflect.MakeSlice(t, int(l), int(l))
|
||||||
|
|
||||||
|
tt := t.Elem()
|
||||||
|
|
||||||
|
if o.class == "Array" {
|
||||||
|
for i := int64(0); i < l; i++ {
|
||||||
|
p, ok := o.property[strconv.FormatInt(i, 10)]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
e, ok := p.value.(Value)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ev := self.convertCallParameter(e, tt)
|
||||||
|
|
||||||
|
s.Index(int(i)).Set(ev)
|
||||||
|
}
|
||||||
|
} else if o.class == "GoArray" {
|
||||||
|
|
||||||
|
var gslice bool
|
||||||
|
switch o.value.(type) {
|
||||||
|
case *_goSliceObject:
|
||||||
|
gslice = true
|
||||||
|
case *_goArrayObject:
|
||||||
|
gslice = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := int64(0); i < l; i++ {
|
||||||
|
var p *_property
|
||||||
|
if gslice {
|
||||||
|
p = goSliceGetOwnProperty(o, strconv.FormatInt(i, 10))
|
||||||
|
} else {
|
||||||
|
p = goArrayGetOwnProperty(o, strconv.FormatInt(i, 10))
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
e, ok := p.value.(Value)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ev := self.convertCallParameter(e, tt)
|
||||||
|
|
||||||
|
s.Index(int(i)).Set(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if o := v._object(); o != nil && t.Key().Kind() == reflect.String {
|
||||||
|
m := reflect.MakeMap(t)
|
||||||
|
|
||||||
|
o.enumerate(false, func(k string) bool {
|
||||||
|
m.SetMapIndex(reflect.ValueOf(k), self.convertCallParameter(o.get(k), t.Elem()))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
case reflect.Func:
|
||||||
|
if t.NumOut() > 1 {
|
||||||
|
panic(self.panicTypeError("converting JavaScript values to Go functions with more than one return value is currently not supported"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if o := v._object(); o != nil && o.class == "Function" {
|
||||||
|
return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value {
|
||||||
|
l := make([]interface{}, len(args))
|
||||||
|
for i, a := range args {
|
||||||
|
if a.CanInterface() {
|
||||||
|
l[i] = a.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv, err := v.Call(nullValue, l...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.NumOut() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []reflect.Value{self.convertCallParameter(rv, t.Out(0))}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
if o := v._object(); o != nil && o.class == "Object" {
|
||||||
|
s := reflect.New(t)
|
||||||
|
|
||||||
|
for _, k := range o.propertyOrder {
|
||||||
|
idx := fieldIndexByName(t, k)
|
||||||
|
|
||||||
|
if idx == nil {
|
||||||
|
panic(self.panicTypeError("can't convert object; field %q was supplied but does not exist on target %v", k, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := s
|
||||||
|
|
||||||
|
for _, i := range idx {
|
||||||
|
if ss.Kind() == reflect.Ptr {
|
||||||
|
if ss.IsNil() {
|
||||||
|
if !ss.CanSet() {
|
||||||
|
panic(self.panicTypeError("can't set embedded pointer to unexported struct: %v", ss.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Set(reflect.New(ss.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = ss.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = ss.Field(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Set(self.convertCallParameter(o.get(k), ss.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk == reflect.String {
|
||||||
|
if o := v._object(); o != nil && o.hasProperty("toString") {
|
||||||
|
if fn := o.get("toString"); fn.IsFunction() {
|
||||||
|
sv, err := fn.Call(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r reflect.Value
|
||||||
|
if err := catchPanic(func() { r = self.convertCallParameter(sv, t) }); err == nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.ValueOf(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.kind == valueString {
|
||||||
|
var s encoding.TextUnmarshaler
|
||||||
|
|
||||||
|
if reflect.PtrTo(t).Implements(reflect.TypeOf(&s).Elem()) {
|
||||||
|
r := reflect.New(t)
|
||||||
|
|
||||||
|
if err := r.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(v.string())); err != nil {
|
||||||
|
panic(self.panicSyntaxError("can't convert to %s: %s", t.String(), err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := "OTTO DOES NOT UNDERSTAND THIS TYPE"
|
||||||
|
switch v.kind {
|
||||||
|
case valueBoolean:
|
||||||
|
s = "boolean"
|
||||||
|
case valueNull:
|
||||||
|
s = "null"
|
||||||
|
case valueNumber:
|
||||||
|
s = "number"
|
||||||
|
case valueString:
|
||||||
|
s = "string"
|
||||||
|
case valueUndefined:
|
||||||
|
s = "undefined"
|
||||||
|
case valueObject:
|
||||||
|
s = v.Class()
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(self.panicTypeError("can't convert from %q to %q", s, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) toValue(value interface{}) Value {
|
||||||
|
switch value := value.(type) {
|
||||||
|
case Value:
|
||||||
|
return value
|
||||||
|
case func(FunctionCall) Value:
|
||||||
|
var name, file string
|
||||||
|
var line int
|
||||||
|
pc := reflect.ValueOf(value).Pointer()
|
||||||
|
fn := runtime.FuncForPC(pc)
|
||||||
|
if fn != nil {
|
||||||
|
name = fn.Name()
|
||||||
|
file, line = fn.FileLine(pc)
|
||||||
|
file = path.Base(file)
|
||||||
|
}
|
||||||
|
return toValue_object(self.newNativeFunction(name, file, line, value))
|
||||||
|
case _nativeFunction:
|
||||||
|
var name, file string
|
||||||
|
var line int
|
||||||
|
pc := reflect.ValueOf(value).Pointer()
|
||||||
|
fn := runtime.FuncForPC(pc)
|
||||||
|
if fn != nil {
|
||||||
|
name = fn.Name()
|
||||||
|
file, line = fn.FileLine(pc)
|
||||||
|
file = path.Base(file)
|
||||||
|
}
|
||||||
|
return toValue_object(self.newNativeFunction(name, file, line, value))
|
||||||
|
case Object, *Object, _object, *_object:
|
||||||
|
// Nothing happens.
|
||||||
|
// FIXME We should really figure out what can come here.
|
||||||
|
// This catch-all is ugly.
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
value := reflect.ValueOf(value)
|
||||||
|
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
switch reflect.Indirect(value).Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
return toValue_object(self.newGoStructObject(value))
|
||||||
|
case reflect.Array:
|
||||||
|
return toValue_object(self.newGoArray(value))
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
return toValue_object(self.newGoStructObject(value))
|
||||||
|
case reflect.Map:
|
||||||
|
return toValue_object(self.newGoMapObject(value))
|
||||||
|
case reflect.Slice:
|
||||||
|
return toValue_object(self.newGoSlice(value))
|
||||||
|
case reflect.Array:
|
||||||
|
return toValue_object(self.newGoArray(value))
|
||||||
|
case reflect.Func:
|
||||||
|
var name, file string
|
||||||
|
var line int
|
||||||
|
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr {
|
||||||
|
pc := v.Pointer()
|
||||||
|
fn := runtime.FuncForPC(pc)
|
||||||
|
if fn != nil {
|
||||||
|
name = fn.Name()
|
||||||
|
file, line = fn.FileLine(pc)
|
||||||
|
file = path.Base(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := value.Type()
|
||||||
|
|
||||||
|
return toValue_object(self.newNativeFunction(name, file, line, func(c FunctionCall) Value {
|
||||||
|
nargs := typ.NumIn()
|
||||||
|
|
||||||
|
if len(c.ArgumentList) != nargs {
|
||||||
|
if typ.IsVariadic() {
|
||||||
|
if len(c.ArgumentList) < nargs-1 {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("expected at least %d arguments; got %d", nargs-1, len(c.ArgumentList))))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("expected %d argument(s); got %d", nargs, len(c.ArgumentList))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
in := make([]reflect.Value, len(c.ArgumentList))
|
||||||
|
|
||||||
|
callSlice := false
|
||||||
|
|
||||||
|
for i, a := range c.ArgumentList {
|
||||||
|
var t reflect.Type
|
||||||
|
|
||||||
|
n := i
|
||||||
|
if n >= nargs-1 && typ.IsVariadic() {
|
||||||
|
if n > nargs-1 {
|
||||||
|
n = nargs - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
t = typ.In(n).Elem()
|
||||||
|
} else {
|
||||||
|
t = typ.In(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a variadic Go function, and the caller has supplied
|
||||||
|
// exactly the number of JavaScript arguments required, and this
|
||||||
|
// is the last JavaScript argument, try treating the it as the
|
||||||
|
// actual set of variadic Go arguments. if that succeeds, break
|
||||||
|
// out of the loop.
|
||||||
|
if typ.IsVariadic() && len(c.ArgumentList) == nargs && i == nargs-1 {
|
||||||
|
var v reflect.Value
|
||||||
|
if err := catchPanic(func() { v = self.convertCallParameter(a, typ.In(n)) }); err == nil {
|
||||||
|
in[i] = v
|
||||||
|
callSlice = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
in[i] = self.convertCallParameter(a, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []reflect.Value
|
||||||
|
if callSlice {
|
||||||
|
out = value.CallSlice(in)
|
||||||
|
} else {
|
||||||
|
out = value.Call(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(out) {
|
||||||
|
case 0:
|
||||||
|
return Value{}
|
||||||
|
case 1:
|
||||||
|
return self.toValue(out[0].Interface())
|
||||||
|
default:
|
||||||
|
s := make([]interface{}, len(out))
|
||||||
|
for i, v := range out {
|
||||||
|
s[i] = self.toValue(v.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.toValue(s)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newGoSlice(value reflect.Value) *_object {
|
||||||
|
self := runtime.newGoSliceObject(value)
|
||||||
|
self.prototype = runtime.global.ArrayPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newGoArray(value reflect.Value) *_object {
|
||||||
|
self := runtime.newGoArrayObject(value)
|
||||||
|
self.prototype = runtime.global.ArrayPrototype
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) parse(filename string, src, sm interface{}) (*ast.Program, error) {
|
||||||
|
return parser.ParseFileWithSourceMap(nil, filename, src, sm, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) cmpl_parse(filename string, src, sm interface{}) (*_nodeProgram, error) {
|
||||||
|
program, err := parser.ParseFileWithSourceMap(nil, filename, src, sm, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmpl_parse(program), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) parseSource(src, sm interface{}) (*_nodeProgram, *ast.Program, error) {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case *ast.Program:
|
||||||
|
return nil, src, nil
|
||||||
|
case *Script:
|
||||||
|
return src.program, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
program, err := self.parse("", src, sm)
|
||||||
|
|
||||||
|
return nil, program, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_runOrEval(src, sm interface{}, eval bool) (Value, error) {
|
||||||
|
result := Value{}
|
||||||
|
cmpl_program, program, err := self.parseSource(src, sm)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
if cmpl_program == nil {
|
||||||
|
cmpl_program = cmpl_parse(program)
|
||||||
|
}
|
||||||
|
err = catchPanic(func() {
|
||||||
|
result = self.cmpl_evaluate_nodeProgram(cmpl_program, eval)
|
||||||
|
})
|
||||||
|
switch result.kind {
|
||||||
|
case valueEmpty:
|
||||||
|
result = Value{}
|
||||||
|
case valueReference:
|
||||||
|
result = result.resolve()
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_run(src, sm interface{}) (Value, error) {
|
||||||
|
return self.cmpl_runOrEval(src, sm, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_eval(src, sm interface{}) (Value, error) {
|
||||||
|
return self.cmpl_runOrEval(src, sm, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) parseThrow(err error) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch err := err.(type) {
|
||||||
|
case parser.ErrorList:
|
||||||
|
{
|
||||||
|
err := err[0]
|
||||||
|
if err.Message == "Invalid left-hand side in assignment" {
|
||||||
|
panic(self.panicReferenceError(err.Message))
|
||||||
|
}
|
||||||
|
panic(self.panicSyntaxError(err.Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(self.panicSyntaxError(err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_runtime) cmpl_parseOrThrow(src, sm interface{}) *_nodeProgram {
|
||||||
|
program, err := self.cmpl_parse("", src, sm)
|
||||||
|
self.parseThrow(err) // Will panic/throw appropriately
|
||||||
|
return program
|
||||||
|
}
|
35
vendor/github.com/robertkrimen/otto/scope.go
generated
vendored
Normal file
35
vendor/github.com/robertkrimen/otto/scope.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
// _scope:
|
||||||
|
// entryFile
|
||||||
|
// entryIdx
|
||||||
|
// top?
|
||||||
|
// outer => nil
|
||||||
|
|
||||||
|
// _stash:
|
||||||
|
// lexical
|
||||||
|
// variable
|
||||||
|
//
|
||||||
|
// _thisStash (ObjectEnvironment)
|
||||||
|
// _fnStash
|
||||||
|
// _dclStash
|
||||||
|
|
||||||
|
// An ECMA-262 ExecutionContext
|
||||||
|
type _scope struct {
|
||||||
|
lexical _stash
|
||||||
|
variable _stash
|
||||||
|
this *_object
|
||||||
|
eval bool // Replace this with kind?
|
||||||
|
outer *_scope
|
||||||
|
depth int
|
||||||
|
|
||||||
|
frame _frame
|
||||||
|
}
|
||||||
|
|
||||||
|
func newScope(lexical _stash, variable _stash, this *_object) *_scope {
|
||||||
|
return &_scope{
|
||||||
|
lexical: lexical,
|
||||||
|
variable: variable,
|
||||||
|
this: this,
|
||||||
|
}
|
||||||
|
}
|
119
vendor/github.com/robertkrimen/otto/script.go
generated
vendored
Normal file
119
vendor/github.com/robertkrimen/otto/script.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrVersion = errors.New("version mismatch")
|
||||||
|
|
||||||
|
var scriptVersion = "2014-04-13/1"
|
||||||
|
|
||||||
|
// Script is a handle for some (reusable) JavaScript.
|
||||||
|
// Passing a Script value to a run method will evaluate the JavaScript.
|
||||||
|
//
|
||||||
|
type Script struct {
|
||||||
|
version string
|
||||||
|
program *_nodeProgram
|
||||||
|
filename string
|
||||||
|
src string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile will parse the given source and return a Script value or nil and
|
||||||
|
// an error if there was a problem during compilation.
|
||||||
|
//
|
||||||
|
// script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`)
|
||||||
|
// vm.Run(script)
|
||||||
|
//
|
||||||
|
func (self *Otto) Compile(filename string, src interface{}) (*Script, error) {
|
||||||
|
return self.CompileWithSourceMap(filename, src, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompileWithSourceMap does the same thing as Compile, but with the obvious
|
||||||
|
// difference of applying a source map.
|
||||||
|
func (self *Otto) CompileWithSourceMap(filename string, src, sm interface{}) (*Script, error) {
|
||||||
|
program, err := self.runtime.parse(filename, src, sm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmpl_program := cmpl_parse(program)
|
||||||
|
|
||||||
|
script := &Script{
|
||||||
|
version: scriptVersion,
|
||||||
|
program: cmpl_program,
|
||||||
|
filename: filename,
|
||||||
|
src: program.File.Source(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return script, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Script) String() string {
|
||||||
|
return "// " + self.filename + "\n" + self.src
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary will marshal a script into a binary form. A marshalled script
|
||||||
|
// that is later unmarshalled can be executed on the same version of the otto runtime.
|
||||||
|
//
|
||||||
|
// The binary format can change at any time and should be considered unspecified and opaque.
|
||||||
|
//
|
||||||
|
func (self *Script) marshalBinary() ([]byte, error) {
|
||||||
|
var bfr bytes.Buffer
|
||||||
|
encoder := gob.NewEncoder(&bfr)
|
||||||
|
err := encoder.Encode(self.version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = encoder.Encode(self.program)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = encoder.Encode(self.filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = encoder.Encode(self.src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bfr.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary will vivify a marshalled script into something usable. If the script was
|
||||||
|
// originally marshalled on a different version of the otto runtime, then this method
|
||||||
|
// will return an error.
|
||||||
|
//
|
||||||
|
// The binary format can change at any time and should be considered unspecified and opaque.
|
||||||
|
//
|
||||||
|
func (self *Script) unmarshalBinary(data []byte) error {
|
||||||
|
decoder := gob.NewDecoder(bytes.NewReader(data))
|
||||||
|
err := decoder.Decode(&self.version)
|
||||||
|
if err != nil {
|
||||||
|
goto error
|
||||||
|
}
|
||||||
|
if self.version != scriptVersion {
|
||||||
|
err = ErrVersion
|
||||||
|
goto error
|
||||||
|
}
|
||||||
|
err = decoder.Decode(&self.program)
|
||||||
|
if err != nil {
|
||||||
|
goto error
|
||||||
|
}
|
||||||
|
err = decoder.Decode(&self.filename)
|
||||||
|
if err != nil {
|
||||||
|
goto error
|
||||||
|
}
|
||||||
|
err = decoder.Decode(&self.src)
|
||||||
|
if err != nil {
|
||||||
|
goto error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
error:
|
||||||
|
self.version = ""
|
||||||
|
self.program = nil
|
||||||
|
self.filename = ""
|
||||||
|
self.src = ""
|
||||||
|
return err
|
||||||
|
}
|
296
vendor/github.com/robertkrimen/otto/stash.go
generated
vendored
Normal file
296
vendor/github.com/robertkrimen/otto/stash.go
generated
vendored
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ======
|
||||||
|
// _stash
|
||||||
|
// ======
|
||||||
|
|
||||||
|
type _stash interface {
|
||||||
|
hasBinding(string) bool //
|
||||||
|
createBinding(string, bool, Value) // CreateMutableBinding
|
||||||
|
setBinding(string, Value, bool) // SetMutableBinding
|
||||||
|
getBinding(string, bool) Value // GetBindingValue
|
||||||
|
deleteBinding(string) bool //
|
||||||
|
setValue(string, Value, bool) // createBinding + setBinding
|
||||||
|
|
||||||
|
outer() _stash
|
||||||
|
runtime() *_runtime
|
||||||
|
|
||||||
|
newReference(string, bool, _at) _reference
|
||||||
|
|
||||||
|
clone(clone *_clone) _stash
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========
|
||||||
|
// _objectStash
|
||||||
|
// ==========
|
||||||
|
|
||||||
|
type _objectStash struct {
|
||||||
|
_runtime *_runtime
|
||||||
|
_outer _stash
|
||||||
|
object *_object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) runtime() *_runtime {
|
||||||
|
return self._runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash {
|
||||||
|
if object == nil {
|
||||||
|
object = runtime.newBaseObject()
|
||||||
|
object.class = "environment"
|
||||||
|
}
|
||||||
|
return &_objectStash{
|
||||||
|
_runtime: runtime,
|
||||||
|
_outer: outer,
|
||||||
|
object: object,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_objectStash) clone(clone *_clone) _stash {
|
||||||
|
out, exists := clone.objectStash(in)
|
||||||
|
if exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
*out = _objectStash{
|
||||||
|
clone.runtime,
|
||||||
|
clone.stash(in._outer),
|
||||||
|
clone.object(in.object),
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) hasBinding(name string) bool {
|
||||||
|
return self.object.hasProperty(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) createBinding(name string, deletable bool, value Value) {
|
||||||
|
if self.object.hasProperty(name) {
|
||||||
|
panic(hereBeDragons())
|
||||||
|
}
|
||||||
|
mode := _propertyMode(0111)
|
||||||
|
if !deletable {
|
||||||
|
mode = _propertyMode(0110)
|
||||||
|
}
|
||||||
|
// TODO False?
|
||||||
|
self.object.defineProperty(name, value, mode, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) setBinding(name string, value Value, strict bool) {
|
||||||
|
self.object.put(name, value, strict)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) setValue(name string, value Value, throw bool) {
|
||||||
|
if !self.hasBinding(name) {
|
||||||
|
self.createBinding(name, true, value) // Configurable by default
|
||||||
|
} else {
|
||||||
|
self.setBinding(name, value, throw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) getBinding(name string, throw bool) Value {
|
||||||
|
if self.object.hasProperty(name) {
|
||||||
|
return self.object.get(name)
|
||||||
|
}
|
||||||
|
if throw { // strict?
|
||||||
|
panic(self._runtime.panicReferenceError("Not Defined", name))
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) deleteBinding(name string) bool {
|
||||||
|
return self.object.delete(name, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) outer() _stash {
|
||||||
|
return self._outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_objectStash) newReference(name string, strict bool, at _at) _reference {
|
||||||
|
return newPropertyReference(self._runtime, self.object, name, strict, at)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========
|
||||||
|
// _dclStash
|
||||||
|
// =========
|
||||||
|
|
||||||
|
type _dclStash struct {
|
||||||
|
_runtime *_runtime
|
||||||
|
_outer _stash
|
||||||
|
property map[string]_dclProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type _dclProperty struct {
|
||||||
|
value Value
|
||||||
|
mutable bool
|
||||||
|
deletable bool
|
||||||
|
readable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash {
|
||||||
|
return &_dclStash{
|
||||||
|
_runtime: runtime,
|
||||||
|
_outer: outer,
|
||||||
|
property: map[string]_dclProperty{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_dclStash) clone(clone *_clone) _stash {
|
||||||
|
out, exists := clone.dclStash(in)
|
||||||
|
if exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
property := make(map[string]_dclProperty, len(in.property))
|
||||||
|
for index, value := range in.property {
|
||||||
|
property[index] = clone.dclProperty(value)
|
||||||
|
}
|
||||||
|
*out = _dclStash{
|
||||||
|
clone.runtime,
|
||||||
|
clone.stash(in._outer),
|
||||||
|
property,
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) hasBinding(name string) bool {
|
||||||
|
_, exists := self.property[name]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) runtime() *_runtime {
|
||||||
|
return self._runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) createBinding(name string, deletable bool, value Value) {
|
||||||
|
_, exists := self.property[name]
|
||||||
|
if exists {
|
||||||
|
panic(fmt.Errorf("createBinding: %s: already exists", name))
|
||||||
|
}
|
||||||
|
self.property[name] = _dclProperty{
|
||||||
|
value: value,
|
||||||
|
mutable: true,
|
||||||
|
deletable: deletable,
|
||||||
|
readable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) setBinding(name string, value Value, strict bool) {
|
||||||
|
property, exists := self.property[name]
|
||||||
|
if !exists {
|
||||||
|
panic(fmt.Errorf("setBinding: %s: missing", name))
|
||||||
|
}
|
||||||
|
if property.mutable {
|
||||||
|
property.value = value
|
||||||
|
self.property[name] = property
|
||||||
|
} else {
|
||||||
|
self._runtime.typeErrorResult(strict)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) setValue(name string, value Value, throw bool) {
|
||||||
|
if !self.hasBinding(name) {
|
||||||
|
self.createBinding(name, false, value) // NOT deletable by default
|
||||||
|
} else {
|
||||||
|
self.setBinding(name, value, throw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME This is called a __lot__
|
||||||
|
func (self *_dclStash) getBinding(name string, throw bool) Value {
|
||||||
|
property, exists := self.property[name]
|
||||||
|
if !exists {
|
||||||
|
panic(fmt.Errorf("getBinding: %s: missing", name))
|
||||||
|
}
|
||||||
|
if !property.mutable && !property.readable {
|
||||||
|
if throw { // strict?
|
||||||
|
panic(self._runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
return property.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) deleteBinding(name string) bool {
|
||||||
|
property, exists := self.property[name]
|
||||||
|
if !exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !property.deletable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
delete(self.property, name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) outer() _stash {
|
||||||
|
return self._outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference {
|
||||||
|
return &_stashReference{
|
||||||
|
name: name,
|
||||||
|
base: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========
|
||||||
|
// _fnStash
|
||||||
|
// ========
|
||||||
|
|
||||||
|
type _fnStash struct {
|
||||||
|
_dclStash
|
||||||
|
arguments *_object
|
||||||
|
indexOfArgumentName map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash {
|
||||||
|
return &_fnStash{
|
||||||
|
_dclStash: _dclStash{
|
||||||
|
_runtime: runtime,
|
||||||
|
_outer: outer,
|
||||||
|
property: map[string]_dclProperty{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *_fnStash) clone(clone *_clone) _stash {
|
||||||
|
out, exists := clone.fnStash(in)
|
||||||
|
if exists {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
dclStash := in._dclStash.clone(clone).(*_dclStash)
|
||||||
|
index := make(map[string]string, len(in.indexOfArgumentName))
|
||||||
|
for name, value := range in.indexOfArgumentName {
|
||||||
|
index[name] = value
|
||||||
|
}
|
||||||
|
*out = _fnStash{
|
||||||
|
_dclStash: *dclStash,
|
||||||
|
arguments: clone.object(in.arguments),
|
||||||
|
indexOfArgumentName: index,
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStashProperties(stash _stash) (keys []string) {
|
||||||
|
switch vars := stash.(type) {
|
||||||
|
case *_dclStash:
|
||||||
|
for k := range vars.property {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
case *_fnStash:
|
||||||
|
for k := range vars.property {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
case *_objectStash:
|
||||||
|
for k := range vars.object.property {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unknown stash type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
2
vendor/github.com/robertkrimen/otto/token/Makefile
generated
vendored
Normal file
2
vendor/github.com/robertkrimen/otto/token/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
token_const.go: tokenfmt
|
||||||
|
./$^ | gofmt > $@
|
171
vendor/github.com/robertkrimen/otto/token/README.markdown
generated
vendored
Normal file
171
vendor/github.com/robertkrimen/otto/token/README.markdown
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
# token
|
||||||
|
--
|
||||||
|
import "github.com/robertkrimen/otto/token"
|
||||||
|
|
||||||
|
Package token defines constants representing the lexical tokens of JavaScript
|
||||||
|
(ECMA5).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
const (
|
||||||
|
ILLEGAL
|
||||||
|
EOF
|
||||||
|
COMMENT
|
||||||
|
KEYWORD
|
||||||
|
|
||||||
|
STRING
|
||||||
|
BOOLEAN
|
||||||
|
NULL
|
||||||
|
NUMBER
|
||||||
|
IDENTIFIER
|
||||||
|
|
||||||
|
PLUS // +
|
||||||
|
MINUS // -
|
||||||
|
MULTIPLY // *
|
||||||
|
SLASH // /
|
||||||
|
REMAINDER // %
|
||||||
|
|
||||||
|
AND // &
|
||||||
|
OR // |
|
||||||
|
EXCLUSIVE_OR // ^
|
||||||
|
SHIFT_LEFT // <<
|
||||||
|
SHIFT_RIGHT // >>
|
||||||
|
UNSIGNED_SHIFT_RIGHT // >>>
|
||||||
|
AND_NOT // &^
|
||||||
|
|
||||||
|
ADD_ASSIGN // +=
|
||||||
|
SUBTRACT_ASSIGN // -=
|
||||||
|
MULTIPLY_ASSIGN // *=
|
||||||
|
QUOTIENT_ASSIGN // /=
|
||||||
|
REMAINDER_ASSIGN // %=
|
||||||
|
|
||||||
|
AND_ASSIGN // &=
|
||||||
|
OR_ASSIGN // |=
|
||||||
|
EXCLUSIVE_OR_ASSIGN // ^=
|
||||||
|
SHIFT_LEFT_ASSIGN // <<=
|
||||||
|
SHIFT_RIGHT_ASSIGN // >>=
|
||||||
|
UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>=
|
||||||
|
AND_NOT_ASSIGN // &^=
|
||||||
|
|
||||||
|
LOGICAL_AND // &&
|
||||||
|
LOGICAL_OR // ||
|
||||||
|
INCREMENT // ++
|
||||||
|
DECREMENT // --
|
||||||
|
|
||||||
|
EQUAL // ==
|
||||||
|
STRICT_EQUAL // ===
|
||||||
|
LESS // <
|
||||||
|
GREATER // >
|
||||||
|
ASSIGN // =
|
||||||
|
NOT // !
|
||||||
|
|
||||||
|
BITWISE_NOT // ~
|
||||||
|
|
||||||
|
NOT_EQUAL // !=
|
||||||
|
STRICT_NOT_EQUAL // !==
|
||||||
|
LESS_OR_EQUAL // <=
|
||||||
|
GREATER_OR_EQUAL // >=
|
||||||
|
|
||||||
|
LEFT_PARENTHESIS // (
|
||||||
|
LEFT_BRACKET // [
|
||||||
|
LEFT_BRACE // {
|
||||||
|
COMMA // ,
|
||||||
|
PERIOD // .
|
||||||
|
|
||||||
|
RIGHT_PARENTHESIS // )
|
||||||
|
RIGHT_BRACKET // ]
|
||||||
|
RIGHT_BRACE // }
|
||||||
|
SEMICOLON // ;
|
||||||
|
COLON // :
|
||||||
|
QUESTION_MARK // ?
|
||||||
|
|
||||||
|
IF
|
||||||
|
IN
|
||||||
|
DO
|
||||||
|
|
||||||
|
VAR
|
||||||
|
FOR
|
||||||
|
NEW
|
||||||
|
TRY
|
||||||
|
|
||||||
|
THIS
|
||||||
|
ELSE
|
||||||
|
CASE
|
||||||
|
VOID
|
||||||
|
WITH
|
||||||
|
|
||||||
|
WHILE
|
||||||
|
BREAK
|
||||||
|
CATCH
|
||||||
|
THROW
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
TYPEOF
|
||||||
|
DELETE
|
||||||
|
SWITCH
|
||||||
|
|
||||||
|
DEFAULT
|
||||||
|
FINALLY
|
||||||
|
|
||||||
|
FUNCTION
|
||||||
|
CONTINUE
|
||||||
|
DEBUGGER
|
||||||
|
|
||||||
|
INSTANCEOF
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### type Token
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Token int
|
||||||
|
```
|
||||||
|
|
||||||
|
Token is the set of lexical tokens in JavaScript (ECMA5).
|
||||||
|
|
||||||
|
#### func IsKeyword
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsKeyword(literal string) (Token, bool)
|
||||||
|
```
|
||||||
|
IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token if
|
||||||
|
the literal is a future keyword (const, let, class, super, ...), or 0 if the
|
||||||
|
literal is not a keyword.
|
||||||
|
|
||||||
|
If the literal is a keyword, IsKeyword returns a second value indicating if the
|
||||||
|
literal is considered a future keyword in strict-mode only.
|
||||||
|
|
||||||
|
7.6.1.2 Future Reserved Words:
|
||||||
|
|
||||||
|
const
|
||||||
|
class
|
||||||
|
enum
|
||||||
|
export
|
||||||
|
extends
|
||||||
|
import
|
||||||
|
super
|
||||||
|
|
||||||
|
7.6.1.2 Future Reserved Words (strict):
|
||||||
|
|
||||||
|
implements
|
||||||
|
interface
|
||||||
|
let
|
||||||
|
package
|
||||||
|
private
|
||||||
|
protected
|
||||||
|
public
|
||||||
|
static
|
||||||
|
|
||||||
|
#### func (Token) String
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (tkn Token) String() string
|
||||||
|
```
|
||||||
|
String returns the string corresponding to the token. For operators, delimiters,
|
||||||
|
and keywords the string is the actual token string (e.g., for the token PLUS,
|
||||||
|
the String() is "+"). For all other tokens the string corresponds to the token
|
||||||
|
name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER").
|
||||||
|
|
||||||
|
--
|
||||||
|
**godocdown** http://github.com/robertkrimen/godocdown
|
116
vendor/github.com/robertkrimen/otto/token/token.go
generated
vendored
Normal file
116
vendor/github.com/robertkrimen/otto/token/token.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// Package token defines constants representing the lexical tokens of JavaScript (ECMA5).
|
||||||
|
package token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token is the set of lexical tokens in JavaScript (ECMA5).
|
||||||
|
type Token int
|
||||||
|
|
||||||
|
// String returns the string corresponding to the token.
|
||||||
|
// For operators, delimiters, and keywords the string is the actual
|
||||||
|
// token string (e.g., for the token PLUS, the String() is
|
||||||
|
// "+"). For all other tokens the string corresponds to the token
|
||||||
|
// name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER").
|
||||||
|
//
|
||||||
|
func (tkn Token) String() string {
|
||||||
|
if 0 == tkn {
|
||||||
|
return "UNKNOWN"
|
||||||
|
}
|
||||||
|
if tkn < Token(len(token2string)) {
|
||||||
|
return token2string[tkn]
|
||||||
|
}
|
||||||
|
return "token(" + strconv.Itoa(int(tkn)) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not used for anything
|
||||||
|
func (tkn Token) precedence(in bool) int {
|
||||||
|
|
||||||
|
switch tkn {
|
||||||
|
case LOGICAL_OR:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
case LOGICAL_AND:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
case OR, OR_ASSIGN:
|
||||||
|
return 3
|
||||||
|
|
||||||
|
case EXCLUSIVE_OR:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
case AND, AND_ASSIGN, AND_NOT, AND_NOT_ASSIGN:
|
||||||
|
return 5
|
||||||
|
|
||||||
|
case EQUAL,
|
||||||
|
NOT_EQUAL,
|
||||||
|
STRICT_EQUAL,
|
||||||
|
STRICT_NOT_EQUAL:
|
||||||
|
return 6
|
||||||
|
|
||||||
|
case LESS, GREATER, LESS_OR_EQUAL, GREATER_OR_EQUAL, INSTANCEOF:
|
||||||
|
return 7
|
||||||
|
|
||||||
|
case IN:
|
||||||
|
if in {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
|
||||||
|
case SHIFT_LEFT, SHIFT_RIGHT, UNSIGNED_SHIFT_RIGHT:
|
||||||
|
fallthrough
|
||||||
|
case SHIFT_LEFT_ASSIGN, SHIFT_RIGHT_ASSIGN, UNSIGNED_SHIFT_RIGHT_ASSIGN:
|
||||||
|
return 8
|
||||||
|
|
||||||
|
case PLUS, MINUS, ADD_ASSIGN, SUBTRACT_ASSIGN:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
case MULTIPLY, SLASH, REMAINDER, MULTIPLY_ASSIGN, QUOTIENT_ASSIGN, REMAINDER_ASSIGN:
|
||||||
|
return 11
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type _keyword struct {
|
||||||
|
token Token
|
||||||
|
futureKeyword bool
|
||||||
|
strict bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token
|
||||||
|
// if the literal is a future keyword (const, let, class, super, ...), or 0 if the literal is not a keyword.
|
||||||
|
//
|
||||||
|
// If the literal is a keyword, IsKeyword returns a second value indicating if the literal
|
||||||
|
// is considered a future keyword in strict-mode only.
|
||||||
|
//
|
||||||
|
// 7.6.1.2 Future Reserved Words:
|
||||||
|
//
|
||||||
|
// const
|
||||||
|
// class
|
||||||
|
// enum
|
||||||
|
// export
|
||||||
|
// extends
|
||||||
|
// import
|
||||||
|
// super
|
||||||
|
//
|
||||||
|
// 7.6.1.2 Future Reserved Words (strict):
|
||||||
|
//
|
||||||
|
// implements
|
||||||
|
// interface
|
||||||
|
// let
|
||||||
|
// package
|
||||||
|
// private
|
||||||
|
// protected
|
||||||
|
// public
|
||||||
|
// static
|
||||||
|
//
|
||||||
|
func IsKeyword(literal string) (Token, bool) {
|
||||||
|
if keyword, exists := keywordTable[literal]; exists {
|
||||||
|
if keyword.futureKeyword {
|
||||||
|
return KEYWORD, keyword.strict
|
||||||
|
}
|
||||||
|
return keyword.token, false
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
349
vendor/github.com/robertkrimen/otto/token/token_const.go
generated
vendored
Normal file
349
vendor/github.com/robertkrimen/otto/token/token_const.go
generated
vendored
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
package token
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ Token = iota
|
||||||
|
|
||||||
|
ILLEGAL
|
||||||
|
EOF
|
||||||
|
COMMENT
|
||||||
|
KEYWORD
|
||||||
|
|
||||||
|
STRING
|
||||||
|
BOOLEAN
|
||||||
|
NULL
|
||||||
|
NUMBER
|
||||||
|
IDENTIFIER
|
||||||
|
|
||||||
|
PLUS // +
|
||||||
|
MINUS // -
|
||||||
|
MULTIPLY // *
|
||||||
|
SLASH // /
|
||||||
|
REMAINDER // %
|
||||||
|
|
||||||
|
AND // &
|
||||||
|
OR // |
|
||||||
|
EXCLUSIVE_OR // ^
|
||||||
|
SHIFT_LEFT // <<
|
||||||
|
SHIFT_RIGHT // >>
|
||||||
|
UNSIGNED_SHIFT_RIGHT // >>>
|
||||||
|
AND_NOT // &^
|
||||||
|
|
||||||
|
ADD_ASSIGN // +=
|
||||||
|
SUBTRACT_ASSIGN // -=
|
||||||
|
MULTIPLY_ASSIGN // *=
|
||||||
|
QUOTIENT_ASSIGN // /=
|
||||||
|
REMAINDER_ASSIGN // %=
|
||||||
|
|
||||||
|
AND_ASSIGN // &=
|
||||||
|
OR_ASSIGN // |=
|
||||||
|
EXCLUSIVE_OR_ASSIGN // ^=
|
||||||
|
SHIFT_LEFT_ASSIGN // <<=
|
||||||
|
SHIFT_RIGHT_ASSIGN // >>=
|
||||||
|
UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>=
|
||||||
|
AND_NOT_ASSIGN // &^=
|
||||||
|
|
||||||
|
LOGICAL_AND // &&
|
||||||
|
LOGICAL_OR // ||
|
||||||
|
INCREMENT // ++
|
||||||
|
DECREMENT // --
|
||||||
|
|
||||||
|
EQUAL // ==
|
||||||
|
STRICT_EQUAL // ===
|
||||||
|
LESS // <
|
||||||
|
GREATER // >
|
||||||
|
ASSIGN // =
|
||||||
|
NOT // !
|
||||||
|
|
||||||
|
BITWISE_NOT // ~
|
||||||
|
|
||||||
|
NOT_EQUAL // !=
|
||||||
|
STRICT_NOT_EQUAL // !==
|
||||||
|
LESS_OR_EQUAL // <=
|
||||||
|
GREATER_OR_EQUAL // >=
|
||||||
|
|
||||||
|
LEFT_PARENTHESIS // (
|
||||||
|
LEFT_BRACKET // [
|
||||||
|
LEFT_BRACE // {
|
||||||
|
COMMA // ,
|
||||||
|
PERIOD // .
|
||||||
|
|
||||||
|
RIGHT_PARENTHESIS // )
|
||||||
|
RIGHT_BRACKET // ]
|
||||||
|
RIGHT_BRACE // }
|
||||||
|
SEMICOLON // ;
|
||||||
|
COLON // :
|
||||||
|
QUESTION_MARK // ?
|
||||||
|
|
||||||
|
firstKeyword
|
||||||
|
IF
|
||||||
|
IN
|
||||||
|
DO
|
||||||
|
|
||||||
|
VAR
|
||||||
|
FOR
|
||||||
|
NEW
|
||||||
|
TRY
|
||||||
|
|
||||||
|
THIS
|
||||||
|
ELSE
|
||||||
|
CASE
|
||||||
|
VOID
|
||||||
|
WITH
|
||||||
|
|
||||||
|
WHILE
|
||||||
|
BREAK
|
||||||
|
CATCH
|
||||||
|
THROW
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
TYPEOF
|
||||||
|
DELETE
|
||||||
|
SWITCH
|
||||||
|
|
||||||
|
DEFAULT
|
||||||
|
FINALLY
|
||||||
|
|
||||||
|
FUNCTION
|
||||||
|
CONTINUE
|
||||||
|
DEBUGGER
|
||||||
|
|
||||||
|
INSTANCEOF
|
||||||
|
lastKeyword
|
||||||
|
)
|
||||||
|
|
||||||
|
var token2string = [...]string{
|
||||||
|
ILLEGAL: "ILLEGAL",
|
||||||
|
EOF: "EOF",
|
||||||
|
COMMENT: "COMMENT",
|
||||||
|
KEYWORD: "KEYWORD",
|
||||||
|
STRING: "STRING",
|
||||||
|
BOOLEAN: "BOOLEAN",
|
||||||
|
NULL: "NULL",
|
||||||
|
NUMBER: "NUMBER",
|
||||||
|
IDENTIFIER: "IDENTIFIER",
|
||||||
|
PLUS: "+",
|
||||||
|
MINUS: "-",
|
||||||
|
MULTIPLY: "*",
|
||||||
|
SLASH: "/",
|
||||||
|
REMAINDER: "%",
|
||||||
|
AND: "&",
|
||||||
|
OR: "|",
|
||||||
|
EXCLUSIVE_OR: "^",
|
||||||
|
SHIFT_LEFT: "<<",
|
||||||
|
SHIFT_RIGHT: ">>",
|
||||||
|
UNSIGNED_SHIFT_RIGHT: ">>>",
|
||||||
|
AND_NOT: "&^",
|
||||||
|
ADD_ASSIGN: "+=",
|
||||||
|
SUBTRACT_ASSIGN: "-=",
|
||||||
|
MULTIPLY_ASSIGN: "*=",
|
||||||
|
QUOTIENT_ASSIGN: "/=",
|
||||||
|
REMAINDER_ASSIGN: "%=",
|
||||||
|
AND_ASSIGN: "&=",
|
||||||
|
OR_ASSIGN: "|=",
|
||||||
|
EXCLUSIVE_OR_ASSIGN: "^=",
|
||||||
|
SHIFT_LEFT_ASSIGN: "<<=",
|
||||||
|
SHIFT_RIGHT_ASSIGN: ">>=",
|
||||||
|
UNSIGNED_SHIFT_RIGHT_ASSIGN: ">>>=",
|
||||||
|
AND_NOT_ASSIGN: "&^=",
|
||||||
|
LOGICAL_AND: "&&",
|
||||||
|
LOGICAL_OR: "||",
|
||||||
|
INCREMENT: "++",
|
||||||
|
DECREMENT: "--",
|
||||||
|
EQUAL: "==",
|
||||||
|
STRICT_EQUAL: "===",
|
||||||
|
LESS: "<",
|
||||||
|
GREATER: ">",
|
||||||
|
ASSIGN: "=",
|
||||||
|
NOT: "!",
|
||||||
|
BITWISE_NOT: "~",
|
||||||
|
NOT_EQUAL: "!=",
|
||||||
|
STRICT_NOT_EQUAL: "!==",
|
||||||
|
LESS_OR_EQUAL: "<=",
|
||||||
|
GREATER_OR_EQUAL: ">=",
|
||||||
|
LEFT_PARENTHESIS: "(",
|
||||||
|
LEFT_BRACKET: "[",
|
||||||
|
LEFT_BRACE: "{",
|
||||||
|
COMMA: ",",
|
||||||
|
PERIOD: ".",
|
||||||
|
RIGHT_PARENTHESIS: ")",
|
||||||
|
RIGHT_BRACKET: "]",
|
||||||
|
RIGHT_BRACE: "}",
|
||||||
|
SEMICOLON: ";",
|
||||||
|
COLON: ":",
|
||||||
|
QUESTION_MARK: "?",
|
||||||
|
IF: "if",
|
||||||
|
IN: "in",
|
||||||
|
DO: "do",
|
||||||
|
VAR: "var",
|
||||||
|
FOR: "for",
|
||||||
|
NEW: "new",
|
||||||
|
TRY: "try",
|
||||||
|
THIS: "this",
|
||||||
|
ELSE: "else",
|
||||||
|
CASE: "case",
|
||||||
|
VOID: "void",
|
||||||
|
WITH: "with",
|
||||||
|
WHILE: "while",
|
||||||
|
BREAK: "break",
|
||||||
|
CATCH: "catch",
|
||||||
|
THROW: "throw",
|
||||||
|
RETURN: "return",
|
||||||
|
TYPEOF: "typeof",
|
||||||
|
DELETE: "delete",
|
||||||
|
SWITCH: "switch",
|
||||||
|
DEFAULT: "default",
|
||||||
|
FINALLY: "finally",
|
||||||
|
FUNCTION: "function",
|
||||||
|
CONTINUE: "continue",
|
||||||
|
DEBUGGER: "debugger",
|
||||||
|
INSTANCEOF: "instanceof",
|
||||||
|
}
|
||||||
|
|
||||||
|
var keywordTable = map[string]_keyword{
|
||||||
|
"if": _keyword{
|
||||||
|
token: IF,
|
||||||
|
},
|
||||||
|
"in": _keyword{
|
||||||
|
token: IN,
|
||||||
|
},
|
||||||
|
"do": _keyword{
|
||||||
|
token: DO,
|
||||||
|
},
|
||||||
|
"var": _keyword{
|
||||||
|
token: VAR,
|
||||||
|
},
|
||||||
|
"for": _keyword{
|
||||||
|
token: FOR,
|
||||||
|
},
|
||||||
|
"new": _keyword{
|
||||||
|
token: NEW,
|
||||||
|
},
|
||||||
|
"try": _keyword{
|
||||||
|
token: TRY,
|
||||||
|
},
|
||||||
|
"this": _keyword{
|
||||||
|
token: THIS,
|
||||||
|
},
|
||||||
|
"else": _keyword{
|
||||||
|
token: ELSE,
|
||||||
|
},
|
||||||
|
"case": _keyword{
|
||||||
|
token: CASE,
|
||||||
|
},
|
||||||
|
"void": _keyword{
|
||||||
|
token: VOID,
|
||||||
|
},
|
||||||
|
"with": _keyword{
|
||||||
|
token: WITH,
|
||||||
|
},
|
||||||
|
"while": _keyword{
|
||||||
|
token: WHILE,
|
||||||
|
},
|
||||||
|
"break": _keyword{
|
||||||
|
token: BREAK,
|
||||||
|
},
|
||||||
|
"catch": _keyword{
|
||||||
|
token: CATCH,
|
||||||
|
},
|
||||||
|
"throw": _keyword{
|
||||||
|
token: THROW,
|
||||||
|
},
|
||||||
|
"return": _keyword{
|
||||||
|
token: RETURN,
|
||||||
|
},
|
||||||
|
"typeof": _keyword{
|
||||||
|
token: TYPEOF,
|
||||||
|
},
|
||||||
|
"delete": _keyword{
|
||||||
|
token: DELETE,
|
||||||
|
},
|
||||||
|
"switch": _keyword{
|
||||||
|
token: SWITCH,
|
||||||
|
},
|
||||||
|
"default": _keyword{
|
||||||
|
token: DEFAULT,
|
||||||
|
},
|
||||||
|
"finally": _keyword{
|
||||||
|
token: FINALLY,
|
||||||
|
},
|
||||||
|
"function": _keyword{
|
||||||
|
token: FUNCTION,
|
||||||
|
},
|
||||||
|
"continue": _keyword{
|
||||||
|
token: CONTINUE,
|
||||||
|
},
|
||||||
|
"debugger": _keyword{
|
||||||
|
token: DEBUGGER,
|
||||||
|
},
|
||||||
|
"instanceof": _keyword{
|
||||||
|
token: INSTANCEOF,
|
||||||
|
},
|
||||||
|
"const": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
"class": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
"enum": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
"export": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
"extends": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
"import": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
"super": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
"implements": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
"interface": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
"let": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
"package": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
"private": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
"protected": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
"public": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
"static": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
}
|
222
vendor/github.com/robertkrimen/otto/token/tokenfmt
generated
vendored
Normal file
222
vendor/github.com/robertkrimen/otto/token/tokenfmt
generated
vendored
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
my (%token, @order, @keywords);
|
||||||
|
|
||||||
|
{
|
||||||
|
my $keywords;
|
||||||
|
my @const;
|
||||||
|
push @const, <<_END_;
|
||||||
|
package token
|
||||||
|
|
||||||
|
const(
|
||||||
|
_ Token = iota
|
||||||
|
_END_
|
||||||
|
|
||||||
|
for (split m/\n/, <<_END_) {
|
||||||
|
ILLEGAL
|
||||||
|
EOF
|
||||||
|
COMMENT
|
||||||
|
KEYWORD
|
||||||
|
|
||||||
|
STRING
|
||||||
|
BOOLEAN
|
||||||
|
NULL
|
||||||
|
NUMBER
|
||||||
|
IDENTIFIER
|
||||||
|
|
||||||
|
PLUS +
|
||||||
|
MINUS -
|
||||||
|
MULTIPLY *
|
||||||
|
SLASH /
|
||||||
|
REMAINDER %
|
||||||
|
|
||||||
|
AND &
|
||||||
|
OR |
|
||||||
|
EXCLUSIVE_OR ^
|
||||||
|
SHIFT_LEFT <<
|
||||||
|
SHIFT_RIGHT >>
|
||||||
|
UNSIGNED_SHIFT_RIGHT >>>
|
||||||
|
AND_NOT &^
|
||||||
|
|
||||||
|
ADD_ASSIGN +=
|
||||||
|
SUBTRACT_ASSIGN -=
|
||||||
|
MULTIPLY_ASSIGN *=
|
||||||
|
QUOTIENT_ASSIGN /=
|
||||||
|
REMAINDER_ASSIGN %=
|
||||||
|
|
||||||
|
AND_ASSIGN &=
|
||||||
|
OR_ASSIGN |=
|
||||||
|
EXCLUSIVE_OR_ASSIGN ^=
|
||||||
|
SHIFT_LEFT_ASSIGN <<=
|
||||||
|
SHIFT_RIGHT_ASSIGN >>=
|
||||||
|
UNSIGNED_SHIFT_RIGHT_ASSIGN >>>=
|
||||||
|
AND_NOT_ASSIGN &^=
|
||||||
|
|
||||||
|
LOGICAL_AND &&
|
||||||
|
LOGICAL_OR ||
|
||||||
|
INCREMENT ++
|
||||||
|
DECREMENT --
|
||||||
|
|
||||||
|
EQUAL ==
|
||||||
|
STRICT_EQUAL ===
|
||||||
|
LESS <
|
||||||
|
GREATER >
|
||||||
|
ASSIGN =
|
||||||
|
NOT !
|
||||||
|
|
||||||
|
BITWISE_NOT ~
|
||||||
|
|
||||||
|
NOT_EQUAL !=
|
||||||
|
STRICT_NOT_EQUAL !==
|
||||||
|
LESS_OR_EQUAL <=
|
||||||
|
GREATER_OR_EQUAL <=
|
||||||
|
|
||||||
|
LEFT_PARENTHESIS (
|
||||||
|
LEFT_BRACKET [
|
||||||
|
LEFT_BRACE {
|
||||||
|
COMMA ,
|
||||||
|
PERIOD .
|
||||||
|
|
||||||
|
RIGHT_PARENTHESIS )
|
||||||
|
RIGHT_BRACKET ]
|
||||||
|
RIGHT_BRACE }
|
||||||
|
SEMICOLON ;
|
||||||
|
COLON :
|
||||||
|
QUESTION_MARK ?
|
||||||
|
|
||||||
|
firstKeyword
|
||||||
|
IF
|
||||||
|
IN
|
||||||
|
DO
|
||||||
|
|
||||||
|
VAR
|
||||||
|
FOR
|
||||||
|
NEW
|
||||||
|
TRY
|
||||||
|
|
||||||
|
THIS
|
||||||
|
ELSE
|
||||||
|
CASE
|
||||||
|
VOID
|
||||||
|
WITH
|
||||||
|
|
||||||
|
WHILE
|
||||||
|
BREAK
|
||||||
|
CATCH
|
||||||
|
THROW
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
TYPEOF
|
||||||
|
DELETE
|
||||||
|
SWITCH
|
||||||
|
|
||||||
|
DEFAULT
|
||||||
|
FINALLY
|
||||||
|
|
||||||
|
FUNCTION
|
||||||
|
CONTINUE
|
||||||
|
DEBUGGER
|
||||||
|
|
||||||
|
INSTANCEOF
|
||||||
|
lastKeyword
|
||||||
|
_END_
|
||||||
|
chomp;
|
||||||
|
|
||||||
|
next if m/^\s*#/;
|
||||||
|
|
||||||
|
my ($name, $symbol) = m/(\w+)\s*(\S+)?/;
|
||||||
|
|
||||||
|
if (defined $symbol) {
|
||||||
|
push @order, $name;
|
||||||
|
push @const, "$name // $symbol";
|
||||||
|
$token{$name} = $symbol;
|
||||||
|
} elsif (defined $name) {
|
||||||
|
$keywords ||= $name eq 'firstKeyword';
|
||||||
|
|
||||||
|
push @const, $name;
|
||||||
|
#$const[-1] .= " Token = iota" if 2 == @const;
|
||||||
|
if ($name =~ m/^([A-Z]+)/) {
|
||||||
|
push @keywords, $name if $keywords;
|
||||||
|
push @order, $name;
|
||||||
|
if ($token{SEMICOLON}) {
|
||||||
|
$token{$name} = lc $1;
|
||||||
|
} else {
|
||||||
|
$token{$name} = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push @const, "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
push @const, ")";
|
||||||
|
print join "\n", @const, "";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
print <<_END_;
|
||||||
|
|
||||||
|
var token2string = [...]string{
|
||||||
|
_END_
|
||||||
|
for my $name (@order) {
|
||||||
|
print "$name: \"$token{$name}\",\n";
|
||||||
|
}
|
||||||
|
print <<_END_;
|
||||||
|
}
|
||||||
|
_END_
|
||||||
|
|
||||||
|
print <<_END_;
|
||||||
|
|
||||||
|
var keywordTable = map[string]_keyword{
|
||||||
|
_END_
|
||||||
|
for my $name (@keywords) {
|
||||||
|
print <<_END_
|
||||||
|
"@{[ lc $name ]}": _keyword{
|
||||||
|
token: $name,
|
||||||
|
},
|
||||||
|
_END_
|
||||||
|
}
|
||||||
|
|
||||||
|
for my $name (qw/
|
||||||
|
const
|
||||||
|
class
|
||||||
|
enum
|
||||||
|
export
|
||||||
|
extends
|
||||||
|
import
|
||||||
|
super
|
||||||
|
/) {
|
||||||
|
print <<_END_
|
||||||
|
"$name": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
},
|
||||||
|
_END_
|
||||||
|
}
|
||||||
|
|
||||||
|
for my $name (qw/
|
||||||
|
implements
|
||||||
|
interface
|
||||||
|
let
|
||||||
|
package
|
||||||
|
private
|
||||||
|
protected
|
||||||
|
public
|
||||||
|
static
|
||||||
|
/) {
|
||||||
|
print <<_END_
|
||||||
|
"$name": _keyword{
|
||||||
|
token: KEYWORD,
|
||||||
|
futureKeyword: true,
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
_END_
|
||||||
|
}
|
||||||
|
|
||||||
|
print <<_END_;
|
||||||
|
}
|
||||||
|
_END_
|
||||||
|
}
|
106
vendor/github.com/robertkrimen/otto/type_arguments.go
generated
vendored
Normal file
106
vendor/github.com/robertkrimen/otto/type_arguments.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (runtime *_runtime) newArgumentsObject(indexOfParameterName []string, stash _stash, length int) *_object {
|
||||||
|
self := runtime.newClassObject("Arguments")
|
||||||
|
|
||||||
|
for index, _ := range indexOfParameterName {
|
||||||
|
name := strconv.FormatInt(int64(index), 10)
|
||||||
|
objectDefineOwnProperty(self, name, _property{Value{}, 0111}, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.objectClass = _classArguments
|
||||||
|
self.value = _argumentsObject{
|
||||||
|
indexOfParameterName: indexOfParameterName,
|
||||||
|
stash: stash,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.prototype = runtime.global.ObjectPrototype
|
||||||
|
|
||||||
|
self.defineProperty("length", toValue_int(length), 0101, false)
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
type _argumentsObject struct {
|
||||||
|
indexOfParameterName []string
|
||||||
|
// function(abc, def, ghi)
|
||||||
|
// indexOfParameterName[0] = "abc"
|
||||||
|
// indexOfParameterName[1] = "def"
|
||||||
|
// indexOfParameterName[2] = "ghi"
|
||||||
|
// ...
|
||||||
|
stash _stash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in _argumentsObject) clone(clone *_clone) _argumentsObject {
|
||||||
|
indexOfParameterName := make([]string, len(in.indexOfParameterName))
|
||||||
|
copy(indexOfParameterName, in.indexOfParameterName)
|
||||||
|
return _argumentsObject{
|
||||||
|
indexOfParameterName,
|
||||||
|
clone.stash(in.stash),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _argumentsObject) get(name string) (Value, bool) {
|
||||||
|
index := stringToArrayIndex(name)
|
||||||
|
if index >= 0 && index < int64(len(self.indexOfParameterName)) {
|
||||||
|
name := self.indexOfParameterName[index]
|
||||||
|
if name == "" {
|
||||||
|
return Value{}, false
|
||||||
|
}
|
||||||
|
return self.stash.getBinding(name, false), true
|
||||||
|
}
|
||||||
|
return Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _argumentsObject) put(name string, value Value) {
|
||||||
|
index := stringToArrayIndex(name)
|
||||||
|
name = self.indexOfParameterName[index]
|
||||||
|
self.stash.setBinding(name, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _argumentsObject) delete(name string) {
|
||||||
|
index := stringToArrayIndex(name)
|
||||||
|
self.indexOfParameterName[index] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func argumentsGet(self *_object, name string) Value {
|
||||||
|
if value, exists := self.value.(_argumentsObject).get(name); exists {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return objectGet(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func argumentsGetOwnProperty(self *_object, name string) *_property {
|
||||||
|
property := objectGetOwnProperty(self, name)
|
||||||
|
if value, exists := self.value.(_argumentsObject).get(name); exists {
|
||||||
|
property.value = value
|
||||||
|
}
|
||||||
|
return property
|
||||||
|
}
|
||||||
|
|
||||||
|
func argumentsDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
|
||||||
|
if _, exists := self.value.(_argumentsObject).get(name); exists {
|
||||||
|
if !objectDefineOwnProperty(self, name, descriptor, false) {
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
if value, valid := descriptor.value.(Value); valid {
|
||||||
|
self.value.(_argumentsObject).put(name, value)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return objectDefineOwnProperty(self, name, descriptor, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func argumentsDelete(self *_object, name string, throw bool) bool {
|
||||||
|
if !objectDelete(self, name, throw) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, exists := self.value.(_argumentsObject).get(name); exists {
|
||||||
|
self.value.(_argumentsObject).delete(name)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
109
vendor/github.com/robertkrimen/otto/type_array.go
generated
vendored
Normal file
109
vendor/github.com/robertkrimen/otto/type_array.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (runtime *_runtime) newArrayObject(length uint32) *_object {
|
||||||
|
self := runtime.newObject()
|
||||||
|
self.class = "Array"
|
||||||
|
self.defineProperty("length", toValue_uint32(length), 0100, false)
|
||||||
|
self.objectClass = _classArray
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func isArray(object *_object) bool {
|
||||||
|
return object != nil && (object.class == "Array" || object.class == "GoArray")
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectLength(object *_object) uint32 {
|
||||||
|
if object == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
switch object.class {
|
||||||
|
case "Array":
|
||||||
|
return object.get("length").value.(uint32)
|
||||||
|
case "String":
|
||||||
|
return uint32(object.get("length").value.(int))
|
||||||
|
case "GoArray":
|
||||||
|
return uint32(object.get("length").value.(int))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func arrayUint32(rt *_runtime, value Value) uint32 {
|
||||||
|
nm := value.number()
|
||||||
|
if nm.kind != numberInteger || !isUint32(nm.int64) {
|
||||||
|
// FIXME
|
||||||
|
panic(rt.panicRangeError())
|
||||||
|
}
|
||||||
|
return uint32(nm.int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func arrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
|
||||||
|
lengthProperty := self.getOwnProperty("length")
|
||||||
|
lengthValue, valid := lengthProperty.value.(Value)
|
||||||
|
if !valid {
|
||||||
|
panic("Array.length != Value{}")
|
||||||
|
}
|
||||||
|
length := lengthValue.value.(uint32)
|
||||||
|
if name == "length" {
|
||||||
|
if descriptor.value == nil {
|
||||||
|
return objectDefineOwnProperty(self, name, descriptor, throw)
|
||||||
|
}
|
||||||
|
newLengthValue, isValue := descriptor.value.(Value)
|
||||||
|
if !isValue {
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
newLength := arrayUint32(self.runtime, newLengthValue)
|
||||||
|
descriptor.value = toValue_uint32(newLength)
|
||||||
|
if newLength > length {
|
||||||
|
return objectDefineOwnProperty(self, name, descriptor, throw)
|
||||||
|
}
|
||||||
|
if !lengthProperty.writable() {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
newWritable := true
|
||||||
|
if descriptor.mode&0700 == 0 {
|
||||||
|
// If writable is off
|
||||||
|
newWritable = false
|
||||||
|
descriptor.mode |= 0100
|
||||||
|
}
|
||||||
|
if !objectDefineOwnProperty(self, name, descriptor, throw) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for newLength < length {
|
||||||
|
length--
|
||||||
|
if !self.delete(strconv.FormatInt(int64(length), 10), false) {
|
||||||
|
descriptor.value = toValue_uint32(length + 1)
|
||||||
|
if !newWritable {
|
||||||
|
descriptor.mode &= 0077
|
||||||
|
}
|
||||||
|
objectDefineOwnProperty(self, name, descriptor, false)
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !newWritable {
|
||||||
|
descriptor.mode &= 0077
|
||||||
|
objectDefineOwnProperty(self, name, descriptor, false)
|
||||||
|
}
|
||||||
|
} else if index := stringToArrayIndex(name); index >= 0 {
|
||||||
|
if index >= int64(length) && !lengthProperty.writable() {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
if !objectDefineOwnProperty(self, strconv.FormatInt(index, 10), descriptor, false) {
|
||||||
|
goto Reject
|
||||||
|
}
|
||||||
|
if index >= int64(length) {
|
||||||
|
lengthProperty.value = toValue_uint32(uint32(index + 1))
|
||||||
|
objectDefineOwnProperty(self, "length", *lengthProperty, false)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objectDefineOwnProperty(self, name, descriptor, throw)
|
||||||
|
Reject:
|
||||||
|
if throw {
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
13
vendor/github.com/robertkrimen/otto/type_boolean.go
generated
vendored
Normal file
13
vendor/github.com/robertkrimen/otto/type_boolean.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (runtime *_runtime) newBooleanObject(value Value) *_object {
|
||||||
|
return runtime.newPrimitiveObject("Boolean", toValue_bool(value.bool()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func booleanToString(value bool) string {
|
||||||
|
return strconv.FormatBool(value)
|
||||||
|
}
|
299
vendor/github.com/robertkrimen/otto/type_date.go
generated
vendored
Normal file
299
vendor/github.com/robertkrimen/otto/type_date.go
generated
vendored
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
Time "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _dateObject struct {
|
||||||
|
time Time.Time // Time from the "time" package, a cached version of time
|
||||||
|
epoch int64
|
||||||
|
value Value
|
||||||
|
isNaN bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
invalidDateObject = _dateObject{
|
||||||
|
time: Time.Time{},
|
||||||
|
epoch: -1,
|
||||||
|
value: NaNValue(),
|
||||||
|
isNaN: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type _ecmaTime struct {
|
||||||
|
year int
|
||||||
|
month int
|
||||||
|
day int
|
||||||
|
hour int
|
||||||
|
minute int
|
||||||
|
second int
|
||||||
|
millisecond int
|
||||||
|
location *Time.Location // Basically, either local or UTC
|
||||||
|
}
|
||||||
|
|
||||||
|
func ecmaTime(goTime Time.Time) _ecmaTime {
|
||||||
|
return _ecmaTime{
|
||||||
|
goTime.Year(),
|
||||||
|
dateFromGoMonth(goTime.Month()),
|
||||||
|
goTime.Day(),
|
||||||
|
goTime.Hour(),
|
||||||
|
goTime.Minute(),
|
||||||
|
goTime.Second(),
|
||||||
|
goTime.Nanosecond() / (100 * 100 * 100),
|
||||||
|
goTime.Location(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ecmaTime) goTime() Time.Time {
|
||||||
|
return Time.Date(
|
||||||
|
self.year,
|
||||||
|
dateToGoMonth(self.month),
|
||||||
|
self.day,
|
||||||
|
self.hour,
|
||||||
|
self.minute,
|
||||||
|
self.second,
|
||||||
|
self.millisecond*(100*100*100),
|
||||||
|
self.location,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dateObject) Time() Time.Time {
|
||||||
|
return self.time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dateObject) Epoch() int64 {
|
||||||
|
return self.epoch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dateObject) Value() Value {
|
||||||
|
return self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME A date should only be in the range of -100,000,000 to +100,000,000 (1970): 15.9.1.1
|
||||||
|
func (self *_dateObject) SetNaN() {
|
||||||
|
self.time = Time.Time{}
|
||||||
|
self.epoch = -1
|
||||||
|
self.value = NaNValue()
|
||||||
|
self.isNaN = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dateObject) SetTime(time Time.Time) {
|
||||||
|
self.Set(timeToEpoch(time))
|
||||||
|
}
|
||||||
|
|
||||||
|
func epoch2dateObject(epoch float64) _dateObject {
|
||||||
|
date := _dateObject{}
|
||||||
|
date.Set(epoch)
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_dateObject) Set(epoch float64) {
|
||||||
|
// epoch
|
||||||
|
self.epoch = epochToInteger(epoch)
|
||||||
|
|
||||||
|
// time
|
||||||
|
time, err := epochToTime(epoch)
|
||||||
|
self.time = time // Is either a valid time, or the zero-value for time.Time
|
||||||
|
|
||||||
|
// value & isNaN
|
||||||
|
if err != nil {
|
||||||
|
self.isNaN = true
|
||||||
|
self.epoch = -1
|
||||||
|
self.value = NaNValue()
|
||||||
|
} else {
|
||||||
|
self.value = toValue_int64(self.epoch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func epochToInteger(value float64) int64 {
|
||||||
|
if value > 0 {
|
||||||
|
return int64(math.Floor(value))
|
||||||
|
}
|
||||||
|
return int64(math.Ceil(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func epochToTime(value float64) (time Time.Time, err error) {
|
||||||
|
epochWithMilli := value
|
||||||
|
if math.IsNaN(epochWithMilli) || math.IsInf(epochWithMilli, 0) {
|
||||||
|
err = fmt.Errorf("Invalid time %v", value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
epoch := int64(epochWithMilli / 1000)
|
||||||
|
milli := int64(epochWithMilli) % 1000
|
||||||
|
|
||||||
|
time = Time.Unix(int64(epoch), milli*1000000).UTC()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeToEpoch(time Time.Time) float64 {
|
||||||
|
return float64(time.UnixNano() / (1000 * 1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newDateObject(epoch float64) *_object {
|
||||||
|
self := runtime.newObject()
|
||||||
|
self.class = "Date"
|
||||||
|
|
||||||
|
// FIXME This is ugly...
|
||||||
|
date := _dateObject{}
|
||||||
|
date.Set(epoch)
|
||||||
|
self.value = date
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) dateValue() _dateObject {
|
||||||
|
value, _ := self.value.(_dateObject)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateObjectOf(rt *_runtime, _dateObject *_object) _dateObject {
|
||||||
|
if _dateObject == nil || _dateObject.class != "Date" {
|
||||||
|
panic(rt.panicTypeError())
|
||||||
|
}
|
||||||
|
return _dateObject.dateValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// JavaScript is 0-based, Go is 1-based (15.9.1.4)
|
||||||
|
func dateToGoMonth(month int) Time.Month {
|
||||||
|
return Time.Month(month + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateFromGoMonth(month Time.Month) int {
|
||||||
|
return int(month) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both JavaScript & Go are 0-based (Sunday == 0)
|
||||||
|
func dateToGoDay(day int) Time.Weekday {
|
||||||
|
return Time.Weekday(day)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateFromGoDay(day Time.Weekday) int {
|
||||||
|
return int(day)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDateTime(argumentList []Value, location *Time.Location) (epoch float64) {
|
||||||
|
|
||||||
|
pick := func(index int, default_ float64) (float64, bool) {
|
||||||
|
if index >= len(argumentList) {
|
||||||
|
return default_, false
|
||||||
|
}
|
||||||
|
value := argumentList[index].float64()
|
||||||
|
if math.IsNaN(value) || math.IsInf(value, 0) {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
return value, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(argumentList) >= 2 { // 2-argument, 3-argument, ...
|
||||||
|
var year, month, day, hour, minute, second, millisecond float64
|
||||||
|
var invalid bool
|
||||||
|
if year, invalid = pick(0, 1900.0); invalid {
|
||||||
|
goto INVALID
|
||||||
|
}
|
||||||
|
if month, invalid = pick(1, 0.0); invalid {
|
||||||
|
goto INVALID
|
||||||
|
}
|
||||||
|
if day, invalid = pick(2, 1.0); invalid {
|
||||||
|
goto INVALID
|
||||||
|
}
|
||||||
|
if hour, invalid = pick(3, 0.0); invalid {
|
||||||
|
goto INVALID
|
||||||
|
}
|
||||||
|
if minute, invalid = pick(4, 0.0); invalid {
|
||||||
|
goto INVALID
|
||||||
|
}
|
||||||
|
if second, invalid = pick(5, 0.0); invalid {
|
||||||
|
goto INVALID
|
||||||
|
}
|
||||||
|
if millisecond, invalid = pick(6, 0.0); invalid {
|
||||||
|
goto INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
if year >= 0 && year <= 99 {
|
||||||
|
year += 1900
|
||||||
|
}
|
||||||
|
|
||||||
|
time := Time.Date(int(year), dateToGoMonth(int(month)), int(day), int(hour), int(minute), int(second), int(millisecond)*1000*1000, location)
|
||||||
|
return timeToEpoch(time)
|
||||||
|
|
||||||
|
} else if len(argumentList) == 0 { // 0-argument
|
||||||
|
time := Time.Now().UTC()
|
||||||
|
return timeToEpoch(time)
|
||||||
|
} else { // 1-argument
|
||||||
|
value := valueOfArrayIndex(argumentList, 0)
|
||||||
|
value = toPrimitive(value)
|
||||||
|
if value.IsString() {
|
||||||
|
return dateParse(value.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.float64()
|
||||||
|
}
|
||||||
|
|
||||||
|
INVALID:
|
||||||
|
epoch = math.NaN()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
dateLayoutList = []string{
|
||||||
|
"2006",
|
||||||
|
"2006-01",
|
||||||
|
"2006-01-02",
|
||||||
|
|
||||||
|
"2006T15:04",
|
||||||
|
"2006-01T15:04",
|
||||||
|
"2006-01-02T15:04",
|
||||||
|
|
||||||
|
"2006T15:04:05",
|
||||||
|
"2006-01T15:04:05",
|
||||||
|
"2006-01-02T15:04:05",
|
||||||
|
|
||||||
|
"2006T15:04:05.000",
|
||||||
|
"2006-01T15:04:05.000",
|
||||||
|
"2006-01-02T15:04:05.000",
|
||||||
|
|
||||||
|
"2006T15:04-0700",
|
||||||
|
"2006-01T15:04-0700",
|
||||||
|
"2006-01-02T15:04-0700",
|
||||||
|
|
||||||
|
"2006T15:04:05-0700",
|
||||||
|
"2006-01T15:04:05-0700",
|
||||||
|
"2006-01-02T15:04:05-0700",
|
||||||
|
|
||||||
|
"2006T15:04:05.000-0700",
|
||||||
|
"2006-01T15:04:05.000-0700",
|
||||||
|
"2006-01-02T15:04:05.000-0700",
|
||||||
|
|
||||||
|
Time.RFC1123,
|
||||||
|
}
|
||||||
|
matchDateTimeZone = regexp.MustCompile(`^(.*)(?:(Z)|([\+\-]\d{2}):(\d{2}))$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func dateParse(date string) (epoch float64) {
|
||||||
|
// YYYY-MM-DDTHH:mm:ss.sssZ
|
||||||
|
var time Time.Time
|
||||||
|
var err error
|
||||||
|
{
|
||||||
|
date := date
|
||||||
|
if match := matchDateTimeZone.FindStringSubmatch(date); match != nil {
|
||||||
|
if match[2] == "Z" {
|
||||||
|
date = match[1] + "+0000"
|
||||||
|
} else {
|
||||||
|
date = match[1] + match[3] + match[4]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, layout := range dateLayoutList {
|
||||||
|
time, err = Time.Parse(layout, date)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN()
|
||||||
|
}
|
||||||
|
return float64(time.UnixNano()) / (1000 * 1000) // UnixMilli()
|
||||||
|
}
|
24
vendor/github.com/robertkrimen/otto/type_error.go
generated
vendored
Normal file
24
vendor/github.com/robertkrimen/otto/type_error.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
func (rt *_runtime) newErrorObject(name string, message Value, stackFramesToPop int) *_object {
|
||||||
|
self := rt.newClassObject("Error")
|
||||||
|
if message.IsDefined() {
|
||||||
|
msg := message.string()
|
||||||
|
self.defineProperty("message", toValue_string(msg), 0111, false)
|
||||||
|
self.value = newError(rt, name, stackFramesToPop, msg)
|
||||||
|
} else {
|
||||||
|
self.value = newError(rt, name, stackFramesToPop)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.defineOwnProperty("stack", _property{
|
||||||
|
value: _propertyGetSet{
|
||||||
|
rt.newNativeFunction("get", "internal", 0, func(FunctionCall) Value {
|
||||||
|
return toValue_string(self.value.(_error).formatWithStack())
|
||||||
|
}),
|
||||||
|
&_nilGetSetObject,
|
||||||
|
},
|
||||||
|
mode: modeConfigureMask & modeOnMask,
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
340
vendor/github.com/robertkrimen/otto/type_function.go
generated
vendored
Normal file
340
vendor/github.com/robertkrimen/otto/type_function.go
generated
vendored
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
// _constructFunction
|
||||||
|
type _constructFunction func(*_object, []Value) Value
|
||||||
|
|
||||||
|
// 13.2.2 [[Construct]]
|
||||||
|
func defaultConstruct(fn *_object, argumentList []Value) Value {
|
||||||
|
object := fn.runtime.newObject()
|
||||||
|
object.class = "Object"
|
||||||
|
|
||||||
|
prototype := fn.get("prototype")
|
||||||
|
if prototype.kind != valueObject {
|
||||||
|
prototype = toValue_object(fn.runtime.global.ObjectPrototype)
|
||||||
|
}
|
||||||
|
object.prototype = prototype._object()
|
||||||
|
|
||||||
|
this := toValue_object(object)
|
||||||
|
value := fn.call(this, argumentList, false, nativeFrame)
|
||||||
|
if value.kind == valueObject {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// _nativeFunction
|
||||||
|
type _nativeFunction func(FunctionCall) Value
|
||||||
|
|
||||||
|
// ===================== //
|
||||||
|
// _nativeFunctionObject //
|
||||||
|
// ===================== //
|
||||||
|
|
||||||
|
type _nativeFunctionObject struct {
|
||||||
|
name string
|
||||||
|
file string
|
||||||
|
line int
|
||||||
|
call _nativeFunction // [[Call]]
|
||||||
|
construct _constructFunction // [[Construct]]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) _newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object {
|
||||||
|
self := runtime.newClassObject("Function")
|
||||||
|
self.value = _nativeFunctionObject{
|
||||||
|
name: name,
|
||||||
|
file: file,
|
||||||
|
line: line,
|
||||||
|
call: native,
|
||||||
|
construct: defaultConstruct,
|
||||||
|
}
|
||||||
|
self.defineProperty("name", toValue_string(name), 0000, false)
|
||||||
|
self.defineProperty("length", toValue_int(length), 0000, false)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object {
|
||||||
|
self := runtime._newNativeFunctionObject(name, file, line, native, length)
|
||||||
|
self.defineOwnProperty("caller", _property{
|
||||||
|
value: _propertyGetSet{
|
||||||
|
runtime._newNativeFunctionObject("get", "internal", 0, func(fc FunctionCall) Value {
|
||||||
|
for sc := runtime.scope; sc != nil; sc = sc.outer {
|
||||||
|
if sc.frame.fn == self {
|
||||||
|
if sc.outer == nil || sc.outer.frame.fn == nil {
|
||||||
|
return nullValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtime.toValue(sc.outer.frame.fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullValue
|
||||||
|
}, 0),
|
||||||
|
&_nilGetSetObject,
|
||||||
|
},
|
||||||
|
mode: 0000,
|
||||||
|
}, false)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================== //
|
||||||
|
// _bindFunctionObject //
|
||||||
|
// =================== //
|
||||||
|
|
||||||
|
type _bindFunctionObject struct {
|
||||||
|
target *_object
|
||||||
|
this Value
|
||||||
|
argumentList []Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object {
|
||||||
|
self := runtime.newClassObject("Function")
|
||||||
|
self.value = _bindFunctionObject{
|
||||||
|
target: target,
|
||||||
|
this: this,
|
||||||
|
argumentList: argumentList,
|
||||||
|
}
|
||||||
|
length := int(toInt32(target.get("length")))
|
||||||
|
length -= len(argumentList)
|
||||||
|
if length < 0 {
|
||||||
|
length = 0
|
||||||
|
}
|
||||||
|
self.defineProperty("name", toValue_string("bound "+target.get("name").String()), 0000, false)
|
||||||
|
self.defineProperty("length", toValue_int(length), 0000, false)
|
||||||
|
self.defineProperty("caller", Value{}, 0000, false) // TODO Should throw a TypeError
|
||||||
|
self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// [[Construct]]
|
||||||
|
func (fn _bindFunctionObject) construct(argumentList []Value) Value {
|
||||||
|
object := fn.target
|
||||||
|
switch value := object.value.(type) {
|
||||||
|
case _nativeFunctionObject:
|
||||||
|
return value.construct(object, fn.argumentList)
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
argumentList = append(fn.argumentList, argumentList...)
|
||||||
|
return object.construct(argumentList)
|
||||||
|
}
|
||||||
|
panic(fn.target.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================== //
|
||||||
|
// _nodeFunctionObject //
|
||||||
|
// =================== //
|
||||||
|
|
||||||
|
type _nodeFunctionObject struct {
|
||||||
|
node *_nodeFunctionLiteral
|
||||||
|
stash _stash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash _stash) *_object {
|
||||||
|
self := runtime.newClassObject("Function")
|
||||||
|
self.value = _nodeFunctionObject{
|
||||||
|
node: node,
|
||||||
|
stash: stash,
|
||||||
|
}
|
||||||
|
self.defineProperty("name", toValue_string(node.name), 0000, false)
|
||||||
|
self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false)
|
||||||
|
self.defineOwnProperty("caller", _property{
|
||||||
|
value: _propertyGetSet{
|
||||||
|
runtime.newNativeFunction("get", "internal", 0, func(fc FunctionCall) Value {
|
||||||
|
for sc := runtime.scope; sc != nil; sc = sc.outer {
|
||||||
|
if sc.frame.fn == self {
|
||||||
|
if sc.outer == nil || sc.outer.frame.fn == nil {
|
||||||
|
return nullValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtime.toValue(sc.outer.frame.fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullValue
|
||||||
|
}),
|
||||||
|
&_nilGetSetObject,
|
||||||
|
},
|
||||||
|
mode: 0000,
|
||||||
|
}, false)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= //
|
||||||
|
// _object //
|
||||||
|
// ======= //
|
||||||
|
|
||||||
|
func (self *_object) isCall() bool {
|
||||||
|
switch fn := self.value.(type) {
|
||||||
|
case _nativeFunctionObject:
|
||||||
|
return fn.call != nil
|
||||||
|
case _bindFunctionObject:
|
||||||
|
return true
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) call(this Value, argumentList []Value, eval bool, frame _frame) Value {
|
||||||
|
switch fn := self.value.(type) {
|
||||||
|
|
||||||
|
case _nativeFunctionObject:
|
||||||
|
// Since eval is a native function, we only have to check for it here
|
||||||
|
if eval {
|
||||||
|
eval = self == self.runtime.eval // If eval is true, then it IS a direct eval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter a scope, name from the native object...
|
||||||
|
rt := self.runtime
|
||||||
|
if rt.scope != nil && !eval {
|
||||||
|
rt.enterFunctionScope(rt.scope.lexical, this)
|
||||||
|
rt.scope.frame = _frame{
|
||||||
|
native: true,
|
||||||
|
nativeFile: fn.file,
|
||||||
|
nativeLine: fn.line,
|
||||||
|
callee: fn.name,
|
||||||
|
file: nil,
|
||||||
|
fn: self,
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
rt.leaveScope()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn.call(FunctionCall{
|
||||||
|
runtime: self.runtime,
|
||||||
|
eval: eval,
|
||||||
|
|
||||||
|
This: this,
|
||||||
|
ArgumentList: argumentList,
|
||||||
|
Otto: self.runtime.otto,
|
||||||
|
})
|
||||||
|
|
||||||
|
case _bindFunctionObject:
|
||||||
|
// TODO Passthrough site, do not enter a scope
|
||||||
|
argumentList = append(fn.argumentList, argumentList...)
|
||||||
|
return fn.target.call(fn.this, argumentList, false, frame)
|
||||||
|
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
rt := self.runtime
|
||||||
|
stash := rt.enterFunctionScope(fn.stash, this)
|
||||||
|
rt.scope.frame = _frame{
|
||||||
|
callee: fn.node.name,
|
||||||
|
file: fn.node.file,
|
||||||
|
fn: self,
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
rt.leaveScope()
|
||||||
|
}()
|
||||||
|
callValue := rt.cmpl_call_nodeFunction(self, stash, fn.node, this, argumentList)
|
||||||
|
if value, valid := callValue.value.(_result); valid {
|
||||||
|
return value.value
|
||||||
|
}
|
||||||
|
return callValue
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) construct(argumentList []Value) Value {
|
||||||
|
switch fn := self.value.(type) {
|
||||||
|
|
||||||
|
case _nativeFunctionObject:
|
||||||
|
if fn.call == nil {
|
||||||
|
panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
|
||||||
|
}
|
||||||
|
if fn.construct == nil {
|
||||||
|
panic(self.runtime.panicTypeError("%v is not a constructor", toValue_object(self)))
|
||||||
|
}
|
||||||
|
return fn.construct(self, argumentList)
|
||||||
|
|
||||||
|
case _bindFunctionObject:
|
||||||
|
return fn.construct(argumentList)
|
||||||
|
|
||||||
|
case _nodeFunctionObject:
|
||||||
|
return defaultConstruct(self, argumentList)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15.3.5.3
|
||||||
|
func (self *_object) hasInstance(of Value) bool {
|
||||||
|
if !self.isCall() {
|
||||||
|
// We should not have a hasInstance method
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
if !of.IsObject() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
prototype := self.get("prototype")
|
||||||
|
if !prototype.IsObject() {
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
prototypeObject := prototype._object()
|
||||||
|
|
||||||
|
value := of._object().prototype
|
||||||
|
for value != nil {
|
||||||
|
if value == prototypeObject {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
value = value.prototype
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ //
|
||||||
|
// FunctionCall //
|
||||||
|
// ============ //
|
||||||
|
|
||||||
|
// FunctionCall is an encapsulation of a JavaScript function call.
|
||||||
|
type FunctionCall struct {
|
||||||
|
runtime *_runtime
|
||||||
|
_thisObject *_object
|
||||||
|
eval bool // This call is a direct call to eval
|
||||||
|
|
||||||
|
This Value
|
||||||
|
ArgumentList []Value
|
||||||
|
Otto *Otto
|
||||||
|
}
|
||||||
|
|
||||||
|
// Argument will return the value of the argument at the given index.
|
||||||
|
//
|
||||||
|
// If no such argument exists, undefined is returned.
|
||||||
|
func (self FunctionCall) Argument(index int) Value {
|
||||||
|
return valueOfArrayIndex(self.ArgumentList, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self FunctionCall) getArgument(index int) (Value, bool) {
|
||||||
|
return getValueOfArrayIndex(self.ArgumentList, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self FunctionCall) slice(index int) []Value {
|
||||||
|
if index < len(self.ArgumentList) {
|
||||||
|
return self.ArgumentList[index:]
|
||||||
|
}
|
||||||
|
return []Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FunctionCall) thisObject() *_object {
|
||||||
|
if self._thisObject == nil {
|
||||||
|
this := self.This.resolve() // FIXME Is this right?
|
||||||
|
self._thisObject = self.runtime.toObject(this)
|
||||||
|
}
|
||||||
|
return self._thisObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FunctionCall) thisClassObject(class string) *_object {
|
||||||
|
thisObject := self.thisObject()
|
||||||
|
if thisObject.class != class {
|
||||||
|
panic(self.runtime.panicTypeError())
|
||||||
|
}
|
||||||
|
return self._thisObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self FunctionCall) toObject(value Value) *_object {
|
||||||
|
return self.runtime.toObject(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallerLocation will return file location information (file:line:pos) where this function is being called.
|
||||||
|
func (self FunctionCall) CallerLocation() string {
|
||||||
|
// see error.go for location()
|
||||||
|
return self.runtime.scope.outer.frame.location()
|
||||||
|
}
|
156
vendor/github.com/robertkrimen/otto/type_go_array.go
generated
vendored
Normal file
156
vendor/github.com/robertkrimen/otto/type_go_array.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (runtime *_runtime) newGoArrayObject(value reflect.Value) *_object {
|
||||||
|
self := runtime.newObject()
|
||||||
|
self.class = "GoArray"
|
||||||
|
self.objectClass = _classGoArray
|
||||||
|
self.value = _newGoArrayObject(value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
type _goArrayObject struct {
|
||||||
|
value reflect.Value
|
||||||
|
writable bool
|
||||||
|
propertyMode _propertyMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func _newGoArrayObject(value reflect.Value) *_goArrayObject {
|
||||||
|
writable := value.Kind() == reflect.Ptr // The Array is addressable (like a Slice)
|
||||||
|
mode := _propertyMode(0010)
|
||||||
|
if writable {
|
||||||
|
mode = 0110
|
||||||
|
}
|
||||||
|
self := &_goArrayObject{
|
||||||
|
value: value,
|
||||||
|
writable: writable,
|
||||||
|
propertyMode: mode,
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goArrayObject) getValue(name string) (reflect.Value, bool) {
|
||||||
|
if index, err := strconv.ParseInt(name, 10, 64); err != nil {
|
||||||
|
v, ok := self.getValueIndex(index)
|
||||||
|
if ok {
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m := self.value.MethodByName(name); m != (reflect.Value{}) {
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goArrayObject) getValueIndex(index int64) (reflect.Value, bool) {
|
||||||
|
value := reflect.Indirect(self.value)
|
||||||
|
if index < int64(value.Len()) {
|
||||||
|
return value.Index(int(index)), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goArrayObject) setValue(index int64, value Value) bool {
|
||||||
|
indexValue, exists := self.getValueIndex(index)
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
reflectValue, err := value.toReflectValue(reflect.Indirect(self.value).Type().Elem().Kind())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
indexValue.Set(reflectValue)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func goArrayGetOwnProperty(self *_object, name string) *_property {
|
||||||
|
// length
|
||||||
|
if name == "length" {
|
||||||
|
return &_property{
|
||||||
|
value: toValue(reflect.Indirect(self.value.(*_goArrayObject).value).Len()),
|
||||||
|
mode: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .0, .1, .2, ...
|
||||||
|
if index := stringToArrayIndex(name); index >= 0 {
|
||||||
|
object := self.value.(*_goArrayObject)
|
||||||
|
value := Value{}
|
||||||
|
reflectValue, exists := object.getValueIndex(index)
|
||||||
|
if exists {
|
||||||
|
value = self.runtime.toValue(reflectValue.Interface())
|
||||||
|
}
|
||||||
|
return &_property{
|
||||||
|
value: value,
|
||||||
|
mode: object.propertyMode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if method := self.value.(*_goArrayObject).value.MethodByName(name); method != (reflect.Value{}) {
|
||||||
|
return &_property{
|
||||||
|
self.runtime.toValue(method.Interface()),
|
||||||
|
0110,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectGetOwnProperty(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goArrayEnumerate(self *_object, all bool, each func(string) bool) {
|
||||||
|
object := self.value.(*_goArrayObject)
|
||||||
|
// .0, .1, .2, ...
|
||||||
|
|
||||||
|
for index, length := 0, object.value.Len(); index < length; index++ {
|
||||||
|
name := strconv.FormatInt(int64(index), 10)
|
||||||
|
if !each(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
objectEnumerate(self, all, each)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goArrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
|
||||||
|
if name == "length" {
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
} else if index := stringToArrayIndex(name); index >= 0 {
|
||||||
|
object := self.value.(*_goArrayObject)
|
||||||
|
if object.writable {
|
||||||
|
if self.value.(*_goArrayObject).setValue(index, descriptor.value.(Value)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
return objectDefineOwnProperty(self, name, descriptor, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goArrayDelete(self *_object, name string, throw bool) bool {
|
||||||
|
// length
|
||||||
|
if name == "length" {
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// .0, .1, .2, ...
|
||||||
|
index := stringToArrayIndex(name)
|
||||||
|
if index >= 0 {
|
||||||
|
object := self.value.(*_goArrayObject)
|
||||||
|
if object.writable {
|
||||||
|
indexValue, exists := object.getValueIndex(index)
|
||||||
|
if exists {
|
||||||
|
indexValue.Set(reflect.Zero(reflect.Indirect(object.value).Type().Elem()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.delete(name, throw)
|
||||||
|
}
|
95
vendor/github.com/robertkrimen/otto/type_go_map.go
generated
vendored
Normal file
95
vendor/github.com/robertkrimen/otto/type_go_map.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (runtime *_runtime) newGoMapObject(value reflect.Value) *_object {
|
||||||
|
self := runtime.newObject()
|
||||||
|
self.class = "Object" // TODO Should this be something else?
|
||||||
|
self.objectClass = _classGoMap
|
||||||
|
self.value = _newGoMapObject(value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
type _goMapObject struct {
|
||||||
|
value reflect.Value
|
||||||
|
keyKind reflect.Kind
|
||||||
|
valueKind reflect.Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
func _newGoMapObject(value reflect.Value) *_goMapObject {
|
||||||
|
if value.Kind() != reflect.Map {
|
||||||
|
dbgf("%/panic//%@: %v != reflect.Map", value.Kind())
|
||||||
|
}
|
||||||
|
self := &_goMapObject{
|
||||||
|
value: value,
|
||||||
|
keyKind: value.Type().Key().Kind(),
|
||||||
|
valueKind: value.Type().Elem().Kind(),
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goMapObject) toKey(name string) reflect.Value {
|
||||||
|
reflectValue, err := stringToReflectValue(name, self.keyKind)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return reflectValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goMapObject) toValue(value Value) reflect.Value {
|
||||||
|
reflectValue, err := value.toReflectValue(self.valueKind)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return reflectValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func goMapGetOwnProperty(self *_object, name string) *_property {
|
||||||
|
object := self.value.(*_goMapObject)
|
||||||
|
value := object.value.MapIndex(object.toKey(name))
|
||||||
|
if value.IsValid() {
|
||||||
|
return &_property{self.runtime.toValue(value.Interface()), 0111}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other methods
|
||||||
|
if method := self.value.(*_goMapObject).value.MethodByName(name); (method != reflect.Value{}) {
|
||||||
|
return &_property{
|
||||||
|
value: self.runtime.toValue(method.Interface()),
|
||||||
|
mode: 0110,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func goMapEnumerate(self *_object, all bool, each func(string) bool) {
|
||||||
|
object := self.value.(*_goMapObject)
|
||||||
|
keys := object.value.MapKeys()
|
||||||
|
for _, key := range keys {
|
||||||
|
if !each(toValue(key).String()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func goMapDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
|
||||||
|
object := self.value.(*_goMapObject)
|
||||||
|
// TODO ...or 0222
|
||||||
|
if descriptor.mode != 0111 {
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
if !descriptor.isDataDescriptor() {
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
object.value.SetMapIndex(object.toKey(name), object.toValue(descriptor.value.(Value)))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func goMapDelete(self *_object, name string, throw bool) bool {
|
||||||
|
object := self.value.(*_goMapObject)
|
||||||
|
object.value.SetMapIndex(object.toKey(name), reflect.Value{})
|
||||||
|
// FIXME
|
||||||
|
return true
|
||||||
|
}
|
126
vendor/github.com/robertkrimen/otto/type_go_slice.go
generated
vendored
Normal file
126
vendor/github.com/robertkrimen/otto/type_go_slice.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (runtime *_runtime) newGoSliceObject(value reflect.Value) *_object {
|
||||||
|
self := runtime.newObject()
|
||||||
|
self.class = "GoArray" // TODO GoSlice?
|
||||||
|
self.objectClass = _classGoSlice
|
||||||
|
self.value = _newGoSliceObject(value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
type _goSliceObject struct {
|
||||||
|
value reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func _newGoSliceObject(value reflect.Value) *_goSliceObject {
|
||||||
|
self := &_goSliceObject{
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goSliceObject) getValue(index int64) (reflect.Value, bool) {
|
||||||
|
if index < int64(self.value.Len()) {
|
||||||
|
return self.value.Index(int(index)), true
|
||||||
|
}
|
||||||
|
return reflect.Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goSliceObject) setValue(index int64, value Value) bool {
|
||||||
|
indexValue, exists := self.getValue(index)
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
indexValue.Set(reflectValue)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func goSliceGetOwnProperty(self *_object, name string) *_property {
|
||||||
|
// length
|
||||||
|
if name == "length" {
|
||||||
|
return &_property{
|
||||||
|
value: toValue(self.value.(*_goSliceObject).value.Len()),
|
||||||
|
mode: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .0, .1, .2, ...
|
||||||
|
index := stringToArrayIndex(name)
|
||||||
|
if index >= 0 {
|
||||||
|
value := Value{}
|
||||||
|
reflectValue, exists := self.value.(*_goSliceObject).getValue(index)
|
||||||
|
if exists {
|
||||||
|
value = self.runtime.toValue(reflectValue.Interface())
|
||||||
|
}
|
||||||
|
return &_property{
|
||||||
|
value: value,
|
||||||
|
mode: 0110,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other methods
|
||||||
|
if method := self.value.(*_goSliceObject).value.MethodByName(name); (method != reflect.Value{}) {
|
||||||
|
return &_property{
|
||||||
|
value: self.runtime.toValue(method.Interface()),
|
||||||
|
mode: 0110,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectGetOwnProperty(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goSliceEnumerate(self *_object, all bool, each func(string) bool) {
|
||||||
|
object := self.value.(*_goSliceObject)
|
||||||
|
// .0, .1, .2, ...
|
||||||
|
|
||||||
|
for index, length := 0, object.value.Len(); index < length; index++ {
|
||||||
|
name := strconv.FormatInt(int64(index), 10)
|
||||||
|
if !each(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
objectEnumerate(self, all, each)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goSliceDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
|
||||||
|
if name == "length" {
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
} else if index := stringToArrayIndex(name); index >= 0 {
|
||||||
|
if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
return objectDefineOwnProperty(self, name, descriptor, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goSliceDelete(self *_object, name string, throw bool) bool {
|
||||||
|
// length
|
||||||
|
if name == "length" {
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// .0, .1, .2, ...
|
||||||
|
index := stringToArrayIndex(name)
|
||||||
|
if index >= 0 {
|
||||||
|
object := self.value.(*_goSliceObject)
|
||||||
|
indexValue, exists := object.getValue(index)
|
||||||
|
if exists {
|
||||||
|
indexValue.Set(reflect.Zero(object.value.Type().Elem()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return self.runtime.typeErrorResult(throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.delete(name, throw)
|
||||||
|
}
|
146
vendor/github.com/robertkrimen/otto/type_go_struct.go
generated
vendored
Normal file
146
vendor/github.com/robertkrimen/otto/type_go_struct.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FIXME Make a note about not being able to modify a struct unless it was
|
||||||
|
// passed as a pointer-to: &struct{ ... }
|
||||||
|
// This seems to be a limitation of the reflect package.
|
||||||
|
// This goes for the other Go constructs too.
|
||||||
|
// I guess we could get around it by either:
|
||||||
|
// 1. Creating a new struct every time
|
||||||
|
// 2. Creating an addressable? struct in the constructor
|
||||||
|
|
||||||
|
func (runtime *_runtime) newGoStructObject(value reflect.Value) *_object {
|
||||||
|
self := runtime.newObject()
|
||||||
|
self.class = "Object" // TODO Should this be something else?
|
||||||
|
self.objectClass = _classGoStruct
|
||||||
|
self.value = _newGoStructObject(value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
type _goStructObject struct {
|
||||||
|
value reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func _newGoStructObject(value reflect.Value) *_goStructObject {
|
||||||
|
if reflect.Indirect(value).Kind() != reflect.Struct {
|
||||||
|
dbgf("%/panic//%@: %v != reflect.Struct", value.Kind())
|
||||||
|
}
|
||||||
|
self := &_goStructObject{
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goStructObject) getValue(name string) reflect.Value {
|
||||||
|
if idx := fieldIndexByName(reflect.Indirect(self.value).Type(), name); len(idx) > 0 {
|
||||||
|
return reflect.Indirect(self.value).FieldByIndex(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if validGoStructName(name) {
|
||||||
|
// Do not reveal hidden or unexported fields
|
||||||
|
if field := reflect.Indirect(self.value).FieldByName(name); (field != reflect.Value{}) {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
if method := self.value.MethodByName(name); (method != reflect.Value{}) {
|
||||||
|
return method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goStructObject) fieldIndex(name string) []int {
|
||||||
|
return fieldIndexByName(reflect.Indirect(self.value).Type(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goStructObject) method(name string) (reflect.Method, bool) {
|
||||||
|
return reflect.Indirect(self.value).Type().MethodByName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _goStructObject) setValue(rt *_runtime, name string, value Value) bool {
|
||||||
|
if idx := fieldIndexByName(reflect.Indirect(self.value).Type(), name); len(idx) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValue := self.getValue(name)
|
||||||
|
fieldValue.Set(rt.convertCallParameter(value, fieldValue.Type()))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func goStructGetOwnProperty(self *_object, name string) *_property {
|
||||||
|
object := self.value.(*_goStructObject)
|
||||||
|
value := object.getValue(name)
|
||||||
|
if value.IsValid() {
|
||||||
|
return &_property{self.runtime.toValue(value.Interface()), 0110}
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectGetOwnProperty(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validGoStructName(name string) bool {
|
||||||
|
if name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return 'A' <= name[0] && name[0] <= 'Z' // TODO What about Unicode?
|
||||||
|
}
|
||||||
|
|
||||||
|
func goStructEnumerate(self *_object, all bool, each func(string) bool) {
|
||||||
|
object := self.value.(*_goStructObject)
|
||||||
|
|
||||||
|
// Enumerate fields
|
||||||
|
for index := 0; index < reflect.Indirect(object.value).NumField(); index++ {
|
||||||
|
name := reflect.Indirect(object.value).Type().Field(index).Name
|
||||||
|
if validGoStructName(name) {
|
||||||
|
if !each(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enumerate methods
|
||||||
|
for index := 0; index < object.value.NumMethod(); index++ {
|
||||||
|
name := object.value.Type().Method(index).Name
|
||||||
|
if validGoStructName(name) {
|
||||||
|
if !each(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
objectEnumerate(self, all, each)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goStructCanPut(self *_object, name string) bool {
|
||||||
|
object := self.value.(*_goStructObject)
|
||||||
|
value := object.getValue(name)
|
||||||
|
if value.IsValid() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectCanPut(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goStructPut(self *_object, name string, value Value, throw bool) {
|
||||||
|
object := self.value.(*_goStructObject)
|
||||||
|
if object.setValue(self.runtime, name, value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
objectPut(self, name, value, throw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goStructMarshalJSON(self *_object) json.Marshaler {
|
||||||
|
object := self.value.(*_goStructObject)
|
||||||
|
goValue := reflect.Indirect(object.value).Interface()
|
||||||
|
switch marshaler := goValue.(type) {
|
||||||
|
case json.Marshaler:
|
||||||
|
return marshaler
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
5
vendor/github.com/robertkrimen/otto/type_number.go
generated
vendored
Normal file
5
vendor/github.com/robertkrimen/otto/type_number.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
func (runtime *_runtime) newNumberObject(value Value) *_object {
|
||||||
|
return runtime.newPrimitiveObject("Number", value.numberValue())
|
||||||
|
}
|
103
vendor/github.com/robertkrimen/otto/type_reference.go
generated
vendored
Normal file
103
vendor/github.com/robertkrimen/otto/type_reference.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
type _reference interface {
|
||||||
|
invalid() bool // IsUnresolvableReference
|
||||||
|
getValue() Value // getValue
|
||||||
|
putValue(Value) string // PutValue
|
||||||
|
delete() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertyReference
|
||||||
|
|
||||||
|
type _propertyReference struct {
|
||||||
|
name string
|
||||||
|
strict bool
|
||||||
|
base *_object
|
||||||
|
runtime *_runtime
|
||||||
|
at _at
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPropertyReference(rt *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference {
|
||||||
|
return &_propertyReference{
|
||||||
|
runtime: rt,
|
||||||
|
name: name,
|
||||||
|
strict: strict,
|
||||||
|
base: base,
|
||||||
|
at: at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_propertyReference) invalid() bool {
|
||||||
|
return self.base == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_propertyReference) getValue() Value {
|
||||||
|
if self.base == nil {
|
||||||
|
panic(self.runtime.panicReferenceError("'%s' is not defined", self.name, self.at))
|
||||||
|
}
|
||||||
|
return self.base.get(self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_propertyReference) putValue(value Value) string {
|
||||||
|
if self.base == nil {
|
||||||
|
return self.name
|
||||||
|
}
|
||||||
|
self.base.put(self.name, value, self.strict)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_propertyReference) delete() bool {
|
||||||
|
if self.base == nil {
|
||||||
|
// TODO Throw an error if strict
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return self.base.delete(self.name, self.strict)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArgumentReference
|
||||||
|
|
||||||
|
func newArgumentReference(runtime *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference {
|
||||||
|
if base == nil {
|
||||||
|
panic(hereBeDragons())
|
||||||
|
}
|
||||||
|
return newPropertyReference(runtime, base, name, strict, at)
|
||||||
|
}
|
||||||
|
|
||||||
|
type _stashReference struct {
|
||||||
|
name string
|
||||||
|
strict bool
|
||||||
|
base _stash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_stashReference) invalid() bool {
|
||||||
|
return false // The base (an environment) will never be nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_stashReference) getValue() Value {
|
||||||
|
return self.base.getBinding(self.name, self.strict)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_stashReference) putValue(value Value) string {
|
||||||
|
self.base.setValue(self.name, value, self.strict)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_stashReference) delete() bool {
|
||||||
|
if self.base == nil {
|
||||||
|
// This should never be reached, but just in case
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return self.base.deleteBinding(self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getIdentifierReference
|
||||||
|
|
||||||
|
func getIdentifierReference(runtime *_runtime, stash _stash, name string, strict bool, at _at) _reference {
|
||||||
|
if stash == nil {
|
||||||
|
return newPropertyReference(runtime, nil, name, strict, at)
|
||||||
|
}
|
||||||
|
if stash.hasBinding(name) {
|
||||||
|
return stash.newReference(name, strict, at)
|
||||||
|
}
|
||||||
|
return getIdentifierReference(runtime, stash.outer(), name, strict, at)
|
||||||
|
}
|
146
vendor/github.com/robertkrimen/otto/type_regexp.go
generated
vendored
Normal file
146
vendor/github.com/robertkrimen/otto/type_regexp.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _regExpObject struct {
|
||||||
|
regularExpression *regexp.Regexp
|
||||||
|
global bool
|
||||||
|
ignoreCase bool
|
||||||
|
multiline bool
|
||||||
|
source string
|
||||||
|
flags string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newRegExpObject(pattern string, flags string) *_object {
|
||||||
|
self := runtime.newObject()
|
||||||
|
self.class = "RegExp"
|
||||||
|
|
||||||
|
global := false
|
||||||
|
ignoreCase := false
|
||||||
|
multiline := false
|
||||||
|
re2flags := ""
|
||||||
|
|
||||||
|
// TODO Maybe clean up the panicking here... TypeError, SyntaxError, ?
|
||||||
|
|
||||||
|
for _, chr := range flags {
|
||||||
|
switch chr {
|
||||||
|
case 'g':
|
||||||
|
if global {
|
||||||
|
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
|
||||||
|
}
|
||||||
|
global = true
|
||||||
|
case 'm':
|
||||||
|
if multiline {
|
||||||
|
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
|
||||||
|
}
|
||||||
|
multiline = true
|
||||||
|
re2flags += "m"
|
||||||
|
case 'i':
|
||||||
|
if ignoreCase {
|
||||||
|
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
|
||||||
|
}
|
||||||
|
ignoreCase = true
|
||||||
|
re2flags += "i"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
re2pattern, err := parser.TransformRegExp(pattern)
|
||||||
|
if err != nil {
|
||||||
|
panic(runtime.panicTypeError("Invalid regular expression: %s", err.Error()))
|
||||||
|
}
|
||||||
|
if len(re2flags) > 0 {
|
||||||
|
re2pattern = fmt.Sprintf("(?%s:%s)", re2flags, re2pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
regularExpression, err := regexp.Compile(re2pattern)
|
||||||
|
if err != nil {
|
||||||
|
panic(runtime.panicSyntaxError("Invalid regular expression: %s", err.Error()[22:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.value = _regExpObject{
|
||||||
|
regularExpression: regularExpression,
|
||||||
|
global: global,
|
||||||
|
ignoreCase: ignoreCase,
|
||||||
|
multiline: multiline,
|
||||||
|
source: pattern,
|
||||||
|
flags: flags,
|
||||||
|
}
|
||||||
|
self.defineProperty("global", toValue_bool(global), 0, false)
|
||||||
|
self.defineProperty("ignoreCase", toValue_bool(ignoreCase), 0, false)
|
||||||
|
self.defineProperty("multiline", toValue_bool(multiline), 0, false)
|
||||||
|
self.defineProperty("lastIndex", toValue_int(0), 0100, false)
|
||||||
|
self.defineProperty("source", toValue_string(pattern), 0, false)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) regExpValue() _regExpObject {
|
||||||
|
value, _ := self.value.(_regExpObject)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func execRegExp(this *_object, target string) (match bool, result []int) {
|
||||||
|
if this.class != "RegExp" {
|
||||||
|
panic(this.runtime.panicTypeError("Calling RegExp.exec on a non-RegExp object"))
|
||||||
|
}
|
||||||
|
lastIndex := this.get("lastIndex").number().int64
|
||||||
|
index := lastIndex
|
||||||
|
global := this.get("global").bool()
|
||||||
|
if !global {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
if 0 > index || index > int64(len(target)) {
|
||||||
|
} else {
|
||||||
|
result = this.regExpValue().regularExpression.FindStringSubmatchIndex(target[index:])
|
||||||
|
}
|
||||||
|
if result == nil {
|
||||||
|
//this.defineProperty("lastIndex", toValue_(0), 0111, true)
|
||||||
|
this.put("lastIndex", toValue_int(0), true)
|
||||||
|
return // !match
|
||||||
|
}
|
||||||
|
match = true
|
||||||
|
startIndex := index
|
||||||
|
endIndex := int(lastIndex) + result[1]
|
||||||
|
// We do this shift here because the .FindStringSubmatchIndex above
|
||||||
|
// was done on a local subordinate slice of the string, not the whole string
|
||||||
|
for index, _ := range result {
|
||||||
|
result[index] += int(startIndex)
|
||||||
|
}
|
||||||
|
if global {
|
||||||
|
//this.defineProperty("lastIndex", toValue_(endIndex), 0111, true)
|
||||||
|
this.put("lastIndex", toValue_int(endIndex), true)
|
||||||
|
}
|
||||||
|
return // match
|
||||||
|
}
|
||||||
|
|
||||||
|
func execResultToArray(runtime *_runtime, target string, result []int) *_object {
|
||||||
|
captureCount := len(result) / 2
|
||||||
|
valueArray := make([]Value, captureCount)
|
||||||
|
for index := 0; index < captureCount; index++ {
|
||||||
|
offset := 2 * index
|
||||||
|
if result[offset] != -1 {
|
||||||
|
valueArray[index] = toValue_string(target[result[offset]:result[offset+1]])
|
||||||
|
} else {
|
||||||
|
valueArray[index] = Value{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matchIndex := result[0]
|
||||||
|
if matchIndex != 0 {
|
||||||
|
matchIndex = 0
|
||||||
|
// Find the rune index in the string, not the byte index
|
||||||
|
for index := 0; index < result[0]; {
|
||||||
|
_, size := utf8.DecodeRuneInString(target[index:])
|
||||||
|
matchIndex += 1
|
||||||
|
index += size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match := runtime.newArrayOf(valueArray)
|
||||||
|
match.defineProperty("input", toValue_string(target), 0111, false)
|
||||||
|
match.defineProperty("index", toValue_int(matchIndex), 0111, false)
|
||||||
|
return match
|
||||||
|
}
|
112
vendor/github.com/robertkrimen/otto/type_string.go
generated
vendored
Normal file
112
vendor/github.com/robertkrimen/otto/type_string.go
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _stringObject interface {
|
||||||
|
Length() int
|
||||||
|
At(int) rune
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type _stringASCII string
|
||||||
|
|
||||||
|
func (str _stringASCII) Length() int {
|
||||||
|
return len(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str _stringASCII) At(at int) rune {
|
||||||
|
return rune(str[at])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str _stringASCII) String() string {
|
||||||
|
return string(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
type _stringWide struct {
|
||||||
|
string string
|
||||||
|
length int
|
||||||
|
runes []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str _stringWide) Length() int {
|
||||||
|
return str.length
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str _stringWide) At(at int) rune {
|
||||||
|
if str.runes == nil {
|
||||||
|
str.runes = []rune(str.string)
|
||||||
|
}
|
||||||
|
return str.runes[at]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str _stringWide) String() string {
|
||||||
|
return str.string
|
||||||
|
}
|
||||||
|
|
||||||
|
func _newStringObject(str string) _stringObject {
|
||||||
|
for i := 0; i < len(str); i++ {
|
||||||
|
if str[i] >= utf8.RuneSelf {
|
||||||
|
goto wide
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _stringASCII(str)
|
||||||
|
|
||||||
|
wide:
|
||||||
|
return &_stringWide{
|
||||||
|
string: str,
|
||||||
|
length: utf8.RuneCountInString(str),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringAt(str _stringObject, index int) rune {
|
||||||
|
if 0 <= index && index < str.Length() {
|
||||||
|
return str.At(index)
|
||||||
|
}
|
||||||
|
return utf8.RuneError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runtime *_runtime) newStringObject(value Value) *_object {
|
||||||
|
str := _newStringObject(value.string())
|
||||||
|
|
||||||
|
self := runtime.newClassObject("String")
|
||||||
|
self.defineProperty("length", toValue_int(str.Length()), 0, false)
|
||||||
|
self.objectClass = _classString
|
||||||
|
self.value = str
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_object) stringValue() _stringObject {
|
||||||
|
if str, ok := self.value.(_stringObject); ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringEnumerate(self *_object, all bool, each func(string) bool) {
|
||||||
|
if str := self.stringValue(); str != nil {
|
||||||
|
length := str.Length()
|
||||||
|
for index := 0; index < length; index++ {
|
||||||
|
if !each(strconv.FormatInt(int64(index), 10)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objectEnumerate(self, all, each)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringGetOwnProperty(self *_object, name string) *_property {
|
||||||
|
if property := objectGetOwnProperty(self, name); property != nil {
|
||||||
|
return property
|
||||||
|
}
|
||||||
|
// TODO Test a string of length >= +int32 + 1?
|
||||||
|
if index := stringToArrayIndex(name); index >= 0 {
|
||||||
|
if chr := stringAt(self.stringValue(), int(index)); chr != utf8.RuneError {
|
||||||
|
return &_property{toValue_string(string(chr)), 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
1033
vendor/github.com/robertkrimen/otto/value.go
generated
vendored
Normal file
1033
vendor/github.com/robertkrimen/otto/value.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
43
vendor/github.com/robertkrimen/otto/value_boolean.go
generated
vendored
Normal file
43
vendor/github.com/robertkrimen/otto/value_boolean.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"unicode/utf16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (value Value) bool() bool {
|
||||||
|
if value.kind == valueBoolean {
|
||||||
|
return value.value.(bool)
|
||||||
|
}
|
||||||
|
if value.IsUndefined() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if value.IsNull() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch value := value.value.(type) {
|
||||||
|
case bool:
|
||||||
|
return value
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return 0 != reflect.ValueOf(value).Int()
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return 0 != reflect.ValueOf(value).Uint()
|
||||||
|
case float32:
|
||||||
|
return 0 != value
|
||||||
|
case float64:
|
||||||
|
if math.IsNaN(value) || value == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case string:
|
||||||
|
return 0 != len(value)
|
||||||
|
case []uint16:
|
||||||
|
return 0 != len(utf16.Decode(value))
|
||||||
|
}
|
||||||
|
if value.IsObject() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
panic(fmt.Errorf("toBoolean(%T)", value.value))
|
||||||
|
}
|
324
vendor/github.com/robertkrimen/otto/value_number.go
generated
vendored
Normal file
324
vendor/github.com/robertkrimen/otto/value_number.go
generated
vendored
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
package otto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var stringToNumberParseInteger = regexp.MustCompile(`^(?:0[xX])`)
|
||||||
|
|
||||||
|
func parseNumber(value string) float64 {
|
||||||
|
value = strings.Trim(value, builtinString_trim_whitespace)
|
||||||
|
|
||||||
|
if value == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
parseFloat := false
|
||||||
|
if strings.IndexRune(value, '.') != -1 {
|
||||||
|
parseFloat = true
|
||||||
|
} else if stringToNumberParseInteger.MatchString(value) {
|
||||||
|
parseFloat = false
|
||||||
|
} else {
|
||||||
|
parseFloat = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if parseFloat {
|
||||||
|
number, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil && err.(*strconv.NumError).Err != strconv.ErrRange {
|
||||||
|
return math.NaN()
|
||||||
|
}
|
||||||
|
return number
|
||||||
|
}
|
||||||
|
|
||||||
|
number, err := strconv.ParseInt(value, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return math.NaN()
|
||||||
|
}
|
||||||
|
return float64(number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (value Value) float64() float64 {
|
||||||
|
switch value.kind {
|
||||||
|
case valueUndefined:
|
||||||
|
return math.NaN()
|
||||||
|
case valueNull:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
switch value := value.value.(type) {
|
||||||
|
case bool:
|
||||||
|
if value {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
case int:
|
||||||
|
return float64(value)
|
||||||
|
case int8:
|
||||||
|
return float64(value)
|
||||||
|
case int16:
|
||||||
|
return float64(value)
|
||||||
|
case int32:
|
||||||
|
return float64(value)
|
||||||
|
case int64:
|
||||||
|
return float64(value)
|
||||||
|
case uint:
|
||||||
|
return float64(value)
|
||||||
|
case uint8:
|
||||||
|
return float64(value)
|
||||||
|
case uint16:
|
||||||
|
return float64(value)
|
||||||
|
case uint32:
|
||||||
|
return float64(value)
|
||||||
|
case uint64:
|
||||||
|
return float64(value)
|
||||||
|
case float64:
|
||||||
|
return value
|
||||||
|
case string:
|
||||||
|
return parseNumber(value)
|
||||||
|
case *_object:
|
||||||
|
return value.DefaultValue(defaultValueHintNumber).float64()
|
||||||
|
}
|
||||||
|
panic(fmt.Errorf("toFloat(%T)", value.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
float_2_64 float64 = 18446744073709551616.0
|
||||||
|
float_2_63 float64 = 9223372036854775808.0
|
||||||
|
float_2_32 float64 = 4294967296.0
|
||||||
|
float_2_31 float64 = 2147483648.0
|
||||||
|
float_2_16 float64 = 65536.0
|
||||||
|
integer_2_32 int64 = 4294967296
|
||||||
|
integer_2_31 int64 = 2146483648
|
||||||
|
sqrt1_2 float64 = math.Sqrt2 / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxInt8 = math.MaxInt8
|
||||||
|
minInt8 = math.MinInt8
|
||||||
|
maxInt16 = math.MaxInt16
|
||||||
|
minInt16 = math.MinInt16
|
||||||
|
maxInt32 = math.MaxInt32
|
||||||
|
minInt32 = math.MinInt32
|
||||||
|
maxInt64 = math.MaxInt64
|
||||||
|
minInt64 = math.MinInt64
|
||||||
|
maxUint8 = math.MaxUint8
|
||||||
|
maxUint16 = math.MaxUint16
|
||||||
|
maxUint32 = math.MaxUint32
|
||||||
|
maxUint64 = math.MaxUint64
|
||||||
|
maxUint = ^uint(0)
|
||||||
|
minUint = 0
|
||||||
|
maxInt = int(^uint(0) >> 1)
|
||||||
|
minInt = -maxInt - 1
|
||||||
|
|
||||||
|
// int64
|
||||||
|
int64_maxInt int64 = int64(maxInt)
|
||||||
|
int64_minInt int64 = int64(minInt)
|
||||||
|
int64_maxInt8 int64 = math.MaxInt8
|
||||||
|
int64_minInt8 int64 = math.MinInt8
|
||||||
|
int64_maxInt16 int64 = math.MaxInt16
|
||||||
|
int64_minInt16 int64 = math.MinInt16
|
||||||
|
int64_maxInt32 int64 = math.MaxInt32
|
||||||
|
int64_minInt32 int64 = math.MinInt32
|
||||||
|
int64_maxUint8 int64 = math.MaxUint8
|
||||||
|
int64_maxUint16 int64 = math.MaxUint16
|
||||||
|
int64_maxUint32 int64 = math.MaxUint32
|
||||||
|
|
||||||
|
// float64
|
||||||
|
float_maxInt float64 = float64(int(^uint(0) >> 1))
|
||||||
|
float_minInt float64 = float64(int(-maxInt - 1))
|
||||||
|
float_minUint float64 = float64(0)
|
||||||
|
float_maxUint float64 = float64(uint(^uint(0)))
|
||||||
|
float_minUint64 float64 = float64(0)
|
||||||
|
float_maxUint64 float64 = math.MaxUint64
|
||||||
|
float_maxInt64 float64 = math.MaxInt64
|
||||||
|
float_minInt64 float64 = math.MinInt64
|
||||||
|
)
|
||||||
|
|
||||||
|
func toIntegerFloat(value Value) float64 {
|
||||||
|
float := value.float64()
|
||||||
|
if math.IsInf(float, 0) {
|
||||||
|
} else if math.IsNaN(float) {
|
||||||
|
float = 0
|
||||||
|
} else if float > 0 {
|
||||||
|
float = math.Floor(float)
|
||||||
|
} else {
|
||||||
|
float = math.Ceil(float)
|
||||||
|
}
|
||||||
|
return float
|
||||||
|
}
|
||||||
|
|
||||||
|
type _numberKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
numberInteger _numberKind = iota // 3.0 => 3.0
|
||||||
|
numberFloat // 3.14159 => 3.0, 1+2**63 > 2**63-1
|
||||||
|
numberInfinity // Infinity => 2**63-1
|
||||||
|
numberNaN // NaN => 0
|
||||||
|
)
|
||||||
|
|
||||||
|
type _number struct {
|
||||||
|
kind _numberKind
|
||||||
|
int64 int64
|
||||||
|
float64 float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
// http://www.goinggo.net/2013/08/gustavos-ieee-754-brain-teaser.html
|
||||||
|
// http://bazaar.launchpad.net/~niemeyer/strepr/trunk/view/6/strepr.go#L160
|
||||||
|
func (value Value) number() (number _number) {
|
||||||
|
switch value := value.value.(type) {
|
||||||
|
case int8:
|
||||||
|
number.int64 = int64(value)
|
||||||
|
return
|
||||||
|
case int16:
|
||||||
|
number.int64 = int64(value)
|
||||||
|
return
|
||||||
|
case uint8:
|
||||||
|
number.int64 = int64(value)
|
||||||
|
return
|
||||||
|
case uint16:
|
||||||
|
number.int64 = int64(value)
|
||||||
|
return
|
||||||
|
case uint32:
|
||||||
|
number.int64 = int64(value)
|
||||||
|
return
|
||||||
|
case int:
|
||||||
|
number.int64 = int64(value)
|
||||||
|
return
|
||||||
|
case int64:
|
||||||
|
number.int64 = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
float := value.float64()
|
||||||
|
if float == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
number.kind = numberFloat
|
||||||
|
number.float64 = float
|
||||||
|
|
||||||
|
if math.IsNaN(float) {
|
||||||
|
number.kind = numberNaN
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.IsInf(float, 0) {
|
||||||
|
number.kind = numberInfinity
|
||||||
|
}
|
||||||
|
|
||||||
|
if float >= float_maxInt64 {
|
||||||
|
number.int64 = math.MaxInt64
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if float <= float_minInt64 {
|
||||||
|
number.int64 = math.MinInt64
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
integer := float64(0)
|
||||||
|
if float > 0 {
|
||||||
|
integer = math.Floor(float)
|
||||||
|
} else {
|
||||||
|
integer = math.Ceil(float)
|
||||||
|
}
|
||||||
|
|
||||||
|
if float == integer {
|
||||||
|
number.kind = numberInteger
|
||||||
|
}
|
||||||
|
number.int64 = int64(float)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECMA 262: 9.5
|
||||||
|
func toInt32(value Value) int32 {
|
||||||
|
{
|
||||||
|
switch value := value.value.(type) {
|
||||||
|
case int8:
|
||||||
|
return int32(value)
|
||||||
|
case int16:
|
||||||
|
return int32(value)
|
||||||
|
case int32:
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
floatValue := value.float64()
|
||||||
|
if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if floatValue == 0 { // This will work for +0 & -0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
remainder := math.Mod(floatValue, float_2_32)
|
||||||
|
if remainder > 0 {
|
||||||
|
remainder = math.Floor(remainder)
|
||||||
|
} else {
|
||||||
|
remainder = math.Ceil(remainder) + float_2_32
|
||||||
|
}
|
||||||
|
if remainder > float_2_31 {
|
||||||
|
return int32(remainder - float_2_32)
|
||||||
|
}
|
||||||
|
return int32(remainder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toUint32(value Value) uint32 {
|
||||||
|
{
|
||||||
|
switch value := value.value.(type) {
|
||||||
|
case int8:
|
||||||
|
return uint32(value)
|
||||||
|
case int16:
|
||||||
|
return uint32(value)
|
||||||
|
case uint8:
|
||||||
|
return uint32(value)
|
||||||
|
case uint16:
|
||||||
|
return uint32(value)
|
||||||
|
case uint32:
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
floatValue := value.float64()
|
||||||
|
if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if floatValue == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
remainder := math.Mod(floatValue, float_2_32)
|
||||||
|
if remainder > 0 {
|
||||||
|
remainder = math.Floor(remainder)
|
||||||
|
} else {
|
||||||
|
remainder = math.Ceil(remainder) + float_2_32
|
||||||
|
}
|
||||||
|
return uint32(remainder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toUint16(value Value) uint16 {
|
||||||
|
{
|
||||||
|
switch value := value.value.(type) {
|
||||||
|
case int8:
|
||||||
|
return uint16(value)
|
||||||
|
case uint8:
|
||||||
|
return uint16(value)
|
||||||
|
case uint16:
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
floatValue := value.float64()
|
||||||
|
if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if floatValue == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
remainder := math.Mod(floatValue, float_2_16)
|
||||||
|
if remainder > 0 {
|
||||||
|
remainder = math.Floor(remainder)
|
||||||
|
} else {
|
||||||
|
remainder = math.Ceil(remainder) + float_2_16
|
||||||
|
}
|
||||||
|
return uint16(remainder)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue